forward type alignment information to seqs (#12430)

This commit is contained in:
Arne Döring
2020-04-19 07:52:01 +02:00
committed by GitHub
parent a8f030fea2
commit 4005f0d0e4
25 changed files with 152 additions and 114 deletions

View File

@@ -1301,9 +1301,10 @@ proc genNewSeq(p: BProc, e: PNode) =
initLocExpr(p, e[2], b)
if optSeqDestructors in p.config.globalOptions:
let seqtype = skipTypes(e[1].typ, abstractVarRange)
linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n",
[a.rdLoc, b.rdLoc, getTypeDesc(p.module, seqtype.lastSon),
getSeqPayloadType(p.module, seqtype)])
linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n",
[a.rdLoc, b.rdLoc,
getTypeDesc(p.module, seqtype.lastSon),
getSeqPayloadType(p.module, seqtype)])
else:
let lenIsZero = optNilSeqs notin p.options and
e[2].kind == nkIntLit and e[2].intVal == 0
@@ -1316,9 +1317,10 @@ proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) =
initLocExpr(p, e[1], a)
if optSeqDestructors in p.config.globalOptions:
if d.k == locNone: getTemp(p, e.typ, d, needsInit=false)
linefmt(p, cpsStmts, "$1.len = 0; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n",
linefmt(p, cpsStmts, "$1.len = 0; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n",
[d.rdLoc, a.rdLoc, getTypeDesc(p.module, seqtype.lastSon),
getSeqPayloadType(p.module, seqtype)])
getSeqPayloadType(p.module, seqtype),
])
else:
putIntoDest(p, d, e, ropecg(p.module,
"($1)#nimNewSeqOfCap($2, $3)", [
@@ -1425,7 +1427,7 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) =
let l = intLiteral(n.len)
if optSeqDestructors in p.config.globalOptions:
let seqtype = n.typ
linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n",
linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n",
[rdLoc dest[], l, getTypeDesc(p.module, seqtype.lastSon),
getSeqPayloadType(p.module, seqtype)])
else:
@@ -1456,7 +1458,7 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
let L = toInt(lengthOrd(p.config, n[1].typ))
if optSeqDestructors in p.config.globalOptions:
let seqtype = n.typ
linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n",
linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n",
[rdLoc d, L, getTypeDesc(p.module, seqtype.lastSon),
getSeqPayloadType(p.module, seqtype)])
else:
@@ -2224,9 +2226,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
putIntoDest(p, d, e, "((NI)sizeof($1))" % [getTypeDesc(p.module, t)])
of mAlignOf:
let t = e[1].typ.skipTypes({tyTypeDesc})
if not p.module.compileToCpp:
p.module.includeHeader("<stdalign.h>")
putIntoDest(p, d, e, "((NI)alignof($1))" % [getTypeDesc(p.module, t)])
putIntoDest(p, d, e, "((NI)NIM_ALIGNOF($1))" % [getTypeDesc(p.module, t)])
of mOffsetOf:
var dotExpr: PNode
block findDotExpr:

View File

