summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/passes/Monomorphize.cpp288
-rw-r--r--test/lit/passes/monomorphize-consts.wast213
-rw-r--r--test/lit/passes/monomorphize-context.wast1805
-rw-r--r--test/lit/passes/monomorphize-mvp.wast26
-rw-r--r--test/lit/passes/monomorphize-types.wast268
-rw-r--r--test/lit/passes/no-inline-monomorphize-inlining.wast64
6 files changed, 2331 insertions, 333 deletions
diff --git a/src/passes/Monomorphize.cpp b/src/passes/Monomorphize.cpp
index 8c08db55b..1fd81b9d1 100644
--- a/src/passes/Monomorphize.cpp
+++ b/src/passes/Monomorphize.cpp
@@ -85,13 +85,18 @@
// 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: Should we look at no-inline flags? We do move code between functions,
+// but it isn't normal inlining.
//
#include "ir/cost.h"
+#include "ir/effects.h"
#include "ir/find_all.h"
+#include "ir/iteration.h"
#include "ir/manipulation.h"
#include "ir/module-utils.h"
#include "ir/names.h"
+#include "ir/properties.h"
#include "ir/return-utils.h"
#include "ir/type-updating.h"
#include "ir/utils.h"
@@ -212,33 +217,204 @@ struct CallContext {
// remaining values by updating |newOperands| (for example, if all the values
// sent are constants, then |newOperands| will end up empty, as we have
// nothing left to send).
+ //
+ // The approach implemented here tries to move as much code into the call
+ // context as possible. That may not always be helpful, say in situations like
+ // this:
+ //
+ // (call $foo
+ // (i32.add
+ // (local.get $x)
+ // (local.get $y)
+ // )
+ // )
+ //
+ // If we move the i32.add into $foo then it will still be adding two unknown
+ // values (which will be parameters rather than locals). Moving the add might
+ // just increase code size if so. However, there are many other situations
+ // where the more code, the better:
+ //
+ // (call $foo
+ // (i32.eqz
+ // (local.get $x)
+ // )
+ // )
+ //
+ // While the value remains unknown, after moving the i32.eqz into the target
+ // function we may be able to use the fact that it has at most 1 bit set.
+ // Even larger benefits can happen in WasmGC:
+ //
+ // (call $foo
+ // (struct.new $T
+ // (local.get $x)
+ // (local.get $y)
+ // )
+ // )
+ //
+ // If the struct never escapes then we may be able to remove the allocation
+ // after monomorphization, even if we know nothing about the values in its
+ // fields.
+ //
+ // TODO: Explore other options that are more careful about how much code is
+ // moved.
void buildFromCall(CallInfo& info,
std::vector<Expression*>& newOperands,
- Module& wasm) {
+ Module& wasm,
+ const PassOptions& options) {
Builder builder(wasm);
+ // First, find the things we can move into the context and the things we
+ // cannot. Some things simply cannot be moved out of the calling function,
+ // such as a local.set, but also we need to handle effect interactions among
+ // the operands, because each time we move code into the context we are
+ // pushing it into the called function, which changes the order of
+ // operations, for example:
+ //
+ // (call $foo
+ // (first
+ // (a)
+ // )
+ // (second
+ // (b)
+ // )
+ // )
+ //
+ // (func $foo (param $first) (param $second)
+ // )
+ //
+ // If we move |first| and |a| into the context then we get this:
+ //
+ // (call $foo
+ // ;; |first| and |a| were removed from here.
+ // (second
+ // (b)
+ // )
+ // )
+ //
+ // (func $foo (param $second)
+ // ;; |first| is now a local, and we assign it inside the called func.
+ // (local $first)
+ // (local.set $first
+ // (first
+ // (a)
+ // )
+ // )
+ // )
+ //
+ // After this code motion we execute |second| and |b| *before* the call, and
+ // |first| and |a| after, so we cannot do this transformation if the order
+ // of operations between them matters.
+ //
+ // The key property here is that all things that are moved into the context
+ // (moved into the monomorphized function) remain ordered with respect to
+ // each other, but must be moved past all non-moving things after them. For
+ // example, say we want to move B and D in this list (of expressions in
+ // execution order):
+ //
+ // A, B, C, D, E
+ //
+ // After moving B and D we end up with this:
+ //
+ // A, C, E and executing later in the monomorphized function: B, D
+ //
+ // Then we must be able to move B past C and E, and D past E. It is simplest
+ // to compute this in reverse order, starting from E and going back, and
+ // then each time we want to move something we can check if it can cross
+ // over all the non-moving effects we've seen so far. To compute this, first
+ // list out the post-order of the expressions, and then we'll iterate in
+ // reverse.
+ struct Lister
+ : public PostWalker<Lister, UnifiedExpressionVisitor<Lister>> {
+ std::vector<Expression*> list;
+ void visitExpression(Expression* curr) { list.push_back(curr); }
+ } lister;
+ // As a quick estimate, we need space for at least the operands.
+ lister.list.reserve(operands.size());
+
+ for (auto* operand : info.call->operands) {
+ lister.walk(operand);
+ }
+
+ // Go in reverse post-order as explained earlier, noting what cannot be
+ // moved into the context, and while accumulating the effects that are not
+ // moving.
+ std::unordered_set<Expression*> immovable;
+ EffectAnalyzer nonMovingEffects(options, wasm);
+ for (auto i = int64_t(lister.list.size()) - 1; i >= 0; i--) {
+ auto* curr = lister.list[i];
+
+ // This may have been marked as immovable because of the parent. We do
+ // that because if a parent is immovable then we can't move the children
+ // into the context (if we did, they would execute after the parent, but
+ // it needs their values).
+ bool currImmovable = immovable.count(curr) > 0;
+ if (!currImmovable) {
+ // This might be movable or immovable. Check both effect interactions
+ // (as described before, we want to move this past immovable code) and
+ // reasons intrinsic to the expression itself that might prevent moving.
+ ShallowEffectAnalyzer currEffects(options, wasm, curr);
+ if (currEffects.invalidates(nonMovingEffects) ||
+ !canBeMovedIntoContext(curr, currEffects)) {
+ immovable.insert(curr);
+ currImmovable = true;
+ }
+ }
+
+ if (currImmovable) {
+ // Regardless of whether this was marked immovable because of the
+ // parent, or because we just found it cannot be moved, accumulate the
+ // effects, and also mark its immediate children (so that we do the same
+ // when we get to them).
+ nonMovingEffects.visit(curr);
+ for (auto* child : ChildIterator(curr)) {
+ immovable.insert(child);
+ }
+ }
+ }
+
+ // We now know which code can be moved and which cannot, so we can do the
+ // final processing of the call operands. We do this as a copy operation,
+ // copying as much as possible into the call context. Code that cannot be
+ // moved ends up as values sent to the monomorphized function.
+ //
+ // The copy operation works in pre-order, which allows us to override
+ // entire children as needed:
+ //
+ // (call $foo
+ // (problem
+ // (a)
+ // )
+ // (later)
+ // )
+ //
+ // We visit |problem| first, and if there is a problem that prevents us
+ // moving it into the context then we override the copy and then it and
+ // its child |a| remain in the caller (and |a| is never visited in the
+ // copy).
for (auto* operand : info.call->operands) {
- // Process the operand. This is a copy operation, as we are trying to move
- // (copy) code from the callsite into the called function. When we find we
- // can copy then we do so, and when we cannot that value remains as a
- // value sent from the call.
operands.push_back(ExpressionManipulator::flexibleCopy(
operand, wasm, [&](Expression* child) -> Expression* {
- if (canBeMovedIntoContext(child)) {
- // This can be moved, great: let the copy happen.
+ if (!child) {
+ // This is an optional child that is not present. Let the copy of
+ // the nullptr happen.
+ return nullptr;
+ }
+
+ if (!immovable.count(child)) {
+ // This can be moved; let the copy happen.
return nullptr;
}
- // This cannot be moved, so we stop here: this is a value that is sent
- // into the monomorphized function. It is a new operand in the call,
- // and in the context operands it is a local.get, that reads that
- // value.
+ // This cannot be moved. Do not copy it into the call context. In the
+ // example above, |problem| remains as an operand on the call (so we
+ // add it to |newOperands|), and in the call context all we have is a
+ // local.get that reads that sent value.
auto paramIndex = newOperands.size();
newOperands.push_back(child);
// TODO: If one operand is a tee and another a get, we could actually
// reuse the local, effectively showing the monomorphized
- // function that the values are the same. (But then the checks
- // later down to is<LocalGet> would need to check index too.)
+ // function that the values are the same. EquivalentSets may
+ // help here.
return builder.makeLocalGet(paramIndex, child->type);
}));
}
@@ -247,12 +423,49 @@ struct CallContext {
}
// Checks whether an expression can be moved into the context.
- bool canBeMovedIntoContext(Expression* curr) {
- // Constant numbers, funcs, strings, etc. can all be copied, so it is ok to
- // add them to the context.
- // TODO: Allow global.get as well, and anything else that is purely
- // copyable.
- return Properties::isSingleConstantExpression(curr);
+ bool canBeMovedIntoContext(Expression* curr,
+ const ShallowEffectAnalyzer& effects) {
+ // Pretty much everything can be moved into the context if we can copy it
+ // between functions, such as constants, globals, etc. The things we cannot
+ // copy are now checked for.
+ if (effects.branchesOut || effects.hasExternalBreakTargets()) {
+ // This branches or returns. We can't move control flow between functions.
+ return false;
+ }
+ if (effects.accessesLocal()) {
+ // Reads/writes to local state cannot be moved around.
+ return false;
+ }
+ if (effects.calls) {
+ // We can in principle move calls, but for simplicity we avoid such
+ // situations (which might involve recursion etc.).
+ return false;
+ }
+ if (Properties::isControlFlowStructure(curr)) {
+ // We can in principle move entire control flow structures with their
+ // children, but for simplicity stop when we see one rather than look
+ // inside to see if we could transfer all its contents. (We would also
+ // need to be careful when handling If arms, etc.)
+ return false;
+ }
+ for (auto* child : ChildIterator(curr)) {
+ if (child->type.isTuple()) {
+ // Consider this:
+ //
+ // (call $target
+ // (tuple.extract 2 1
+ // (local.get $tuple)
+ // )
+ // )
+ //
+ // We cannot move the tuple.extract into the context, because then the
+ // call would have a tuple param. While it is possible to split up the
+ // tuple, or to check if we can also move the children with the parent,
+ // for simplicity just ignore this rare situation.
+ return false;
+ }
+ }
+ return true;
}
// Check if a context is trivial relative to a call, that is, the context
@@ -389,7 +602,7 @@ struct Monomorphize : public Pass {
// if we use that context.
CallContext context;
std::vector<Expression*> newOperands;
- context.buildFromCall(info, newOperands, wasm);
+ context.buildFromCall(info, newOperands, wasm, getPassOptions());
// See if we've already evaluated this function + call context. If so, then
// we've memoized the result.
@@ -447,8 +660,22 @@ struct Monomorphize : public Pass {
doOpts(func);
doOpts(monoFunc.get());
+ // The cost before monomorphization is the old body + the context
+ // operands. The operands will be *removed* from the calling code if we
+ // optimize, and moved into the monomorphized function, so the proper
+ // comparison is the context + the old body, versus the new body (which
+ // includes the reverse-inlined call context).
auto costBefore = CostAnalyzer(func->body).cost;
+ for (auto* operand : context.operands) {
+ // Note that a slight oddity is that we have *not* optimized the
+ // operands before. We optimize func before and after, but the operands
+ // are in the calling function, which we are not modifying here. In
+ // theory that might lead to false positives, if the call's operands are
+ // very unoptimized.
+ costBefore += CostAnalyzer(operand).cost;
+ }
auto costAfter = CostAnalyzer(monoFunc->body).cost;
+
// TODO: We should probably only accept improvements above some minimum,
// to avoid optimizing cases where we duplicate a huge function but
// only optimize a tiny part of it compared to the original.
@@ -486,14 +713,19 @@ struct Monomorphize : public Pass {
// Copy the function as the base for the new one.
auto newFunc = ModuleUtils::copyFunctionWithoutAdd(func, wasm, newName);
- // Generate the new signature, and apply it to the new function.
+ // A local.get is a value that arrives in a parameter. Anything else is
+ // something that we are reverse-inlining into the function, so we don't
+ // need a param for it. Note that we might have multiple gets nested here,
+ // if we are copying part of the original parameter but not all children, so
+ // we scan each operand for all such local.gets.
+ //
+ // Use this information to generate the new signature, and apply it to the
+ // new function.
std::vector<Type> newParams;
for (auto* operand : context.operands) {
- // A local.get is a value that arrives in a parameter. Anything else is
- // something that we are reverse-inlining into the function, so we don't
- // need a param for it.
- if (operand->is<LocalGet>()) {
- newParams.push_back(operand->type);
+ FindAll<LocalGet> gets(operand);
+ for (auto* get : gets.list) {
+ newParams.push_back(get->type);
}
}
// If we were dropped then we are pulling the drop into the monomorphized
@@ -501,7 +733,7 @@ struct Monomorphize : public Pass {
auto newResults = context.dropped ? Type::none : func->getResults();
newFunc->type = Signature(Type(newParams), newResults);
- // We must update local indexes: the new function has a potentially
+ // We must update local indexes: the new function has a potentially
// different number of parameters, and parameters are at the very bottom of
// the local index space. We are also replacing old params with vars. To
// track this, map each old index to the new one.
@@ -559,7 +791,7 @@ struct Monomorphize : public Pass {
// (local.get $param) ;; copied old body
// )
//
- // We need to add such an local.set in the prelude of the function for each
+ // We need to add such a local.set in the prelude of the function for each
// operand in the context.
std::vector<Expression*> pre;
for (Index i = 0; i < context.operands.size(); i++) {
diff --git a/test/lit/passes/monomorphize-consts.wast b/test/lit/passes/monomorphize-consts.wast
index ec59edfea..d71367569 100644
--- a/test/lit/passes/monomorphize-consts.wast
+++ b/test/lit/passes/monomorphize-consts.wast
@@ -12,85 +12,59 @@
;; ALWAYS: (type $0 (func (param i32)))
- ;; ALWAYS: (type $1 (func))
+ ;; ALWAYS: (type $1 (func (param i32 i32 funcref stringref)))
- ;; ALWAYS: (type $2 (func (param i32 i32 funcref stringref)))
+ ;; ALWAYS: (type $2 (func (param i32) (result i32)))
- ;; ALWAYS: (type $3 (func (param i32) (result i32)))
+ ;; ALWAYS: (type $3 (func (result i32)))
- ;; ALWAYS: (type $4 (func (result i32)))
+ ;; ALWAYS: (type $4 (func (param i32 i32)))
;; ALWAYS: (import "a" "b" (func $import (type $0) (param i32)))
- ;; CAREFUL: (type $0 (func))
+ ;; CAREFUL: (type $0 (func (param i32)))
;; CAREFUL: (type $1 (func (param i32 i32 funcref stringref)))
- ;; CAREFUL: (type $2 (func (param i32)))
+ ;; CAREFUL: (type $2 (func (param i32) (result i32)))
- ;; CAREFUL: (type $3 (func (param i32) (result i32)))
+ ;; CAREFUL: (type $3 (func (result i32)))
- ;; CAREFUL: (type $4 (func (result i32)))
+ ;; CAREFUL: (type $4 (func (param i32 i32)))
- ;; CAREFUL: (import "a" "b" (func $import (type $2) (param i32)))
+ ;; CAREFUL: (import "a" "b" (func $import (type $0) (param i32)))
(import "a" "b" (func $import (param i32)))
;; ALWAYS: (elem declare func $calls)
- ;; ALWAYS: (func $calls (type $1)
+ ;; ALWAYS: (func $calls (type $4) (param $x i32) (param $y i32)
;; ALWAYS-NEXT: (call $target_9
- ;; ALWAYS-NEXT: (i32.eqz
- ;; ALWAYS-NEXT: (i32.const 2)
- ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (local.get $x)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: (call $target_9
- ;; ALWAYS-NEXT: (i32.eqz
- ;; ALWAYS-NEXT: (i32.const 3)
- ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (local.get $y)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: (call $target_10
- ;; ALWAYS-NEXT: (i32.eqz
- ;; ALWAYS-NEXT: (i32.const 2)
- ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (local.get $x)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
;; CAREFUL: (elem declare func $calls)
- ;; CAREFUL: (func $calls (type $0)
- ;; CAREFUL-NEXT: (call $target
- ;; CAREFUL-NEXT: (i32.const 1)
- ;; CAREFUL-NEXT: (i32.eqz
- ;; CAREFUL-NEXT: (i32.const 2)
- ;; CAREFUL-NEXT: )
- ;; CAREFUL-NEXT: (ref.func $calls)
- ;; CAREFUL-NEXT: (string.const "foo")
+ ;; CAREFUL: (func $calls (type $4) (param $x i32) (param $y i32)
+ ;; CAREFUL-NEXT: (call $target_9
+ ;; CAREFUL-NEXT: (local.get $x)
;; CAREFUL-NEXT: )
- ;; CAREFUL-NEXT: (call $target
- ;; CAREFUL-NEXT: (i32.const 1)
- ;; CAREFUL-NEXT: (i32.eqz
- ;; CAREFUL-NEXT: (i32.const 3)
- ;; CAREFUL-NEXT: )
- ;; CAREFUL-NEXT: (ref.func $calls)
- ;; CAREFUL-NEXT: (string.const "foo")
+ ;; CAREFUL-NEXT: (call $target_9
+ ;; CAREFUL-NEXT: (local.get $y)
;; CAREFUL-NEXT: )
- ;; CAREFUL-NEXT: (call $target
- ;; CAREFUL-NEXT: (i32.const 3)
- ;; CAREFUL-NEXT: (i32.eqz
- ;; CAREFUL-NEXT: (i32.const 2)
- ;; CAREFUL-NEXT: )
- ;; CAREFUL-NEXT: (ref.func $calls)
- ;; CAREFUL-NEXT: (string.const "foo")
+ ;; CAREFUL-NEXT: (call $target_10
+ ;; CAREFUL-NEXT: (local.get $x)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: )
- (func $calls
- ;; All but the eqz parameter are constants that can be handled. In ALWAYS
- ;; mode we optimize and remove all but that one. In CAREFUL mode we end up
- ;; not doing anything, as the target function's body optimizes out anyhow
- ;; (so there is no benefit from monomorphization, after opts).
+ (func $calls (param $x i32) (param $y i32)
+ ;; All but the local.get are constants that can be handled.
(call $target
(i32.const 1)
- (i32.eqz
- (i32.const 2)
- )
+ (local.get $x)
(ref.func $calls)
(string.const "foo")
)
@@ -100,9 +74,7 @@
;; This will call the same refined function as the previous call.
(call $target
(i32.const 1)
- (i32.eqz
- (i32.const 3) ;; this changed
- )
+ (local.get $y) ;; this changed
(ref.func $calls)
(string.const "foo")
)
@@ -111,64 +83,42 @@
;; call a different refined function.
(call $target
(i32.const 3) ;; this changed
- (i32.eqz
- (i32.const 2)
- )
+ (local.get $x)
(ref.func $calls)
(string.const "foo")
)
)
- ;; ALWAYS: (func $more-calls (type $1)
+ ;; ALWAYS: (func $more-calls (type $0) (param $x i32)
;; ALWAYS-NEXT: (call $target_9
- ;; ALWAYS-NEXT: (i32.eqz
- ;; ALWAYS-NEXT: (i32.const 999)
- ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (local.get $x)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: (call $other-target_11
- ;; ALWAYS-NEXT: (i32.eqz
- ;; ALWAYS-NEXT: (i32.const 999)
- ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (local.get $x)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: (call $work_12
- ;; ALWAYS-NEXT: (i32.eqz
- ;; ALWAYS-NEXT: (i32.const 999)
- ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (local.get $x)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
- ;; CAREFUL: (func $more-calls (type $0)
- ;; CAREFUL-NEXT: (call $target
- ;; CAREFUL-NEXT: (i32.const 1)
- ;; CAREFUL-NEXT: (i32.eqz
- ;; CAREFUL-NEXT: (i32.const 999)
- ;; CAREFUL-NEXT: )
- ;; CAREFUL-NEXT: (ref.func $calls)
- ;; CAREFUL-NEXT: (string.const "foo")
+ ;; CAREFUL: (func $more-calls (type $0) (param $x i32)
+ ;; CAREFUL-NEXT: (call $target_9
+ ;; CAREFUL-NEXT: (local.get $x)
;; CAREFUL-NEXT: )
- ;; CAREFUL-NEXT: (call $other-target
- ;; CAREFUL-NEXT: (i32.const 1)
- ;; CAREFUL-NEXT: (i32.eqz
- ;; CAREFUL-NEXT: (i32.const 999)
- ;; CAREFUL-NEXT: )
- ;; CAREFUL-NEXT: (ref.func $calls)
- ;; CAREFUL-NEXT: (string.const "foo")
+ ;; CAREFUL-NEXT: (call $other-target_11
+ ;; CAREFUL-NEXT: (local.get $x)
;; CAREFUL-NEXT: )
- ;; CAREFUL-NEXT: (call $work_9
- ;; CAREFUL-NEXT: (i32.eqz
- ;; CAREFUL-NEXT: (i32.const 999)
- ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (call $work_12
+ ;; CAREFUL-NEXT: (local.get $x)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: )
- (func $more-calls
+ (func $more-calls (param $x i32)
;; Identical to the first call in the previous function (except for the non-
;; constant second param, which is ok to be different). We should call the
;; same refined function before, even though we are in a different
;; function here.
(call $target
(i32.const 1)
- (i32.eqz
- (i32.const 999)
- )
+ (local.get $x)
(ref.func $calls)
(string.const "foo")
)
@@ -178,9 +128,7 @@
;; a different refined function than before
(call $other-target
(i32.const 1)
- (i32.eqz
- (i32.const 999)
- )
+ (local.get $x)
(ref.func $calls)
(string.const "foo")
)
@@ -190,22 +138,16 @@
;; unlock actual work.
(call $work
(i32.const 3)
- (i32.eqz
- (i32.const 999)
- )
+ (local.get $x)
(ref.func $calls)
(string.const "foo")
)
)
- ;; ALWAYS: (func $fail (type $1)
+ ;; ALWAYS: (func $fail (type $0) (param $x i32)
;; ALWAYS-NEXT: (call $target
- ;; ALWAYS-NEXT: (i32.eqz
- ;; ALWAYS-NEXT: (i32.const 1)
- ;; ALWAYS-NEXT: )
- ;; ALWAYS-NEXT: (i32.eqz
- ;; ALWAYS-NEXT: (i32.const 999)
- ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (local.get $x)
+ ;; ALWAYS-NEXT: (local.get $x)
;; ALWAYS-NEXT: (block (result funcref)
;; ALWAYS-NEXT: (ref.func $calls)
;; ALWAYS-NEXT: )
@@ -214,14 +156,10 @@
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
- ;; CAREFUL: (func $fail (type $0)
+ ;; CAREFUL: (func $fail (type $0) (param $x i32)
;; CAREFUL-NEXT: (call $target
- ;; CAREFUL-NEXT: (i32.eqz
- ;; CAREFUL-NEXT: (i32.const 1)
- ;; CAREFUL-NEXT: )
- ;; CAREFUL-NEXT: (i32.eqz
- ;; CAREFUL-NEXT: (i32.const 999)
- ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (local.get $x)
+ ;; CAREFUL-NEXT: (local.get $x)
;; CAREFUL-NEXT: (block (result funcref)
;; CAREFUL-NEXT: (ref.func $calls)
;; CAREFUL-NEXT: )
@@ -230,15 +168,11 @@
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: )
- (func $fail
+ (func $fail (param $x i32)
;; No operand is a constant here, so we do nothing.
(call $target
- (i32.eqz
- (i32.const 1)
- )
- (i32.eqz
- (i32.const 999)
- )
+ (local.get $x)
+ (local.get $x)
(block (result funcref)
(ref.func $calls)
)
@@ -248,7 +182,7 @@
)
)
- ;; ALWAYS: (func $mutual-recursion-a (type $3) (param $x i32) (result i32)
+ ;; ALWAYS: (func $mutual-recursion-a (type $2) (param $x i32) (result i32)
;; ALWAYS-NEXT: (if (result i32)
;; ALWAYS-NEXT: (local.get $x)
;; ALWAYS-NEXT: (then
@@ -264,7 +198,7 @@
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
- ;; CAREFUL: (func $mutual-recursion-a (type $3) (param $0 i32) (result i32)
+ ;; CAREFUL: (func $mutual-recursion-a (type $2) (param $0 i32) (result i32)
;; CAREFUL-NEXT: (if (result i32)
;; CAREFUL-NEXT: (local.get $0)
;; CAREFUL-NEXT: (then
@@ -272,9 +206,7 @@
;; CAREFUL-NEXT: (call $mutual-recursion-b
;; CAREFUL-NEXT: (local.get $0)
;; CAREFUL-NEXT: )
- ;; CAREFUL-NEXT: (call $mutual-recursion-b
- ;; CAREFUL-NEXT: (i32.const 0)
- ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (call $mutual-recursion-b_13)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: (else
@@ -308,15 +240,15 @@
)
)
- ;; ALWAYS: (func $mutual-recursion-b (type $3) (param $x i32) (result i32)
+ ;; ALWAYS: (func $mutual-recursion-b (type $2) (param $x i32) (result i32)
;; ALWAYS-NEXT: (i32.add
;; ALWAYS-NEXT: (call $mutual-recursion-a_14)
;; ALWAYS-NEXT: (i32.const 1337)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
- ;; CAREFUL: (func $mutual-recursion-b (type $3) (param $0 i32) (result i32)
+ ;; CAREFUL: (func $mutual-recursion-b (type $2) (param $0 i32) (result i32)
;; CAREFUL-NEXT: (i32.add
- ;; CAREFUL-NEXT: (call $mutual-recursion-a_10)
+ ;; CAREFUL-NEXT: (call $mutual-recursion-a_14)
;; CAREFUL-NEXT: (i32.const 1337)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: )
@@ -330,7 +262,7 @@
)
)
- ;; ALWAYS: (func $target (type $2) (param $x i32) (param $y i32) (param $func funcref) (param $str stringref)
+ ;; ALWAYS: (func $target (type $1) (param $x i32) (param $y i32) (param $func funcref) (param $str stringref)
;; ALWAYS-NEXT: (drop
;; ALWAYS-NEXT: (local.get $x)
;; ALWAYS-NEXT: )
@@ -362,7 +294,7 @@
)
)
- ;; ALWAYS: (func $other-target (type $2) (param $x i32) (param $y i32) (param $func funcref) (param $str stringref)
+ ;; ALWAYS: (func $other-target (type $1) (param $x i32) (param $y i32) (param $func funcref) (param $str stringref)
;; ALWAYS-NEXT: (drop
;; ALWAYS-NEXT: (local.get $func)
;; ALWAYS-NEXT: )
@@ -395,7 +327,7 @@
)
)
- ;; ALWAYS: (func $work (type $2) (param $x i32) (param $y i32) (param $func funcref) (param $str stringref)
+ ;; ALWAYS: (func $work (type $1) (param $x i32) (param $y i32) (param $func funcref) (param $str stringref)
;; ALWAYS-NEXT: (call $import
;; ALWAYS-NEXT: (i32.add
;; ALWAYS-NEXT: (local.get $x)
@@ -590,7 +522,7 @@
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
-;; ALWAYS: (func $mutual-recursion-b_13 (type $4) (result i32)
+;; ALWAYS: (func $mutual-recursion-b_13 (type $3) (result i32)
;; ALWAYS-NEXT: (local $x i32)
;; ALWAYS-NEXT: (local.set $x
;; ALWAYS-NEXT: (i32.const 0)
@@ -603,7 +535,7 @@
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
-;; ALWAYS: (func $mutual-recursion-a_14 (type $4) (result i32)
+;; ALWAYS: (func $mutual-recursion-a_14 (type $3) (result i32)
;; ALWAYS-NEXT: (local $x i32)
;; ALWAYS-NEXT: (local.set $x
;; ALWAYS-NEXT: (i32.const 0)
@@ -624,7 +556,19 @@
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
-;; CAREFUL: (func $work_9 (type $2) (param $0 i32)
+;; CAREFUL: (func $target_9 (type $0) (param $0 i32)
+;; CAREFUL-NEXT: (nop)
+;; CAREFUL-NEXT: )
+
+;; CAREFUL: (func $target_10 (type $0) (param $0 i32)
+;; CAREFUL-NEXT: (nop)
+;; CAREFUL-NEXT: )
+
+;; CAREFUL: (func $other-target_11 (type $0) (param $0 i32)
+;; CAREFUL-NEXT: (nop)
+;; CAREFUL-NEXT: )
+
+;; CAREFUL: (func $work_12 (type $0) (param $0 i32)
;; CAREFUL-NEXT: (call $import
;; CAREFUL-NEXT: (i32.const 3)
;; CAREFUL-NEXT: )
@@ -633,6 +577,15 @@
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: )
-;; CAREFUL: (func $mutual-recursion-a_10 (type $4) (result i32)
+;; CAREFUL: (func $mutual-recursion-b_13 (type $3) (result i32)
+;; CAREFUL-NEXT: (i32.add
+;; CAREFUL-NEXT: (call $mutual-recursion-a
+;; CAREFUL-NEXT: (i32.const 0)
+;; CAREFUL-NEXT: )
+;; CAREFUL-NEXT: (i32.const 1337)
+;; CAREFUL-NEXT: )
+;; CAREFUL-NEXT: )
+
+;; CAREFUL: (func $mutual-recursion-a_14 (type $3) (result i32)
;; CAREFUL-NEXT: (i32.const 42)
;; CAREFUL-NEXT: )
diff --git a/test/lit/passes/monomorphize-context.wast b/test/lit/passes/monomorphize-context.wast
new file mode 100644
index 000000000..d3bc4a242
--- /dev/null
+++ b/test/lit/passes/monomorphize-context.wast
@@ -0,0 +1,1805 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
+
+;; As in monomorphize-types.wast, test in both "always" mode, which always
+;; monomorphizes, and in "careful" mode which does it only when it appears to
+;; actually help.
+
+;; RUN: foreach %s %t wasm-opt --monomorphize-always -all -S -o - | filecheck %s --check-prefix ALWAYS
+;; RUN: foreach %s %t wasm-opt --monomorphize -all -S -o - | filecheck %s --check-prefix CAREFUL
+
+(module
+ ;; ALWAYS: (type $0 (func (param i32) (result i32)))
+
+ ;; ALWAYS: (type $1 (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 anyref funcref i32 f64 i32 anyref anyref)))
+
+ ;; ALWAYS: (type $2 (func (param i32 i32 i32 i32 i32 i32)))
+
+ ;; ALWAYS: (type $struct (struct))
+ (type $struct (struct))
+
+ (memory 10 20)
+
+ ;; ALWAYS: (global $imm i32 (i32.const 10))
+ ;; CAREFUL: (type $0 (func (param i32) (result i32)))
+
+ ;; CAREFUL: (type $1 (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 anyref funcref i32 f64 i32 anyref anyref)))
+
+ ;; CAREFUL: (type $2 (func (param i32 i32 i32 i32 i32 i32)))
+
+ ;; CAREFUL: (global $imm i32 (i32.const 10))
+ (global $imm i32 (i32.const 10))
+
+ ;; ALWAYS: (global $mut (mut i32) (i32.const 20))
+ ;; CAREFUL: (global $mut (mut i32) (i32.const 20))
+ (global $mut (mut i32) (i32.const 20))
+
+ ;; ALWAYS: (memory $0 10 20)
+
+ ;; ALWAYS: (elem declare func $target)
+
+ ;; ALWAYS: (func $caller (type $0) (param $x i32) (result i32)
+ ;; ALWAYS-NEXT: (block $out (result i32)
+ ;; ALWAYS-NEXT: (call $target_2
+ ;; ALWAYS-NEXT: (br_if $out
+ ;; ALWAYS-NEXT: (i32.const 12)
+ ;; ALWAYS-NEXT: (i32.const 13)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (block (result i32)
+ ;; ALWAYS-NEXT: (i32.const 0)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (if (result i32)
+ ;; ALWAYS-NEXT: (i32.const 1)
+ ;; ALWAYS-NEXT: (then
+ ;; ALWAYS-NEXT: (i32.const 2)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (else
+ ;; ALWAYS-NEXT: (i32.const 3)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (call $caller
+ ;; ALWAYS-NEXT: (i32.const 4)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (local.get $x)
+ ;; ALWAYS-NEXT: (local.tee $x
+ ;; ALWAYS-NEXT: (i32.const 5)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (i32.const 14)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (memory $0 10 20)
+
+ ;; CAREFUL: (func $caller (type $0) (param $x i32) (result i32)
+ ;; CAREFUL-NEXT: (block $out (result i32)
+ ;; CAREFUL-NEXT: (call $target_2
+ ;; CAREFUL-NEXT: (br_if $out
+ ;; CAREFUL-NEXT: (i32.const 12)
+ ;; CAREFUL-NEXT: (i32.const 13)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (block (result i32)
+ ;; CAREFUL-NEXT: (i32.const 0)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (if (result i32)
+ ;; CAREFUL-NEXT: (i32.const 1)
+ ;; CAREFUL-NEXT: (then
+ ;; CAREFUL-NEXT: (i32.const 2)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (else
+ ;; CAREFUL-NEXT: (i32.const 3)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (call $caller
+ ;; CAREFUL-NEXT: (i32.const 4)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (local.get $x)
+ ;; CAREFUL-NEXT: (local.tee $x
+ ;; CAREFUL-NEXT: (i32.const 5)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (i32.const 14)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ (func $caller (param $x i32) (result i32)
+ ;; Show the variety of things we can and cannot move into the call context.
+ ;;
+ ;; Note that in CAREFUL mode we only optimize here if we properly take into
+ ;; account the call context in the cost. The function we are calling has
+ ;; an empty body, so the monomorphized function will contain basically just
+ ;; the moved code from the call context. If we didn't measure that in the
+ ;; cost before monomorphization then it would seem like we went from cost 0
+ ;; (empty body) to the cost of the operations that remain after we
+ ;; optimize (which is the i32.load, which might trap so it remains). But if
+ ;; we take into account the context, then monomorphization definitely helps
+ ;; as it removes a bunch of constants.
+ (block $out (result i32)
+ (call $target
+ ;; We can't move control flow.
+ (br_if $out
+ (i32.const 12)
+ (i32.const 13)
+ )
+ ;; We can't move control flow structures.
+ (block (result i32)
+ (i32.const 0)
+ )
+ (if (result i32)
+ (i32.const 1)
+ (then
+ (i32.const 2)
+ )
+ (else
+ (i32.const 3)
+ )
+ )
+ ;; We don't move calls.
+ (call $caller
+ (i32.const 4)
+ )
+ ;; We can't move local operations.
+ (local.get $x)
+ (local.tee $x
+ (i32.const 5)
+ )
+ ;; We can move globals, even mutable.
+ (global.get $imm)
+ (global.get $mut)
+ ;; We can move loads and other options that might trap.
+ (i32.load
+ (i32.const 6)
+ )
+ ;; We can move constants.
+ (i32.const 7)
+ (ref.null any)
+ (ref.func $target)
+ ;; We can move math operations.
+ (i32.eqz
+ (i32.const 8)
+ )
+ (f64.add
+ (f64.const 2.71828)
+ (f64.const 3.14159)
+ )
+ ;; We can move selects.
+ (select
+ (i32.const 9)
+ (i32.const 10)
+ (i32.const 11)
+ )
+ ;; We can move GC operations.
+ (ref.cast (ref null none)
+ (ref.null none)
+ )
+ (struct.new $struct)
+ )
+ (i32.const 14)
+ )
+ )
+
+ ;; ALWAYS: (func $target (type $1) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32) (param $6 i32) (param $7 i32) (param $8 i32) (param $9 i32) (param $10 anyref) (param $11 funcref) (param $12 i32) (param $13 f64) (param $14 i32) (param $15 anyref) (param $16 anyref)
+ ;; ALWAYS-NEXT: (nop)
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (func $target (type $1) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32) (param $6 i32) (param $7 i32) (param $8 i32) (param $9 i32) (param $10 anyref) (param $11 funcref) (param $12 i32) (param $13 f64) (param $14 i32) (param $15 anyref) (param $16 anyref)
+ ;; CAREFUL-NEXT: (nop)
+ ;; CAREFUL-NEXT: )
+ (func $target
+ (param i32)
+ (param i32)
+ (param i32)
+ (param i32)
+ (param i32)
+ (param i32)
+ (param i32)
+ (param i32)
+ (param i32)
+ (param i32)
+ (param anyref)
+ (param funcref)
+ (param i32)
+ (param f64)
+ (param i32)
+ (param anyref)
+ (param anyref)
+ )
+)
+
+;; ALWAYS: (func $target_2 (type $2) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32)
+;; ALWAYS-NEXT: (local $6 i32)
+;; ALWAYS-NEXT: (local $7 i32)
+;; ALWAYS-NEXT: (local $8 i32)
+;; ALWAYS-NEXT: (local $9 i32)
+;; ALWAYS-NEXT: (local $10 i32)
+;; ALWAYS-NEXT: (local $11 i32)
+;; ALWAYS-NEXT: (local $12 i32)
+;; ALWAYS-NEXT: (local $13 i32)
+;; ALWAYS-NEXT: (local $14 i32)
+;; ALWAYS-NEXT: (local $15 i32)
+;; ALWAYS-NEXT: (local $16 anyref)
+;; ALWAYS-NEXT: (local $17 funcref)
+;; ALWAYS-NEXT: (local $18 i32)
+;; ALWAYS-NEXT: (local $19 f64)
+;; ALWAYS-NEXT: (local $20 i32)
+;; ALWAYS-NEXT: (local $21 anyref)
+;; ALWAYS-NEXT: (local $22 anyref)
+;; ALWAYS-NEXT: (local.set $6
+;; ALWAYS-NEXT: (local.get $0)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $7
+;; ALWAYS-NEXT: (local.get $1)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $8
+;; ALWAYS-NEXT: (local.get $2)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $9
+;; ALWAYS-NEXT: (local.get $3)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $10
+;; ALWAYS-NEXT: (local.get $4)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $11
+;; ALWAYS-NEXT: (local.get $5)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $12
+;; ALWAYS-NEXT: (global.get $imm)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $13
+;; ALWAYS-NEXT: (global.get $mut)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $14
+;; ALWAYS-NEXT: (i32.load
+;; ALWAYS-NEXT: (i32.const 6)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $15
+;; ALWAYS-NEXT: (i32.const 7)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $16
+;; ALWAYS-NEXT: (ref.null none)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $17
+;; ALWAYS-NEXT: (ref.func $target)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $18
+;; ALWAYS-NEXT: (i32.eqz
+;; ALWAYS-NEXT: (i32.const 8)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $19
+;; ALWAYS-NEXT: (f64.add
+;; ALWAYS-NEXT: (f64.const 2.71828)
+;; ALWAYS-NEXT: (f64.const 3.14159)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $20
+;; ALWAYS-NEXT: (select
+;; ALWAYS-NEXT: (i32.const 9)
+;; ALWAYS-NEXT: (i32.const 10)
+;; ALWAYS-NEXT: (i32.const 11)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $21
+;; ALWAYS-NEXT: (ref.cast nullref
+;; ALWAYS-NEXT: (ref.null none)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $22
+;; ALWAYS-NEXT: (struct.new_default $struct)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (nop)
+;; ALWAYS-NEXT: )
+
+;; CAREFUL: (func $target_2 (type $2) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32)
+;; CAREFUL-NEXT: (drop
+;; CAREFUL-NEXT: (i32.load
+;; CAREFUL-NEXT: (i32.const 6)
+;; CAREFUL-NEXT: )
+;; CAREFUL-NEXT: )
+;; CAREFUL-NEXT: )
+(module
+ ;; ALWAYS: (type $0 (func (param i32) (result i32)))
+
+ ;; ALWAYS: (type $1 (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 anyref funcref i32 f64 i32 anyref anyref) (result i32)))
+
+ ;; ALWAYS: (type $2 (func (param i32 i32 i32 i32 i32 i32)))
+
+ ;; ALWAYS: (type $struct (struct))
+ (type $struct (struct))
+
+ (memory 10 20)
+
+ ;; ALWAYS: (global $imm i32 (i32.const 10))
+ ;; CAREFUL: (type $0 (func (param i32) (result i32)))
+
+ ;; CAREFUL: (type $1 (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 anyref funcref i32 f64 i32 anyref anyref) (result i32)))
+
+ ;; CAREFUL: (type $2 (func (param i32 i32 i32 i32 i32 i32)))
+
+ ;; CAREFUL: (global $imm i32 (i32.const 10))
+ (global $imm i32 (i32.const 10))
+
+ ;; ALWAYS: (global $mut (mut i32) (i32.const 20))
+ ;; CAREFUL: (global $mut (mut i32) (i32.const 20))
+ (global $mut (mut i32) (i32.const 20))
+
+ ;; ALWAYS: (memory $0 10 20)
+
+ ;; ALWAYS: (elem declare func $target)
+
+ ;; ALWAYS: (func $caller (type $0) (param $x i32) (result i32)
+ ;; ALWAYS-NEXT: (block $out (result i32)
+ ;; ALWAYS-NEXT: (call $target_2
+ ;; ALWAYS-NEXT: (br_if $out
+ ;; ALWAYS-NEXT: (i32.const 12)
+ ;; ALWAYS-NEXT: (i32.const 13)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (block (result i32)
+ ;; ALWAYS-NEXT: (i32.const 0)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (if (result i32)
+ ;; ALWAYS-NEXT: (i32.const 1)
+ ;; ALWAYS-NEXT: (then
+ ;; ALWAYS-NEXT: (i32.const 2)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (else
+ ;; ALWAYS-NEXT: (i32.const 3)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (call $caller
+ ;; ALWAYS-NEXT: (i32.const 4)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (local.get $x)
+ ;; ALWAYS-NEXT: (local.tee $x
+ ;; ALWAYS-NEXT: (i32.const 5)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (i32.const 14)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (memory $0 10 20)
+
+ ;; CAREFUL: (func $caller (type $0) (param $x i32) (result i32)
+ ;; CAREFUL-NEXT: (block $out (result i32)
+ ;; CAREFUL-NEXT: (call $target_2
+ ;; CAREFUL-NEXT: (br_if $out
+ ;; CAREFUL-NEXT: (i32.const 12)
+ ;; CAREFUL-NEXT: (i32.const 13)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (block (result i32)
+ ;; CAREFUL-NEXT: (i32.const 0)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (if (result i32)
+ ;; CAREFUL-NEXT: (i32.const 1)
+ ;; CAREFUL-NEXT: (then
+ ;; CAREFUL-NEXT: (i32.const 2)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (else
+ ;; CAREFUL-NEXT: (i32.const 3)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (call $caller
+ ;; CAREFUL-NEXT: (i32.const 4)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (local.get $x)
+ ;; CAREFUL-NEXT: (local.tee $x
+ ;; CAREFUL-NEXT: (i32.const 5)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (i32.const 14)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ (func $caller (param $x i32) (result i32)
+ ;; As above, but now the call is dropped, so the context can include the
+ ;; drop.
+ (block $out (result i32)
+ (drop
+ (call $target
+ (br_if $out
+ (i32.const 12)
+ (i32.const 13)
+ )
+ (block (result i32)
+ (i32.const 0)
+ )
+ (if (result i32)
+ (i32.const 1)
+ (then
+ (i32.const 2)
+ )
+ (else
+ (i32.const 3)
+ )
+ )
+ (call $caller
+ (i32.const 4)
+ )
+ (local.get $x)
+ (local.tee $x
+ (i32.const 5)
+ )
+ (global.get $imm)
+ (global.get $mut)
+ (i32.load
+ (i32.const 6)
+ )
+ (i32.const 7)
+ (ref.null any)
+ (ref.func $target)
+ (i32.eqz
+ (i32.const 8)
+ )
+ (f64.add
+ (f64.const 2.71828)
+ (f64.const 3.14159)
+ )
+ (select
+ (i32.const 9)
+ (i32.const 10)
+ (i32.const 11)
+ )
+ (ref.cast (ref null none)
+ (ref.null none)
+ )
+ (struct.new $struct)
+ )
+ )
+ (i32.const 14)
+ )
+ )
+
+ ;; ALWAYS: (func $target (type $1) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32) (param $6 i32) (param $7 i32) (param $8 i32) (param $9 i32) (param $10 anyref) (param $11 funcref) (param $12 i32) (param $13 f64) (param $14 i32) (param $15 anyref) (param $16 anyref) (result i32)
+ ;; ALWAYS-NEXT: (local.get $7)
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (func $target (type $1) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32) (param $6 i32) (param $7 i32) (param $8 i32) (param $9 i32) (param $10 anyref) (param $11 funcref) (param $12 i32) (param $13 f64) (param $14 i32) (param $15 anyref) (param $16 anyref) (result i32)
+ ;; CAREFUL-NEXT: (local.get $7)
+ ;; CAREFUL-NEXT: )
+ (func $target
+ (param i32)
+ (param i32)
+ (param i32)
+ (param i32)
+ (param i32)
+ (param i32)
+ (param i32)
+ (param i32)
+ (param i32)
+ (param i32)
+ (param anyref)
+ (param funcref)
+ (param i32)
+ (param f64)
+ (param i32)
+ (param anyref)
+ (param anyref)
+ (result i32)
+ (local.get 7)
+ )
+)
+
+
+
+
+;; ALWAYS: (func $target_2 (type $2) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32)
+;; ALWAYS-NEXT: (local $6 i32)
+;; ALWAYS-NEXT: (local $7 i32)
+;; ALWAYS-NEXT: (local $8 i32)
+;; ALWAYS-NEXT: (local $9 i32)
+;; ALWAYS-NEXT: (local $10 i32)
+;; ALWAYS-NEXT: (local $11 i32)
+;; ALWAYS-NEXT: (local $12 i32)
+;; ALWAYS-NEXT: (local $13 i32)
+;; ALWAYS-NEXT: (local $14 i32)
+;; ALWAYS-NEXT: (local $15 i32)
+;; ALWAYS-NEXT: (local $16 anyref)
+;; ALWAYS-NEXT: (local $17 funcref)
+;; ALWAYS-NEXT: (local $18 i32)
+;; ALWAYS-NEXT: (local $19 f64)
+;; ALWAYS-NEXT: (local $20 i32)
+;; ALWAYS-NEXT: (local $21 anyref)
+;; ALWAYS-NEXT: (local $22 anyref)
+;; ALWAYS-NEXT: (drop
+;; ALWAYS-NEXT: (block (result i32)
+;; ALWAYS-NEXT: (local.set $6
+;; ALWAYS-NEXT: (local.get $0)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $7
+;; ALWAYS-NEXT: (local.get $1)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $8
+;; ALWAYS-NEXT: (local.get $2)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $9
+;; ALWAYS-NEXT: (local.get $3)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $10
+;; ALWAYS-NEXT: (local.get $4)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $11
+;; ALWAYS-NEXT: (local.get $5)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $12
+;; ALWAYS-NEXT: (global.get $imm)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $13
+;; ALWAYS-NEXT: (global.get $mut)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $14
+;; ALWAYS-NEXT: (i32.load
+;; ALWAYS-NEXT: (i32.const 6)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $15
+;; ALWAYS-NEXT: (i32.const 7)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $16
+;; ALWAYS-NEXT: (ref.null none)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $17
+;; ALWAYS-NEXT: (ref.func $target)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $18
+;; ALWAYS-NEXT: (i32.eqz
+;; ALWAYS-NEXT: (i32.const 8)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $19
+;; ALWAYS-NEXT: (f64.add
+;; ALWAYS-NEXT: (f64.const 2.71828)
+;; ALWAYS-NEXT: (f64.const 3.14159)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $20
+;; ALWAYS-NEXT: (select
+;; ALWAYS-NEXT: (i32.const 9)
+;; ALWAYS-NEXT: (i32.const 10)
+;; ALWAYS-NEXT: (i32.const 11)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $21
+;; ALWAYS-NEXT: (ref.cast nullref
+;; ALWAYS-NEXT: (ref.null none)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $22
+;; ALWAYS-NEXT: (struct.new_default $struct)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.get $13)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+
+;; CAREFUL: (func $target_2 (type $2) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32)
+;; CAREFUL-NEXT: (drop
+;; CAREFUL-NEXT: (i32.load
+;; CAREFUL-NEXT: (i32.const 6)
+;; CAREFUL-NEXT: )
+;; CAREFUL-NEXT: )
+;; CAREFUL-NEXT: )
+(module
+ (memory 10 20)
+
+ ;; ALWAYS: (type $0 (func))
+
+ ;; ALWAYS: (type $1 (func (param f32)))
+
+ ;; ALWAYS: (type $2 (func (param f64)))
+
+ ;; ALWAYS: (memory $0 10 20)
+
+ ;; ALWAYS: (func $caller (type $0)
+ ;; ALWAYS-NEXT: (call $target_2
+ ;; ALWAYS-NEXT: (block $label$1 (result f64)
+ ;; ALWAYS-NEXT: (f64.const 0)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (call $target_3)
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (type $0 (func))
+
+ ;; CAREFUL: (type $1 (func (param f32)))
+
+ ;; CAREFUL: (memory $0 10 20)
+
+ ;; CAREFUL: (func $caller (type $0)
+ ;; CAREFUL-NEXT: (call $target
+ ;; CAREFUL-NEXT: (f32.demote_f64
+ ;; CAREFUL-NEXT: (block $label$1 (result f64)
+ ;; CAREFUL-NEXT: (f64.const 0)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (call $target_2)
+ ;; CAREFUL-NEXT: )
+ (func $caller
+ ;; Nesting: the f32.demote_f64 operation can be moved into the context, but
+ ;; its child cannot, so we stop there. (In CAREFUL mode, we end up doing
+ ;; nothing here, as the benefit of monomorphization is not worth it.)
+ (call $target
+ (f32.demote_f64
+ (block $label$1 (result f64)
+ (f64.const 0)
+ )
+ )
+ )
+
+ ;; Now the child is an f64.abs, which can be moved into the context, so it
+ ;; all is moved. This ends up worthwhile in CAREFUL mode (since we can
+ ;; optimize all the math here).
+ (call $target
+ (f32.demote_f64
+ (f64.abs ;; this changed
+ (f64.const 0)
+ )
+ )
+ )
+ )
+
+ ;; ALWAYS: (func $target (type $1) (param $f32 f32)
+ ;; ALWAYS-NEXT: (f32.store
+ ;; ALWAYS-NEXT: (i32.const 42)
+ ;; ALWAYS-NEXT: (local.get $f32)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (func $target (type $1) (param $0 f32)
+ ;; CAREFUL-NEXT: (f32.store
+ ;; CAREFUL-NEXT: (i32.const 42)
+ ;; CAREFUL-NEXT: (local.get $0)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ (func $target (param $f32 f32)
+ ;; When monomorphized the first time, the param here will be f64 and not
+ ;; i32, showing we handle a type change.
+ ;;
+ ;; When monomorphized the second time, the param will go away entirely.
+ (f32.store
+ (i32.const 42)
+ (local.get $f32)
+ )
+ )
+)
+
+
+;; ALWAYS: (func $target_2 (type $2) (param $0 f64)
+;; ALWAYS-NEXT: (local $f32 f32)
+;; ALWAYS-NEXT: (local.set $f32
+;; ALWAYS-NEXT: (f32.demote_f64
+;; ALWAYS-NEXT: (local.get $0)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (f32.store
+;; ALWAYS-NEXT: (i32.const 42)
+;; ALWAYS-NEXT: (local.get $f32)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+
+;; ALWAYS: (func $target_3 (type $0)
+;; ALWAYS-NEXT: (local $f32 f32)
+;; ALWAYS-NEXT: (local.set $f32
+;; ALWAYS-NEXT: (f32.demote_f64
+;; ALWAYS-NEXT: (f64.abs
+;; ALWAYS-NEXT: (f64.const 0)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (f32.store
+;; ALWAYS-NEXT: (i32.const 42)
+;; ALWAYS-NEXT: (local.get $f32)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+
+;; CAREFUL: (func $target_2 (type $0)
+;; CAREFUL-NEXT: (f32.store
+;; CAREFUL-NEXT: (i32.const 42)
+;; CAREFUL-NEXT: (f32.const 0)
+;; CAREFUL-NEXT: )
+;; CAREFUL-NEXT: )
+(module
+ ;; ALWAYS: (type $struct (struct (field (mut f32)) (field (mut f64))))
+ ;; CAREFUL: (type $struct (struct (field (mut f32)) (field (mut f64))))
+ (type $struct (struct (field (mut f32)) (field (mut f64))))
+
+ ;; ALWAYS: (type $1 (func (param f32) (result anyref)))
+
+ ;; ALWAYS: (type $2 (func (param f64) (result anyref)))
+
+ ;; ALWAYS: (type $3 (func (param f64)))
+
+ ;; ALWAYS: (type $4 (func (param (ref $struct)) (result anyref)))
+
+ ;; ALWAYS: (func $caller (type $1) (param $x f32) (result anyref)
+ ;; ALWAYS-NEXT: (call $target_4
+ ;; ALWAYS-NEXT: (local.get $x)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (type $1 (func (param f64)))
+
+ ;; CAREFUL: (type $2 (func (param f32) (result anyref)))
+
+ ;; CAREFUL: (type $3 (func (param f64) (result anyref)))
+
+ ;; CAREFUL: (type $4 (func (param (ref $struct)) (result anyref)))
+
+ ;; CAREFUL: (func $caller (type $2) (param $x f32) (result anyref)
+ ;; CAREFUL-NEXT: (call $target
+ ;; CAREFUL-NEXT: (struct.new $struct
+ ;; CAREFUL-NEXT: (local.get $x)
+ ;; CAREFUL-NEXT: (f64.const 4.2)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ (func $caller (param $x f32) (result anyref)
+ ;; We can reverse-inline the struct.new and the nested constant, leaving
+ ;; only the local.get as a remaining param. (In CAREFUL mode, however, this
+ ;; does not look promising enough to optimize.)
+ (call $target
+ (struct.new $struct
+ (local.get $x)
+ (f64.const 4.2)
+ )
+ )
+ )
+
+ ;; ALWAYS: (func $caller-flip (type $2) (param $x f64) (result anyref)
+ ;; ALWAYS-NEXT: (call $target_5
+ ;; ALWAYS-NEXT: (local.get $x)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (func $caller-flip (type $3) (param $x f64) (result anyref)
+ ;; CAREFUL-NEXT: (call $target
+ ;; CAREFUL-NEXT: (struct.new $struct
+ ;; CAREFUL-NEXT: (f32.const 13.369999885559082)
+ ;; CAREFUL-NEXT: (local.get $x)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ (func $caller-flip (param $x f64) (result anyref)
+ ;; As above, but with struct.new's children flipped (which does not change
+ ;; anything).
+ (call $target
+ (struct.new $struct
+ (f32.const 13.37)
+ (local.get $x)
+ )
+ )
+ )
+
+ ;; ALWAYS: (func $dropped-caller (type $3) (param $x f64)
+ ;; ALWAYS-NEXT: (call $target_6
+ ;; ALWAYS-NEXT: (local.get $x)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (func $dropped-caller (type $1) (param $x f64)
+ ;; CAREFUL-NEXT: (call $target_4
+ ;; CAREFUL-NEXT: (local.get $x)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ (func $dropped-caller (param $x f64)
+ ;; As above, but the outcome is dropped. As the target function only does
+ ;; some struct.sets, escape analysis after monomorphization can prove that
+ ;; we can remove all the code, so this is optimized in CAREFUL mode.
+ (drop
+ (call $target
+ (struct.new $struct
+ (f32.const 13.37)
+ (local.get $x)
+ )
+ )
+ )
+ )
+
+ ;; ALWAYS: (func $target (type $4) (param $ref (ref $struct)) (result anyref)
+ ;; ALWAYS-NEXT: (struct.set $struct 0
+ ;; ALWAYS-NEXT: (local.get $ref)
+ ;; ALWAYS-NEXT: (f32.add
+ ;; ALWAYS-NEXT: (struct.get $struct 0
+ ;; ALWAYS-NEXT: (local.get $ref)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (f32.const 1.100000023841858)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (struct.set $struct 1
+ ;; ALWAYS-NEXT: (local.get $ref)
+ ;; ALWAYS-NEXT: (f64.max
+ ;; ALWAYS-NEXT: (struct.get $struct 1
+ ;; ALWAYS-NEXT: (local.get $ref)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (f64.const -1.2)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (local.get $ref)
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (func $target (type $4) (param $0 (ref $struct)) (result anyref)
+ ;; CAREFUL-NEXT: (struct.set $struct 0
+ ;; CAREFUL-NEXT: (local.get $0)
+ ;; CAREFUL-NEXT: (f32.add
+ ;; CAREFUL-NEXT: (struct.get $struct 0
+ ;; CAREFUL-NEXT: (local.get $0)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (f32.const 1.100000023841858)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (struct.set $struct 1
+ ;; CAREFUL-NEXT: (local.get $0)
+ ;; CAREFUL-NEXT: (f64.max
+ ;; CAREFUL-NEXT: (struct.get $struct 1
+ ;; CAREFUL-NEXT: (local.get $0)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (f64.const -1.2)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (local.get $0)
+ ;; CAREFUL-NEXT: )
+ (func $target (param $ref (ref $struct)) (result anyref)
+ ;; Do some operations on the reference, but do not escape it.
+ (struct.set $struct 0
+ (local.get $ref)
+ (f32.add
+ (struct.get $struct 0
+ (local.get $ref)
+ )
+ (f32.const 1.1)
+ )
+ )
+ (struct.set $struct 1
+ (local.get $ref)
+ (f64.max
+ (struct.get $struct 1
+ (local.get $ref)
+ )
+ (f64.const -1.2)
+ )
+ )
+ (local.get $ref)
+ )
+)
+
+;; ALWAYS: (func $target_4 (type $1) (param $0 f32) (result anyref)
+;; ALWAYS-NEXT: (local $ref (ref $struct))
+;; ALWAYS-NEXT: (local.set $ref
+;; ALWAYS-NEXT: (struct.new $struct
+;; ALWAYS-NEXT: (local.get $0)
+;; ALWAYS-NEXT: (f64.const 4.2)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (block (result anyref)
+;; ALWAYS-NEXT: (struct.set $struct 0
+;; ALWAYS-NEXT: (local.get $ref)
+;; ALWAYS-NEXT: (f32.add
+;; ALWAYS-NEXT: (struct.get $struct 0
+;; ALWAYS-NEXT: (local.get $ref)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (f32.const 1.100000023841858)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (struct.set $struct 1
+;; ALWAYS-NEXT: (local.get $ref)
+;; ALWAYS-NEXT: (f64.max
+;; ALWAYS-NEXT: (struct.get $struct 1
+;; ALWAYS-NEXT: (local.get $ref)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (f64.const -1.2)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.get $ref)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+
+;; ALWAYS: (func $target_5 (type $2) (param $0 f64) (result anyref)
+;; ALWAYS-NEXT: (local $ref (ref $struct))
+;; ALWAYS-NEXT: (local.set $ref
+;; ALWAYS-NEXT: (struct.new $struct
+;; ALWAYS-NEXT: (f32.const 13.369999885559082)
+;; ALWAYS-NEXT: (local.get $0)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (block (result anyref)
+;; ALWAYS-NEXT: (struct.set $struct 0
+;; ALWAYS-NEXT: (local.get $ref)
+;; ALWAYS-NEXT: (f32.add
+;; ALWAYS-NEXT: (struct.get $struct 0
+;; ALWAYS-NEXT: (local.get $ref)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (f32.const 1.100000023841858)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (struct.set $struct 1
+;; ALWAYS-NEXT: (local.get $ref)
+;; ALWAYS-NEXT: (f64.max
+;; ALWAYS-NEXT: (struct.get $struct 1
+;; ALWAYS-NEXT: (local.get $ref)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (f64.const -1.2)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.get $ref)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+
+;; ALWAYS: (func $target_6 (type $3) (param $0 f64)
+;; ALWAYS-NEXT: (local $ref (ref $struct))
+;; ALWAYS-NEXT: (drop
+;; ALWAYS-NEXT: (block (result anyref)
+;; ALWAYS-NEXT: (local.set $ref
+;; ALWAYS-NEXT: (struct.new $struct
+;; ALWAYS-NEXT: (f32.const 13.369999885559082)
+;; ALWAYS-NEXT: (local.get $0)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (block (result anyref)
+;; ALWAYS-NEXT: (struct.set $struct 0
+;; ALWAYS-NEXT: (local.get $ref)
+;; ALWAYS-NEXT: (f32.add
+;; ALWAYS-NEXT: (struct.get $struct 0
+;; ALWAYS-NEXT: (local.get $ref)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (f32.const 1.100000023841858)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (struct.set $struct 1
+;; ALWAYS-NEXT: (local.get $ref)
+;; ALWAYS-NEXT: (f64.max
+;; ALWAYS-NEXT: (struct.get $struct 1
+;; ALWAYS-NEXT: (local.get $ref)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (f64.const -1.2)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.get $ref)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+
+;; CAREFUL: (func $target_4 (type $1) (param $0 f64)
+;; CAREFUL-NEXT: (nop)
+;; CAREFUL-NEXT: )
+(module
+ ;; ALWAYS: (type $struct (struct (field i16) (field (mut i8)) (field (mut f64))))
+ ;; CAREFUL: (type $0 (func))
+
+ ;; CAREFUL: (type $struct (struct (field i16) (field (mut i8)) (field (mut f64))))
+ (type $struct (struct (field i16) (field (mut i8)) (field (mut f64))))
+
+ ;; ALWAYS: (type $1 (func))
+
+ ;; ALWAYS: (type $2 (func (param (ref $struct))))
+
+ ;; ALWAYS: (type $3 (func (param i32 f64)))
+
+ ;; ALWAYS: (func $caller (type $1)
+ ;; ALWAYS-NEXT: (local $i32 i32)
+ ;; ALWAYS-NEXT: (local $f64 f64)
+ ;; ALWAYS-NEXT: (call $target_2
+ ;; ALWAYS-NEXT: (local.get $i32)
+ ;; ALWAYS-NEXT: (local.get $f64)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (type $2 (func (param (ref $struct))))
+
+ ;; CAREFUL: (type $3 (func (param i32 f64)))
+
+ ;; CAREFUL: (func $caller (type $0)
+ ;; CAREFUL-NEXT: (local $i32 i32)
+ ;; CAREFUL-NEXT: (local $f64 f64)
+ ;; CAREFUL-NEXT: (call $target_2
+ ;; CAREFUL-NEXT: (local.get $i32)
+ ;; CAREFUL-NEXT: (local.get $f64)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ (func $caller
+ (local $i32 i32)
+ (local $f64 f64)
+ ;; The first operand can be moved to the context, but not the other two. Of
+ ;; those two, the order of iteration matters, as they have different types
+ ;; (if we got mixed up and reordered them, we'd error).
+ (call $target
+ (struct.new $struct
+ (i32.const 0)
+ (local.get $i32)
+ (local.get $f64)
+ )
+ )
+ )
+
+ ;; ALWAYS: (func $target (type $2) (param $0 (ref $struct))
+ ;; ALWAYS-NEXT: (nop)
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (func $target (type $2) (param $0 (ref $struct))
+ ;; CAREFUL-NEXT: (nop)
+ ;; CAREFUL-NEXT: )
+ (func $target (param (ref $struct))
+ (nop)
+ )
+)
+
+;; ALWAYS: (func $target_2 (type $3) (param $0 i32) (param $1 f64)
+;; ALWAYS-NEXT: (local $2 (ref $struct))
+;; ALWAYS-NEXT: (local.set $2
+;; ALWAYS-NEXT: (struct.new $struct
+;; ALWAYS-NEXT: (i32.const 0)
+;; ALWAYS-NEXT: (local.get $0)
+;; ALWAYS-NEXT: (local.get $1)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (nop)
+;; ALWAYS-NEXT: )
+
+;; CAREFUL: (func $target_2 (type $3) (param $0 i32) (param $1 f64)
+;; CAREFUL-NEXT: (nop)
+;; CAREFUL-NEXT: )
+(module
+ ;; ALWAYS: (type $array (sub (array (mut i8))))
+ (type $array (sub (array (mut i8))))
+
+ ;; ALWAYS: (type $1 (func))
+
+ ;; ALWAYS: (type $2 (func (param i32)))
+
+ ;; ALWAYS: (type $3 (func (param anyref anyref) (result i32)))
+
+ ;; ALWAYS: (type $4 (func (param i32 i32 i32)))
+
+ ;; ALWAYS: (func $caller (type $1)
+ ;; ALWAYS-NEXT: (call $target_3)
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (type $0 (func))
+
+ ;; CAREFUL: (type $1 (func (param i32)))
+
+ ;; CAREFUL: (type $2 (func (param anyref anyref) (result i32)))
+
+ ;; CAREFUL: (type $3 (func (param i32 i32 i32)))
+
+ ;; CAREFUL: (func $caller (type $0)
+ ;; CAREFUL-NEXT: (call $target_3)
+ ;; CAREFUL-NEXT: )
+ (func $caller
+ ;; Call the target with array.new which has an optional child, the initial
+ ;; value. Set it in one and leave it as nullptr in the other to see we
+ ;; handle both properly when we move the array.new + children into the
+ ;; monomorphized function.
+ (drop
+ (call $target
+ (array.new_default $array
+ (i32.const 1)
+ )
+ (array.new $array
+ (i32.const 2)
+ (i32.const 3)
+ )
+ )
+ )
+ )
+
+ ;; ALWAYS: (func $caller-unknown (type $2) (param $x i32)
+ ;; ALWAYS-NEXT: (call $target_4
+ ;; ALWAYS-NEXT: (local.get $x)
+ ;; ALWAYS-NEXT: (local.get $x)
+ ;; ALWAYS-NEXT: (local.get $x)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (func $caller-unknown (type $1) (param $x i32)
+ ;; CAREFUL-NEXT: (call $target_4
+ ;; CAREFUL-NEXT: (local.get $x)
+ ;; CAREFUL-NEXT: (local.get $x)
+ ;; CAREFUL-NEXT: (local.get $x)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ (func $caller-unknown (param $x i32)
+ ;; As above, but now there are unknown children, which are not moved.
+ (drop
+ (call $target
+ (array.new_default $array
+ (local.get $x)
+ )
+ (array.new $array
+ (local.get $x)
+ (local.get $x)
+ )
+ )
+ )
+ )
+
+ ;; ALWAYS: (func $target (type $3) (param $0 anyref) (param $1 anyref) (result i32)
+ ;; ALWAYS-NEXT: (unreachable)
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (func $target (type $2) (param $0 anyref) (param $1 anyref) (result i32)
+ ;; CAREFUL-NEXT: (unreachable)
+ ;; CAREFUL-NEXT: )
+ (func $target (param anyref) (param anyref) (result i32)
+ (unreachable)
+ )
+)
+
+
+;; ALWAYS: (func $target_3 (type $1)
+;; ALWAYS-NEXT: (local $0 anyref)
+;; ALWAYS-NEXT: (local $1 anyref)
+;; ALWAYS-NEXT: (local.set $0
+;; ALWAYS-NEXT: (array.new_default $array
+;; ALWAYS-NEXT: (i32.const 1)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $1
+;; ALWAYS-NEXT: (array.new $array
+;; ALWAYS-NEXT: (i32.const 2)
+;; ALWAYS-NEXT: (i32.const 3)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (unreachable)
+;; ALWAYS-NEXT: )
+
+;; ALWAYS: (func $target_4 (type $4) (param $0 i32) (param $1 i32) (param $2 i32)
+;; ALWAYS-NEXT: (local $3 anyref)
+;; ALWAYS-NEXT: (local $4 anyref)
+;; ALWAYS-NEXT: (local.set $3
+;; ALWAYS-NEXT: (array.new_default $array
+;; ALWAYS-NEXT: (local.get $0)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $4
+;; ALWAYS-NEXT: (array.new $array
+;; ALWAYS-NEXT: (local.get $1)
+;; ALWAYS-NEXT: (local.get $2)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (unreachable)
+;; ALWAYS-NEXT: )
+
+;; CAREFUL: (func $target_3 (type $0)
+;; CAREFUL-NEXT: (unreachable)
+;; CAREFUL-NEXT: )
+
+;; CAREFUL: (func $target_4 (type $3) (param $0 i32) (param $1 i32) (param $2 i32)
+;; CAREFUL-NEXT: (unreachable)
+;; CAREFUL-NEXT: )
+(module
+ ;; ALWAYS: (type $struct (struct (field anyref)))
+ (type $struct (struct (field anyref)))
+
+ ;; ALWAYS: (type $1 (func (param anyref) (result anyref)))
+
+ ;; ALWAYS: (func $caller (type $1) (param $x anyref) (result anyref)
+ ;; ALWAYS-NEXT: (call $target_2
+ ;; ALWAYS-NEXT: (local.get $x)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (type $0 (func (param anyref) (result anyref)))
+
+ ;; CAREFUL: (func $caller (type $0) (param $x anyref) (result anyref)
+ ;; CAREFUL-NEXT: (call $target_2
+ ;; CAREFUL-NEXT: (local.get $x)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ (func $caller (param $x anyref) (result anyref)
+ ;; A call with a deeply nested param. We can move all of it but the
+ ;; local.get into the monomorphized function.
+ (call $target
+ (struct.new $struct
+ (struct.new $struct
+ (struct.new $struct
+ (struct.new $struct
+ (struct.new $struct
+ (struct.new $struct
+ (local.get $x)
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+
+ ;; ALWAYS: (func $target (type $1) (param $0 anyref) (result anyref)
+ ;; ALWAYS-NEXT: (unreachable)
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (func $target (type $0) (param $0 anyref) (result anyref)
+ ;; CAREFUL-NEXT: (unreachable)
+ ;; CAREFUL-NEXT: )
+ (func $target (param anyref) (result anyref)
+ (unreachable)
+ )
+)
+
+;; ALWAYS: (func $target_2 (type $1) (param $0 anyref) (result anyref)
+;; ALWAYS-NEXT: (local $1 anyref)
+;; ALWAYS-NEXT: (local.set $1
+;; ALWAYS-NEXT: (struct.new $struct
+;; ALWAYS-NEXT: (struct.new $struct
+;; ALWAYS-NEXT: (struct.new $struct
+;; ALWAYS-NEXT: (struct.new $struct
+;; ALWAYS-NEXT: (struct.new $struct
+;; ALWAYS-NEXT: (struct.new $struct
+;; ALWAYS-NEXT: (local.get $0)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (unreachable)
+;; ALWAYS-NEXT: )
+
+;; CAREFUL: (func $target_2 (type $0) (param $0 anyref) (result anyref)
+;; CAREFUL-NEXT: (unreachable)
+;; CAREFUL-NEXT: )
+(module
+ (memory 10 20)
+
+ ;; ALWAYS: (type $0 (func))
+
+ ;; ALWAYS: (type $1 (func (param i32 i32)))
+
+ ;; ALWAYS: (type $2 (func (param i32)))
+
+ ;; ALWAYS: (memory $0 10 20)
+
+ ;; ALWAYS: (func $caller (type $0)
+ ;; ALWAYS-NEXT: (call $target
+ ;; ALWAYS-NEXT: (i32.load
+ ;; ALWAYS-NEXT: (i32.const 0)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (block (result i32)
+ ;; ALWAYS-NEXT: (i32.store
+ ;; ALWAYS-NEXT: (i32.const 0)
+ ;; ALWAYS-NEXT: (i32.const -1)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (i32.const 11)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (type $0 (func))
+
+ ;; CAREFUL: (type $1 (func (param i32 i32)))
+
+ ;; CAREFUL: (memory $0 10 20)
+
+ ;; CAREFUL: (func $caller (type $0)
+ ;; CAREFUL-NEXT: (call $target
+ ;; CAREFUL-NEXT: (i32.load
+ ;; CAREFUL-NEXT: (i32.const 0)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (block (result i32)
+ ;; CAREFUL-NEXT: (i32.store
+ ;; CAREFUL-NEXT: (i32.const 0)
+ ;; CAREFUL-NEXT: (i32.const -1)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (i32.const 11)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ (func $caller
+ ;; The two operands here cannot be reordered: the first loads, and the
+ ;; second stores. If we move the first into the context but not the second
+ ;; (which is what we'd normally do: the first can be copied, while the
+ ;; second is a control flow structure) then we'd execute the second before
+ ;; the first, as we'd execute the first only after doing the call, which
+ ;; would be wrong.
+ (call $target
+ (i32.load
+ (i32.const 0)
+ )
+ (block (result i32)
+ (i32.store
+ (i32.const 0)
+ (i32.const 0xffffffff)
+ )
+ (i32.const 11)
+ )
+ )
+ )
+
+ ;; ALWAYS: (func $caller-flip (type $0)
+ ;; ALWAYS-NEXT: (call $target_3
+ ;; ALWAYS-NEXT: (block (result i32)
+ ;; ALWAYS-NEXT: (i32.store
+ ;; ALWAYS-NEXT: (i32.const 0)
+ ;; ALWAYS-NEXT: (i32.const -1)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (i32.const 11)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (func $caller-flip (type $0)
+ ;; CAREFUL-NEXT: (call $target
+ ;; CAREFUL-NEXT: (block (result i32)
+ ;; CAREFUL-NEXT: (i32.store
+ ;; CAREFUL-NEXT: (i32.const 0)
+ ;; CAREFUL-NEXT: (i32.const -1)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (i32.const 11)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (i32.load
+ ;; CAREFUL-NEXT: (i32.const 0)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ (func $caller-flip
+ ;; With the order reversed, there is no problem: the load can be moved into
+ ;; the context (in ALWAYS, at least).
+ (call $target
+ (block (result i32)
+ (i32.store
+ (i32.const 0)
+ (i32.const 0xffffffff)
+ )
+ (i32.const 11)
+ )
+ (i32.load
+ (i32.const 0)
+ )
+ )
+ )
+
+ ;; ALWAYS: (func $target (type $1) (param $0 i32) (param $1 i32)
+ ;; ALWAYS-NEXT: (nop)
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (func $target (type $1) (param $0 i32) (param $1 i32)
+ ;; CAREFUL-NEXT: (nop)
+ ;; CAREFUL-NEXT: )
+ (func $target (param i32) (param i32)
+ )
+)
+
+;; ALWAYS: (func $target_3 (type $2) (param $0 i32)
+;; ALWAYS-NEXT: (local $1 i32)
+;; ALWAYS-NEXT: (local $2 i32)
+;; ALWAYS-NEXT: (local.set $1
+;; ALWAYS-NEXT: (local.get $0)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $2
+;; ALWAYS-NEXT: (i32.load
+;; ALWAYS-NEXT: (i32.const 0)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (nop)
+;; ALWAYS-NEXT: )
+(module
+ (memory 10 20)
+
+ ;; ALWAYS: (type $0 (func (param i32)))
+
+ ;; ALWAYS: (type $1 (func))
+
+ ;; ALWAYS: (memory $0 10 20)
+
+ ;; ALWAYS: (func $caller (type $1)
+ ;; ALWAYS-NEXT: (call $target_2
+ ;; ALWAYS-NEXT: (block (result i32)
+ ;; ALWAYS-NEXT: (i32.load
+ ;; ALWAYS-NEXT: (i32.const 0)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (type $0 (func))
+
+ ;; CAREFUL: (type $1 (func (param i32)))
+
+ ;; CAREFUL: (memory $0 10 20)
+
+ ;; CAREFUL: (func $caller (type $0)
+ ;; CAREFUL-NEXT: (call $target
+ ;; CAREFUL-NEXT: (i32.atomic.rmw8.cmpxchg_u
+ ;; CAREFUL-NEXT: (i32.const 0)
+ ;; CAREFUL-NEXT: (block (result i32)
+ ;; CAREFUL-NEXT: (i32.load
+ ;; CAREFUL-NEXT: (i32.const 0)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (i32.const 1)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ (func $caller
+ ;; Effect interaction between a parent and child: the child reads while the
+ ;; parent writes (and also reads), so they cannot be reordered. But the
+ ;; parent executes later anyhow, so it is fine to optimize the atomic
+ ;; operation into the context.
+ (call $target
+ (i32.atomic.rmw8.cmpxchg_u
+ (i32.const 0)
+ (block (result i32) ;; Use a block to prevent the child from being
+ ;; moved into the context.
+ (i32.load
+ (i32.const 0)
+ )
+ )
+ (i32.const 1)
+ )
+ )
+ ;; Note that we cannot test the reverse, since if the block were on the
+ ;; outside then anything inside it would not be moved.
+ )
+
+ ;; ALWAYS: (func $target (type $0) (param $0 i32)
+ ;; ALWAYS-NEXT: (nop)
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (func $target (type $1) (param $0 i32)
+ ;; CAREFUL-NEXT: (nop)
+ ;; CAREFUL-NEXT: )
+ (func $target (param i32)
+ )
+)
+
+;; ALWAYS: (func $target_2 (type $0) (param $0 i32)
+;; ALWAYS-NEXT: (local $1 i32)
+;; ALWAYS-NEXT: (local.set $1
+;; ALWAYS-NEXT: (i32.atomic.rmw8.cmpxchg_u
+;; ALWAYS-NEXT: (i32.const 0)
+;; ALWAYS-NEXT: (local.get $0)
+;; ALWAYS-NEXT: (i32.const 1)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (nop)
+;; ALWAYS-NEXT: )
+(module
+ ;; ALWAYS: (type $0 (func))
+
+ ;; ALWAYS: (type $struct (struct (field i32) (field i32)))
+ (type $struct (struct (field i32) (field i32)))
+
+ (memory 10 20)
+
+ ;; ALWAYS: (type $2 (func (param anyref)))
+
+ ;; ALWAYS: (type $3 (func (param i32 i32)))
+
+ ;; ALWAYS: (type $4 (func (param i32)))
+
+ ;; ALWAYS: (memory $0 10 20)
+
+ ;; ALWAYS: (func $caller (type $0)
+ ;; ALWAYS-NEXT: (call $target_3
+ ;; ALWAYS-NEXT: (i32.load
+ ;; ALWAYS-NEXT: (i32.const 0)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (block (result i32)
+ ;; ALWAYS-NEXT: (i32.store
+ ;; ALWAYS-NEXT: (i32.const 0)
+ ;; ALWAYS-NEXT: (i32.const 1)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (i32.const 2)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (type $0 (func))
+
+ ;; CAREFUL: (type $1 (func (param anyref)))
+
+ ;; CAREFUL: (type $2 (func (param i32 i32)))
+
+ ;; CAREFUL: (type $3 (func (param i32)))
+
+ ;; CAREFUL: (memory $0 10 20)
+
+ ;; CAREFUL: (func $caller (type $0)
+ ;; CAREFUL-NEXT: (call $target_3
+ ;; CAREFUL-NEXT: (i32.load
+ ;; CAREFUL-NEXT: (i32.const 0)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (block (result i32)
+ ;; CAREFUL-NEXT: (i32.store
+ ;; CAREFUL-NEXT: (i32.const 0)
+ ;; CAREFUL-NEXT: (i32.const 1)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (i32.const 2)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ (func $caller
+ ;; Effect interaction between children of a call operand. The read and write
+ ;; cannot be reordered, so all we can move into the call context is the
+ ;; struct.new.
+ (call $target
+ (struct.new $struct
+ (i32.load
+ (i32.const 0)
+ )
+ (block (result i32) ;; Use a block to prevent the child from being
+ ;; moved into the context.
+ (i32.store
+ (i32.const 0)
+ (i32.const 1)
+ )
+ (i32.const 2)
+ )
+ )
+ )
+ )
+
+ ;; ALWAYS: (func $caller-flip (type $0)
+ ;; ALWAYS-NEXT: (call $target_4
+ ;; ALWAYS-NEXT: (block (result i32)
+ ;; ALWAYS-NEXT: (i32.store
+ ;; ALWAYS-NEXT: (i32.const 0)
+ ;; ALWAYS-NEXT: (i32.const 1)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (i32.const 2)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (func $caller-flip (type $0)
+ ;; CAREFUL-NEXT: (call $target_4
+ ;; CAREFUL-NEXT: (block (result i32)
+ ;; CAREFUL-NEXT: (i32.store
+ ;; CAREFUL-NEXT: (i32.const 0)
+ ;; CAREFUL-NEXT: (i32.const 1)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (i32.const 2)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ (func $caller-flip
+ ;; With the order reversed, there is no problem, and the load can also be
+ ;; optimized into the context.
+ (call $target
+ (struct.new $struct
+ (block (result i32)
+ (i32.store
+ (i32.const 0)
+ (i32.const 1)
+ )
+ (i32.const 2)
+ )
+ (i32.load
+ (i32.const 0)
+ )
+ )
+ )
+ )
+
+ ;; ALWAYS: (func $target (type $2) (param $0 anyref)
+ ;; ALWAYS-NEXT: (nop)
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (func $target (type $1) (param $0 anyref)
+ ;; CAREFUL-NEXT: (nop)
+ ;; CAREFUL-NEXT: )
+ (func $target (param anyref)
+ )
+)
+
+
+;; ALWAYS: (func $target_3 (type $3) (param $0 i32) (param $1 i32)
+;; ALWAYS-NEXT: (local $2 anyref)
+;; ALWAYS-NEXT: (local.set $2
+;; ALWAYS-NEXT: (struct.new $struct
+;; ALWAYS-NEXT: (local.get $0)
+;; ALWAYS-NEXT: (local.get $1)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (nop)
+;; ALWAYS-NEXT: )
+
+;; ALWAYS: (func $target_4 (type $4) (param $0 i32)
+;; ALWAYS-NEXT: (local $1 anyref)
+;; ALWAYS-NEXT: (local.set $1
+;; ALWAYS-NEXT: (struct.new $struct
+;; ALWAYS-NEXT: (local.get $0)
+;; ALWAYS-NEXT: (i32.load
+;; ALWAYS-NEXT: (i32.const 0)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (nop)
+;; ALWAYS-NEXT: )
+
+;; CAREFUL: (func $target_3 (type $2) (param $0 i32) (param $1 i32)
+;; CAREFUL-NEXT: (nop)
+;; CAREFUL-NEXT: )
+
+;; CAREFUL: (func $target_4 (type $3) (param $0 i32)
+;; CAREFUL-NEXT: (drop
+;; CAREFUL-NEXT: (i32.load
+;; CAREFUL-NEXT: (i32.const 0)
+;; CAREFUL-NEXT: )
+;; CAREFUL-NEXT: )
+;; CAREFUL-NEXT: )
+(module
+ (memory 10 20)
+
+ ;; ALWAYS: (type $0 (func))
+
+ ;; ALWAYS: (type $1 (func (param i32 i32 i32 i32 i32 i32)))
+
+ ;; ALWAYS: (type $2 (func (param i32 i32 i32 i32 i32)))
+
+ ;; ALWAYS: (memory $0 10 20)
+
+ ;; ALWAYS: (func $caller (type $0)
+ ;; ALWAYS-NEXT: (call $target_2
+ ;; ALWAYS-NEXT: (block (result i32)
+ ;; ALWAYS-NEXT: (i32.store
+ ;; ALWAYS-NEXT: (i32.const 0)
+ ;; ALWAYS-NEXT: (i32.const -1)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (i32.const 11)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (i32.load
+ ;; ALWAYS-NEXT: (i32.const 0)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (block (result i32)
+ ;; ALWAYS-NEXT: (i32.store
+ ;; ALWAYS-NEXT: (i32.const 0)
+ ;; ALWAYS-NEXT: (i32.const -1)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (i32.const 11)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (i32.load
+ ;; ALWAYS-NEXT: (i32.const 0)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (block (result i32)
+ ;; ALWAYS-NEXT: (i32.store
+ ;; ALWAYS-NEXT: (i32.const 0)
+ ;; ALWAYS-NEXT: (i32.const -1)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (i32.const 11)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (type $0 (func))
+
+ ;; CAREFUL: (type $1 (func (param i32 i32 i32 i32 i32 i32)))
+
+ ;; CAREFUL: (memory $0 10 20)
+
+ ;; CAREFUL: (func $caller (type $0)
+ ;; CAREFUL-NEXT: (call $target
+ ;; CAREFUL-NEXT: (block (result i32)
+ ;; CAREFUL-NEXT: (i32.store
+ ;; CAREFUL-NEXT: (i32.const 0)
+ ;; CAREFUL-NEXT: (i32.const -1)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (i32.const 11)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (i32.load
+ ;; CAREFUL-NEXT: (i32.const 0)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (block (result i32)
+ ;; CAREFUL-NEXT: (i32.store
+ ;; CAREFUL-NEXT: (i32.const 0)
+ ;; CAREFUL-NEXT: (i32.const -1)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (i32.const 11)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (i32.load
+ ;; CAREFUL-NEXT: (i32.const 0)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (block (result i32)
+ ;; CAREFUL-NEXT: (i32.store
+ ;; CAREFUL-NEXT: (i32.const 0)
+ ;; CAREFUL-NEXT: (i32.const -1)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (i32.const 11)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (i32.load
+ ;; CAREFUL-NEXT: (i32.const 0)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ (func $caller
+ ;; Similar to before, but with a sequence of interleaved things with
+ ;; interactions. The same two items repeat three times. Any load cannot be
+ ;; moved into the context if there is a store after it, which means that the
+ ;; last load can be optimized and nothing else (in CAREFUL mode, however,
+ ;; that ends up not worthwhile).
+ (call $target
+ (block (result i32)
+ (i32.store
+ (i32.const 0)
+ (i32.const 0xffffffff)
+ )
+ (i32.const 11)
+ )
+ (i32.load
+ (i32.const 0)
+ )
+ (block (result i32)
+ (i32.store
+ (i32.const 0)
+ (i32.const 0xffffffff)
+ )
+ (i32.const 11)
+ )
+ (i32.load
+ (i32.const 0)
+ )
+ (block (result i32)
+ (i32.store
+ (i32.const 0)
+ (i32.const 0xffffffff)
+ )
+ (i32.const 11)
+ )
+ (i32.load
+ (i32.const 0)
+ )
+ )
+ )
+
+ ;; ALWAYS: (func $target (type $1) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32)
+ ;; ALWAYS-NEXT: (nop)
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (func $target (type $1) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32)
+ ;; CAREFUL-NEXT: (nop)
+ ;; CAREFUL-NEXT: )
+ (func $target (param i32) (param i32) (param i32) (param i32) (param i32) (param i32)
+ )
+)
+
+
+;; ALWAYS: (func $target_2 (type $2) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32)
+;; ALWAYS-NEXT: (local $5 i32)
+;; ALWAYS-NEXT: (local $6 i32)
+;; ALWAYS-NEXT: (local $7 i32)
+;; ALWAYS-NEXT: (local $8 i32)
+;; ALWAYS-NEXT: (local $9 i32)
+;; ALWAYS-NEXT: (local $10 i32)
+;; ALWAYS-NEXT: (local.set $5
+;; ALWAYS-NEXT: (local.get $0)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $6
+;; ALWAYS-NEXT: (local.get $1)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $7
+;; ALWAYS-NEXT: (local.get $2)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $8
+;; ALWAYS-NEXT: (local.get $3)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $9
+;; ALWAYS-NEXT: (local.get $4)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $10
+;; ALWAYS-NEXT: (i32.load
+;; ALWAYS-NEXT: (i32.const 0)
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (nop)
+;; ALWAYS-NEXT: )
+(module
+ ;; ALWAYS: (type $0 (func))
+
+ ;; ALWAYS: (type $1 (func (param f32)))
+
+ ;; ALWAYS: (func $caller (type $0)
+ ;; ALWAYS-NEXT: (local $tuple (tuple i32 f32))
+ ;; ALWAYS-NEXT: (call $target
+ ;; ALWAYS-NEXT: (tuple.extract 2 1
+ ;; ALWAYS-NEXT: (local.get $tuple)
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (type $0 (func))
+
+ ;; CAREFUL: (type $1 (func (param f32)))
+
+ ;; CAREFUL: (func $caller (type $0)
+ ;; CAREFUL-NEXT: (local $tuple (tuple i32 f32))
+ ;; CAREFUL-NEXT: (call $target
+ ;; CAREFUL-NEXT: (tuple.extract 2 1
+ ;; CAREFUL-NEXT: (local.get $tuple)
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: )
+ (func $caller
+ (local $tuple (tuple i32 f32))
+ ;; We cannot move the tuple.extract into the context, as that would mean the
+ ;; new call has a tuple param. Rather than handle that somehow, ignore it.
+ (call $target
+ (tuple.extract 2 1
+ (local.get $tuple)
+ )
+ )
+ )
+
+ ;; ALWAYS: (func $target (type $1) (param $0 f32)
+ ;; ALWAYS-NEXT: (nop)
+ ;; ALWAYS-NEXT: )
+ ;; CAREFUL: (func $target (type $1) (param $0 f32)
+ ;; CAREFUL-NEXT: (nop)
+ ;; CAREFUL-NEXT: )
+ (func $target (param $0 f32)
+ )
+)
+
diff --git a/test/lit/passes/monomorphize-mvp.wast b/test/lit/passes/monomorphize-mvp.wast
index 567a4c2ce..ade1f6895 100644
--- a/test/lit/passes/monomorphize-mvp.wast
+++ b/test/lit/passes/monomorphize-mvp.wast
@@ -12,38 +12,28 @@
;; RUN: foreach %s %t wasm-opt --monomorphize -S -o - | filecheck %s --check-prefix CAREFUL
(module
- ;; ALWAYS: (type $0 (func (result i32)))
+ ;; ALWAYS: (type $0 (func (param i32) (result i32)))
;; ALWAYS: (type $1 (func (param i32 i32) (result i32)))
- ;; ALWAYS: (type $2 (func (param i32) (result i32)))
-
- ;; ALWAYS: (func $call (result i32)
+ ;; ALWAYS: (func $call (param $x i32) (result i32)
;; ALWAYS-NEXT: (call $target_2
- ;; ALWAYS-NEXT: (i32.eqz
- ;; ALWAYS-NEXT: (i32.const 2)
- ;; ALWAYS-NEXT: )
+ ;; ALWAYS-NEXT: (local.get $x)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
- ;; CAREFUL: (type $0 (func (result i32)))
+ ;; CAREFUL: (type $0 (func (param i32) (result i32)))
;; CAREFUL: (type $1 (func (param i32 i32) (result i32)))
- ;; CAREFUL: (type $2 (func (param i32) (result i32)))
-
- ;; CAREFUL: (func $call (result i32)
+ ;; CAREFUL: (func $call (param $x i32) (result i32)
;; CAREFUL-NEXT: (call $target_2
- ;; CAREFUL-NEXT: (i32.eqz
- ;; CAREFUL-NEXT: (i32.const 2)
- ;; CAREFUL-NEXT: )
+ ;; CAREFUL-NEXT: (local.get $x)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: )
- (func $call (result i32)
+ (func $call (param $x i32) (result i32)
;; The second parameter can be monomorphized.
(call $target
- (i32.eqz
- (i32.const 2)
- )
+ (local.get $x)
(i32.const 1)
)
)
diff --git a/test/lit/passes/monomorphize-types.wast b/test/lit/passes/monomorphize-types.wast
index 49ae8353a..7d90a0f87 100644
--- a/test/lit/passes/monomorphize-types.wast
+++ b/test/lit/passes/monomorphize-types.wast
@@ -10,95 +10,97 @@
;; ALWAYS: (type $A (sub (struct)))
;; CAREFUL: (type $A (sub (struct)))
(type $A (sub (struct)))
+ ;; ALWAYS: (type $1 (func (param (ref $A))))
+
;; ALWAYS: (type $B (sub $A (struct)))
+ ;; CAREFUL: (type $1 (func (param (ref $A))))
+
;; CAREFUL: (type $B (sub $A (struct)))
(type $B (sub $A (struct)))
- ;; ALWAYS: (type $2 (func (param (ref $A))))
-
- ;; ALWAYS: (type $3 (func))
+ ;; ALWAYS: (type $3 (func (param (ref $B))))
- ;; ALWAYS: (type $4 (func (param (ref $B))))
+ ;; ALWAYS: (type $4 (func (param (ref $A) (ref $B))))
- ;; ALWAYS: (import "a" "b" (func $import (type $2) (param (ref $A))))
- ;; CAREFUL: (type $2 (func (param (ref $A))))
+ ;; ALWAYS: (import "a" "b" (func $import (type $1) (param (ref $A))))
+ ;; CAREFUL: (type $3 (func (param (ref $A) (ref $B))))
- ;; CAREFUL: (type $3 (func))
+ ;; CAREFUL: (type $4 (func (param (ref $B))))
- ;; CAREFUL: (import "a" "b" (func $import (type $2) (param (ref $A))))
+ ;; CAREFUL: (import "a" "b" (func $import (type $1) (param (ref $A))))
(import "a" "b" (func $import (param (ref $A))))
- ;; ALWAYS: (func $calls (type $3)
+ ;; ALWAYS: (func $calls (type $4) (param $A (ref $A)) (param $B (ref $B))
;; ALWAYS-NEXT: (call $refinable
- ;; ALWAYS-NEXT: (struct.new_default $A)
+ ;; ALWAYS-NEXT: (local.get $A)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: (call $refinable
- ;; ALWAYS-NEXT: (struct.new_default $A)
+ ;; ALWAYS-NEXT: (local.get $A)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: (call $refinable_4
- ;; ALWAYS-NEXT: (struct.new_default $B)
+ ;; ALWAYS-NEXT: (local.get $B)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: (call $refinable_4
- ;; ALWAYS-NEXT: (struct.new_default $B)
+ ;; ALWAYS-NEXT: (local.get $B)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
- ;; CAREFUL: (func $calls (type $3)
+ ;; CAREFUL: (func $calls (type $3) (param $A (ref $A)) (param $B (ref $B))
;; CAREFUL-NEXT: (call $refinable
- ;; CAREFUL-NEXT: (struct.new_default $A)
+ ;; CAREFUL-NEXT: (local.get $A)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: (call $refinable
- ;; CAREFUL-NEXT: (struct.new_default $A)
+ ;; CAREFUL-NEXT: (local.get $A)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: (call $refinable
- ;; CAREFUL-NEXT: (struct.new_default $B)
+ ;; CAREFUL-NEXT: (local.get $B)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: (call $refinable
- ;; CAREFUL-NEXT: (struct.new_default $B)
+ ;; CAREFUL-NEXT: (local.get $B)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: )
- (func $calls
+ (func $calls (param $A (ref $A)) (param $B (ref $B))
;; Two calls with $A, two with $B. The calls to $B should both go to the
;; same new function which has a refined parameter of $B.
;;
;; However, in CAREFUL mode we won't do that, as there is no helpful
;; improvement in the target functions even with the refined types.
(call $refinable
- (struct.new $A)
+ (local.get $A)
)
(call $refinable
- (struct.new $A)
+ (local.get $A)
)
(call $refinable
- (struct.new $B)
+ (local.get $B)
)
(call $refinable
- (struct.new $B)
+ (local.get $B)
)
)
- ;; ALWAYS: (func $call-import (type $3)
+ ;; ALWAYS: (func $call-import (type $3) (param $B (ref $B))
;; ALWAYS-NEXT: (call $import
- ;; ALWAYS-NEXT: (struct.new_default $B)
+ ;; ALWAYS-NEXT: (local.get $B)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
- ;; CAREFUL: (func $call-import (type $3)
+ ;; CAREFUL: (func $call-import (type $4) (param $B (ref $B))
;; CAREFUL-NEXT: (call $import
- ;; CAREFUL-NEXT: (struct.new_default $B)
+ ;; CAREFUL-NEXT: (local.get $B)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: )
- (func $call-import
+ (func $call-import (param $B (ref $B))
;; Calls to imports are left as they are.
(call $import
- (struct.new $B)
+ (local.get $B)
)
)
- ;; ALWAYS: (func $refinable (type $2) (param $ref (ref $A))
+ ;; ALWAYS: (func $refinable (type $1) (param $ref (ref $A))
;; ALWAYS-NEXT: (drop
;; ALWAYS-NEXT: (local.get $ref)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
- ;; CAREFUL: (func $refinable (type $2) (param $0 (ref $A))
+ ;; CAREFUL: (func $refinable (type $1) (param $0 (ref $A))
;; CAREFUL-NEXT: (nop)
;; CAREFUL-NEXT: )
(func $refinable (param $ref (ref $A))
@@ -115,7 +117,7 @@
)
-;; ALWAYS: (func $refinable_4 (type $4) (param $0 (ref $B))
+;; ALWAYS: (func $refinable_4 (type $3) (param $0 (ref $B))
;; ALWAYS-NEXT: (local $ref (ref $A))
;; ALWAYS-NEXT: (local.set $ref
;; ALWAYS-NEXT: (local.get $0)
@@ -129,8 +131,6 @@
;; requires a fixup.
;; ALWAYS: (type $A (sub (struct)))
- ;; CAREFUL: (type $0 (func))
-
;; CAREFUL: (type $A (sub (struct)))
(type $A (sub (struct)))
;; ALWAYS: (type $B (sub $A (struct)))
@@ -139,27 +139,27 @@
- ;; ALWAYS: (type $2 (func))
+ ;; ALWAYS: (type $2 (func (param (ref $B))))
;; ALWAYS: (type $3 (func (param (ref $A))))
- ;; ALWAYS: (type $4 (func (param (ref $B))))
-
- ;; ALWAYS: (func $calls (type $2)
+ ;; ALWAYS: (func $calls (type $2) (param $B (ref $B))
;; ALWAYS-NEXT: (call $refinable_2
- ;; ALWAYS-NEXT: (struct.new_default $B)
+ ;; ALWAYS-NEXT: (local.get $B)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
+ ;; CAREFUL: (type $2 (func (param (ref $B))))
+
;; CAREFUL: (type $3 (func (param (ref $A))))
- ;; CAREFUL: (func $calls (type $0)
+ ;; CAREFUL: (func $calls (type $2) (param $B (ref $B))
;; CAREFUL-NEXT: (call $refinable
- ;; CAREFUL-NEXT: (struct.new_default $B)
+ ;; CAREFUL-NEXT: (local.get $B)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: )
- (func $calls
+ (func $calls (param $B (ref $B))
(call $refinable
- (struct.new $B)
+ (local.get $B)
)
)
@@ -190,7 +190,7 @@
)
-;; ALWAYS: (func $refinable_2 (type $4) (param $0 (ref $B))
+;; ALWAYS: (func $refinable_2 (type $2) (param $0 (ref $B))
;; ALWAYS-NEXT: (local $unref (ref $A))
;; ALWAYS-NEXT: (local $ref (ref $A))
;; ALWAYS-NEXT: (local.set $ref
@@ -209,84 +209,88 @@
;; Multiple refinings of the same function, and of different functions.
;; ALWAYS: (type $A (sub (struct)))
- ;; CAREFUL: (type $0 (func))
-
;; CAREFUL: (type $A (sub (struct)))
(type $A (sub (struct)))
;; ALWAYS: (type $B (sub $A (struct)))
+ ;; CAREFUL: (type $1 (func (param (ref $A))))
+
;; CAREFUL: (type $B (sub $A (struct)))
(type $B (sub $A (struct)))
- ;; ALWAYS: (type $2 (func))
+ ;; ALWAYS: (type $2 (func (param (ref $A))))
+
+ ;; ALWAYS: (type $3 (func (param (ref $B))))
;; ALWAYS: (type $C (sub $B (struct)))
- ;; CAREFUL: (type $3 (func (param (ref $A))))
+ ;; CAREFUL: (type $3 (func (param (ref $A) (ref $B))))
;; CAREFUL: (type $C (sub $B (struct)))
(type $C (sub $B (struct)))
- ;; ALWAYS: (type $4 (func (param (ref $A))))
+ ;; ALWAYS: (type $5 (func (param (ref $A) (ref $B))))
- ;; ALWAYS: (type $5 (func (param (ref $B))))
+ ;; ALWAYS: (type $6 (func (param (ref $C) (ref $B))))
- ;; ALWAYS: (type $6 (func (param (ref $C))))
+ ;; ALWAYS: (type $7 (func (param (ref $C))))
- ;; ALWAYS: (func $calls1 (type $2)
+ ;; ALWAYS: (func $calls1 (type $5) (param $A (ref $A)) (param $B (ref $B))
;; ALWAYS-NEXT: (call $refinable1
- ;; ALWAYS-NEXT: (struct.new_default $A)
+ ;; ALWAYS-NEXT: (local.get $A)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: (call $refinable1_4
- ;; ALWAYS-NEXT: (struct.new_default $B)
+ ;; ALWAYS-NEXT: (local.get $B)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
- ;; CAREFUL: (func $calls1 (type $0)
+ ;; CAREFUL: (type $5 (func (param (ref $C) (ref $B))))
+
+ ;; CAREFUL: (func $calls1 (type $3) (param $A (ref $A)) (param $B (ref $B))
;; CAREFUL-NEXT: (call $refinable1
- ;; CAREFUL-NEXT: (struct.new_default $A)
+ ;; CAREFUL-NEXT: (local.get $A)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: (call $refinable1
- ;; CAREFUL-NEXT: (struct.new_default $B)
+ ;; CAREFUL-NEXT: (local.get $B)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: )
- (func $calls1
+ (func $calls1 (param $A (ref $A)) (param $B (ref $B))
(call $refinable1
- (struct.new $A)
+ (local.get $A)
)
(call $refinable1
- (struct.new $B)
+ (local.get $B)
)
)
- ;; ALWAYS: (func $calls2 (type $2)
+ ;; ALWAYS: (func $calls2 (type $6) (param $C (ref $C)) (param $B (ref $B))
;; ALWAYS-NEXT: (call $refinable1_5
- ;; ALWAYS-NEXT: (struct.new_default $C)
+ ;; ALWAYS-NEXT: (local.get $C)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: (call $refinable2_6
- ;; ALWAYS-NEXT: (struct.new_default $B)
+ ;; ALWAYS-NEXT: (local.get $B)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
- ;; CAREFUL: (func $calls2 (type $0)
+ ;; CAREFUL: (func $calls2 (type $5) (param $C (ref $C)) (param $B (ref $B))
;; CAREFUL-NEXT: (call $refinable1
- ;; CAREFUL-NEXT: (struct.new_default $C)
+ ;; CAREFUL-NEXT: (local.get $C)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: (call $refinable2
- ;; CAREFUL-NEXT: (struct.new_default $B)
+ ;; CAREFUL-NEXT: (local.get $B)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: )
- (func $calls2
+ (func $calls2 (param $C (ref $C)) (param $B (ref $B))
(call $refinable1
- (struct.new $C)
+ (local.get $C)
)
(call $refinable2
- (struct.new $B)
+ (local.get $B)
)
)
- ;; ALWAYS: (func $refinable1 (type $4) (param $ref (ref $A))
+ ;; ALWAYS: (func $refinable1 (type $2) (param $ref (ref $A))
;; ALWAYS-NEXT: (drop
;; ALWAYS-NEXT: (local.get $ref)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
- ;; CAREFUL: (func $refinable1 (type $3) (param $0 (ref $A))
+ ;; CAREFUL: (func $refinable1 (type $1) (param $0 (ref $A))
;; CAREFUL-NEXT: (nop)
;; CAREFUL-NEXT: )
(func $refinable1 (param $ref (ref $A))
@@ -295,12 +299,12 @@
)
)
- ;; ALWAYS: (func $refinable2 (type $4) (param $ref (ref $A))
+ ;; ALWAYS: (func $refinable2 (type $2) (param $ref (ref $A))
;; ALWAYS-NEXT: (drop
;; ALWAYS-NEXT: (local.get $ref)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
- ;; CAREFUL: (func $refinable2 (type $3) (param $0 (ref $A))
+ ;; CAREFUL: (func $refinable2 (type $1) (param $0 (ref $A))
;; CAREFUL-NEXT: (nop)
;; CAREFUL-NEXT: )
(func $refinable2 (param $ref (ref $A))
@@ -310,7 +314,7 @@
)
)
-;; ALWAYS: (func $refinable1_4 (type $5) (param $0 (ref $B))
+;; ALWAYS: (func $refinable1_4 (type $3) (param $0 (ref $B))
;; ALWAYS-NEXT: (local $ref (ref $A))
;; ALWAYS-NEXT: (local.set $ref
;; ALWAYS-NEXT: (local.get $0)
@@ -320,7 +324,7 @@
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
-;; ALWAYS: (func $refinable1_5 (type $6) (param $0 (ref $C))
+;; ALWAYS: (func $refinable1_5 (type $7) (param $0 (ref $C))
;; ALWAYS-NEXT: (local $ref (ref $A))
;; ALWAYS-NEXT: (local.set $ref
;; ALWAYS-NEXT: (local.get $0)
@@ -330,7 +334,7 @@
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
-;; ALWAYS: (func $refinable2_6 (type $5) (param $0 (ref $B))
+;; ALWAYS: (func $refinable2_6 (type $3) (param $0 (ref $B))
;; ALWAYS-NEXT: (local $ref (ref $A))
;; ALWAYS-NEXT: (local.set $ref
;; ALWAYS-NEXT: (local.get $0)
@@ -351,72 +355,84 @@
;; CAREFUL: (type $B (sub $A (struct)))
(type $B (sub $A (struct)))
- ;; ALWAYS: (type $2 (func (param (ref $B))))
+ ;; ALWAYS: (type $2 (func (param (ref $A) (ref $B))))
- ;; ALWAYS: (type $3 (func))
+ ;; ALWAYS: (type $3 (func (param (ref $B))))
- ;; ALWAYS: (type $4 (func (param (ref $A))))
+ ;; ALWAYS: (type $4 (func (param (ref $B) (ref $B))))
- ;; ALWAYS: (import "a" "b" (func $import (type $2) (param (ref $B))))
- ;; CAREFUL: (type $2 (func (param (ref $B))))
+ ;; ALWAYS: (import "a" "b" (func $import (type $3) (param (ref $B))))
+ ;; CAREFUL: (type $2 (func (param (ref $A) (ref $B))))
- ;; CAREFUL: (type $3 (func))
+ ;; CAREFUL: (type $3 (func (param (ref $B))))
- ;; CAREFUL: (type $4 (func (param (ref $A))))
+ ;; CAREFUL: (type $4 (func (param (ref $B) (ref $B))))
- ;; CAREFUL: (import "a" "b" (func $import (type $2) (param (ref $B))))
+ ;; CAREFUL: (import "a" "b" (func $import (type $3) (param (ref $B))))
(import "a" "b" (func $import (param (ref $B))))
;; ALWAYS: (global $global (mut i32) (i32.const 1))
;; CAREFUL: (global $global (mut i32) (i32.const 1))
(global $global (mut i32) (i32.const 1))
- ;; ALWAYS: (func $calls (type $3)
+ ;; ALWAYS: (func $calls (type $2) (param $A (ref $A)) (param $B (ref $B))
;; ALWAYS-NEXT: (call $refinable
- ;; ALWAYS-NEXT: (struct.new_default $A)
+ ;; ALWAYS-NEXT: (local.get $A)
+ ;; ALWAYS-NEXT: (local.get $B)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: (call $refinable
- ;; ALWAYS-NEXT: (struct.new_default $A)
+ ;; ALWAYS-NEXT: (local.get $A)
+ ;; ALWAYS-NEXT: (local.get $B)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: (call $refinable_3
- ;; ALWAYS-NEXT: (struct.new_default $B)
+ ;; ALWAYS-NEXT: (local.get $B)
+ ;; ALWAYS-NEXT: (local.get $B)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: (call $refinable_3
- ;; ALWAYS-NEXT: (struct.new_default $B)
+ ;; ALWAYS-NEXT: (local.get $B)
+ ;; ALWAYS-NEXT: (local.get $B)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
- ;; CAREFUL: (func $calls (type $3)
+ ;; CAREFUL: (func $calls (type $2) (param $A (ref $A)) (param $B (ref $B))
;; CAREFUL-NEXT: (call $refinable
- ;; CAREFUL-NEXT: (struct.new_default $A)
+ ;; CAREFUL-NEXT: (local.get $A)
+ ;; CAREFUL-NEXT: (local.get $B)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: (call $refinable
- ;; CAREFUL-NEXT: (struct.new_default $A)
+ ;; CAREFUL-NEXT: (local.get $A)
+ ;; CAREFUL-NEXT: (local.get $B)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: (call $refinable_3
- ;; CAREFUL-NEXT: (struct.new_default $B)
+ ;; CAREFUL-NEXT: (local.get $B)
+ ;; CAREFUL-NEXT: (local.get $B)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: (call $refinable_3
- ;; CAREFUL-NEXT: (struct.new_default $B)
+ ;; CAREFUL-NEXT: (local.get $B)
+ ;; CAREFUL-NEXT: (local.get $B)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: )
- (func $calls
+ (func $calls (param $A (ref $A)) (param $B (ref $B))
;; The calls sending $B will switch to calling a refined version, as the
;; refined version is better, even in CAREFUL mode.
(call $refinable
- (struct.new $A)
+ (local.get $A)
+ (local.get $B)
)
(call $refinable
- (struct.new $A)
+ (local.get $A)
+ (local.get $B)
)
(call $refinable
- (struct.new $B)
+ (local.get $B)
+ (local.get $B)
)
(call $refinable
- (struct.new $B)
+ (local.get $B)
+ (local.get $B)
)
)
- ;; ALWAYS: (func $refinable (type $4) (param $ref (ref $A))
+ ;; ALWAYS: (func $refinable (type $2) (param $ref (ref $A)) (param $B (ref $B))
;; ALWAYS-NEXT: (local $x (ref $A))
;; ALWAYS-NEXT: (call $import
;; ALWAYS-NEXT: (ref.cast (ref $B)
@@ -426,7 +442,7 @@
;; ALWAYS-NEXT: (local.set $x
;; ALWAYS-NEXT: (select (result (ref $A))
;; ALWAYS-NEXT: (local.get $ref)
- ;; ALWAYS-NEXT: (struct.new_default $B)
+ ;; ALWAYS-NEXT: (local.get $B)
;; ALWAYS-NEXT: (global.get $global)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
@@ -446,33 +462,32 @@
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
- ;; CAREFUL: (func $refinable (type $4) (param $0 (ref $A))
- ;; CAREFUL-NEXT: (local $1 (ref $B))
+ ;; CAREFUL: (func $refinable (type $2) (param $0 (ref $A)) (param $1 (ref $B))
;; CAREFUL-NEXT: (local $2 (ref $B))
;; CAREFUL-NEXT: (call $import
- ;; CAREFUL-NEXT: (local.tee $1
+ ;; CAREFUL-NEXT: (local.tee $2
;; CAREFUL-NEXT: (ref.cast (ref $B)
;; CAREFUL-NEXT: (local.get $0)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: (call $import
- ;; CAREFUL-NEXT: (local.tee $2
+ ;; CAREFUL-NEXT: (local.tee $1
;; CAREFUL-NEXT: (select (result (ref $B))
+ ;; CAREFUL-NEXT: (local.get $2)
;; CAREFUL-NEXT: (local.get $1)
- ;; CAREFUL-NEXT: (struct.new_default $B)
;; CAREFUL-NEXT: (global.get $global)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: (call $import
- ;; CAREFUL-NEXT: (local.get $2)
+ ;; CAREFUL-NEXT: (local.get $1)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: (call $import
- ;; CAREFUL-NEXT: (local.get $1)
+ ;; CAREFUL-NEXT: (local.get $2)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: )
- (func $refinable (param $ref (ref $A))
+ (func $refinable (param $ref (ref $A)) (param $B (ref $B))
;; Note that this large function will end up optimized in CAREFUL mode, as a
;; side effect of our keeping optimizations we run for comparison purposes.
@@ -494,7 +509,7 @@
;; Use a select here so optimizations don't just merge $x and $ref.
(select (result (ref $A))
(local.get $ref)
- (struct.new $B)
+ (local.get $B)
(global.get $global)
)
)
@@ -517,12 +532,16 @@
)
)
-;; ALWAYS: (func $refinable_3 (type $2) (param $0 (ref $B))
+;; ALWAYS: (func $refinable_3 (type $4) (param $0 (ref $B)) (param $1 (ref $B))
;; ALWAYS-NEXT: (local $x (ref $A))
;; ALWAYS-NEXT: (local $ref (ref $A))
+;; ALWAYS-NEXT: (local $B (ref $B))
;; ALWAYS-NEXT: (local.set $ref
;; ALWAYS-NEXT: (local.get $0)
;; ALWAYS-NEXT: )
+;; ALWAYS-NEXT: (local.set $B
+;; ALWAYS-NEXT: (local.get $1)
+;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: (block
;; ALWAYS-NEXT: (call $import
;; ALWAYS-NEXT: (ref.cast (ref $B)
@@ -532,7 +551,7 @@
;; ALWAYS-NEXT: (local.set $x
;; ALWAYS-NEXT: (select (result (ref $A))
;; ALWAYS-NEXT: (local.get $ref)
-;; ALWAYS-NEXT: (struct.new_default $B)
+;; ALWAYS-NEXT: (local.get $B)
;; ALWAYS-NEXT: (global.get $global)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
@@ -554,8 +573,7 @@
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
-;; CAREFUL: (func $refinable_3 (type $2) (param $0 (ref $B))
-;; CAREFUL-NEXT: (local $1 (ref $B))
+;; CAREFUL: (func $refinable_3 (type $4) (param $0 (ref $B)) (param $1 (ref $B))
;; CAREFUL-NEXT: (call $import
;; CAREFUL-NEXT: (local.get $0)
;; CAREFUL-NEXT: )
@@ -563,7 +581,7 @@
;; CAREFUL-NEXT: (local.tee $1
;; CAREFUL-NEXT: (select (result (ref $B))
;; CAREFUL-NEXT: (local.get $0)
-;; CAREFUL-NEXT: (struct.new_default $B)
+;; CAREFUL-NEXT: (local.get $1)
;; CAREFUL-NEXT: (global.get $global)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: )
@@ -581,30 +599,30 @@
;; ALWAYS: (type $A (sub (struct)))
;; CAREFUL: (type $A (sub (struct)))
(type $A (sub (struct)))
- ;; ALWAYS: (type $1 (func (param (ref $A))))
-
;; ALWAYS: (type $B (sub $A (struct)))
- ;; CAREFUL: (type $1 (func (param (ref $A))))
-
;; CAREFUL: (type $B (sub $A (struct)))
(type $B (sub $A (struct)))
- ;; ALWAYS: (func $calls (type $1) (param $ref (ref $A))
+ ;; ALWAYS: (type $2 (func (param (ref $B))))
+
+ ;; ALWAYS: (func $calls (type $2) (param $B (ref $B))
;; ALWAYS-NEXT: (call $calls
- ;; ALWAYS-NEXT: (struct.new_default $B)
+ ;; ALWAYS-NEXT: (local.get $B)
;; ALWAYS-NEXT: )
;; ALWAYS-NEXT: )
- ;; CAREFUL: (func $calls (type $1) (param $ref (ref $A))
+ ;; CAREFUL: (type $2 (func (param (ref $B))))
+
+ ;; CAREFUL: (func $calls (type $2) (param $B (ref $B))
;; CAREFUL-NEXT: (call $calls
- ;; CAREFUL-NEXT: (struct.new_default $B)
+ ;; CAREFUL-NEXT: (local.get $B)
;; CAREFUL-NEXT: )
;; CAREFUL-NEXT: )
- (func $calls (param $ref (ref $A))
+ (func $calls (param $B (ref $B))
;; We should change nothing in this recursive call, even though we are
;; sending a more refined type, so we could try to monomorphize in theory.
(call $calls
- (struct.new $B)
+ (local.get $B)
)
)
)
diff --git a/test/lit/passes/no-inline-monomorphize-inlining.wast b/test/lit/passes/no-inline-monomorphize-inlining.wast
index b5dc2ed39..2479b46f4 100644
--- a/test/lit/passes/no-inline-monomorphize-inlining.wast
+++ b/test/lit/passes/no-inline-monomorphize-inlining.wast
@@ -19,87 +19,87 @@
;; YESINLINE: (type $B (sub $A (struct)))
(type $B (sub $A (struct)))
- ;; NO_INLINE: (type $2 (func))
+ ;; NO_INLINE: (type $2 (func (param (ref $A) (ref $B))))
;; NO_INLINE: (type $3 (func (param (ref $A))))
;; NO_INLINE: (type $4 (func (param (ref $B))))
- ;; NO_INLINE: (func $calls (type $2)
+ ;; NO_INLINE: (func $calls (type $2) (param $A (ref $A)) (param $B (ref $B))
;; NO_INLINE-NEXT: (call $refinable_noinline
- ;; NO_INLINE-NEXT: (struct.new_default $A)
+ ;; NO_INLINE-NEXT: (local.get $A)
;; NO_INLINE-NEXT: )
;; NO_INLINE-NEXT: (call $refinable_noinline
- ;; NO_INLINE-NEXT: (struct.new_default $A)
+ ;; NO_INLINE-NEXT: (local.get $A)
;; NO_INLINE-NEXT: )
;; NO_INLINE-NEXT: (call $refinable_noinline_2
- ;; NO_INLINE-NEXT: (struct.new_default $B)
+ ;; NO_INLINE-NEXT: (local.get $B)
;; NO_INLINE-NEXT: )
;; NO_INLINE-NEXT: (call $refinable_noinline_2
- ;; NO_INLINE-NEXT: (struct.new_default $B)
+ ;; NO_INLINE-NEXT: (local.get $B)
;; NO_INLINE-NEXT: )
;; NO_INLINE-NEXT: )
- ;; YESINLINE: (type $2 (func))
+ ;; YESINLINE: (type $2 (func (param (ref $A) (ref $B))))
- ;; YESINLINE: (func $calls (type $2)
- ;; YESINLINE-NEXT: (local $0 (ref $A))
- ;; YESINLINE-NEXT: (local $1 (ref $A))
- ;; YESINLINE-NEXT: (local $2 (ref $B))
+ ;; YESINLINE: (func $calls (type $2) (param $A (ref $A)) (param $B (ref $B))
+ ;; YESINLINE-NEXT: (local $2 (ref $A))
;; YESINLINE-NEXT: (local $3 (ref $A))
;; YESINLINE-NEXT: (local $4 (ref $B))
;; YESINLINE-NEXT: (local $5 (ref $A))
+ ;; YESINLINE-NEXT: (local $6 (ref $B))
+ ;; YESINLINE-NEXT: (local $7 (ref $A))
;; YESINLINE-NEXT: (block
;; YESINLINE-NEXT: (block $__inlined_func$refinable_noinline
- ;; YESINLINE-NEXT: (local.set $0
- ;; YESINLINE-NEXT: (struct.new_default $A)
+ ;; YESINLINE-NEXT: (local.set $2
+ ;; YESINLINE-NEXT: (local.get $A)
;; YESINLINE-NEXT: )
;; YESINLINE-NEXT: (drop
- ;; YESINLINE-NEXT: (local.get $0)
+ ;; YESINLINE-NEXT: (local.get $2)
;; YESINLINE-NEXT: )
;; YESINLINE-NEXT: )
;; YESINLINE-NEXT: )
;; YESINLINE-NEXT: (block
;; YESINLINE-NEXT: (block $__inlined_func$refinable_noinline$1
- ;; YESINLINE-NEXT: (local.set $1
- ;; YESINLINE-NEXT: (struct.new_default $A)
+ ;; YESINLINE-NEXT: (local.set $3
+ ;; YESINLINE-NEXT: (local.get $A)
;; YESINLINE-NEXT: )
;; YESINLINE-NEXT: (drop
- ;; YESINLINE-NEXT: (local.get $1)
+ ;; YESINLINE-NEXT: (local.get $3)
;; YESINLINE-NEXT: )
;; YESINLINE-NEXT: )
;; YESINLINE-NEXT: )
;; YESINLINE-NEXT: (block
;; YESINLINE-NEXT: (block $__inlined_func$refinable_noinline_2$2
- ;; YESINLINE-NEXT: (local.set $2
- ;; YESINLINE-NEXT: (struct.new_default $B)
+ ;; YESINLINE-NEXT: (local.set $4
+ ;; YESINLINE-NEXT: (local.get $B)
;; YESINLINE-NEXT: )
;; YESINLINE-NEXT: (block
- ;; YESINLINE-NEXT: (local.set $3
- ;; YESINLINE-NEXT: (local.get $2)
+ ;; YESINLINE-NEXT: (local.set $5
+ ;; YESINLINE-NEXT: (local.get $4)
;; YESINLINE-NEXT: )
;; YESINLINE-NEXT: (drop
- ;; YESINLINE-NEXT: (local.get $3)
+ ;; YESINLINE-NEXT: (local.get $5)
;; YESINLINE-NEXT: )
;; YESINLINE-NEXT: )
;; YESINLINE-NEXT: )
;; YESINLINE-NEXT: )
;; YESINLINE-NEXT: (block
;; YESINLINE-NEXT: (block $__inlined_func$refinable_noinline_2$3
- ;; YESINLINE-NEXT: (local.set $4
- ;; YESINLINE-NEXT: (struct.new_default $B)
+ ;; YESINLINE-NEXT: (local.set $6
+ ;; YESINLINE-NEXT: (local.get $B)
;; YESINLINE-NEXT: )
;; YESINLINE-NEXT: (block
- ;; YESINLINE-NEXT: (local.set $5
- ;; YESINLINE-NEXT: (local.get $4)
+ ;; YESINLINE-NEXT: (local.set $7
+ ;; YESINLINE-NEXT: (local.get $6)
;; YESINLINE-NEXT: )
;; YESINLINE-NEXT: (drop
- ;; YESINLINE-NEXT: (local.get $5)
+ ;; YESINLINE-NEXT: (local.get $7)
;; YESINLINE-NEXT: )
;; YESINLINE-NEXT: )
;; YESINLINE-NEXT: )
;; YESINLINE-NEXT: )
;; YESINLINE-NEXT: )
- (func $calls
+ (func $calls (param $A (ref $A)) (param $B (ref $B))
;; Two calls with $A, two with $B. The calls to $B will both go to the
;; same new monomorphized function which has a refined parameter of $B.
;;
@@ -108,16 +108,16 @@
;; inline the monomorphized ones). In YESINLINE mode we will inline all 4.
;;
(call $refinable_noinline
- (struct.new $A)
+ (local.get $A)
)
(call $refinable_noinline
- (struct.new $A)
+ (local.get $A)
)
(call $refinable_noinline
- (struct.new $B)
+ (local.get $B)
)
(call $refinable_noinline
- (struct.new $B)
+ (local.get $B)
)
)