Skip to content
Merged
Changes from 1 commit
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
f6bd1b0
work
kripken Jun 5, 2024
dd673f8
note
kripken Jun 6, 2024
3232bae
note
kripken Jun 6, 2024
4517e55
Merge remote-tracking branch 'origin/main' into mono.moar
kripken Jun 11, 2024
d47f4c2
Merge remote-tracking branch 'origin/main' into mono.moar
kripken Jun 27, 2024
c876ff5
comments
kripken Jun 27, 2024
f804c95
comments
kripken Jun 27, 2024
6817d67
comments
kripken Jun 27, 2024
7c975c3
work
kripken Jun 27, 2024
38488b9
work
kripken Jun 27, 2024
0877c0a
work
kripken Jun 27, 2024
bc47504
work
kripken Jul 1, 2024
a8fbc5e
format
kripken Jul 1, 2024
a11b9d3
Merge remote-tracking branch 'origin/main' into mono.moar
kripken Jul 1, 2024
38ab29a
work
kripken Jul 1, 2024
3339ef7
work
kripken Jul 1, 2024
d978b55
work
kripken Jul 1, 2024
52ad9f0
work
kripken Jul 1, 2024
e428820
work
kripken Jul 1, 2024
996a95d
bad
kripken Jul 1, 2024
2aa99fa
work
kripken Jul 1, 2024
235c5fd
work
kripken Jul 1, 2024
9bb4af5
work
kripken Jul 1, 2024
7ca4545
work
kripken Jul 1, 2024
4d04aea
work
kripken Jul 1, 2024
e26d753
work
kripken Jul 1, 2024
e48ebb8
work
kripken Jul 1, 2024
a669e47
work
kripken Jul 1, 2024
1d598ef
work
kripken Jul 1, 2024
aaacb45
work
kripken Jul 1, 2024
21a879f
work
kripken Jul 1, 2024
71f3d43
test
kripken Jul 2, 2024
1b7b27b
test
kripken Jul 2, 2024
b52f1ff
test
kripken Jul 2, 2024
91c6143
test
kripken Jul 2, 2024
27bbb77
test
kripken Jul 2, 2024
bdf8250
work
kripken Jul 2, 2024
9c7a6d6
test
kripken Jul 2, 2024
3b50f1b
almost
kripken Jul 2, 2024
8ef96f7
work
kripken Jul 2, 2024
5afbd14
work
kripken Jul 2, 2024
4867379
moar
kripken Jul 2, 2024
3a72b38
work
kripken Jul 2, 2024
e9505a0
work
kripken Jul 2, 2024
c3dca8b
work
kripken Jul 2, 2024
90e7ebb
work
kripken Jul 2, 2024
baca207
work
kripken Jul 2, 2024
c2a1dbd
work
kripken Jul 2, 2024
aca108c
work
kripken Jul 2, 2024
9c2d3fa
work
kripken Jul 2, 2024
b4933b2
work
kripken Jul 3, 2024
62fb4eb
work
kripken Jul 3, 2024
4742228
work
kripken Jul 3, 2024
8bb827f
work
kripken Jul 3, 2024
6bedb13
fix
kripken Jul 3, 2024
d24bb59
format
kripken Jul 3, 2024
cb50b7c
Merge remote-tracking branch 'origin/main' into mono.moar
kripken Jul 3, 2024
2091277
work
kripken Jul 3, 2024
8ddbc3e
work
kripken Jul 3, 2024
d781015
work
kripken Jul 3, 2024
2aa8509
work
kripken Jul 3, 2024
b4f6a41
work
kripken Jul 3, 2024
b3fce26
work
kripken Jul 3, 2024
a3b8153
fix
kripken Jul 3, 2024
57da116
fix test
kripken Jul 3, 2024
533e849
format
kripken Jul 3, 2024
decabfb
Merge remote-tracking branch 'myself/mono.moar' into mono.moar
kripken Jul 10, 2024
0a8e2ab
feedback: move cheaper check earlier
kripken Jul 10, 2024
649e8ad
feedback: TODO for global.get etc.
kripken Jul 10, 2024
b6cfd63
feedback: rename debug method
kripken Jul 10, 2024
e8df956
format
kripken Jul 10, 2024
d32d44a
feedback: improve TODO
kripken Jul 10, 2024
89b6bd4
feedback: remove second copy of function body
kripken Jul 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
work
  • Loading branch information
