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
28 changes: 26 additions & 2 deletions tslang/lib/TypeScript/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3681,6 +3681,23 @@ struct GlobalOpLowering : public TsLlvmPattern<mlir_ts::GlobalOp>

LLVMCodeHelper lch(globalOp, rewriter, getTypeConverter(), tsLlvmContext->compileOptions);

// A ConstantOp whose *TupleType/ConstTupleType result* has a bound-method
// field (e.g. a plain `const obj = { ..., method() {} }` at global scope)
// lowers its method field via LLVM::AddressOfOp + LLVM::InsertValueOp (see
// LLVMCodeHelper::getTupleFromArrayAttr) -- not valid inside a static LLVM
// global initializer region, only inside real code (a function), so such a
// global must go through the constructor path below. Deliberately narrower
// than "any ConstantOp whose attribute contains a FlatSymbolRefAttr": that
// broader check also caught MSVC RTTI/EH type-descriptor globals (e.g.
// `??_R0PEAD@8`, which reference the `type_info` vtable symbol) that are
// NOT mlir_ts tuple types and were already lowering correctly as static
// data -- forcing those through the constructor path broke JIT symbol
// materialization for the whole module.
MLIRTypeHelper mth(rewriter.getContext(), tsLlvmContext->compileOptions);
auto isBoundMethodTupleConstant = [&](mlir_ts::ConstantOp constantOp) {
return mth.hasBoundMethodField(constantOp.getType());
};

auto createAsGlobalConstructor = false;
// TODO: we need to write correct attributes to Ops and detect which ops should be in GlobalConstructor
auto visitorAllOps = [&](Operation *op) {
Expand All @@ -3700,9 +3717,16 @@ struct GlobalOpLowering : public TsLlvmPattern<mlir_ts::GlobalOp>
else if (auto castOp = dyn_cast<mlir_ts::CastOp>(op))
{
auto castType = castOp.getRes().getType();
if (isa<mlir_ts::ArrayType>(castType) || isa<mlir_ts::TupleType>(castType))
if (isa<mlir_ts::ArrayType>(castType) || isa<mlir_ts::TupleType>(castType))
{
createAsGlobalConstructor = true;
}
}
else if (auto constantOp = dyn_cast<mlir_ts::ConstantOp>(op))
{
if (isBoundMethodTupleConstant(constantOp))
{
createAsGlobalConstructor = true;
createAsGlobalConstructor = true;
}
}

Expand Down
2 changes: 2 additions & 0 deletions tslang/test/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ add_test(NAME test-compile-00-bool-arith-ops COMMAND test-runner "${PROJECT_SOUR
add_test(NAME test-compile-00-mixed-type-ops COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00mixed_type_ops.ts")
add_test(NAME test-compile-00-generator-manual-next COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00generator_manual_next.ts")
add_test(NAME test-compile-00-generator-manual-next-2 COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00generator_manual_next2.ts")
add_test(NAME test-compile-00-global-const-object-method COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00global_const_object_method.ts")
add_test(NAME test-compile-00-funcs COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs.ts")
add_test(NAME test-compile-00-funcs-capture COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_capture.ts")
add_test(NAME test-compile-00-funcs-vararg COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_vararg.ts")
Expand Down Expand Up @@ -472,6 +473,7 @@ add_test(NAME test-jit-00-bool-arith-ops COMMAND test-runner -jit "${PROJECT_SOU
add_test(NAME test-jit-00-mixed-type-ops COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00mixed_type_ops.ts")
add_test(NAME test-jit-00-generator-manual-next COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00generator_manual_next.ts")
add_test(NAME test-jit-00-generator-manual-next-2 COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00generator_manual_next2.ts")
add_test(NAME test-jit-00-global-const-object-method COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00global_const_object_method.ts")
add_test(NAME test-jit-00-funcs COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs.ts")
add_test(NAME test-jit-00-funcs-capture COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_capture.ts")
add_test(NAME test-jit-00-funcs-vararg COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_vararg.ts")
Expand Down
22 changes: 22 additions & 0 deletions tslang/test/tester/tests/00global_const_object_method.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// regression test: a top-level (global) `const` object literal with a method that
// mutates `this` used to crash the compiler (0xC0000005) at JIT materialization time.
// Root cause: GlobalOpLowering's visitorAllOps walk decided whether a global needs a
// runtime constructor function or can be a plain static LLVM `constant` initializer,
// but had no case for a ConstantOp whose TupleType/ConstTupleType result has a
// bound-method field -- such a field lowers via LLVM::AddressOfOp + InsertValueOp,
// which is not valid inside a static global initializer region, only inside real code.
// `let` at global scope, and `const` declared locally inside a function, were both
// unaffected (they already go through -- or don't need -- the constructor path).
const obj = {
count: 0,
inc() { this.count = this.count + 1; },
greet() { print("hi"); }
};

function main() {
obj.greet();
obj.inc();
obj.inc();
assert(obj.count == 2);
print("done.");
}
Loading