summaryrefslogtreecommitdiff
path: root/src
Commit message (Collapse)AuthorAgeFilesLines
* Fixup block-nested pops even when EH is not enabled (#7130)Thomas Lively2024-12-033-5/+15
| | | | | | | | | | While parsing a binary file, there may be pops that need to be fixed up even if EH is not (yet) enabled because the target features section has not been parsed yet. Previously `EHUtils::handleBlockNestedPops` did not do anything if EH was not enabled, so the binary parser would fail to fix up pops in that case. Add an optional parameter to override this behavior so the parser can fix up pops unconditionally. Fixes #7127.
* Do not sink blocks into ifs with unreachable conditions (#7129)Thomas Lively2024-12-021-0/+6
| | | | | | | | | | RemoveUnusedBrs sinks blocks into If arms when those arms contain branches to the blocks and the other arm and condition do not. Now that we type Ifs with unreachable conditions as unreachable, it is possible for the If arms to have a different type than the block that would be sunk, so sinking the block would produce invalid IR. Fix the problem by never sinking blocks into Ifs with unreachable conditions. Fixes #7128.
* [GC] Fix trapping on array.new_data of dropped segments of offset > 0 (#7124)Alon Zakai2024-12-021-3/+12
| | | | Even if the size is 0, if the offset is > 0 then we should trap.
* [NFC] Rename {F32,F64}NearestInt to {F32,F64}Nearest (#7089)Thomas Lively2024-11-273-9/+5
| | | | | | Rename the opcode values in wasm-binary.h to better match the names of the corresponding instructions. This also makes these names match the scheme used by the rest of the basic unary operations, allowing for more macro use in the binary reader.
* Use IRBuilder in the binary parser (#6963)Thomas Lively2024-11-264-4419/+1487
| | | | | | | | | | | | | | | | | | | | | | IRBuilder is a utility for turning arbitrary valid streams of Wasm instructions into valid Binaryen IR. It is already used in the text parser, so now use it in the binary parser as well. Since the IRBuilder API for building each intruction requires only the information that the binary and text formats include as immediates to that instruction, the parser is now much simpler than before. In particular, it does not need to manage a stack of instructions to figure out what the children of each expression should be; IRBuilder handles this instead. There are some differences between the IR constructed by IRBuilder and the IR the binary parser constructed before this change. Most importantly, IRBuilder generates better multivalue code because it avoids eagerly breaking up multivalue results into individual components that might need to be immediately reassembled into a tuple. It also parses try-delegate more correctly, allowing the delegate to target arbitrary labels, not just other `try`s. There are also a couple superficial differences in the generated label and scratch local names. As part of this change, add support for recording binary source locations in IRBuilder.
* Make more Ifs unreachable (#7094)Thomas Lively2024-11-276-41/+48
| | | | | | | | | | | | | | | | | | | Previously the only Ifs that were typed unreachable were those in which both arms were unreachable and those in which the condition was unreachable that would have otherwise been typed none. This caused problems in IRBuilder because Ifs with unreachable conditions and value-returning arms would have concrete types, effectively hiding the unreachable condition from the logic for dropping concretely typed expressions preceding an unreachable expression when finishing a scope. Relax the conditions under which an If can be typed unreachable so that all Ifs with unreachable conditions or two unreachable arms are typed unreachable. Propagating unreachability more eagerly this way makes various optimizations of Ifs more powerful. It also requires new handling for unreachable Ifs with concretely typed arms in the Printer to ensure that printed wat remains valid. Also update Unsubtyping, Flatten, and CodeFolding to account for the newly unreachable Ifs.
* Handle concrete values in CodeFolding (#7117)Thomas Lively2024-11-261-123/+111
| | | | | | | | | | | | | | | | CodeFolding previously only worked on blocks that did not produce values. It worked on Ifs that produced values, but only by accident; the logic for folding matching tails was not written to support tails producing concrete values, but it happened to work for Ifs because subsequent ReFinalize runs fixed all the incorrect types it produced. Improve the power of the optimization by explicitly handling tails that produce concrete values for both blocks and ifs. Now that the core logic handles concrete values correctly, remove the unnecessary ReFinalize run. Also remove the separate optimization of Ifs with identical arms; this optimization requires ReFinalize and is already performed by OptimizeInstructions.
* ReFinalize after merging siblings in TypeMerging (#7121)Thomas Lively2024-11-261-0/+11
| | | | | | | | The LUB of sibling types is their common supertype, but after the sibling types are merged, their LUB is the merged type, which is a strict subtype of the previous LUB. This means that merging sibling types causes `selects` to have stale types when the two select arms previously had the two merged sibling types. To fix any potential stale types, ReFinalize after merging sibling types.
* [wasm2js] Run LLVM nontrapping fptoint lowering when running for emscripten ↵Derek Schuff2024-11-261-0/+3
| | | | | | (#7116) Lower away saturating fptoint operations when we know we are using emscripten.
* Fix ArenaVector::swap (#7115)Alon Zakai2024-11-261-6/+3
| | | | | This was never right for over a decade, and just never used I suppose... it should have been called "take" since it grabbed data from the other item and then set that other item to empty. Fix it so it swaps properly.
* Fix memory.grow bounds and overflow checks for mem64 (#7112)Thomas Lively2024-11-251-2/+7
| | | | | Previously the interpreter only executed overflow and bounds checks for memory.grow on 32-bit memories. Run the checks on 64-bit memories as well.
* Handle unoptimized branches in CodeFolding (#7111)Thomas Lively2024-11-251-8/+14
| | | | | | | | | | | | | | | CodeFolding previously did not consider br_on_* instructions at all, so it would happily merge tails even if there were br_on_* branches to the same label with non-matching tails. Fix the bug by making any label targeted by any instruction not explicitly handled by CodeFolding unoptimizable. This will gracefully handle other branching instructions like `resume` and `resume_throw` as well. Folding these branches properly is left as future work. Also rename the test file from code-folding_enable-threads.wast to just code-folding.wast and enable all features instead of just threads. The old name was left over from when the test was originally ported to lit, and the new feature is necessary because the new test uses GC instructions.
* [GC] Refinalize after selectify in RemoveUnusedBrs (#7104)Alon Zakai2024-11-251-2/+12
| | | | Replacing an if with a select may have refined the type. Without this fix, the sharper stale type checks complain.
* Remove AutoDrop (#7106)Thomas Lively2024-11-226-105/+1
| | | | The only internal use was in wasm2js, which doesn't need it. Fix API tests to explicitly drop expressions as necessary.
* Print castType for unreachable br_on_cast{_fail} (#7107)Thomas Lively2024-11-221-6/+4
| | | | | | | | I forgot that there is a validation rule that the output type for br_on_cast and br_on_cast_fail must be a subtype of the input type. We were previously printing bottom input types in cases where the cast operand was unreachable, but that's only valid if the cast type is the same bottom type. Instead print the most precise valid input type, which is the cast type itself.
* Print unreachable loads with valid types (#7108)Thomas Lively2024-11-221-1/+9
| | | | | | | | | Since Load expressions use their `type` field to encode the type of the loaded value, unreachable loads need to come up with some other valid type to print. Previously we always chose i32 as that type, but that's not valid when the load was originally a v128 load with an alignment of 8, since 8 is greater than the maximum valid alignment of 4 for an i32. Fix the problem by taking alignment into account when choosing a type for the unreachable load.
* Propagate public visibility through all types (#7105)Thomas Lively2024-11-211-11/+8
| | | | | | | | | | | | | | Previously the classification of public types propagated public visibility only through types that had previously been collected by `collectHeapTypes`. Since there are settings that cause `collectHeapTypes` to collect fewer types, it was possible for public types to be missed if they were only public because they were reached by an uncollected types. Ensure that all public heap types are properly classified by propagating public visibility even through types that are not part of the collected output. Fixes #7103.
* Fix printing of unreachable br_on_cast{_fail} (#7102)Thomas Lively2024-11-211-2/+15
| | | | | | | | | | | | | br_on_cast and br_on_cast_fail have two type annotations: one for their input type and one for their cast type. In cases where their operands were unreachable, we were previously printing "unreachable" for the input type annotation. This is not valid wat because "unreachable" is not a reference type. To fix the problem, print the bottom type of the cast type's hierarchy as the input type for br_on_cast and br_on_cast_fail when the operand is unreachable. This ensures that the instructions have the most precise possible output type according to Wasm typing rules, so it maximizes the number of contexts in which the printed instructions are valid.
* Make validation of stale types stricter (#7097)Thomas Lively2024-11-2115-63/+36
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | We previously allowed valid expressions to have stale types as long as those stale types were supertypes of the most precise possible types for the expressions. Allowing stale types like this could mask bugs where we failed to propagate precise type information, though. Make validation stricter by requiring all expressions except for control flow structures to have the most precise possible types. Control flow structures are exempt because many passes that can refine types wrap the refined expressions in blocks with the old type to avoid the need for refinalization. This pattern would be broken and we would need to refinalize more frequently without this exception for control flow structures. Now that all non-control flow expressions must have precise types, remove functionality relating to building select instructions with non-precise types. Since finalization of selects now always calculates a LUB rather than using a provided type, remove the type parameter from BinaryenSelect in the C and JS APIs. Now that stale types are no longer valid, fix a bug in TypeSSA where it failed to refinalize module-level code. This bug previously would not have caused problems on its own, but the stale types could cause problems for later runs of Unsubtyping. Now the stale types would cause TypeSSA output to fail validation. Also fix a bug where Builder::replaceWithIdenticalType was in fact replacing with refined types. Fixes #7087.
* [wasm2js] Properly handle loops without labels (#7100)Alon Zakai2024-11-211-2/+7
| | | | | | | | | | | When a loop has no name, the name does not matter, but we also cannot emit the same name for all such loops, as that is invalid JS. Just do not emit a while(){} at all in that case, as no continue can exist anyhow. Fixes #7099 Also fix two missing * in error reporting logic, that was printing pointers rather than the expression we wanted to print. I think we changed how iostream prints things years ago, and forgot to update these.
* Fuzzer: Legalize and prune the JS interface in pickPasses (#7092)Alon Zakai2024-11-201-0/+7
| | | | Also add a test that the ClusterFuzz run.py does not warn, which was helpful when debugging this.
* Improve fuzzing of both closed and open world styles of modules (#7090)Alon Zakai2024-11-196-21/+177
| | | | | | | | | | Before, we would simply not export a function that had an e.g. anyref param. As a result, the modules were effectively "closed", which was good for testing full closed-world mode, but not for testing degrees of open world. To improve that, this PR allows the fuzzer to export such functions, and an "enclose world" pass is added that "closes" the wasm (makes it more compatible with closed-world) that is run 50% of the time, giving us coverage of both styles.
* Add nontrapping-fptoint lowering pass (#7016)Derek Schuff2024-11-194-0/+186
| | | | | | | | | | | This pass lowers nontrapping FP to int instructions to implement LLVM's conversion behavior. This means that they are not fully complete lowerings according to the wasm spec, but have the same undefined behavior that LLM does. This keeps the pass simpler and preserves existing behavior when compiling without nontrapping-ft. This will be used in emscripten, so that we can build libraries with nontrapping-fp and lower them away after link if desired.
* Fuzzing: ClusterFuzz integration (#7079)Alon Zakai2024-11-192-14/+123
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The main addition here is a bundle_clusterfuzz.py script which will package up the exact files that should be uploaded to ClusterFuzz. It also documents the process and bundling and testing. You can do bundle.py OUTPUT_FILE.tgz That bundles wasm-opt from ./bin., which is enough for local testing. For actually uploading to ClusterFuzz, we need a portable build, and @dschuff had the idea to reuse the emsdk build, which works nicely. Doing bundle.py OUTPUT_FILE.tgz --build-dir=/path/to/emsdk/upstream/ will bundle wasm-opt (+libs) from the emsdk. I verified that those builds work on ClusterFuzz. I added several forms of testing here. First, our main fuzzer fuzz_opt.py now has a ClusterFuzz testcase handler, which simulates a ClusterFuzz environment. Second, there are smoke tests that run in the unit test suite, and can also be run separately: python -m unittest test/unit/test_cluster_fuzz.py Those unit tests can also run on a given bundle, e.g. one created from an emsdk build, for testing right before upload: BINARYEN_CLUSTER_FUZZ_BUNDLE=/path/to/bundle.tgz python -m unittest test/unit/test_cluster_fuzz.py A third piece of testing is to add a --fuzz-passes test. That is a mode for -ttf (translate random data into a valid wasm fuzz testcase) that uses random data to pick and run a set of passes, to further shape the wasm. (--fuzz-passes had no previous testing, and this PR fixes it and tidies it up a little, adding some newer passes too). Otherwise this PR includes the key run.py script that is bundled and then executed by ClusterFuzz, basically a python script that runs wasm-opt -ttf [..] to generate testcases, sets up their JS, and emits them. fuzz_shell.js, which is the JS to execute testcases, will now check if it is provided binary data of a wasm file. If so, it does not read a wasm file from argv[1]. (This is needed because ClusterFuzz expects a single file for the testcase, so we make a JS file with bundled wasm inside it.)
* Use hints when generating fresh labels in IRBuilder (#7086)Thomas Lively2024-11-182-4/+9
| | | | | | | | | | | | IRBuilder often has to generate new label names for blocks and other scopes. Previously it would generate each new name by starting with "block" or "label" and incrementing a suffix until finding a fresh name, but this made name generation quadratic in the number of names to generate. To spend less time generating names, track a hint index at which to start looking for a fresh name and increment it every time a name is generated. This speeds up a version of the binary parser that uses IRBuilder by about 15%.
* [NFC] Finalize blocks with explicit breakability in IRBuilder (#7085)Thomas Lively2024-11-182-4/+16
| | | | | | Since IRBuilder already knows what labels are used by branches, it is easy for it to pass that information when finalizing blocks. This avoids finalization having to walk the blocks looking for branches, speeding up a future version of the binary parser that uses IRBuilder by 10%.
* Rename memory-copy-fill-lowering pass (#7082)Derek Schuff2024-11-164-8/+8
| | | | Since the resulting code has the same undefined behavior as LLVM, make the pass name reflect that.
* [NFC] Remove redundant [[nodiscard]] attributes (#7084)Thomas Lively2024-11-152-150/+140
| | | | | | Now that Result and MaybeResult are annotated [[nodiscard]] at the type level, individual functions and methods that return these types do not need to be annotated [[nodiscard]] themselves. Remove the newly redundant annotations.
* Mark Result and MaybeResult [[nodiscard]] (#7083)Thomas Lively2024-11-152-5/+5
| | | | | | Since these types may be carrying errors that need to be handled or propagated, it is always an error not to use them in some way. Adding the [[nodiscard]] attribute caused the compiler to find a few instances where we were incorrectly ignoring results. Fix these places.
* Reset function context when ending a function in IRBuilder (#7081)Thomas Lively2024-11-154-10/+20
| | | | | | | | | | | | | | | | | | | IRBuilder contains a pointer to the current function that is used to create scratch locals, look up the operand types for returns, etc. This pointer is nullable because IRBuilder can also be used in non-function contexts such as global initializers. Visiting the start of a function sets the function pointer, and after this change visiting the end of a function resets the pointer to null. This avoids potential problems where code outside a function would be able to incorrectly use scratch locals and returns if the IRBuilder had previously been used to build a function. This change requires some adjustments to Outlining, which visits code out of order, so ends up visiting code from inside a function after visiting the end of the function. To support this use case, add a `setFunction` method to IRBuilder that lets the user explicitly control its function context. Also remove the optional function pointer parameter to the IRBuilder constructor since it is less flexible and not used.
* Use empty blocks instead of nops for empty scopes in IRBuilder (#7080)Thomas Lively2024-11-142-3/+5
| | | | | | | | | | When IRBuilder builds an empty non-block scope such as a function body, an if arm, a try block, etc, it needs to produce some expression to represent the empty contents. Previously it produced a nop, but change it to produce an empty block instead. The binary writer and printer have special logic to elide empty blocks, so this produces smaller output. Update J2CLOpts to recognize functions containing empty blocks as trivial to avoid regressing one of its tests.
* Record binary locations for nested blocks (#7078)Thomas Lively2024-11-141-0/+20
| | | | | | | | | | | The binary reader has special handling for blocks immediately nested inside other blocks to eliminate recursion while parsing very deep stacks of blocks. This special handling did not record binary locations for the nested blocks, though. Add logic to record binary locations for nested blocks. This binary reading code is about to be replaced with completely different code that uses IRBuilder instead, but this change will eliminate some test differences that we would otherwise see when we make that change.
* [NFC] Eagerly set local names in binary reader (#7076)Thomas Lively2024-11-142-19/+26
| | | | | | | | | | | | | | | | Instead of setting the local names at the end of binary reading, eagerly set them before parsing function bodies. This is NFC now, but will fix a future bug once the binary reader uses IRBuilder. IRBuilder can introduce new scratch locals, and it gives them the names `$scratch`, `$scratch_1`, etc. If the name section includes locals with the same names and we set those local names after parsing function bodies, then we can end up with multiple locals with the same names. Setting the names before parsing the function bodies ensures that IRBuilder will generate different names for the scratch locals. The alternative fix would be to generate fresh names when setting names from the name section, but it is better to respect the names in the name section and use fresh names for the newly introduced scratch locals instead.
* [SignExt] OptimizeInstructions: Remove signexts of already-extended values ↵Alon Zakai2024-11-131-15/+37
| | | | (#7072)
* Fixup pops when necessary in IRBuilder (#7075)Thomas Lively2024-11-132-3/+38
| | | | | | | | | | | | | IRBuilder introduces scratch locals to hoist values from underneath stacky code to the top of the stack for consumption by the next instruction. When it does so, the sequence of instructions from the set to the get of the scratch local is packaged in a block so the entire sequence can be made a child of the next instruction. In cases where the hoisted value comes from a `pop`, this packaging can make the IR invalid, since `pop`s are not allowed to appear inside blocks. Detect when this problem might occur and fix it by running `EHUtils::handleBlockNestedPops` after the function containing the problem has been constructed.
* Read the names section first (#7074)Thomas Lively2024-11-132-355/+342
| | | | | | | | | Rather than back-patching names when we get to the names section in the binary reader, skip ahead to read the names section before anything else so we can use the final names right away. This is a prerequisite for using IRBuilder in the binary reader. The only functional change is that we now allow empty local names. Empty names are perfectly valid.
* Consolidate printing of function signatures (#7073)Thomas Lively2024-11-122-57/+44
| | | | | | | | | | | There were previously two separate code paths for printing function signatures, one for imported functions and one for declared functions. The only intended difference was that parameter names were printed for declared functions but not for imported functions. Reduce duplication by consolidating the code paths, and add support for printing names for imported function parameters that have them. Also fix a bug where empty names were printed as `$` rather than the correct `$""`.
* Introduce pass to lower memory.copy and memory.fill (#7021)Derek Schuff2024-11-134-0/+266
| | | | | | | | This pass lowers away memory.copy and memory.fill operations. It generates a function that implements the each of the instructions and replaces the instructions with calls to those functions. It does not handle other bulk memory operations (e.g. passive segments and table operations) because they are not used by emscripten to enable targeting old browsers that don't support bulk memory.
* HeapStoreOptimization: Fix a bug with jumping from the later value (v2) (#7070)Alon Zakai2024-11-121-3/+76
| | | | | | | | | | | | | | | | | | | | | | | | | | This PR fixes this situation: (block $out (local.set $x (struct.new X Y Z)) (struct.set $X 0 (local.get $x) (..br $out..)) ;; X' here has a br ) (local.get $x) => (block $out (local.set $x (struct.new (..br $out..) Y Z)) ) (local.get $x) We want to fold the struct.set into the struct.new, but the br is a problem: if it executes then we skip the struct.set, and the last local.get in fact reads the struct before the write. And, if we did this optimization, we'd end up with the br on the struct.new, so it would skip that instruction and even the local.set. To fix this, we use the new API from #7039, which lets us query, "is it ok to move the local.set to where the struct.set is?"
* [wasm64] Fuzzer: Fix type of unimported offsets (#7071)Alon Zakai2024-11-111-2/+2
| | | | | | When the fuzzer sees an imported segment, it makes it non-imported (because imported ones would trap when we tried to run them: we don't have the normal runtime they expect). We had hardcoded i32 offets there, which need to be generalized.
* Fix PickLoadSigns on SignExt feature instructions (#7069)Alon Zakai2024-11-111-17/+22
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | I believe the history here is that 1. We added a PickLoadSigns pass. It checks if a load from memory is stored in a local that is only every used in a signed or an unsigned manner. If it is, we can adjust the sign of the load (load8_u/s) to do the sign/unsign during the load. 2. The pass finds each LocalGet and looks either 2 or 3 parents above it. For a sign operation, we need to look up 3, since the operation is x << K >> K. For an unsigned, we need only 2, since we have x & M. We hardcoded those numbers 2 and 3. 3. We added the SignExt feature, which adds i32.extend8_s. This does a sign extend with a single instruction, not two nested ones, so now we can sign- extend at depth 2, unlike before. Properties::getSignExtValue was updated for this, but not the pass PickLoadSigns. The bug that is fixed here is that we looked at depth 3 for a sign-extend, and we blindly accepted it if we found one. So we ended up accepting (i32.extend8_s (ANYTHING (x))), which is a sign-extend of something, but not of x, which is bad. We were also missing an optimization opportunity, as we didn't look for depth 2 sign extends. This bug is quite old, from when Properties got SignExt support, in #3910. But the blame isn't there - to notice this then, we'd have had to check each caller of getSignExtValue throughout the codebase, which isn't reasonable. The fault is mine, from the first write-up of PickLoadSigns in 2017: the code should have been fully general, handling 2/3 and checking the output when it does so (adding == curr, that the sign/zero-extended value is the one we expect). That is what this PR does.
* LocalGraph::canMoveSet (#7039)Alon Zakai2024-11-112-41/+203
| | | | | This new API lets us ask if a set can be safely moved to a new position. The new position must be the location of an expression from a particular class (this allows us to populate the IR once and then query any of those locations).
* [EH] Fuzz calls from JS by calling wasm exports, sometimes catching (#7067)Alon Zakai2024-11-083-5/+136
| | | | | | | | | | | | | | | | This adds two new imports to fuzzer modules: * call-export, which gets an export index and calls it. * call-export-catch, which does the call in a try-catch, swallowing any error, and returning 1 if it saw an error. The former gives us calls back into the wasm, possibly making various trips between wasm and JS in interesting ways. The latter adds a try-catch which helps fuzz wasm EH. We do these calls using a wasm export index, i.e., the index in the list of exports. This is simple, but it does have the downside that it makes executing the wasm sensitive to changes in exports (e.g. wasm-merge adds more), which requires some handling in the fuzzer.
* [wasm64] Fix 32-bit address computation in execution of SIMDLoadExtend (#7068)Alon Zakai2024-11-081-3/+6
|
* Rename indexType -> addressType. NFC (#7060)Sam Clegg2024-11-0729-214/+223
| | | See https://github.com/WebAssembly/memory64/pull/92
* [wasm64] Fix copying of 64-bit tables, and fuzz them (#7065)Alon Zakai2024-11-072-2/+21
| | | | `ModuleUtils::copyTable` was not copying the `indexType` property.
* [wasm64] Fuzz wasm64 memories (#7064)Alon Zakai2024-11-072-8/+27
| | | | | | | * Remove the code that prevented fuzzing wasm64 test files. * Ignore a run that hits the V8 implementation limit on memory size. * Disable wasm64 fuzzing in wasm2js (like almost all post-MVP features). * Add fuzzer logic to emit a 64-bit memory sometimes. * Fix various places in the fuzzer that assumed 32-bit indexes
* [wasm64] Fix Directize on indexes > 32 bits (#7063)Alon Zakai2024-11-071-1/+1
|
* [wasm64] Make interpreter table methods operate on Address, not Index (#7062)Alon Zakai2024-11-074-29/+23
| | | This allows 64-bit bounds checking to work properly.
* [wasm64] Fix wasm-ctor-eval + utils on 64-bit indexes for memory64 (#7059)Alon Zakai2024-11-062-5/+12
| | | | Some places assumed a 32-bit index.