diff --git a/tslang/lib/TypeScript/GCPass.cpp b/tslang/lib/TypeScript/GCPass.cpp index 11db0c0d3..80debfc78 100644 --- a/tslang/lib/TypeScript/GCPass.cpp +++ b/tslang/lib/TypeScript/GCPass.cpp @@ -206,9 +206,14 @@ class GCPass : public mlir::PassWrapper return; } + // The allocator's own state is inaccessible memory it reads and writes, as LLVM declares + // malloc: `memory(inaccessiblemem: readwrite)`. `allockind` alone no longer stops the + // merge - with this LLVM two GC_malloc(8) calls, the storage of two empty arrays, became + // one call, both arrays grew through GC_realloc of the same block, and the heap was + // corrupted (`s += x` produced an empty string further on, only with optimization). auto *context = funcOp->getContext(); auto memoryEffects = LLVM::MemoryEffectsAttr::get(context, LLVM::ModRefInfo::Mod, LLVM::ModRefInfo::NoModRef, - LLVM::ModRefInfo::NoModRef, LLVM::ModRefInfo::NoModRef, + LLVM::ModRefInfo::ModRef, LLVM::ModRefInfo::NoModRef, LLVM::ModRefInfo::NoModRef, LLVM::ModRefInfo::NoModRef); funcOp.setMemoryEffectsAttr(memoryEffects); diff --git a/tslang/lib/TypeScript/LowerToLLVM.cpp b/tslang/lib/TypeScript/LowerToLLVM.cpp index b3311de30..89bd928e7 100644 --- a/tslang/lib/TypeScript/LowerToLLVM.cpp +++ b/tslang/lib/TypeScript/LowerToLLVM.cpp @@ -6638,9 +6638,10 @@ class GCNewExplicitlyTypedOpLowering : public TsLlvmPattern(loc, gcMallocExplicitlyTypedFunc, ValueRange{sizeOfTypeValue, transformed.getTypeDescr()}); diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index c23b97214..f13890fe9 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -276,6 +276,7 @@ tslang_add_test(NAME test-compile-00-funcs-capture COMMAND test-runner "${PROJEC tslang_add_test(NAME test-compile-00-funcs-vararg COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_vararg.ts") tslang_add_test(NAME test-compile-00-declare-function-twice COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00declare_function_twice.ts") tslang_add_test(NAME test-compile-00-function-overloads COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00function_overloads.ts") +tslang_add_test(NAME test-compile-00-gc-allocations-distinct COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00gc_allocations_distinct.ts") tslang_add_test(NAME test-compile-01-funcs-vararg COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/01funcs_vararg.ts") # printf is avaliable in JIT only, C has snprintf #tslang_add_test(NAME test-compile-02-funcs-vararg COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/02funcs_vararg.ts") @@ -726,6 +727,7 @@ tslang_add_test(NAME test-jit-00-funcs-capture COMMAND test-runner -jit "${PROJE tslang_add_test(NAME test-jit-00-funcs-vararg COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_vararg.ts") tslang_add_test(NAME test-jit-00-declare-function-twice COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00declare_function_twice.ts") tslang_add_test(NAME test-jit-00-function-overloads COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00function_overloads.ts") +tslang_add_test(NAME test-jit-00-gc-allocations-distinct COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00gc_allocations_distinct.ts") tslang_add_test(NAME test-jit-01-funcs-vararg COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/01funcs_vararg.ts") tslang_add_test(NAME test-jit-02-funcs-vararg COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/02funcs_vararg.ts") tslang_add_test(NAME test-jit-00-funcs-bindings COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_bindings.ts") @@ -1651,6 +1653,7 @@ set(TSLANG_CORPUS 00funcs_typeless_function_as_generic.ts 00funcs_vararg.ts 00function_overloads.ts + 00gc_allocations_distinct.ts 00funcs.ts 00generator_manual_next.ts 00generator_manual_next2.ts diff --git a/tslang/test/tester/tests/00gc_allocations_distinct.ts b/tslang/test/tester/tests/00gc_allocations_distinct.ts new file mode 100644 index 000000000..5b5332dbd --- /dev/null +++ b/tslang/test/tester/tests/00gc_allocations_distinct.ts @@ -0,0 +1,36 @@ +// Two allocations of the same size are two blocks. The collector's allocators were marked so that +// the optimizer could merge two identical calls: the storage of two empty arrays became one block, +// both arrays grew through GC_realloc of it, and the heap was corrupted - here, `s += a` came out +// empty. Only with optimization, which the compile and jit tiers use. +class Point { + x = 0; +} + +function main() { + const src: number[] = [1, 2]; + let nums: number[] = []; + const t: number[] = [...src]; + for (const v of t) nums.push(v); + assert(nums.length == 2, "nums"); + + const a = nums[0] + ","; + let s = ""; + s += a; + assert(s == "1,", "append"); + + let e1: number[] = []; + let e2: number[] = []; + e1.push(1); + e2.push(2); + e2.push(3); + assert(e1.length == 1 && e1[0] == 1, "first empty array"); + assert(e2.length == 2 && e2[0] == 2, "second empty array"); + + const p = new Point(); + const q = new Point(); + p.x = 1; + q.x = 2; + assert(p.x == 1 && q.x == 2, "two instances"); + + print("done."); +}