summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Lively <tlively@google.com>2023-12-12 18:50:44 -0800
committerGitHub <noreply@github.com>2023-12-12 18:50:44 -0800
commit8e9199274e4dbd93b1ca65d9c053c4610599e69f (patch)
tree48927ac72d6ada4ca61dc0bad9a0db3378746394
parent28bea549061d88a8a7f7d05f3acf3bca175f0102 (diff)
downloadbinaryen-8e9199274e4dbd93b1ca65d9c053c4610599e69f.tar.gz
binaryen-8e9199274e4dbd93b1ca65d9c053c4610599e69f.tar.bz2
binaryen-8e9199274e4dbd93b1ca65d9c053c4610599e69f.zip
[Parser] Parse rethrow (#6155)
Like `delegate`, rethrow takes a `Try` label. Refactor the delegate handling so that `Try` can share its logic.
-rw-r--r--src/parser/contexts.h5
-rw-r--r--src/parser/parsers.h4
-rw-r--r--src/wasm-ir-builder.h3
-rw-r--r--src/wasm/wasm-ir-builder.cpp54
-rw-r--r--test/lit/wat-kitchen-sink.wast158
5 files changed, 207 insertions, 17 deletions
diff --git a/src/parser/contexts.h b/src/parser/contexts.h
index 7ce5bbf96..15b82fe64 100644
--- a/src/parser/contexts.h
+++ b/src/parser/contexts.h
@@ -418,6 +418,7 @@ struct NullInstrParserCtx {
Result<> makeTableFill(Index, TableIdxT*) { return Ok{}; }
Result<> makeTableCopy(Index, TableIdxT*, TableIdxT*) { return Ok{}; }
Result<> makeThrow(Index, TagIdxT) { return Ok{}; }
+ Result<> makeRethrow(Index, LabelIdxT) { return Ok{}; }
template<typename HeapTypeT> Result<> makeCallRef(Index, HeapTypeT, bool) {
return Ok{};
}
@@ -1572,6 +1573,10 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
return withLoc(pos, irBuilder.makeThrow(tag));
}
+ Result<> makeRethrow(Index pos, Index label) {
+ return withLoc(pos, irBuilder.makeRethrow(label));
+ }
+
Result<> makeCallRef(Index pos, HeapType type, bool isReturn) {
return withLoc(pos, irBuilder.makeCallRef(type, isReturn));
}
diff --git a/src/parser/parsers.h b/src/parser/parsers.h
index 67bd8bddd..be8db49a9 100644
--- a/src/parser/parsers.h
+++ b/src/parser/parsers.h
@@ -1480,7 +1480,9 @@ template<typename Ctx> Result<> makeThrow(Ctx& ctx, Index pos) {
}
template<typename Ctx> Result<> makeRethrow(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
+ auto label = labelidx(ctx);
+ CHECK_ERR(label);
+ return ctx.makeRethrow(pos, *label);
}
template<typename Ctx> Result<> makeTupleMake(Ctx& ctx, Index pos) {
diff --git a/src/wasm-ir-builder.h b/src/wasm-ir-builder.h
index ab633b9fa..a7e36bd10 100644
--- a/src/wasm-ir-builder.h
+++ b/src/wasm-ir-builder.h
@@ -156,7 +156,7 @@ public:
[[nodiscard]] Result<> makeTableCopy(Name destTable, Name srcTable);
[[nodiscard]] Result<> makeTry(Name label, Type type);
[[nodiscard]] Result<> makeThrow(Name tag);
- // [[nodiscard]] Result<> makeRethrow();
+ [[nodiscard]] Result<> makeRethrow(Index label);
// [[nodiscard]] Result<> makeTupleMake();
// [[nodiscard]] Result<> makeTupleExtract();
[[nodiscard]] Result<> makeRefI31();
@@ -459,6 +459,7 @@ private:
Result<Expression*> finishScope(Block* block = nullptr);
[[nodiscard]] Result<Name> getLabelName(Index label);
+ [[nodiscard]] Result<Name> getDelegateLabelName(Index label);
[[nodiscard]] Result<Index> addScratchLocal(Type);
[[nodiscard]] Result<Expression*> pop();
diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp
index a95329f7d..5301b75dc 100644
--- a/src/wasm/wasm-ir-builder.cpp
+++ b/src/wasm/wasm-ir-builder.cpp
@@ -499,8 +499,8 @@ Result<> IRBuilder::visitLoopStart(Loop* loop) {
}
Result<> IRBuilder::visitTryStart(Try* tryy, Name label) {
- // The delegate label will be regenerated if we need it. See `visitDelegate`
- // for details.
+ // The delegate label will be regenerated if we need it. See
+ // `getDelegateLabelName` for details.
tryy->name = Name();
pushScope(ScopeCtx::makeTry(tryy, label));
return Ok{};
@@ -663,6 +663,33 @@ Result<> IRBuilder::visitCatchAll() {
return Ok{};
}
+Result<Name> IRBuilder::getDelegateLabelName(Index label) {
+ if (label >= scopeStack.size()) {
+ return Err{"invalid label: " + std::to_string(label)};
+ }
+ auto& scope = scopeStack[scopeStack.size() - label - 1];
+ auto* delegateTry = scope.getTry();
+ if (!delegateTry) {
+ delegateTry = scope.getCatch();
+ }
+ if (!delegateTry) {
+ delegateTry = scope.getCatchAll();
+ }
+ if (!delegateTry) {
+ return Err{"expected try scope at label " + std::to_string(label)};
+ }
+ // Only delegate and rethrow can reference the try name in Binaryen IR, so
+ // trys might need two labels: one for delegate/rethrow 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;
+ return delegateName;
+}
+
Result<> IRBuilder::visitDelegate(Index label) {
auto& scope = getScope();
auto* tryy = scope.getTry();
@@ -676,17 +703,10 @@ Result<> IRBuilder::visitDelegate(Index label) {
++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;
+ if (delegateScope.getTry()) {
+ auto delegateName = getDelegateLabelName(label);
+ CHECK_ERR(delegateName);
+ tryy->delegateTarget = *delegateName;
break;
} else if (delegateScope.getFunction()) {
tryy->delegateTarget = DELEGATE_CALLER_TARGET;
@@ -1215,7 +1235,13 @@ Result<> IRBuilder::makeThrow(Name tag) {
return Ok{};
}
-// Result<> IRBuilder::makeRethrow() {}
+Result<> IRBuilder::makeRethrow(Index label) {
+ // Rethrow references `Try` labels directly, just like `delegate`.
+ auto name = getDelegateLabelName(label);
+ CHECK_ERR(name);
+ push(builder.makeRethrow(*name));
+ return Ok{};
+}
// Result<> IRBuilder::makeTupleMake() {}
diff --git a/test/lit/wat-kitchen-sink.wast b/test/lit/wat-kitchen-sink.wast
index 2be098baf..c4bd48260 100644
--- a/test/lit/wat-kitchen-sink.wast
+++ b/test/lit/wat-kitchen-sink.wast
@@ -2024,6 +2024,162 @@
)
)
+ ;; CHECK: (func $rethrow (type $void)
+ ;; CHECK-NEXT: (block $label
+ ;; CHECK-NEXT: (try $__delegate__label
+ ;; CHECK-NEXT: (do
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (catch $empty
+ ;; CHECK-NEXT: (rethrow $__delegate__label)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $rethrow
+ try
+ catch $empty
+ rethrow 0
+ end
+ )
+
+ ;; CHECK: (func $rethrow-named (type $void)
+ ;; CHECK-NEXT: (block $l
+ ;; CHECK-NEXT: (try $__delegate__l
+ ;; CHECK-NEXT: (do
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (catch $empty
+ ;; CHECK-NEXT: (rethrow $__delegate__l)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $rethrow-named
+ try $l
+ catch $empty
+ rethrow $l
+ end
+ )
+
+ ;; CHECK: (func $rethrow-nested (type $void)
+ ;; CHECK-NEXT: (block $label
+ ;; CHECK-NEXT: (try $__delegate__label
+ ;; CHECK-NEXT: (do
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (catch $empty
+ ;; CHECK-NEXT: (try
+ ;; CHECK-NEXT: (do
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (catch $empty
+ ;; CHECK-NEXT: (rethrow $__delegate__label)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $rethrow-nested
+ try
+ catch $empty
+ block
+ try
+ catch $empty
+ rethrow 2
+ end
+ end
+ end
+ )
+
+ ;; CHECK: (func $rethrow-nested-named (type $void)
+ ;; CHECK-NEXT: (block $l
+ ;; CHECK-NEXT: (try $__delegate__l
+ ;; CHECK-NEXT: (do
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (catch $empty
+ ;; CHECK-NEXT: (try
+ ;; CHECK-NEXT: (do
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (catch $empty
+ ;; CHECK-NEXT: (rethrow $__delegate__l)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $rethrow-nested-named
+ try $l
+ catch $empty
+ block
+ try
+ catch $empty
+ rethrow $l
+ end
+ end
+ end
+ )
+
+ ;; CHECK: (func $rethrow-try-nested (type $void)
+ ;; CHECK-NEXT: (block $label
+ ;; CHECK-NEXT: (try $__delegate__label
+ ;; CHECK-NEXT: (do
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (catch $empty
+ ;; CHECK-NEXT: (try
+ ;; CHECK-NEXT: (do
+ ;; CHECK-NEXT: (rethrow $__delegate__label)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $rethrow-try-nested
+ try
+ catch $empty
+ block
+ try
+ ;; Same as before, but now the rethrow is in the inner try instead of the
+ ;; inner catch.
+ rethrow 2
+ end
+ end
+ end
+ )
+
+ ;; CHECK: (func $rethrow-try-nested-named (type $void)
+ ;; CHECK-NEXT: (block $l
+ ;; CHECK-NEXT: (try $__delegate__l
+ ;; CHECK-NEXT: (do
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (catch $empty
+ ;; CHECK-NEXT: (try
+ ;; CHECK-NEXT: (do
+ ;; CHECK-NEXT: (rethrow $__delegate__l)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $rethrow-try-nested-named
+ try $l
+ catch $empty
+ block
+ try
+ rethrow $l
+ end
+ end
+ end
+ )
+
;; CHECK: (func $label-siblings (type $void)
;; CHECK-NEXT: (block $l
;; CHECK-NEXT: (br $l)
@@ -2922,7 +3078,7 @@
(func $ref-func
ref.func $ref-func
drop
- ref.func 129
+ ref.func 135
drop
)