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:
Andreas Rumpf 2019-08-28 19:36:29 +02:00 • committed by GitHub
commit 21fc8b4d4d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 198 additions and 230 deletions

View file

@ -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

View file

@ -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

View file

@ -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:

View file

@ -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)

View file

@ -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:
maxChildAlign = max(maxChildAlign, align)
else: else:
internalError(conf, "computeObjectOffsetsFoldFunction(record case branch)") 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: else:
n.sym.offset = align(initialOffset, align).int accum.maxAlign = szUnknownSize
result.offset = n.sym.offset + n.sym.typ.size accum.offset = szUnknownSize
else:
result.align = szUnknownSize
result.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: else:
n.sym.offset = 0 accum.maxAlign = szUnknownSize
result.offset = n.sym.typ.size accum.offset = szUnknownSize
else:
result.offset = szUnknownSize
result.align = 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:
# if the target is C++ the members of this type are written
# into the padding byets at the end of the parent type. At the
# moment it is not supported to calculate that.
headerSize = szUnknownSize
headerAlign = szUncomputedSize
else:
var st = typ.sons[0] var st = typ.sons[0]
while st.kind in skipPtrs: while st.kind in skipPtrs:
st = st.sons[^1] st = st.sons[^1]
computeSizeAlign(conf, st) computeSizeAlign(conf, st)
if st.size == szIllegalRecursion: if conf.cmd == cmdCompileToCpp:
typ.size = st.size OffsetAccum(
typ.align = st.align offset: int(st.size) - int(st.paddingAtEnd),
return maxAlign: st.align
headerSize = st.size )
headerAlign = st.align else:
OffsetAccum(
offset: int(st.size),
maxAlign: st.align
)
elif isObjectWithTypeFieldPredicate(typ): elif isObjectWithTypeFieldPredicate(typ):
# this branch is taken for RootObj # this branch is taken for RootObj
headerSize = conf.target.intSize OffsetAccum(
headerAlign = conf.target.intSize.int16 offset: conf.target.intSize,
maxAlign: conf.target.intSize
)
else: else:
headerSize = 0 OffsetAccum(maxAlign: 1)
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())
typ.size = szIllegalRecursion if typ.sym != nil and
typ.align = szIllegalRecursion typ.sym.flags * {sfCompilerProc, sfImportc} == {sfImportc}:
return
if offset == szUnknownSize or (
typ.sym != nil and
typ.sym.flags * {sfCompilerProc, sfImportc} == {sfImportc}):
typ.size = szUnknownSize typ.size = szUnknownSize
typ.align = szUnknownSize typ.align = szUnknownSize
return typ.paddingAtEnd = szUnknownSize
# 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: else:
typ.align = int16(max(align, headerAlign)) typ.size = accum.offset
typ.size = align(offset, typ.align) typ.align = int16(accum.maxAlign)
typ.paddingAtEnd = paddingAtEnd
except IllegalTypeRecursionError:
typ.size = szIllegalRecursion
typ.align = szIllegalRecursion
typ.paddingAtEnd = szIllegalRecursion
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

View file

@ -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,9 +368,9 @@ 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 type
WithBitsize {.objectconfig.} = object WithBitsize {.objectconfig.} = object
bitfieldA {.bitsize: 16.}: uint32 bitfieldA {.bitsize: 16.}: uint32
@ -383,7 +405,9 @@ testinstance:
testOffsetOf(Foobar, c) testOffsetOf(Foobar, c)
when not defined(cpp): testOffsetOf(PaddingOfSetEnum33, cause)
testOffsetOf(PaddingOfSetEnum33, theSet)
testOffsetOf(Bazing, a) testOffsetOf(Bazing, a)
testOffsetOf(InheritanceA, a) testOffsetOf(InheritanceA, a)
testOffsetOf(InheritanceB, b) testOffsetOf(InheritanceB, 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)