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
5 changes: 4 additions & 1 deletion tslang/include/TypeScript/TypeScriptOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -2215,8 +2215,11 @@ def TypeScript_AddressOfOp : TypeScript_Op<"AddressOf"> {
}];
}

// The initializer is one block as MLIRGen builds it, ending in ts.GlobalResult; lowering a branch
// in it (a conditional, an optional widened into a union) adds blocks, and GlobalOpLowering then
// makes the global a constructor (#462).
def TypeScript_GlobalOp : TypeScript_Op<"Global",
[IsolatedFromAbove, SingleBlockImplicitTerminator<"GlobalResultOp">, Symbol]> {
[IsolatedFromAbove, Symbol]> {
let arguments = (ins
TypeAttr:$type,
UnitAttr:$constant,
Expand Down
7 changes: 7 additions & 0 deletions tslang/lib/TypeScript/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4454,6 +4454,13 @@ struct GlobalOpLowering : public TsLlvmPattern<mlir_ts::GlobalOp>

globalOp.getInitializerRegion().walk(visitorAllOps);

// a branch in the initializer (a conditional, an optional widened into a union) has left
// it more than one block, which only a function can hold (#462)
if (!globalOp.getInitializerRegion().empty() && !globalOp.getInitializerRegion().hasOneBlock())
{
createAsGlobalConstructor = true;
}

auto linkage = globalOp.getLinkage();
LLVM::GlobalOp llvmGlobalOp;
if (createAsGlobalConstructor)
Expand Down
71 changes: 71 additions & 0 deletions tslang/lib/TypeScript/OwnedReturnConsumptionPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,77 @@ class OwnedReturnConsumptionPass
return;
}

// An optional widened into a union (`let g: C | null | undefined = mkU()`, #462) is a
// ts.If over the call made before it: each branch yields that call's value through
// views, or a value that holds nothing (the empty optional). The global takes the call's
// count, as it would take the call's straight.
if (auto ifOp = value.getDefiningOp<mlir_ts::IfOp>())
{
auto throughViews = [](mlir::Value yielded) {
while (auto *definingOp = yielded.getDefiningOp())
{
if (auto castOp = mlir::dyn_cast<mlir_ts::CastOp>(definingOp))
{
yielded = castOp.getIn();
}
else if (auto optionalValueOp = mlir::dyn_cast<mlir_ts::OptionalValueOp>(definingOp))
{
yielded = optionalValueOp.getIn();
}
else if (auto createUnionInstanceOp = mlir::dyn_cast<mlir_ts::CreateUnionInstanceOp>(definingOp))
{
yielded = createUnionInstanceOp.getIn();
}
else if (auto valueOp = mlir::dyn_cast<mlir_ts::ValueOp>(definingOp))
{
yielded = valueOp.getIn();
}
else
{
break;
}
}

return yielded;
};

mlir::Value producer;
auto agrees = true;
for (auto &region : ifOp->getRegions())
{
auto resultOp = region.empty() ? mlir_ts::ResultOp()
: mlir::dyn_cast<mlir_ts::ResultOp>(region.back().getTerminator());
if (!resultOp || resultOp->getNumOperands() != 1)
{
agrees = false;
break;
}

auto yielded = throughViews(resultOp->getOperand(0));
auto *yieldedOp = yielded.getDefiningOp();
if (yieldedOp && mlir::isa<mlir_ts::OptionalUndefOp, mlir_ts::NullOp, mlir_ts::UndefOp>(yieldedOp))
{
continue;
}

if (producer && producer != yielded)
{
agrees = false;
break;
}

producer = yielded;
}

if (agrees && producer && producesOwnedResult(producer) &&
!ifOp->isAncestor(producer.getDefiningOp()))
{
producer.getDefiningOp()->setAttr(OWNED_RESULT_CONSUMED_ATTR_NAME,
mlir::UnitAttr::get(&getContext()));
return;
}
}

// An object literal (`let o = { s: mk() }`) is built in a temporary slot and read out
// whole: what its fields were given goes into the global with it (#461), nested
// literals included.
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 @@ -430,6 +430,7 @@ tslang_add_test(NAME test-compile-00-throw-f32 COMMAND test-runner "${PROJECT_SO
tslang_add_test(NAME test-compile-00-throw-u32 COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00throw_u32.ts")
tslang_add_test(NAME test-compile-00-cast-to-f32 COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00cast_to_f32.ts")
tslang_add_test(NAME test-compile-00-instanceof-namespace-narrowing COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00instanceof_namespace_narrowing.ts")
tslang_add_test(NAME test-compile-00-global-initializer-branches COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00global_initializer_branches.ts")
tslang_add_test(NAME test-compile-00-using-nested-scopes COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00using_nested_scopes.ts")
tslang_add_test(NAME test-compile-00-using-unwind-progress COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00using_unwind_progress.ts")
tslang_add_test(NAME test-compile-00-break-continue-scope-exit COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00break_continue_scope_exit.ts")
Expand Down Expand Up @@ -917,6 +918,7 @@ tslang_add_test(NAME test-jit-00-throw-f32 COMMAND test-runner -jit "${PROJECT_S
tslang_add_test(NAME test-jit-00-throw-u32 COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00throw_u32.ts")
tslang_add_test(NAME test-jit-00-cast-to-f32 COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00cast_to_f32.ts")
tslang_add_test(NAME test-jit-00-instanceof-namespace-narrowing COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00instanceof_namespace_narrowing.ts")
tslang_add_test(NAME test-jit-00-global-initializer-branches COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00global_initializer_branches.ts")
tslang_add_test(NAME test-jit-00-using-nested-scopes COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00using_nested_scopes.ts")
tslang_add_test(NAME test-jit-00-using-unwind-progress COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00using_unwind_progress.ts")
tslang_add_test(NAME test-jit-00-break-continue-scope-exit COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00break_continue_scope_exit.ts")
Expand Down Expand Up @@ -1779,6 +1781,7 @@ set(TSLANG_CORPUS
00instanceof.ts
00const_of_let.ts
00instanceof_namespace_narrowing.ts
00global_initializer_branches.ts
00instanceof_union.ts
00nullable_to_any.ts
00ternary_new_or_null.ts
Expand Down
42 changes: 42 additions & 0 deletions tslang/test/tester/tests/00global_initializer_branches.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// A module-level variable whose initializer branches: an optional widened into a union with null
// (#462), and a conditional expression. Lowered, the initializer had more than one block, which a
// global's region could not hold: "'ts.Global' op expects region #0 to have 0 or 1 blocks".
class Node {
v = 0;
tag = "";
}

function mk(v: number) {
const n = new Node();
n.v = v;
n.tag = "t" + v;
return n;
}

function mkU(): Node | undefined {
return mk(7);
}

function none(): Node | undefined {
return undefined;
}

function on() {
return true;
}

let g: Node | null | undefined = mkU();
let h: Node | null | undefined = none();
let x = on() ? 1 : 2;
const label = on() ? "yes" : "no";
let pick: Node | null = on() ? mk(3) : null;

function main() {
assert(g !== undefined && g !== null && g.tag == "t7", "an optional widened into a union");
assert(h === undefined, "an empty optional widened into a union");
assert(x == 1, "a conditional number");
assert(label == "yes", "a conditional string");
assert(pick !== null && pick.v == 3, "a conditional object");

print("done.");
}
Loading