Skip to content

Commit 63be8c0

Browse files
authored
Lower branches with unsupported extra values in IRBuilder (#7202)
WebAssembly allows all branch instructions to carry an arbitrary number of values, but Binaryen IR only supports this for br, br_if, and br_table. Previously we failed to parse any other branches that sent additional arguments. To be able to parse more standard modules, support extra values sent by other branches by lowering them away using scratch locals and trampolines in IRBuilder. For now, only lower BrOn expressions with extra values. Lowering for other branches will be done in follow-on commits. Fixes #7182.
1 parent caefb33 commit 63be8c0

File tree

3 files changed

+3381
-62
lines changed

3 files changed

+3381
-62
lines changed

src/wasm-ir-builder.h

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,16 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
342342
Type inputType;
343343
Index inputLocal = -1;
344344

345+
// If there are br_on_*, try_table, or resume branches that target this
346+
// scope and carry additional values, we need to use a scratch local to
347+
// deliver those additional values because the IR does not support them. We
348+
// may need scratch locals of different arities for the same branch target.
349+
// For each arity we also need a trampoline label to branch to. TODO:
350+
// Support additional values on any branch once we have better multivalue
351+
// optimization support.
352+
std::vector<Index> outputLocals;
353+
std::vector<Name> outputLabels;
354+
345355
// The stack of instructions being built in this scope.
346356
std::vector<Expression*> exprStack;
347357

@@ -372,42 +382,43 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
372382
static ScopeCtx makeIf(If* iff, Name originalLabel, Type inputType) {
373383
return ScopeCtx(IfScope{iff, originalLabel}, inputType);
374384
}
375-
static ScopeCtx makeElse(If* iff,
376-
Name originalLabel,
377-
Name label,
378-
bool labelUsed,
379-
Type inputType,
380-
Index inputLocal) {
381-
return ScopeCtx(
382-
ElseScope{iff, originalLabel}, label, labelUsed, inputType, inputLocal);
385+
static ScopeCtx makeElse(ScopeCtx&& scope) {
386+
scope.scope = ElseScope{scope.getIf(), scope.getOriginalLabel()};
387+
scope.resetForDelimiter(/*keepInput=*/true);
388+
return scope;
383389
}
384390
static ScopeCtx makeLoop(Loop* loop, Type inputType) {
385391
return ScopeCtx(LoopScope{loop}, inputType);
386392
}
387393
static ScopeCtx makeTry(Try* tryy, Name originalLabel, Type inputType) {
388394
return ScopeCtx(TryScope{tryy, originalLabel}, inputType);
389395
}
390-
static ScopeCtx makeCatch(Try* tryy,
391-
Name originalLabel,
392-
Name label,
393-
bool labelUsed,
394-
Name branchLabel) {
395-
return ScopeCtx(
396-
CatchScope{tryy, originalLabel}, label, labelUsed, branchLabel);
397-
}
398-
static ScopeCtx makeCatchAll(Try* tryy,
399-
Name originalLabel,
400-
Name label,
401-
bool labelUsed,
402-
Name branchLabel) {
403-
return ScopeCtx(
404-
CatchAllScope{tryy, originalLabel}, label, labelUsed, branchLabel);
396+
static ScopeCtx makeCatch(ScopeCtx&& scope, Try* tryy) {
397+
scope.scope = CatchScope{tryy, scope.getOriginalLabel()};
398+
scope.resetForDelimiter(/*keepInput=*/false);
399+
return scope;
400+
}
401+
static ScopeCtx makeCatchAll(ScopeCtx&& scope, Try* tryy) {
402+
scope.scope = CatchAllScope{tryy, scope.getOriginalLabel()};
403+
scope.resetForDelimiter(/*keepInput=*/false);
404+
return scope;
405405
}
406406
static ScopeCtx
407407
makeTryTable(TryTable* trytable, Name originalLabel, Type inputType) {
408408
return ScopeCtx(TryTableScope{trytable, originalLabel}, inputType);
409409
}
410-
410+
// When transitioning to a new scope for a delimiter like `else` or catch,
411+
// most of the scope context is preserved, but some parts need to be reset.
412+
// `keepInput` means that control flow parameters are available at the
413+
// begninning of the scope after the delimiter.
414+
void resetForDelimiter(bool keepInput) {
415+
exprStack.clear();
416+
unreachable = false;
417+
if (!keepInput) {
418+
inputType = Type::none;
419+
inputLocal = -1;
420+
}
421+
}
411422
bool isNone() { return std::get_if<NoScope>(&scope); }
412423
Function* getFunction() {
413424
if (auto* funcScope = std::get_if<FuncScope>(&scope)) {
@@ -514,6 +525,10 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
514525
}
515526
WASM_UNREACHABLE("unexpected scope kind");
516527
}
528+
Type getLabelType() {
529+
// Loops receive their input type rather than their output type.
530+
return getLoop() ? inputType : getResultType();
531+
}
517532
Name getOriginalLabel() {
518533
if (std::get_if<NoScope>(&scope) || getFunction()) {
519534
return Name{};
@@ -651,6 +666,9 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
651666
Result<Type> getLabelType(Index label);
652667
Result<Type> getLabelType(Name labelName);
653668

669+
Result<std::pair<Index, Name>> getExtraOutputLocalAndLabel(Index label,
670+
size_t extraArity);
671+
Expression* fixExtraOutput(ScopeCtx& scope, Name label, Expression* expr);
654672
void fixLoopWithInput(Loop* loop, Type inputType, Index scratch);
655673

656674
void dump();

0 commit comments

Comments
 (0)