| Commit message (Collapse) | Author | Age | Files | Lines |
... | |
|
|
|
| |
The validation code can be further simplified after adding castType, and
we were missing a test for the type that flows out of br_on_cast.
|
| |
|
|
|
|
|
|
|
|
|
| |
`ModuleSplitter::thunkExportedSecondaryFunctions` creates a thunk for each
secondary function that needs to be exported from the main module. Previously,
if a secondary function was exported twice, this code would try to create two
thunks for it rather than just making one thunk and exporting it twice. This
caused a fatal error because the second thunk had the same name as the first
thunk and therefore could not be added to the module. This PR fixes the issue by
creating no more than one thunk per function.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The tricky part here, as pointed out by aheejin in my previous attempt, is that
we need to know the type of the value we send if the branch is taken. We can
normally calculate that from the rtt parameter's type - we are casting to that
RTT, so we know what type that is - but if the rtt is unreachable, that's a problem.
To fix that, store the cast type on BrOnCast instructions.
This includes a test with a br_on_cast that succeeds and sends the cast value,
one that fails and passes through the uncast value, and also of one with an
unreachable RTT.
This includes a fix for Precompute, as noticed by that new test. If a break is
taken, with a ref as a value, we can't precompute it - for the same reasons
we can't precompute a ref in general, that it is a pointer to possibly shared
data.
|
|
|
|
|
|
|
|
| |
Adds an option to wasm-split to allow the user to specify the initial table size
for both instrumenting and splitting use cases. In the short term this will be
used in Emscripten SPLIT_MODULE + dynamic linking workflow to ensure that the
expected table size baked into the JS works for both the instrumented and the
primary split modules. In the longer term this may be replaced with a more
elegant mechanism for making the JS works in both cases.
|
|
|
|
|
|
|
|
| |
This avoids needing to add include wasm-printing if a file doesn't already have it.
To achieve that, add the std::ostream hooks in wasm.h, and also use them
when possible, removing the need for the special WasmPrinter object.
Also stop printing in "full" (print types on each line) in error messages by default. The
user can still get that, as always, using BINARYEN_PRINT_FULL=1 in the env.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This is almost NFC, but it may emit slightly different IR in cases that
don't matter much. Specifically,
(block (result i32) ;; can also be unreachable
(unreachable)
(i32.const 1)
)
That can be finalized to have type unreachable or i32, as both are
valid. After this PR we should consistently do the same thing in all
places. (Either option would be ok - we prefer to keep the type if
there is one.)
In practice, DCE will remove all the dead code anyhow, leaving no
difference to matter. However, the IR is different without DCE, and
that may be noticeable in an unoptimized build - but it should have
no effect on behavior, just on the binary.
|
|
|
|
|
|
|
|
|
| |
During module splitting, a map is constructed from internal names to their
corresponding export names. This code previously did not take into account the
fact that the same internal name may be used by different kinds of
entities (e.g. a table and a memory may have the same internal name), which
resulted in the secondary module incorrectly using the same import name for all
of the entities that shared an internal name. This PR fixes the problem by
including the ExternalKind of the entity in the keys of the export map.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This adds info to RTT literals so that they can represent the chain of
rtt.canon/sub commands that generated them, and it adds an internal
RTT for each GC allocation (array or struct).
The approach taken is to simply store the full chain of rtt.sub types
that led to each literal. This is not efficient, but it is simple and seems
sufficient for the semantics described in the GC MVP doc - specifically,
only the types matter, in that repeated executions of rtt.canon/sub
on the same inputs yield equal outputs.
This PR fixes a bunch of minor issues regarding that, enough to allow testing
of the optimization and execution of ref.test/cast.
|
|
|
|
|
| |
Previously we were using bools for both of these concepts, but using enums makes
the code clearer. In particular, the PR removes many instances of
`/*nullability=*/ true`.
|
|
|
|
|
| |
(#3431)
Also improved the LLD test scripts to accomodate 64-bit tests.
|
|
|
|
|
| |
Add Names::getValidGlobalName calls to ensure we don't collide with an existing name.
Fixes emscripten-core/emscripten#12834
|
|
|
| |
This should make it easier to add br_on_cast for example.
|
|
|
|
| |
Passing --detect-features there doesn't work (as there is no feature
section).
|
|
|
|
|
|
| |
- i64x2.eq (https://github.com/WebAssembly/simd/pull/381)
- i64x2 widens (https://github.com/WebAssembly/simd/pull/290)
- i64x2.bitmask (https://github.com/WebAssembly/simd/pull/368)
- signselect ops (https://github.com/WebAssembly/simd/pull/124)
|
|
|
|
|
|
|
|
|
|
|
| |
Precompute still tried to precompute a reference because the check was
not in the topmost place.
Also we truncated i8/i16 values, but did not extend them properly. That
was also an issue with structs.
The new test replaces the old one by moving from -O1 to -Oz (which
runs more opts, and would have noticed this earlier), and adds array
operations too, including sign extends.
|
|
|
|
| |
This adds enough to read and write them and test that, but leaves
interpreter support for later.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Introduce TypeBuilder, a utility for constructing heap types in terms of other
heap types that may have not yet been defined. Internally, it works by creating
HeapTypes backed by mutable HeapTypeInfos owned by the TypeBuilder. That lets
the TypeBuilder create temporary Types that can refer to the TypeBuilder-managed
HeapTypes. Those temporary Types can in turn be used to initialize the very
HeapTypes they refer to. Since the TypeBuilder-managed HeapTypes are only valid
for the lifetime of their TypeBuilder, there is a canonicalization step that
converts them into globally interned canonical HeapTypes.
This PR allows HeapTypes to be built in terms of as of yet undefined HeapTypes,
but it currently errors out in the presence of recursive types. Supporting
recursive types will require further work to canonicalize them into finite,
acyclic representations. Currently any attempt to compare, print, or otherwise
manipulate recursive types would infinitely recurse.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
array.new/get/set/len - pretty straightforward after structs and all the
infrastructure for them.
Also fixes validation of the unnecessary heapType param in the
text and binary formats in structs as well as arrays.
Fixes printing of packed types in type names, which emitted i32
for them. That broke when we emitted the same name for an array
of i8 and i32 as in the new testing here.
Also fix a bug in Field::operator< which was wrong for packed
types; again, this was easy to notice with the new testing.
|
| |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
With struct.new read/write support, we can start to do interesting
things! This adds a test of creating a struct and seeing that references
behave like references, that is, if we write to the value X refers to, and
if Y refers to the same thing, when reading from Y's value we see the
change as well.
The test is run through all of -O1, which uncovered a minor issue in
Precompute: We can't try to precompute a reference type, as we can't
replace a reference with a value.
Note btw that the test shows the optimizer properly running
CoalesceLocals on reference types, merging two locals.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This adds rtt.canon and rtt.sub together with RTT type support
that is necessary for them. Together this lets us test roundtripping the
instructions and types.
Also fixes a missing traversal over globals in collectHeapTypes,
which the example from the GC docs requires, as the RTTs are in
globals there.
This does not yet add full interpreter support and other things. It
disables initial contents on GC in the fuzzer, to avoid the fuzzer
breaking.
Renames the binary ID for exnref, which is being removed from
the spec, and which overlaps with the binary ID for rtt.
|
|
|
|
|
|
|
|
|
| |
Interns HeapTypes using the same patterns and utilities already used to intern
Types. This allows HeapTypes to efficiently be compared for equality and hashed,
which may be important for very large struct types in the future. This change
also has the benefit of increasing symmetry between the APIs of Type and
HeapType, which will make the developer experience more consistent. Finally,
this change will make TypeBuilder (#3418) much simpler because it will no longer
have to introduce TypeInfo variants to refer to HeapTypes indirectly.
|
|
|
|
|
|
|
|
|
|
| |
Mostly straightforward after struct.get.
This renames the value field in struct.get to ref. I think this makes
more sense because struct.set has both a reference to a thing, and a
value to set onto that thing. So calling the former ref seems more
consistent, giving us ref, value. This mirrors load/store for example
where we use ptr, value, and ref is playing the role of ptr here
basically.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This is the first instruction that uses a GC Struct or Array, so it's where
we start to actually need support in the interpreter for those values, which
is added here.
GC data is modeled as a gcData field on a Literal, which is just a
Literals. That is, both a struct and an array are represented as an
array of values. The type which is alongside would indicate if it's a
struct or an array. Note that the data is referred to using a shared_ptr
so it should "just work", but we'll only be able to really test that once we
add struct.new and so can verify that references are by reference and
not value, etc.
As the first instruction to care about i8/16 types (which are only possible
in a Struct or Array) this adds support for parsing and emitting them.
This PR includes fuzz fixes for some minor things the fuzzer found, including
some bad printing of not having ResultTypeName in necessary places
(found by the text format roundtripping fuzzer).
|
|
|
|
|
|
| |
Compiling scipy requires a `NUM_PARAMS` of at least 61 (!)
https://github.com/iodide-project/pyodide patches emsdk in order
to compile, which this PR can avoid.
|
|
|
|
|
|
| |
This adds support in the text and binary format handling, which allows us
to have a full test of reading and writing the types.
This also adds a "name" field to struct fields, which the text format supports.
|
|
|
|
|
|
|
|
|
|
|
| |
With PIC + threads the offset of passive segments is not constant but
relative to `__memory_base`.
When trying to find passive segment offset based on the target of the
`memory.init` instruction we need to consider this possibility as well as
the regular constant one.
For the llvm side of this that generates the calls to memory.init
see: https://reviews.llvm.org/D92620
|
| |
|
|
|
|
|
|
| |
This is shorter by using ChildIterator, so it's more general. The greater generality
should not be noticeable atm, but it will allow this pass to just work on GC
instructions once we have them (without this, in my testing the optimizer does not
do very well on GC).
|
|
|
|
|
|
| |
In relocable code (MAIN/SIDE modules) we use the start function to run
`__wasm_init_memory` which loads the data segments into place. We
can't call get_sbkr pointer during that function because the sbrk
pointer itself lives in static data segment.
|
|
|
|
|
|
|
|
|
| |
This will allow writing GC types in the future, which are non-signature
heap types.
To allow this PR to work, it adds operator< for HeapType so that it
can be used in the data structures that collect uses.
Drive-by fix of a weird hack with sending a Name* in Print.
|
|
|
|
| |
This name is more descriptive and paves the way toward interning HeapTypes as
well, with an analogous BasicHeapType.
|
|
|
|
|
|
|
|
|
| |
I am starting to write lines like
curr->value->type.getHeapType().getStruct().fields[curr->index].type
and it scares me to think that there may be copies going on there.
The issue is that Types are interned, but HeapTypes and their components
are not.
|
|
|
|
|
| |
See https://reviews.llvm.org/D91803 - there are now -1 or -2 in places that
mark something that the linker removed as not existing/not relevant. We should
ignore those like we ignore 0s there.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The code there looks for a "sign-extend": (x << a) >> b where the
right shift is signed. If a = b = 24 for example then that is a sign
extend of an 8-bit value (it works by shifting the 8-bit value's sign bit
to the position of the 32-bit value's sign bit, then shifting all the way
back, which fills everything above 8 bits with the sign bit). The tricky
thing is that in some cases we can handle a != b - but we forgot a
place to check that. Specifically, a repeated sign-extend is not
necessary, but if the outer one has extra shifts, we can't do it.
This is annoyingly complex code, but for purposes of reviewing this
PR, you can see (unless I messed up) that the only change is to
ensure that when we look for a repeated sign extend, then we
only optimize that case when there are no extra shifts. And a
repeated sign-extend is obviously ok to remove,
(((x << a) >> a) << a) >> a => (x << a) >> a
This is an ancient bug, showing how hard it can be to find certain
patterns either by fuzzing or in the real world...
Fixes #3362
|
|
|
|
| |
unreachable (#3413)
|
|
|
|
|
|
|
|
|
|
| |
Defined types in wasm are really one of the "heap types": a signature type, or
(with GC) a struct or an array type. This refactors the binary and text parsers
to load the defined types into an array of heap types, so that we can start to
parse GC types. This replaces the existing array of signature types (which
could not support a struct or an array).
Locally this PR can parse and print as text simple GC types. For that it was
necessary to also fix Type::getFeatures for GC.
|
|
|
|
|
|
|
|
|
|
|
| |
Calculate a checksum of the original uninstrumented module and emit it as part
of the profile data. When reading the profile, compare the checksum it contains
to the checksum of the module that is being split. Error out if the module being
split is not the same as the module that was originally instrumented.
Also fixes a bug in how the profile data was being read. When `char` is signed,
bytes read from the profile were being incorrectly sign extended. We had not
noticed this before because the profiles we have tested have contained only
small-valued counts.
|
|
|
|
|
|
|
|
|
|
| |
Extend the splitting logic to handle splitting modules with a single table
segment with a non-const offset. In this situation the placeholder function
names are interpreted as offsets from the table base global rather than absolute
indices into the table. Since addition is not allowed in segment offset
expressions, the secondary module's segment must start at the same place as the
first table's segment. That means that some primary functions must be duplicated
in the secondary segment to fill any gaps. They are exported and imported as
necessary.
|
|
|
|
|
|
|
|
|
|
|
|
| |
For a nested type, we used to print e.g.
(param $x (ref (func (param i32))))
Instead of expanding the full type inline, which can get long for
a deeply nested type, print a name when running the Print pass.
In this example that would be something like
(param $x (ref $i32_=>_none))
|
|
|
|
| |
values (#3399)
|
|
|
|
| |
Use matchers and more descriptive variable names to clarify the intent of the
functions for finding and inspecting sign extension patterns.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
bugs (#3401)
* Count signatures in tuple locals.
* Count nested signature types (confirming @aheejin was right, that was missing).
* Inlining was using the wrong type.
* OptimizeInstructions should return -1 for unhandled types, not error.
* The fuzzer should check for ref types as well, not just typed function references,
similar to what GC does.
* The fuzzer now creates a function if it has no other option for creating a constant
expression of a function type, then does a ref.func of that.
* Handle unreachability in call_ref binary reading.
* S-expression parsing fixes in more places, and add a tiny fuzzer for it.
* Switch fuzzer test to just have the metrics, and not print all the fuzz output which
changes a lot. Also fix noprint handling which only worked on binaries before.
* Fix Properties::getLiteral() to use the specific function type properly, and make
Literal's function constructor require that, to prevent future bugs.
* Turn all input types into nullable types, for now.
|
|
|
|
|
|
|
| |
Although there is only one "type store" right now, a subsequent PR will add a
new "TypeBuilder" class that manages its own universe of temporary types. Rather
than duplicate all the logic behind type creation and canonicalization, it makes
more sense to encapsulate that logic in a class that TypeBuilder will be able to
reuse.
|
|
|
|
|
|
| |
Read the profiles produced by wasm-split's instrumentation to guide splitting.
In this initial implementation, all functions that the profile shows to have
been called are kept in the initial module. In the future, users may be able to
tune this so that functions that are run later will still be split out.
|
|
|
|
|
|
|
|
| |
Includes minimal support in various passes. Also includes actual optimization
work in Directize, which was easy to add.
Almost has fuzzer support, but the actual makeCallRef is just a stub so far.
Includes s-parser support for parsing typed function references types.
|
|
|
|
| |
These can be simply raw pointers, given that they are stored in modules
using `unique_ptr`s.
|