Skip to content

Commit 4c53361

Browse files
authored
[EH] Add instructions for new proposal (#6181)
This adds basic support for the new instructions in the new EH proposal passed at the Oct CG hybrid CG meeting: https://github.com/WebAssembly/meetings/blob/main/main/2023/CG-10.md https://github.com/WebAssembly/exception-handling/blob/main/proposals/exception-handling/Exceptions.md This mainly adds two instructions: `try_table` and `throw_ref`. This is the bare minimum required to read and write text and binary format, and does not include analyses or optimizations. (It includes some analysis required for validation of existing instructions.) Validation for the new instructions is not yet included. `try_table` faces the same problem with the `resume` instruction in #6083 that without the module-level tag info, we are unable to know the 'sent types' of `try_table`. This solves it with a similar approach taken in #6083: this adds `Module*` parameter to `finalize` methods, which defaults to `nullptr` when not given. The `Module*` parameter is given when called from the binary and text parser, and we cache those tag types in `sentTypes` array within `TryTable` class. In later optimization passes, as long as they don't touch tags, it is fine to call `finalize` without the `Module*`. Refer to #6083 (comment) and #6096 for related discussions when `resume` was added.
1 parent cad983c commit 4c53361

35 files changed

+1185
-72
lines changed

scripts/gen-s-parser.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -554,8 +554,10 @@
554554
#
555555
# exception handling instructions
556556
("try", "makeTry(s)"),
557+
("try_table", "makeTryTable(s)"),
557558
("throw", "makeThrow(s)"),
558559
("rethrow", "makeRethrow(s)"),
560+
("throw_ref", "makeThrowRef(s)"),
559561
# Multivalue pseudoinstructions
560562
("tuple.make", "makeTupleMake(s)"),
561563
("tuple.extract", "makeTupleExtract(s)"),
@@ -714,7 +716,7 @@ def instruction_parser(new_parser=False):
714716
inst_length = 0
715717
for inst, expr in instructions:
716718
if new_parser and inst in {"block", "loop", "if", "try", "then",
717-
"else"}:
719+
"else", "try_table"}:
718720
# These are either control flow handled manually or not real
719721
# instructions. Skip them.
720722
continue

