summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/wasm-ir-builder.h25
-rw-r--r--src/wasm/wasm-ir-builder.cpp16
-rw-r--r--test/lit/basic/pop-fixup.wast209
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
+ )
+)