diff --git a/src/passes/Precompute.cpp b/src/passes/Precompute.cpp index f3a6e52e8c2..02cf27a26b6 100644 --- a/src/passes/Precompute.cpp +++ b/src/passes/Precompute.cpp @@ -210,7 +210,13 @@ class PrecomputingExpressionRunner if (hasEffects) { // Visit, so we recompute the effects. (This is rare, see comment // above.) - visitFunc(); + auto flow = visitFunc(); + // Also check the result of the effects - if it is non-constant, we + // cannot use it. (This can happen during propagation, when we see that + // other inputs exist to something we depend on.) + if (flow.breaking()) { + return flow; + } } // Refer to the same canonical GCData that we already created. return Literal(data, curr->type.getHeapType()); diff --git a/test/lit/passes/precompute-gc-loop.wast b/test/lit/passes/precompute-gc-loop.wast new file mode 100644 index 00000000000..d1752f23f5c --- /dev/null +++ b/test/lit/passes/precompute-gc-loop.wast @@ -0,0 +1,89 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. + +;; RUN: wasm-opt %s --remove-unused-names --precompute-propagate --fuzz-exec -all -S -o - \ +;; RUN: | filecheck %s + +(module + ;; CHECK: (type $struct (sub (struct (field i32)))) + (type $struct (sub (struct (field i32)))) + + ;; CHECK: (func $loop-different (type $1) + ;; CHECK-NEXT: (local $struct (ref null $struct)) + ;; CHECK-NEXT: (local $int i32) + ;; CHECK-NEXT: (local.set $int + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (loop $loop + ;; CHECK-NEXT: (local.set $struct + ;; CHECK-NEXT: (struct.new $struct + ;; CHECK-NEXT: (local.get $int) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $int + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br_if $loop + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (block $block1 (result i32) + ;; CHECK-NEXT: (block $block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (br_on_null $block + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $block1 + ;; CHECK-NEXT: (struct.get $struct 0 + ;; CHECK-NEXT: (local.get $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $loop-different + (local $struct (ref null $struct)) + (local $int i32) + ;; This int is set before the loop, and read at the loop top. The value there + ;; is *not* 0, because we set a new value in the loop, which takes effect in + ;; later iterations. + (local.set $int + (i32.const 0) + ) + (loop $loop + (local.set $struct + (struct.new $struct + (local.get $int) + ) + ) + (local.set $int + (i32.const 1) + ) + + ;; The rest of the code is needed to prevent the testcase from getting + ;; optimized trivially away. This br should not be optimized away or + ;; simplfied - it executes in the first loop iteration but not the second. + (br_if $loop + (i32.eqz + (block $block1 (result i32) + (block $block + (drop + (br_on_null $block + (local.get $struct) + ) + ) + (br $block1 + (struct.get $struct 0 + (local.get $struct) + ) + ) + ) + (i32.const 0) + ) + ) + ) + ) + ) +)