diff options
-rw-r--r-- | src/passes/Asyncify.cpp | 53 | ||||
-rw-r--r-- | test/lit/passes/asyncify_pass-arg=asyncify-asserts_pass-arg=asyncify-onlylist@waka.wast | 81 |
2 files changed, 123 insertions, 11 deletions
diff --git a/src/passes/Asyncify.cpp b/src/passes/Asyncify.cpp index 365891db4..7b2d57574 100644 --- a/src/passes/Asyncify.cpp +++ b/src/passes/Asyncify.cpp @@ -921,9 +921,6 @@ struct AsyncifyFlow : public Pass { // If the function cannot change our state, we have nothing to do - // we will never unwind or rewind the stack here. if (!analyzer->needsInstrumentation(func)) { - if (analyzer->asserts) { - addAssertsInNonInstrumented(func); - } return; } // Rewrite the function body. @@ -947,7 +944,6 @@ struct AsyncifyFlow : public Pass { private: std::unique_ptr<AsyncifyBuilder> builder; - Module* module; Function* func; @@ -1216,6 +1212,41 @@ private: // don't want it to be seen by asyncify itself. return builder->makeCall(ASYNCIFY_GET_CALL_INDEX, {}, Type::none); } +}; + +// Add asserts in non-instrumented code. +struct AsyncifyAssertInNonInstrumented : public Pass { + bool isFunctionParallel() override { return true; } + + ModuleAnalyzer* analyzer; + Type pointerType; + Name asyncifyMemory; + + std::unique_ptr<Pass> create() override { + return std::make_unique<AsyncifyAssertInNonInstrumented>( + analyzer, pointerType, asyncifyMemory); + } + + AsyncifyAssertInNonInstrumented(ModuleAnalyzer* analyzer, + Type pointerType, + Name asyncifyMemory) + : analyzer(analyzer), pointerType(pointerType), + asyncifyMemory(asyncifyMemory) {} + + void runOnFunction(Module* module_, Function* func) override { + // FIXME: This looks like it was never right, as it should ignore the top- + // most runtime, but it will actually instrument it (as it needs no + // instrumentation, like random code - but the top-most runtime is + // actually a place that needs neither instrumentation *nor* + // assertions, as the assertions will error when it changes the + // state). + if (!analyzer->needsInstrumentation(func)) { + module = module_; + builder = + make_unique<AsyncifyBuilder>(*module, pointerType, asyncifyMemory); + addAssertsInNonInstrumented(func); + } + } // Given a function that is not instrumented - because we proved it doesn't // need it, or depending on the only-list / remove-list - add assertions that @@ -1275,6 +1306,10 @@ private: walker.oldState = oldState; walker.walk(func->body); } + +private: + std::unique_ptr<AsyncifyBuilder> builder; + Module* module; }; // Instrument local saving/restoring. @@ -1689,6 +1724,16 @@ struct Asyncify : public Pass { runner.setValidateGlobally(false); runner.run(); } + if (asserts) { + // Add asserts in non-instrumented code. Note we do not use an + // instrumented pass runner here as we do want to run on all functions. + PassRunner runner(module); + runner.add(make_unique<AsyncifyAssertInNonInstrumented>( + &analyzer, pointerType, asyncifyMemory)); + runner.setIsNested(true); + runner.setValidateGlobally(false); + runner.run(); + } // Next, add local saving/restoring logic. We optimize before doing this, // to undo the extra code generated by flattening, and to arrive at the // minimal amount of locals (which is important as we must save and diff --git a/test/lit/passes/asyncify_pass-arg=asyncify-asserts_pass-arg=asyncify-onlylist@waka.wast b/test/lit/passes/asyncify_pass-arg=asyncify-asserts_pass-arg=asyncify-onlylist@waka.wast index cd42c9c2d..a43f13013 100644 --- a/test/lit/passes/asyncify_pass-arg=asyncify-asserts_pass-arg=asyncify-onlylist@waka.wast +++ b/test/lit/passes/asyncify_pass-arg=asyncify-asserts_pass-arg=asyncify-onlylist@waka.wast @@ -3,6 +3,11 @@ ;; RUN: foreach %s %t wasm-opt --asyncify --pass-arg=asyncify-asserts --pass-arg=asyncify-onlylist@waka -S -o - | filecheck %s +;; This test has an only-list, and that list has a non-existent function, so we +;; do not actually instrument anything. But we should still add the assertions +;; we add in non-instrumented code, namely asserts that they don't change the +;; state. + (module (memory 1 2) ;; CHECK: (type $f (func)) @@ -39,14 +44,44 @@ ;; CHECK: (export "asyncify_get_state" (func $asyncify_get_state)) ;; CHECK: (func $calls-import - ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $calls-import (call $import) ) ;; CHECK: (func $calls-import2-drop + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (call $import2) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (call $import2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $calls-import2-drop @@ -54,10 +89,29 @@ ) ;; CHECK: (func $returns (result i32) ;; CHECK-NEXT: (local $x i32) - ;; CHECK-NEXT: (local.set $x - ;; CHECK-NEXT: (call $import2) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (local $2 i32) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (local.set $x + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (call $import2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) (func $returns (result i32) (local $x i32) @@ -65,8 +119,21 @@ (local.get $x) ) ;; CHECK: (func $calls-indirect (param $x i32) - ;; CHECK-NEXT: (call_indirect (type $f) - ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (call_indirect (type $f) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $calls-indirect (param $x i32) |