@@ -999,11 +999,14 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
let nameHcr = tiNameForHcr(m, name)
var size: Rope
if tfIncompleteStruct in typ.flags: size = rope"void*"
else: size = getTypeDesc(m, origType)
if tfIncompleteStruct in typ.flags:
size = rope"void*"
else:
size = getTypeDesc(m, origType)
m.s[cfsTypeInit3].addf(
"$1.size = sizeof($2);$n" & "$1.kind = $3;$n" & "$1.base = $4;$n",
[nameHcr, size, rope(nimtypeKind), base])
"$1.size = sizeof($2);$n$1.align = NIM_ALIGNOF($2);$n$1.kind = $3;$n$1.base = $4;$n",
[nameHcr, size, rope(nimtypeKind), base]
)
# compute type flags for GC optimization
var flags = 0
if not containsGarbageCollectedRef(typ): flags = flags or 1
@@ -1313,10 +1316,7 @@ proc genTypeInfoV2(m: BModule, t, origType: PType, name: Rope; info: TLineInfo)
let traceImpl = genHook(m, t, info, attachedTrace)
let disposeImpl = genHook(m, t, info, attachedDispose)
m.s[cfsTypeInit3].addf("$1.destructor = (void*)$2; $1.size = sizeof($3);$n" &
"$1.name = $4;$n" &
"$1.traceImpl = (void*)$5;$n" &
"$1.disposeImpl = (void*)$6;$n", [
addf(m.s[cfsTypeInit3], "$1.destructor = (void*)$2; $1.size = sizeof($3); $1.align = NIM_ALIGNOF($3); $1.name = $4;$n; $1.traceImpl = (void*)$5; $1.disposeImpl = (void*)$6;", [
name, destroyImpl, getTypeDesc(m, t), typeName,
traceImpl, disposeImpl])

View File

@@ -102,6 +102,7 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimnomagic64")
defineSymbol("nimNewShiftOps")
defineSymbol("nimHasCursor")
defineSymbol("nimAlignPragma")
defineSymbol("nimHasExceptionsQuery")
defineSymbol("nimHasIsNamedTuple")
defineSymbol("nimHashOrdinalFixed")

View File

@@ -12,15 +12,15 @@
## what this module allows you to do.
##
## Note that even though ``Any`` and its operations hide the nasty low level
## details from its clients, it remains inherently unsafe! Also, Nim's
## details from its clients, it remains inherently unsafe! Also, Nim's
## runtime type information will evolve and may eventually be deprecated.
## As an alternative approach to programmatically understanding and
## manipulating types, consider using the `macros <macros.html>`_ package to
## work with the types' AST representation at compile time. See, for example,
## the `getTypeImpl proc<macros.html#getTypeImpl,NimNode>`_. As an alternative
## the `getTypeImpl proc<macros.html#getTypeImpl,NimNode>`_. As an alternative
## approach to storing arbitrary types at runtime, consider using generics.
##
##
##
{.push hints: off.}
@@ -98,7 +98,7 @@ else:
proc genericAssign(dest, src: pointer, mt: PNimType) {.importCompilerProc.}
proc genericShallowAssign(dest, src: pointer, mt: PNimType) {.
importCompilerProc.}
proc incrSeq(seq: PGenSeq, elemSize: int): PGenSeq {.importCompilerProc.}
proc incrSeq(seq: PGenSeq, elemSize, elemAlign: int): PGenSeq {.importCompilerProc.}
proc newObj(typ: PNimType, size: int): pointer {.importCompilerProc.}
proc newSeq(typ: PNimType, len: int): pointer {.importCompilerProc.}
proc objectInit(dest: pointer, typ: PNimType) {.importCompilerProc.}
@@ -180,7 +180,7 @@ proc extendSeq*(x: Any) =
## performs ``setLen(x, x.len+1)``. `x` needs to represent a ``seq``.
assert x.rawType.kind == tySequence
var y = cast[ptr PGenSeq](x.value)[]
var z = incrSeq(y, x.rawType.base.size)
var z = incrSeq(y, x.rawType.base.size, x.rawType.base.align)
# 'incrSeq' already freed the memory for us and copied over the RC!
# So we simply copy the raw pointer into 'x.value':
cast[ppointer](x.value)[] = z
@@ -195,6 +195,9 @@ proc skipRange(x: PNimType): PNimType {.inline.} =
result = x
if result.kind == tyRange: result = result.base
proc align(address, alignment: int): int =
result = (address + (alignment - 1)) and not (alignment - 1)
proc `[]`*(x: Any, i: int): Any =
## accessor for an any `x` that represents an array or a sequence.
case x.rawType.kind
@@ -209,7 +212,7 @@ proc `[]`*(x: Any, i: int): Any =
var bs = x.rawType.base.size
if i >=% cast[PGenSeq](s).len:
raise newException(IndexError, formatErrorIndexBound(i, cast[PGenSeq](s).len-1))
return newAny(s +!! (GenericSeqSize+i*bs), x.rawType.base)
return newAny(s +!! (align(GenericSeqSize, x.rawType.base.align)+i*bs), x.rawType.base)
else: assert false
proc `[]=`*(x: Any, i: int, y: Any) =
@@ -228,7 +231,7 @@ proc `[]=`*(x: Any, i: int, y: Any) =
if i >=% cast[PGenSeq](s).len:
raise newException(IndexError, formatErrorIndexBound(i, cast[PGenSeq](s).len-1))
assert y.rawType == x.rawType.base
genericAssign(s +!! (GenericSeqSize+i*bs), y.value, y.rawType)
genericAssign(s +!! (align(GenericSeqSize, x.rawType.base.align)+i*bs), y.value, y.rawType)
else: assert false
proc len*(x: Any): int =

View File

@@ -477,7 +477,6 @@ typedef char* NCSTRING;
#define ALLOC_0(size) calloc(1, size)
#define DL_ALLOC_0(size) dlcalloc(1, size)
#define GenericSeqSize sizeof(TGenericSeq)
#define paramCount() cmdCount
// NAN definition copied from math.h included in the Windows SDK version 10.0.14393.0
@@ -546,8 +545,10 @@ NIM_STATIC_ASSERT(sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8, ""
#if defined(_MSC_VER)
# define NIM_ALIGN(x) __declspec(align(x))
# define NIM_ALIGNOF(x) __alignof(x)
#else
# define NIM_ALIGN(x) __attribute__((aligned(x)))
# define NIM_ALIGNOF(x) __alignof__(x)
#endif
/* ---------------- platform specific includes ----------------------- */

View File

@@ -93,8 +93,7 @@ else:
when useNSGetEnviron:
var gEnv = NSGetEnviron()[]
var i = 0
while true:
if gEnv[i] == nil: break
while gEnv[i] != nil:
add environment, $gEnv[i]
inc(i)
envComputed = true

View File

@@ -514,6 +514,7 @@ when not defined(js) and not defined(nimSeqsV2):
len, reserved: int
when defined(gogc):
elemSize: int
elemAlign: int
PGenericSeq {.exportc.} = ptr TGenericSeq
# len and space without counting the terminating zero:
NimStringDesc {.compilerproc, final.} = object of TGenericSeq
@@ -1123,6 +1124,13 @@ elif hostOS != "standalone":
var programResult* {.compilerproc, exportc: "nim_program_result".}: int
## deprecated, prefer ``quit``
proc align(address, alignment: int): int =
if alignment == 0: # Actually, this is illegal. This branch exists to actively
# hide problems.
result = address
else:
result = (address + (alignment - 1)) and not (alignment - 1)
when defined(nimdoc):
proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn.}
## Stops the program immediately with an exit code.
@@ -1698,6 +1706,7 @@ when not defined(js) and defined(nimV2):
TNimType {.compilerproc.} = object
destructor: pointer
size: int
align: int
name: cstring
traceImpl: pointer
disposeImpl: pointer
@@ -1709,7 +1718,6 @@ when notJSnotNims and defined(nimSeqsV2):
{.pop.}
when notJSnotNims:
proc writeStackTrace*() {.tags: [], gcsafe, raises: [].}
## Writes the current stack trace to ``stderr``. This is only works
@@ -1988,8 +1996,8 @@ proc abs*(x: int64): int64 {.magic: "AbsI", noSideEffect.} =
result = if x < 0: -x else: x
{.pop.}
when not defined(js):
proc likelyProc(val: bool): bool {.importc: "NIM_LIKELY", nodecl, noSideEffect.}
proc unlikelyProc(val: bool): bool {.importc: "NIM_UNLIKELY", nodecl, noSideEffect.}

View File

@@ -45,7 +45,6 @@ type
data: TrunkBuckets
type
AlignType = BiggestFloat
FreeCell {.final, pure.} = object
next: ptr FreeCell # next free cell in chunk (overlaid with refcount)
when not defined(gcDestructors):
@@ -68,16 +67,20 @@ type
freeList: ptr FreeCell
free: int # how many bytes remain
acc: int # accumulator for small object allocation
when defined(cpu32):
align: int
data: AlignType # start of usable memory
when defined(nimAlignPragma):
data {.align: MemAlign.}: UncheckedArray[byte] # start of usable memory
else:
data: UncheckedArray[byte]
BigChunk = object of BaseChunk # not necessarily > PageSize!
next, prev: PBigChunk # chunks of the same (or bigger) size
data: AlignType # start of usable memory
when defined(nimAlignPragma):
data {.align: MemAlign.}: UncheckedArray[byte] # start of usable memory
else:
data: UncheckedArray[byte]
template smallChunkOverhead(): untyped = sizeof(SmallChunk)-sizeof(AlignType)
template bigChunkOverhead(): untyped = sizeof(BigChunk)-sizeof(AlignType)
template smallChunkOverhead(): untyped = sizeof(SmallChunk)
template bigChunkOverhead(): untyped = sizeof(BigChunk)
# ------------- chunk table ---------------------------------------------------
# We use a PtrSet of chunk starts and a table[Page, chunksize] for chunk

View File

@@ -66,17 +66,16 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) =
cast[PGenericSeq](ss).len = seq.len
unsureAsgnRef(x, ss)
var dst = cast[ByteAddress](cast[PPointer](dest)[])
copyMem(cast[pointer](dst +% GenericSeqSize),
cast[pointer](cast[ByteAddress](s2) +% GenericSeqSize),
seq.len * mt.base.size)
copyMem(cast[pointer](dst +% align(GenericSeqSize, mt.base.align)),
cast[pointer](cast[ByteAddress](s2) +% align(GenericSeqSize, mt.base.align)),
seq.len *% mt.base.size)
else:
unsureAsgnRef(x, newSeq(mt, seq.len))
var dst = cast[ByteAddress](cast[PPointer](dest)[])
for i in 0..seq.len-1:
genericAssignAux(
cast[pointer](dst +% i *% mt.base.size +% GenericSeqSize),
cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +%
GenericSeqSize),
cast[pointer](dst +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size ),
cast[pointer](cast[ByteAddress](s2) +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size ),
mt.base, shallow)
of tyObject:
var it = mt.base

