| Commit message (Collapse) | Author | Age | Files | Lines |
... | |
|
|
|
|
|
|
|
| |
`skipCast` takes an optional parameter that bounds how general the resulting
type is allowed to be. That parameter previously had a default value of `anyref`
with the intention of allowing all casts to be skipped, but that default
inadvertently prevented any casts in the `func` or `extern` type hierarchies
from being skipped. Update `skipCast` so that the default parameter allows all
casts to be skipped in all hierarchies.
|
|
|
|
|
|
|
|
| |
Since we refactored all the old kind-checking instructions to be represented as
general cast instructions, `GCTypeUtils::evaluateKindCheck` had become a
vestigial wrapper around `GCTypeUtils::evaluateCastCheck` that was only used in
RemoveUnusedBrs. Remove `evaluateKindCheck` and use `evaluateCastCheck` in
RemoveUnusedBrs without changing any functionality. A future PR may use the
extra information from `evaluateCastCheck` to further optimize branching casts.
|
| |
|
|
|
| |
Without this we hit an assertion on unreachable not being a heap type.
|
|
|
|
| |
We already handled Success and Failure. Also handle SuccessOnlyIfNull and
SuccessOnlyIfNonNull.
|
|
|
|
|
|
| |
`struct` has replaced `data` in the upstream spec, so update Binaryen's types to
match. We had already supported `struct` as an alias for data, but now remove
support for `data` entirely. Also remove instructions like `ref.is_data` that
are deprecated and do not make sense without a `data` type.
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
These operations are deprecated and directly representable as casts, so remove
their opcodes in the internal IR and parse them as casts instead. For now, add
logic to the printing and binary writing of RefCast to continue emitting the
legacy instructions to minimize test changes. The few test changes necessary are
because it is no longer valid to perform a ref.as_func on values outside the
func type hierarchy now that ref.as_func is subject to the ref.cast validation
rules.
RefAsExternInternalize, RefAsExternExternalize, and RefAsNonNull are left
unmodified. A future PR may remove RefAsNonNull as well, since it is also
expressible with casts.
|
|
|
| |
Add a new evaluateCastCheck() utility and use that in relevant places.
|
| |
|
|
|
| |
Fixes #5406
|
|
|
|
|
|
|
|
| |
Look for definitely-failing casts along all the fallthrough values. Specifically, if any
cast in the middle proves the final cast will fail, then we know we will trap.
Fully optimize redundant casts, considering both the type and the heap type.
Combine a cast with a ref.as_non_null.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* Replace `RefIs` with `RefIsNull`
The other `ref.is*` instructions are deprecated and expressible in terms of
`ref.test`. Update binary and text parsing to parse those instructions as
`RefTest` expressions. Also update the printing and emitting of `RefTest`
expressions to emit the legacy instructions for now to minimize test changes and
make this a mostly non-functional change. Since `ref.is_null` is the only
`RefIs` instruction left, remove the `RefIsOp` field and rename the expression
class to `RefIsNull`.
The few test changes are due to the fact that `ref.is*` instructions are now
subject to `ref.test` validation, and in particular it is no longer valid to
perform a `ref.is_func` on a value outside of the `func` type hierarchy.
|
|
|
|
|
|
|
|
| |
In particular, do not treat the converted value as "falling through" the
conversion. Since the conversions cross type hierarchies, treating the converted
values as fallthrough values would make subsequent casts look like they must
fail, when in fact they may not.
Fixes #5407.
|
|
|
|
|
|
| |
We use TypeUpdater there, which handles updating unreachability. But with wasm GC
we also need to refinalize if we refine types. Somehow, this was not noticed until now,
but the new ref.cast null assertion on not losing type info was enough to uncover
this long-existing issue.
|
| |
|
|
|
|
|
|
|
| |
We were considering casts between unrelated types as unconditionally failing,
but in the case where the unrelated types are nullable, the cast could still
succeed if the value is null.
This bug was introduced in #5397.
|
|
|
|
|
|
| |
Also add some comments on related optimization opportunities.
Also delete a test of a combination of types between hierarchies, which will soon
not be expressible at all in the IR.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The `br_on{_non}_{data,i31,func}` operations are deprecated and directly
representable in terms of the new `br_on_cast` and `br_on_cast_fail`
instructions, so remove their dedicated IR opcodes in favor of representing them
as casts. `br_on_null` and `br_on_non_null` cannot be consolidated the same way
because their behavior is not directly representable in terms of `br_on_cast`
and `br_on_cast_fail`; when the cast to null bottom type succeeds, the null
check instructions implicitly drop the null value whereas the cast instructions
would propagate it.
Add special logic to the binary writer and printer to continue emitting the
deprecated instructions for now. This will allow us to update the test suite in
a separate future PR with no additional functional changes.
Some tests are updated because the validator no longer allows passing non-func
data to `br_on_func`. Doing so has not made sense since we separated the three
reference type hierarchies.
|
|
|
|
|
|
|
|
|
|
| |
ref.as_non_null (#5398)
We were checking the heap type, but now casts care about the nullability as
well.
If the nullability is the only problem, that is, the heap type will be fine but we
might have a null, we can at least switch a ref.cast (non-null) to a
ref.as_non_null.
|
|
|
|
|
|
|
|
|
| |
As well as br_on_cast_fail null. Unlike the existing br_on_cast* instructions,
these new instructions treat the cast as succeeding when the input is a null.
Update the internal representation of the cast type in `BrOn` expressions to be
a `Type` rather than a `HeapType` so it will include nullability information.
Also update and improve `RemoveUnusedBrs` to handle the new instructions
correctly and optimize in more cases.
|
|
|
| |
This fixes an oversight in #5395
|
|
|
|
| |
(#5395)
|
|
|
|
|
|
|
|
|
| |
visitRefCast can use trapOnNonNull. To make this not regress, add fallthrough
analysis there as well.
Minor test changes are due to trapOnNonNull using getDroppedChildren which
only emits drops of necessary children. It also tells us to refinalize so it is ok for it
to change the type to unreachable.
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Parse both the folded and unfolded forms of blocks and structure the code to
make supporting additional block instructions like if-else and try-catch
relatively simple.
Parsing block types is extra fun because they may implicitly define new
signature heap types via a typeuse, but only if their types are not given by a
single result type. To figuring out whether a new type may be introduced in all
the relevant parsing stages, always track at least the arity of parsed results.
The parser parses block labels, but more work will be required to support branch
instructions that use them.
|
|
|
|
|
|
|
| |
As noted in #4739, legacy language emitting nan and infinity
exists, with the observation that it can be removed once asm.js
is no longer used and global NaN is available.
This commit removes that asm.js-specific code accordingly.
|
|
|
|
| |
It is implemented as an import, but functionally it is a call within the
module, so it does not cause types to be public.
|
|
|
|
|
|
|
|
|
|
|
| |
As noted in #4806, trying to optimize past level 0 can result in
passes emitting non-JS code, which is then unable to be converted during
final output.
This commit creates a new targetJS option in PassOptions, which can
be checked inside each pass where non-JS code might be emitted.
This commit initially adds that logic to OptimizeInstructions, where
this issue was first noticed.
|
|
|
|
|
|
|
| |
This new cast configuration was not expressible with the legacy cast
instructions. Although it is valid in Wasm, do not allow nullable casts of
non-nullable references, since those would unnecessarily lose type information.
Convert such casts to be non-nullable during expression finalization.
|
|
|
|
|
|
|
| |
After making the pass error when not in closed world, some testcases
required changes. One forward-looking testcase can just be removed - the
point of it was to see what happens if a type escapes, and now closed-world
errors on it - and another testcase needed to avoid types on the boundary,
and to use anyref+casts.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
We have a data structure there, typeGlobals, which maps types to the list of
globals for that type. Previously we used the convention of not having an
entry in the map to mean that a type is unoptimizable. However, this was not
used consistently, and in fact one place could insert to the map in a dangerous
way: a subtype's global is added to the list of globals of the super, and
typeGlobals[super].add(sub-global) would then effectively make an unoptimizable
super into an optimizable one.
To fix that, check for unoptimizability before propagating sub-globals. We do
still use the convention of not keeping data in typeGlobals for unoptimizable
things as it is a minor optimization to avoid wasted work.
Fixes #5379
|
|
|
|
| |
Without the names section debugging can be hard sometimes, on the binaries
that that mode emits for each pass.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* [NFC][Parser] Track definition indices
For each definition in a module, record that definition's index in the relevant
index space. Previously the index was inferred from its position in a list of
module definitions, but that scheme does not scale to data segments defined
inline inside memory definitions because these data segments occupy a slot in
the data segment index space but do not have their own independent definitions.
* clarify comment
* [Parser] Parse data segments
Parse active and passive data segments, including all their variations and
abbreviations as well as data segments declared inline in memory declarations.
Switch to parsing data strings, memory limits, and memory types during the
ParseDecls phase so that the inline data segments can be completely parsed
during that phase and never revisited. Parsing the inline data segments in a
later phase would not work because they would be incorrectly inserted at the end
of the data segment index space.
Also update the printer to print a memory use on active data segments that are
initialized in a non-default memory.
* [Parser] Parse array creation and data segment instructions
* [Parser] Parse array access instructions
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* [NFC][Parser] Track definition indices
For each definition in a module, record that definition's index in the relevant
index space. Previously the index was inferred from its position in a list of
module definitions, but that scheme does not scale to data segments defined
inline inside memory definitions because these data segments occupy a slot in
the data segment index space but do not have their own independent definitions.
* clarify comment
* [Parser] Parse data segments
Parse active and passive data segments, including all their variations and
abbreviations as well as data segments declared inline in memory declarations.
Switch to parsing data strings, memory limits, and memory types during the
ParseDecls phase so that the inline data segments can be completely parsed
during that phase and never revisited. Parsing the inline data segments in a
later phase would not work because they would be incorrectly inserted at the end
of the data segment index space.
Also update the printer to print a memory use on active data segments that are
initialized in a non-default memory.
* [Parser] Parse array creation and data segment instructions
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* [NFC][Parser] Track definition indices
For each definition in a module, record that definition's index in the relevant
index space. Previously the index was inferred from its position in a list of
module definitions, but that scheme does not scale to data segments defined
inline inside memory definitions because these data segments occupy a slot in
the data segment index space but do not have their own independent definitions.
* clarify comment
* [Parser] Parse data segments
Parse active and passive data segments, including all their variations and
abbreviations as well as data segments declared inline in memory declarations.
Switch to parsing data strings, memory limits, and memory types during the
ParseDecls phase so that the inline data segments can be completely parsed
during that phase and never revisited. Parsing the inline data segments in a
later phase would not work because they would be incorrectly inserted at the end
of the data segment index space.
Also update the printer to print a memory use on active data segments that are
initialized in a non-default memory.
|
|
|
| |
This PR maintains the first memory's import/export in the single combined memory after multi-memories are lowered.
|
|
|
|
|
|
|
|
|
|
|
| |
* [NFC][Parser] Track definition indices
For each definition in a module, record that definition's index in the relevant
index space. Previously the index was inferred from its position in a list of
module definitions, but that scheme does not scale to data segments defined
inline inside memory definitions because these data segments occupy a slot in
the data segment index space but do not have their own independent definitions.
* clarify comment
|
|
|
| |
Moved the assert that checks whether the DataSegment* offset type is Const. This assert only needs to happen when the DataSegment belongs to a memory other than the first.
|
|
|
|
|
|
| |
Doing so can cause us to switch from a private type to a public type and error.
Also refactor export-utils to make this easy.
|
|
|
| |
Fixes #5370
|
|
|
| |
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.
|