123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556 |
- #
- #
- # The Nim Compiler
- #
- # See the file "copying.txt", included in this
- # distribution, for details about the copyright.
- #
- ## code owner: Arne Döring
- ## e-mail: arne.doering@gmx.net
- proc align(address, alignment: BiggestInt): BiggestInt =
- result = (address + (alignment - 1)) and not (alignment - 1)
- proc align(address, alignment: int): int =
- result = (address + (alignment - 1)) and not (alignment - 1)
- const
- ## a size is concidered "unknown" when it is an imported type from C
- ## or C++.
- szUnknownSize* = -3
- szIllegalRecursion* = -2
- szUncomputedSize* = -1
- type IllegalTypeRecursionError = object of Exception
- proc raiseIllegalTypeRecursion() =
- raise newException(IllegalTypeRecursionError, "illegal type recursion")
- type
- OffsetAccum = object
- maxAlign: int
- offset: int
- proc inc(arg: var OffsetAccum; value: int) =
- if unlikely(value == szIllegalRecursion): raiseIllegalTypeRecursion()
- if value == szUnknownSize or arg.offset == szUnknownSize:
- arg.offset = szUnknownSize
- else:
- arg.offset += value
- proc align(arg: var OffsetAccum; value: int) =
- if unlikely(value == szIllegalRecursion): raiseIllegalTypeRecursion()
- if value == szUnknownSize or arg.maxAlign == szUnknownSize or arg.offset == szUnknownSize:
- arg.maxAlign = szUnknownSize
- arg.offset = szUnknownSize
- else:
- arg.maxAlign = max(value, arg.maxAlign)
- arg.offset = align(arg.offset, value)
- proc finish(arg: var OffsetAccum) =
- if arg.maxAlign == szUnknownSize or arg.offset == szUnknownSize:
- arg.offset = szUnknownSize
- else:
- arg.offset = align(arg.offset, arg.maxAlign)
- proc computeSizeAlign(conf: ConfigRef; typ: PType)
- proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt =
- ## returns object alignment
- case n.kind
- of nkRecCase:
- assert(n.sons[0].kind == nkSym)
- result = computeSubObjectAlign(conf, n.sons[0])
- for i in 1 ..< sonsLen(n):
- let child = n.sons[i]
- case child.kind
- of nkOfBranch, nkElse:
- let align = computeSubObjectAlign(conf, child.lastSon)
- if align < 0:
- return align
- result = max(result, align)
- else:
- internalError(conf, "computeSubObjectAlign")
- of nkRecList:
- result = 1
- for i, child in n.sons:
- let align = computeSubObjectAlign(conf, n.sons[i])
- if align < 0:
- return align
- result = max(result, align)
- of nkSym:
- computeSizeAlign(conf, n.sym.typ)
- result = n.sym.typ.align
- else:
- result = 1
- proc setOffsetsToUnknown(n: PNode) =
- if n.kind == nkSym and n.sym.kind == skField:
- n.sym.offset = szUnknownSize
- else:
- for i in 0 ..< safeLen(n):
- setOffsetsToUnknown(n[i])
- proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode,
- initialOffset: BiggestInt): tuple[offset, align: BiggestInt] =
- ## ``offset`` is the offset within the object, after the node has been written, no padding bytes added
- ## ``align`` maximum alignment from all sub nodes
- assert n != nil
- if n.typ != nil and n.typ.size == szIllegalRecursion:
- result.offset = szIllegalRecursion
- result.align = szIllegalRecursion
- return
- result.align = 1
- case n.kind
- of nkRecCase:
- assert(n.sons[0].kind == nkSym)
- let (kindOffset, kindAlign) = computeObjectOffsetsFoldFunction(conf, n.sons[0], initialOffset)
- var maxChildAlign: BiggestInt = if initialOffset == szUnknownSize: szUnknownSize else: 0
- for i in 1 ..< sonsLen(n):
- let child = n.sons[i]
- case child.kind
- of nkOfBranch, nkElse:
- # offset parameter cannot be known yet, it needs to know the alignment first
- let align = computeSubObjectAlign(conf, n.sons[i].lastSon)
- if align == szIllegalRecursion:
- result.offset = szIllegalRecursion
- result.align = szIllegalRecursion
- return
- if align == szUnknownSize or maxChildAlign == szUnknownSize:
- maxChildAlign = szUnknownSize
- else:
- maxChildAlign = max(maxChildAlign, align)
- else:
- internalError(conf, "computeObjectOffsetsFoldFunction(record case branch)")
- if maxChildAlign == szUnknownSize:
- setOffsetsToUnknown(n)
- result.align = szUnknownSize
- result.offset = szUnknownSize
- else:
- # the union neds to be aligned first, before the offsets can be assigned
- let kindUnionOffset = align(kindOffset, maxChildAlign)
- var maxChildOffset: BiggestInt = 0
- for i in 1 ..< sonsLen(n):
- let (offset, align) = computeObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset)
- maxChildOffset = max(maxChildOffset, offset)
- result.align = max(kindAlign, maxChildAlign)
- result.offset = maxChildOffset
- of nkRecList:
- result.align = 1 # maximum of all member alignments
- var offset = initialOffset
- for i, child in n.sons:
- let (newOffset, align) = computeObjectOffsetsFoldFunction(conf, child, offset)
- 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:
- var size = szUnknownSize
- var align = szUnknownSize
- if n.sym.bitsize == 0: # 0 represents bitsize not set
- computeSizeAlign(conf, n.sym.typ)
- size = n.sym.typ.size.int
- align = n.sym.typ.align.int
- result.align = align
- if initialOffset == szUnknownSize or size == szUnknownSize:
- 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:
- result.align = szUnknownSize
- result.offset = szUnknownSize
- proc computePackedObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOffset: BiggestInt, debug: bool): BiggestInt =
- ## ``result`` is the offset within the object, after the node has been written, no padding bytes added
- case n.kind
- of nkRecCase:
- assert(n.sons[0].kind == nkSym)
- let kindOffset = computePackedObjectOffsetsFoldFunction(conf, n.sons[0], initialOffset, debug)
- # 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.")
- #internalError(conf, "Illegal use of ``case`` in union type.")
- of nkRecList:
- var maxChildOffset: BiggestInt = 0
- for i, child in n.sons:
- let (offset, align) = computeUnionObjectOffsetsFoldFunction(conf, child, debug)
- if offset == szIllegalRecursion or align == szIllegalRecursion:
- result.offset = szIllegalRecursion
- 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:
- var size = szUnknownSize
- var align = szUnknownSize
- if n.sym.bitsize == 0: # 0 represents bitsize not set
- computeSizeAlign(conf, n.sym.typ)
- size = n.sym.typ.size.int
- align = n.sym.typ.align.int
- result.align = align
- if size == szUnknownSize:
- n.sym.offset = szUnknownSize
- result.offset = szUnknownSize
- else:
- n.sym.offset = 0
- result.offset = n.sym.typ.size
- else:
- result.offset = szUnknownSize
- result.align = szUnknownSize
- proc computeSizeAlign(conf: ConfigRef; typ: PType) =
- ## computes and sets ``size`` and ``align`` members of ``typ``
- assert typ != nil
- let hasSize = typ.size != szUncomputedSize
- let hasAlign = typ.align != szUncomputedSize
- if hasSize and hasAlign:
- # nothing to do, size and align already computed
- return
- # This function can only calculate both, size and align at the same time.
- # If one of them is already set this value is stored here and reapplied
- let revertSize = typ.size
- let revertAlign = typ.align
- defer:
- if hasSize:
- typ.size = revertSize
- if hasAlign:
- typ.align = revertAlign
- if typ.size == szIllegalRecursion or typ.align == szIllegalRecursion:
- # we are already computing the size of the type
- # --> illegal recursion in type
- return
- # mark computation in progress
- typ.size = szIllegalRecursion
- typ.align = szIllegalRecursion
- var maxAlign, sizeAccum, length: BiggestInt
- var tk = typ.kind
- case tk
- of tyProc:
- if typ.callConv == ccClosure:
- typ.size = 2 * conf.target.ptrSize
- else:
- typ.size = conf.target.ptrSize
- typ.align = int16(conf.target.ptrSize)
- of tyNil:
- typ.size = conf.target.ptrSize
- typ.align = int16(conf.target.ptrSize)
- of tyString:
- if conf.selectedGC == gcDestructors:
- typ.size = conf.target.ptrSize * 2
- else:
- typ.size = conf.target.ptrSize
- typ.align = int16(conf.target.ptrSize)
- of tyCString, tySequence, tyPtr, tyRef, tyVar, tyLent, tyOpenArray:
- let base = typ.lastSon
- if base == typ:
- # this is not the correct location to detect ``type A = ptr A``
- typ.size = szIllegalRecursion
- typ.align = szIllegalRecursion
- return
- typ.align = int16(conf.target.ptrSize)
- if typ.kind == tySequence and conf.selectedGC == gcDestructors:
- typ.size = conf.target.ptrSize * 2
- else:
- typ.size = conf.target.ptrSize
- of tyArray:
- computeSizeAlign(conf, typ.sons[1])
- let elemSize = typ.sons[1].size
- if elemSize < 0:
- typ.size = elemSize
- typ.align = int16(elemSize)
- else:
- typ.size = lengthOrd(conf, typ.sons[0]) * elemSize
- typ.align = typ.sons[1].align
- of tyUncheckedArray:
- let base = typ.lastSon
- computeSizeAlign(conf, base)
- typ.size = 0
- typ.align = base.align
- of tyEnum:
- if firstOrd(conf, typ) < 0:
- typ.size = 4 # use signed int32
- typ.align = 4
- else:
- length = lastOrd(conf, typ) # BUGFIX: use lastOrd!
- if length + 1 < `shl`(1, 8):
- typ.size = 1
- typ.align = 1
- elif length + 1 < `shl`(1, 16):
- typ.size = 2
- typ.align = 2
- elif length + 1 < `shl`(BiggestInt(1), 32):
- typ.size = 4
- typ.align = 4
- else:
- typ.size = 8
- typ.align = 8
- of tySet:
- if typ.sons[0].kind == tyGenericParam:
- typ.size = szUncomputedSize
- typ.align = szUncomputedSize # in original version this was 1
- else:
- length = lengthOrd(conf, typ.sons[0])
- if length <= 8:
- typ.size = 1
- elif length <= 16:
- typ.size = 2
- elif length <= 32:
- typ.size = 4
- elif length <= 64:
- typ.size = 8
- elif align(length, 8) mod 8 == 0:
- typ.size = align(length, 8) div 8
- else:
- typ.size = align(length, 8) div 8 + 1
- typ.align = int16(typ.size)
- of tyRange:
- computeSizeAlign(conf, typ.sons[0])
- typ.size = typ.sons[0].size
- typ.align = typ.sons[0].align
- of tyTuple:
- try:
- var accum = OffsetAccum(maxAlign: 1)
- for i in 0 ..< sonsLen(typ):
- let child = typ.sons[i]
- computeSizeAlign(conf, child)
- accum.align(child.align)
- if typ.n != nil: # is named tuple (has field symbols)?
- let sym = typ.n[i].sym
- sym.offset = accum.offset
- accum.inc(int(child.size))
- accum.finish
- typ.size = accum.offset
- typ.align = int16(accum.maxAlign)
- except IllegalTypeRecursionError:
- typ.size = szIllegalRecursion
- typ.align = szIllegalRecursion
- of tyObject:
- var headerSize: BiggestInt
- var headerAlign: int16
- if typ.sons[0] != nil:
- # 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]
- while st.kind in skipPtrs:
- st = st.sons[^1]
- computeSizeAlign(conf, st)
- if st.size == szIllegalRecursion:
- typ.size = st.size
- typ.align = st.align
- return
- headerSize = st.size
- headerAlign = st.align
- elif isObjectWithTypeFieldPredicate(typ):
- # this branch is taken for RootObj
- headerSize = conf.target.intSize
- headerAlign = conf.target.intSize.int16
- else:
- headerSize = 0
- headerAlign = 1
- let (offset, align) =
- if tfUnion in typ.flags:
- if tfPacked in typ.flags:
- 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.")
- (BiggestInt(szUnknownSize), BiggestInt(szUnknownSize))
- else:
- computeUnionObjectOffsetsFoldFunction(conf, typ.n, false)
- elif tfPacked in typ.flags:
- (computePackedObjectOffsetsFoldFunction(conf, typ.n, headerSize, false), BiggestInt(1))
- else:
- computeObjectOffsetsFoldFunction(conf, typ.n, headerSize)
- if offset == szIllegalRecursion:
- typ.size = szIllegalRecursion
- typ.align = szIllegalRecursion
- return
- 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:
- if typ.len > 1:
- computeSizeAlign(conf, typ.lastSon)
- typ.size = typ.lastSon.size
- typ.align = typ.lastSon.align
- of tyGenericInst, tyDistinct, tyGenericBody, tyAlias, tySink, tyOwned:
- computeSizeAlign(conf, typ.lastSon)
- typ.size = typ.lastSon.size
- typ.align = typ.lastSon.align
- of tyTypeClasses:
- if typ.isResolvedUserTypeClass:
- computeSizeAlign(conf, typ.lastSon)
- typ.size = typ.lastSon.size
- typ.align = typ.lastSon.align
- else:
- typ.size = szUncomputedSize
- typ.align = szUncomputedSize
- of tyTypeDesc:
- computeSizeAlign(conf, typ.base)
- typ.size = typ.base.size
- typ.align = typ.base.align
- of tyForward:
- # is this really illegal recursion, or maybe just unknown?
- typ.size = szIllegalRecursion
- typ.align = szIllegalRecursion
- of tyStatic:
- if typ.n != nil:
- computeSizeAlign(conf, typ.lastSon)
- typ.size = typ.lastSon.size
- typ.align = typ.lastSon.align
- else:
- typ.size = szUncomputedSize
- typ.align = szUncomputedSize
- else:
- typ.size = szUncomputedSize
- typ.align = szUncomputedSize
- template foldSizeOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
- let config = conf
- let node = n
- let typ = node[1].typ
- computeSizeAlign(config, typ)
- let size = typ.size
- if size >= 0:
- let res = newIntNode(nkIntLit, size)
- res.info = node.info
- res.typ = node.typ
- res
- else:
- fallback
- template foldAlignOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
- let config = conf
- let node = n
- let typ = node[1].typ
- computeSizeAlign(config, typ)
- let align = typ.align
- if align >= 0:
- let res = newIntNode(nkIntLit, align)
- res.info = node.info
- res.typ = node.typ
- res
- else:
- fallback
- template foldOffsetOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
- ## Returns an int literal node of the given offsetof expression in `n`.
- ## Falls back to `fallback`, if the `offsetof` expression can't be processed.
- let config = conf
- let node : PNode = n
- var dotExpr: PNode
- block findDotExpr:
- if node[1].kind == nkDotExpr:
- dotExpr = node[1]
- elif node[1].kind == nkCheckedFieldExpr:
- dotExpr = node[1][0]
- else:
- localError(config, node.info, "can't compute offsetof on this ast")
- assert dotExpr != nil
- let value = dotExpr[0]
- let member = dotExpr[1]
- computeSizeAlign(config, value.typ)
- let offset = member.sym.offset
- if offset >= 0:
- let tmp = newIntNode(nkIntLit, offset)
- tmp.info = node.info
- tmp.typ = node.typ
- tmp
- else:
- fallback
|