| Commit message (Collapse) | Author | Age | Files | Lines |
... | |
| |
|
|
|
|
| |
Also removes experimental SIMD instructions that were not included in the final
spec proposal.
|
|
|
|
|
|
|
|
| |
We must write them to a tuple with nullable types, then fix that up when
reading. This is similar to what we do in handleNonNullableLocals, except
that it operates on the entire tuple type, so it can't share that code.
This fixes a regression from #3710 that was harder to notice by the fuzzer
until now.
|
|
|
|
|
|
|
|
|
|
| |
Makes TypeBuilders growable, adds a `getTempHeapType` method, allows the
`getTemp*Type` methods to take arbitrary temporary or canonical HeapTypes rather
than just an index, and allows BasicHeapTypes to be assigned to TypeBuilder
slots. All of these changes are necessary for the upcoming re-implementation of
equirecursive LUB calculation.
Also adds a new utility to TypeBuilder for using `operator[]` as an intuitive
and readable wrapper around the `getTempHeapType` and `setHeapType` methods.
|
|
|
|
|
|
| |
This PR adds support for `ref.null t` as a valid element segment
item. The abbreviated format of `(elem ... func $f $g...)` is kept in
both printing and binary emitting if all items are `ref.func`s. Public
APIs aren't updated in this PR.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
After this PR we still do not support non-nullable locals. But we no longer
turn all types into nullable upon load. In particular, we support non-nullable
types on function parameters and struct fields, etc. This should be enough to
experiment with optimizations in both binaryen and in VMs regarding non-
nullability (since we expect that optimizing VMs can do well inside functions
anyhow; it's non-nullability across calls and from data that the VM can't be
expected to think about).
Let is handled as before, by lowering it into gets and sets. In addition, we
turn non-nullable locals into nullable ones, and add a ref.as_non_null on
all their gets (to keep the type identical there). This is used not just for
loading code with a let but also is needed after inlining.
Most of the code changes here are removing FIXMEs for allowing
non-nullable types. But there is also code to handle the issues mentioned
above.
Most of the test updates are removing extra nulls that we added before
when we turned all types nullable. A few tests had actual issues, though,
and also some new tests are added to cover the code changes here.
|
|
|
|
|
|
| |
When we can skip function bodies, we still need to parse the start function
for the pthreads case, see details in the comments. This still gives us 99%
of the speedup as the start function is just 1 function and it's not that big,
so with this we return to full speed after the reversion in #3705
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
output (#3698)
When not writing output we don't need debug info, as it is not relevant for
our metadata. This saves loading and interning all the names, which takes
several seconds on massive inputs.
This is possible in principle in other tools, but this does not change anything
in them for now. (We do use names internally in some nontrivial ways without
opting in to it, so that would require further refactoring. Also the other tools
almost always do write an output.)
This is not 100% unobservable. If validation fails then the validation error would
just contain the function index instead of the name from the Names section if
there is one. However finalize does not validate atm so that would only matter
if we change that later.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
After sbc100 's work on EM_ASM and EM_JS they are now parsed from
the wasm using exports etc. and so we no longer need to parse function bodies.
As a result if we are not emitting a wasm from wasm-emscripten-finalize then all we are
doing is scanning global structures like imports and exports and emitting metadata
about them. And indeed we do not need to emit a wasm in some cases, specifically
when not optimizing and when using WASM_BIGINT (to avoid needing to
legalize).
We had considering skipping wasm-emscripten-finalize entirely in that situation,
and instead to parse the metadata from the wasm in python on the emscripten
side. However sbc100 had the brilliant idea today to just skip function bodies.
That is very simple to do - no need to write another parser for wasm, and also
look at how simple this PR is - and also it will be faster to run
wasm-emscripten-finalize in this mode than to run python. (With the only
downside that the bytes of the wasm are loaded even if they aren't parsed; but
almost certainly they are in the disk cache anyhow.)
This PR implements that idea: when wasm-emscripten-finalize knows it will
not write a wasm output, it notes "skip function bodies". The binary reader then
skips the bodies and places unreachables there instead (so that the wasm still
validates).
There are no new tests here because this can't be tested - by design it is an
unobservable optimization. (If we could notice the bodies have been skipped,
we would not have skipped them.) This is also why no changes are needed on
the emscripten side to benefit from this speedup. Basically when binaryen sees
it will not need X, it skips parsing of X automatically.
Benchmarking speed, it is as fast as you'd expect: the wasm-emscripten-finalize
step is 15x faster on SQLite (1MB of wasm) and almost 50x faster on the biggest
wasm I have on my drive (40MB of LLVM). (These numbers are on release
builds, without debug info - debug into makes things slower, so the speedup is
lower there, and will need further work.)
Tested manually and also on wasm0 wasm2 other on emscripten.
|
|
|
|
|
|
| |
This was missing from #3663
Fixes #3656
|
|
|
|
|
|
|
|
|
| |
We handled them as S63 instead of U32. That should be fine, as all U32 values fit
in S63. But it is not strictly correct. The signed encoding may use an additional byte
which is unnecessary, and there is an actual correctness issue where a U32 may
be interpreted as a large negative S63 (because it sign extends a final bit that
happens to be 1).
May help #3656 but that testcase still does not pass even with this.
|
|
|
|
|
|
|
|
|
|
|
| |
Passive element segments do not belong to any table, so the link between
Table and elem needs to be weaker; i.e. an elem may have a table in case
of active segments, or simply be a collection of function references in
case of passive/declarative segments.
This PR takes Table::Segment out and turns it into a first class module
element just like tables and functions. It also implements early support
for parsing, printing, encoding and decoding passive/declarative elem
segments.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
When writing a binary, we take the local indexes in the IR and turn
them into the format in the binary, which clumps them by type. When
writing the names section we should be aware of that ordering, but
we never were, as noticed in #3499
This fixes that by saving the mapping of locals when we are emitting
the name section, then using it when emitting the local names.
This also fixes the order of the types themselves as part of the
refactoring. We used to depend on the ordering of types to decide
which to emit first, but that isn't good for at least two reasons. First,
it hits #3648 - that order is not fully
defined for recursive types. Also, it's not good for code size - we've
ordered the locals in a way we think is best already (ReorderLocals pass).
This PR makes us pick an order of types based on that, as much as
possible, that is, when we see a type for the first time we append it to
a list whose order we use.
Test changes: Some are just because we use a different order than
before, as in atomics64. But some are actual fixes, e.g. in heap-types
where we now have (local $tv (ref null $vector)) which is indeed
right - v there is for vector, and likewise m for matrix etc. - we
just had wrong names before. Another example, we now have
(local $local_externref externref) whereas before the name was
funcref, and which was wrong... seems like the incorrectness was
more common on reference types and GC types, which is why this was
not noticed before.
Fixes #3499
Makes part of #3648 moot.
|
|
|
|
|
|
|
| |
This adds support for reading (elem declare func $foo .. in the text and
binary formats. We can simply ignore it: we don't need to represent it in
IR, rather we find what needs to be declared when writing. That part takes
a little more work, for which this adds a shared helper function.
|
|
|
|
|
|
|
|
|
| |
Note that Binaryen "canonicalizes" the type, so in the test output here
we end up with $grandchild twice. This is a consequence of us not
storing the heap type as an extra field. I can't think of a downside to
this canonicalization, aside from losing perfect roundtripping, but I think
that's a worthwhile tradeoff for efficiency as we've been thinking so far.
Fixes #3636
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Adds support for GC struct fields in the binary format, implementing
WebAssembly/gc#193
No extra tests needed, see the .fromBinary output which shows this working.
This also has a minor fix in the s-parser, we should not always add a name
to the map of index=>name - only if it exists. Without that fix, the binary
emitter would write out null strings.
|
|
|
|
|
|
| |
This adds ValidationBuilder which can allow sharing of builder code that also
validates, between the text and binary parsers. In general we share that code in
the validator, but the validator can only run once IR exists, and in some cases we
can't even emit valid IR structure at all.
|
|
|
| |
This as a consequence of https://reviews.llvm.org/D95651
|
| |
|
|
|
|
| |
(not 100% NFC since it also fixes a bug by moving a line out of
a loop)
|
|
|
|
|
|
| |
Update parsing of binary type sections to use TypeBuilder to support uses before
definitions. Now that both the binary and text parsers support out-of-order type
uses, this PR also relaxes the logic for emitting types to allow uses to be
emitted before definitions.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
singleton (#3581)
The fix here is to remove the code with
// maybe we don't need a block here?
That would remove a try's block if we thought it wasn't needed. However,
it just checked for exception branches, but not normal branches, which are
also possible.
At that location, we don't have a good way to find out if the block has other
branches to it aside from scanning its contents. So this PR just gives up on
doing so, which means we add an unnecessary block if the optimizer is not
run. If this matters we could make the binary parser more complicated by
remembering whether a block had branches in the past, but I'm not sure if
it's worth it.
|
|
|
|
|
|
|
|
|
|
| |
We decided to change `catch_all`'s opcode from 0x05, which is the same
as `else`, to 0x19, to avoid some complicated handling in the tools.
See: https://github.com/WebAssembly/exception-handling/issues/147
lso this contains the original cpp file used to generate
dwarf_with_exceptions.wasm; instructions to generate the wasm from that
cpp file are in the comments.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
I was previously mistaken about `rethrow`'s argument rule and thought
it only counted `catch`'s depth. But it turns out it follows the same
rule `delegate`'s label: the immediate argument follows the same rule as
when computing branch labels, but it only can target `try` labels
(semantically it targets that `try`'s corresponding `catch`); otherwise
it will be a validation failure. Unlike `delegate`, `rethrow`'s label
denotes not where to rethrow, but which exception to rethrow. For
example,
```wasm
try $l0
catch ($l0)
try $l1
catch ($l1)
rethrow $l0 ;; rethrow the exception caught by 'catch ($l0)'
end
end
```
Refer to this comment for the more detailed informal semantics:
https://github.com/WebAssembly/exception-handling/issues/146#issuecomment-777714491
---
This also reverts some of `delegateTarget` -> `exceptionTarget` changes
done in #3562 in the validator. Label validation rules apply differently
for `delegate` and `rethrow` for try-catch. For example, this is valid:
```wasm
try $l0
try
delegate $l0
catch ($l0)
end
```
But this is NOT valid:
```wasm
try $l0
catch ($l0)
try
delegate $l0
end
```
So `try`'s label should be used within try-catch range (not catch-end
range) for `delegate`s.
But for the `rethrow` the rule is different. For example, this is valid:
```wasm
try $l0
catch ($l0)
rethrow $l0
end
```
But this is NOT valid:
```wasm
try $l0
rethrow $l0
catch ($l0)
end
```
So the `try`'s label should be used within catch-end range instead.
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
So far `Try`'s label is only targetted by `delegate`s, but it turns out
`rethrow` also has to follow the same rule as `delegate` so it needs to
target a `Try` label. So this renames variables like
`delegateTargetNames` to `exceptionTargetNames` and methods like
`replaceDelegateTargets` to `replaceExceptionTargets`.
I considered `tryTarget`, but the branch/block counterpart name we use
is not `blockTarget` but `branchTarget`, so I chose `exceptionTarget`.
The patch that fixes `rethrow`'s target will follow; this is the
preparation for that.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This adds support for reading/writing of the new `delegate` instruction
in the folded wast format, the stack IR format, the poppy IR format, and
the binary format in Binaryen. We don't have a formal spec written down
yet, but please refer to WebAssembly/exception-handling#137 and
WebAssembly/exception-handling#146 for the informal semantics. In the
current version of spec `delegate` is basically a rethrow, but with
branch-like immediate argument so that it can bypass other
catches/delegates in between.
`delegate` is not represented as a new `Expression`, but it is rather
an option within a `Try` class, like `catch`/`catch_all`.
One special thing about `delegate` is, even though it is written
_within_ a `try` in the folded wat format, like
```wasm
(try
(do
...
)
(delegate $l)
)
```
In the unfolded wat format or in the binary format, `delegate` serves as
a scope end instruction so there is no separate `end`:
```wasm
try
...
delegate $l
```
`delegate` semantically targets an outer `catch` or `delegate`, but we
write `delegate` target as a `try` label because we only give labels to
block-like scoping expressions. So far we have not given `Try` a label
and used inner blocks or a wrapping block in case a branch targets the
`try`. But in case of `delegate`, it can syntactically only target `try`
and if it targets blocks or loops it is a validation failure.
So after discussions in #3497, we give `Try` a label but this label can
only be targeted by `delegate`s. Unfortunately this makes parsing and
writing of `Try` expression somewhat complicated. Also there is one
special case; if the immediate argument of `try` is the same as the
depth of control flow stack, this means the 'delegate' delegates to the
caller. To handle this case this adds a fake label
`DELEGATE_CALLER_TARGET`, and when writing it back to the wast format
writes it as an immediate value, unlike other cases in which we write
labels.
This uses `DELEGATE_FIELD_SCOPE_NAME_DEF/USE` to represent `try`'s label
and `delegate`'s target. There are many cases that `try` and
`delegate`'s labels need to be treated in the same way as block and
branch labels, such as for hashing or comparing. But there are routines
in which we automatically assume all label uses are branches. I thought
about adding a new kind of defines such as
`DELEGATE_FIELD_TRY_NAME_DEF/USE`, but I think it will also involve some
duplication of existing routines or classes. So at the moment this PR
chooses to use the existing `DELEGATE_FIELD_SCOPE_NAME_DEF/USE` for
`try` and `delegate` labels and makes only necessary amount of changes
in branch-utils. We can revisit this decision later if necessary.
Many of changes to the existing test cases are because now all `try`s
are automatically assigned a label. They will be removed in
`RemoveUnusedNames` pass in the same way as block labels if not targeted
by any delegates.
This only supports reading and writing and has not been tested against
any optimization passes yet.
---
Original unfolded wat file to generate test/try-delegate.wasm:
```wasm
(module
(event $e)
(func
try
try
delegate 0
catch $e
end)
(func
try
try
catch $e
i32.const 0
drop
try
delegate 1
end
catch $e
end
)
)
```
|
|
|
| |
Adds support for modules with multiple tables. Adds a field for the table name to `CallIndirect` and updates the C/JS APIs accordingly.
|
|
|
|
|
|
|
| |
Unordered maps will hash the pointer, while ordered ones will compare the
strings to find where to insert in the tree. I cannot confirm a speedup in time
from this, though others can, but I do see a consistent improvement of a
few % in perf stat results like number of instructions and cycles (and those
results have little noise). And it seems logical that this could be faster.
|
|
|
|
|
|
|
|
| |
As proposed in https://github.com/WebAssembly/simd/pull/395. Note that the other
instructions in the proposal have not been implemented in LLVM or in V8, so
there is no need to implement them in Binaryen right now either. This PR
introduces a new expression class for the new instructions because they uniquely
take an immediate argument identifying which portion of the input vector to
widen.
|
|
|
|
|
|
|
|
|
|
|
| |
This is only partial support, as br_on_null also has an extra optional
value in the spec. Implementing that is cumbersome in binaryen, and
there is ongoing spec discussions about it (see
https://github.com/WebAssembly/function-references/issues/45 ), so
for now we only support the simple case without the default value.
Also fix prefixed opcodes to be LEBs in RefAs, which was noticed here
as the change here made it noticeable whether the values were int8 or
LEBs.
|
|
|
|
|
|
| |
This is different than the other RefAs variants in that it is part of the
typed functions proposal, and not GC. But it is part of GC prototype 3.
Note: This is not useful to us yet as we don't support non-nullable types.
|
|
|
|
|
|
|
|
| |
This expands the existing BrOnCast into BrOn that can also handle the
func/data/i31 variants. This is not as elegant as RefIs / RefAs in that BrOnCast
has an extra rtt field, but I think it is still the best option. We already have optional
fields on Break (the value and condition), so making rtt optional is not odd. And
it allows us to share all the behavior of br_on_* which aside from the cast or the
check itself, is identical - returning the value if the branch is not taken, etc.
|
|
|
|
|
|
|
| |
As a result, we cannot handle a br_on_cast with an unreachable RTT. The
binary format solves the problem by ignoring unreachable code, and this makes
the text format do the same.
A nice benefit of this is that we can remove the castType extra field.
|
|
|
|
|
|
| |
wasm-finalize currently makes byte-wise copies of section data in the
user and data sections. If the section is large, that's extraordinarily
expensive. With a memcpy instead I see a speedup of 1.6 for a large
wasm binary with DWARF data.
|
|
|
|
|
|
|
|
| |
These are similar to is, but instead of returning an i32 answer, they trap on
an invalid value, and return it otherwise.
These could in theory be in a single RefDoThing, with opcodes for both As
and Is, but as the return values are different, that would be a little odd, and
the name would be less clear.
|
| |
|
|
|
|
|
|
|
|
| |
This internal refactoring prepares us for ref.is_func/data/i31, by renaming
the node and adding an "op" field. For now that field must always be "Null"
which means it is a ref.is_null.
This adjusts the C API to match the new IR shape. The high-level JS API
is unchanged.
|
|
|
|
|
|
|
|
| |
We now have multiple catches in each try, and a possible catch-all.
This changes our "extra delimiter" storage to store either an "else"
(unchanged from before) or an arbitrary list of things - we use that
for catches.
|
|
|
|
|
|
|
|
|
|
| |
The binary spec
(https://docs.google.com/document/d/1yAWU3dbs8kUa_wcnnirDxUu9nEBsNfq0Xo90OWx6yuo/edit#)
lists `dataref` after `i31ref`, and `dataref` also comes after `i31ref`
in its binary code in the value-increasing order. This reorders these
two in wasm-type.h and other places, although in most of those places
the order is irrelevant.
This also adds C and JS API for `dataref`.
|
|
|
|
|
| |
Some fields were removed, see
https://docs.google.com/document/d/1yAWU3dbs8kUa_wcnnirDxUu9nEBsNfq0Xo90OWx6yuo/edit#
|
|
|
| |
This removes `exnref` type and `br_on_exn` instruction.
|
|
|
|
|
| |
This is not 100% of everything, but is enough to get tests passing, which
includes full binary and text format support, getting all switches to compile
without error, and some additions to InstrumentLocals.
|
|
|
|
| |
As proposed in https://github.com/WebAssembly/simd/pull/383, with opcodes
coordinated with the WIP V8 prototype.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
For now we don't support non-nullability, and can therefore lower a let into
simpler things. That is,
(let $x = ...
;;
)
=>
(block
$x = ...
;;
)
This lets us handle wasm binaries with let, so that we can optimize them
(with the current downside of losing non-nullability).
This is still not trivial to do, sadly, because the indexing of lets is somewhat
odd in the binary. A let modifies the indexes of other things declared before it,
which means that index "0" means different things at different times. And this
is trickier for us because we add more locals as needed for tuples and stacky
code. So this PR makes us track the absolute local indexes from which each
let started to allocate its locals.
The binary testcase was created from this wat using wasp:
(module
(type $vector (array (field (mut f64))))
(func $main
(local $x i32)
(local $y i32)
(drop (local.get $x)) ;; 0 is the index appearing in the binary
;; first let
(array.new_with_rtt $vector
(f64.const 3.14159)
(i32.const 1)
(rtt.canon $vector)
)
(let (local $v (ref $vector))
(drop (local.get $v)) ;; 0
(drop (local.get $x)) ;; 1
;; another one, nested
(array.new_with_rtt $vector
(f64.const 1234)
(i32.const 2)
(rtt.canon $vector)
)
(let (local $w (ref $vector))
(drop (local.get $v)) ;; 1
(drop (local.get $w)) ;; 0
(drop (local.get $x)) ;; 2
)
)
;; another one, later
(array.new_with_rtt $vector
(f64.const 2.1828)
(i32.const 3)
(rtt.canon $vector)
)
(let (local $v (ref $vector))
(drop (local.get $v)) ;; 0
(drop (local.get $x)) ;; 1
)
(drop (local.get $x)) ;; 0
)
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This updates `try`-`catch`-`catch_all` and `rethrow` instructions to
match the new spec. `delegate` is not included. Now `Try` contains not a
single `catchBody` expression but a vector of catch
bodies and events.
This updates most existing routines, optimizations, and tests modulo the
interpreter and the CFG traversal. Because the interpreter has not been
updated yet, the EH spec test is temporarily disabled in check.py. Also,
because the CFG traversal for EH is not yet updated, several EH tests in
`rse_all-features.wast`, which uses CFG traversal, are temporarily
commented out.
Also added a few more tests in existing EH test functions in
test/passes. In the previous spec, `catch` was catching all exceptions
so it was assumed that anything `try` body throws is caught by its
`catch`, but now we can assume the same only if there is a `catch_all`.
Newly added tests test cases when there is a `catch_all` and cases there
are only `catch`es separately.
|
|
|
|
|
|
|
|
|
| |
Noticed by comparing to V8 and Wasp. After this things are almost identical,
but there is also at least https://bugs.chromium.org/p/v8/issues/detail?id=11300
Test updates are due to having an instruction with two operands of which one is
unreachable. The new order puts the non-unreachable first, so it is not removed by
round-tripping through the binary format like before (which removes all unreachable
code).
|
|
|
|
|
|
|
|
|
|
|
|
| |
This lets us parse (ref null i31) and (ref i31) and not just i31ref.
It also fixes the parsing of i31ref, making it nullable for now, which we
need to do until we support non-nullability.
Fix some internal handling of i31 where we had just i31ref (which meant we
just handled the non-nullable type).
After fixing a bug in printing (where we didn't print out (ref null i31)
properly), I found some a simplification, to remove TypeName.
|
|
|
|
| |
As proposed in https://github.com/WebAssembly/simd/pull/352, using the opcodes
used in the LLVM and V8 implementations.
|