From f73773b055466a0b152311680ebe4647d339a3cc Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Thu, 7 Dec 2023 15:09:57 -0800 Subject: [PATCH] [Parser] Parse rethrow Like `delegate`, rethrow takes a `Try` label. Refactor the delegate handling so that `Try` can share its logic. --- src/parser/contexts.h | 5 ++ src/parser/parsers.h | 4 +- src/wasm-ir-builder.h | 3 +- src/wasm/wasm-ir-builder.cpp | 54 ++++++++++++----- test/lit/wat-kitchen-sink.wast | 102 ++++++++++++++++++++++++++++++++- 5 files changed, 151 insertions(+), 17 deletions(-) diff --git a/src/parser/contexts.h b/src/parser/contexts.h index 7ce5bbf96cd..15b82fe646f 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 Result<> makeCallRef(Index, HeapTypeT, bool) { return Ok{}; } @@ -1572,6 +1573,10 @@ struct ParseDefsCtx : TypeParserCtx { 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 19dc8736d45..677bc71f091 100644 --- a/src/parser/parsers.h +++ b/src/parser/parsers.h @@ -1479,7 +1479,9 @@ template Result<> makeThrow(Ctx& ctx, Index pos) { } template 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 Result<> makeTupleMake(Ctx& ctx, Index pos) { diff --git a/src/wasm-ir-builder.h b/src/wasm-ir-builder.h index ab633b9fac5..a7e36bd1060 100644 --- a/src/wasm-ir-builder.h +++ b/src/wasm-ir-builder.h @@ -156,7 +156,7 @@ class IRBuilder : public UnifiedExpressionVisitor> { [[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 @@ class IRBuilder : public UnifiedExpressionVisitor> { Result finishScope(Block* block = nullptr); [[nodiscard]] Result getLabelName(Index label); + [[nodiscard]] Result getDelegateLabelName(Index label); [[nodiscard]] Result addScratchLocal(Type); [[nodiscard]] Result pop(); diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index a95329f7d85..5301b75dc9c 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 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 208e4a7a719..ca6a04473a1 100644 --- a/test/lit/wat-kitchen-sink.wast +++ b/test/lit/wat-kitchen-sink.wast @@ -2024,6 +2024,106 @@ ) ) + ;; 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 $label-siblings (type $void) ;; CHECK-NEXT: (block $l ;; CHECK-NEXT: (br $l) @@ -2922,7 +3022,7 @@ (func $ref-func ref.func $ref-func drop - ref.func 129 + ref.func 133 drop )