make rawAlloc support alignment

This commit is contained in:
ringabout
2026-01-29 18:14:25 +08:00
parent abf434a336
commit e076e02349
3 changed files with 80 additions and 8 deletions

View File

@@ -845,17 +845,24 @@ when defined(heaptrack):
proc heaptrack_malloc(a: pointer, size: int) {.cdecl, importc, dynlib: heaptrackLib.}
proc heaptrack_free(a: pointer) {.cdecl, importc, dynlib: heaptrackLib.}
proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
proc rawAlloc(a: var MemRegion, requestedSize: int, alignment: int = MemAlign, extraSize: int = 0): pointer =
when defined(nimTypeNames):
inc(a.allocCounter)
sysAssert(allocInv(a), "rawAlloc: begin")
sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken")
var size = roundup(requestedSize, MemAlign)
# Calculate the actual size we need to allocate
# If alignment > MemAlign, we need extra space to ensure we can align the result
let alignmentOverhead = if alignment > MemAlign: alignment - 1 + extraSize else: 0
let totalSize = requestedSize + alignmentOverhead
var size = roundup(totalSize, MemAlign)
sysAssert(size >= sizeof(FreeCell), "rawAlloc: requested size too small")
sysAssert(size >= requestedSize, "insufficient allocated size!")
#c_fprintf(stdout, "alloc; size: %ld; %ld\n", requestedSize, size)
if size <= SmallChunkSize-smallChunkOverhead():
# Force big chunk allocation for aligned allocations to avoid cell boundary issues
if size <= SmallChunkSize-smallChunkOverhead() and alignment <= MemAlign:
template fetchSharedCells(tc: PSmallChunk) =
# Consumes cells from (potentially) foreign threads from `a.sharedFreeLists[s]`
when defined(gcDestructors):
@@ -965,6 +972,16 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
inc a.occ, c.size
trackSize(c.size)
sysAssert(isAccessible(a, result), "rawAlloc 14")
# Handle alignment if specified
if alignment > MemAlign:
# We need to ensure that (result + extraSize) is aligned
# So find the aligned address for user data, then back up by extraSize
let userDataAddr = cast[int](result) + extraSize
let alignedUserDataAddr = (userDataAddr + alignment - 1) and not (alignment - 1)
# The result should be extraSize bytes before the aligned user data
result = cast[pointer](alignedUserDataAddr - extraSize)
sysAssert(allocInv(a), "rawAlloc: end")
when logAlloc: cprintf("var pointer_%p = alloc(%ld) # %p\n", result, requestedSize, addr a)
when defined(heaptrack):
@@ -1067,7 +1084,12 @@ when not defined(gcDestructors):
(cast[ptr FreeCell](p).zeroField >% 1)
else:
var c = cast[PBigChunk](c)
result = p == addr(c.data) and cast[ptr FreeCell](p).zeroField >% 1
# For aligned allocations, p might be offset from addr(c.data), but should be within data bounds
# Note: We cannot check zeroField/refcount as it may not be initialized yet when called from rawNewObj
let dataStart = cast[int](addr(c.data))
let dataEnd = dataStart + c.size - bigChunkOverhead()
let pAddr = cast[int](p)
result = (pAddr >= dataStart) and (pAddr < dataEnd)
proc prepareForInteriorPointerChecking(a: var MemRegion) {.inline.} =
a.minLargeObj = lowGauge(a.root)

View File

@@ -458,9 +458,12 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
sysAssert(allocInv(gch.region), "rawNewObj begin")
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
collectCT(gch)
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
# Use alignment from typ.base if available, otherwise use MemAlign
let alignment = if typ.base != nil: max(typ.base.align, MemAlign) else: MemAlign
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell), alignment, sizeof(Cell)))
#gcAssert typ.kind in {tyString, tySequence} or size >= typ.base.size, "size too small"
gcAssert((cast[int](res) and (MemAlign-1)) == 0, "newObj: 2")
# Check that the user data (after the Cell header) is properly aligned
gcAssert((cast[int](cellToUsr(res)) and (alignment-1)) == 0, "newObj: 2")
# now it is buffered in the ZCT
res.typ = typ
setFrameInfo(res)
@@ -508,9 +511,12 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl, noinline, raise
collectCT(gch)
sysAssert(allocInv(gch.region), "newObjRC1 after collectCT")
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
# Use alignment from typ.base if available, otherwise use MemAlign
let alignment = if typ.base != nil: max(typ.base.align, MemAlign) else: MemAlign
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell), alignment, sizeof(Cell)))
sysAssert(allocInv(gch.region), "newObjRC1 after rawAlloc")
sysAssert((cast[int](res) and (MemAlign-1)) == 0, "newObj: 2")
# Check that the user data (after the Cell header) is properly aligned
sysAssert((cast[int](cellToUsr(res)) and (alignment-1)) == 0, "newObj: 2")
# now it is buffered in the ZCT
res.typ = typ
setFrameInfo(res)

View File

@@ -1,5 +1,6 @@
discard """
ccodeCheck: "\\i @'NIM_ALIGN(128) NI mylocal1' .*"
matrix: "--mm:refc -d:useGcAssert; --mm:orc"
targets: "c cpp"
output: "align ok"
"""
@@ -67,3 +68,46 @@ block: # bug #22419
f()()
type Xxx = object
v {.align: 128.}: byte
type Yyy = object
v: byte
v2: Xxx
for i in 0..<3:
let x = new Yyy
# echo "addr v2.v:", cast[uint](addr x.v2.v)
doAssert cast[uint](addr x.v2.v) mod 128 == 0
let m = new Yyy
m.v2.v = 42
doAssert m.v2.v == 42
m.v = 7
doAssert m.v == 7
type
MyType16 = object
a {.align(16).}: int
var x: array[10, ref MyType16]
for q in 0..500:
for i in 0..<x.len:
new x[i]
x[i].a = q
doAssert(cast[int](x[i]) mod alignof(MyType16) == 0)
type
MyType32 = object
a{.align(32).}: int
var y: array[10, ref MyType32]
for q in 0..500:
for i in 0..<y.len:
new y[i]
y[i].a = q
doAssert(cast[int](y[i]) mod alignof(MyType32) == 0)