diff options
-rw-r--r-- | src/parser/contexts.h | 5 | ||||
-rw-r--r-- | src/parser/parsers.h | 4 | ||||
-rw-r--r-- | src/wasm-ir-builder.h | 3 | ||||
-rw-r--r-- | src/wasm/wasm-ir-builder.cpp | 54 | ||||
-rw-r--r-- | test/lit/wat-kitchen-sink.wast | 158 |
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 ) |