diff --git a/src/passes/Asyncify.cpp b/src/passes/Asyncify.cpp index 48f9cc59419..036044176af 100644 --- a/src/passes/Asyncify.cpp +++ b/src/passes/Asyncify.cpp @@ -665,6 +665,14 @@ class ModuleAnalyzer { } // TODO optimize the other case, at least by type } + void visitCallRef(CallRef* curr) { + if (curr->isReturn) { + Fatal() << "tail calls not yet supported in asyncify"; + } + if (canIndirectChangeState) { + info.canChangeState = true; + } + } }; Walker walker(info, module, canIndirectChangeState); walker.walk(func->body); @@ -834,6 +842,7 @@ class ModuleAnalyzer { } } void visitCallIndirect(CallIndirect* curr) { hasIndirectCall = true; } + void visitCallRef(CallRef* curr) { hasIndirectCall = true; } Module* module; ModuleAnalyzer* analyzer; Map* map; @@ -863,16 +872,16 @@ class ModuleAnalyzer { bool verbose; }; -// Checks if something performs a call: either a direct or indirect call, -// and perhaps it is dropped or assigned to a local. This captures all the -// cases of a call in flat IR. +// Checks if something performs a call: a direct call, indirect call, or +// call_ref, and perhaps it is dropped or assigned to a local. This captures +// all the cases of a call in flat IR. static bool doesCall(Expression* curr) { if (auto* set = curr->dynCast()) { curr = set->value; } else if (auto* drop = curr->dynCast()) { curr = drop->value; } - return curr->is() || curr->is(); + return curr->is() || curr->is() || curr->is(); } class AsyncifyBuilder : public Builder { diff --git a/test/lit/passes/asyncify-call-ref.wast b/test/lit/passes/asyncify-call-ref.wast new file mode 100644 index 00000000000..71b8d209525 --- /dev/null +++ b/test/lit/passes/asyncify-call-ref.wast @@ -0,0 +1,113 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. + +;; Test that asyncify properly instruments call_ref, just like call_indirect. + +;; RUN: wasm-opt %s --asyncify --pass-arg=asyncify-addlist@caller -all -S -o - | filecheck %s + +(module + ;; CHECK: (type $func-type (func)) + (type $func-type (func)) + + ;; CHECK: (import "env" "import" (func $import (type $func-type))) + (import "env" "import" (func $import)) + + (memory 1 2) + + ;; CHECK: (func $caller (type $func-type) + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (block $__asyncify_unwind (result i32) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const -4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.or + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (call_ref $func-type + ;; CHECK-NEXT: (ref.func $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (br $__asyncify_unwind + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $caller + (call_ref $func-type + (ref.func $import) + ) + ) +)