summaryrefslogtreecommitdiff
path: root/src
Commit message (Collapse)AuthorAgeFilesLines
* [ctor-eval] Partial evaluation (#4438)Alon Zakai2022-01-112-26/+184
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* SafeHeap: Avoid instrumenting functions directly called from the "start" (#4439)Sam Clegg2022-01-101-20/+36
|
* Escape \t as well as \n when writing JSON output. (#4437)Sam Clegg2022-01-101-0/+5
| | | | | | | | 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.
* Fix emscripten build by removing dummy atexit function (#4435)Sam Clegg2022-01-091-4/+0
| | | | | | | | | | | | | | | | | | | | | | | Since https://github.com/emscripten-core/emscripten/pull/15905 landed emscripten now includes its own dummy atexit function when building with EXIT_RUNTIME=0. This dummy function conflicts with the emscripten-provided one: ``` wasm-ld: error: duplicate symbol: atexit >>> defined in CMakeFiles/binaryen_wasm.dir/src/binaryen-c.cpp.o >>> defined in ...wasm32-emscripten/lto/libnoexit.a(atexit_dummy.o) ``` Normally overriding symbols from libc does not causes issues but one needs to be sure to override all the symbols in a given object file so that the object in question (atexit_dummy.o) does not get linked in. In this case some other symbol being defined in in atexit_dummy.o (e.g. __cxa_atexit) is likely the cause of the conflict. Overriding symbols from libc is likely to break in this way as the libc evolves, and since emscripten is now providing a dummy, just as we want, its better/safer to simply remove our dummy.
* [ctor-eval] Switch logging from stderr to stdout (#4432)Alon Zakai2022-01-071-7/+7
| | | | | This logging is central to what this tool does, and not optional, so stdout makes more sense I think. Also, as I'm re-integrating this on the emscripten side, this makes it simpler.
* Warn about and ignore empty local/param names in name section (#4426)Alon Zakai2022-01-071-3/+17
| | | | | | | 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.
* [ctor-eval] Eval and store changes to globals (#4430)Alon Zakai2022-01-071-16/+11
| | | | | | | | | | 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).
* [ctor-eval] Add --ignore-external-input option (#4428)Alon Zakai2022-01-062-11/+73
| | | | | | | | | | | | 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.
* [ctor-eval] Refactor an applyToModule() method instead of hacks [NFC] (#4425)Alon Zakai2022-01-061-19/+38
| | | | | | | Previously this would hackishly apply all execution changes to the memory all the time, and then "undo" it by saving the state before and copying that in. Instead, this PR makes execution write into a side buffer, and now there is a clear method for when we want to actually apply the results to the module.
* [C API] Fix BinaryenTypeCreate argument numTypes type (#4417)chai20102022-01-062-3/+3
| | | All other numXxxs argument use BinaryenIndex type.
* [ctor-eval] Remove stack hacks (#4429)Alon Zakai2022-01-061-55/+2
| | | | | | | | | 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.
* Add categories to --help text (#4421)Alon Zakai2022-01-0516-13/+216
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* Turn an assertion on not colliding with an internal name into an error (#4422)Alon Zakai2022-01-052-3/+6
| | | | | | Without this, the result in a build without assertions might be quite confusing. See #4410 Also make the internal names more obviously internal names.
* Add binary format parse check for imported function types (#4423)Alon Zakai2022-01-051-1/+7
| | | | | Without this we hit an assertion later, which is less clear. See #4413
* [EH] Fixup nested pops after reading stacky binary (#4420)Heejin Ahn2022-01-043-45/+55
| | | | | | 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.
* [EH] Enable fuzzer with initial contents (#4409)Heejin Ahn2022-01-045-6/+16
| | | | | | | | | 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.
* Factor out a utility for replacing types during canonicalization (#4416)Thomas Lively2022-01-041-253/+219
| | | | | | | | | | | | | Equirecursive canonicalization generates new minimal type definitions for each distinct type, but then it must replace the old, non-minimial definitions with the new ones. That second part where the types are replaced is not unique to equirecursive canonicalization; the same replacement happens with BasicKind types and in the future the hybrid isorecursive system will also need to do that kind of replacement. To improve code reuse and separation of concerns, factor the type replacement logic out into a separate utility. This change slightly slows down shape canonicalization, but shape canonicalization is still much faster than global canonicalization. Nominal typing performance is not affected.
* Remove python from CMake build (#4324)Blaine Bublitz2022-01-042-6/+20
| | | Use CMake's configure_file() instead.
* Compare traps in ExecutionResults (#4405)Heejin Ahn2021-12-292-23/+39
| | | | | | | | 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.
* [EH][GC] Fix nested pop after removing ref.cast (#4407)Heejin Ahn2021-12-281-0/+4
| | | | | | | | | | | | | | | | `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.
* [Fuzzer] Allow empty data in --translate-to-fuzz (#4406)Heejin Ahn2021-12-281-2/+2
| | | | | | | 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.
* [EH] Support try-delegate in interpreter (#4408)Heejin Ahn2021-12-281-0/+16
|
* Remove tableSize from emscripten metadata (#4415)Sam Clegg2021-12-281-6/+0
| | | See https://github.com/emscripten-core/emscripten/pull/15855
* [EH] Handle nested pops after inlining (#4404)Heejin Ahn2021-12-201-0/+5
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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) ) ) ) ```
* Add binary format parse checking for ref.as input type (#4389)Alon Zakai2021-12-161-0/+3
| | | | | | | 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
* Validate LUBs in the type fuzzer (#4396)Thomas Lively2021-12-153-82/+150
| | | | | Update the LUB calculation code to use std::optional rather than out params and validate LUBs in the fuzzer to ensure that the change is NFC as intended. Also add HeapType::getLeastUpperBound to the public API as a convenience.
* [NFC] Reuse `globallyCanonicalize` for nominal types (#4395)Thomas Lively2021-12-141-36/+17
| | | | | | | | | | Hashing and comparison of nominal HeapTypeInfos previously observed their child Types, so the Types had to be canonicalized before the HeapTypes. Unfortunately equirecursive canonicalization requires that the HeapTypes be canonicalized before the Types, so this was a point of divergence between the two systems. However, #4394 updated hashing and comparison of nominal types to not depend on child Types, so now we can harmonize the two systems by having them use the same `globallyCanonicalize` function to canonicalize their HeapTypes followed by their Types.
* [Wasm GC] Refine results in SignatureRefining (#4380)Alon Zakai2021-12-141-17/+70
| | | | | | 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.
* [NFC] Simplify HeapTypeInfo hashing and comparison (#4394)Thomas Lively2021-12-141-47/+4
| | | | | | | Now that caching of "canonical" nominal signatures is handled at a separate layer, we can remove the separate code paths for hashing and comparing HeapTypeInfos based on their structure even in nominal mode. Now hashing and comparing of HeapTypeInfos is uniformly handled by FiniteShapeHasher and FiniteShapeEquator.
* [OptimizeInstructions] Combine some relational ops joined Or/And (Part 4) ↵Max Graey2021-12-141-4/+53
| | | | | | (#4339) (i32(x) < 0) & (i32(y) < 0) ==> i32(x & y) < 0 (i64(x) < 0) & (i64(y) < 0) ==> i64(x & y) < 0
* Add requireFunctionContext in necessary places (#4388)Alon Zakai2021-12-141-0/+4
| | | Fixes #4384
* Allow fractional timeouts in wasm2js Atomics.wait. Followup to #4385 (#4387)Alon Zakai2021-12-141-1/+1
|
* Fix a DeadArgumentElimination determinism bug (#4386)Alon Zakai2021-12-131-1/+1
| | | | We iterate on that data structure in two loops, and the fuzzer found a case where the difference in ordering actually ended up mattering in the output.
* [Precompute][SIMD] Enable constant folding for simd (#4381)Max Graey2021-12-131-9/+0
|
* Implement timeout argument in wasm2js_atomic_wait_i32 (#4385)Sam Clegg2021-12-111-2/+7
| | | | | | 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
* [NFC] Refactor result type LUB computation into a helper function (#4379)Alon Zakai2021-12-094-82/+123
|
* SimplifyGlobals: Handle nested read-only-to-write patterns (#4365)Alon Zakai2021-12-081-5/+21
| | | | | | | | | | | | | | | | | | | 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.
* [NFC] Add a separate cache for nominal signature types (#4375)Thomas Lively2021-12-081-18/+49
| | | | | | | We have always cached nominal signature types keyed on their signatures to avoid creating extra nominal types through the `HeapType::HeapType(Signature)` constructor. However, that logic was previously built into the HeapTypeInfo canonicalization system. To allow that system to be simplified in future PRs, separate the caching into its own explicit layer.
* Do not track effects of immutable things (#4376)Alon Zakai2021-12-081-18/+2
| | | | 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.
* [NFC] Deduplicate Store insertion logic (#4374)Thomas Lively2021-12-071-50/+51
| | | | | | | Types and HeapTypes are inserted into their respective stores either by copying a reference to a `TypeInfo` or `HeapTypeInfo` or by moving a `std::unique_ptr<TypeInfo>` or `std::unique_ptr<HeapTypeInfo>`. Previously these two code paths had separate, similar logic. To reduce deduplication, combine both code paths into a single method.
* [NFC] Use std::optional for `getCanonical` in wasm-type.cpp (#4373)Thomas Lively2021-12-071-42/+37
|
* [EH] Make interpreter handle uncaught exceptions (#4369)Heejin Ahn2021-12-061-24/+28
| | | | | | | | | 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.
* [EH] Fix binary parsing for catchless try + inner delegate (#4370)Heejin Ahn2021-12-061-7/+0
| | | | | | | | | | | | | | | | | | | | | | 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.)
* [EH] Support try-delegate in EffectAnalyzer (#4368)Heejin Ahn2021-12-066-19/+43
| | | | | | | | | | | | | | | | 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
* [OptimizeInstructions] Combine some relational ops joined Or/And (Part 3) ↵Max Graey2021-12-041-9/+47
| | | | | | | | (#4338) (i32(x) < 0) | (i32(y) < 0) ==> i32(x | y) < 0 (i32(x) != 0) | (i32(y) != 0) ==> i32(x | y) != 0 Likewise for i64.
* SimplifyGlobals: Ignore irrelevant effects in read-only-to-write (#4363)Alon Zakai2021-12-021-41/+105
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* [NFC] Avoid some unnecessary copies of PassOptions (#4361)Alon Zakai2021-12-012-10/+10
| | | | | | PassOptions is a fairly large structure and even includes a std::map. I also have plans to add further fields there to make it even larger. Before doing that I noticed that in some places we copy it instead of being consistent and taking it by reference, which this PR fixes.
* Handle try in Flatten pass (#2567)Heejin Ahn2021-11-292-0/+40
| | | This adds handling of try in the Flatten pass.
* CoalesceLocals: Use ValueNumbering (#4355)Alon Zakai2021-11-241-14/+20
| | | | | | | | | | | | 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
* wasm2js: Don't assume the existence of js assert function (#4357)Sam Clegg2021-11-241-1/+1
| | | | | | 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`.