diff options
-rw-r--r-- | src/passes/Heap2Local.cpp | 78 | ||||
-rw-r--r-- | test/lit/passes/heap2local.wast | 262 |
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)) + ) + ) + ) ) |