src/gen-s-parser.inc

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3393,15 +3393,31 @@ switch (buf[0]) {
33933393
case 'e':
33943394
if (op == "then"sv) { return makeThenOrElse(s); }
33953395
goto parse_error;
3396-
case 'r':
3397-
if (op == "throw"sv) { return makeThrow(s); }
3396+
case 'r': {
3397+
switch (buf[5]) {
3398+
case '\0':
3399+
if (op == "throw"sv) { return makeThrow(s); }
3400+
goto parse_error;
3401+
case '_':
3402+
if (op == "throw_ref"sv) { return makeThrowRef(s); }
3403+
goto parse_error;
3404+
default: goto parse_error;
3405+
}
3406+
}
3407+
default: goto parse_error;
3408+
}
3409+
}
3410+
case 'r': {
3411+
switch (buf[3]) {
3412+
case '\0':
3413+
if (op == "try"sv) { return makeTry(s); }
3414+
goto parse_error;
3415+
case '_':
3416+
if (op == "try_table"sv) { return makeTryTable(s); }
33983417
goto parse_error;
33993418
default: goto parse_error;
34003419
}
34013420
}
3402-
case 'r':
3403-
if (op == "try"sv) { return makeTry(s); }
3404-
goto parse_error;
34053421
case 'u': {
34063422
switch (buf[6]) {
34073423
case 'd':
@@ -8646,12 +8662,23 @@ switch (buf[0]) {
86468662
default: goto parse_error;
86478663
}
86488664
}
8649-
case 'h':
8650-
if (op == "throw"sv) {
8651-
CHECK_ERR(makeThrow(ctx, pos));
8652-
return Ok{};
8665+
case 'h': {
8666+
switch (buf[5]) {
8667+
case '\0':
8668+
if (op == "throw"sv) {
8669+
CHECK_ERR(makeThrow(ctx, pos));
8670+
return Ok{};
8671+
}
8672+
goto parse_error;
8673+
case '_':
8674+
if (op == "throw_ref"sv) {
8675+
CHECK_ERR(makeThrowRef(ctx, pos));
8676+
return Ok{};
8677+
}
8678+
goto parse_error;
8679+
default: goto parse_error;
86538680
}
8654-
goto parse_error;
8681+
}
86558682
case 'u': {
86568683
switch (buf[6]) {
86578684
case 'd':

src/ir/ExpressionAnalyzer.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,9 @@ bool ExpressionAnalyzer::flexibleEqual(Expression* left,
205205
}
206206

207207
#define DELEGATE_FIELD_INT_ARRAY(id, field) COMPARE_LIST(field)
208+
#define DELEGATE_FIELD_INT_VECTOR(id, field) COMPARE_LIST(field)
208209
#define DELEGATE_FIELD_NAME_VECTOR(id, field) COMPARE_LIST(field)
210+
#define DELEGATE_FIELD_TYPE_VECTOR(id, field) COMPARE_LIST(field)
209211

210212
#define DELEGATE_FIELD_SCOPE_NAME_DEF(id, field) \
211213
if (castLeft->field.is() != castRight->field.is()) { \

src/ir/ExpressionManipulator.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,10 @@ flexibleCopy(Expression* original, Module& wasm, CustomCopier custom) {
9494

9595
#define DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(id, field) COPY_VECTOR(field)
9696
#define DELEGATE_FIELD_NAME_VECTOR(id, field) COPY_VECTOR(field)
97+
#define DELEGATE_FIELD_TYPE_VECTOR(id, field) COPY_VECTOR(field)
9798

9899
#define DELEGATE_FIELD_INT_ARRAY(id, field) COPY_ARRAY(field)
100+
#define DELEGATE_FIELD_INT_VECTOR(id, field) COPY_VECTOR(field)
99101

100102
#include "wasm-delegations-fields.def"
101103

src/ir/LocalStructuralDominance.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,14 @@ LocalStructuralDominance::LocalStructuralDominance(Function* func,
207207
currp = &curr->cast<Try>()->body;
208208
continue;
209209
}
210+
case Expression::Id::TryTableId: {
211+
self->pushTask(Scanner::doEndScope, currp);
212+
// Just call the task immediately.
213+
doBeginScope(self, currp);
214+
// Immediately continue in the try_table.
215+
currp = &curr->cast<TryTable>()->body;
216+
continue;
217+
}
210218

211219
default: {
212220
// Control flow structures have been handled. This is an expression,

src/ir/ReFinalize.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,10 @@ void ReFinalize::visitTableGrow(TableGrow* curr) { curr->finalize(); }
127127
void ReFinalize::visitTableFill(TableFill* curr) { curr->finalize(); }
128128
void ReFinalize::visitTableCopy(TableCopy* curr) { curr->finalize(); }
129129
void ReFinalize::visitTry(Try* curr) { curr->finalize(); }
130+
void ReFinalize::visitTryTable(TryTable* curr) { curr->finalize(); }
130131
void ReFinalize::visitThrow(Throw* curr) { curr->finalize(); }
131132
void ReFinalize::visitRethrow(Rethrow* curr) { curr->finalize(); }
133+
void ReFinalize::visitThrowRef(ThrowRef* curr) { curr->finalize(); }
132134
void ReFinalize::visitNop(Nop* curr) { curr->finalize(); }
133135
void ReFinalize::visitUnreachable(Unreachable* curr) { curr->finalize(); }
134136
void ReFinalize::visitPop(Pop* curr) { curr->finalize(); }

src/ir/branch-utils.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,22 @@ void operateOnScopeNameUsesAndSentTypes(Expression* expr, T func) {
7575
func(name, sw->value ? sw->value->type : Type::none);
7676
} else if (auto* br = expr->dynCast<BrOn>()) {
7777
func(name, br->getSentType());
78+
} else if (auto* tt = expr->dynCast<TryTable>()) {
79+
for (Index i = 0; i < tt->catchTags.size(); i++) {
80+
auto dest = tt->catchDests[i];
81+
if (dest == name) {
82+
func(name, tt->sentTypes[i]);
83+
}
84+
}
7885
} else {
7986
assert(expr->is<Try>() || expr->is<Rethrow>()); // delegate or rethrow
8087
}
8188
});
8289
}
8390

8491
// Similar to operateOnScopeNameUses, but also passes in the expression that is
85-
// sent if the branch is taken. nullptr is given if there is no value.
92+
// sent if the branch is taken. nullptr is given if there is no value or there
93+
// is a value but it is not known statically.
8694
template<typename T>
8795
void operateOnScopeNameUsesAndSentValues(Expression* expr, T func) {
8896
operateOnScopeNameUses(expr, [&](Name& name) {
@@ -94,6 +102,10 @@ void operateOnScopeNameUsesAndSentValues(Expression* expr, T func) {
94102
func(name, sw->value);
95103
} else if (auto* br = expr->dynCast<BrOn>()) {
96104
func(name, br->ref);
105+
} else if (auto* tt = expr->dynCast<TryTable>()) {
106+
// The values are supplied by throwing instructions, so we are unable to
107+
// know what they will be here.
108+
func(name, nullptr);
97109
} else {
98110
assert(expr->is<Try>() || expr->is<Rethrow>()); // delegate or rethrow
99111
}

src/ir/cost.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,10 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
584584
// We assume no exception will be thrown in most cases
585585
return visit(curr->body);
586586
}
587+
CostType visitTryTable(TryTable* curr) {
588+
// We assume no exception will be thrown in most cases
589+
return visit(curr->body);
590+
}
587591
CostType visitThrow(Throw* curr) {
588592
CostType ret = Unacceptable;
589593
for (auto* child : curr->operands) {
@@ -592,6 +596,7 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
592596
return ret;
593597
}
594598
CostType visitRethrow(Rethrow* curr) { return Unacceptable; }
599+
CostType visitThrowRef(ThrowRef* curr) { return Unacceptable; }
595600
CostType visitTupleMake(TupleMake* curr) {
596601
CostType ret = 0;
597602
for (auto* child : curr->operands) {

src/ir/effects.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,11 @@ class EffectAnalyzer {
706706
parent.delegateTargets.insert(curr->delegateTarget);
707707
}
708708
}
709+
void visitTryTable(TryTable* curr) {
710+
for (auto name : curr->catchDests) {
711+
parent.breakTargets.insert(name);
712+
}
713+
}
709714
void visitThrow(Throw* curr) {
710715
if (parent.tryDepth == 0) {
711716
parent.throws_ = true;
@@ -715,6 +720,11 @@ class EffectAnalyzer {
715720
if (parent.tryDepth == 0) {
716721
parent.throws_ = true;
717722
}
723+
}
724+
void visitThrowRef(ThrowRef* curr) {
725+
if (parent.tryDepth == 0) {
726+
parent.throws_ = true;
727+
}
718728
// traps when the arg is null
719729
parent.implicitTrap = true;
720730
}

src/ir/possible-contents.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,6 +1152,10 @@ struct InfoCollector
11521152
#endif
11531153
}
11541154
}
1155+
void visitTryTable(TryTable* curr) {
1156+
// TODO: optimize when possible
1157+
addRoot(curr);
1158+
}
11551159
void visitThrow(Throw* curr) {
11561160
auto& operands = curr->operands;
11571161
if (!isRelevant(operands)) {
@@ -1165,6 +1169,7 @@ struct InfoCollector
11651169
}
11661170
}
11671171
void visitRethrow(Rethrow* curr) {}
1172+
void visitThrowRef(ThrowRef* curr) {}
11681173

11691174
void visitTupleMake(TupleMake* curr) {
11701175
if (isRelevant(curr->type)) {

0 commit comments

Comments
 (0)