diff options
-rw-r--r-- | src/ir/drop.cpp | 10 | ||||
-rw-r--r-- | test/lit/passes/optimize-instructions-gc.wast | 43 |
2 files changed, 46 insertions, 7 deletions
diff --git a/src/ir/drop.cpp b/src/ir/drop.cpp index 49bff55f7..5a860fa29 100644 --- a/src/ir/drop.cpp +++ b/src/ir/drop.cpp @@ -59,7 +59,12 @@ Expression* getDroppedChildrenAndAppend(Expression* curr, if (effects.hasUnremovableSideEffects() || curr->is<If>() || curr->is<Try>() || curr->is<Pop>() || BranchUtils::getDefinedName(curr).is()) { - return builder.makeSequence(builder.makeDrop(curr), last); + // If curr is concrete we must drop it. Or, if it is unreachable or none, + // then we can leave it as it is. + if (curr->type.isConcrete()) { + curr = builder.makeDrop(curr); + } + return builder.makeSequence(curr, last); } std::vector<Expression*> contents; @@ -67,11 +72,10 @@ Expression* getDroppedChildrenAndAppend(Expression* curr, if (!EffectAnalyzer(options, wasm, child).hasUnremovableSideEffects()) { continue; } + // See above. if (child->type.isConcrete()) { contents.push_back(builder.makeDrop(child)); } else { - // The child is unreachable, or none (none is possible as a child of a - // block or loop, etc.); in both cases we do not need a drop. contents.push_back(child); } } diff --git a/test/lit/passes/optimize-instructions-gc.wast b/test/lit/passes/optimize-instructions-gc.wast index ff880b475..c7238b353 100644 --- a/test/lit/passes/optimize-instructions-gc.wast +++ b/test/lit/passes/optimize-instructions-gc.wast @@ -28,17 +28,17 @@ (type $B (struct_subtype (field i32) (field i32) (field f32) $A)) + ;; CHECK: (type $void (func)) + ;; CHECK: (type $B-child (struct_subtype (field i32) (field i32) (field f32) (field i64) $B)) + ;; NOMNL: (type $void (func)) + ;; NOMNL: (type $B-child (struct_subtype (field i32) (field i32) (field f32) (field i64) $B)) (type $B-child (struct_subtype (field i32) (field i32) (field f32) (field i64) $B)) (type $empty (struct)) - ;; CHECK: (type $void (func)) - ;; CHECK: (type $C (struct_subtype (field i32) (field i32) (field f64) $A)) - ;; NOMNL: (type $void (func)) - ;; NOMNL: (type $C (struct_subtype (field i32) (field i32) (field f64) $A)) (type $C (struct_subtype (field i32) (field i32) (field f64) $A)) @@ -3157,4 +3157,39 @@ ) ) ) + + ;; CHECK: (func $struct.set.null.fallthrough (type $void) + ;; CHECK-NEXT: (local $temp (ref null $struct)) + ;; CHECK-NEXT: (struct.set $struct $i8 + ;; CHECK-NEXT: (local.tee $temp + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 100) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; NOMNL: (func $struct.set.null.fallthrough (type $void) + ;; NOMNL-NEXT: (local $temp (ref null $struct)) + ;; NOMNL-NEXT: (struct.set $struct $i8 + ;; NOMNL-NEXT: (local.tee $temp + ;; NOMNL-NEXT: (ref.null none) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (i32.const 100) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (unreachable) + ;; NOMNL-NEXT: ) + (func $struct.set.null.fallthrough + (local $temp (ref null $struct)) + ;; The value falling through the tee shows the local.set will trap. We can + ;; append an unreachable after it. While doing so we must not emit a drop of + ;; the struct.set (which would be valid for a struct.get etc.). + (struct.set $struct 0 + (local.tee $temp + (ref.as_non_null + (ref.null none) + ) + ) + (i32.const 100) + ) + ) ) |