View File

@@ -214,7 +214,7 @@ when not usesDestructors:
x[] = nil
else:
var ss = cast[NimString](s2)
var ns = cast[NimString](alloc(t.region, ss.len+1 + GenericSeqSize))
var ns = cast[NimString](alloc(t.region, GenericSeqSize + ss.len+1))
copyMem(ns, ss, ss.len+1 + GenericSeqSize)
x[] = ns
else:
@@ -239,7 +239,7 @@ when not usesDestructors:
else:
sysAssert(dest != nil, "dest == nil")
if mode == mStore:
x[] = alloc0(t.region, seq.len *% mt.base.size +% GenericSeqSize)
x[] = alloc0(t.region, align(GenericSeqSize, mt.base.align) +% seq.len *% mt.base.size)
else:
unsureAsgnRef(x, newSeq(mt, seq.len))
var dst = cast[ByteAddress](cast[PPointer](dest)[])
@@ -248,9 +248,9 @@ when not usesDestructors:
dstseq.reserved = seq.len
for i in 0..seq.len-1:
storeAux(
cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +%
GenericSeqSize),
cast[pointer](dst +% align(GenericSeqSize, mt.base.align) +% i*% mt.base.size),
cast[pointer](cast[ByteAddress](s2) +% align(GenericSeqSize, mt.base.align) +%
i *% mt.base.size),
mt.base, t, mode)
if mode != mStore: dealloc(t.region, s2)
of tyObject:
@@ -452,4 +452,3 @@ proc ready*[TMsg](c: var Channel[TMsg]): bool =
## new messages.
var q = cast[PRawChannel](addr(c))
result = q.ready

