Skip to content

Declare the process-heap allocator to LLVM as LLVM declares malloc - #683

Merged
ASDAlexander77 merged 1 commit into
mainfrom
process-heap-malloc-attrs
Oct 24, 2026
Merged

ASDAlexander77 merged 1 commit into
mainfrom
process-heap-malloc-attrs

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

Problem

ProcessHeapPass::markAsAllocator declared __tslang_heap_malloc/calloc/aligned_alloc as writing other memory (memory(write, argmem: none, inaccessiblemem: readwrite)), and their result had no noalias. So every allocation clobbered whatever the program had just stored. A field written into a fresh object was loaded back from the heap after the next new instead of being reused, and the object could never be deleted. In the ray tracer, LLVM inlines Vector.plus, Color.scale and the rest, but every inlined copy still allocated and kept its block.

Fix

An allocation now touches only the allocator's state (memory(inaccessiblemem: readwrite)) and returns a noalias block, as LLVM's own malloc does. Writing the allocator's state still keeps two equal allocations from merging (the GC_malloc case described in GCPass). Releases and resizes are unchanged.

Results

tslang/benchmarks/raytrace, 512x512, median of 7, bench.ps1; every build prints the same checksum:

main 63712f3 this PR
own 597 ms 444 ms
none 642 ms 434 ms
gc 1189 ms 1199 ms (own allocator, GCPass)
rc 606 ms 630 ms

rc is 4% slower in both runs I made; I have not found why. The benchmark README gets a "Results 2026-10-24" section with these numbers and an account of what still separates own from Rust (54 ms): the immortal-header check on each own release, and objects escaping into interface calls, interface literals and closures.

Tests

  • 00fresh_objects_folded.ts (compile and JIT): a fresh object's field read after another new.
  • fresh-objects-folded.cmake: the optimized IR of keep loads no field back, under none, rc and own. It fails on main.
  • Full suites: Windows 5525/5525, Linux 5503/5503.

🤖 Generated with Claude Code

ProcessHeapPass marked __tslang_heap_malloc/calloc/aligned_alloc as
writing other memory and gave their result no `noalias`. So every
allocation clobbered whatever the program had just stored: a field
written into a fresh object was loaded back from the heap after the
next `new` instead of reused, and the object could never be deleted.
In the ray tracer every vector of `Vector.plus(Vector.times(k, d), p)`
stayed on the heap although LLVM had inlined the helpers.

An allocation now touches only the allocator's state
(`memory(inaccessiblemem: readwrite)`) and returns a `noalias` block,
as LLVM's own malloc does. Writing the allocator's state still keeps two
equal allocations from merging (the GC_malloc case in GCPass).

Ray tracer, 512x512, median of 7: own 597 -> 444 ms, none 642 -> 434 ms,
gc unchanged (its own allocator), rc 606 -> 630 ms. The benchmark README
records the numbers and what is left between own and Rust.

Test: 00fresh_objects_folded.ts runs, and fresh-objects-folded.cmake
checks that the optimized IR of `keep` loads no field back under none,
rc and own (it fails before this change).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit 66eaa55 into main Oct 24, 2026
2 of 3 checks passed
@ASDAlexander77
ASDAlexander77 deleted the process-heap-malloc-attrs branch October 24, 2026 10:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant