diff options
-rw-r--r-- | src/passes/OptimizeInstructions.cpp | 30 | ||||
-rw-r--r-- | test/lit/passes/optimize-instructions-gc.wast | 89 |
2 files changed, 96 insertions, 23 deletions
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index e6b19a6de..a546930c6 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -262,6 +262,11 @@ struct OptimizeInstructions bool inReplaceCurrent = false; void replaceCurrent(Expression* rep) { + if (rep->type != getCurrent()->type) { + // This operation will change the type, so refinalize. + refinalize = true; + } + WalkerPass<PostWalker<OptimizeInstructions>>::replaceCurrent(rep); // We may be able to apply multiple patterns as one may open opportunities // for others. NB: patterns must not have cycles @@ -1707,8 +1712,6 @@ struct OptimizeInstructions if (fallthrough->type.isNull()) { replaceCurrent( getDroppedChildrenAndAppend(curr, builder.makeUnreachable())); - // Propagate the unreachability. - refinalize = true; return true; } return false; @@ -2083,25 +2086,6 @@ struct OptimizeInstructions if (result == GCTypeUtils::Success) { replaceCurrent(curr->ref); - - // We must refinalize here, as we may be returning a more specific - // type, which can alter the parent. For example: - // - // (struct.get $parent 0 - // (ref.cast_static $parent - // (local.get $child) - // ) - // ) - // - // Try to cast a $child to its parent, $parent. That always works, - // so the cast can be removed. - // Then once the cast is removed, the outer struct.get - // will have a reference with a different type, making it a - // (struct.get $child ..) instead of $parent. - // But if $parent and $child have different types on field 0 (the - // child may have a more refined one) then the struct.get must be - // refinalized so the IR node has the expected type. - refinalize = true; return; } else if (result == GCTypeUtils::SuccessOnlyIfNonNull) { // All we need to do is check for a null here. @@ -2109,7 +2093,6 @@ struct OptimizeInstructions // As above, we must refinalize as we may now be emitting a more refined // type (specifically a more refined heap type). replaceCurrent(builder.makeRefAs(RefAsNonNull, curr->ref)); - refinalize = true; return; } @@ -2752,7 +2735,8 @@ private: // must drop one value, so 3, while we save the condition, so it's // not clear this is worth it, TODO } else { - // value has no side effects + // The value has no side effects, so we can replace ourselves with one + // of the two identical values in the arms. auto condition = effects(c); if (!condition.hasSideEffects()) { return ifTrue; diff --git a/test/lit/passes/optimize-instructions-gc.wast b/test/lit/passes/optimize-instructions-gc.wast index 5711d9bc2..593de7dec 100644 --- a/test/lit/passes/optimize-instructions-gc.wast +++ b/test/lit/passes/optimize-instructions-gc.wast @@ -38,12 +38,18 @@ (type $empty (struct)) + ;; CHECK: (type $void2 (func_subtype $void)) + ;; CHECK: (type $C (struct_subtype (field i32) (field i32) (field f64) $A)) + ;; NOMNL: (type $void2 (func_subtype $void)) + ;; NOMNL: (type $C (struct_subtype (field i32) (field i32) (field f64) $A)) (type $C (struct_subtype (field i32) (field i32) (field f64) $A)) (type $void (func)) + (type $void2 (func_subtype $void)) + ;; CHECK: (import "env" "get-i32" (func $get-i32 (result i32))) ;; NOMNL: (import "env" "get-i32" (func $get-i32 (result i32))) (import "env" "get-i32" (func $get-i32 (result i32))) @@ -3253,4 +3259,87 @@ (i32.const 3) ) ) + + ;; CHECK: (func $refinalize.select.arm (type $void) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.cast $void2 + ;; CHECK-NEXT: (ref.func $refinalize.select.arm) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; NOMNL: (func $refinalize.select.arm (type $void) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (ref.cast $void2 + ;; NOMNL-NEXT: (ref.func $refinalize.select.arm) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + (func $refinalize.select.arm (type $void) + ;; Pick one of the two select sides using the condition. This changes the + ;; type (the arms are more refined than the declared type), so we must + ;; refinalize or we'll error. + (drop + (ref.cast null $void2 + (select (result (ref null $void)) + (ref.func $refinalize.select.arm) + (ref.func $refinalize.select.arm) + (i32.const 1) + ) + ) + ) + ) + + ;; CHECK: (func $refinalize.select.arm.flip (type $void) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.cast $void2 + ;; CHECK-NEXT: (ref.func $refinalize.select.arm) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; NOMNL: (func $refinalize.select.arm.flip (type $void) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (ref.cast $void2 + ;; NOMNL-NEXT: (ref.func $refinalize.select.arm) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + (func $refinalize.select.arm.flip + ;; Flipped of the above. + (drop + (ref.cast null $void2 + (select (result (ref null $void)) + (ref.func $refinalize.select.arm) + (ref.func $refinalize.select.arm) + (i32.const 0) + ) + ) + ) + ) + + ;; CHECK: (func $refinalize.select.arm.unknown (type $i32_=>_none) (param $x i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.cast $void2 + ;; CHECK-NEXT: (ref.func $refinalize.select.arm) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; NOMNL: (func $refinalize.select.arm.unknown (type $i32_=>_none) (param $x i32) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (ref.cast $void2 + ;; NOMNL-NEXT: (ref.func $refinalize.select.arm) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + (func $refinalize.select.arm.unknown (param $x i32) + ;; As above but use an unknown value at compile time for the condition. + (drop + (ref.cast null $void2 + (select (result (ref null $void)) + (ref.func $refinalize.select.arm) + (ref.func $refinalize.select.arm) + (local.get $x) + ) + ) + ) + ) ) |