Skip to content

[Outlining] Add Try/Catch/CatchAll #7472

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Apr 29, 2025
Merged
14 changes: 14 additions & 0 deletions src/passes/Outlining.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,20 @@ struct ReconstructStringifyWalker
} else if (auto curr = reason.getLoopStart()) {
ODBG(desc = "Loop Start at ");
ASSERT_OK(existingBuilder.visitLoopStart(curr->loop));
} else if (auto curr = reason.getTryStart()) {
// We preserve the name of the tryy because IRBuilder expects
// visitTryStart() to be called on an empty Try, during the normal case of
// parsing. TODO: Fix this.
auto name = curr->tryy->name;
ASSERT_OK(existingBuilder.visitTryStart(curr->tryy, Name()));
ODBG(desc = "Try Start at ");
curr->tryy->name = name;
} else if (auto curr = reason.getCatchStart()) {
ASSERT_OK(existingBuilder.visitCatch(curr->tag));
ODBG(desc = "Catch Start at ");
} else if (reason.getCatchAllStart()) {
ASSERT_OK(existingBuilder.visitCatchAll());
ODBG(desc = "Catch All Start at");
} else if (reason.getEnd()) {
ODBG(desc = "End at ");
ASSERT_OK(existingBuilder.visitEnd());
Expand Down
16 changes: 10 additions & 6 deletions src/passes/stringify-walker-impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,18 @@ template<typename SubType> void StringifyWalker<SubType>::dequeueControlFlow() {
}
case Expression::Id::TryId: {
auto* tryy = curr->cast<Try>();
addUniqueSymbol(SeparatorReason::makeTryBodyStart());

addUniqueSymbol(SeparatorReason::makeTryStart(tryy));
Super::walk(tryy->body);
addUniqueSymbol(SeparatorReason::makeEnd());
for (auto& child : tryy->catchBodies) {
addUniqueSymbol(SeparatorReason::makeTryCatchStart());
Super::walk(child);
addUniqueSymbol(SeparatorReason::makeEnd());
for (size_t i = 0; i < tryy->catchBodies.size(); i++) {
if (tryy->hasCatchAll() && i == tryy->catchBodies.size() - 1) {
addUniqueSymbol(SeparatorReason::makeCatchAllStart());
} else {
addUniqueSymbol(SeparatorReason::makeCatchStart(tryy->catchTags[i]));
}
Super::walk(tryy->catchBodies[i]);
}
addUniqueSymbol(SeparatorReason::makeEnd());
break;
}
case Expression::Id::LoopId: {
Expand Down
45 changes: 28 additions & 17 deletions src/passes/stringify-walker.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,15 @@ struct StringifyWalker
Loop* loop;
};

struct TryBodyStart {};
struct TryStart {
Try* tryy;
};

struct CatchStart {
Name tag;
};

struct TryCatchStart {};
struct CatchAllStart {};

struct End {
Expression* curr;
Expand All @@ -101,8 +107,9 @@ struct StringifyWalker
IfStart,
ElseStart,
LoopStart,
TryBodyStart,
TryCatchStart,
TryStart,
CatchStart,
CatchAllStart,
End>;

Separator reason;
Expand All @@ -124,23 +131,25 @@ struct StringifyWalker
static SeparatorReason makeLoopStart(Loop* loop) {
return SeparatorReason(LoopStart{loop});
}
static SeparatorReason makeTryCatchStart() {
return SeparatorReason(TryCatchStart{});
static SeparatorReason makeTryStart(Try* tryy) {
return SeparatorReason(TryStart{tryy});
}
static SeparatorReason makeTryBodyStart() {
return SeparatorReason(TryBodyStart{});
static SeparatorReason makeCatchStart(Name tag) {
return SeparatorReason(CatchStart{tag});
}
static SeparatorReason makeCatchAllStart() {
return SeparatorReason(CatchAllStart{});
}
static SeparatorReason makeEnd() { return SeparatorReason(End{}); }
FuncStart* getFuncStart() { return std::get_if<FuncStart>(&reason); }
BlockStart* getBlockStart() { return std::get_if<BlockStart>(&reason); }
IfStart* getIfStart() { return std::get_if<IfStart>(&reason); }
ElseStart* getElseStart() { return std::get_if<ElseStart>(&reason); }
LoopStart* getLoopStart() { return std::get_if<LoopStart>(&reason); }
TryBodyStart* getTryBodyStart() {
return std::get_if<TryBodyStart>(&reason);
}
TryCatchStart* getTryCatchStart() {
return std::get_if<TryCatchStart>(&reason);
TryStart* getTryStart() { return std::get_if<TryStart>(&reason); }
CatchStart* getCatchStart() { return std::get_if<CatchStart>(&reason); }
CatchAllStart* getCatchAllStart() {
return std::get_if<CatchAllStart>(&reason);
}
End* getEnd() { return std::get_if<End>(&reason); }
};
Expand All @@ -158,10 +167,12 @@ struct StringifyWalker
return o << "Else Start";
} else if (reason.getLoopStart()) {
return o << "Loop Start";
} else if (reason.getTryBodyStart()) {
return o << "Try Body Start";
} else if (reason.getTryCatchStart()) {
return o << "Try Catch Start";
} else if (reason.getTryStart()) {
return o << "Try Start";
} else if (reason.getCatchStart()) {
return o << "Catch Start";
} else if (reason.getCatchAllStart()) {
return o << "Catch All Start";
} else if (reason.getEnd()) {
return o << "End";
}
Expand Down
23 changes: 20 additions & 3 deletions src/wasm-ir-builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -306,14 +306,17 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
struct TryScope {
Try* tryy;
Name originalLabel;
Index index;
};
struct CatchScope {
Try* tryy;
Name originalLabel;
Index index;
};
struct CatchAllScope {
Try* tryy;
Name originalLabel;
Index index;
};
struct TryTableScope {
TryTable* trytable;
Expand Down Expand Up @@ -396,15 +399,17 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
return ScopeCtx(LoopScope{loop}, inputType);
}
static ScopeCtx makeTry(Try* tryy, Name originalLabel, Type inputType) {
return ScopeCtx(TryScope{tryy, originalLabel}, inputType);
return ScopeCtx(TryScope{tryy, originalLabel, 0}, inputType);
}
static ScopeCtx makeCatch(ScopeCtx&& scope, Try* tryy) {
scope.scope = CatchScope{tryy, scope.getOriginalLabel()};
scope.scope =
CatchScope{tryy, scope.getOriginalLabel(), scope.getIndex() + 1};
scope.resetForDelimiter(/*keepInput=*/false);
return scope;
}
static ScopeCtx makeCatchAll(ScopeCtx&& scope, Try* tryy) {
scope.scope = CatchAllScope{tryy, scope.getOriginalLabel()};
scope.scope =
CatchAllScope{tryy, scope.getOriginalLabel(), scope.getIndex() + 1};
scope.resetForDelimiter(/*keepInput=*/false);
return scope;
}
Expand Down Expand Up @@ -530,6 +535,18 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
}
WASM_UNREACHABLE("unexpected scope kind");
}
Index getIndex() {
if (auto* tryScope = std::get_if<TryScope>(&scope)) {
return tryScope->index;
}
if (auto* catchScope = std::get_if<CatchScope>(&scope)) {
return catchScope->index;
}
if (auto* catchAllScope = std::get_if<CatchAllScope>(&scope)) {
return catchAllScope->index;
}
WASM_UNREACHABLE("unexpected scope kind");
}
Type getLabelType() {
// Loops receive their input type rather than their output type.
return getLoop() ? inputType : getResultType();
Expand Down
29 changes: 25 additions & 4 deletions src/wasm/wasm-ir-builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,21 @@ Result<> IRBuilder::visitElse() {
return pushScope(ScopeCtx::makeElse(std::move(scope)));
}

void setCatchBody(Try* tryy, Expression* expr, Index index) {
// Indexes are managed manually to support Outlining.
// Its prepopulated try catchBodies and catchTags vectors
// cannot be appended to, as in the case of the empty try
// used during parsing.
if (tryy->catchBodies.size() < index) {
tryy->catchBodies.resize(tryy->catchBodies.size() + 1);
}
// The first time visitCatch is called: the body of the
// try is set and catchBodies is not appended to, but the tag
// for the following catch is appended. So, catchTags uses
// index as-is, but catchBodies uses index-1.
tryy->catchBodies[index - 1] = expr;
}

Result<> IRBuilder::visitCatch(Name tag) {
auto scope = getScope();
bool wasTry = true;
Expand All @@ -942,14 +957,18 @@ Result<> IRBuilder::visitCatch(Name tag) {
if (!tryy) {
return Err{"unexpected catch"};
}
auto index = scope.getIndex();
auto expr = finishScope();
CHECK_ERR(expr);
if (wasTry) {
tryy->body = *expr;
} else {
tryy->catchBodies.push_back(*expr);
setCatchBody(tryy, *expr, index);
}
if (tryy->catchTags.size() == index) {
tryy->catchTags.resize(tryy->catchTags.size() + 1);
}
tryy->catchTags.push_back(tag);
tryy->catchTags[index] = tag;

if (binaryPos && func) {
auto& delimiterLocs = func->delimiterLocations[tryy];
Expand Down Expand Up @@ -980,12 +999,13 @@ Result<> IRBuilder::visitCatchAll() {
if (!tryy) {
return Err{"unexpected catch"};
}
auto index = scope.getIndex();
auto expr = finishScope();
CHECK_ERR(expr);
if (wasTry) {
tryy->body = *expr;
} else {
tryy->catchBodies.push_back(*expr);
setCatchBody(tryy, *expr, index);
}

if (binaryPos && func) {
Expand Down Expand Up @@ -1123,7 +1143,8 @@ Result<> IRBuilder::visitEnd() {
push(maybeWrapForLabel(tryy));
} else if (Try * tryy;
(tryy = scope.getCatch()) || (tryy = scope.getCatchAll())) {
tryy->catchBodies.push_back(*expr);
auto index = scope.getIndex();
setCatchBody(tryy, *expr, index);
tryy->name = scope.label;
tryy->finalize(tryy->type);
push(maybeWrapForLabel(tryy));
Expand Down
13 changes: 5 additions & 8 deletions test/gtest/stringify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,19 +98,16 @@ adding unique symbol for End
adding unique symbol for If Start
in visitExpression for i32.const 30
adding unique symbol for End
adding unique symbol for Try Body Start
adding unique symbol for Try Start
in visitExpression for nop
adding unique symbol for End
adding unique symbol for Try Catch Start
adding unique symbol for Catch Start
in visitExpression for block
adding unique symbol for End
adding unique symbol for Try Catch Start
adding unique symbol for Catch Start
in visitExpression for block
adding unique symbol for End
adding unique symbol for Try Body Start
adding unique symbol for Try Start
in visitExpression for nop
adding unique symbol for End
adding unique symbol for Try Catch Start
adding unique symbol for Catch Start
in visitExpression for block
adding unique symbol for End
adding unique symbol for Block Start
Expand Down
80 changes: 80 additions & 0 deletions test/lit/passes/outlining.wast
Original file line number Diff line number Diff line change
Expand Up @@ -1164,3 +1164,83 @@
)
)
)

;; Tests that the contents of Catch are outlined
(module
;; CHECK: (type $1 (func (result i32)))

;; CHECK: (type $0 (func))
(type $0 (func))
(type $1 (func (result i32)))
;; CHECK: (tag $eimport$1 (type $0))
(tag $eimport$1 (type $0))
;; CHECK: (func $outline$ (type $1) (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const -12)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const -2147483647)
;; CHECK-NEXT: )

;; CHECK: (func $a (type $1) (result i32)
;; CHECK-NEXT: (local $0 externref)
;; CHECK-NEXT: (try (result i32)
;; CHECK-NEXT: (do
;; CHECK-NEXT: (i32.const -20)
;; CHECK-NEXT: )
;; CHECK-NEXT: (catch $eimport$1
;; CHECK-NEXT: (call $outline$)
;; CHECK-NEXT: )
;; CHECK-NEXT: (catch_all
;; CHECK-NEXT: (i32.const -15)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $a (result i32)
(local $0 externref)
(try (result i32)
(do
(i32.const -20)
)
(catch $eimport$1
(drop
(i32.const -12)
)
(i32.const -2147483647)
)
(catch_all
(i32.const -15)
)
)
)
;; CHECK: (func $b (type $1) (result i32)
;; CHECK-NEXT: (local $0 externref)
;; CHECK-NEXT: (try (result i32)
;; CHECK-NEXT: (do
;; CHECK-NEXT: (i32.const -20)
;; CHECK-NEXT: )
;; CHECK-NEXT: (catch $eimport$1
;; CHECK-NEXT: (call $outline$)
;; CHECK-NEXT: )
;; CHECK-NEXT: (catch_all
;; CHECK-NEXT: (i32.const -15)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $b (result i32)
(local $0 externref)
(try (result i32)
(do
(i32.const -20)
)
(catch $eimport$1
(drop
(i32.const -12)
)
(i32.const -2147483647)
)
(catch_all
(i32.const -15)
)
)
)
)
Loading