diff --git a/src/passes/Heap2Local.cpp b/src/passes/Heap2Local.cpp index 7515d1999e9..ca5a4a3413c 100644 --- a/src/passes/Heap2Local.cpp +++ b/src/passes/Heap2Local.cpp @@ -461,40 +461,31 @@ struct EscapeAnalyzer { } } void visitArraySet(ArraySet* curr) { - if (!curr->index->is()) { - // Array operations on nonconstant indexes do not escape in the normal - // sense, but they do escape from our being able to analyze them, so - // stop as soon as we see one. - return; - } - - // As StructGet. - if (curr->ref == child) { + // Arrays flowing into array operations on nonconstant indexes do not + // escape in the normal sense, but they do escape from our being able to + // analyze them, so stop as soon as we see one. + if (child == curr->ref && curr->index->is()) { escapes = false; fullyConsumes = true; } } void visitArrayGet(ArrayGet* curr) { - if (!curr->index->is()) { - return; + if (child == curr->ref && curr->index->is()) { + escapes = false; + fullyConsumes = true; } - escapes = false; - fullyConsumes = true; } void visitArrayRMW(ArrayRMW* curr) { - if (!curr->index->is()) { - return; - } - if (curr->ref == child) { + if (child == curr->ref && curr->index->is()) { escapes = false; fullyConsumes = true; } } void visitArrayCmpxchg(ArrayCmpxchg* curr) { - if (!curr->index->is()) { - return; - } - if (curr->ref == child || curr->expected == child) { + // Allocations flowing into `expected` are fully consumed and + // optimizable even if the index is not constant. + if (child == curr->expected || + (child == curr->ref && curr->index->is())) { escapes = false; fullyConsumes = true; } @@ -1233,9 +1224,15 @@ struct Struct2Local : PostWalker { auto refScratch = builder.addVar(func, refType); auto* setRefScratch = builder.makeLocalSet(refScratch, curr->ref); auto* getRefScratch = builder.makeLocalGet(refScratch, refType); + + auto indexScratch = builder.addVar(func, Type::i32); + auto* setIndexScratch = builder.makeLocalSet(indexScratch, curr->index); + auto* getIndexScratch = builder.makeLocalGet(indexScratch, Type::i32); + auto* arrayGet = builder.makeArrayGet( - getRefScratch, curr->index, curr->order, curr->type); + getRefScratch, getIndexScratch, curr->order, curr->type); auto* block = builder.makeBlock({setRefScratch, + setIndexScratch, builder.makeDrop(curr->expected), builder.makeDrop(curr->replacement), arrayGet}); @@ -1467,20 +1464,20 @@ struct Array2Struct : PostWalker { return; } - auto index = getIndex(curr->index); - if (index >= numFields) { - replaceCurrent(builder.makeBlock({builder.makeDrop(curr->ref), - builder.makeDrop(curr->expected), - builder.makeDrop(curr->replacement), - builder.makeUnreachable()})); - refinalize = true; - return; - } - // The allocation might flow into `ref` or `expected`, but not // `replacement`, because then it would be considered to have escaped. if (analyzer.getInteraction(curr->ref) == ParentChildInteraction::Flows) { - // The accessed array is being optimzied. Convert the ArrayCmpxchg into a + auto index = getIndex(curr->index); + if (index >= numFields) { + replaceCurrent(builder.makeBlock({builder.makeDrop(curr->ref), + builder.makeDrop(curr->expected), + builder.makeDrop(curr->replacement), + builder.makeUnreachable()})); + refinalize = true; + return; + } + + // The accessed array is being optimized. Convert the ArrayCmpxchg into a // StructCmpxchg. replaceCurrent(builder.makeStructCmpxchg( index, curr->ref, curr->expected, curr->replacement, curr->order)); diff --git a/test/lit/passes/heap2local-rmw.wast b/test/lit/passes/heap2local-rmw.wast index 48fb88df62b..7d329d2b53e 100644 --- a/test/lit/passes/heap2local-rmw.wast +++ b/test/lit/passes/heap2local-rmw.wast @@ -1465,11 +1465,15 @@ ;; CHECK: (func $array-cmpxchg-expected (type $1) (param $array (ref $array)) ;; CHECK-NEXT: (local $1 eqref) ;; CHECK-NEXT: (local $2 (ref null $array)) + ;; CHECK-NEXT: (local $3 i32) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result eqref) ;; CHECK-NEXT: (local.set $2 ;; CHECK-NEXT: (local.get $array) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $3 + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result nullref) ;; CHECK-NEXT: (local.set $1 @@ -1483,7 +1487,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: (array.atomic.get $array ;; CHECK-NEXT: (local.get $2) - ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (local.get $3) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -1504,3 +1508,104 @@ ) ) ) + +(module + ;; CHECK: (type $array (array (mut eqref))) + (type $array (array (mut eqref))) + ;; CHECK: (type $1 (func (param (ref $array)))) + + ;; CHECK: (type $2 (func (result i32))) + + ;; CHECK: (type $3 (func (result eqref))) + + ;; CHECK: (import "" "" (func $effect-i32 (type $2) (result i32))) + (import "" "" (func $effect-i32 (result i32))) + ;; CHECK: (import "" "" (func $effect-eq (type $3) (result eqref))) + (import "" "" (func $effect-eq (result eqref))) + + ;; CHECK: (func $array-cmpxchg-expected-index-effect (type $1) (param $array (ref $array)) + ;; CHECK-NEXT: (local $1 eqref) + ;; CHECK-NEXT: (local $2 (ref null $array)) + ;; CHECK-NEXT: (local $3 i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result eqref) + ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (local.get $array) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $3 + ;; CHECK-NEXT: (call $effect-i32) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $effect-eq) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (array.atomic.get $array + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: (local.get $3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $array-cmpxchg-expected-index-effect (param $array (ref $array)) + (drop + ;; The index is non-constant, but we can still optimize the expected + ;; field. We must preserve the index and the order of its effects. + (array.atomic.rmw.cmpxchg $array + (local.get $array) + (call $effect-i32) + (array.new_default $array (i32.const 1)) + (call $effect-eq) + ) + ) + ) + + ;; CHECK: (func $array-cmpxchg-expected-index-oob (type $1) (param $array (ref $array)) + ;; CHECK-NEXT: (local $1 eqref) + ;; CHECK-NEXT: (local $2 (ref null $array)) + ;; CHECK-NEXT: (local $3 i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result eqref) + ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (local.get $array) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $3 + ;; CHECK-NEXT: (i32.const -1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result nullref) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $effect-eq) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (array.atomic.get $array + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: (local.get $3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $array-cmpxchg-expected-index-oob (param $array (ref $array)) + (drop + ;; Now the index is constant but surely out-of-bounds. We still optimize + ;; the same, way leaving the array.atomic.get to trap. + (array.atomic.rmw.cmpxchg $array + (local.get $array) + (i32.const -1) + (array.new_default $array (i32.const 1)) + (call $effect-eq) + ) + ) + ) +)