kripken committed Jun 5, 2024
commit f6bd1b081b3efeb2f4ace46b3e87ee629f16b1ab
89 changes: 78 additions & 11 deletions src/passes/Monomorphize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,28 @@
*/

//
// When we see a call foo(arg1, arg2) and at least one of the arguments has a
// more refined type than is declared in the function being called, create a
// copy of the function with the refined type. That copy can then potentially be
// optimized in useful ways later.
// When we see a call, see if the information at the callsite can allow us to
// optimize. This is related to inlining, as when we inline the calling function
// then optimizes the inlined code together with the code around the callsite;
// in monomorphization we handle cases that inlining cannot do, by creating a
// specialized version of the called function tuned for the particular call. In
// particular, we may benefit from monomorphizing in the following cases:
//
// Inlining also monomorphizes in effect. What this pass does is handle the
// cases where inlining cannot be done.
// * If a call provides a more refined type than the function declares for a
// parameter.
// * If a call provides a constant as a parameter. TODO
// * If a call provides a GC allocation as a parameter. TODO
// * If a call is dropped. TODO
//
// For example, if a call provides a constant then the call + called function
// may optimize well together if constant propagation leads to removal of code.
// GC allocations may also be useful as Heap2Local may operate (if the
// allocation does not escape). And for a dropped call, if we optimize with the
// drop inside the function then all the computation of that result may be
// removed.
//
// To see when monomorphizing makes sense, this optimizes the target function
// both with and without the more refined types. If the refined types help then
// the version with might remove a cast, for example. Note that while doing so
// both with and without the callsite info. Note that while doing so
// we keep the optimization results of the version without - there is no reason
// to forget them since we've gone to the trouble anyhow. So this pass may have
// the side effect of performing minor optimizations on functions. There is also
Expand Down Expand Up @@ -54,9 +65,6 @@
// compute the LUB of a bunch of calls to a target and then investigate
// that one case and use it in all those callers.
// TODO: Not just direct calls? But updating vtables is complex.
// TODO: Not just types? We could monomorphize using Literal values. E.g. for
// function references, if we monomorphized we'd end up specializing qsort
// for the particular functions it is given.
//

#include "ir/cost.h"
Expand All @@ -73,6 +81,65 @@ namespace wasm {

namespace {

// Relevant information about a call for purposes of monomorphization.
struct CallInfo {
// The operands the call sends as parameters, in a general form. Each
// local.get here is of a parameter, and these operands appear in the called
// function. For example, consider this call, and the function it calls:
//
// (call $foo
// (i32.const 10)
// (struct.new $struct
// (..something complicated..)
// )
// )
//
// (func $foo (param $int i32) (param $ref (ref $struct))
// ..
//
// The generalized operands are
//
// [
// (i32.const 10) ;; unchanged
// (struct.new $struct ;; the struct.new can be handled
// (local.get $0) ;; the complicated child cannot; make it a param
// )
// ]
//
// We can then optimize a version of $foo that looks like this:
//
// (func $foo-monomorphized (param $0 ..)
// (..local defs..)
// (local.set $int
// (i32.const 10)
// )
// (local.set $ref
// (struct.new $struct
// (local.get $0)
// )
// )
// ..
//
// The $int param is no longer a parameter, and it is set in a local at the
// top. The $ref parameter is likewise removed. We have a new parameter for
// the internal part of the struct.new that we could not handle, and the call
// would send only that:
//
// (call $foo-monomorphized
// (..something complicated..)
// )
//
// $foo-monomorphized is now a version of $monomorphized that has "pulled in"
// parts of the call, which may allow it to get optimized better.
//
// Note how local.gets in this list correspond to gets of the *new*
// parameters.
const ExpressionList& operands;

// Whether the call is dropped.
bool dropped;
};

struct Monomorphize : public Pass {
// If set, we run some opts to see if monomorphization helps, and skip it if
// not.
Expand Down