From 3bc8a5444c0bc6e4d13dd4da58e389821f9f0f36 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Oct 2025 11:10:20 -0700 Subject: [PATCH 01/18] start --- src/literal.h | 12 ------------ src/wasm-interpreter.h | 11 +++++++++++ test/lit/exec/tag-cross-module.wast | 18 ++++++++++++++++++ test/lit/exec/tag-cross-module.wast.second | 17 +++++++++++++++++ 4 files changed, 46 insertions(+), 12 deletions(-) create mode 100644 test/lit/exec/tag-cross-module.wast create mode 100644 test/lit/exec/tag-cross-module.wast.second diff --git a/src/literal.h b/src/literal.h index f20213e0529..10636f8b89a 100644 --- a/src/literal.h +++ b/src/literal.h @@ -785,18 +785,6 @@ struct GCData { : type(type), values(std::move(values)), desc(desc) {} }; -// The data of a (ref exn) literal. -struct ExnData { - // The tag of this exn data. - // TODO: handle cross-module calls using something other than a Name here. - Name tag; - - // The payload of this exn data. - Literals payload; - - ExnData(Name tag, Literals payload) : tag(tag), payload(payload) {} -}; - } // namespace wasm namespace std { diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index c37a76807c7..a5ef90946fc 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -157,6 +157,17 @@ struct FuncData { } }; +// The data of a (ref exn) literal. +struct ExnData { + // The tag of this exn data. + Tag* tag; + + // The payload of this exn data. + Literals payload; + + ExnData(Tag* tag, Literals payload) : tag(tag), payload(payload) {} +}; + // Suspend/resume support. // // As we operate directly on our structured IR, we do not have a program counter diff --git a/test/lit/exec/tag-cross-module.wast b/test/lit/exec/tag-cross-module.wast new file mode 100644 index 00000000000..95edee62269 --- /dev/null +++ b/test/lit/exec/tag-cross-module.wast @@ -0,0 +1,18 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --output=fuzz-exec and should not be edited. + +;; RUN: wasm-opt %s -all --fuzz-exec-before --fuzz-exec-second=%s.second -q -o /dev/null 2>&1 | filecheck %s + +;; Define a tag in this module, and another tag in the secondary module, with +;; the same name but different (incompatible) contents. The second module will +;; call our export, and when we throw our tag, it should not catch it. + +(module + (tag $tag (param structref)) + + (func $func (export "func") (result i32) + (throw $tag + (ref.null struct) + ) + ) +) + diff --git a/test/lit/exec/tag-cross-module.wast.second b/test/lit/exec/tag-cross-module.wast.second new file mode 100644 index 00000000000..6ff39406264 --- /dev/null +++ b/test/lit/exec/tag-cross-module.wast.second @@ -0,0 +1,17 @@ +(module + (import "primary" "func" (func $import (result i32))) + + (tag $tag (param (ref array))) + + (func $func2 (export "func2") (result (ref array)) + (block $block (result (ref array)) + (try_table (catch $tag $block) + (drop + (call $import) + ) + ) + (unreachable) + ) + ) +) + From 403269cf7034b32366878e9b1bae2bae48f6718e Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Oct 2025 11:16:17 -0700 Subject: [PATCH 02/18] work --- src/wasm-interpreter.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index a5ef90946fc..44aff9bf3c0 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -332,7 +332,7 @@ class ExpressionRunner : public OverriddenVisitor { } // Same as makeGCData but for ExnData. - Literal makeExnData(Name tag, const Literals& payload) { + Literal makeExnData(Tag* tag, const Literals& payload) { auto allocation = std::make_shared(tag, payload); #if __has_feature(leak_sanitizer) || __has_feature(address_sanitizer) __lsan_ignore_object(allocation.get()); @@ -4347,7 +4347,8 @@ class ModuleRunnerBase : public ExpressionRunner { auto exnData = e.exn.getExnData(); for (size_t i = 0; i < curr->catchTags.size(); i++) { - if (curr->catchTags[i] == exnData->tag) { + auto* tag = self()->getModule()->getTag(curr->catchTags[i]); + if (tag == exnData->tag) { multiValues.push_back(exnData->payload); return processCatchBody(curr->catchBodies[i]); } @@ -4370,7 +4371,8 @@ class ModuleRunnerBase : public ExpressionRunner { auto exnData = e.exn.getExnData(); for (size_t i = 0; i < curr->catchTags.size(); i++) { auto catchTag = curr->catchTags[i]; - if (!catchTag.is() || catchTag == exnData->tag) { + if (!catchTag.is() || + self()->getModule()->getTag(catchTag) == exnData->tag) { Flow ret; ret.breakTo = curr->catchDests[i]; if (catchTag.is()) { @@ -4662,8 +4664,9 @@ class ModuleRunnerBase : public ExpressionRunner { self()->continuationStore->resuming = false; if (auto* tag = currContinuation->exceptionTag) { // XXX tag->name lacks cross-module support + // TODO: find the original tag through imports, like we do for globals throwException(WasmException{ - self()->makeExnData(tag->name, currContinuation->resumeArguments)}); + self()->makeExnData(tag, currContinuation->resumeArguments)}); } } } From 1e98549db3ee4e3d8d33abed394bdb36e9a57050 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Oct 2025 11:18:07 -0700 Subject: [PATCH 03/18] work --- src/wasm-interpreter.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 44aff9bf3c0..fa44a82205e 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -1900,7 +1900,7 @@ class ExpressionRunner : public OverriddenVisitor { Flow visitThrow(Throw* curr) { Literals arguments; VISIT_ARGUMENTS(flow, curr->operands, arguments); - throwException(WasmException{makeExnData(curr->tag, arguments)}); + throwException(WasmException{getCanonicalTag(curr->tag), arguments)}); WASM_UNREACHABLE("throw"); } Flow visitRethrow(Rethrow* curr) { WASM_UNREACHABLE("unimp"); } @@ -4347,7 +4347,7 @@ class ModuleRunnerBase : public ExpressionRunner { auto exnData = e.exn.getExnData(); for (size_t i = 0; i < curr->catchTags.size(); i++) { - auto* tag = self()->getModule()->getTag(curr->catchTags[i]); + auto* tag = self()->getCanonicalTag(curr->catchTags[i]); if (tag == exnData->tag) { multiValues.push_back(exnData->payload); return processCatchBody(curr->catchBodies[i]); @@ -4372,7 +4372,7 @@ class ModuleRunnerBase : public ExpressionRunner { for (size_t i = 0; i < curr->catchTags.size(); i++) { auto catchTag = curr->catchTags[i]; if (!catchTag.is() || - self()->getModule()->getTag(catchTag) == exnData->tag) { + self()->getCanonicalTag(catchTag) == exnData->tag) { Flow ret; ret.breakTo = curr->catchDests[i]; if (catchTag.is()) { From 18acf83d06136eaefd1ed888c898d1b9c224f100 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Oct 2025 11:24:01 -0700 Subject: [PATCH 04/18] work --- src/wasm-interpreter.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index fa44a82205e..69e31ef2a40 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -1897,12 +1897,7 @@ class ExpressionRunner : public OverriddenVisitor { Flow visitElemDrop(ElemDrop* curr) { WASM_UNREACHABLE("unimp"); } Flow visitTry(Try* curr) { WASM_UNREACHABLE("unimp"); } Flow visitTryTable(TryTable* curr) { WASM_UNREACHABLE("unimp"); } - Flow visitThrow(Throw* curr) { - Literals arguments; - VISIT_ARGUMENTS(flow, curr->operands, arguments); - throwException(WasmException{getCanonicalTag(curr->tag), arguments)}); - WASM_UNREACHABLE("throw"); - } + Flow visitThrow(Throw* curr) { WASM_UNREACHABLE("unimp"); } Flow visitRethrow(Rethrow* curr) { WASM_UNREACHABLE("unimp"); } Flow visitThrowRef(ThrowRef* curr) { VISIT(flow, curr->exnref) @@ -4390,6 +4385,12 @@ class ModuleRunnerBase : public ExpressionRunner { throw; } } + Flow visitThrow(Throw* curr) { + Literals arguments; + VISIT_ARGUMENTS(flow, curr->operands, arguments); + throwException(WasmException{self()->getCanonicalTag(curr->tag), arguments}); + WASM_UNREACHABLE("throw"); + } Flow visitRethrow(Rethrow* curr) { for (int i = exceptionStack.size() - 1; i >= 0; i--) { if (exceptionStack[i].second == curr->target) { @@ -4458,9 +4459,8 @@ class ModuleRunnerBase : public ExpressionRunner { assert(self()->restoredValuesMap.empty()); // Throw, if we were resumed by resume_throw; if (auto* tag = currContinuation->exceptionTag) { - // XXX tag->name lacks cross-module support throwException(WasmException{ - self()->makeExnData(tag->name, currContinuation->resumeArguments)}); + self()->makeExnData(tag, currContinuation->resumeArguments)}); } return currContinuation->resumeArguments; } From 6b45a185658c2fbf911d342bed9459d26ab7e32f Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Oct 2025 11:24:45 -0700 Subject: [PATCH 05/18] work --- src/wasm-interpreter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 69e31ef2a40..5330645790d 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -4388,7 +4388,7 @@ class ModuleRunnerBase : public ExpressionRunner { Flow visitThrow(Throw* curr) { Literals arguments; VISIT_ARGUMENTS(flow, curr->operands, arguments); - throwException(WasmException{self()->getCanonicalTag(curr->tag), arguments}); + throwException(WasmException{self()->makeExnData(self()->getCanonicalTag(curr->tag), arguments)}); WASM_UNREACHABLE("throw"); } Flow visitRethrow(Rethrow* curr) { From 167cc41f7e8d2601b70f4b2952a8442973be9652 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Oct 2025 13:12:05 -0700 Subject: [PATCH 06/18] work --- src/tools/execution-results.h | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/tools/execution-results.h b/src/tools/execution-results.h index 0afdff0cde8..74118b6e5ec 100644 --- a/src/tools/execution-results.h +++ b/src/tools/execution-results.h @@ -42,13 +42,11 @@ struct LoggingExternalInterface : public ShellExternalInterface { Name exportedTable; Module& wasm; - // The name of the imported fuzzing tag for wasm. - Name wasmTag; + // The imported fuzzing tag for wasm. + Tag wasmTag; - // The name of the imported tag for js exceptions. If it is not imported, we - // use a default name here (which should differentiate it from any wasm - // exceptions). - Name jsTag = "__private"; + // The imported tag for js exceptions. + Tag jsTag; // The ModuleRunner and this ExternalInterface end up needing links both ways, // so we cannot init this in the constructor. @@ -67,12 +65,20 @@ struct LoggingExternalInterface : public ShellExternalInterface { } } + // Default names for tags. + wasmTag.module = "fuzzing-support"; + wasmTag.base = wasmTag.name = "wasmtag"; + + jsTag.module = "fuzzing-support"; + jsTag.base = "jstag"; + jsTag.name = "__private"; + for (auto& tag : wasm.tags) { if (tag->module == "fuzzing-support") { if (tag->base == "wasmtag") { - wasmTag = tag->name; + wasmTag.name = tag->name; } else if (tag->base == "jstag") { - jsTag = tag->name; + jsTag.name = tag->name; } } } @@ -117,7 +123,7 @@ struct LoggingExternalInterface : public ShellExternalInterface { if (arguments[0].geti32() == 0) { throwJSException(); } else { - auto payload = std::make_shared(wasmTag, arguments); + auto payload = std::make_shared(&wasmTag, arguments); throwException(WasmException{Literal(payload)}); } } else if (import->base == "table-get") { @@ -206,7 +212,7 @@ struct LoggingExternalInterface : public ShellExternalInterface { auto empty = HeapType(Struct{}); auto inner = Literal(std::make_shared(empty, Literals{}), empty); Literals arguments = {inner.externalize()}; - auto payload = std::make_shared(jsTag, arguments); + auto payload = std::make_shared(&jsTag, arguments); throwException(WasmException{Literal(payload)}); } From 2b5a6d7a13f169ae96470549f8da07860bc33f00 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Oct 2025 13:17:13 -0700 Subject: [PATCH 07/18] work --- src/wasm/wasm-interpreter.cpp | 2 +- test/lit/exec/tag-cross-module.wast | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/wasm/wasm-interpreter.cpp b/src/wasm/wasm-interpreter.cpp index 37ac05556ac..3af29d2c773 100644 --- a/src/wasm/wasm-interpreter.cpp +++ b/src/wasm/wasm-interpreter.cpp @@ -4,7 +4,7 @@ namespace wasm { std::ostream& operator<<(std::ostream& o, const WasmException& exn) { auto exnData = exn.exn.getExnData(); - return o << exnData->tag << " " << exnData->payload; + return o << exnData->tag->name << " " << exnData->payload; } } // namespace wasm diff --git a/test/lit/exec/tag-cross-module.wast b/test/lit/exec/tag-cross-module.wast index 95edee62269..7901c0b8719 100644 --- a/test/lit/exec/tag-cross-module.wast +++ b/test/lit/exec/tag-cross-module.wast @@ -9,6 +9,8 @@ (module (tag $tag (param structref)) + ;; CHECK: [fuzz-exec] calling func + ;; CHECK-NEXT: [exception thrown: tag nullref] (func $func (export "func") (result i32) (throw $tag (ref.null struct) From 08827eeb568a408dfadfcc07b2e23d97574ba9b1 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Oct 2025 13:20:46 -0700 Subject: [PATCH 08/18] work --- test/lit/exec/tag-cross-module.wast | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/lit/exec/tag-cross-module.wast b/test/lit/exec/tag-cross-module.wast index 7901c0b8719..e87372742de 100644 --- a/test/lit/exec/tag-cross-module.wast +++ b/test/lit/exec/tag-cross-module.wast @@ -1,5 +1,3 @@ -;; NOTE: Assertions have been generated by update_lit_checks.py --output=fuzz-exec and should not be edited. - ;; RUN: wasm-opt %s -all --fuzz-exec-before --fuzz-exec-second=%s.second -q -o /dev/null 2>&1 | filecheck %s ;; Define a tag in this module, and another tag in the secondary module, with @@ -9,8 +7,6 @@ (module (tag $tag (param structref)) - ;; CHECK: [fuzz-exec] calling func - ;; CHECK-NEXT: [exception thrown: tag nullref] (func $func (export "func") (result i32) (throw $tag (ref.null struct) @@ -18,3 +14,8 @@ ) ) +;; CHECK: [fuzz-exec] calling func +;; CHECK-NEXT: [exception thrown: tag nullref] +;; CHECK-NEXT: [fuzz-exec] calling func2 +;; CHECK-NEXT: [exception thrown: tag nullref] + From 7cd8db09da0b393603d9a212fd81bc4d21d1deeb Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Oct 2025 13:43:47 -0700 Subject: [PATCH 09/18] work --- src/tools/execution-results.h | 9 +++++++++ src/wasm-interpreter.h | 11 ++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/tools/execution-results.h b/src/tools/execution-results.h index 74118b6e5ec..915bc43d217 100644 --- a/src/tools/execution-results.h +++ b/src/tools/execution-results.h @@ -84,6 +84,15 @@ struct LoggingExternalInterface : public ShellExternalInterface { } } + Tag* getHostTag(Name name) override { + if (name == jsTag.name) { + return &jsTag; + } else if (name == wasmTag.name) { + return &wasmTag; + } + Fatal() << "missing host tag " << name; + } + Flow callImport(Function* import, const Literals& arguments) override { if (import->module == "fuzzing-support") { if (import->base.startsWith("log")) { diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 5330645790d..68c746b9d74 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -2911,6 +2911,11 @@ class ModuleRunnerBase : public ExpressionRunner { virtual void trap(const char* why) = 0; virtual void hostLimit(const char* why) = 0; virtual void throwException(const WasmException& exn) = 0; + // Get the Tag instance for a tag implemented in the host, that is, not + // among the linked ModuleRunner instances. + virtual Tag* getHostTag(Name name) { + WASM_UNREACHABLE("missing host tag"); + } // the default impls for load and store switch on the sizes. you can either // customize load/store, or the sub-functions which they call @@ -3434,7 +3439,11 @@ class ModuleRunnerBase : public ExpressionRunner { auto* inst = self(); auto* tag = inst->wasm.getTag(name); while (tag->imported()) { - inst = inst->linkedInstances.at(tag->module).get(); + auto iter = inst->linkedInstances.find(tag->module); + if (iter == inst->linkedInstances.end()) { + return externalInterface->getHostTag(name); + } + inst = iter->second.get(); auto* tagExport = inst->wasm.getExport(tag->base); tag = inst->wasm.getTag(*tagExport->getInternalName()); } From b876369f90f4d511921d98b89101ae0e0f1e6f9d Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Oct 2025 13:46:12 -0700 Subject: [PATCH 10/18] fix --- src/wasm-interpreter.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 68c746b9d74..2fc9c1575da 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -2913,9 +2913,7 @@ class ModuleRunnerBase : public ExpressionRunner { virtual void throwException(const WasmException& exn) = 0; // Get the Tag instance for a tag implemented in the host, that is, not // among the linked ModuleRunner instances. - virtual Tag* getHostTag(Name name) { - WASM_UNREACHABLE("missing host tag"); - } + virtual Tag* getHostTag(Name name) { WASM_UNREACHABLE("missing host tag"); } // the default impls for load and store switch on the sizes. you can either // customize load/store, or the sub-functions which they call @@ -4397,7 +4395,8 @@ class ModuleRunnerBase : public ExpressionRunner { Flow visitThrow(Throw* curr) { Literals arguments; VISIT_ARGUMENTS(flow, curr->operands, arguments); - throwException(WasmException{self()->makeExnData(self()->getCanonicalTag(curr->tag), arguments)}); + throwException(WasmException{ + self()->makeExnData(self()->getCanonicalTag(curr->tag), arguments)}); WASM_UNREACHABLE("throw"); } Flow visitRethrow(Rethrow* curr) { From 9bbdfe72666c0f23a4967ff6c26958298702cc00 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Oct 2025 13:55:45 -0700 Subject: [PATCH 11/18] fix --- test/lit/exec/tag-cross-module.wast | 7 ++++++- test/lit/exec/tag-cross-module.wast.second | 17 ++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/test/lit/exec/tag-cross-module.wast b/test/lit/exec/tag-cross-module.wast index e87372742de..9ae493c87b5 100644 --- a/test/lit/exec/tag-cross-module.wast +++ b/test/lit/exec/tag-cross-module.wast @@ -7,6 +7,8 @@ (module (tag $tag (param structref)) + (export "primary-tag" (tag $tag)) + (func $func (export "func") (result i32) (throw $tag (ref.null struct) @@ -16,6 +18,9 @@ ;; CHECK: [fuzz-exec] calling func ;; CHECK-NEXT: [exception thrown: tag nullref] -;; CHECK-NEXT: [fuzz-exec] calling func2 +;; CHECK-NEXT: [fuzz-exec] calling func2-internal ;; CHECK-NEXT: [exception thrown: tag nullref] +;; CHECK-NEXT: [fuzz-exec] calling func2-imported +;; CHECK-NEXT: func2-imported => null + diff --git a/test/lit/exec/tag-cross-module.wast.second b/test/lit/exec/tag-cross-module.wast.second index 6ff39406264..3384f19d9b3 100644 --- a/test/lit/exec/tag-cross-module.wast.second +++ b/test/lit/exec/tag-cross-module.wast.second @@ -1,9 +1,12 @@ (module (import "primary" "func" (func $import (result i32))) + (import "primary" "primary-tag" (tag $ptag (param structref))) + (tag $tag (param (ref array))) - (func $func2 (export "func2") (result (ref array)) + (func $func2-internal (export "func2-internal") (result (ref array)) + ;; Try to catch the internal tag. This fails to catch. (block $block (result (ref array)) (try_table (catch $tag $block) (drop @@ -13,5 +16,17 @@ (unreachable) ) ) + + (func $func2-imported (export "func2-imported") (result structref) + ;; Try to catch the imported tag. This successfully catches. + (block $block (result structref) + (try_table (catch $ptag $block) + (drop + (call $import) + ) + ) + (unreachable) + ) + ) ) From fbcfc9c94ef393696a5ac420fc35abd6fa95f337 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Oct 2025 13:58:04 -0700 Subject: [PATCH 12/18] work --- src/tools/execution-results.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/execution-results.h b/src/tools/execution-results.h index 2a65d845706..2a94d06f826 100644 --- a/src/tools/execution-results.h +++ b/src/tools/execution-results.h @@ -137,7 +137,7 @@ struct LoggingExternalInterface : public ShellExternalInterface { if (arguments[0].geti32() == 0) { throwJSException(); } else { - auto payload = std::make_shared(wasmTag, arguments); + auto payload = std::make_shared(&wasmTag, arguments); throwException(WasmException{Literal(payload)}); } } else if (import->base == "table-get") { From ac39b5f6da15c8896721638c3a8695b876d6b991 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Oct 2025 14:09:33 -0700 Subject: [PATCH 13/18] fix --- src/wasm-interpreter.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index baf5e92b3f7..8761bbb78d4 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -1900,7 +1900,16 @@ class ExpressionRunner : public OverriddenVisitor { Flow visitElemDrop(ElemDrop* curr) { WASM_UNREACHABLE("unimp"); } Flow visitTry(Try* curr) { WASM_UNREACHABLE("unimp"); } Flow visitTryTable(TryTable* curr) { WASM_UNREACHABLE("unimp"); } - Flow visitThrow(Throw* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitThrow(Throw* curr) { + // Single-module implementation. This is used from Precompute, for example. + // It is overriden in ModuleRunner to add logic for finding the proper + // imported tag (which single-module cases don't care about). + Literals arguments; + VISIT_ARGUMENTS(flow, curr->operands, arguments); + throwException(WasmException{ + self()->makeExnData(self()->getModule()->getTag(curr->tag), arguments)}); + WASM_UNREACHABLE("throw"); + } Flow visitRethrow(Rethrow* curr) { WASM_UNREACHABLE("unimp"); } Flow visitThrowRef(ThrowRef* curr) { VISIT(flow, curr->exnref) From d13eca96671cbe614555393783dd20a1b1aebd05 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Oct 2025 14:21:51 -0700 Subject: [PATCH 14/18] todone --- src/wasm-interpreter.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 8761bbb78d4..51db07221a3 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -4698,8 +4698,6 @@ class ModuleRunnerBase : public ExpressionRunner { // set), so resuming is done. (And throw, if resume_throw.) self()->continuationStore->resuming = false; if (auto* tag = currContinuation->exceptionTag) { - // XXX tag->name lacks cross-module support - // TODO: find the original tag through imports, like we do for globals throwException(WasmException{ self()->makeExnData(tag, currContinuation->resumeArguments)}); } From dda230bc81ea953d8015fc901ea8b513e7729c5f Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Oct 2025 15:42:25 -0700 Subject: [PATCH 15/18] todo --- src/wasm-interpreter.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 51db07221a3..a3fe4e34b4e 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -163,6 +163,8 @@ struct FuncData { // The data of a (ref exn) literal. struct ExnData { // The tag of this exn data. + // TODO: Add self, like in FuncData, to handle the case of a module that is + // instantiated multiple times. Tag* tag; // The payload of this exn data. From 0eb991380a8b87e475e8503eb3d2e8261b458ae1 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Oct 2025 15:47:48 -0700 Subject: [PATCH 16/18] Enable spec test --- scripts/test/shared.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/test/shared.py b/scripts/test/shared.py index 2fd1934f4c9..5b49475345b 100644 --- a/scripts/test/shared.py +++ b/scripts/test/shared.py @@ -442,7 +442,6 @@ def get_tests(test_dir, extensions=[], recursive=False): 'instance.wast', # Requires wast `module definition` support 'table64.wast', # Requires wast `module definition` support 'table_grow.wast', # Incorrect table linking semantics in interpreter - 'try_catch.wast', # Incorrect imported tag semantics in interpreter 'tag.wast', # Non-empty tag results allowed by stack switching 'try_table.wast', # Requires try_table interpretation 'local_init.wast', # Requires local validation to respect unnamed blocks From 360f53f68db457293a312cab314d909f6f8ac7ca Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Oct 2025 16:16:47 -0700 Subject: [PATCH 17/18] rename to getImportedTag --- src/tools/execution-results.h | 2 +- src/wasm-interpreter.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/execution-results.h b/src/tools/execution-results.h index 2a94d06f826..f61cc2a8ae9 100644 --- a/src/tools/execution-results.h +++ b/src/tools/execution-results.h @@ -84,7 +84,7 @@ struct LoggingExternalInterface : public ShellExternalInterface { } } - Tag* getHostTag(Name name) override { + Tag* getImportedTag(Name name) override { if (name == jsTag.name) { return &jsTag; } else if (name == wasmTag.name) { diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index a3fe4e34b4e..0874003b00c 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -2927,7 +2927,7 @@ class ModuleRunnerBase : public ExpressionRunner { virtual void throwException(const WasmException& exn) = 0; // Get the Tag instance for a tag implemented in the host, that is, not // among the linked ModuleRunner instances. - virtual Tag* getHostTag(Name name) { WASM_UNREACHABLE("missing host tag"); } + virtual Tag* getImportedTag(Name name) { WASM_UNREACHABLE("missing imported tag"); } // the default impls for load and store switch on the sizes. you can either // customize load/store, or the sub-functions which they call @@ -3468,7 +3468,7 @@ class ModuleRunnerBase : public ExpressionRunner { while (tag->imported()) { auto iter = inst->linkedInstances.find(tag->module); if (iter == inst->linkedInstances.end()) { - return externalInterface->getHostTag(name); + return externalInterface->getImportedTag(name); } inst = iter->second.get(); auto* tagExport = inst->wasm.getExport(tag->base); From 0b9465802b331c4173800bae43f256b800d31f0b Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Oct 2025 16:22:14 -0700 Subject: [PATCH 18/18] Add and use getExportedTag --- src/wasm-interpreter.h | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 0874003b00c..34339f625cf 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -3214,6 +3214,18 @@ class ModuleRunnerBase : public ExpressionRunner { return iter->second; } + Tag* getExportedTag(Name name) { + Export* export_ = wasm.getExportOrNull(name); + if (!export_ || export_->kind != ExternalKind::Tag) { + externalInterface->trap("exported tag not found"); + } + auto* tag = wasm.getTag(*export_->getInternalName()); + if (tag->imported()) { + tag = externalInterface->getImportedTag(name); + } + return tag; + } + std::string printFunctionStack() { std::string ret = "/== (binaryen interpreter stack trace)\n"; for (int i = int(functionStack.size()) - 1; i >= 0; i--) { @@ -3471,8 +3483,7 @@ class ModuleRunnerBase : public ExpressionRunner { return externalInterface->getImportedTag(name); } inst = iter->second.get(); - auto* tagExport = inst->wasm.getExport(tag->base); - tag = inst->wasm.getTag(*tagExport->getInternalName()); + tag = inst->getExportedTag(tag->base); } return tag; }