| Commit message (Collapse) | Author | Age | Files | Lines |
... | |
|
|
|
| |
(#4027)
|
|
|
|
|
|
| |
callees (#4025)
If there is a tail call, we can't change the return type of the function, as it
must match in the functions doing a tail call of it.
|
|
|
|
|
|
| |
Corresponds to #4014 which did the same for parameter types. This sees
whether the return types actually returned from a function allow us to use
a more specific type for the function's return. If so, we update that type, as
well as calls to the function.
|
| |
|
| |
|
| |
|
|
|
|
|
| |
The pass handled non-nullability, but another case is a tuple with nullable
values in it that is assigned non-nullable values, and in general, other stuff
that is nondefaultable (but not non-nullable). Ignore those.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Given
(local $x (ref $foo))
(local.set $x ..)
(.. (local.get $x))
If we remove the local.set but not the get, then we end up with
(local $x (ref $foo))
(.. (local.get $x))
It looks like the local.get reads the initial value of a non-nullable local,
which is not allowed.
In practice, this would crash in precompute-propagate which would
try to propagate the initial value to the get. Add an assertion there with
a clear message, as until we have full validation of non-nullable locals
(and the spec for that is in flux), that pass is where bugs will end up
being noticed.
To fix this, replace the get as well. We can replace it with a null
for simplicity; it will never be used anyhow.
This also uncovered a small bug with reached not containing all
the things we reached - it was missing local.gets.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
If a local is say anyref, but all values assigned to it are something
more specific like funcref, then we can make the type of the local
more specific. In principle that might allow further optimizations, as
the local.gets of that local will have a more specific type that their
users can see, like this:
(import .. (func $get-funcref (result funcref)))
(func $foo (result i32)
(local $x anyref)
(local.set $x (call $get-funcref))
(ref.is_func (local.get $x))
)
=>
(func $foo (result i32)
(local $x funcref) ;; updated to a subtype of the original
(local.set $x (call $get-funcref))
(ref.is_func (local.get $x)) ;; this can now be optimized to "1"
)
A possible downside is that using more specific types may not end
up allowing optimizations but may end up increasing the size of
the binary (say, replacing lots of anyref with various specific
types that compress more poorly; also, for recursive types the LUB
may be a unique type appearing nowhere else in the wasm). We
should investigate the code size factors more later.
|
|
|
|
|
|
|
|
|
| |
Practically NFC, but it does reorder some code a little. Previously we would
find a "zero", then shrink segments, then use that zero - which might no
longer be in the table. That seems weird, so this reorders that, but there
should be no significant difference in the output.
Also reduce the factor of 100 to 1, which in practice is important on one of
the Dart GC benchmarks that has a huge number of table segments.
|
|
|
|
|
|
|
|
| |
If a function is always called with a more specific type than it is declared, we can
make the type more specific.
DeadArgumentElimination's name is becoming increasingly misleading, and should
maybe be renamed. But it is the right place for this as it already does an LTO
scan of the call graph and builds up parameter data structures etc.
|
|
|
|
|
|
|
|
|
|
|
|
| |
tryToRemoveFunctions (#4013)
tryToRemoveFunctions() will reload the wasm from binary if it fails to
optimize, and without the names section we don't have a guarantee on the
names being the same after that. And then tryToEmptyFunctions would
look for a name, and crash.
In the reverse order there is no risk, as tryToEmptyFunctions does not
reload the wasm from binary, it carefully undoes what it tried to do when
it fails.
|
|
|
|
|
|
| |
Instead of skipping to the end, move quickly towards the end. This is
sometimes more efficient (as jumping from a big factor to a factor of 1
can skip over big opportunities to remove code all at once instead of
once instruction at a time).
|
|
|
|
|
|
|
| |
For signed or unsigned extension to 64-bits after lowering from partially filled 64-bit arguments:
```rust
i64.extend_i32_u(i32.wrap_i64(x)) => x // where maxBits(x) <= 32
i64.extend_i32_s(i32.wrap_i64(x)) => x // where maxBits(x) <= 31
```
|
|
|
| |
Similar to #4005 but on OptimizeInstructions instead of RemoveUnusedBrs.
|
|
|
|
|
|
|
|
| |
removeUnneededBlocks() can force us to use a local when we
load the emitted wasm, which can't work for something nondefaultable
like an RTT.
For now, just don't run that optimization if GC is enabled. Eventually,
perhaps we'll want to enable this optimization in a different way.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Fixes #3973
Loads:
f32.reinterpret_i32(i32.load(x)) => f32.load(x)
f64.reinterpret_i64(i64.load(x)) => f64.load(x)
i32.reinterpret_f32(f32.load(x)) => i32.load(x)
i64.reinterpret_f64(f64.load(x)) => i64.load(x)
Stores:
f32.store(y, f32.reinterpret_i32(x)) => i32.store(y, x)
f64.store(y, f64.reinterpret_i64(x)) => i64.store(y, x)
i32.store(y, i32.reinterpret_f32(x)) => f32.store(y, x)
i64.store(y, i64.reinterpret_f64(x)) => f64.store(y, x)
Also optimize reinterprets that are undone:
i32.reinterpret_f32(f32.reinterpret_i32(x)) => x
i64.reinterpret_f64(f64.reinterpret_i64(x)) => x
f32.reinterpret_i32(i32.reinterpret_f32(x)) => x
f64.reinterpret_i64(i64.reinterpret_f64(x)) => x
|
|
|
|
|
|
|
|
|
| |
This removes the code that did so one at a time, and instead adds it
in a way that we can do it in an exponentially growing set of functions.
On large testcases where other methods do not work, this is very
useful.
Also adjust the factor to do this 20x more often, which in practice
is very useful too.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
requiring sign-extension:
```
i64(x) << 56 >> 56 ==> i64.extend8_s(x)
i64(x) << 48 >> 48 ==> i64.extend16_s(x)
i64(x) << 32 >> 32 ==> i64.extend32_s(x)
i64.extend_i32_s(i32.wrap_i64(x)) ==> i64.extend32_s(x)
```
general:
```
i32.wrap_i64(i64.extend_i32_s(x)) ==> x
i32.wrap_i64(i64.extend_i32_u(x)) ==> x
```
|
|
|
|
|
| |
The spec disallows that.
Fixes #3990
|
| |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This reverts commit b68691e826a46d1b03b27c552b1f5b7f51f92665.
Instead of applying the workaround to avoid precomputing SIMD in more places,
which prevents things we could optimize before, we should probably focus on
making the workaround not needed - that is, implement full SIMD support in the
intepreter (the reason for that PR), and the other TODO in the comment here,
// Until engines implement v128.const and we have SIMD-aware optimizations
// that can break large v128.const instructions into smaller consts and
// splats, do not try to precompute v128 expressions.
|
|
|
|
|
|
| |
i32(x) < 0 ? i32(-1) : i32(1) -> x >> 31 | 1
i64(x) < 0 ? i64(-1) : i64(1) -> x >> 63 | 1
This shrinks 2 bytes.
|
|
|
|
| |
Without this fix, DCE would end up calling getHeapType() on the unreachable
input, which hits an assertion as it has no heap type.
|
|
|
|
|
|
|
|
| |
This adds calls to imports around every struct load and store, to
note their values, and also to arrays (where it also notes the
index).
This has been very useful in debugging LowerGC (lowering of Wasm
GC to wasm MVP).
|
| |
|
|
|
| |
We had the logic in only one place.
|
|
|
|
|
|
|
| |
Like a few other SIMD operations, this i64x2.bitmask had not been implemented in
the interpreter yet. Unlike the others, i64x2.bitmask has type i32 rather than
type v128, so Precompute was not skipping it, leading to a crash, as in
https://github.com/emscripten-core/emscripten/issues/14629. Fix the problem by
implementing i64x2.bitmask in the interpreter.
|
|
|
|
| |
We only set a name now if there was no name, or the existing name
was really really long.
|
| |
|
|
|
| |
That were somehow missed.. triggered by emscripten tests
|
|
|
|
|
|
|
| |
We only tested that feature on the text format. For binary support, the reader needs
to know that the feature is enabled, so that it allows non-nullable locals in that
case (i.e., does not apply the workarounds to remove them).
Fixes #3953
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
As suggested in
https://github.com/WebAssembly/binaryen/pull/3955#issuecomment-871016647
This applies commandline features first. If the features section is present, and
disallows some of them, then we warn. Otherwise, the features can combine
(for example, a wasm may enable feature X because it has to use it, and a user
can simply add the flag for feature Y if they want the optimizer to try to use it;
both flags will then be enabled).
This is important because in some cases we need to know the features before
parsing the wasm, in the case that the wasm does not use the features section.
In particular, non-nullable GC locals have an effect during parsing. (Typed
function references also does, but we found a way to apply its effect all the time,
that is, always use the refined type, and that happened to not break the case
where the feature is disabled - but such a workaround is not possible with
non-nullable locals.)
To make this less error-prone, add a FeatureSet input as a parameter to
WasmBinaryBuilder. That is, when building a module, we must give it the
features to use while doing so.
This will unblock #3955 . That PR will also add a test for the actual usage
of a feature during loading (the test can only be added there, after that PR
unbreaks things).
|
|
|
|
|
|
|
|
|
|
|
| |
We have seen some cases in both Chrome and Firefox where
extremely large modules cause overhead,
#3730 (comment) (and link therein)
emscripten-core/emscripten#13899 (comment)
There is no "right" value to use as a limit here, but pick an
arbitrary one that is very high. (This setting is verified to have
no effect on the emscripten benchmark suite.)
|
|
|
|
|
|
|
|
|
| |
When using nominal types, func.ref of two functions with identical signatures
but different HeapTypes will yield different types. To preserve these semantics,
Functions need to track their HeapTypes, not just their Signatures.
This PR replaces the Signature field in Function with a HeapType field and adds
new utility methods to make it almost as simple to update and query the function
HeapType as it was to update and query the Function Signature.
|
|
|
|
|
|
|
|
|
|
| |
That traversal did not mention BrOn, which led to it doing incorrect work in
SimplifyLocals.
Also add assertions at the end, that aim to prevent future issues.
The rest of the fix is to make SimplifyLocals not assume that things
are a Switch if they are not an If/Block/etc., so that we don't crash
on a BrOn.
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The code for printing and emitting the experimental nominal type constructors
added in #3933 assumes that supertypes were only returned from `getSuperType`
when nominal typing was enabled. `getSuperType` in turn was assuming that the
supertype field would only be set if nominal typing was enabled, but this was
not the case. This bug caused use-after-free errors because equirecursive
canonicalization left the supertype field pointing to a temporary HeapTypeInfo
that would be freed at the end of parsing but then accessed during module
writing.
To fix the issue, only set `supertype` if nominal typing is enabled, as
originally intended.
|
| |
|
|
|
|
|
|
|
|
|
| |
PostEmscripten will turn an invoke of a constant function
pointer index into a direct call. However, due to UB it is possible to
have invalid function pointers, and we should not crash on that
(and do nothing to optimize, of course).
Mostly whitespace; to avoid deep nesting, I added more
early returns.
|
|
|
|
|
|
|
|
|
|
|
| |
This changes the encoding of the `attribute` field, which currently only
contains the value `0` denoting this tag is for an exception, from
`varuint32` to `uint8`. This field is effectively unused at the moment
and reserved for future use, and it is not likely to need `varuint32`
even in future.
See https://github.com/WebAssembly/exception-handling/pull/162.
This does not change any encoded binaries because `0` is encoded in the
same way both in `varuint32` and `uint8`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Previously, ref.func instructions would be assigned the canonical (i.e. first
parsed) heap type for the referenced function signature rather than the HeapType
actually specified in the type definition. In nominal mode, this could cause
validation failures because the types assigned to ref.func instructions would
not be correct.
Fix the problem by tracking function HeapTypes rather than function Signatures
when parsing the text format.
There can still be validation failures when round-tripping modules because
function HeapTypes are not properly preserved after parsing, but that will be
addressed in a follow-up PR.
|
|
|
|
|
|
|
|
|
| |
This removes `attribute` field from `Tag` class, making the reserved and
unused field known only to binary encoder and decoder. This also removes
the `attribute` parameter from `makeTag` and `addTag` methods in
wasm-builder.h, C API, and Binaryen JS API.
Suggested in
https://github.com/WebAssembly/binaryen/pull/3946#pullrequestreview-687756523.
|
|
|
|
|
|
|
|
| |
This attribute is always 0 and reserved for future use. In Binayren's
unofficial text format we were writing this field as `(attr 0)`, but we
have recently come to the conclusion that this is not necessary.
Relevant discussion:
https://github.com/WebAssembly/exception-handling/pull/160#discussion_r653254680
|
|
|
|
|
|
| |
We don't need to assign a zero value for such locals (and we can't, as no
default value exists for them).
Fixes #3944
|
|
|
|
|
|
|
|
|
|
|
| |
We recently decided to change 'event' to 'tag', and to 'event section'
to 'tag section', out of the rationale that the section contains a
generalized tag that references a type, which may be used for something
other than exceptions, and the name 'event' can be confusing in the web
context.
See
- https://github.com/WebAssembly/exception-handling/issues/159#issuecomment-857910130
- https://github.com/WebAssembly/exception-handling/pull/161
|
|
|
|
|
|
|
|
|
|
|
| |
These files are special in that they use define symbols that are not
defined within those files or other files included in those files; they
are supposed to be defined in source files that include these headers.
This has caused clang-tidy to fail every time these files have changed
because they are not compilable per se.
This PR solves the problem by changing their extension to `def`, which
is also used in LLVM codebase. LLVM has dozens of files like this whose
extension is `def`, which makes these not checked by clang-tidy.
|
|
|
|
|
|
|
|
|
|
| |
RedundantSetElimination checks if a local already has the default
value when we assign the default to it. For a non-nullable local, however,
there is no initial value - it cannot be used before it is assigned to. So
we just need to skip such locals, and not assume they contain a default
value we can compare against (we would assert on trying to create a
"zero" for such a non-nullable type).
Fixes #3942
|
|
|
|
|
|
|
|
| |
This is a useful alternative to extract-function when you don't know the
function's name.
Also moves the extract-function tests to be lit tests and re-uses them as
extract-function-index tests.
|