| Commit message (Collapse) | Author | Age | Files | Lines |
|
|
| |
This new variant of ref.test returns 1 if the input is null.
|
|
|
|
|
|
|
|
|
|
|
| |
Fix a regression from #5025 : we subtract constants there, and we need to be aware
that such subtraction can change a constant from signed to unsigned if the comparison
is signed, as
0x80000000 - 1 = 0x7fffffff
0x8000000 is a negative number when seen as signed, but always positive after the
subtraction.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Since #5347 public types are never updated by type optimizations, but the
optimization passes have not yet been updated to take that into account, so they
are all buggy under an open world assumption. In #5359 we worked around many
closed world validation errors in the fuzzer by treating --closed-world like a
feature flag and checking whether it was necessary for fuzzer input, but that
did not prevent the type optimization passes from running under an open world,
so it did not work around all the potential issues.
Work around the problem more thoroughly by not running any type optimization
passes in the fuzzer without --closed-world. Also add logic to those passes to
error out if they are run without --closed-world and update the tests
accordingly.
|
|
|
|
|
|
|
|
|
| |
The latest upstream version of ref.cast is parameterized with a target reference
type, not just a heap type, because the nullability of the result is
parameterizable. As a first step toward implementing these new, more flexible
ref.cast instructions, change the internal representation of ref.cast to use the
expression type as the cast target rather than storing a separate heap type
field. For now require that the encoded semantics match the previously allowed
semantics, though, so that none of the optimization passes need to be updated.
|
|
|
|
|
|
|
| |
(#5364)
This reduces the amount of public types, since if there is a super then using the
type in a public place would make the super also public. It is safer for closed-world
mode to reuse types without supers.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The type rewriting utility in type-updating.cpp gathers all the used heap types,
then rewrites them to newly built and possibly modified heap types. The problem
is that for the isorecursive type system, the set of "used" heap types was
overly broad because it also included unused heap types that are in a rec group
with used types. In the context of emitting a binary, it is important to treat
these types as used because failing to emit them would change the identity of
the used types, but in the context of type optimizations it is ok to treat them
as truly unused because we are changing type identities anyway.
Update the type rewriting utility to only include truly used types in the set of
output types. This causes all existing type optimizations to implicitly drop
unused types, but only if they find any other optimizations to do and actually
run the rewriter utitility. Their output will also still include unused types
that were used before their optimizations were applied.
To overcome these limitations and better match the optimizing power of nominal
mode, which never includes unused types in the output, add a new type
optimization pass that removes unused types and does nothing else and run it
near the end of the global optimization pipeline.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Do not optimize or modify public heap types in any way. Public heap types
include the types of imported or exported functions, tables, globals, etc. This
is important to maintain the public interface of a module and ensure it can
still link interact as intended with the outside world.
Also add validation error if we find any nontrivial public types that are not
the types of imported or exported functions. This error is meant to help the
user ensure that type optimizations are not silently inhibited. In the future,
we may want to add options to silence this error or downgrade it to a warning.
This commit only updates the type updating machinery to avoid updating public
types. It does not update any optimization passes accordingly. Since we avoid
modifying public signature types already, this is not expected to break
anything, but in the future once we have function subtyping or if we make the
error optional, we may have to update some of our optimization passes.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
E.g.
(struct.get
(select
(ref.null ..)
(something)
(condition)
)
)
If traps-never-happen then this can be
(drop (condition))
(struct.get
(something)
)
That is, we can remove the arm that is null, as it would trap but traps are
assumed to not happen.
Also fix a bug this uncovers on struct.set on a null type.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
If wasm-opt or wasm-dis are given an invalid binary, after the error
message we can also print out the wasm we did manage to read. That
includes global stuff like imports and also all the functions up until
there. This can help debugging in some situations.
Only do this when --debug is passed as it can be very verbose and
in general users might not want it.
This is technically easy to do, it turns out, since we already use a
thrown exception on an error in parsing, and we fill up the wasm as
we go, so it just contains what we've read so far, and we can just
print it.
Fixes #5344
Also switch an existing test's comments to ;; from # which was
noticed here.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This fixes a TODO.
There is a runtime cost to this in higher opt levels, as passing through -O3
makes nested optimization work take longer. But it can lead to better results.
For now, this PR moves us from 0 before to a maximum of 1, as a compromise.
1 does not regress compile times, but there may be further benefits to allowing
2 and 3 in the future.
Also fix a fuzzer bug that becomes uncovered by tihs PR: Now that we actually
optimize in simplify-globals, we need to handle the case of the optimizer there
seeing a call with the effects of writing to a global (we had an assert on that
never happening, but with function effects that can happen, and so a GlobalSet
is not the only thing that can set a global).
Aside from the opt and shrink levels this passes through all other options,
like trapsNeverHappen.
|
|
|
| |
This PR adds support for memory.init, memory.copy, and memory.fill instructions in the multi-memory lowering pass. Also includes optional bounds checks per the wasm spec guidelines.
|
|
|
|
| |
MSVC is making `NAN` negative, so use an explicitly constructed positive NaN
instead.
|
|
|
|
|
|
|
|
|
|
| |
Previously -O3 -O1 would run -O1 twice since the last flag set the global opt level
to 1, and then all invocations of the optimizer pipeline read that. This makes each
pipeline define its own opt level.
This has been a long-standing annoyance, which wasn't much noticed except that
with wasm GC there is more of a need to run the optimization pipeline more than
once. And sometimes it is nice to run different levels.
|
|
|
|
|
|
|
| |
If a global's type is not fully refined, then when --gsi replaces a reference with
a global.get, we end up with a type that might not be good enough. For example,
if the type is any then it is not a subtype of eq and we can't do ref.eq on it, which
this pass requires. We also can't just do struct.get on it if it is a too-distant parent
or such.
|
|
|
|
|
| |
This PR adds support for Atomic instructions in the multi-memory lowering pass. Also includes optional bounds checks per the wasm spec guidelines, (visitAtomicRMW, visitAtomicCmpxchg, visitAtomicWait, visitAtomicNotify).
Note: The latter two instructions, memory.atomic.wait and memory.atomic.notify, have browser engine implementations that predate the still-in-progress threads spec. And whether or not atomic.notify should trap for out-of-bounds addresses remains an open issue. For now, this PR is using the same semantics as v8, which is to bounds check all Atomic instructions the same way and trap for out-of-bounds.
|
|
|
| |
This PR adds support for SIMD instructions in the multi-memory lowering pass. Also includes optional bounds checks per the wasm spec guidelines, (SIMDLoad, SIMDLoadSplat, SIMDLoadExtend, SIMDLoadZero, SIMDLoadStoreLane load | store).
|
|
|
| |
Per the wasm spec guidelines for Load (rule 10) & Store (rule 12), this PR adds an option for bounds checking, producing a runtime error if the instruction exceeds the bounds of the particular memory within the combined memory.
|
|
|
|
|
|
|
|
|
|
|
|
| |
We switched from emitting the legacy `ref.cast_static` instruction to emitting
`ref.cast null` in #5331, but that wasn't quite correct. The legacy instruction
had polymorphic typing so that its output type was nullable if and only if its
input type was nullable. In contrast, `ref.cast null` always has a a nullable
output type.
Fix our output by instead emitting non-nullable `ref.cast` if the output should
be non-nullable. Parse `ref.cast` in binary and text forms as well. Since the IR
can only represent the legacy polymorphic semantics, disallow unsupported casts
from nullable to non-nullable references or vice versa for now.
|
|
|
|
|
|
|
| |
The standard casting instructions now allow casting to basic heap types, not
just user-defined types, but they also require that the intended type and
argument type have a common supertype. Update the validator to use the standard
rules, update the binary parser and printer to allow basic types, and update the
tests to remove or modify newly invalid test cases.
|
|
|
|
|
|
|
| |
We previously supported only the non-standard cast instructions introduced when
we were experimenting with nominal types. Parse the names and opcodes of their
standard counterparts and switch to emitting the standard names and opcodes.
Port all of the tests to use the standard instructions, but add additional tests
showing that the non-standard versions are still parsed correctly.
|
| |
|
|
|
|
|
|
|
|
| |
This finds types that can be merged into their super: types that add no
fields, and are not used in casts, etc. - so we might as well use the super.
This complements TypeSSA, in that it can merge back the new types that
TypeSSA created, if we never found a use for them. Without this, TypeSSA
can bloat binary size quite a lot (I see 10-20%).
|
|
|
|
|
|
|
|
|
|
| |
The upstream WasmGC spec has removed `data` and introduced `struct`. To make the
migration easier, we have been supporting `struct` as an `alias` for `data` and
`structref` as an alias for `dataref`.
Update the tests to prefer the `struct` aliases over `data` for test input to
make the future migration easier. Also update some tests that had stale comments
about ref.null types being updated and remove some tests for instructions like
br_on_data and ref.as_data that do not make sense without a `data` type.
|
|
|
| |
Previously it only handled structs.
|
|
|
|
|
|
|
|
|
|
|
| |
Followup to #5293, this fixes a small regression there regarding assertions. We do have
a need to visit non-instrumented functions if we want assertions, as we assert on some
things there, namely that such functions do not change the state (if they changed it,
we'd need to instrument them to handle that properly).
This moves that logic into a new pass. We run that pass when assertions are enabled.
Test diff basically undoes part the test diff from that earlier PR for that one file.
|
| |
|
|
|
|
|
|
|
|
|
| |
Add a way to proxy passes and the addition of passes in pass runners. With
that we can make Asyncify only modify functions it actually needs to. On a
project that Asyncify only needs to modify a few functions on, this can save
a huge amount of time as it avoids flattening+optimizing the majority of
the module.
Fixes #4822
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This creates new nominal types for each (interesting) struct.new. That then allows
type-based optimizations to be more precise, as those optimizations will track
separate info for each struct.new, in effect. That is kind of like SSA, however, we
do not handle merges. For example:
x = struct.new $A (5);
print(x.value);
y = struct.new $A (11);
print(y.value);
// => //
x = struct.new $A.x (5);
print(x.value);
y = struct.new $A.y (11);
print(y.value);
After the pass runs each of those struct.new creates a unique type, and type-based
analysis can see that 5 or 11 are the only values written in that type (if nothing else
writes there).
This bloats the type section with the new subtypes, so it is best used with a pass
to merge unneeded duplicate types, which a later PR will add. That later PR will
exactly merge back in the types created here, which are nominally different but
indistinguishable otherwise.
This pass is not enabled by default. It's not clear yet where is the best place to do it,
as it must be balanced by type merging, but it might be better to do multiple
rounds of optimization between the two. Needs more investigation.
|
|
|
|
|
| |
In favor of the more portable code snippet using `std::copysign`. Also
reintroduce assertions that the NaNs have the expected signs. This continues
work started in #5302.
|
|
|
|
|
|
|
| |
Since `data` has been removed from the upstream proposal and `struct` has been
added in its place, update the type fuzzer to be structured around `struct` and
`array` (which it had not previously been updated to support) rather than
`data`. A follow-on PR will make the broader change of removing `data` and
adding `struct`.
|
|
|
|
|
|
|
|
|
|
|
|
| |
Before we implemented bottom heap types, `ref.null` had to be annotated with
specific types. The `LUBFinder` utility ignored these types so that it could
find the best LUB from all considered non-null expressions, then go back and
update the type annotations on the nulls to match that LUB. Now that we have
bottom types, however, none of that is necessary, and in fact ignoring nulls can
miss possible refinements to bottom types.
Update and simplify `LUBFinder` so that it is a simple wrapper around the
underlying `Type::getLeastUpperBound` utility with no additional logic. Update
tests to account for the more powerful optimizations.
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
With this change we default to an open world, that is, we do the safe thing
by default: we no longer assume a closed world. Users that want a closed
world must pass --closed-world.
Atm we just do not run passes that assume a closed world. (We might later
refine them to find which types don't escape and only optimize those.) The
RemoveUnusedModuleElements is an exception in that the closed-world
flag influences one part of its operation, but not the rest.
Fixes #5292
|
|
|
|
|
| |
As noticed in #5303, the test changes here are because we did unnecessary work
which created a new rec group, which then led to a rec group being printed out.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Inlining had a bug where it gave return_calls in inlined callees concrete types
even when they should have remained unreachable. This bug flew under the radar
because validation had a bug where it allowed expressions to have concrete types
when they should have been unreachable. The fuzzer found this bug by adding
another pass after inlining where the unexpected types caused an assertion
failure.
Fix the bugs and add a test that would have triggered the inlining bug.
Unfortunately the test would have also passed before this change due to the
validation bug, but it's better than nothing.
Fixes #5294.
|
|
|
| |
The flag does nothing so far.
|
|
|
|
| |
Equirecursive is no longer standards track and its implementation is extremely
complex. Remove it.
|
|
|
|
|
|
|
|
|
|
| |
This makes Binaryen's default type system match the WasmGC spec.
Update the way type definitions without supertypes are printed to reduce the
output diff for MVP tests that do not involve WasmGC. Also port some
type-builder.cpp tests from test/example to test/gtest since they needed to be
rewritten to work with isorecursive type anyway.
A follow-on PR will remove equirecursive types completely.
|
|
|
| |
Same testcase as in #5287 but in another pass.
|
| |
|
|
|
|
|
|
|
|
|
| |
Update `HeapType::getFeatures` to report that GC is used for heap types that
have nontrivial recursion groups or supertypes. Update validation to check the
features on function heap types, not just their individual params and results.
This fixes a fuzz bug in #5239 where initial contents included a rec group but
the fuzzer disabled GC. Since the resulting module passed validation, the rec
groups made it into the binary output, making the type section malformed.
|
|
|
|
|
| |
Normally we ignore them anyhow (unreachability is an effect, either a trap or
a control flow switch), but in traps-never-happen mode we can ignore a trap, so
we need to check this manually.
|
|
|
|
|
|
|
| |
Since we optimize assuming a closed world, optimizations can change the types
and structure of GC data even in externally-visible ways. Because differences
are expected, the fuzzer already did not compare reference-typed values from
before and after optimizations when running with nominal typing. Update it to
not compare these values under any type system.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
(some.operation
(ref.cast .. (local.get $ref))
(local.get $ref)
)
=>
(some.operation
(local.tee $temp
(ref.cast .. (local.get $ref))
)
(local.get $temp)
)
This can help cases where we cast for some reason but happen to not use the
cast value in all places. This occurs in j2wasm in itable calls sometimes: The
this pointer is is refined, but the itable may be done with an unrefined pointer,
which is less optimizable.
So far this is just inside basic blocks, but that is enough for the cast of itable
calls and other common patterns I see.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Fixes a longstanding problem with isorecursive canonicalization that only showed
up in MacOS and occasionally Windows builds. The problem was that
`RecGroupEquator` was not quite correct in the presence of self-references in
rec groups. Specifically, `RecGroupEquator` did not differentiate between
instances of the same type appearing across two rec groups where the type was a
self-reference in one group but not in the other.
The reason this only showed up occasionally on some platforms was that this bug
could only cause incorrect behavior if two groups that would incorrectly be
compared as equal were hashed into the same bucket of a hash map. Apparently the
hash map used on Linux never hashes the two problematic groups into the same
bucket.
|
|
|
|
|
| |
(#5266)
This reverts commit 570007dbecf86db5ddba8d303896d841fc2b2d27.
|
|
|
|
|
| |
This reverts commit b2054b72b7daa89b7ad161c0693befad06a20c90.
It looks like the necessary V8 change has not rolled out everywhere yet.
|
|
|
|
| |
If the target is a bottom type then it is a heap type but it is not a signature
type, and we should treat it as unreachable (and not crash).
|
|
|
|
|
|
|
|
|
|
|
|
| |
#5253 handled the case of just one possible global. It is also possible we have
multiple globals but just one value. This handles that case. (It slightly overlaps
with other passes, but as this pass actually identifies the creations of the objects
in globals, it has a guarantee of success that the others don't, and it is very easy
to just do given all the work done to handle the case of 2 values).
Also fix a minor bug in #5253 - we need to trap if the old reference were null.
That is, we know the reference must point to the only object ever created of
that type, but that is only if it is not null; if it's null we need to trap.
|
|
|
|
|
| |
Including support for parsing field indices. Although only numeric field indices
are supported at the moment, set up the code to make it straightforward to
implement type-dependent symbolic field names in the future.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Expand GlobalStructInference to operate on cases with a single possible global, and
not just 2 or more. Even the case of a single global is useful, it turns out, as we can
alter the reference in places like this:
(struct.get $type 0
(..ref..)
)
No matter what ref is, if there is a single global it must refer to, we can switch to
this:
(struct.get $type 0
(global.get $global)
)
That can unlock further opts later. Note that we can do this even if we don't know
what the value actually is - we may not know what the struct.get returns, but we
do know what it reads from.
|