diff options
author | Alon Zakai <azakai@google.com> | 2024-07-18 11:21:23 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-07-18 11:21:23 -0700 |
commit | b91966f7999175e8b03da9a7a9dcdb07e4749fb1 (patch) | |
tree | 10e8a1decebfb26ff32bd0d28a5ed5c343aa8f04 /src/wasm/wasm-binary.cpp | |
parent | 08436189d3f35c958872b34bf8b0a8575e186d27 (diff) | |
download | binaryen-b91966f7999175e8b03da9a7a9dcdb07e4749fb1.tar.gz binaryen-b91966f7999175e8b03da9a7a9dcdb07e4749fb1.tar.bz2 binaryen-b91966f7999175e8b03da9a7a9dcdb07e4749fb1.zip |
Monomorphize all the things (#6760)
Previously call operands were monomorphized (considered as part of the
call context, so we can create a specialized function with those operands
fixed) if they were constant or had a different type than the function
parameter's type. This generalizes that to pull in pretty much all the code
we possibly can, including nested code. For example:
(call $foo
(struct.new $struct
(i32.const 10)
(local.get $x)
(local.get $y)
)
)
This can turn into
(call $foo_mono
(local.get $x)
(local.get $y)
)
The struct.new and even one of the struct.new's children is moved into the
called function, replacing the original ref argument with two other ones. If the
original called function was this:
(func $foo (param $ref (ref ..))
..
)
then the monomorphized function then looks like this:
(func $foo_mono (param $x i32) (param $y i32)
(local $ref (ref ..))
(local.set $ref
(struct.new $struct
(i32.const 10)
(local.get $x)
(local.get $y)
)
)
..
)
The struct.new and its constant child appear here, and we read the
parameters.
To do this, generalize the code that creates the call context to accept
everything that is impossible to copy (like a local.get) or that would be
tricky and likely unworthwhile (like another call or a tuple). Also check
for effect interactions, as this code motion does some reordering.
For this to work, we need to adjust how we compute the costs we
compare when deciding what to monomorphize. Before we just
compared the called function to the monomorphized called function,
which was good enough when the call context only contained consts,
but now it can contain arbitrarily nested code. The proper comparison
is between these two:
* Old function + call context
* New monomorphized function
Including the call context makes this a fair comparison. In the example
above, the struct.new and the i32.const are part of the call context,
and so they are in the monomorphized function, so if we didn't count
them in other function we'd decide not to optimize anything with a large
context.
The new functionality is tested in a new file. A few parts of existing
tests needed changes to not become pointless after this improvement,
namely by replacing stuff that we now optimize with things that we
don't like replacing an i32.eqz with a local.get. There are also a
handful of test outcomes that change in CAREFUL mode due to the
new cost analysis.
Diffstat (limited to 'src/wasm/wasm-binary.cpp')
0 files changed, 0 insertions, 0 deletions