View File

@@ -108,9 +108,8 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) =
var dst = cast[ByteAddress](cast[PPointer](dest)[])
for i in 0..seq.len-1:
genericDeepCopyAux(
cast[pointer](dst +% i *% mt.base.size +% GenericSeqSize),
cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +%
GenericSeqSize),
cast[pointer](dst +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size),
cast[pointer](cast[ByteAddress](s2) +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size),
mt.base, tab)
of tyObject:
# we need to copy m_type field for tyObject, as it could be empty for

View File

@@ -339,8 +339,7 @@ proc forAllChildren(cell: PCell, op: WalkOp) =
var s = cast[PGenericSeq](d)
if s != nil:
for i in 0..s.len-1:
forAllChildrenAux(cast[pointer](d +% i *% cell.typ.base.size +%
GenericSeqSize), cell.typ.base, op)
forAllChildrenAux(cast[pointer](d +% align(GenericSeqSize, cell.typ.base.align) +% i *% cell.typ.base.size), cell.typ.base, op)
else: discard
proc addNewObjToZCT(res: PCell, gch: var GcHeap) {.inline.} =
@@ -444,7 +443,7 @@ proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
{.push overflowChecks: on.}
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
# `newObj` already uses locks, so no need for them here.
let size = len * typ.base.size + GenericSeqSize
let size = align(GenericSeqSize, typ.base.align) + len * typ.base.size
result = newObj(typ, size)
cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len
@@ -480,7 +479,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
{.push overflowChecks: on.}
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
let size = len * typ.base.size + GenericSeqSize
let size = align(GenericSeqSize, typ.base.align) + len * typ.base.size
result = newObjRC1(typ, size)
cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len
@@ -495,11 +494,13 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
sysAssert(allocInv(gch.region), "growObj begin")
var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
var elemSize = 1
if ol.typ.kind != tyString: elemSize = ol.typ.base.size
var elemSize,elemAlign = 1
if ol.typ.kind != tyString:
elemSize = ol.typ.base.size
elemAlign = ol.typ.base.align
incTypeSize ol.typ, newsize
var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
var oldsize = align(GenericSeqSize, elemAlign) + cast[PGenericSeq](old).len * elemSize
copyMem(res, ol, oldsize + sizeof(Cell))
zeroMem(cast[pointer](cast[ByteAddress](res) +% oldsize +% sizeof(Cell)),
newsize-oldsize)

View File

