| Commit message (Collapse) | Author | Age | Files | Lines |
|
|
|
|
|
|
| |
together (#3647)
Names of structurally identical types end up "collapsed" together after the
types are canonicalized, but with this PR we can properly read content that
has structurally identical types with different names.
|
|
|
| |
catch_all should not have a pop.
|
| |
|
|
|
| |
This updates them to be correct in the current spec and prototype v3.
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
| |
|
|
|
|
|
|
|
|
|
|
| |
1. Ignore the fake delegate target in the unique name mapper. The mapper
is run after inlining, so this fixes inlining into a function that has a delegate
to the caller.
2. Do not inline a function with a delegate. We should support this eventually,
but for now I think this is good enough.
After this Inlining should be safe to run on exceptions code.
|
|
|
|
|
|
|
|
|
| |
The old code here just referred to Block and Loop. Refactor it to use the
generic helper code that also handles Try.
Also add validation of Try names in the validator.
The testcase here would have $label appear twice before this fix. After
the fix there is $label0 for one of them.
|
|
|
|
|
|
|
|
| |
Previously we assumed catch body's size should be at least 3: `catch`
keyword, event name, and body. But catch's body can be empty when the
event's type is none. This PR fixes the bug and allows empty catch
bodies to be parsed correctly.
Fixes #3629.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This PR makes the TypeBuilder move self-referential HeapTypes to global HeapType
store so that they are considered canonical. This means that when a HeapType is
constructed with an identical definition, it will be equivalent to the original
HeapType constructed by the TypeBuilder, but it does _not_ mean that
self-referential HeapTypes are deduplicated.
This fixes a bug in which two versions of each self-referential function
signature were emitted. Before this PR, the global HeapType store was not aware
of the original self-referential HeapTypes. When the function signatures were
used to construct HeapTypes during type collection, new HeapTypes were allocated
and emitted in addition to the original HeapTypes. Now the global HeapType store
returns the original HeapTypes, so the extra HeapType is never allocated.
|
|
|
|
|
| |
As a readability improvement, use an enum with `Polymorphic` and `Fixed`
variants to represent the polymorphic behavior of StackSignatures rather than a
`bool uneachable`.
|
|
|
|
|
| |
Also fixes a few locations in Print.cpp where types were being printed directly
rather than going through the s-expression type printer and removes vestigial
wrapper types that were no longer used.
|
| |
|
|
|
| |
Uses BinaryenIndex instead of int to mirror parameter types in table construction, and adds setters for name, initial and max.
|
| |
|
| |
|
|
|
|
|
|
| |
We can just iterate on children using the standard order as in
delegates.h, as used by the binary format as well. The only
exceptions are control flow instructions which need some
special handling.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
When the type section is emitted, types with an equal amount of references are
ordered by an arbitrary measure of simplicity, which previously would infinitely
recurse on structurally equivalent recursive types. Similarly, calculating
whether an recursive type was a subtype of another recursive type could have
infinitely recursed. This PR avoids infinite recursions in both cases by
switching the algorithms from using normal inductive recursion to using
coinductive recursion. The difference is that while the inductive algorithms
assume the relations do not hold for a pair of HeapTypes until they have been
exhaustively shown to hold, the coinductive algorithms assume the relations hold
unless a counterexample can be found.
In addition to those two algorithms, this PR also implement name generation for
recursive types, using de Bruijn indices to stand in for inner uses of the
recursive types.
There are additional algorithms that will need to be switched from inductive to
coinductive recursion, such as least upper bound generation, but these presented
a good starting point and are sufficient to get some interesting programs
working end-to-end.
|
|
|
| |
Also add a missing source file for a GC test, let.wasm.
|
|
|
| |
This as a consequence of https://reviews.llvm.org/D95651
|
| |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Updates TypeBuilder to support recursive types. Recursive types are particularly
problematic because under the current scheme it is necessary to canonicalize the
uses of a type's immediate children before canonicalizing the type itself to
avoid leaking non-canonical, temporary types out of the TypeBuilder and into the
global type stores. In the case of recursive types, it is not possible to do
this because of their cyclic nature. In principle this could be overcome by
hashing recursive types based on their structure rather than their contents, but
that would be complicated. Instead, this PR takes the shortcut of not
canonicalizing self-referential HeapTypes at all, but rather moving them out of the
TypeBuilder and into the global type store without changing their addresses or
needing to update any of their use sites. This breaks all cycles and makes it
possible to canonicalize the other types correctly.
Note that this PR only adds support for building recursive types. Doing almost
anything with the types, such as printing, comparing, or emitting them will
certainly lead to infinite recursions. A follow up PR will update all these
operations to work correctly with recursive types.
|
|
|
|
|
|
|
|
|
|
| |
Before this we would assert on hashing e.g. (br $x) by itself, without the
context so we recognized the name $x. Somehow that was not an issue
until delegate, we just happened to not hash such things. I believe I remember
that @aheejin noticed this issue before, but given we didn't have a testcase,
we deferred fixing it - now is the time, I guess, as with delegate it is easy to
get e.g. CodeFolding to hash a Try with a delegate.
Issue found by emscripten-core/emscripten#13485
|
|
|
|
|
|
| |
We were missing a pop of catchIndexStack at a Delegate. It ends the scope,
so it should do that, like TryEnd does.
Found by emscripten-core/emscripten#13485 on -O2.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This adds a TypeNames entry to modules, which can store names for types. So
far this PR uses that to store type names from text format. Future PRs will add
support for field names and for the binary format.
(Field names are added to wasm.h here to see if we agree on this direction.)
Most of the work here is threading a module through the various functions in
Print.cpp. This keeps the module optional, so that we can still print an
expression independently of a module, which has always been the case, and
which I think we should keep (but, if a module was mandatory perhaps this
would be a little simpler, and could be refactored into a form that depends on
that).
99% of this diff are test updates, since almost all our tests use the text
format, and many of them specify a type name but we used to ignore it.
This is a step towards a proper solution for #3589
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
(#3594)
This was an unfortunate case of the order of execution of call
arguments. link(self->currBasicBlock, self->startBasicBlock()) would
run the call first, which sets currBasicBlock, so we'd end up with
the same value for both parameters.
Without this fix, the testcase would drop the result of the call,
as it thought it had no uses.
Also improve debug logging here a tiny bit.
Found by emscripten-core/emscripten#13485
|
|
|
|
|
|
| |
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.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Traverses the module to find type definitions and uses a TypeBuilder to
construct the corresponding HeapTypes rather than constructing them directly.
This allows types to be used in the definitions of other types before they
themselves are defined, which is an important step toward supporting recursive
types. After this PR, no further text parsing changes will be necessary to
support recursive types.
Beyond allowing types to be used before their definitions, this PR also makes a
couple incidental changes to the parser's behavior. First, compound heaptypes
can now only be declared in `(type ...)` elements and cannot be declared inline
at their site of use. This reduces the flexibility of the parser, but is in line
with what the text format spec will probably look like eventually (see
https://github.com/WebAssembly/function-references/issues/42).
The second change is that `(type ...)` elements are now all parsed before `(func
...)` elements rather than in text order with them, so the type indices will be
different and wasts using numeric type indices will be broken. Note however,
that we were already not completely spec compliant in this regard because we
parsed types defined by `(type...)` and `(func...)` elements before types
defined by the type uses of `call_indirect` instructions.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
When types or heap types were used multiple times in a TypeBuilder instance, it
was possible for the canonicalization algorithm to canonicalize a parent type
before canonicalizing all of its component child types, leaking the temporary
types into globally interned types. This bug led to incorrect canonicalization
results and use-after free bugs.
The cause of the bug was that types were canonicalized in the reverse of the
order that they were visited in, but children were visited after the first
occurrence of their parents, not necessarily after the last occurrence of their
parents. One fix could have been to remove the logic that prevented types from
being visited multiple times so that children would always be visited after
their parents. That simple fix, however, would not scale gracefully to handle
recursive types because it would require some way to detect recursions without
accidentally reintroducing these bugs.
This PR implements a more robust solution: topologically sorting the traversed
types to ensure that children are canonicalized before their parents. This
solution will be trivial to adapt for recursive types because recursive types
are trivial to detect from the reachability graph used to perform the
topological sort.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The delegate field of Try was not being scanned, so we could
remove a name that was used only by a delegate.
The bug was because visitTry overrides the generic visitor
visitExpression. So we need to call it manually. Sadly the code here
was pretty old (I probably wrote it back in 2015 or so) and it was
misleading, as it had unnecessary calls from the generic visitor
to visitBlock, visitLoop, which are not needed. This PR removes
them which is shorter and cleaner.
Also, we must handle the case of the delegate field being unset,
so check name.is().
|
|
|
|
| |
Also refactor away some annoying repeated code in that pass. visitTry is
the only actual change.
|
|
|
|
|
|
|
|
| |
The new spec does not have `exnref` so EH does not have dependency of
the reference types proposal anymore.
exception_handling_target_feature.wasm's contents are the same except
previously its target features section contained both reference-types
and exception-handling but now it only has exception-handling.
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
| |
|
|
|
|
|
|
|
|
|
|
| |
This updates C and binaryen.js API to match the new `Try` structure to
support `delegate`, added in #3561. Now `try` can take a name (which can
be null) like a block, and also has an additional `delegateTarget` field
argument which should only be used for try-delegate and otherwise null.
This also adds several more variant of `makeTry` methods in
wasm-builder. Some are for making try-delegate and some are for
try-catch(_all).
|
| |
|
|
|
|
|
| |
That is, if a wasm says "simd", it is ok to let the user specify simd
as well as more features, and the the optimizer can perhaps do something
with them.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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
)
)
```
|
|
|
|
|
|
|
| |
If we find a data segment whose entire contents is EM_JS or EM_ASM
strings then strip it from the binary.
See: https://github.com/emscripten-core/emscripten/pull/13443
|
|
|
|
|
|
|
|
| |
Add a utility for calculating the least upper bounds of two StackSignatures,
taking into account polymorphic unreachable behavior. This will important in the
finalization and validation of Poppy IR blocks, where a block is allowed to
directly produce fewer values than the branches that target it carry if the
difference can be made up for by polymorphism due to an unreachable instruction
in the block.
|