summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Lively <7121787+tlively@users.noreply.github.com>2021-07-28 12:00:11 -0700
committerGitHub <noreply@github.com>2021-07-28 19:00:11 +0000
commit8670f15676b3c6406d6e82327a7258c7c4d08b43 (patch)
tree59338a9debe510a297b90dd7d51eedf209010f02
parentbe580c66645cdea13c8e8b3b77f12ef3a52e5f2e (diff)
downloadbinaryen-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.cpp12
-rw-r--r--test/lit/tail-call.wast78
-rw-r--r--test/tail-call.wast11
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