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
7 changes: 6 additions & 1 deletion tslang/lib/TypeScript/GCPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,14 @@ class GCPass : public mlir::PassWrapper<GCPass, ModulePass>
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);

Expand Down
5 changes: 3 additions & 2 deletions tslang/lib/TypeScript/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6638,9 +6638,10 @@ class GCNewExplicitlyTypedOpLowering : public TsLlvmPattern<mlir_ts::GCNewExplic
auto gcMallocExplicitlyTypedFunc = ch.getOrInsertFunction("GC_malloc_explicitly_typed", th.getFunctionType(i8PtrTy, {th.getSizeType(), th.getSizeType()}));
// Without this, two `new` sites with identical (size, typeDescr) args - e.g. two
// instances of the same class - look like redundant calls to GVN/EarlyCSE at -O3
// and get merged into one shared allocation. See GCPass.cpp markAsAllocatorIfNeeded.
// and get merged into one shared allocation. See GCPass.cpp markAsAllocatorIfNeeded, and
// the allocator's state as inaccessible memory it reads and writes, which is what stops it.
gcMallocExplicitlyTypedFunc.setMemoryEffectsAttr(LLVM::MemoryEffectsAttr::get(
rewriter.getContext(), LLVM::ModRefInfo::Mod, LLVM::ModRefInfo::NoModRef, LLVM::ModRefInfo::NoModRef,
rewriter.getContext(), LLVM::ModRefInfo::Mod, LLVM::ModRefInfo::NoModRef, LLVM::ModRefInfo::ModRef,
LLVM::ModRefInfo::NoModRef, LLVM::ModRefInfo::NoModRef, LLVM::ModRefInfo::NoModRef));
markGCMallocExplicitlyTypedAsAllocator(gcMallocExplicitlyTypedFunc);
auto value = rewriter.create<LLVM::CallOp>(loc, gcMallocExplicitlyTypedFunc, ValueRange{sizeOfTypeValue, transformed.getTypeDescr()});
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 @@ -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")
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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
Expand Down
36 changes: 36 additions & 0 deletions tslang/test/tester/tests/00gc_allocations_distinct.ts
Original file line number Diff line number Diff line change
@@ -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.");
}
Loading