refactor sizealignoffset (#12077)
* small refactoring * refactor computeObjectOffsetFoldFunction with AccumObject * refactor packed object offstes fold function * refactor compute union object offsets fold function * merge normal/packed object offset fold function * compiletime offsetof in c++ inheritance objects * enable c++ inheritance offsetof tests * correct alignment for big sets/enums on weird 32bit platforms * uncomputedSize -> unknownSize * workaround for travis * fixes win32 alignment problems
This commit is contained in:
parent
42d2e68bca
commit
21fc8b4d4d
6 changed files with 198 additions and 230 deletions
|
|
@ -899,6 +899,7 @@ type
|
||||||
size*: BiggestInt # the size of the type in bytes
|
size*: BiggestInt # the size of the type in bytes
|
||||||
# -1 means that the size is unkwown
|
# -1 means that the size is unkwown
|
||||||
align*: int16 # the type's alignment requirements
|
align*: int16 # the type's alignment requirements
|
||||||
|
paddingAtEnd*: int16 #
|
||||||
lockLevel*: TLockLevel # lock level as required for deadlock checking
|
lockLevel*: TLockLevel # lock level as required for deadlock checking
|
||||||
loc*: TLoc
|
loc*: TLoc
|
||||||
typeInst*: PType # for generic instantiations the tyGenericInst that led to this
|
typeInst*: PType # for generic instantiations the tyGenericInst that led to this
|
||||||
|
|
|
||||||
|
|
@ -719,3 +719,13 @@ proc `$`*(c: IdeCmd): string =
|
||||||
of ideOutline: "outline"
|
of ideOutline: "outline"
|
||||||
of ideKnown: "known"
|
of ideKnown: "known"
|
||||||
of ideMsg: "msg"
|
of ideMsg: "msg"
|
||||||
|
|
||||||
|
proc floatInt64Align*(conf: ConfigRef): int16 =
|
||||||
|
## Returns either 4 or 8 depending on reasons.
|
||||||
|
if conf.target.targetCPU == cpuI386:
|
||||||
|
#on Linux/BSD i386, double are aligned to 4bytes (except with -malign-double)
|
||||||
|
if conf.target.targetOS != osWindows:
|
||||||
|
# on i386 for all known POSIX systems, 64bits ints are aligned
|
||||||
|
# to 4bytes (except with -malign-double)
|
||||||
|
return 4
|
||||||
|
return 8
|
||||||
|
|
|
||||||
|
|
@ -816,12 +816,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||||
if sym.typ == nil: invalidPragma(c, it)
|
if sym.typ == nil: invalidPragma(c, it)
|
||||||
var size = expectIntLit(c, it)
|
var size = expectIntLit(c, it)
|
||||||
case size
|
case size
|
||||||
of 1, 2, 4, 8:
|
of 1, 2, 4:
|
||||||
sym.typ.size = size
|
sym.typ.size = size
|
||||||
if size == 8 and c.config.target.targetCPU == cpuI386:
|
sym.typ.align = int16 size
|
||||||
sym.typ.align = 4
|
of 8:
|
||||||
else:
|
sym.typ.size = 8
|
||||||
sym.typ.align = int16(size)
|
sym.typ.align = floatInt64Align(c.config)
|
||||||
else:
|
else:
|
||||||
localError(c.config, it.info, "size may only be 1, 2, 4 or 8")
|
localError(c.config, it.info, "size may only be 1, 2, 4 or 8")
|
||||||
of wNodecl:
|
of wNodecl:
|
||||||
|
|
|
||||||
|
|
@ -1819,13 +1819,8 @@ proc setMagicType(conf: ConfigRef; m: PSym, kind: TTypeKind, size: int) =
|
||||||
|
|
||||||
# FIXME: proper support for clongdouble should be added.
|
# FIXME: proper support for clongdouble should be added.
|
||||||
# long double size can be 8, 10, 12, 16 bytes depending on platform & compiler
|
# long double size can be 8, 10, 12, 16 bytes depending on platform & compiler
|
||||||
if conf.target.targetCPU == cpuI386 and size == 8:
|
if kind in {tyFloat64, tyFloat, tyInt, tyUInt, tyInt64, tyUInt64} and size == 8:
|
||||||
#on Linux/BSD i386, double are aligned to 4bytes (except with -malign-double)
|
m.typ.align = int16(conf.floatInt64Align)
|
||||||
if conf.target.targetOS != osWindows:
|
|
||||||
if kind in {tyFloat64, tyFloat, tyInt, tyUInt, tyInt64, tyUInt64}:
|
|
||||||
# on i386 for all known POSIX systems, 64bits ints are aligned
|
|
||||||
# to 4bytes (except with -malign-double)
|
|
||||||
m.typ.align = 4
|
|
||||||
|
|
||||||
proc setMagicIntegral(conf: ConfigRef; m: PSym, kind: TTypeKind, size: int) =
|
proc setMagicIntegral(conf: ConfigRef; m: PSym, kind: TTypeKind, size: int) =
|
||||||
setMagicType(conf, m, kind, size)
|
setMagicType(conf, m, kind, size)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
#
|
#
|
||||||
## code owner: Arne Döring
|
## code owner: Arne Döring
|
||||||
## e-mail: arne.doering@gmx.net
|
## e-mail: arne.doering@gmx.net
|
||||||
|
## included from types.nim
|
||||||
|
|
||||||
proc align(address, alignment: BiggestInt): BiggestInt =
|
proc align(address, alignment: BiggestInt): BiggestInt =
|
||||||
result = (address + (alignment - 1)) and not (alignment - 1)
|
result = (address + (alignment - 1)) and not (alignment - 1)
|
||||||
|
|
@ -14,7 +15,6 @@ proc align(address, alignment: BiggestInt): BiggestInt =
|
||||||
proc align(address, alignment: int): int =
|
proc align(address, alignment: int): int =
|
||||||
result = (address + (alignment - 1)) and not (alignment - 1)
|
result = (address + (alignment - 1)) and not (alignment - 1)
|
||||||
|
|
||||||
|
|
||||||
const
|
const
|
||||||
## a size is concidered "unknown" when it is an imported type from C
|
## a size is concidered "unknown" when it is an imported type from C
|
||||||
## or C++.
|
## or C++.
|
||||||
|
|
@ -39,6 +39,13 @@ proc inc(arg: var OffsetAccum; value: int) =
|
||||||
else:
|
else:
|
||||||
arg.offset += value
|
arg.offset += value
|
||||||
|
|
||||||
|
proc alignmentMax(a,b: int): int =
|
||||||
|
if unlikely(a == szIllegalRecursion or b == szIllegalRecursion): raiseIllegalTypeRecursion()
|
||||||
|
if a == szUnknownSize or b == szUnknownSize:
|
||||||
|
szUnknownSize
|
||||||
|
else:
|
||||||
|
max(a,b)
|
||||||
|
|
||||||
proc align(arg: var OffsetAccum; value: int) =
|
proc align(arg: var OffsetAccum; value: int) =
|
||||||
if unlikely(value == szIllegalRecursion): raiseIllegalTypeRecursion()
|
if unlikely(value == szIllegalRecursion): raiseIllegalTypeRecursion()
|
||||||
if value == szUnknownSize or arg.maxAlign == szUnknownSize or arg.offset == szUnknownSize:
|
if value == szUnknownSize or arg.maxAlign == szUnknownSize or arg.offset == szUnknownSize:
|
||||||
|
|
@ -48,11 +55,22 @@ proc align(arg: var OffsetAccum; value: int) =
|
||||||
arg.maxAlign = max(value, arg.maxAlign)
|
arg.maxAlign = max(value, arg.maxAlign)
|
||||||
arg.offset = align(arg.offset, value)
|
arg.offset = align(arg.offset, value)
|
||||||
|
|
||||||
proc finish(arg: var OffsetAccum) =
|
proc mergeBranch(arg: var OffsetAccum; value: OffsetAccum) =
|
||||||
if arg.maxAlign == szUnknownSize or arg.offset == szUnknownSize:
|
if value.maxAlign == szUnknownSize or arg.maxAlign == szUnknownSize or
|
||||||
|
value.offset == szUnknownSize or arg.offset == szUnknownSize:
|
||||||
|
arg.maxAlign = szUnknownSize
|
||||||
arg.offset = szUnknownSize
|
arg.offset = szUnknownSize
|
||||||
else:
|
else:
|
||||||
arg.offset = align(arg.offset, arg.maxAlign)
|
arg.offset = max(arg.offset, value.offset)
|
||||||
|
arg.maxAlign = max(arg.maxAlign, value.maxAlign)
|
||||||
|
|
||||||
|
proc finish(arg: var OffsetAccum): int =
|
||||||
|
if arg.maxAlign == szUnknownSize or arg.offset == szUnknownSize:
|
||||||
|
result = szUnknownSize
|
||||||
|
arg.offset = szUnknownSize
|
||||||
|
else:
|
||||||
|
result = align(arg.offset, arg.maxAlign) - arg.offset
|
||||||
|
arg.offset += result
|
||||||
|
|
||||||
proc computeSizeAlign(conf: ConfigRef; typ: PType)
|
proc computeSizeAlign(conf: ConfigRef; typ: PType)
|
||||||
|
|
||||||
|
|
@ -93,154 +111,69 @@ proc setOffsetsToUnknown(n: PNode) =
|
||||||
for i in 0 ..< safeLen(n):
|
for i in 0 ..< safeLen(n):
|
||||||
setOffsetsToUnknown(n[i])
|
setOffsetsToUnknown(n[i])
|
||||||
|
|
||||||
proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode,
|
proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, packed: bool, accum: var OffsetAccum): void =
|
||||||
initialOffset: BiggestInt): tuple[offset, align: BiggestInt] =
|
|
||||||
## ``offset`` is the offset within the object, after the node has been written, no padding bytes added
|
## ``offset`` is the offset within the object, after the node has been written, no padding bytes added
|
||||||
## ``align`` maximum alignment from all sub nodes
|
## ``align`` maximum alignment from all sub nodes
|
||||||
assert n != nil
|
assert n != nil
|
||||||
if n.typ != nil and n.typ.size == szIllegalRecursion:
|
if n.typ != nil and n.typ.size == szIllegalRecursion:
|
||||||
result.offset = szIllegalRecursion
|
raiseIllegalTypeRecursion()
|
||||||
result.align = szIllegalRecursion
|
|
||||||
return
|
|
||||||
|
|
||||||
result.align = 1
|
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkRecCase:
|
of nkRecCase:
|
||||||
assert(n.sons[0].kind == nkSym)
|
assert(n.sons[0].kind == nkSym)
|
||||||
let (kindOffset, kindAlign) = computeObjectOffsetsFoldFunction(conf, n.sons[0], initialOffset)
|
computeObjectOffsetsFoldFunction(conf, n.sons[0], packed, accum)
|
||||||
|
var maxChildAlign: int = if accum.offset == szUnknownSize: szUnknownSize else: 1
|
||||||
var maxChildAlign: BiggestInt = if initialOffset == szUnknownSize: szUnknownSize else: 0
|
if not packed:
|
||||||
for i in 1 ..< sonsLen(n):
|
for i in 1 ..< sonsLen(n):
|
||||||
let child = n.sons[i]
|
let child = n.sons[i]
|
||||||
case child.kind
|
case child.kind
|
||||||
of nkOfBranch, nkElse:
|
of nkOfBranch, nkElse:
|
||||||
# offset parameter cannot be known yet, it needs to know the alignment first
|
# offset parameter cannot be known yet, it needs to know the alignment first
|
||||||
let align = computeSubObjectAlign(conf, n.sons[i].lastSon)
|
let align = int(computeSubObjectAlign(conf, n.sons[i].lastSon))
|
||||||
if align == szIllegalRecursion:
|
maxChildAlign = alignmentMax(maxChildAlign, align)
|
||||||
result.offset = szIllegalRecursion
|
|
||||||
result.align = szIllegalRecursion
|
|
||||||
return
|
|
||||||
if align == szUnknownSize or maxChildAlign == szUnknownSize:
|
|
||||||
maxChildAlign = szUnknownSize
|
|
||||||
else:
|
else:
|
||||||
maxChildAlign = max(maxChildAlign, align)
|
internalError(conf, "computeObjectOffsetsFoldFunction(record case branch)")
|
||||||
else:
|
|
||||||
internalError(conf, "computeObjectOffsetsFoldFunction(record case branch)")
|
|
||||||
if maxChildAlign == szUnknownSize:
|
if maxChildAlign == szUnknownSize:
|
||||||
setOffsetsToUnknown(n)
|
setOffsetsToUnknown(n)
|
||||||
result.align = szUnknownSize
|
accum.offset = szUnknownSize
|
||||||
result.offset = szUnknownSize
|
accum.maxAlign = szUnknownSize
|
||||||
else:
|
else:
|
||||||
# the union neds to be aligned first, before the offsets can be assigned
|
# the union neds to be aligned first, before the offsets can be assigned
|
||||||
let kindUnionOffset = align(kindOffset, maxChildAlign)
|
accum.align(maxChildAlign)
|
||||||
var maxChildOffset: BiggestInt = 0
|
let accumRoot = accum # copy, because each branch should start af the same offset
|
||||||
for i in 1 ..< sonsLen(n):
|
for i in 1 ..< sonsLen(n):
|
||||||
let (offset, align) = computeObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset)
|
var branchAccum = accumRoot
|
||||||
maxChildOffset = max(maxChildOffset, offset)
|
computeObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, packed, branchAccum)
|
||||||
result.align = max(kindAlign, maxChildAlign)
|
accum.mergeBranch(branchAccum)
|
||||||
result.offset = maxChildOffset
|
|
||||||
of nkRecList:
|
of nkRecList:
|
||||||
result.align = 1 # maximum of all member alignments
|
|
||||||
var offset = initialOffset
|
|
||||||
for i, child in n.sons:
|
for i, child in n.sons:
|
||||||
let (newOffset, align) = computeObjectOffsetsFoldFunction(conf, child, offset)
|
computeObjectOffsetsFoldFunction(conf, child, packed, accum)
|
||||||
if newOffset == szIllegalRecursion:
|
|
||||||
result.offset = szIllegalRecursion
|
|
||||||
result.align = szIllegalRecursion
|
|
||||||
return
|
|
||||||
elif newOffset == szUnknownSize or offset == szUnknownSize:
|
|
||||||
# if anything is unknown, the rest becomes unknown as well
|
|
||||||
offset = szUnknownSize
|
|
||||||
result.align = szUnknownSize
|
|
||||||
else:
|
|
||||||
offset = newOffset
|
|
||||||
result.align = max(result.align, align)
|
|
||||||
# final alignment
|
|
||||||
if offset == szUnknownSize:
|
|
||||||
result.offset = szUnknownSize
|
|
||||||
else:
|
|
||||||
result.offset = align(offset, result.align)
|
|
||||||
of nkSym:
|
of nkSym:
|
||||||
var size = szUnknownSize
|
var size = szUnknownSize
|
||||||
var align = szUnknownSize
|
var align = szUnknownSize
|
||||||
if n.sym.bitsize == 0: # 0 represents bitsize not set
|
if n.sym.bitsize == 0: # 0 represents bitsize not set
|
||||||
computeSizeAlign(conf, n.sym.typ)
|
computeSizeAlign(conf, n.sym.typ)
|
||||||
size = n.sym.typ.size.int
|
size = n.sym.typ.size.int
|
||||||
align = n.sym.typ.align.int
|
align = if packed: 1 else: n.sym.typ.align.int
|
||||||
|
accum.align(align)
|
||||||
result.align = align
|
n.sym.offset = accum.offset
|
||||||
if initialOffset == szUnknownSize or size == szUnknownSize or align == szUnknownSize:
|
accum.inc(size)
|
||||||
n.sym.offset = szUnknownSize
|
|
||||||
result.offset = szUnknownSize
|
|
||||||
else:
|
|
||||||
n.sym.offset = align(initialOffset, align).int
|
|
||||||
result.offset = n.sym.offset + n.sym.typ.size
|
|
||||||
else:
|
else:
|
||||||
result.align = szUnknownSize
|
accum.maxAlign = szUnknownSize
|
||||||
result.offset = szUnknownSize
|
accum.offset = szUnknownSize
|
||||||
|
|
||||||
proc computePackedObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOffset: BiggestInt, debug: bool): BiggestInt =
|
proc computeUnionObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode; accum: var OffsetAccum) =
|
||||||
## ``result`` is the offset within the object, after the node has been written, no padding bytes added
|
## ``accum.offset`` will the offset from the larget member of the union.
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkRecCase:
|
of nkRecCase:
|
||||||
assert(n.sons[0].kind == nkSym)
|
accum.offset = szUnknownSize
|
||||||
let kindOffset = computePackedObjectOffsetsFoldFunction(conf, n.sons[0], initialOffset, debug)
|
accum.maxAlign = szUnknownSize
|
||||||
# the union neds to be aligned first, before the offsets can be assigned
|
|
||||||
let kindUnionOffset = kindOffset
|
|
||||||
var maxChildOffset: BiggestInt = kindUnionOffset
|
|
||||||
for i in 1 ..< sonsLen(n):
|
|
||||||
let offset = computePackedObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset, debug)
|
|
||||||
if offset == szIllegalRecursion:
|
|
||||||
return szIllegalRecursion
|
|
||||||
if offset == szUnknownSize or maxChildOffset == szUnknownSize:
|
|
||||||
maxChildOffset = szUnknownSize
|
|
||||||
else:
|
|
||||||
maxChildOffset = max(maxChildOffset, offset)
|
|
||||||
result = maxChildOffset
|
|
||||||
of nkRecList:
|
|
||||||
result = initialOffset
|
|
||||||
for i, child in n.sons:
|
|
||||||
result = computePackedObjectOffsetsFoldFunction(conf, child, result, debug)
|
|
||||||
if result == szIllegalRecursion:
|
|
||||||
break
|
|
||||||
of nkSym:
|
|
||||||
var size = szUnknownSize
|
|
||||||
if n.sym.bitsize == 0:
|
|
||||||
computeSizeAlign(conf, n.sym.typ)
|
|
||||||
size = n.sym.typ.size.int
|
|
||||||
|
|
||||||
if initialOffset == szUnknownSize or size == szUnknownSize:
|
|
||||||
n.sym.offset = szUnknownSize
|
|
||||||
result = szUnknownSize
|
|
||||||
else:
|
|
||||||
n.sym.offset = int(initialOffset)
|
|
||||||
result = initialOffset + n.sym.typ.size
|
|
||||||
else:
|
|
||||||
result = szUnknownSize
|
|
||||||
|
|
||||||
proc computeUnionObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, debug: bool): tuple[offset, align: BiggestInt] =
|
|
||||||
## ``result`` is the offset from the larget member of the union.
|
|
||||||
case n.kind
|
|
||||||
of nkRecCase:
|
|
||||||
result.offset = szUnknownSize
|
|
||||||
result.align = szUnknownSize
|
|
||||||
localError(conf, n.info, "Illegal use of ``case`` in union type.")
|
localError(conf, n.info, "Illegal use of ``case`` in union type.")
|
||||||
#internalError(conf, "Illegal use of ``case`` in union type.")
|
|
||||||
of nkRecList:
|
of nkRecList:
|
||||||
var maxChildOffset: BiggestInt = 0
|
let accumRoot = accum # copy, because each branch should start af the same offset
|
||||||
for i, child in n.sons:
|
for i, child in n.sons:
|
||||||
let (offset, align) = computeUnionObjectOffsetsFoldFunction(conf, child, debug)
|
var branchAccum = accumRoot
|
||||||
if offset == szIllegalRecursion or align == szIllegalRecursion:
|
computeUnionObjectOffsetsFoldFunction(conf, child, branchAccum)
|
||||||
result.offset = szIllegalRecursion
|
accum.mergeBranch(branchAccum)
|
||||||
result.align = szIllegalRecursion
|
|
||||||
elif offset == szUnknownSize or align == szUnknownSize:
|
|
||||||
result.offset = szUnknownSize
|
|
||||||
result.align = szUnknownSize
|
|
||||||
else:
|
|
||||||
assert offset != szUncomputedSize
|
|
||||||
assert align != szUncomputedSize
|
|
||||||
result.offset = max(result.offset, offset)
|
|
||||||
result.align = max(result.align, align)
|
|
||||||
of nkSym:
|
of nkSym:
|
||||||
var size = szUnknownSize
|
var size = szUnknownSize
|
||||||
var align = szUnknownSize
|
var align = szUnknownSize
|
||||||
|
|
@ -248,17 +181,12 @@ proc computeUnionObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, debug: boo
|
||||||
computeSizeAlign(conf, n.sym.typ)
|
computeSizeAlign(conf, n.sym.typ)
|
||||||
size = n.sym.typ.size.int
|
size = n.sym.typ.size.int
|
||||||
align = n.sym.typ.align.int
|
align = n.sym.typ.align.int
|
||||||
|
accum.align(align)
|
||||||
result.align = align
|
n.sym.offset = accum.offset
|
||||||
if size == szUnknownSize:
|
accum.inc(size)
|
||||||
n.sym.offset = szUnknownSize
|
|
||||||
result.offset = szUnknownSize
|
|
||||||
else:
|
|
||||||
n.sym.offset = 0
|
|
||||||
result.offset = n.sym.typ.size
|
|
||||||
else:
|
else:
|
||||||
result.offset = szUnknownSize
|
accum.maxAlign = szUnknownSize
|
||||||
result.align = szUnknownSize
|
accum.offset = szUnknownSize
|
||||||
|
|
||||||
proc computeSizeAlign(conf: ConfigRef; typ: PType) =
|
proc computeSizeAlign(conf: ConfigRef; typ: PType) =
|
||||||
## computes and sets ``size`` and ``align`` members of ``typ``
|
## computes and sets ``size`` and ``align`` members of ``typ``
|
||||||
|
|
@ -288,8 +216,7 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
|
||||||
# mark computation in progress
|
# mark computation in progress
|
||||||
typ.size = szIllegalRecursion
|
typ.size = szIllegalRecursion
|
||||||
typ.align = szIllegalRecursion
|
typ.align = szIllegalRecursion
|
||||||
|
typ.paddingAtEnd = 0
|
||||||
var maxAlign, sizeAccum, length: BiggestInt
|
|
||||||
|
|
||||||
var tk = typ.kind
|
var tk = typ.kind
|
||||||
case tk
|
case tk
|
||||||
|
|
@ -299,26 +226,23 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
|
||||||
else:
|
else:
|
||||||
typ.size = conf.target.ptrSize
|
typ.size = conf.target.ptrSize
|
||||||
typ.align = int16(conf.target.ptrSize)
|
typ.align = int16(conf.target.ptrSize)
|
||||||
|
|
||||||
of tyNil:
|
of tyNil:
|
||||||
typ.size = conf.target.ptrSize
|
typ.size = conf.target.ptrSize
|
||||||
typ.align = int16(conf.target.ptrSize)
|
typ.align = int16(conf.target.ptrSize)
|
||||||
|
|
||||||
of tyString:
|
of tyString:
|
||||||
if conf.selectedGC == gcDestructors:
|
if conf.selectedGC == gcDestructors:
|
||||||
typ.size = conf.target.ptrSize * 2
|
typ.size = conf.target.ptrSize * 2
|
||||||
else:
|
else:
|
||||||
typ.size = conf.target.ptrSize
|
typ.size = conf.target.ptrSize
|
||||||
typ.align = int16(conf.target.ptrSize)
|
typ.align = int16(conf.target.ptrSize)
|
||||||
|
|
||||||
of tyCString, tySequence, tyPtr, tyRef, tyVar, tyLent, tyOpenArray:
|
of tyCString, tySequence, tyPtr, tyRef, tyVar, tyLent, tyOpenArray:
|
||||||
let base = typ.lastSon
|
let base = typ.lastSon
|
||||||
if base == typ:
|
if base == typ:
|
||||||
# this is not the correct location to detect ``type A = ptr A``
|
# this is not the correct location to detect ``type A = ptr A``
|
||||||
typ.size = szIllegalRecursion
|
typ.size = szIllegalRecursion
|
||||||
typ.align = szIllegalRecursion
|
typ.align = szIllegalRecursion
|
||||||
|
typ.paddingAtEnd = szIllegalRecursion
|
||||||
return
|
return
|
||||||
|
|
||||||
typ.align = int16(conf.target.ptrSize)
|
typ.align = int16(conf.target.ptrSize)
|
||||||
if typ.kind == tySequence and conf.selectedGC == gcDestructors:
|
if typ.kind == tySequence and conf.selectedGC == gcDestructors:
|
||||||
typ.size = conf.target.ptrSize * 2
|
typ.size = conf.target.ptrSize * 2
|
||||||
|
|
@ -340,12 +264,13 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
|
||||||
computeSizeAlign(conf, base)
|
computeSizeAlign(conf, base)
|
||||||
typ.size = 0
|
typ.size = 0
|
||||||
typ.align = base.align
|
typ.align = base.align
|
||||||
|
|
||||||
of tyEnum:
|
of tyEnum:
|
||||||
if firstOrd(conf, typ) < Zero:
|
if firstOrd(conf, typ) < Zero:
|
||||||
typ.size = 4 # use signed int32
|
typ.size = 4 # use signed int32
|
||||||
typ.align = 4
|
typ.align = 4
|
||||||
else:
|
else:
|
||||||
length = toInt64(lastOrd(conf, typ)) # BUGFIX: use lastOrd!
|
let length = toInt64(lastOrd(conf, typ)) # BUGFIX: use lastOrd!
|
||||||
if length + 1 < `shl`(1, 8):
|
if length + 1 < `shl`(1, 8):
|
||||||
typ.size = 1
|
typ.size = 1
|
||||||
typ.align = 1
|
typ.align = 1
|
||||||
|
|
@ -357,30 +282,37 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
|
||||||
typ.align = 4
|
typ.align = 4
|
||||||
else:
|
else:
|
||||||
typ.size = 8
|
typ.size = 8
|
||||||
typ.align = 8
|
typ.align = int16(conf.floatInt64Align)
|
||||||
of tySet:
|
of tySet:
|
||||||
if typ.sons[0].kind == tyGenericParam:
|
if typ.sons[0].kind == tyGenericParam:
|
||||||
typ.size = szUncomputedSize
|
typ.size = szUncomputedSize
|
||||||
typ.align = szUncomputedSize # in original version this was 1
|
typ.align = szUncomputedSize
|
||||||
else:
|
else:
|
||||||
length = toInt64(lengthOrd(conf, typ.sons[0]))
|
let length = toInt64(lengthOrd(conf, typ.sons[0]))
|
||||||
if length <= 8:
|
if length <= 8:
|
||||||
typ.size = 1
|
typ.size = 1
|
||||||
|
typ.align = 1
|
||||||
elif length <= 16:
|
elif length <= 16:
|
||||||
typ.size = 2
|
typ.size = 2
|
||||||
|
typ.align = 2
|
||||||
elif length <= 32:
|
elif length <= 32:
|
||||||
typ.size = 4
|
typ.size = 4
|
||||||
|
typ.align = 4
|
||||||
elif length <= 64:
|
elif length <= 64:
|
||||||
typ.size = 8
|
typ.size = 8
|
||||||
|
typ.align = int16(conf.floatInt64Align)
|
||||||
elif align(length, 8) mod 8 == 0:
|
elif align(length, 8) mod 8 == 0:
|
||||||
typ.size = align(length, 8) div 8
|
typ.size = align(length, 8) div 8
|
||||||
|
typ.align = int16(conf.floatInt64Align)
|
||||||
else:
|
else:
|
||||||
typ.size = align(length, 8) div 8 + 1
|
typ.size = align(length, 8) div 8 + 1
|
||||||
typ.align = int16(typ.size)
|
typ.align = int16(conf.floatInt64Align)
|
||||||
of tyRange:
|
of tyRange:
|
||||||
computeSizeAlign(conf, typ.sons[0])
|
computeSizeAlign(conf, typ.sons[0])
|
||||||
typ.size = typ.sons[0].size
|
typ.size = typ.sons[0].size
|
||||||
typ.align = typ.sons[0].align
|
typ.align = typ.sons[0].align
|
||||||
|
typ.paddingAtEnd = typ.sons[0].paddingAtEnd
|
||||||
|
|
||||||
of tyTuple:
|
of tyTuple:
|
||||||
try:
|
try:
|
||||||
var accum = OffsetAccum(maxAlign: 1)
|
var accum = OffsetAccum(maxAlign: 1)
|
||||||
|
|
@ -392,112 +324,121 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
|
||||||
let sym = typ.n[i].sym
|
let sym = typ.n[i].sym
|
||||||
sym.offset = accum.offset
|
sym.offset = accum.offset
|
||||||
accum.inc(int(child.size))
|
accum.inc(int(child.size))
|
||||||
accum.finish
|
typ.paddingAtEnd = int16(accum.finish())
|
||||||
typ.size = accum.offset
|
typ.size = accum.offset
|
||||||
typ.align = int16(accum.maxAlign)
|
typ.align = int16(accum.maxAlign)
|
||||||
except IllegalTypeRecursionError:
|
except IllegalTypeRecursionError:
|
||||||
|
typ.paddingAtEnd = szIllegalRecursion
|
||||||
typ.size = szIllegalRecursion
|
typ.size = szIllegalRecursion
|
||||||
typ.align = szIllegalRecursion
|
typ.align = szIllegalRecursion
|
||||||
|
|
||||||
of tyObject:
|
of tyObject:
|
||||||
var headerSize: BiggestInt
|
try:
|
||||||
var headerAlign: int16
|
var accum =
|
||||||
if typ.sons[0] != nil:
|
if typ.sons[0] != nil:
|
||||||
# compute header size
|
# compute header size
|
||||||
if conf.cmd == cmdCompileToCpp:
|
var st = typ.sons[0]
|
||||||
# if the target is C++ the members of this type are written
|
while st.kind in skipPtrs:
|
||||||
# into the padding byets at the end of the parent type. At the
|
st = st.sons[^1]
|
||||||
# moment it is not supported to calculate that.
|
computeSizeAlign(conf, st)
|
||||||
headerSize = szUnknownSize
|
if conf.cmd == cmdCompileToCpp:
|
||||||
headerAlign = szUncomputedSize
|
OffsetAccum(
|
||||||
else:
|
offset: int(st.size) - int(st.paddingAtEnd),
|
||||||
var st = typ.sons[0]
|
maxAlign: st.align
|
||||||
while st.kind in skipPtrs:
|
)
|
||||||
st = st.sons[^1]
|
else:
|
||||||
computeSizeAlign(conf, st)
|
OffsetAccum(
|
||||||
if st.size == szIllegalRecursion:
|
offset: int(st.size),
|
||||||
typ.size = st.size
|
maxAlign: st.align
|
||||||
typ.align = st.align
|
)
|
||||||
return
|
elif isObjectWithTypeFieldPredicate(typ):
|
||||||
headerSize = st.size
|
# this branch is taken for RootObj
|
||||||
headerAlign = st.align
|
OffsetAccum(
|
||||||
elif isObjectWithTypeFieldPredicate(typ):
|
offset: conf.target.intSize,
|
||||||
# this branch is taken for RootObj
|
maxAlign: conf.target.intSize
|
||||||
headerSize = conf.target.intSize
|
)
|
||||||
headerAlign = conf.target.intSize.int16
|
else:
|
||||||
else:
|
OffsetAccum(maxAlign: 1)
|
||||||
headerSize = 0
|
|
||||||
headerAlign = 1
|
|
||||||
let (offset, align) =
|
|
||||||
if tfUnion in typ.flags:
|
if tfUnion in typ.flags:
|
||||||
if tfPacked in typ.flags:
|
if tfPacked in typ.flags:
|
||||||
let info = if typ.sym != nil: typ.sym.info else: unknownLineInfo()
|
let info = if typ.sym != nil: typ.sym.info else: unknownLineInfo()
|
||||||
localError(conf, info, "type may not be packed and union at the same time.")
|
localError(conf, info, "union type may not be packed.")
|
||||||
(BiggestInt(szUnknownSize), BiggestInt(szUnknownSize))
|
accum = OffsetAccum(offset: szUnknownSize, maxAlign: szUnknownSize)
|
||||||
|
elif accum.offset != 0:
|
||||||
|
let info = if typ.sym != nil: typ.sym.info else: unknownLineInfo()
|
||||||
|
localError(conf, info, "union type may not have an object header")
|
||||||
|
accum = OffsetAccum(offset: szUnknownSize, maxAlign: szUnknownSize)
|
||||||
else:
|
else:
|
||||||
computeUnionObjectOffsetsFoldFunction(conf, typ.n, false)
|
computeUnionObjectOffsetsFoldFunction(conf, typ.n, accum)
|
||||||
elif tfPacked in typ.flags:
|
elif tfPacked in typ.flags:
|
||||||
(computePackedObjectOffsetsFoldFunction(conf, typ.n, headerSize, false), BiggestInt(1))
|
accum.maxAlign = 1
|
||||||
|
computeObjectOffsetsFoldFunction(conf, typ.n, true, accum)
|
||||||
else:
|
else:
|
||||||
computeObjectOffsetsFoldFunction(conf, typ.n, headerSize)
|
computeObjectOffsetsFoldFunction(conf, typ.n, false, accum)
|
||||||
if offset == szIllegalRecursion:
|
let paddingAtEnd = int16(accum.finish())
|
||||||
|
if typ.sym != nil and
|
||||||
|
typ.sym.flags * {sfCompilerProc, sfImportc} == {sfImportc}:
|
||||||
|
typ.size = szUnknownSize
|
||||||
|
typ.align = szUnknownSize
|
||||||
|
typ.paddingAtEnd = szUnknownSize
|
||||||
|
else:
|
||||||
|
typ.size = accum.offset
|
||||||
|
typ.align = int16(accum.maxAlign)
|
||||||
|
typ.paddingAtEnd = paddingAtEnd
|
||||||
|
except IllegalTypeRecursionError:
|
||||||
typ.size = szIllegalRecursion
|
typ.size = szIllegalRecursion
|
||||||
typ.align = szIllegalRecursion
|
typ.align = szIllegalRecursion
|
||||||
return
|
typ.paddingAtEnd = szIllegalRecursion
|
||||||
if offset == szUnknownSize or (
|
|
||||||
typ.sym != nil and
|
|
||||||
typ.sym.flags * {sfCompilerProc, sfImportc} == {sfImportc}):
|
|
||||||
typ.size = szUnknownSize
|
|
||||||
typ.align = szUnknownSize
|
|
||||||
return
|
|
||||||
# header size is already in size from computeObjectOffsetsFoldFunction
|
|
||||||
# maxAlign is probably not changed at all from headerAlign
|
|
||||||
if tfPacked in typ.flags:
|
|
||||||
typ.size = offset
|
|
||||||
typ.align = 1
|
|
||||||
else:
|
|
||||||
typ.align = int16(max(align, headerAlign))
|
|
||||||
typ.size = align(offset, typ.align)
|
|
||||||
of tyInferred:
|
of tyInferred:
|
||||||
if typ.len > 1:
|
if typ.len > 1:
|
||||||
computeSizeAlign(conf, typ.lastSon)
|
computeSizeAlign(conf, typ.lastSon)
|
||||||
typ.size = typ.lastSon.size
|
typ.size = typ.lastSon.size
|
||||||
typ.align = typ.lastSon.align
|
typ.align = typ.lastSon.align
|
||||||
|
typ.paddingAtEnd = typ.lastSon.paddingAtEnd
|
||||||
|
|
||||||
of tyGenericInst, tyDistinct, tyGenericBody, tyAlias, tySink, tyOwned:
|
of tyGenericInst, tyDistinct, tyGenericBody, tyAlias, tySink, tyOwned:
|
||||||
computeSizeAlign(conf, typ.lastSon)
|
computeSizeAlign(conf, typ.lastSon)
|
||||||
typ.size = typ.lastSon.size
|
typ.size = typ.lastSon.size
|
||||||
typ.align = typ.lastSon.align
|
typ.align = typ.lastSon.align
|
||||||
|
typ.paddingAtEnd = typ.lastSon.paddingAtEnd
|
||||||
|
|
||||||
of tyTypeClasses:
|
of tyTypeClasses:
|
||||||
if typ.isResolvedUserTypeClass:
|
if typ.isResolvedUserTypeClass:
|
||||||
computeSizeAlign(conf, typ.lastSon)
|
computeSizeAlign(conf, typ.lastSon)
|
||||||
typ.size = typ.lastSon.size
|
typ.size = typ.lastSon.size
|
||||||
typ.align = typ.lastSon.align
|
typ.align = typ.lastSon.align
|
||||||
|
typ.paddingAtEnd = typ.lastSon.paddingAtEnd
|
||||||
else:
|
else:
|
||||||
typ.size = szUncomputedSize
|
typ.size = szUnknownSize
|
||||||
typ.align = szUncomputedSize
|
typ.align = szUnknownSize
|
||||||
|
typ.paddingAtEnd = szUnknownSize
|
||||||
|
|
||||||
of tyTypeDesc:
|
of tyTypeDesc:
|
||||||
computeSizeAlign(conf, typ.base)
|
computeSizeAlign(conf, typ.base)
|
||||||
typ.size = typ.base.size
|
typ.size = typ.base.size
|
||||||
typ.align = typ.base.align
|
typ.align = typ.base.align
|
||||||
|
typ.paddingAtEnd = typ.base.paddingAtEnd
|
||||||
|
|
||||||
of tyForward:
|
of tyForward:
|
||||||
# is this really illegal recursion, or maybe just unknown?
|
# is this really illegal recursion, or maybe just unknown?
|
||||||
typ.size = szIllegalRecursion
|
typ.size = szIllegalRecursion
|
||||||
typ.align = szIllegalRecursion
|
typ.align = szIllegalRecursion
|
||||||
|
typ.paddingAtEnd = szIllegalRecursion
|
||||||
|
|
||||||
of tyStatic:
|
of tyStatic:
|
||||||
if typ.n != nil:
|
if typ.n != nil:
|
||||||
computeSizeAlign(conf, typ.lastSon)
|
computeSizeAlign(conf, typ.lastSon)
|
||||||
typ.size = typ.lastSon.size
|
typ.size = typ.lastSon.size
|
||||||
typ.align = typ.lastSon.align
|
typ.align = typ.lastSon.align
|
||||||
|
typ.paddingAtEnd = typ.lastSon.paddingAtEnd
|
||||||
else:
|
else:
|
||||||
typ.size = szUncomputedSize
|
typ.size = szUnknownSize
|
||||||
typ.align = szUncomputedSize
|
typ.align = szUnknownSize
|
||||||
|
typ.paddingAtEnd = szUnknownSize
|
||||||
else:
|
else:
|
||||||
typ.size = szUncomputedSize
|
typ.size = szUnknownSize
|
||||||
typ.align = szUncomputedSize
|
typ.align = szUnknownSize
|
||||||
|
typ.paddingAtEnd = szUnknownSize
|
||||||
|
|
||||||
template foldSizeOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
|
template foldSizeOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
|
||||||
let config = conf
|
let config = conf
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
|
targets: "c cpp"
|
||||||
output: '''
|
output: '''
|
||||||
body executed
|
body executed
|
||||||
body executed
|
body executed
|
||||||
|
|
@ -7,6 +8,14 @@ macros api OK
|
||||||
'''
|
'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# This is for travis. The keyword ``alignof`` only exists in ``c++11``
|
||||||
|
# and newer. On travis gcc does not default to c++11 yet.
|
||||||
|
when defined(cpp) and not defined(windows):
|
||||||
|
{.passC: "-std=c++11".}
|
||||||
|
|
||||||
|
# Object offsets are different for inheritance objects when compiling
|
||||||
|
# to c++.
|
||||||
|
|
||||||
type
|
type
|
||||||
TMyEnum = enum
|
TMyEnum = enum
|
||||||
tmOne, tmTwo, tmThree, tmFour
|
tmOne, tmTwo, tmThree, tmFour
|
||||||
|
|
@ -143,6 +152,14 @@ type
|
||||||
ValueA
|
ValueA
|
||||||
ValueB
|
ValueB
|
||||||
|
|
||||||
|
# Must have more than 32 elements so that set[MyEnum33] will become compile to an int64.
|
||||||
|
MyEnum33 {.pure.} = enum
|
||||||
|
Value1, Value2, Value3, Value4, Value5, Value6,
|
||||||
|
Value7, Value8, Value9, Value10, Value11, Value12,
|
||||||
|
Value13, Value14, Value15, Value16, Value17, Value18,
|
||||||
|
Value19, Value20, Value21, Value22, Value23, Value24,
|
||||||
|
Value25, Value26, Value27, Value28, Value29, Value30,
|
||||||
|
Value31, Value32, Value33
|
||||||
|
|
||||||
proc transformObjectconfigPacked(arg: NimNode): NimNode =
|
proc transformObjectconfigPacked(arg: NimNode): NimNode =
|
||||||
let debug = arg.kind == nnkPragmaExpr
|
let debug = arg.kind == nnkPragmaExpr
|
||||||
|
|
@ -296,6 +313,10 @@ testinstance:
|
||||||
b: int8
|
b: int8
|
||||||
c: int8
|
c: int8
|
||||||
|
|
||||||
|
PaddingOfSetEnum33 = object
|
||||||
|
cause: int8
|
||||||
|
theSet: set[MyEnum33]
|
||||||
|
|
||||||
Bazing {.objectconfig.} = object of RootObj
|
Bazing {.objectconfig.} = object of RootObj
|
||||||
a: int64
|
a: int64
|
||||||
# TODO test on 32 bit system
|
# TODO test on 32 bit system
|
||||||
|
|
@ -328,6 +349,7 @@ testinstance:
|
||||||
var g : RecursiveStuff
|
var g : RecursiveStuff
|
||||||
var ro : RootObj
|
var ro : RootObj
|
||||||
var go : GenericObject[int64]
|
var go : GenericObject[int64]
|
||||||
|
var po : PaddingOfSetEnum33
|
||||||
|
|
||||||
var
|
var
|
||||||
e1: Enum1
|
e1: Enum1
|
||||||
|
|
@ -346,16 +368,16 @@ testinstance:
|
||||||
else:
|
else:
|
||||||
doAssert sizeof(SimpleAlignment) > 10
|
doAssert sizeof(SimpleAlignment) > 10
|
||||||
|
|
||||||
testSizeAlignOf(t,a,b,c,d,e,f,g,ro,go, e1, e2, e4, e8, eoa, eob)
|
testSizeAlignOf(t,a,b,c,d,e,f,g,ro,go,po, e1, e2, e4, e8, eoa, eob)
|
||||||
|
|
||||||
when not defined(cpp):
|
|
||||||
type
|
|
||||||
WithBitsize {.objectconfig.} = object
|
|
||||||
bitfieldA {.bitsize: 16.}: uint32
|
|
||||||
bitfieldB {.bitsize: 16.}: uint32
|
|
||||||
|
|
||||||
var wbs: WithBitsize
|
type
|
||||||
testSize(wbs)
|
WithBitsize {.objectconfig.} = object
|
||||||
|
bitfieldA {.bitsize: 16.}: uint32
|
||||||
|
bitfieldB {.bitsize: 16.}: uint32
|
||||||
|
|
||||||
|
var wbs: WithBitsize
|
||||||
|
testSize(wbs)
|
||||||
|
|
||||||
testOffsetOf(TrivialType, x)
|
testOffsetOf(TrivialType, x)
|
||||||
testOffsetOf(TrivialType, y)
|
testOffsetOf(TrivialType, y)
|
||||||
|
|
@ -383,11 +405,13 @@ testinstance:
|
||||||
|
|
||||||
testOffsetOf(Foobar, c)
|
testOffsetOf(Foobar, c)
|
||||||
|
|
||||||
when not defined(cpp):
|
testOffsetOf(PaddingOfSetEnum33, cause)
|
||||||
testOffsetOf(Bazing, a)
|
testOffsetOf(PaddingOfSetEnum33, theSet)
|
||||||
testOffsetOf(InheritanceA, a)
|
|
||||||
testOffsetOf(InheritanceB, b)
|
testOffsetOf(Bazing, a)
|
||||||
testOffsetOf(InheritanceC, c)
|
testOffsetOf(InheritanceA, a)
|
||||||
|
testOffsetOf(InheritanceB, b)
|
||||||
|
testOffsetOf(InheritanceC, c)
|
||||||
|
|
||||||
testOffsetOf(EnumObjectA, a)
|
testOffsetOf(EnumObjectA, a)
|
||||||
testOffsetOf(EnumObjectA, b)
|
testOffsetOf(EnumObjectA, b)
|
||||||
|
|
@ -619,9 +643,6 @@ doAssert offsetof(MyPackedCaseObject, val3) == 13
|
||||||
doAssert offsetof(MyPackedCaseObject, val4) == 9
|
doAssert offsetof(MyPackedCaseObject, val4) == 9
|
||||||
doAssert offsetof(MyPackedCaseObject, val5) == 13
|
doAssert offsetof(MyPackedCaseObject, val5) == 13
|
||||||
|
|
||||||
reject:
|
|
||||||
const off4 = offsetof(MyPackedCaseObject, val1)
|
|
||||||
|
|
||||||
reject:
|
reject:
|
||||||
const off5 = offsetof(MyPackedCaseObject, val2)
|
const off5 = offsetof(MyPackedCaseObject, val2)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue