Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions tslang/include/TypeScript/MLIRLogic/MLIRGenContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,15 @@ struct GenContext
// Set by a loop on the context it hands its body, and inherited by every context copied
// from it - so it answers "somewhere inside a loop", not "is the loop".
bool isLoop = false;
// the label of the loop that set `isLoop`, handed to its body (see loopBodyLabel)
std::string loopLabel;
// With isLoopBodyScope: the label of the loop this is the body of. Kept apart from `loopLabel`
// because a loop's context is a copy of the scope around it and stands in for that scope in the
// walk outwards - it overwrites `loopLabel` for its own body, and keeps this for the scope it copies.
std::string loopBodyLabel;
// Set on the scope of a labelled statement that is not a loop (`a: { ... }`): where a
// `break a` or `continue a` walking outwards stops.
std::string labelledScopeLabel;
// Set by the block that becomes the loop's own body scope, and cleared for anything nested
// further in. That is the distinction a `break` or `continue` needs: it owes a dispose and
// a release to every scope between itself and the loop, so the walk outwards can only stop
Expand Down
17 changes: 10 additions & 7 deletions tslang/lib/TypeScript/LowerToAffineLoops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -471,8 +471,8 @@ struct ResultOpLowering : public TsPattern<mlir_ts::ResultOp>
};

// Populates tsContext->jumps for break/continue ops that belong to this loop/label scope.
// An unlabeled break/continue binds to the *nearest* enclosing loop/label (continue) or
// loop/label/switch (break), so once the walk descends past such a boundary it must stop
// An unlabeled break/continue binds to the *nearest* enclosing loop (continue) or
// loop/switch (break), so once the walk descends past such a boundary it must stop
// treating unlabeled break/continue as claimable by this (outer) scope -- otherwise it can
// steal a jump target that belongs to the nested scope, depending on which scope's pattern
// happens to run last in the dialect-conversion worklist. A nested Switch is a boundary for
Expand Down Expand Up @@ -514,14 +514,15 @@ static void visitBreakContinueInScope(mlir::Region &region, mlir::StringAttr sco
continue;
}

auto isLoopOrLabelBoundary = isa<mlir_ts::WhileOp, mlir_ts::DoWhileOp, mlir_ts::ForOp, mlir_ts::LabelOp>(op);
// not a LabelOp: an unlabelled break or continue never targets a labelled block
auto isLoopBoundary = isa<mlir_ts::WhileOp, mlir_ts::DoWhileOp, mlir_ts::ForOp>(op);
auto isSwitchBoundary = isa<mlir_ts::SwitchOp>(op);

for (auto &nested : op.getRegions())
{
visitBreakContinueInScope(nested, scopeLabel,
eligibleForUnlabeledBreak && !isLoopOrLabelBoundary && !isSwitchBoundary,
eligibleForUnlabeledContinue && !isLoopOrLabelBoundary, onMatch);
eligibleForUnlabeledBreak && !isLoopBoundary && !isSwitchBoundary,
eligibleForUnlabeledContinue && !isLoopBoundary, onMatch);
}
}
}
Expand Down Expand Up @@ -737,8 +738,10 @@ struct LabelOpLowering : public TsPattern<mlir_ts::LabelOp>

// logic to support continue/break

visitBreakContinueInScope(labelOp.getLabelRegion(), labelAttr, /*eligibleForUnlabeledBreak*/ true,
/*eligibleForUnlabeledContinue*/ true, [&](Operation *op, bool isBreak) {
// only its own label: in JS an unlabelled break or continue in a labelled block belongs to the
// loop or switch around it
visitBreakContinueInScope(labelOp.getLabelRegion(), labelAttr, /*eligibleForUnlabeledBreak*/ false,
/*eligibleForUnlabeledContinue*/ false, [&](Operation *op, bool isBreak) {
tsContext->jumps[op] = isBreak ? continuation : begin;
});

Expand Down
34 changes: 20 additions & 14 deletions tslang/lib/TypeScript/MLIRGenImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -748,18 +748,24 @@ class MLIRGenImpl
return false;
}

// The label comparison is left exactly as it was, including the empty label the
// recursion below hands the parent. It looks like it should carry the target label
// outwards instead, but the loop sites clear `label` before storing it, so a labelled
// loop's context holds an empty one too - and `continue cont1` then relies on the outer
// loop matching that empty label. 02disposable.ts is the case that proves it.
// (The `for` site now takes the label before its initializer and stores "" outright.)
// Known gap: a labelled `continue` written inside an `if` in an inner loop stops at the
// inner loop's body, so the outer body's `using` is not disposed. Carrying real labels
// needs the loop context, which stands in for the enclosing body scope in this walk, to
// keep that scope's label apart from the label of the loop it starts.
auto isTargetLoop = genContext->isLoopBodyScope && genContext->loopLabel == loopLabel;
return !isTargetLoop;
// The target label is carried outwards unchanged. An unlabelled jump stops at the nearest
// loop body. A labelled one stops at the body of the loop of that label, or at the scope of
// the labelled statement (`a: { ... break a; }`), and owes every scope it passes on the way,
// the bodies of inner loops included.
//
// This used to hand the parent an empty label, and every loop's context held an empty one
// too, so a labelled jump stopped at the first loop body after the one it started in: a
// `continue outer` written in an `if` in an inner loop skipped the outer body's `using`.
// A jump to a labelled block matched nothing and ran on to the function's scope,
// disposing its `using` and releasing its locals there and then.
if (loopLabel.empty())
{
return !genContext->isLoopBodyScope;
}

auto isTarget = (genContext->isLoopBodyScope && genContext->loopBodyLabel == loopLabel) ||
genContext->labelledScopeLabel == loopLabel;
return !isTarget;
}

// Drops the reference each local of this scope took when it was declared. Shaped after
Expand Down Expand Up @@ -814,7 +820,7 @@ class MLIRGenImpl
// OwnedReturnConsumptionPass, not this.
if (scopeExitContinuesOutwards(disposeDepth, loopLabel, genContext))
{
EXIT_IF_FAILED(mlirGenReleaseOwned(location, disposeDepth, {}, genContext->parentBlockContext));
EXIT_IF_FAILED(mlirGenReleaseOwned(location, disposeDepth, loopLabel, genContext->parentBlockContext));
}

return mlir::success();
Expand Down Expand Up @@ -1225,7 +1231,7 @@ class MLIRGenImpl
// something a program was found to need.
if (scopeExitContinuesOutwards(disposeDepth, loopLabel, genContext))
{
EXIT_IF_FAILED(mlirGenDisposable(location, disposeDepth, {}, genContext->parentBlockContext));
EXIT_IF_FAILED(mlirGenDisposable(location, disposeDepth, loopLabel, genContext->parentBlockContext));
}

return mlir::success();
Expand Down
65 changes: 50 additions & 15 deletions tslang/lib/TypeScript/MLIRGenStatements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@ namespace mlirgen
// the context it handed down. Take it, and clear the offer so a block nested further in
// does not claim to be the loop as well - see GenContext::isLoopBodyScope.
genContextUsing.isLoopBodyScope = genContext.isLoop;
genContextUsing.loopBodyLabel = genContext.loopLabel;
// a scope of its own, not the labelled statement's: see labelledContext in mlirGen(LabeledStatement)
genContextUsing.labelledScopeLabel.clear();
genContextUsing.isLoop = false;

DITableScopeT debugBlockScope(debugScope);
Expand Down Expand Up @@ -256,6 +259,8 @@ namespace mlirgen
// as in mlirGen(Block): this is the block's own scope, so it is where a break or
// continue walking outwards stops
tryBodyGenContext.isLoopBodyScope = tryGenContext.isLoop;
tryBodyGenContext.loopBodyLabel = tryGenContext.loopLabel;
tryBodyGenContext.labelledScopeLabel.clear();
tryBodyGenContext.isLoop = false;

auto usingVars = std::make_unique<SmallVector<ts::VariableDeclarationDOM::TypePtr>>();
Expand Down Expand Up @@ -662,6 +667,10 @@ namespace mlirgen
// generator's iterator, a `using` - belongs to the branch, whose region it is defined in, not
// to the block around the `if`, whose end is outside that region (#458). A braced branch is a
// Block and makes this scope itself.
//
// A loop's body written without braces (`for (...) if (c) continue;`) is made a scope here too,
// and, as in mlirGen(Block), the loop's body scope: without one a `break` or `continue` found no
// body to stop at and disposed and released the scopes around the loop.
mlir::LogicalResult MLIRGenImpl::mlirGenBranchStatement(Statement statement, const GenContext &genContext)
{
if (statement == SyntaxKind::Block)
Expand All @@ -673,7 +682,10 @@ namespace mlirgen

GenContext branchContext(genContext);
branchContext.parentBlockContext = &genContext;
branchContext.isLoopBodyScope = false;
branchContext.isLoopBodyScope = genContext.isLoop;
branchContext.loopBodyLabel = genContext.loopLabel;
branchContext.isLoop = false;
branchContext.labelledScopeLabel.clear();

auto usingVars = std::make_unique<SmallVector<ts::VariableDeclarationDOM::TypePtr>>();
branchContext.usingVars = usingVars.get();
Expand All @@ -696,22 +708,23 @@ namespace mlirgen
SmallVector<mlir::Value, 0> operands;

auto doWhileOp = builder.create<mlir_ts::DoWhileOp>(location, types, operands);
if (!label.empty())
auto loopLabel = label;
label = "";
if (!loopLabel.empty())
{
doWhileOp->setAttr(LABEL_ATTR_NAME, builder.getStringAttr(label));
label = "";
doWhileOp->setAttr(LABEL_ATTR_NAME, builder.getStringAttr(loopLabel));
}

GenContext loopGenContext(genContext);
loopGenContext.isLoop = true;
loopGenContext.loopLabel = label;
loopGenContext.loopLabel = loopLabel;

/*auto *cond =*/builder.createBlock(&doWhileOp.getCond(), {}, types);
/*auto *body =*/builder.createBlock(&doWhileOp.getBody(), {}, types);

// body in condition
builder.setInsertionPointToStart(&doWhileOp.getBody().front());
auto result2 = mlirGen(doStatementAST->statement, loopGenContext);
auto result2 = mlirGenBranchStatement(doStatementAST->statement, loopGenContext);
EXIT_IF_FAILED(result2)
// just simple return, as body in cond
builder.create<mlir_ts::ResultOp>(location);
Expand Down Expand Up @@ -742,15 +755,16 @@ namespace mlirgen
SmallVector<mlir::Value, 0> operands;

auto whileOp = builder.create<mlir_ts::WhileOp>(location, types, operands);
if (!label.empty())
auto loopLabel = label;
label = "";
if (!loopLabel.empty())
{
whileOp->setAttr(LABEL_ATTR_NAME, builder.getStringAttr(label));
label = "";
whileOp->setAttr(LABEL_ATTR_NAME, builder.getStringAttr(loopLabel));
}

GenContext loopGenContext(genContext);
loopGenContext.isLoop = true;
loopGenContext.loopLabel = label;
loopGenContext.loopLabel = loopLabel;

/*auto *cond =*/builder.createBlock(&whileOp.getCond(), {}, types);
/*auto *body =*/builder.createBlock(&whileOp.getBody(), {}, types);
Expand Down Expand Up @@ -782,7 +796,7 @@ namespace mlirgen
SafeTypesMapScopeT safeTypesMapScope(safeTypesMap);
checkSafeCast(whileStatementAST->expression, conditionValue, nullptr, loopGenContext);

auto result2 = mlirGen(whileStatementAST->statement, loopGenContext);
auto result2 = mlirGenBranchStatement(whileStatementAST->statement, loopGenContext);
EXIT_IF_FAILED(result2)
}

Expand Down Expand Up @@ -835,8 +849,7 @@ namespace mlirgen

GenContext loopGenContext(genContext);
loopGenContext.isLoop = true;
// empty, as at the other loop sites: see scopeExitContinuesOutwards
loopGenContext.loopLabel = "";
loopGenContext.loopLabel = loopLabel;

/*auto *cond =*/builder.createBlock(&forOp.getCond(), {}, types);
/*auto *body =*/builder.createBlock(&forOp.getBody(), {}, types);
Expand Down Expand Up @@ -889,7 +902,7 @@ namespace mlirgen
checkSafeCast(forStatementAST->condition, testedValue, nullptr, loopGenContext);
}

auto bodyResult = mlirGen(forStatementAST->statement, loopGenContext);
auto bodyResult = mlirGenBranchStatement(forStatementAST->statement, loopGenContext);
EXIT_IF_FAILED(bodyResult)
}

Expand Down Expand Up @@ -1049,7 +1062,27 @@ namespace mlirgen

builder.setInsertionPointToStart(mergeBlock);

auto res = mlirGen(labeledStatementAST->statement, genContext);
// The labelled statement's own scope, made as mlirGenBranchStatement makes one: where a
// `break label` or `continue label` walking outwards stops (scopeExitContinuesOutwards).
// `isLoopBodyScope` is cleared because this context is a copy of the scope around it - one that
// may stand in for a loop's body, and an unlabelled jump would stop here, short of the body's
// own lists.
GenContext labelledContext(genContext);
labelledContext.parentBlockContext = &genContext;
labelledContext.labelledScopeLabel = label;
labelledContext.isLoopBodyScope = false;

auto usingVars = std::make_unique<SmallVector<ts::VariableDeclarationDOM::TypePtr>>();
labelledContext.usingVars = usingVars.get();

auto ownedVars = std::make_unique<SmallVector<mlir::Value>>();
labelledContext.ownedVars = ownedVars.get();

auto res = mlirGen(labeledStatementAST->statement, labelledContext);
if (mlir::succeeded(res))
{
res = mlirGenScopeExit(location, DisposeDepth::CurrentScope, {}, &labelledContext);
}

builder.setInsertionPointAfter(labelOp);

Expand Down Expand Up @@ -1337,6 +1370,8 @@ namespace mlirgen
// as in mlirGen(Block): this is the block's own scope, so it is where a break or
// continue walking outwards stops
tryBodyGenContext.isLoopBodyScope = tryGenContext.isLoop;
tryBodyGenContext.loopBodyLabel = tryGenContext.loopLabel;
tryBodyGenContext.labelledScopeLabel.clear();
tryBodyGenContext.isLoop = false;

auto usingVars = std::make_unique<SmallVector<ts::VariableDeclarationDOM::TypePtr>>();
Expand Down
3 changes: 3 additions & 0 deletions tslang/test/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ tslang_add_test(NAME test-compile-00-for COMMAND test-runner "${PROJECT_SOURCE_D
tslang_add_test(NAME test-compile-00-for-infinite-result COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00for_infinite_result.ts")
tslang_add_test(NAME test-compile-00-break-continue COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00break_continue.ts")
tslang_add_test(NAME test-compile-00-label-chain-loop COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00label_chain_loop.ts")
tslang_add_test(NAME test-compile-00-labelled-scope-exit COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00labelled_scope_exit.ts")
tslang_add_test(NAME test-compile-00-vars COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00vars.ts")
tslang_add_test(NAME test-compile-00-var-bindings COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00var_bindings.ts")
tslang_add_test(NAME test-compile-00-globals COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00globals.ts")
Expand Down Expand Up @@ -872,6 +873,7 @@ tslang_add_test(NAME test-jit-00-for COMMAND test-runner -jit "${PROJECT_SOURCE_
tslang_add_test(NAME test-jit-00-for-infinite-result COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00for_infinite_result.ts")
tslang_add_test(NAME test-jit-00-break-continue COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00break_continue.ts")
tslang_add_test(NAME test-jit-00-label-chain-loop COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00label_chain_loop.ts")
tslang_add_test(NAME test-jit-00-labelled-scope-exit COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00labelled_scope_exit.ts")
tslang_add_test(NAME test-jit-00-vars COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00vars.ts")
tslang_add_test(NAME test-jit-00-var-bindings COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00var_bindings.ts")
tslang_add_test(NAME test-jit-00-globals COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00globals.ts")
Expand Down Expand Up @@ -1822,6 +1824,7 @@ set(TSLANG_CORPUS
00break_continue_scope_exit.ts
00break_continue.ts
00label_chain_loop.ts
00labelled_scope_exit.ts
00capture_in_new_arguments.ts
00cast_to_f32.ts
00catch_untyped_value.ts
Expand Down
Loading
Loading