diff options
-rw-r--r-- | src/wasm-ir-builder.h | 25 | ||||
-rw-r--r-- | src/wasm/wasm-ir-builder.cpp | 16 | ||||
-rw-r--r-- | test/lit/basic/pop-fixup.wast | 209 |
3 files changed, 247 insertions, 3 deletions
diff --git a/src/wasm-ir-builder.h b/src/wasm-ir-builder.h index d7a1dde87..40689a879 100644 --- a/src/wasm-ir-builder.h +++ b/src/wasm-ir-builder.h @@ -259,6 +259,10 @@ private: struct NoScope {}; struct FuncScope { Function* func; + // Used to determine whether we need to run a fixup after creating the + // function. + bool hasSyntheticBlock = false; + bool hasPop = false; }; struct BlockScope { Block* block; @@ -369,6 +373,27 @@ private: } return nullptr; } + void noteSyntheticBlock() { + if (auto* funcScope = std::get_if<FuncScope>(&scope)) { + funcScope->hasSyntheticBlock = true; + } + } + void notePop() { + if (auto* funcScope = std::get_if<FuncScope>(&scope)) { + funcScope->hasPop = true; + } + } + bool needsPopFixup() { + // If the function has a synthetic block and it has a pop, then it's + // possible that the pop is inside the synthetic block and we should run + // the fixup. Determining more precisely that a pop is inside the + // synthetic block when it is created would be complicated and expensive, + // so we are conservative here. + if (auto* funcScope = std::get_if<FuncScope>(&scope)) { + return funcScope->hasSyntheticBlock && funcScope->hasPop; + } + return false; + } Block* getBlock() { if (auto* blockScope = std::get_if<BlockScope>(&scope)) { return blockScope->block; diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index b238a926c..4cb9514f5 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -17,6 +17,7 @@ #include <cassert> #include "ir/child-typer.h" +#include "ir/eh-utils.h" #include "ir/names.h" #include "ir/properties.h" #include "ir/utils.h" @@ -98,7 +99,10 @@ Result<> IRBuilder::packageHoistedValue(const HoistedVal& hoisted, auto packageAsBlock = [&](Type type) { // Create a block containing the producer of the hoisted value, the final - // get of the hoisted value, and everything in between. + // get of the hoisted value, and everything in between. Record the fact that + // we are synthesizing a block to help us determine later whether we need to + // run the nested pop fixup. + scopeStack[0].noteSyntheticBlock(); std::vector<Expression*> exprs(scope.exprStack.begin() + hoisted.valIndex, scope.exprStack.end()); auto* block = builder.makeBlock(exprs, type); @@ -865,9 +869,12 @@ Result<> IRBuilder::visitCatch(Name tag) { tryy->catchTags.push_back(tag); pushScope( ScopeCtx::makeCatch(tryy, originalLabel, label, labelUsed, branchLabel)); - // Push a pop for the exception payload. + // Push a pop for the exception payload if necessary. auto params = wasm.getTag(tag)->sig.params; if (params != Type::none) { + // Note that we have a pop to help determine later whether we need to run + // the fixup for pops within blocks. + scopeStack[0].notePop(); push(builder.makePop(params)); } return Ok{}; @@ -935,7 +942,7 @@ Result<> IRBuilder::visitEnd() { if (scope.isNone()) { return Err{"unexpected end"}; } - if (auto* func = scope.getFunction(); func) { + if (auto* func = scope.getFunction()) { if (auto* loc = std::get_if<Function::DebugLocation>(&debugLoc)) { func->epilogLocation.insert(*loc); } @@ -970,6 +977,9 @@ Result<> IRBuilder::visitEnd() { if (auto* func = scope.getFunction()) { func->body = maybeWrapForLabel(*expr); labelDepths.clear(); + if (scope.needsPopFixup()) { + EHUtils::handleBlockNestedPops(func, wasm); + } } else if (auto* block = scope.getBlock()) { assert(*expr == block); block->name = scope.label; diff --git a/test/lit/basic/pop-fixup.wast b/test/lit/basic/pop-fixup.wast new file mode 100644 index 000000000..371365a0c --- /dev/null +++ b/test/lit/basic/pop-fixup.wast @@ -0,0 +1,209 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. +;; RUN: wasm-opt %s -all -S -o - | filecheck %s + +(module + ;; CHECK: (tag $e (param i32)) + (tag $e (param i32)) + ;; CHECK: (tag $e2 (param i32 i32)) + (tag $e2 (param i32 i32)) + + ;; CHECK: (func $stacky (type $0) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (try + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch $e + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (pop i32) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $stacky + try + catch $e + nop + drop + end + ) + + ;; CHECK: (func $stacky-later (type $0) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (try + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch $e + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (pop i32) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $stacky-later + try + catch $e + i32.eqz + nop + drop + end + ) + + ;; CHECK: (func $tuple (type $0) + ;; CHECK-NEXT: (local $scratch (tuple i32 i32)) + ;; CHECK-NEXT: (local $scratch_1 i32) + ;; CHECK-NEXT: (local $2 (tuple i32 i32)) + ;; CHECK-NEXT: (try + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch $e2 + ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (pop (tuple i32 i32)) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (local.set $scratch_1 + ;; CHECK-NEXT: (tuple.extract 2 0 + ;; CHECK-NEXT: (local.tee $scratch + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (tuple.extract 2 1 + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $scratch_1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $tuple + try + catch $e2 + drop + drop + end + ) + + ;; CHECK: (func $stacky-tuple (type $0) + ;; CHECK-NEXT: (local $scratch (tuple i32 i32)) + ;; CHECK-NEXT: (local $scratch_1 i32) + ;; CHECK-NEXT: (local $2 (tuple i32 i32)) + ;; CHECK-NEXT: (try + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch $e2 + ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (pop (tuple i32 i32)) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (local.set $scratch_1 + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (tuple.extract 2 0 + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (tuple.extract 2 1 + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $scratch_1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $stacky-tuple + try + catch $e2 + nop + drop + drop + end + ) + + ;; CHECK: (func $stacky-later-tuple (type $0) + ;; CHECK-NEXT: (local $0 (tuple i32 i32)) + ;; CHECK-NEXT: (local $scratch (tuple i32 i32)) + ;; CHECK-NEXT: (local $scratch_2 i32) + ;; CHECK-NEXT: (local $3 (tuple i32 i32)) + ;; CHECK-NEXT: (try + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch $e2 + ;; CHECK-NEXT: (local.set $3 + ;; CHECK-NEXT: (pop (tuple i32 i32)) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (local.set $scratch_2 + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (local.tee $0 + ;; CHECK-NEXT: (local.get $3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (tuple.extract 2 0 + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (tuple.extract 2 1 + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $scratch_2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $stacky-later-tuple + (local (tuple i32 i32)) + try + catch $e2 + local.tee 0 + nop + drop + drop + end + ) +) |