Skip to content

Commit 7d9f8d6

Browse files
Fix crash for global const object methods during JIT materialization and add regression test (#246)
1 parent 7dc8ff8 commit 7d9f8d6

3 files changed

Lines changed: 50 additions & 2 deletions

File tree

tslang/lib/TypeScript/LowerToLLVM.cpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3681,6 +3681,23 @@ struct GlobalOpLowering : public TsLlvmPattern<mlir_ts::GlobalOp>
36813681

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

3684+
// A ConstantOp whose *TupleType/ConstTupleType result* has a bound-method
3685+
// field (e.g. a plain `const obj = { ..., method() {} }` at global scope)
3686+
// lowers its method field via LLVM::AddressOfOp + LLVM::InsertValueOp (see
3687+
// LLVMCodeHelper::getTupleFromArrayAttr) -- not valid inside a static LLVM
3688+
// global initializer region, only inside real code (a function), so such a
3689+
// global must go through the constructor path below. Deliberately narrower
3690+
// than "any ConstantOp whose attribute contains a FlatSymbolRefAttr": that
3691+
// broader check also caught MSVC RTTI/EH type-descriptor globals (e.g.
3692+
// `??_R0PEAD@8`, which reference the `type_info` vtable symbol) that are
3693+
// NOT mlir_ts tuple types and were already lowering correctly as static
3694+
// data -- forcing those through the constructor path broke JIT symbol
3695+
// materialization for the whole module.
3696+
MLIRTypeHelper mth(rewriter.getContext(), tsLlvmContext->compileOptions);
3697+
auto isBoundMethodTupleConstant = [&](mlir_ts::ConstantOp constantOp) {
3698+
return mth.hasBoundMethodField(constantOp.getType());
3699+
};
3700+
36843701
auto createAsGlobalConstructor = false;
36853702
// TODO: we need to write correct attributes to Ops and detect which ops should be in GlobalConstructor
36863703
auto visitorAllOps = [&](Operation *op) {
@@ -3700,9 +3717,16 @@ struct GlobalOpLowering : public TsLlvmPattern<mlir_ts::GlobalOp>
37003717
else if (auto castOp = dyn_cast<mlir_ts::CastOp>(op))
37013718
{
37023719
auto castType = castOp.getRes().getType();
3703-
if (isa<mlir_ts::ArrayType>(castType) || isa<mlir_ts::TupleType>(castType))
3720+
if (isa<mlir_ts::ArrayType>(castType) || isa<mlir_ts::TupleType>(castType))
3721+
{
3722+
createAsGlobalConstructor = true;
3723+
}
3724+
}
3725+
else if (auto constantOp = dyn_cast<mlir_ts::ConstantOp>(op))
3726+
{
3727+
if (isBoundMethodTupleConstant(constantOp))
37043728
{
3705-
createAsGlobalConstructor = true;
3729+
createAsGlobalConstructor = true;
37063730
}
37073731
}
37083732

tslang/test/tester/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ add_test(NAME test-compile-00-bool-arith-ops COMMAND test-runner "${PROJECT_SOUR
125125
add_test(NAME test-compile-00-mixed-type-ops COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00mixed_type_ops.ts")
126126
add_test(NAME test-compile-00-generator-manual-next COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00generator_manual_next.ts")
127127
add_test(NAME test-compile-00-generator-manual-next-2 COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00generator_manual_next2.ts")
128+
add_test(NAME test-compile-00-global-const-object-method COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00global_const_object_method.ts")
128129
add_test(NAME test-compile-00-funcs COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs.ts")
129130
add_test(NAME test-compile-00-funcs-capture COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_capture.ts")
130131
add_test(NAME test-compile-00-funcs-vararg COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_vararg.ts")
@@ -472,6 +473,7 @@ add_test(NAME test-jit-00-bool-arith-ops COMMAND test-runner -jit "${PROJECT_SOU
472473
add_test(NAME test-jit-00-mixed-type-ops COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00mixed_type_ops.ts")
473474
add_test(NAME test-jit-00-generator-manual-next COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00generator_manual_next.ts")
474475
add_test(NAME test-jit-00-generator-manual-next-2 COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00generator_manual_next2.ts")
476+
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")
475477
add_test(NAME test-jit-00-funcs COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs.ts")
476478
add_test(NAME test-jit-00-funcs-capture COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_capture.ts")
477479
add_test(NAME test-jit-00-funcs-vararg COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_vararg.ts")
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// regression test: a top-level (global) `const` object literal with a method that
2+
// mutates `this` used to crash the compiler (0xC0000005) at JIT materialization time.
3+
// Root cause: GlobalOpLowering's visitorAllOps walk decided whether a global needs a
4+
// runtime constructor function or can be a plain static LLVM `constant` initializer,
5+
// but had no case for a ConstantOp whose TupleType/ConstTupleType result has a
6+
// bound-method field -- such a field lowers via LLVM::AddressOfOp + InsertValueOp,
7+
// which is not valid inside a static global initializer region, only inside real code.
8+
// `let` at global scope, and `const` declared locally inside a function, were both
9+
// unaffected (they already go through -- or don't need -- the constructor path).
10+
const obj = {
11+
count: 0,
12+
inc() { this.count = this.count + 1; },
13+
greet() { print("hi"); }
14+
};
15+
16+
function main() {
17+
obj.greet();
18+
obj.inc();
19+
obj.inc();
20+
assert(obj.count == 2);
21+
print("done.");
22+
}

0 commit comments

Comments
 (0)