diff options
-rw-r--r-- | src/passes/DeadArgumentElimination.cpp | 20 | ||||
-rw-r--r-- | test/lit/passes/dae-gc.wast | 112 |
2 files changed, 127 insertions, 5 deletions
diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index f3e3ae98c..601acfc5c 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -660,9 +660,25 @@ private: } // Scan the body and look at the returns. + auto processReturnType = [&](Type type) { + refinedType = Type::getLeastUpperBound(refinedType, type); + // Return whether we still look ok to do the optimization. If this is + // false then we can stop here. + return refinedType != originalType; + }; for (auto* ret : FindAll<Return>(func->body).list) { - refinedType = Type::getLeastUpperBound(refinedType, ret->value->type); - if (refinedType == originalType) { + if (!processReturnType(ret->value->type)) { + return false; + } + } + for (auto* call : FindAll<Call>(func->body).list) { + if (call->isReturn && + !processReturnType(module->getFunction(call->target)->getResults())) { + return false; + } + } + for (auto* call : FindAll<CallIndirect>(func->body).list) { + if (call->isReturn && !processReturnType(call->sig.results)) { return false; } } diff --git a/test/lit/passes/dae-gc.wast b/test/lit/passes/dae-gc.wast index 833a56a1f..07bc84f5e 100644 --- a/test/lit/passes/dae-gc.wast +++ b/test/lit/passes/dae-gc.wast @@ -3,18 +3,24 @@ (module ;; CHECK: (type ${i32} (struct (field i32))) + (type ${i32} (struct (field i32))) ;; CHECK: (type ${} (struct )) (type ${} (struct)) - (type ${i32} (struct (field i32))) + ;; CHECK: (type ${i32_i64} (struct (field i32) (field i64))) + (type ${i32_i64} (struct (field i32) (field i64))) + + ;; CHECK: (type $return_{} (func (result (ref ${})))) + (type $return_{} (func (result (ref ${})))) ;; CHECK: (type ${i32_f32} (struct (field i32) (field f32))) + (type ${i32_f32} (struct (field i32) (field f32))) ;; CHECK: (type ${f64} (struct (field f64))) (type ${f64} (struct (field f64))) - (type ${i32_i64} (struct (field i32) (field i64))) - (type ${i32_f32} (struct (field i32) (field f32))) + + (table 1 1 funcref) ;; CHECK: (func $foo ;; CHECK-NEXT: (call $bar) @@ -594,4 +600,104 @@ (func $return-ref-func (result funcref) (ref.func $do-return-call) ) + + ;; Show that we can optimize the return type of a function that does a tail + ;; call. + ;; CHECK: (func $tail-callee (result (ref ${})) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $tail-callee (result (ref ${})) + (unreachable) + ) + ;; CHECK: (func $tail-caller-yes (result (ref ${})) + ;; CHECK-NEXT: (return_call $tail-callee) + ;; CHECK-NEXT: ) + (func $tail-caller-yes (result anyref) + ;; This function's return type can be refined because of this call, whose + ;; target's return type is more specific than anyref. + (return_call $tail-callee) + ) + ;; CHECK: (func $tail-caller-no (result anyref) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (return + ;; CHECK-NEXT: (ref.null any) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return_call $tail-callee) + ;; CHECK-NEXT: ) + (func $tail-caller-no (result anyref) + ;; This function's return type cannot be refined because of another return + ;; whose type prevents it. + (if (i32.const 1) + (return (ref.null any)) + ) + (return_call $tail-callee) + ) + ;; CHECK: (func $tail-call-caller + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $tail-caller-yes) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $tail-caller-no) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $tail-call-caller + ;; Call the functions to cause optimization to happen. + (drop + (call $tail-caller-yes) + ) + (drop + (call $tail-caller-no) + ) + ) + + ;; As above, but with an indirect tail call. + ;; CHECK: (func $tail-callee-indirect (result (ref ${})) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $tail-callee-indirect (result (ref ${})) + (unreachable) + ) + ;; CHECK: (func $tail-caller-indirect-yes (result (ref ${})) + ;; CHECK-NEXT: (return_call_indirect $0 (type $return_{}) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $tail-caller-indirect-yes (result anyref) + (return_call_indirect (type $return_{}) (i32.const 0)) + ) + ;; CHECK: (func $tail-caller-indirect-no (result anyref) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (return + ;; CHECK-NEXT: (ref.null any) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return_call_indirect $0 (type $return_{}) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $tail-caller-indirect-no (result anyref) + (if (i32.const 1) + (return (ref.null any)) + ) + (return_call_indirect (type $return_{}) (i32.const 0)) + ) + ;; CHECK: (func $tail-call-caller-indirect + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $tail-caller-indirect-yes) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $tail-caller-indirect-no) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $tail-call-caller-indirect + (drop + (call $tail-caller-indirect-yes) + ) + (drop + (call $tail-caller-indirect-no) + ) + ) ) |