summaryrefslogtreecommitdiff
path: root/src/wasm/wasm-ir-builder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm/wasm-ir-builder.cpp')
-rw-r--r--src/wasm/wasm-ir-builder.cpp172
1 files changed, 161 insertions, 11 deletions
diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp
index 9bca1967b..ed81a5c18 100644
--- a/src/wasm/wasm-ir-builder.cpp
+++ b/src/wasm/wasm-ir-builder.cpp
@@ -207,18 +207,33 @@ void IRBuilder::dump() {
for (auto& scope : scopeStack) {
std::cerr << " scope ";
- if (std::get_if<ScopeCtx::NoScope>(&scope.scope)) {
+ if (scope.isNone()) {
std::cerr << "none";
- } else if (auto* f = std::get_if<ScopeCtx::FuncScope>(&scope.scope)) {
- std::cerr << "func " << f->func->name;
- } else if (std::get_if<ScopeCtx::BlockScope>(&scope.scope)) {
+ } else if (auto* f = scope.getFunction()) {
+ std::cerr << "func " << f->name;
+ } else if (scope.getBlock()) {
std::cerr << "block";
- } else if (std::get_if<ScopeCtx::IfScope>(&scope.scope)) {
+ } else if (scope.getIf()) {
std::cerr << "if";
- } else if (std::get_if<ScopeCtx::ElseScope>(&scope.scope)) {
+ } else if (scope.getElse()) {
std::cerr << "else";
- } else if (std::get_if<ScopeCtx::LoopScope>(&scope.scope)) {
+ } else if (scope.getLoop()) {
std::cerr << "loop";
+ } else if (auto* tryy = scope.getTry()) {
+ std::cerr << "try";
+ if (tryy->name) {
+ std::cerr << " " << tryy->name;
+ }
+ } else if (auto* tryy = scope.getCatch()) {
+ std::cerr << "catch";
+ if (tryy->name) {
+ std::cerr << " " << tryy->name;
+ }
+ } else if (auto* tryy = scope.getCatchAll()) {
+ std::cerr << "catch_all";
+ if (tryy->name) {
+ std::cerr << " " << tryy->name;
+ }
} else {
WASM_UNREACHABLE("unexpected scope");
}
@@ -483,6 +498,14 @@ Result<> IRBuilder::visitLoopStart(Loop* loop) {
return Ok{};
}
+Result<> IRBuilder::visitTryStart(Try* tryy, Name label) {
+ // The delegate label will be regenerated if we need it. See `visitDelegate`
+ // for details.
+ tryy->name = Name();
+ pushScope(ScopeCtx::makeTry(tryy, label));
+ return Ok{};
+}
+
Result<Expression*> IRBuilder::finishScope(Block* block) {
if (scopeStack.empty() || scopeStack.back().isNone()) {
return Err{"unexpected end of scope"};
@@ -586,6 +609,97 @@ Result<> IRBuilder::visitElse() {
return Ok{};
}
+Result<> IRBuilder::visitCatch(Name tag) {
+ auto& scope = getScope();
+ bool wasTry = true;
+ auto* tryy = scope.getTry();
+ if (!tryy) {
+ wasTry = false;
+ tryy = scope.getCatch();
+ }
+ if (!tryy) {
+ return Err{"unexpected catch"};
+ }
+ auto originalLabel = scope.getOriginalLabel();
+ auto label = scope.label;
+ auto expr = finishScope();
+ CHECK_ERR(expr);
+ if (wasTry) {
+ tryy->body = *expr;
+ } else {
+ tryy->catchBodies.push_back(*expr);
+ }
+ tryy->catchTags.push_back(tag);
+ pushScope(ScopeCtx::makeCatch(tryy, originalLabel, label));
+ // Push a pop for the exception payload.
+ auto params = wasm.getTag(tag)->sig.params;
+ if (params != Type::none) {
+ push(builder.makePop(params));
+ }
+ return Ok{};
+}
+
+Result<> IRBuilder::visitCatchAll() {
+ auto& scope = getScope();
+ bool wasTry = true;
+ auto* tryy = scope.getTry();
+ if (!tryy) {
+ wasTry = false;
+ tryy = scope.getCatch();
+ }
+ if (!tryy) {
+ return Err{"unexpected catch"};
+ }
+ auto originalLabel = scope.getOriginalLabel();
+ auto label = scope.label;
+ auto expr = finishScope();
+ CHECK_ERR(expr);
+ if (wasTry) {
+ tryy->body = *expr;
+ } else {
+ tryy->catchBodies.push_back(*expr);
+ }
+ pushScope(ScopeCtx::makeCatchAll(tryy, originalLabel, label));
+ return Ok{};
+}
+
+Result<> IRBuilder::visitDelegate(Index label) {
+ auto& scope = getScope();
+ auto* tryy = scope.getTry();
+ if (!tryy) {
+ return Err{"unexpected delegate"};
+ }
+ // In Binaryen IR, delegates can only target try or function scopes directly.
+ // Search upward to find the nearest enclosing try or function scope. Since
+ // the given label is relative the parent scope of the try, start by adjusting
+ // it to be relative to the try scope.
+ ++label;
+ for (size_t size = scopeStack.size(); label < size; ++label) {
+ auto& delegateScope = scopeStack[size - label - 1];
+ if (auto* delegateTry = delegateScope.getTry()) {
+ // Only delegates can reference the try name in Binaryen IR, so trys might
+ // need two labels: one for delegates and one for all other control flow.
+ // These labels must be different to satisfy the Binaryen validator. To
+ // keep this complexity contained within the handling of trys and
+ // delegates, pretend there is just the single normal label and add a
+ // prefix to it to generate the delegate label.
+ auto delegateName =
+ Name(std::string("__delegate__") + getLabelName(label)->toString());
+ delegateTry->name = delegateName;
+ tryy->delegateTarget = delegateName;
+ break;
+ } else if (delegateScope.getFunction()) {
+ tryy->delegateTarget = DELEGATE_CALLER_TARGET;
+ break;
+ }
+ }
+ if (label == scopeStack.size()) {
+ return Err{"unexpected delegate"};
+ }
+ // Delegate ends the try.
+ return visitEnd();
+}
+
Result<> IRBuilder::visitEnd() {
auto scope = getScope();
if (scope.isNone()) {
@@ -634,18 +748,50 @@ Result<> IRBuilder::visitEnd() {
iff->ifFalse = *expr;
iff->finalize(iff->type);
push(maybeWrapForLabel(iff));
+ } else if (auto* tryy = scope.getTry()) {
+ tryy->body = *expr;
+ tryy->finalize(tryy->type);
+ push(maybeWrapForLabel(tryy));
+ } else if (Try * tryy;
+ (tryy = scope.getCatch()) || (tryy = scope.getCatchAll())) {
+ tryy->catchBodies.push_back(*expr);
+ tryy->finalize(tryy->type);
+ push(maybeWrapForLabel(tryy));
} else {
WASM_UNREACHABLE("unexpected scope kind");
}
return Ok{};
}
-Result<Index> IRBuilder::getLabelIndex(Name label) {
+Result<Index> IRBuilder::getLabelIndex(Name label, bool inDelegate) {
auto it = labelDepths.find(label);
if (it == labelDepths.end() || it->second.empty()) {
- return Err{"unexpected label '"s + label.toString()};
+ return Err{"unexpected label '"s + label.toString() + "'"};
}
- return scopeStack.size() - it->second.back();
+ auto index = scopeStack.size() - it->second.back();
+ if (inDelegate) {
+ if (index == 0) {
+ // The real label we're referencing, if it exists, has been shadowed by
+ // the `try`. Get the previous label with this name instead. For example:
+ //
+ // block $l
+ // try $l
+ // delegate $l
+ // end
+ //
+ // The `delegate $l` should target the block, not the try, even though a
+ // normal branch to $l in the try's scope would target the try.
+ if (it->second.size() <= 1) {
+ return Err{"unexpected self-referencing label '"s + label.toString() +
+ "'"};
+ }
+ index = scopeStack.size() - it->second[it->second.size() - 2];
+ assert(index != 0);
+ }
+ // Adjust the index to be relative to the try.
+ --index;
+ }
+ return index;
}
Result<Name> IRBuilder::getLabelName(Index label) {
@@ -1014,7 +1160,11 @@ Result<> IRBuilder::makeRefEq() {
// Result<> IRBuilder::makeTableGrow() {}
-// Result<> IRBuilder::makeTry() {}
+Result<> IRBuilder::makeTry(Name label, Type type) {
+ auto* tryy = wasm.allocator.alloc<Try>();
+ tryy->type = type;
+ return visitTryStart(tryy, label);
+}
Result<> IRBuilder::makeThrow(Name tag) {
Throw curr(wasm.allocator);