| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
| |
SafeHeap was emitting them, but it looks like they are invalid according to the wasm-threads spec.
Fixes kripken/emscripten#7208
|
| |
|
|
|
| |
While debugging to fix the waterfall regressions I noticed that wasm-reduce regressed. We need to be more careful with visitFunction which now may visit an imported function - I found a few not-well-tested passes that also regressed that way.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Fixes #1649
This moves us to a single object for functions, which can be imported or nor, and likewise for globals (as a result, GetGlobals do not need to check if the global is imported or not, etc.). All imported things now inherit from Importable, which has the module and base of the import, and if they are set then it is an import.
For convenient iteration, there are a few helpers like
ModuleUtils::iterDefinedGlobals(wasm, [&](Global* global) {
.. use global ..
});
as often iteration only cares about imported or defined (non-imported) things.
|
|
|
|
|
|
|
| |
The current patch:
* Preserves the debug locations from function prolog and epilog
* Preserves the debug locations of the nested blocks
|
|
|
|
| |
(#1681)
|
|
|
|
| |
* call_import is not lightweight for inlining purposes - we had call but not callimport, which is even less light
|
|
|
|
|
|
|
|
|
|
|
|
| |
* show a proper error for an empty asm2wasm input
* handle end of input in processExpressions in binary reading
* memory segment sizes should be unsigned
* validate input in wasm-ctor-eval
* update tests
|
| |
|
|
|
|
|
|
| |
* Error if there are more locals than browsers allow (50,000). We usually just warn about stuff like this, but we do need some limit (or else we hang or OOM), and if so, why not use the agreed-upon Web limit.
* Do not generate nice string names for locals in binary parsing - the name is just $var$x instead of $x, so not much benefit, and worse as our names are interned this is actually slow (which is why the fuzz testcase here hangs instead of OOMing).
Testcases and bugreport in #1663.
|
|
|
| |
The static std::string base64Encode(std::vector<char> &data) { uses signed char in input data. The ((int)data[0]) converts it the signed int, making '\xFF' char into -1. The patch fixes casting.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This adds a pass to remove unnecessary call arguments in an LTO-like manner, that is:
* If a parameter is not actually used in a function, we don't need to send anything, and can remove it from the function's declaration. Concretely,
(func $a (param $x i32)
..no uses of $x..
)
(func $b
(call $a (..))
)
=>
(func $a
..no uses of $x..
)
(func $b
(call $a)
)
And
* If a parameter is only ever sent the same constant value, we can just set that constant value in the function (which then means that the values sent from the outside are no longer used, as in the previous point). Concretely,
(func $a (param $x i32)
..may use $x..
)
(func $b
(call $a (i32.const 1))
(call $a (i32.const 1))
)
=>
(func $a
(local $x i32)
(set_local $x (i32.const 1)
..may use $x..
)
(func $b
(call $a)
(call $a)
)
How much this helps depends on the codebase obviously, but sometimes it is pretty useful. For example, it shrinks 0.72% on Unity and 0.37% on Mono. Note that those numbers include not just the optimization itself, but the other optimizations it then enables - in particular the second point from earlier leads to inlining a constant value, which often allows constant propagation, and also removing parameters may enable more duplicate function elimination, etc. - which explains how this can shrink Unity by almost 1%.
Implementation is pretty straightforward, but there is some work to make the heavy part of the pass parallel, and a bunch of corner cases to avoid (can't change a function that is exported or in the table, etc.). Like the Inlining pass, there is both a standard and an "optimizing" version of this pass - the latter also optimizes the functions it changes, as like Inlining, it's useful to not need to re-run all function optimizations on the whole module.
|
|
|
|
| |
In wasmjs, the nested selects are trying to use the same temporary locals. The patch reserves temporaries "at right time" to avoid overriding the values.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This adds an licm pass. Not that important for LLVM-originating code obviously, but for AssemblyScript and other non-LLVM compilers this might help a lot. Also when wasm has GC a bunch more non-LLVM languages may arrive that can benefit.
The pass is mostly straightforward. I considered using the DataFlow IR since it's in SSA form, or the CFG IR, but in the end it's actually pretty convenient to use the main IR as it is - with explicit loops already present - plus LocalGraph which connects each get to the sets influencing it.
Passed a bunch of fuzzing, and also the emscripten test suite at -O1 with licm added to the default passes (but I don't think it would make sense to run this by default, as LLVM doesn't need it).
We limit code moved by this pass as follows: An increased code size on fuzz testcases (and, more rarely, on real inputs) can happen due to stuff like this:
(loop
(set_local $x (i32.const 1))
..
)
=>
(set_local $x (i32.const 1))
(loop
..
)
For a const or a get_local, such an assignment to a local is both very cheap (a copy to another local may be optimized out later), and moving it out may prevent other optimizations (since we have no pass that tries to move code back in to a loop edit well, not by default, precompute-propagate etc. would do it, but are only run on high opt levels). So I made the pass not move such trivial code (sets/tees of consts or gets). However, the risk remains if code is moved out that is later reduced to a constant, so something like -Os --flatten --licm -Os may make sense.
|
|
|
|
|
| |
The change means that nan values will be compared bitwise when writing A == B, and so the float rule of a nan is different from itself would not apply.
I think this is a safer default. In particular this PR fixes a fuzz bug in the rse pass, which placed Literals in a hash table, and due to nan != nan, an infinite loop... Also, looks like we really want a bitwise comparison pretty much everywhere anyhow, as can be seen in the diff here. Really the single place we need a floaty comparison is in the intepreter where we implement f32.eq etc., and there the code was already using the proper code path anyhow.
|
|
|
|
|
| |
This allows using imports in the table.
Fixes #1645
|
|
|
|
| |
spec compatible. (#1646)
|
|
|
|
| |
This pass was replacing all the uses of certain imports but
wasn't bothering to delete the old ones.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* Rename the `wasm2asm` tool to `wasm2js`
This commit performs a relatively simple rename of the `wasm2asm` tool to
`wasm2js`. The functionality of the tool doesn't change just yet but it's
intended that we'll start generating an ES module instead of just an `asm.js`
function soon.
* wasm2js: Support `*.wasm` input files
Previously `wasm2js` only supported `*.wast` files but to make it a bit easier
to use in tooling pipelines this commit adds support for reading in a `*.wasm`
file directly. Determining which parser to use depends on the input filename,
where the binary parser is used with `*.wasm` files and the wast parser is used
for all other files.
* wasm2js: Emit ESM imports/exports by default
This commit alters the default behavior of `wasm2js` to emit an ESM by default,
either importing items from the environment or exporting. Items like
initialization of memory are also handled here.
|
| |
|
|
|
|
|
| |
Allowing duplicates here was causes emscripten to generate a JS
object with duplicate keys.
|
|
|
|
|
| |
That method looks through tee_locals and other operations that receive a value and let it flow through them, like a block's final value, etc. It just handled a few such operations, with this PR all of them should be handled.
Also refactor it out of the OptimizeInstructions pass as I think it may be useful for propagating returned constants.
|
|
|
|
|
|
|
|
|
| |
Background: google/souper#323
This adds a --souperify pass, which emits Souper IR in text format. That can then be read by Souper which can emit superoptimization rules. We hope that eventually we can integrate those rules into Binaryen.
How this works is we emit an internal "DataFlow IR", which is an SSA-based IR, and then write that out into Souper text.
This also adds a --dfo pass, which stands for data-flow optimizations. A DataFlow IR is generated, like in souperify, and then performs some trivial optimizations using it. There are very few things that can do that our other optimizations can't already, but this is also good testing for the DataFlow IR, plus it is good preparation for using Souper's superoptimization output (which would also construct DataFlow IR, like here, but then do some matching on the Souper rules).
|
|
|
|
|
| |
The fuzzer found a bug with flowing of values in that pass: when one arm of an if is none-typed, we can't flow a value through the other. Odd the fuzzer didn't find this earlier, as it's been a bug since the pass was written years ago, but in practice it seems you need a specific set of circumstances on the outside for it to be hit.
The fix is to stop flowing a value in that case. Also, I realized after fixing it that the valueCanFlow global state variable is entirely unneeded. Removing it makes the pass significantly simpler: at all times, flows contains branches and values that might be flowing, and if the flow stops we remove them, etc. - we don't need an extra state variable to say if flowing is possible. So when we want to use the flows, we just check what is there (and then for a flowing branch we can remove it, and for a flowing value we can replace the branch with the value, etc., as in both cases they flow to the right place anyhow).
|
|
|
| |
This now makes --generate-stack-ir --print-stack-ir emit a fully valid .wat wasm file, in stacky format.
|
|
|
|
|
| |
* When we eval a ctor, don't just nop the function body that no longer needs to be executed, also remove the export (as we report the ctor being evalled, and the outside will no longer call it).
* Run the pass to remove unused global things. This can usually remove evalled ctors (unless something else happens to call them, which can't happen normally as LLVM wouldn't use a ctor in another place, but e.g. duplicate function merging might merge a ctor with another function).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This adds a new IR, "Stack IR". This represents wasm at a very low level, as a simple stream of instructions, basically the same as wasm's binary format. This is unlike Binaryen IR which is structured and in a tree format.
This gives some small wins on binary sizes, less than 1% in most cases, usually 0.25-0.50% or so. That's not much by itself, but looking forward this prepares us for multi-value, which we really need an IR like this to be able to optimize well. Also, it's possible there is more we can do already - currently there are just a few stack IR optimizations implemented,
DCE
local2stack - check if a set_local/get_local pair can be removed, which keeps the set's value on the stack, which if the stars align it can be popped instead of the get.
Block removal - remove any blocks with no branches, as they are valid in wasm binary format.
Implementation-wise, the IR is defined in wasm-stack.h. A new StackInst is defined, representing a single instruction. Most are simple reflections of Binaryen IR (an add, a load, etc.), and just pointers to them. Control flow constructs are expanded into multiple instructions, like a block turns into a block begin and end, and we may also emit extra unreachables to handle the fact Binaryen IR has unreachable blocks/ifs/loops but wasm does not. Overall, all the Binaryen IR differences with wasm vanish on the way to stack IR.
Where this IR lives: Each Function now has a unique_ptr to stack IR, that is, a function may have stack IR alongside the main IR. If the stack IR is present, we write it out during binary writing; if not, we do the same binaryen IR => wasm binary process as before (this PR should not affect speed there). This design lets us use normal Passes on stack IR, in particular this PR defines 3 passes:
Generate stack IR
Optimize stack IR (might be worth splitting out into separate passes eventually)
Print stack IR for debugging purposes
Having these as normal passes is convenient as then they can run in parallel across functions and all the other conveniences of our current Pass system. However, a downside of keeping the second IR as an option on Functions, and using normal Passes to operate on it, means that we may get out of sync: if you generate stack IR, then modify binaryen IR, then the stack IR may no longer be valid (for example, maybe you removed locals or modified instructions in place etc.). To avoid that, Passes now define if they modify Binaryen IR or not; if they do, we throw away the stack IR.
Miscellaneous notes:
Just writing Stack IR, then writing to binary - no optimizations - is 20% slower than going directly to binary, which is one reason why we still support direct writing. This does lead to some "fun" C++ template code to make that convenient: there is a single StackWriter class, templated over the "mode", which is either Binaryen2Binary (direct writing), Binaryen2Stack, or Stack2Binary. This avoids a lot of boilerplate as the 3 modes share a lot of code in overlapping ways.
Stack IR does not support source maps / debug info. We just don't use that IR if debug info is present.
A tiny text format comment (if emitting non-minified text) indicates stack IR is present, if it is ((; has Stack IR ;)). This may help with debugging, just in case people forget. There is also a pass to print out the stack IR for debug purposes, as mentioned above.
The sieve binaryen.js test was actually not validating all along - these new opts broke it in a more noticeable manner. Fixed.
Added extra checks in pass-debug mode, to verify that if stack IR should have been thrown out, it was. This should help avoid any confusion with the IR being invalid.
Added a comment about the possible future of stack IR as the main IR, depending on optimization results, following some discussion earlier today.
|
| |
|
|
|
|
|
|
|
| |
* - Throw ParseException when istringstream failed to read a number.
- Modify now invalid tests.
* Add invalid_number.wast test
|
|
|
|
|
|
|
|
|
| |
This PR includes non-mutable globals in precompute, which will allow me to continue removing manual inlining of constants in AssemblyScript without breaking something. Related: #1621, i.e.
enum Animal {
CAT = 0,
DOG = CAT + 1 // requires that `Animal.CAT` is evaluated to
// precompute the constant value for `Animal.DOG`
}
|
|
|
|
|
|
|
| |
See #1479 (comment)
Also a one-line readme update, remove an obsolete compiler (mir2wasm) and add a new one (asterius).
Also improve warning and error reporting in binaryen.js - show a stack trace when relevant (instead of node.js process.exit), and avoid atexit warning spam in debug builds.
|
|
|
|
| |
s2wasm is no longer used my emscripten and as far as I know now
as no other users.
|
|
|
|
| |
The wasm waterfall is moving away from testing with s2wasm
and s2wasm hopefully going to be removed soon.
|
|
|
|
|
|
|
|
|
| |
This defines a new -O4 optimization mode, as flatten + flat-only opts (currently local-cse) + -O3.
In practice, flattening is not needed for LLVM output, which is pretty flat already (no block or if values, etc., even if it does use tees and does nest expressions; and LLVM has already done gvn etc. anyhow). In general, though, wasm generated by a non-LLVM compiler may naturally be nested because wasm allows that. See for example #1593 where an AssemblyScript testcase requires flattening to be fully optimized. So -O4 can help there.
-O4 takes 3x longer to run than -O3 in my testing, basically because flat IR is much bigger. But when it's useful it may be worth it. It does handle that AssemblyScript testcase and others like it. There's not much big real-world code that isn't LLVM yet, but running the fuzzer - which happily creates nested stuff all the time - I see -O4 consistently shrink the size by around 20% over -O3.
|
|
|
|
|
| |
This makes it much more effective, by rewriting it to depend on flatten. In flattened IR, it is very simple to check if an expression is equivalent to one already available for use in a local, and use that one instead, basically we just track values in locals.
Helps with #1521
|
|
|
|
|
|
|
|
|
|
| |
* support source map input in wasm-opt, refactoring the loading code into wasm-io
* use wasm-io in wasm-as
* support output source maps in wasm-opt
* add a test for wasm-opt and source maps
|
|
|
|
|
|
|
| |
On a codebase with 370K functions, 160K were in fact duplicate (!)... and it took many many passes to figure that out, over 2 minutes in fact (!), as A and B may be identical only after we see that the functions C1, C2 that they call are identical (so there can be long "chains" here).
To avoid this, limit how many passes we do. In -O1, just do one pass - that gets most duplicates. In -O2, do 10 passes - that gets almost all of it on this codebase. And in -O3 (or -Os/-Oz) do as many passes as necessary (i.e., the old behavior). This at least lets iteration builds (-O1) be nice and fast.
This PR also refactors the hashing code used in that pass, moving it to nicer header files for clearer readability. Also some other minor cleanups in hashing code that helped debug this.
|
| |
|
|
|
|
| |
helpful in both positions on general code (#1581)
|
|
|
|
| |
jsCallStartIndex (#1579)
|
|
|
|
|
|
|
| |
On the testcase from https://github.com/tweag/asterius/issues/19#issuecomment-393052653 this makes us almost 3x faster, and use 25% less memory.
The main improvement here is to simplify and optimize the data structures the validator uses to validate br targets: use unordered maps, and use one less of them. Also some speedups from using that map more effectively (use of iterators to avoid multiple lookups).
Also move the duplicate-node checks to the internal IR validation section, which makes more sense anyhow (it's not wasm validation, it's internal IR validation, which like the check for stale internal types, we do only if debugging).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* Import `abort` from the environment
* Add passing spec tests
* Bind the abort function
* wasm2asm: Fix name collisions
Currently function names and local names can collide in namespaces, causing
buggy results when a function intends to call another function but ends up using
a local value as the target!
This fix was required to enable the `fac` spec test
* wasm2asm: Get multiple modules in one file working
The spec tests seem to have multiple modules defined in some tests and the
invocations all use the most recently defined module. This commit updates the
`--allow-asserts` mode of wasm2asm to work with this mode of tests, enabling us
to enable more spec tests for wasm2asm.
* wasm2asm: Enable the float_literals spec test
This needed to be modified to account for how JS engines don't work with NaN
bits the same way, but it's otherwise largely the same test. Additionally it
turns out that asm.js doesn't accept either `Infinity` or `NaN` ambient globals
so they needed to get imported through the `global` variable rather than defined
as literals in code
* wasm2asm: Fix function pointer invocations
This commit fixes invocations of functions through function pointers as
previously the table names on lookup and definition were mismatched. Both tables
now go through signature-based namification rather than athe name of the type
itself.
Overall this enables a slew of spec tests
* wasm2asm: Enable the left-to-right spec test
There were two small bugs in the order of evaluation of operators with
wasm2asm. The `select` instruction would sometimes evaluate the condition first
when it was supposed to be last. Similarly a `call_indirect` instruction would
evaluate the function pointer first when it was supposed to be evaluated last.
The `select` instruction case was a relatively small fix but the one for
`call_indirect` was a bit more pessimized to generate some temporaries.
Hopefully if this becomes up a problem it can be tightened up.
* wasm2asm: Fix signed load promotions of 64-bit ints
This commit enables the `endianness` spec test which revealed a bug in 64-bit
loads from smaller sizes which were signed. Previously the upper bits of the
64-bit number were all set to zero but the fix was for signed loads to have all
the upper bits match the highest bit of the low 32 bits that we load.
* wasm2asm: Enable the `stack` spec test
Internally the spec test uses a mixture of the s-expression syntax and the wat
syntax, so this is copied over into the `wasm2asm` folder after going through
`wat2wasm` to ensure it's consistent for binaryen.
* wasm2asm: Fix unaligned loads/stores of floats
Replace these operations in `RemoveNonJSOps` by using reinterpretation to
translate floats to integers and then use the existing code for unaligned
loads/stores of integers.
* wasm2asm: Fix a tricky grow_memory codegen bug
This commit fixes a tricky codegen bug found in the `grow_memory` instruction.
Specifically if you stored the result of `grow_memory` immediately into memory
it would look like:
HEAP32[..] = __wasm_grow_memory(..);
Here though it looks like JS evaluates the destination *before* the grow
function is called, but the grow function will invalidate the destination!
Furthermore this is actually generalizable to all function calls:
HEAP32[..] = foo(..);
Because any function could transitively call `grow_memory`. This commit fixes
the issue by ensuring that store instructions are always considered statements,
unconditionally evaluating the value into a temporary and then storing that into
the destination. While a bit of a pessmimization for now it should hopefully fix
the bug here.
* wasm2asm: Handle offsets in tables
This commit fixes initializing tables whose elements have an initial offset.
This should hopefully help fix some more Rust code which has all function
pointers offset by default!
* Update tests
* Tweak * location on types
* Rename entries of NameScope and document fromName
* Comment on lowercase names
* Update compiled JS
* Update js test output expectation
* Rename NameScope::Global to NameScope::Top
* Switch to `enum class`
* Switch to `Fatal()`
* Add TODO for when asm.js is no longer generated
|
|
|
|
|
|
|
|
|
|
| |
Remove executable bit and #! from scripts that don't have
entry point.
Add missing licence test.
Move arg parsing into a function.
Remove legacy --only_prepare (with underscrore) argument.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* wasm2asm: Finish i64 lowering operations
This commit finishes out lowering i64 operations to JS with implementations of
division and remainder for JS. The primary change here is to have these compiled
from Rust to wasm and then have them "linked in" via intrinsics. The
`RemoveNonJSOps` pass has been updated to include some of what
`I64ToI32Lowering` was previously doing, basically replacing some instructions
with calls to intrinsics. The intrinsics are now all tracked in one location.
Hopefully the intrinsics don't need to be regenerated too much, but for
posterity the source currently [lives in a gist][gist], although I suspect that
gist won't continue to compile and work as-is for all of time.
[gist]: https://gist.github.com/alexcrichton/e7ea67bcdd17ce4b6254e66f77165690
|
| |
|
|
|
| |
Don't skip through flowing tee values, just drop the current outermost which we find is redundant. the child tees may still be necessary.
|
|
|
|
|
|
|
|
|
| |
This commit lifts the same conversion strategy that `emcc` takes to convert
between floats point numbers and integers, and it should implement all the
various matrices of i32/u32/i64/u64 to f32/f64
Some refactoring was performed in the i64->i32 pass to allow for temporary
variables to get allocated which have types other than i32, but otherwise this
contains a pretty direct translation of `emcc`'s operations to `wasm2asm`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
implementFunctions should use the export names, not the
internal/debug name for a function. This is especially
imported with lld where the debug names are demanagled.
implementFunctions should only contain functions that are
accessible from outside the module. i.e. those that have
been exported. There is no point in adding internal-only
functions to this list as they won't be accessible from
outside anyway.
Tesed with emscripten using: ./tests/runner.py binaryen2.test_time
|
|
|
|
|
|
| |
This commit implements the `copysign` instruction for the wasm2asm binary. The
implementation here is a new pass which wholesale replaces `copysign`
instructions with the equivalent bit ops and reinterpretation instructions. It's
intended that this matches Emscripten's implementation of lowering here.
|