diff options
author | Thomas Lively <7121787+tlively@users.noreply.github.com> | 2021-07-28 12:00:11 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-07-28 19:00:11 +0000 |
commit | 8670f15676b3c6406d6e82327a7258c7c4d08b43 (patch) | |
tree | 59338a9debe510a297b90dd7d51eedf209010f02 | |
parent | be580c66645cdea13c8e8b3b77f12ef3a52e5f2e (diff) | |
download | binaryen-8670f15676b3c6406d6e82327a7258c7c4d08b43.tar.gz binaryen-8670f15676b3c6406d6e82327a7258c7c4d08b43.tar.bz2 binaryen-8670f15676b3c6406d6e82327a7258c7c4d08b43.zip |
Support subtyping in tail calls (#4032)
The tail call spec does not include subtyping because it is based on the
upstream spec, which does not contain subtyping. However, there is no reason
that subtyping shouldn't apply to tail calls like it does for any other call or
return. Update the validator to allow subtyping and avoid a related null pointer
dereference while we're at it.
Do not run the test in with --nominal because it is buggy in that mode.
-rw-r--r-- | src/wasm/wasm-validator.cpp | 12 | ||||
-rw-r--r-- | test/lit/tail-call.wast | 78 | ||||
-rw-r--r-- | test/tail-call.wast | 11 |
3 files changed, 85 insertions, 16 deletions
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index f492c7110..450a7b956 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -460,9 +460,9 @@ private: Type(Type::unreachable), curr, "return_call* should have unreachable type"); - shouldBeEqual( - getFunction()->getResults(), + shouldBeSubType( sig.results, + getFunction()->getResults(), curr, "return_call* callee return type must match caller return type"); } else { @@ -798,9 +798,11 @@ void FunctionValidator::visitCallIndirect(CallIndirect* curr) { if (curr->target->type != Type::unreachable) { auto* table = getModule()->getTableOrNull(curr->table); shouldBeTrue(!!table, curr, "call-indirect table must exist"); - shouldBeTrue(table->type.isFunction(), - curr, - "call-indirect table must be of function type."); + if (table) { + shouldBeTrue(table->type.isFunction(), + curr, + "call-indirect table must be of function type."); + } } validateCallParamsAndResult(curr, curr->sig); diff --git a/test/lit/tail-call.wast b/test/lit/tail-call.wast new file mode 100644 index 000000000..92dfdbb4e --- /dev/null +++ b/test/lit/tail-call.wast @@ -0,0 +1,78 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. + +;; Check that tail calls are parsed, validated, and printed correctly + +;; RUN: foreach %s %t wasm-opt -all -S -o - | filecheck %s +;; TODO: --nominal as well + +(module + + ;; CHECK: (type $void (func)) + (type $void (func)) + + ;; CHECK: (table $t 1 1 funcref) + (table $t 1 1 funcref) + + ;; CHECK: (elem $e (i32.const 0) $foo) + (elem $e (i32.const 0) $foo) + + ;; CHECK: (func $foo + ;; CHECK-NEXT: (return_call $bar) + ;; CHECK-NEXT: ) + (func $foo + (return_call $bar) + ) + + ;; CHECK: (func $bar + ;; CHECK-NEXT: (return_call_indirect $t (type $void) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $bar + (return_call_indirect (type $void) (i32.const 0)) + ) +) + +;; Check GC types and subtyping +(module + ;; CHECK: (type $return-B (func (result (ref $B)))) + (type $return-B (func (result (ref $B)))) + + ;; CHECK: (type $return-A (func (result (ref null $A)))) + (type $return-A (func (result (ref null $A)))) + + ;; CHECK: (type $A (struct (field i32))) + (type $A (struct i32)) + + ;; CHECK: (type $B (struct (field i32) (field i32))) + (type $B (struct i32 i32) (supertype $A)) + + ;; CHECK: (table $t 1 1 funcref) + (table $t 1 1 funcref) + + ;; CHECK: (elem $e (i32.const 0) $callee) + (elem $e (i32.const 0) $callee) + + ;; CHECK: (func $caller (result (ref null $A)) + ;; CHECK-NEXT: (return_call $callee) + ;; CHECK-NEXT: ) + (func $caller (type $return-A) + (return_call $callee) + ) + + ;; CHECK: (func $caller-indirect (result (ref $B)) + ;; CHECK-NEXT: (return_call_indirect $t (type $return-B) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $caller-indirect (type $return-B) + (return_call_indirect $t (type $return-B) (i32.const 0)) + ) + + ;; CHECK: (func $callee (result (ref $B)) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $callee (type $return-B) + (unreachable) + ) +) diff --git a/test/tail-call.wast b/test/tail-call.wast deleted file mode 100644 index ceea1239e..000000000 --- a/test/tail-call.wast +++ /dev/null @@ -1,11 +0,0 @@ -(module - (type $void (func)) - (table 1 1 funcref) - (elem (i32.const 0) $foo) - (func $foo - (return_call $bar) - ) - (func $bar - (return_call_indirect (type $void) (i32.const 0)) - ) -)
\ No newline at end of file |