summaryrefslogtreecommitdiff
path: root/src
Commit message (Collapse)AuthorAgeFilesLines
* [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`.
* SimplifyGlobals: If all writes write the initial value, they are unneeded ↵Alon Zakai2021-11-231-10/+32
| | | | (#4356)
* Modernize code to C++17 (#3104)Max Graey2021-11-2282-534/+268
|
* Change from storing Signature to HeapType on CallIndirect (#4352)Thomas Lively2021-11-2230-107/+65
| | | | | | | | | | | | With nominal function types, this change makes it so that we preserve the identity of the function type used with call_indirect instructions rather than recreating a function heap type, which may or may not be the same as the originally parsed heap type, from the function signature during module writing. This will simplify the type system implementation by removing the need to store a "canonical" nominal heap type for each unique signature. We previously depended on those canonical types to avoid creating multiple duplicate function types during module writing, but now we aren't creating any new function types at all.
* Add missing include in numbering.h (#4354)Alon Zakai2021-11-221-0/+1
|
* Add fixup function for nested pops in catch (#4348)Heejin Ahn2021-11-2210-66/+246
| | | | | | | | | | | | | | | | | | | | | | | | | 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.
* Remove 'using namespace std' (NFC) (#4349)Heejin Ahn2021-11-228-19/+9
|
* Check for correct subtyping in the type fuzzer (#4350)Thomas Lively2021-11-205-91/+126
| | | | | Check that types that were meant to have a subtype relationship actually do. To expose the intended subtyping to the fuzzer, expose `subtypeIndices` in the return value of the type generation function.
* [NFC] Move value numbering code to a header (#4345)Alon Zakai2021-11-192-48/+100
| | | This just moves code out of RedundantSetElimination.
* [Wasm GC] Signature Refining pass (#4326)Alon Zakai2021-11-194-0/+213
| | | | | | | | | | | | | | | | | | | 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.
* Allow building basic HeapTypes in nominal mode (#4346)Thomas Lively2021-11-192-94/+157
| | | | | | | | | | | | | | | | 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.
* [Wasm GC] Global Refining pass (#4344)Alon Zakai2021-11-184-0/+144
| | | | | | | | 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.
* [Wasm GC] Update nulls to allow finding better LUBs (#4340)Alon Zakai2021-11-184-75/+142
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | It is common in GC code to have stuff like this: x = null; .. x = Data(); Nulls in wasm have a type, and if that initial null has say anyref then before this PR we would keep the type of x as anyref. However, while nulls have types, all null values are identical, and so we can in fact change x's type to a nullable reference of Data, by also changing the null's type to something more specific. LUBFinder now has an API that can return the best possible LUB so far, and that can be told to update nulls if we decide that the new LUB is worth using. This updates the passes using LUBFinder to use the new API. Note how TypeRefining becomes simpler because the special logic it had in a subclass of LUBFinder is now part of the main class (it used to remember if there was a null default; LUBFinder now handles both a null default as well as other nulls). This requires some changes to existing tests to avoid them from optimizing using nulls in ways that ends up not testing the original intent. Specifically the dae-gc-refine-params.wast now has calls to get a null of a type, instead of just having a ref.null of that type (which could be optimized now). And dae-gc-refine-return uses locals instead of ref.nulls.
* Small cleanups in type fuzzer (#4337)Thomas Lively2021-11-172-20/+14
| | | | | | | - Do not require defaultable types in function returns - Increase likelihood of `none` function return types - Correctly generate subtypes of basic types - Actually check output in tests - Print to cout instead of cerr
* [OptimizeInstructions] Combine some relational ops joined Or/And (Part 2) ↵Max Graey2021-11-161-5/+24
| | | | | | (#4336) (i32(x) != 0) | (i32(y) != 0) ==> i32(x | y) != 0 (i64(x) != 0) | (i64(y) != 0) ==> i64(x | y) != 0
* [OptimizeInstructions] Combine some relational ops joined Or/And (Part 1) ↵Max Graey2021-11-161-8/+34
| | | | | | (#4333) (i32(x) == 0) & (i32(y) == 0) ==> i32(x | y) == 0 (i64(x) == 0) & (i64(y) == 0) ==> i64(x | y) == 0
* Add a fuzzer specifically for types (#4328)Thomas Lively2021-11-1511-120/+875
| | | | | | | | | | | | | | | Add a new fuzzer binary that repeatedly generates random types to find bugs in the type system implementation. Each iteration creates some number of root types followed by some number of subtypes thereof. Each built type can contain arbitrary references to other built types, regardless of their order of construction. Right now the fuzzer only finds fatal errors in type building (and in its own implementation), but it is meant to be extended to check other properties in the future, such as that LUB calculations work as expected. The logic for creating types is also intended to be integrated into the main fuzzer in a follow-on PR so that the main fuzzer can fuzz with arbitrarily more interesting GC types.
* [NFC] HeapRefining => TypeRefining (#4332)Alon Zakai2021-11-164-10/+10
|
* [NFC] Rename GlobalSubtyping => HeapRefining (#4331)Alon Zakai2021-11-164-10/+10
|
* Add support for relaxed-simd instructions (#4320)Ng Zhi An2021-11-1512-33/+495
| | | | | | | | | | | | | | | | | | | | | This adds relaxed-simd instructions based on the current status of the proposal https://github.com/WebAssembly/relaxed-simd/blob/main/proposals/relaxed-simd/Overview.md. Binary opcodes are based on what is listed in https://github.com/WebAssembly/relaxed-simd/blob/main/proposals/relaxed-simd/Overview.md#binary-format. Text names are not fixed yet, and some sort sort of names that maps to the non-relaxed versions are chosen for this prototype. Support for these instructions have been added to LLVM via builtins, adding support here will allow Emscripten to successfully compile files that use those builtins. Interpreter support has also been added, and they delegate to the non-relaxed versions of the instructions. Most instructions are implemented in the interpreter the same way as the non-relaxed simd128 instructions, except for fma/fms, which is always fused.