summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/passes/Heap2Local.cpp78
-rw-r--r--test/lit/passes/heap2local.wast262
2 files changed, 249 insertions, 91 deletions
diff --git a/src/passes/Heap2Local.cpp b/src/passes/Heap2Local.cpp
index afdcab06f..a2665958e 100644
--- a/src/passes/Heap2Local.cpp
+++ b/src/passes/Heap2Local.cpp
@@ -265,6 +265,31 @@ struct Heap2LocalOptimizer {
walk(func->body);
}
+ // Rewrite the code in visit* methods. The general approach taken is to
+ // replace the allocation with a null reference (which may require changing
+ // types in some places, like making a block return value nullable), and to
+ // remove all uses of it as much as possible, using the information we have
+ // (for example, when our allocation reaches a RefAsNonNull we can simply
+ // remove that operation as we know it would not throw). Some things are
+ // left to other passes, like getting rid of dropped code without side
+ // effects.
+
+ void visitBlock(Block* curr) {
+ if (!reached.count(curr)) {
+ return;
+ }
+
+ // Our allocation passes through this block. We must turn its type into a
+ // nullable one, because we will remove things like RefAsNonNull of it,
+ // which means we may no longer have a non-nullable value as our input,
+ // and we could fail to validate. It is safe to make this change in terms
+ // of our parent, since we know very specifically that only safe things
+ // will end up using our value, like a StructGet or a Drop, which do not
+ // care about non-nullability.
+ assert(curr->type.isRef());
+ curr->type = Type(curr->type.getHeapType(), Nullable);
+ }
+
void visitLocalSet(LocalSet* curr) {
if (!reached.count(curr)) {
return;
@@ -285,16 +310,21 @@ struct Heap2LocalOptimizer {
}
}
+ void visitBreak(Break* curr) {
+ if (!reached.count(curr)) {
+ return;
+ }
+
+ // Breaks that our allocation flows through may change type, as we now
+ // have a nullable type there.
+ curr->finalize();
+ }
+
void visitStructNew(StructNew* curr) {
if (curr != allocation) {
return;
}
- // We do not remove the allocation itself here, rather we make it
- // unnecessary, and then depend on other optimizations to clean up. (We
- // cannot simply remove it because we need to replace it with something of
- // the same non-nullable type.)
-
// First, assign the initial values to the new locals.
std::vector<Expression*> contents;
@@ -340,21 +370,12 @@ struct Heap2LocalOptimizer {
builder.makeLocalGet(tempIndexes[i], fields[i].type)));
}
- // Read the values in the allocation (we don't need to, as the
- // allocation is not used after our optimization, but we need something
- // with the right type anyhow).
- for (Index i = 0; i < tempIndexes.size(); i++) {
- allocation->operands[i] =
- builder.makeLocalGet(localIndexes[i], fields[i].type);
- }
-
// TODO Check if the nondefault case does not increase code size in some
// cases. A heap allocation that implicitly sets the default values
// is smaller than multiple explicit settings of locals to
// defaults.
} else {
- // Set the default values, and replace the allocation with a block that
- // first does that, then contains the allocation.
+ // Set the default values.
// Note that we must assign the defaults because we might be in a loop,
// that is, there might be a previous value.
for (Index i = 0; i < localIndexes.size(); i++) {
@@ -364,12 +385,27 @@ struct Heap2LocalOptimizer {
}
}
- // Put the allocation itself at the end of the block, so the block has the
- // exact same type as the allocation it replaces.
- contents.push_back(allocation);
+ // Drop the RTT (as it may have side effects; leave it to other passes).
+ contents.push_back(builder.makeDrop(allocation->rtt));
+ // Replace the allocation with a null reference. This changes the type
+ // from non-nullable to nullable, but as we optimize away the code that
+ // the allocation reaches, we will handle that.
+ contents.push_back(
+ builder.makeRefNull(Type(allocation->type.getHeapType(), Nullable)));
replaceCurrent(builder.makeBlock(contents));
}
+ void visitRefAs(RefAs* curr) {
+ if (!reached.count(curr)) {
+ return;
+ }
+
+ // It is safe to optimize out this RefAsNonNull, since we proved it
+ // contains our allocation, and so cannot trap.
+ assert(curr->op == RefAsNonNull);
+ replaceCurrent(curr->value);
+ }
+
void visitStructSet(StructSet* curr) {
if (!reached.count(curr)) {
return;
@@ -570,12 +606,6 @@ struct Heap2LocalOptimizer {
// null (so there is no trap), and we can continue to (hopefully)
// optimize this allocation.
escapes = false;
-
- // Note that while we can look through this operation, we cannot get
- // rid of it later, as its parent might depend on receiving a
- // non-nullable type. So we will leave the RefAsNonNull as it is,
- // even if we do optimize the allocation, and we depend on other
- // passes to remove the RefAsNonNull.
}
}
diff --git a/test/lit/passes/heap2local.wast b/test/lit/passes/heap2local.wast
index 3736e4990..9f0807b3b 100644
--- a/test/lit/passes/heap2local.wast
+++ b/test/lit/passes/heap2local.wast
@@ -17,16 +17,17 @@
;; CHECK-NEXT: (local $0 i32)
;; CHECK-NEXT: (local $1 f64)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $0
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
@@ -45,16 +46,17 @@
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local $2 f64)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
@@ -77,16 +79,17 @@
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $0
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $0)
@@ -114,16 +117,17 @@
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result f64)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $0
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $1)
@@ -145,16 +149,17 @@
;; CHECK-NEXT: (local $0 i32)
;; CHECK-NEXT: (local $1 f64)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $0
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $0
@@ -199,7 +204,7 @@
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
@@ -212,11 +217,10 @@
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (local.get $3)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_with_rtt $struct.A
- ;; CHECK-NEXT: (local.get $0)
- ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $0)
@@ -292,16 +296,17 @@
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local $2 f64)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block
@@ -334,16 +339,17 @@
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local $2 f64)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block (result f64)
@@ -377,16 +383,17 @@
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local $2 f64)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
@@ -450,16 +457,17 @@
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local $2 f64)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
@@ -580,16 +588,17 @@
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local $2 f64)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
@@ -624,16 +633,17 @@
;; CHECK-NEXT: (local $2 i32)
;; CHECK-NEXT: (local $3 f64)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $3
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
@@ -686,16 +696,17 @@
;; CHECK-NEXT: (local $2 i32)
;; CHECK-NEXT: (local $3 f64)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $3
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (if
@@ -736,16 +747,17 @@
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local $2 f64)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block (result f64)
@@ -820,16 +832,17 @@
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local $2 f64)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $1)
@@ -850,13 +863,14 @@
;; CHECK-NEXT: (local $ref (ref null $struct.recursive))
;; CHECK-NEXT: (local $1 (ref null $struct.recursive))
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.recursive))
+ ;; CHECK-NEXT: (block (result (ref null $struct.recursive))
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (ref.null $struct.recursive)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.recursive
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.recursive)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.recursive)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $1
@@ -906,7 +920,7 @@
;; CHECK-NEXT: (local $1 (ref null $struct.recursive))
;; CHECK-NEXT: (local $2 (ref null $struct.recursive))
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.recursive))
+ ;; CHECK-NEXT: (block (result (ref null $struct.recursive))
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (struct.new_default_with_rtt $struct.recursive
;; CHECK-NEXT: (rtt.canon $struct.recursive)
@@ -915,10 +929,10 @@
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (local.get $2)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_with_rtt $struct.recursive
- ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.recursive)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.recursive)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
@@ -1017,7 +1031,7 @@
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref $struct.A))
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.nonnullable))
+ ;; CHECK-NEXT: (block (result (ref null $struct.nonnullable))
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (local.get $a)
;; CHECK-NEXT: )
@@ -1026,12 +1040,10 @@
;; CHECK-NEXT: (local.get $2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_with_rtt $struct.nonnullable
- ;; CHECK-NEXT: (ref.as_non_null
- ;; CHECK-NEXT: (local.get $1)
- ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.nonnullable)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.nonnullable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.as_non_null
@@ -1061,7 +1073,7 @@
;; CHECK-NEXT: (local $5 f64)
;; CHECK-NEXT: (loop $outer
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $4
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
@@ -1074,11 +1086,10 @@
;; CHECK-NEXT: (local.set $3
;; CHECK-NEXT: (local.get $5)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_with_rtt $struct.A
- ;; CHECK-NEXT: (local.get $2)
- ;; CHECK-NEXT: (local.get $3)
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
@@ -1219,16 +1230,17 @@
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $0
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $0)
@@ -1237,16 +1249,17 @@
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $3
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $2)
@@ -1255,16 +1268,17 @@
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result f64)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $4
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $5
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $5)
@@ -1303,16 +1317,17 @@
;; CHECK-NEXT: (local $3 i32)
;; CHECK-NEXT: (local $4 f64)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
@@ -1324,16 +1339,17 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $3
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $4
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
@@ -1379,29 +1395,31 @@
;; CHECK-NEXT: (local $4 i32)
;; CHECK-NEXT: (local $5 f64)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $3
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $4
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $5
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
@@ -1537,16 +1555,17 @@
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local $2 f64)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block (result f64)
@@ -1749,23 +1768,22 @@
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local $2 f64)
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct.A))
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (f64.const 0)
;; CHECK-NEXT: )
- ;; CHECK-NEXT: (struct.new_default_with_rtt $struct.A
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (rtt.canon $struct.A)
;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (ref.as_non_null
- ;; CHECK-NEXT: (local.get $ref)
- ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (i32.const 1)
@@ -1800,4 +1818,114 @@
)
)
)
+
+ ;; CHECK: (func $ref-as-non-null-through-local (result i32)
+ ;; CHECK-NEXT: (local $ref (ref null $struct.A))
+ ;; CHECK-NEXT: (local $1 i32)
+ ;; CHECK-NEXT: (local $2 f64)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
+ ;; CHECK-NEXT: (local.set $1
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.set $2
+ ;; CHECK-NEXT: (f64.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (rtt.canon $struct.A)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $ref)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block (result i32)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $ref)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $ref-as-non-null-through-local (result i32)
+ (local $ref (ref null $struct.A))
+ (local.set $ref
+ (struct.new_default_with_rtt $struct.A
+ (rtt.canon $struct.A)
+ )
+ )
+ ;; Copy the allocation through a ref.as_non_null. This must not trap: it may
+ ;; trap if we leave the ref.as_non_null there and also we do not assign
+ ;; anything to the local (if we skip assignments to the local when we
+ ;; optimize). To avoid that, we should remove the ref.as_non_null, which is
+ ;; safe since we know our allocation is passed into it, which is not null,
+ ;; and will not trap.
+ (local.set $ref
+ (ref.as_non_null
+ (local.get $ref)
+ )
+ )
+ (struct.get $struct.A 0
+ (local.get $ref)
+ )
+ )
+
+ ;; CHECK: (func $br_if-allocation (result f64)
+ ;; CHECK-NEXT: (local $0 (ref null $struct.A))
+ ;; CHECK-NEXT: (local $1 i32)
+ ;; CHECK-NEXT: (local $2 f64)
+ ;; CHECK-NEXT: (local $3 i32)
+ ;; CHECK-NEXT: (local $4 f64)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $block (result (ref null $struct.A))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (br_if $block
+ ;; CHECK-NEXT: (block (result (ref null $struct.A))
+ ;; CHECK-NEXT: (local.set $3
+ ;; CHECK-NEXT: (i32.const 42)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.set $4
+ ;; CHECK-NEXT: (f64.const 13.37)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.set $1
+ ;; CHECK-NEXT: (local.get $3)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.set $2
+ ;; CHECK-NEXT: (local.get $4)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (rtt.canon $struct.A)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null $struct.A)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (return
+ ;; CHECK-NEXT: (f64.const 2.1828)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.get $2)
+ ;; CHECK-NEXT: )
+ (func $br_if-allocation (result f64)
+ (local $0 (ref null $struct.A))
+ (struct.get $struct.A 1
+ (block $block (result (ref null $struct.A))
+ (drop
+ ;; Our allocation flows into a br_if, which therefore has non-nullable
+ ;; type, which we must update after optimizing.
+ (br_if $block
+ (struct.new_with_rtt $struct.A
+ (i32.const 42)
+ (f64.const 13.37)
+ (rtt.canon $struct.A)
+ )
+ (i32.const 0)
+ )
+ )
+ (return (f64.const 2.1828))
+ )
+ )
+ )
)