summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/passes/DeadArgumentElimination.cpp20
-rw-r--r--test/lit/passes/dae-gc.wast112
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)
+ )
+ )
)