Skip to content

Commit 6566329

Browse files
authored
[EH][GC] Send a non-nullable exnref from TryTable (WebAssembly#7013)
When EH+GC are enabled then wasm has non-nullable types, and the sent exnref should be non-nullable. In BinaryenIR we use the non- nullable type all the time, which we also do for function references and other things; we lower it if GC is not enabled to a nullable type for the binary format (see `WasmBinaryWriter::writeType`, to which comments were added in this PR). That is, this PR makes us handle exnref the same as those other types. A new test verifies that behavior. Various existing tests are updated because ReFinalize will now use the more refined type, so this is an optimization. It is also a bugfix as in WebAssembly#6987 we started to emit the refined form in the fuzzer, and this PR makes us handle it properly in validation and ReFinalization.
1 parent fd86ead commit 6566329

File tree

7 files changed

+119
-76
lines changed

7 files changed

+119
-76
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,16 @@ There are a few differences between Binaryen IR and the WebAssembly language:
148148
much about this when writing Binaryen passes. For more details see the
149149
`requiresNonNullableLocalFixups()` hook in `pass.h` and the
150150
`LocalStructuralDominance` class.
151+
* Binaryen IR uses the most refined types possible for references,
152+
specifically:
153+
* The IR type of a `ref.func` is always a specific function type, and not
154+
plain `funcref`. It is also non-nullable.
155+
* Non-nullable types are also used for the type that `try_table` sends
156+
on branches (if we branch, a null is never sent), that is, it sends
157+
(ref exn) and not (ref null exn).
158+
In both cases if GC is not enabled then we emit the less-refined type in the
159+
binary. When reading a binary, the more refined types will be applied as we
160+
build the IR.
151161
* `br_if` output types are more refined in Binaryen IR: they have the type of
152162
the value, when a value flows in. In the wasm spec the type is that of the
153163
branch target, which may be less refined. Using the more refined type here

src/wasm/wasm-binary.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1544,9 +1544,9 @@ void WasmBinaryWriter::writeInlineBuffer(const char* data, size_t size) {
15441544

15451545
void WasmBinaryWriter::writeType(Type type) {
15461546
if (type.isRef()) {
1547-
// The only reference types allowed without GC are funcref and externref. We
1548-
// internally use more refined versions of those types, but we cannot emit
1549-
// those more refined types.
1547+
// The only reference types allowed without GC are funcref, externref, and
1548+
// exnref. We internally use more refined versions of those types, but we
1549+
// cannot emit those without GC.
15501550
if (!wasm->features.hasGC()) {
15511551
auto ht = type.getHeapType();
15521552
if (ht.isMaybeShared(HeapType::string)) {
@@ -1555,6 +1555,8 @@ void WasmBinaryWriter::writeType(Type type) {
15551555
// string, the stringref feature must be enabled.
15561556
type = Type(HeapTypes::string.getBasic(ht.getShared()), Nullable);
15571557
} else {
1558+
// Only the top type (func, extern, exn) is available, and only the
1559+
// nullable version.
15581560
type = Type(type.getHeapType().getTop(), Nullable);
15591561
}
15601562
}

src/wasm/wasm-validator.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2658,7 +2658,7 @@ void FunctionValidator::visitTryTable(TryTable* curr) {
26582658
"the number of catch tags and sent types do not match");
26592659

26602660
const char* invalidSentTypeMsg = "invalid catch sent type information";
2661-
Type exnref = Type(HeapType::exn, Nullable);
2661+
Type exnref = Type(HeapType::exn, NonNullable);
26622662
for (Index i = 0; i < curr->catchTags.size(); i++) {
26632663
auto sentType = curr->sentTypes[i];
26642664
size_t tagTypeSize;

src/wasm/wasm.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -933,7 +933,11 @@ static void populateTryTableSentTypes(TryTable* curr, Module* wasm) {
933933
return;
934934
}
935935
curr->sentTypes.clear();
936-
Type exnref = Type(HeapType::exn, Nullable);
936+
// We always use the refined non-nullable type in our IR, which is what the
937+
// wasm spec defines when GC is enabled (=== non-nullable types are allowed).
938+
// If GC is not enabled then we emit a nullable type in the binary format in
939+
// WasmBinaryWriter::writeType.
940+
Type exnref = Type(HeapType::exn, NonNullable);
937941
for (Index i = 0; i < curr->catchTags.size(); i++) {
938942
auto tagName = curr->catchTags[i];
939943
std::vector<Type> sentType;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
2+
3+
;; Test that we do not emit an invalid (ref exn) when exceptions are enabled
4+
;; but not GC. GC is required for us to be non-nullable.
5+
6+
;; RUN: wasm-opt %s --enable-reference-types --enable-exception-handling --disable-gc --roundtrip -S -o - | filecheck %s
7+
8+
(module
9+
;; CHECK: (func $test (result exnref)
10+
;; CHECK-NEXT: (block $label$1 (result exnref)
11+
;; CHECK-NEXT: (try_table (catch_all_ref $label$1)
12+
;; CHECK-NEXT: (unreachable)
13+
;; CHECK-NEXT: )
14+
;; CHECK-NEXT: )
15+
;; CHECK-NEXT: )
16+
(func $test (result exnref)
17+
;; It is valid to write (ref exn) in Binaryen IR, and internally that is how
18+
;; we represent things, but when we emit the binary we emit a nullable type,
19+
;; so after the roundtrip we are less refined.
20+
(block $label (result (ref exn))
21+
(try_table (catch_all_ref $label)
22+
(unreachable)
23+
)
24+
)
25+
)
26+
)
27+

test/lit/passes/merge-blocks-eh.wast

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@
100100

101101
;; CHECK: (func $drop-block-try_catch_multi_partial (type $0)
102102
;; CHECK-NEXT: (tuple.drop 2
103-
;; CHECK-NEXT: (block $outer (type $1) (result i32 exnref)
103+
;; CHECK-NEXT: (block $outer (type $2) (result i32 (ref exn))
104104
;; CHECK-NEXT: (block $inner
105105
;; CHECK-NEXT: (try_table (catch_ref $i32 $outer) (catch_all $inner)
106106
;; CHECK-NEXT: (call $import)

0 commit comments

Comments
 (0)