| Commit message (Collapse) | Author | Age | Files | Lines |
|
|
|
|
|
|
|
|
|
|
| |
This PR is part of the solution to emscripten-core/emscripten#15594.
emscripten Asyncify won't work properly in side modules, because the
globals, __asyncify_state and __asyncify_data, are not synchronized
between main-module and side-modules.
A new pass arg, asyncify-side-module, is added to make
__asyncify_state and __asyncify_data imported in the instrumented
wasm.
|
|
|
|
|
| |
without "ignoreImplicitTraps" (#4295)" (#4459)
This reverts commit 5cf3521708cfada341285414df2dc7366d7e5454.
|
|
|
|
|
|
|
| |
LiteralList overlaps with Literals, but is less efficient as it is not a
SmallVector.
Add reserve/capacity methods to SmallVector which are now
necessary to compile.
|
|
|
|
| |
"ignoreImplicitTraps" (#4295)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
When ignoring external input, assume params have a value of 0. This
makes it possible to eval main(argc, argv) if one is careful and does
not actually use those values.
This is basically a workaround for main always receiving argc/argv,
even if the C code has no args (in that case the compiler emits
__original_main for the user's main, and wraps it with a main
that adds the args, hence the problem).
This is similar to the existing support for handling wasi_args_get
when ignoring external input, although it just sets values of zeros for
the params. Perhaps it could check for main() specifically and return
1 for argc and a proper buffer for argv somehow, but I think if a program
wants to use --ignore-external-input it can avoid actually reading
argc/argv.
|
|
|
| |
This is necessary for e.g. main() which returns an i32.
|
|
|
|
|
|
|
|
| |
This tool depends (atm) on flattening memory segments. That is not compatible
with memory.init which cares about segment identities.
This changes flatten() only by adding the check for MemoryInit. The rest is
unchanged, although I saw the other two params are not needed and I removed
them while I was there.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
By default wasm-ctor-eval removes exports that it manages to completely
eval (if it just partially evals then the export remains, but points to a function
with partially-evalled contents). However, in some cases we do want to keep
the export around even so, for example during fuzzing (as the fuzzer wants
to call the same exports before and after wasm-ctor-eval runs) and also
if there is an ABI we need to preserve (like if we manage to eval all of
main()), or if the function returns a value (which we don't support yet, but
this is a PR to prepare for that).
Specifically, there is now a new option:
--kept-exports foo,bar
That is a list of exports to keep around.
Note that when we keep around an export after evalling the ctor we
make the export point to a new function. That new function just
contains a nop, so that nothing happens when it is called. But the
original function is kept around as it may have other callers, who we
do not want to modify.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This lets us eval part of a function but not all, which is necessary to handle
real-world things like __wasm_call_ctors in LLVM output, as that is the
single ctor that is exported and it has calls to the actual ctors.
To do so, we look for a toplevel block and execute its items one by one, in
a FunctionScope. If we stop in the middle, then we are performing a partial
eval. In that case, we only remove the parts of the function that we removed,
and we also serialize the locals whose values we read from the
FunctionScope.
For example, consider this:
function foo() {
return 10;
}
function __wasm_call_ctors() {
var x;
x = foo();
x++;
// We stop evalling here.
import1();
import2(x);
}
We can eval x = foo() and x++, but we must stop evalling when
we reach the first of those imports. The partially-evalled function
then looks like this:
function __wasm_call_ctors() {
var x;
x = 11;
import1();
import2(x);
}
That is, we evalled two lines of executing code and simply removed
them, and then we wrote out the value of the local at that point, and then
the rest of the code in the function is as it used to be.
|
| |
|
|
|
|
|
|
|
|
| |
As it happens, this doesn't (normally) break the resulting EM_ASM or
EM_JS strings because (IIUC) JS supports the tab literal inside of
strings as well as "\t".
However, it's better to preserve the original text so that it looks
the same in the JS file as it did in the original source.
|
|
|
|
|
|
|
|
|
| |
This change was generated by running:
./scripts/test/generate_lld_tests.py
and
./auto_update_tests.py lld
|
|
|
|
|
|
|
| |
Fixes the crash in #4418
Also replace the .at() there with better logic to handle imported functions.
See WebAssembly/wabt#1799 for details on why wabt sometimes emits this.
|
|
|
|
|
|
|
|
|
|
| |
This is necessary for being able to optimize real-world code, as it lets us
use the stack pointer for example. With this PR we allow changes to
globals, and we simply store the final state of the global in the global at
the end. Basically the same as we do for memory, but for globals.
Remove a test that now fails ("imported2"). Replace it with a nicer test
of saving the values of globals. Also add a test for an imported global,
which we do not allow (we never did, but I don't see a test for it).
|
|
|
|
|
|
|
|
|
|
|
|
| |
This is meant to address one of the main limitations of wasm-ctor-eval in
emscripten atm, that libc++ global ctors will read env vars, which means they
call an import, which stops us from evalling,
emscripten-core/emscripten#15403 (comment)
To handle that, this adds an option to ignore external input. When set, we can
assume that no env vars will be read, no reading from stdin, no arguments to
main(), etc. Perhaps these could each be separate options, but I think keeping it
simple for now might be good enough.
|
|
|
|
|
|
|
|
|
| |
Remove some hackish code for fastcomp's stack handling. The stack pointer arrives
in an imported global there. Upstream does not do this, so this code is completely
unneeded these days (and, frankly, kind of scary as I read it now... it modeled the
stack as separate memory from the heap...).
Remove the tests for this as well. I verified that there was nothing else in those
tests that we need to keep.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The general shape of the --help output is now:
========================
wasm-foo
Does the foo operation
========================
wasm-foo opts:
--------------
--foo-bar ..
Tool opts:
----------
..
The options are now in categories, with the more specific ones - most likely to be
wanted by the user - first. I think this makes the list a lot less confusing.
In particular, in wasm-opt all the opt passes are now in their own category.
Also add a script to make it easy to update the help tests.
|
|
|
|
|
|
| |
Without this, the result in a build without assertions might be quite
confusing. See #4410
Also make the internal names more obviously internal names.
|
|
|
| |
Apparently it is not a binary test?
|
|
|
|
|
|
| |
When reading stacky code in the binary reader, we create `block`s to
make it fit into Binaryen AST, within which `pop`s can be nested, making
the resulting AST invalid. This PR runs the fixup function after reading
each `Try` to fix this.
|
|
|
|
|
|
|
|
|
| |
This enables fuzzing EH with initial contents. fuzzing.cpp/h does not
yet support generation of EH instructions, but with this we can still
fuzz EH based on initial contents.
The fuzzer ran successfully for more than 1,900,000 iterations, with my
local modification that always enables EH and lets the fuzzer select
only EH tests for its initial contents.
|
|
|
|
|
|
|
|
| |
We used to only compare return values, and in #4369 we started comparing
whether an uncaught exception was thrown. This also adds whether a trap
occurred to `ExecutionResults`. So in `--fuzz-exec`, if a program with a
trap loses the trap or vice versa, it will error out saying the result
has changed, unless either of `--ignore-implicit-traps` or
`--trans-never-happen` is set.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
`ref.cast` can be statically removed when the ref's type is a subtype of
the intended RTT type and either of `--ignore-implicit-traps` or
`--traps-never-happen` is given: https://github.com/WebAssembly/binaryen/blob/083ab9842ec3d4ca278c95e1a33112ae7cd4d9e5/src/passes/OptimizeInstructions.cpp#L1603-L1624
Some more context: https://github.com/WebAssembly/binaryen/pull/4097#discussion_r694456784
But this can create a block in which a `pop` is nested, which makes the
`catch` invalid. The test in this PR is the same as the example given by
@kripken in #4237. This calls the fixup function
`EHUtils::handleBlockNestedPops` at the end of the pass to fix this.
Also, because this pass creates a lot of blocks in other patterns, I
think it is possible there can be other patterns to cause this kind of
`pop` nesting.
|
|
|
|
|
|
|
| |
When a parameter and a member variable have the same name within a
constructor, to access (and change) the member variable, we need to
either use `this->` or change the name of the parameter. The current
code ended up changing the parameter and didn't affect the status of the
member variable, which remained empty.
|
| |
|
|
|
| |
See https://github.com/emscripten-core/emscripten/pull/15855
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Inlining creates additional `block`s at inlined call sites, which can be
inside a `catch`. For example:
```wast
(try
(do)
(catch $tag
(call $callee
(pop i32)
)
)
)
```
After inlining, this becomes
```wast
(try
(do)
(catch $tag
(block $__inlined_func$callee
(local.set $0
(pop i32) ;; Invalid!!
)
(nop)
)
)
)
```
Now the `pop` is nested in a `block`, which makes this invalid. This PR
runs `EHUtils::handleBlockNestedPops` at the end to assign the `pop` to
a local right after the `catch`, making the code valid again:
```wast
(try
(do)
(catch $tag
(local.set $new ;; New local to store `pop` result
(pop i32)
)
(block $__inlined_func$callee
(local.set $0
(local.get $new)
)
(nop)
)
)
)
```
|
|
|
|
|
|
|
| |
If that type is not valid then we cannot even create and finalize the node,
which means we'd hit an assertion inside finalize(), before we reach the
validator.
Fixes #4383
|
|
|
|
|
|
| |
Similar to what DeadArgumentElimination does for individual functions, this
can refine the results of a set of functions all using the same heap type, when
they all return something more specific. After this PR SignatureRefining can
refine both params and results and is basically complete.
|
|
|
|
|
|
| |
(#4339)
(i32(x) < 0) & (i32(y) < 0) ==> i32(x & y) < 0
(i64(x) < 0) & (i64(y) < 0) ==> i64(x & y) < 0
|
| |
|
| |
|
|
|
|
|
|
| |
Also, fix bug where pointer was being used direcltly to
index into Int32Array. I suppose this code had basically
zero users until I tried to land this change in emscripten:
https://github.com/emscripten-core/emscripten/pull/15742
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The general pattern is
if (!global) { global = 1 }
This PR generalizes that to handle nested appearances,
if ({
if (!global) { global = 1 }
!global
}) {
global = 1
}
With this I can finally see no more "once" global operations on the hottest
function in the currently slowest j2wasm benchmark ("filter").
Also added a failing testcase for something we do not handle yet.
|
|
|
|
| |
We don't use those effects now in any way, and if we need them some day
we can add them back. For now they just add overhead and complexity.
|
|
|
|
|
|
|
|
|
| |
When a wasm exception is thrown and uncaught in the interpreter, it
caused the whole interpreter to crash, rather than gracefully reporting
it. This fixes the problem, and also compares whether an uncaught
exception happened when comparing the results before and after
optimizations in `--fuzz-exec`. To do that, when `--fuzz-exec` is given,
we now compare results even when the function does not have return
values. Logs for some existing test have changed because of this.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
We do some postprocessing after parsing `Try` to make sure `delegate`
only targets `try`s and not `block`s:
https://github.com/WebAssembly/binaryen/blob/9659f9b07c1196447edee68fe04c8d7dd2480652/src/wasm/wasm-binary.cpp#L6404-L6426
But in case the outer `try` has neither of `catch` nor `delegate`, the
previous code just return prematurely, skipping the postprocessing part,
resulting in a binary parsing error. This PR removes that early-exiting
code.
Some test outputs have changed because `try`s are assigned labels after
the early exit. But those labels can be removed by other optimization
passes when there is no inner `rethrow` or `delegate` that targets them.
(On a side note, the restriction that `delegate` cannot target a `block`
has been removed a few months ago in the spec, so if a `delegate`
targets a `block`, it means it is just rethrown from that block. But I
still think this is a convenient invariant to hold at least within the
binaryen IR. I'm planning to allow parsing of `delegate` targeting
`block`s later, but I will make them point to `try` when read in the
IR. At the moment the LLVM toolchain does not generate such code.)
|
|
|
|
| |
All EH tests in test/lit/passes currently have the suffix `-eh`, so I
think it's better be consistent for this one.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This adds support for try-delegate in `EffectAnalyzer`. Without this
support, the expresion below has been incorrectly classified as "cannot
throw", because the previous code considered everything inside
`try`-`catch_all` as "cannot throw". This is not the case when there is
a `delegate` that can bypass the `catch_all`.
```wasm
try $l0
try
try
throw $e
delegate $l0
catch_all
end
end
|
|
|
|
|
|
|
|
| |
(#4338)
(i32(x) < 0) | (i32(y) < 0) ==> i32(x | y) < 0
(i32(x) != 0) | (i32(y) != 0) ==> i32(x | y) != 0
Likewise for i64.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Previously this pass would see something like this and fail:
if (foo() + global) {
global = 1;
}
The call to foo() has side effects, so we did not optimize. However, in such
a case the side effects are safe: they happen anyhow, regardless of the global
that we are optimizing. That is, "global" is read only to be written, even though
other things also influence the decision to write it. But "global" is not used in a
way that is observable: we can remove it, and nothing will notice (except for
things getting smaller/faster).
In other words, this PR will let us optimize the above example, while it also
needs to avoid optimizing the dangerous cases, like this:
if (foo(global)) {
global = 1;
}
Here "global" flows into a place that notices its value and may use it aside
from deciding to write that global.
A common case where we want to optimize is combined ifs,
if (foo()) {
if (global) {
global = 1;
}
}
which the optimizer turns into
if (foo() & global) {
global = 1;
}
With this PR we can handle those things too.
This lets us optimize out some important globals in j2wasm like the initializer
boolean for the Math object, reducing some total 0.5% of code size.
|
|
|
| |
This adds handling of try in the Flatten pass.
|
|
|
|
|
|
|
|
|
|
|
|
| |
This removes the old hardcoded value numbering in that pass and makes
it use the new code that was split into helper code. The immediate benefit
of this is to make the code aware of identical constants: if two locals have
the same constant then they do not interfere. Future improvements to
numbering will also automatically help here.
This changes some constants in existing tests so that they keep testing
what they were testing before, and adds new tests for the new benefit here.
This implements a proposed TODO from #4314
|
|
|
|
|
|
| |
Its seems that with this emscripten change DCE is able to remove
the `assert` JS runtime function making this call to assert fail
with `ReferenceError: assert is not defined`.
|
|
|
|
| |
(#4356)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This adds `EHUtils::handleBlockNestedPops`, which can be called at the
end of passes that has a possibility to put `pop`s inside `block`s. This
method assumes there exists a `pop` in a first-descendant line, even
though it can be nested within a block. This allows a `pop` to be nested
within a `block` or a `try`, but not a `loop`, since that means the
`pop` can run multile times. In case of `if`, `pop` can exist only in
its condition; if a `pop` is in its true or false body, that's not in
the first-descendant line.
This can be useful when optimization passes create blocks to do
transformations. Wrapping expressions wiith a block does not change
semantics most of the time, but if pops happen to be inside a block
generated by those passes, they can result in invalid binaries.
To test this, this adds `passes/test_passes.cpp`, which is intended to
contain multiple test passes that test a single (or more) utility
functions separately. Without this kind of pass, it is hard to test
various cases in which nested `pop`s can be generated in existing
passes. This PR also adds `PassRegistry::registerTestPass`, which
registers a pass that's intended only for internal testing and does not
show up in `wasm-opt --help`.
Fixes #4237.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This is fairly short and simple after the recent refactorings. This basically
just finds all uses of each signature/function type, and then sees if it
receives more specific types as params. It then rewrites the types if so.
This just handles arguments so far, and not return types.
This differs from DeadArgumentElimination's refineArguments() in that
that pass modifies each function by itself, changing the type of the
function as needed. That is only valid if the type is not observable, that
is, if the function is called indirectly then DAE ignores it. This pass will
work on the types themselves, so it considers all functions sharing a
type as a whole, and when it upgrades that type it ends up affecting them
all.
This finds optimization opportunities on 4% of the total signature
types in j2wasm. Those lead to some benefits in later opts, but the
effect is not huge.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
As we work toward allowing nominal and structural types to coexist, any
difference in how they can be built or used will be an inconvenient footgun that
we will have to work around. In the spirit of reducing the differences between
the type systems, allow TypeBuilder to construct basic HeapTypes in nominal mode
just as it can in equirecursive mode.
Although this change is a net increase in code complexity for not much
benefit (wasm-opt never needs to build basic HeapTypes), it is also an
incremental step toward getting rid of separate type system modes, so I expect
it to simplify other PRs in the near future.
This change also uncovered a bug in how the type fuzzer generated subtypes of
basic HeapTypes. The generated subtypes did not necessarily have the intended
`Kind`, which caused failures in nominal subtype validation in the fuzzer.
|
|
|
|
|
|
|
|
| |
Fairly simple, this uses the existing infrastructure to find opportunities
to refine the type of a global variable. This a common pattern in j2wasm
for example, where a global begins as a null of $java.lang.Object (the
least specific type) but it is in practice always assigned an object of
some specific type.
|