Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 55 additions & 30 deletions src/wasm/wasm-ir-builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -901,16 +901,57 @@ Result<> IRBuilder::pushScope(ScopeCtx&& scope) {
scope.startPos = lastBinaryPos;
lastBinaryPos = *binaryPos;
}
// Tunnel catch pops directly into unnamed blocks. Allow this even when the
// block has no input type so we can round-trip WAT without the printer
// needing to synthesize block input types, as well as when the block's input
// type matches the pop's type.
bool isUnnamedBlock = scope.getBlock() && !scope.getBlock()->name;
bool hasPop =
getScope().exprStack.size() == 1 && getScope().exprStack[0]->is<Pop>();
if (isUnnamedBlock && hasPop &&
(scope.inputType == Type::none ||
scope.inputType == getScope().exprStack[0]->type)) {
// Move the pop into the new scope.
auto* pop = getScope().exprStack.back();
getScope().exprStack.pop_back();
scopeStack.emplace_back(std::move(scope));
pushSynthetic(pop);
// If this unnamed block later receives a name from a branch targeting it,
// the nested pop will need to be fixed up.
scopeStack[0].noteSyntheticBlock();
return Ok{};
}

bool hasInput = scope.inputType != Type::none;
if (hasInput && !scope.isDelimiter()) {
auto inputs = ChildPopper{*this}.popInputs(scope.inputType);
CHECK_ERR(inputs);
const auto& parentLocals = getScope().inputLocals;
// The new scope may only receive a suffix of the outer scope's parameters,
// so match inputs starting from the remaining size of the outer stack
// rather than from the beginning.
size_t baseIndex = getScope().exprStack.size();
scope.inputLocals.reserve(inputs->size());
for (size_t i = 0; i < inputs->size(); ++i) {
auto scratch = addScratchLocal(scope.inputType[i]);
CHECK_ERR(scratch);
scope.inputLocals.push_back(*scratch);
pushSynthetic(builder.makeLocalSet(*scratch, (*inputs)[i]));
// Reuse the enclosing scope's scratch local rather than allocating a new
// one when an input comes directly from an enclosing scope's input.
// Earlier inputs that allocate new scratch locals (e.g. due to type
// widening) are copied before this scope starts, which safely reorders
// scratch `local.get`s past any subsequent reused inputs.
auto* input = (*inputs)[i];
size_t parentIndex = baseIndex + i;
bool isInputGet =
parentIndex < parentLocals.size() && input->is<LocalGet>() &&
input->cast<LocalGet>()->index == parentLocals[parentIndex] &&
input->type == scope.inputType[i];
if (isInputGet) {
scope.inputLocals.push_back(parentLocals[parentIndex]);
} else {
auto scratch = addScratchLocal(scope.inputType[i]);
CHECK_ERR(scratch);
scope.inputLocals.push_back(*scratch);
pushSynthetic(builder.makeLocalSet(*scratch, input));
}
}
Comment thread
tlively marked this conversation as resolved.
Resolved
}
scopeStack.emplace_back(std::move(scope));
Expand Down Expand Up @@ -2022,38 +2063,22 @@ Result<> IRBuilder::makeUnreachable() {
Result<> IRBuilder::makePop(Type type) {
// We don't actually want to create a new Pop expression here because we
// already create them automatically when starting a legacy catch block that
// needs one. Just verify that the Pop we are being asked to make is the same
// type as the Pop we have already made, and move it into the current scope if
// we have entered unnamed blocks since the catch began. (Note that the block
// may receive a name later, if we encounter a break to it, if we process
// that after the pop. In that case, the fixups that run later will have work
// to do.)
ScopeCtx* catchScope = nullptr;
for (int i = int(scopeStack.size()) - 1; i >= 0; --i) {
auto& scope = scopeStack[i];
if (scope.getCatch()) {
catchScope = &scope;
break;
}
auto* block = scope.getBlock();
if (!block || block->name || !scope.exprStack.empty()) {
break;
// needs one (and move them into nested unnamed blocks in pushScope). Just
// verify that the Pop we are being asked to make is the same type as the Pop
// we have already made.
auto& scope = getScope();
if (scope.exprStack.size() != 1 || !scope.exprStack[0]->is<Pop>()) {
if (auto* tryy = scope.getCatch();
tryy && wasm.getTag(tryy->catchTags.back())->params() == Type::none) {
return Err{"cannot pop from empty tag"};
}
}
if (!catchScope || catchScope->exprStack.size() != 1 ||
!catchScope->exprStack[0]->is<Pop>()) {
return Err{
"pop instructions may only appear at the beginning of catch blocks"};
}
auto* pop = catchScope->exprStack[0];
auto expectedType = pop->type;
if (!Type::isSubType(expectedType, type)) {
auto expectedType = scope.exprStack[0]->type;
if (type != expectedType) {
return Err{std::string("Expected pop of type ") + expectedType.toString()};
}
if (catchScope != &getScope()) {
catchScope->exprStack.pop_back();
pushSynthetic(pop);
}
return Ok{};
}

Expand Down
49 changes: 2 additions & 47 deletions test/lit/basic/exception-handling-legacy.wast
Original file line number Diff line number Diff line change
Expand Up @@ -1181,39 +1181,6 @@
)
)

;; CHECK-TEXT: (func $pop-can-be-supertype (type $0)
;; CHECK-TEXT-NEXT: (try
;; CHECK-TEXT-NEXT: (do
;; CHECK-TEXT-NEXT: )
;; CHECK-TEXT-NEXT: (catch $e-eqref
;; CHECK-TEXT-NEXT: (drop
;; CHECK-TEXT-NEXT: (pop eqref)
;; CHECK-TEXT-NEXT: )
;; CHECK-TEXT-NEXT: )
;; CHECK-TEXT-NEXT: )
;; CHECK-TEXT-NEXT: )
;; CHECK-BIN: (func $pop-can-be-supertype (type $0)
;; CHECK-BIN-NEXT: (try
;; CHECK-BIN-NEXT: (do
;; CHECK-BIN-NEXT: )
;; CHECK-BIN-NEXT: (catch $e-eqref
;; CHECK-BIN-NEXT: (drop
;; CHECK-BIN-NEXT: (pop eqref)
;; CHECK-BIN-NEXT: )
;; CHECK-BIN-NEXT: )
;; CHECK-BIN-NEXT: )
;; CHECK-BIN-NEXT: )
(func $pop-can-be-supertype
(try
(do)
(catch $e-eqref
(drop
(pop anyref) ;; pop can be supertype
)
)
)
)

;; CHECK-TEXT: (func $catchless-try-with-inner-delegate (type $0)
;; CHECK-TEXT-NEXT: (try $label$0
;; CHECK-TEXT-NEXT: (do
Expand Down Expand Up @@ -1780,18 +1747,6 @@
;; CHECK-BIN-NODEBUG-NEXT: )

;; CHECK-BIN-NODEBUG: (func $22 (type $0)
;; CHECK-BIN-NODEBUG-NEXT: (try
;; CHECK-BIN-NODEBUG-NEXT: (do
;; CHECK-BIN-NODEBUG-NEXT: )
;; CHECK-BIN-NODEBUG-NEXT: (catch $tag$3
;; CHECK-BIN-NODEBUG-NEXT: (drop
;; CHECK-BIN-NODEBUG-NEXT: (pop eqref)
;; CHECK-BIN-NODEBUG-NEXT: )
;; CHECK-BIN-NODEBUG-NEXT: )
;; CHECK-BIN-NODEBUG-NEXT: )
;; CHECK-BIN-NODEBUG-NEXT: )

;; CHECK-BIN-NODEBUG: (func $23 (type $0)
;; CHECK-BIN-NODEBUG-NEXT: (try $label
;; CHECK-BIN-NODEBUG-NEXT: (do
;; CHECK-BIN-NODEBUG-NEXT: (try
Expand All @@ -1808,7 +1763,7 @@
;; CHECK-BIN-NODEBUG-NEXT: (unreachable)
;; CHECK-BIN-NODEBUG-NEXT: )

;; CHECK-BIN-NODEBUG: (func $24 (type $0)
;; CHECK-BIN-NODEBUG: (func $23 (type $0)
;; CHECK-BIN-NODEBUG-NEXT: (block
;; CHECK-BIN-NODEBUG-NEXT: (block
;; CHECK-BIN-NODEBUG-NEXT: )
Expand All @@ -1821,7 +1776,7 @@
;; CHECK-BIN-NODEBUG-NEXT: (nop)
;; CHECK-BIN-NODEBUG-NEXT: )

;; CHECK-BIN-NODEBUG: (func $25 (type $0)
;; CHECK-BIN-NODEBUG: (func $24 (type $0)
;; CHECK-BIN-NODEBUG-NEXT: (if
;; CHECK-BIN-NODEBUG-NEXT: (try (result i32)
;; CHECK-BIN-NODEBUG-NEXT: (do
Expand Down
Loading
Loading