@@ -291,8 +291,7 @@ proc forAllChildren(cell: PCell, op: WalkOp) =
var s = cast[PGenericSeq](d)
if s != nil:
for i in 0..s.len-1:
forAllChildrenAux(cast[pointer](d +% i *% cell.typ.base.size +%
GenericSeqSize), cell.typ.base, op)
forAllChildrenAux(cast[pointer](d +% align(GenericSeqSize, cell.typ.base.align) +% i *% cell.typ.base.size), cell.typ.base, op)
else: discard
{.push stackTrace: off, profiler:off.}
@@ -359,7 +358,7 @@ proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
# `newObj` already uses locks, so no need for them here.
let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
let size = addInt(align(GenericSeqSize, typ.base.align), mulInt(len, typ.base.size))
result = newObj(typ, size)
cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len
@@ -378,11 +377,13 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
var elemSize = 1
if ol.typ.kind != tyString: elemSize = ol.typ.base.size
var elemSize, elemAlign = 1
if ol.typ.kind != tyString:
elemSize = ol.typ.base.size
elemAlign = ol.typ.base.align
incTypeSize ol.typ, newsize
var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
var oldsize = align(GenericSeqSize, elemAlign) + cast[PGenericSeq](old).len*elemSize
copyMem(res, ol, oldsize + sizeof(Cell))
zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)),
newsize-oldsize)

View File

@@ -78,8 +78,11 @@ template decTypeSize(cell, t) =
when defined(nimTypeNames):
if t.kind in {tyString, tySequence}:
let cap = cast[PGenericSeq](cellToUsr(cell)).space
let size = if t.kind == tyString: cap+1+GenericSeqSize
else: cap * t.base.size + GenericSeqSize
let size =
if t.kind == tyString:
cap + 1 + GenericSeqSize
else:
align(GenericSeqSize, t.base.align) + cap * t.base.size
atomicDec t.sizes, size+sizeof(Cell)
else:
atomicDec t.sizes, t.base.size+sizeof(Cell)

View File

@@ -259,8 +259,7 @@ proc forAllChildren(cell: PCell, op: WalkOp) =
var s = cast[PGenericSeq](d)
if s != nil:
for i in 0..s.len-1:
forAllChildrenAux(cast[pointer](d +% i *% cell.typ.base.size +%
GenericSeqSize), cell.typ.base, op)
forAllChildrenAux(cast[pointer](d +% align(GenericSeqSize, cell.typ.base.align) +% i *% cell.typ.base.size), cell.typ.base, op)
else: discard
proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
@@ -308,14 +307,14 @@ when not defined(nimSeqsV2):
{.push overflowChecks: on.}
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
# `newObj` already uses locks, so no need for them here.
let size = len * typ.base.size + GenericSeqSize
let size = align(GenericSeqSize, typ.base.align) + len * typ.base.size
result = newObj(typ, size)
cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len
when defined(memProfiler): nimProfile(size)
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
let size = len * typ.base.size + GenericSeqSize
let size = align(GenericSeqSize, typ.base.align) + len * typ.base.size
result = newObj(typ, size)
cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len
@@ -329,11 +328,13 @@ when not defined(nimSeqsV2):
gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
var elemSize = 1
if ol.typ.kind != tyString: elemSize = ol.typ.base.size
var elemSize, elemAlign = 1
if ol.typ.kind != tyString:
elemSize = ol.typ.base.size
elemAlign = ol.typ.base.align
incTypeSize ol.typ, newsize
var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
var oldsize = align(GenericSeqSize, elemAlign) + cast[PGenericSeq](old).len*elemSize
copyMem(res, ol, oldsize + sizeof(Cell))
zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)),
newsize-oldsize)

View File

@@ -321,7 +321,7 @@ proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
{.push overflowChecks: on.}
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
let size = roundup(len * typ.base.size + GenericSeqSize, MemAlign)
let size = roundup(align(GenericSeqSize, typ.base.align) + len * typ.base.size, MemAlign)
result = rawNewSeq(tlRegion, typ, size)
zeroMem(result, size)
cast[PGenericSeq](result).len = len
@@ -348,7 +348,8 @@ proc growObj(regionUnused: var MemRegion; old: pointer, newsize: int): pointer =
result = rawNewSeq(sh.region[], typ,
roundup(newsize, MemAlign))
let elemSize = if typ.kind == tyString: 1 else: typ.base.size
let oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
let elemAlign = if typ.kind == tyString: 1 else: typ.base.align
let oldsize = align(GenericSeqSize, elemAlign) + cast[PGenericSeq](old).len*elemSize
zeroMem(result +! oldsize, newsize-oldsize)
copyMem(result, old, oldsize)
dealloc(sh.region[], old, roundup(oldsize, MemAlign))

View File

@@ -90,6 +90,7 @@ type
when defined(gcHooks):
head*: pointer
size*: int
align*: int
kind: TNimKind
flags: set[TNimTypeFlag]
base*: ptr TNimType

View File

@@ -1,4 +1,6 @@
proc boehmGCinit {.importc: "GC_init", boehmGC.}
proc boehmGC_disable {.importc: "GC_disable", boehmGC.}
proc boehmGC_enable {.importc: "GC_enable", boehmGC.}
@@ -96,6 +98,7 @@ proc initGC() =
proc boehmgc_finalizer(obj: pointer, typedFinalizer: (proc(x: pointer) {.cdecl.})) =
typedFinalizer(obj)
proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
if ntfNoRefs in typ.flags: result = allocAtomic(size)
else: result = alloc(size)
@@ -103,7 +106,7 @@ proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
boehmRegisterFinalizer(result, boehmgc_finalizer, typ.finalizer, nil, nil)
{.push overflowChecks: on.}
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
result = newObj(typ, len * typ.base.size + GenericSeqSize)
result = newObj(typ, align(GenericSeqSize, typ.base.align) + len * typ.base.size)
cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len
{.pop.}
@@ -135,4 +138,3 @@ proc deallocOsPages(r: var MemRegion) {.inline.} = discard
proc deallocOsPages() {.inline.} = discard
include "system/cellsets"

View File

@@ -101,28 +101,32 @@ proc newObjNoInit(typ: PNimType, size: int): pointer =
writebarrierptr(addr(result), newObj(typ, size))
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
writebarrierptr(addr(result), newObj(typ, len * typ.base.size + GenericSeqSize))
writebarrierptr(addr(result), newObj(typ, align(GenericSeqSize, typ.base.align) + len * typ.base.size))
cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len
cast[PGenericSeq](result).elemSize = typ.base.size
cast[PGenericSeq](result).elemAlign = typ.base.align
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
writebarrierptr(addr(result), newSeq(typ, len))
proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} =
result = newObj(typ, cap * typ.base.size + GenericSeqSize)
result = newObj(typ, align(GenericSeqSize, typ.base.align) + cap * typ.base.size)
cast[PGenericSeq](result).len = 0
cast[PGenericSeq](result).reserved = cap
cast[PGenericSeq](result).elemSize = typ.base.size
cast[PGenericSeq](result).elemAlign = typ.base.align
proc typedMemMove(dest: pointer, src: pointer, size: uint) {.importc: "typedmemmove", dynlib: goLib.}
proc growObj(old: pointer, newsize: int): pointer =
# the Go GC doesn't have a realloc
let old = cast[PGenericSeq](old)
var metadataOld = cast[PGenericSeq](old)
if metadataOld.elemSize == 0:
metadataOld.elemSize = 1
let oldsize = cast[PGenericSeq](old).len * cast[PGenericSeq](old).elemSize + GenericSeqSize
let oldsize = align(GenericSeqSize, old.elemAlign) + old.len * old.elemSize
writebarrierptr(addr(result), goMalloc(newsize.uint))
typedMemMove(result, old, oldsize.uint)
@@ -149,4 +153,3 @@ proc alloc0(r: var MemRegion, size: int): pointer =
proc dealloc(r: var MemRegion, p: pointer) = dealloc(p)
proc deallocOsPages(r: var MemRegion) {.inline.} = discard
proc deallocOsPages() {.inline.} = discard

View File

@@ -21,7 +21,7 @@ proc newObjNoInit(typ: PNimType, size: int): pointer =
{.push overflowChecks: on.}
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
result = newObj(typ, len * typ.base.size + GenericSeqSize)
result = newObj(typ, align(GenericSeqSize, typ.align) + len * typ.base.size)
cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len
{.pop.}

View File

@@ -46,7 +46,7 @@ const
PageSize = 1 shl PageShift
PageMask = PageSize-1
MemAlign = 8 # also minimal allocatable memory block
MemAlign = 16 # also minimal allocatable memory block
BitsPerPage = PageSize div MemAlign
UnitsPerPage = BitsPerPage div (sizeof(int)*8)
@@ -108,7 +108,7 @@ when not declared(nimNewSeqOfCap) and not defined(nimSeqsV2):
let s = cap * typ.base.size # newStr already adds GenericSeqSize
result = newStr(typ, s, ntfNoRefs notin typ.base.flags)
else:
let s = cap * typ.base.size + GenericSeqSize
let s = align(GenericSeqSize, typ.base.align) + cap * typ.base.size
when declared(newObjNoInit):
result = if ntfNoRefs in typ.base.flags: newObjNoInit(typ, s) else: newObj(typ, s)
else:

View File

@@ -172,7 +172,7 @@ when not defined(useNimRtl):
template payloadPtr(x: untyped): untyped = cast[PGenericSeq](x).p
else:
const payloadOffset = GenericSeqSize
const payloadOffset = GenericSeqSize ## the payload offset always depends on the alignment of the member type.
template payloadPtr(x: untyped): untyped = x
proc reprSequence(result: var string, p: pointer, typ: PNimType,
@@ -185,7 +185,7 @@ when not defined(useNimRtl):
var bs = typ.base.size
for i in 0..cast[PGenericSeq](p).len-1:
if i > 0: add result, ", "
reprAux(result, cast[pointer](cast[ByteAddress](payloadPtr(p)) + payloadOffset + i*bs),
reprAux(result, cast[pointer](cast[ByteAddress](payloadPtr(p)) + align(payloadOffset, typ.align) + i*bs),
typ.base, cl)
add result, "]"

View File

@@ -31,27 +31,27 @@ const nimSeqVersion {.core.} = 2
# XXX make code memory safe for overflows in '*'
proc newSeqPayload(cap, elemSize: int): pointer {.compilerRtl, raises: [].} =
proc newSeqPayload(cap, elemSize, elemAlign: int): pointer {.compilerRtl, raises: [].} =
# we have to use type erasure here as Nim does not support generic
# compilerProcs. Oh well, this will all be inlined anyway.
if cap > 0:
var p = cast[ptr NimSeqPayloadBase](allocShared0(cap * elemSize + sizeof(NimSeqPayloadBase)))
var p = cast[ptr NimSeqPayloadBase](allocShared0(align(sizeof(NimSeqPayloadBase), elemAlign) + cap * elemSize))
p.cap = cap
result = p
else:
result = nil
proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize: int): pointer {.
proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {.
noSideEffect, raises: [].} =
{.noSideEffect.}:
template `+!`(p: pointer, s: int): pointer =
cast[pointer](cast[int](p) +% s)
const headerSize = sizeof(NimSeqPayloadBase)
let headerSize = align(sizeof(NimSeqPayloadBase), elemAlign)
if addlen <= 0:
result = p
elif p == nil:
result = newSeqPayload(len+addlen, elemSize)
result = newSeqPayload(len+addlen, elemSize, elemAlign)
else:
# Note: this means we cannot support things that have internal pointers as
# they get reallocated here. This needs to be documented clearly.
@@ -87,7 +87,7 @@ proc grow*[T](x: var seq[T]; newLen: Natural; value: T) =
if newLen <= oldLen: return
var xu = cast[ptr NimSeqV2[T]](addr x)
if xu.p == nil or xu.p.cap < newLen:
xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, newLen - oldLen, sizeof(T)))
xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, newLen - oldLen, sizeof(T), alignof(T)))
xu.len = newLen
for i in oldLen .. newLen-1:
xu.p.data[i] = value
@@ -102,7 +102,7 @@ proc add*[T](x: var seq[T]; value: sink T) {.magic: "AppendSeqElem", noSideEffec
let oldLen = x.len
var xu = cast[ptr NimSeqV2[T]](addr x)
if xu.p == nil or xu.p.cap < oldLen+1:
xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, 1, sizeof(T)))
xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, 1, sizeof(T), alignof(T)))
xu.len = oldLen+1
# .nodestroy means `xu.p.data[oldLen] = value` is compiled into a
# copyMem(). This is fine as know by construction that
@@ -119,5 +119,5 @@ proc setLen[T](s: var seq[T], newlen: Natural) =
if newlen <= oldLen: return
var xu = cast[ptr NimSeqV2[T]](addr s)
if xu.p == nil or xu.p.cap < newlen:
xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, newlen - oldLen, sizeof(T)))
xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, newlen - oldLen, sizeof(T), alignof(T)))
xu.len = newlen

View File

@@ -245,7 +245,7 @@ proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} =
# ----------------- sequences ----------------------------------------------
proc incrSeq(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerproc.} =
proc incrSeq(seq: PGenericSeq, elemSize, elemAlign: int): PGenericSeq {.compilerproc.} =
# increments the length by one:
# this is needed for supporting ``add``;
#
@@ -255,18 +255,16 @@ proc incrSeq(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerproc.} =
result = seq
if result.len >= result.space:
let r = resize(result.space)
result = cast[PGenericSeq](growObj(result, elemSize * r +
GenericSeqSize))
result = cast[PGenericSeq](growObj(result, align(GenericSeqSize, elemAlign) + elemSize * r))
result.reserved = r
inc(result.len)
proc incrSeqV2(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerproc.} =
proc incrSeqV2(seq: PGenericSeq, elemSize, elemAlign: int): PGenericSeq {.compilerproc.} =
# incrSeq version 2
result = seq
if result.len >= result.space:
let r = resize(result.space)
result = cast[PGenericSeq](growObj(result, elemSize * r +
GenericSeqSize))
result = cast[PGenericSeq](growObj(result, align(GenericSeqSize, elemAlign) + elemSize * r))
result.reserved = r
template `+!`(p: pointer, s: int): pointer =
@@ -283,21 +281,19 @@ proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerproc.} =
when defined(nimIncrSeqV3):
result = cast[PGenericSeq](newSeq(typ, r))
result.len = s.len
copyMem(result +! GenericSeqSize, s +! GenericSeqSize, s.len * typ.base.size)
copyMem(result +! align(GenericSeqSize, typ.base.align), s +! align(GenericSeqSize, typ.base.align), s.len * typ.base.size)
# since we steal the content from 's', it's crucial to set s's len to 0.
s.len = 0
else:
result = cast[PGenericSeq](growObj(result, typ.base.size * r +
GenericSeqSize))
result = cast[PGenericSeq](growObj(result, align(GenericSeqSize, typ.base.align) + typ.base.size * r))
result.reserved = r
proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
proc setLengthSeq(seq: PGenericSeq, elemSize, elemAlign, newLen: int): PGenericSeq {.
compilerRtl, inl.} =
result = seq
if result.space < newLen:
let r = max(resize(result.space), newLen)
result = cast[PGenericSeq](growObj(result, elemSize * r +
GenericSeqSize))
result = cast[PGenericSeq](growObj(result, align(GenericSeqSize, elemAlign) + elemSize * r))
result.reserved = r
elif newLen < result.len:
# we need to decref here, otherwise the GC leaks!
@@ -307,8 +303,7 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
when false: # compileOption("gc", "v2"):
for i in newLen..result.len-1:
let len0 = gch.tempStack.len
forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +%
GenericSeqSize +% (i*%elemSize)),
forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +% align(GenericSeqSize, elemAlign) +% (i*%elemSize)),
extGetCellType(result).base, waPush)
let len1 = gch.tempStack.len
for i in len0 ..< len1:
@@ -318,7 +313,7 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
if ntfNoRefs notin extGetCellType(result).base.flags:
for i in newLen..result.len-1:
forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +%
GenericSeqSize +% (i*%elemSize)),
align(GenericSeqSize, elemAlign) +% (i*%elemSize)),
extGetCellType(result).base, waZctDecRef)
# XXX: zeroing out the memory can still result in crashes if a wiped-out
@@ -327,7 +322,7 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
# presence of user defined destructors, the user will expect the cell to be
# "destroyed" thus creating the same problem. We can destroy the cell in the
# finalizer of the sequence, but this makes destruction non-deterministic.
zeroMem(cast[pointer](cast[ByteAddress](result) +% GenericSeqSize +%
zeroMem(cast[pointer](cast[ByteAddress](result) +% align(GenericSeqSize, elemAlign) +%
(newLen*%elemSize)), (result.len-%newLen) *% elemSize)
result.len = newLen
@@ -339,10 +334,11 @@ proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
else:
when defined(nimIncrSeqV3):
let elemSize = typ.base.size
let elemAlign = typ.base.align
if s.space < newLen:
let r = max(resize(s.space), newLen)
result = cast[PGenericSeq](newSeq(typ, r))
copyMem(result +! GenericSeqSize, s +! GenericSeqSize, s.len * elemSize)
copyMem(result +! align(GenericSeqSize, elemAlign), s +! align(GenericSeqSize, elemAlign), s.len * elemSize)
# since we steal the content from 's', it's crucial to set s's len to 0.
s.len = 0
elif newLen < s.len:
@@ -354,7 +350,7 @@ proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
if ntfNoRefs notin typ.base.flags:
for i in newLen..result.len-1:
forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +%
GenericSeqSize +% (i*%elemSize)),
align(GenericSeqSize, elemAlign) +% (i*%elemSize)),
extGetCellType(result).base, waZctDecRef)
# XXX: zeroing out the memory can still result in crashes if a wiped-out
@@ -363,7 +359,7 @@ proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
# presence of user defined destructors, the user will expect the cell to be
# "destroyed" thus creating the same problem. We can destroy the cell in the
# finalizer of the sequence, but this makes destruction non-deterministic.
zeroMem(cast[pointer](cast[ByteAddress](result) +% GenericSeqSize +%
zeroMem(cast[pointer](cast[ByteAddress](result) +% align(GenericSeqSize, elemAlign) +%
(newLen*%elemSize)), (result.len-%newLen) *% elemSize)
else:
result = s

View File

@@ -0,0 +1,17 @@
discard """
disabled: true
"""
# does not yet work
{.passC: "-march=native".}
type
m256d {.importc: "__m256d", header: "immintrin.h".} = object
proc set1(x: float): m256d {.importc: "_mm256_set1_pd", header: "immintrin.h".}
for _ in 1..1000:
var x = newSeq[m256d](1)
x[0] = set1(1.0) # test if operation causes segfault
doAssert (cast[uint](x[0].addr) and 31) == 0