diff options
author | Arne Döring <arne.doering@gmx.net> | 2019-07-09 09:07:45 +0200 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2019-07-09 09:07:45 +0200 |
commit | 11dad688fea4033417c17c5e5b8e3c61ddb51da8 (patch) | |
tree | dbce48861c2e6b266829abf23d21e558e5a6a17f /compiler | |
parent | b50ae6817a8cf558eedbee5e405dd68a7d22edd8 (diff) | |
download | Nim-11dad688fea4033417c17c5e5b8e3c61ddb51da8.tar.gz |
Offsetof fixes (#11690)
* first fixes * more tests and fixes * code normalization
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/ccgexprs.nim | 16 | ||||
-rw-r--r-- | compiler/semexprs.nim | 12 | ||||
-rw-r--r-- | compiler/semfold.nim | 12 | ||||
-rw-r--r-- | compiler/semmagic.nim | 47 | ||||
-rw-r--r-- | compiler/sizealignoffsetimpl.nim | 141 | ||||
-rw-r--r-- | compiler/vmgen.nim | 8 |
6 files changed, 156 insertions, 80 deletions
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 9211786da..140f7e1d4 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2168,6 +2168,22 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = if not p.module.compileToCpp: p.module.includeHeader("<stdalign.h>") putIntoDest(p, d, e, "((NI)alignof($1))" % [getTypeDesc(p.module, t)]) + of mOffsetOf: + var dotExpr: PNode + block findDotExpr: + if e[1].kind == nkDotExpr: + dotExpr = e[1] + elif e[1].kind == nkCheckedFieldExpr: + dotExpr = e[1][0] + else: + internalError(p.config, e.info, "unknown ast") + let t = dotExpr[0].typ.skipTypes({tyTypeDesc}) + let member = + if t.kind == tyTuple: + "Field" & rope(dotExpr[1].sym.position) + else: + rope(dotExpr[1].sym.name.s) + putIntoDest(p,d,e, "((NI)offsetof($1, $2))" % [getTypeDesc(p.module, t), member]) of mChr: genSomeCast(p, e, d) of mOrd: genOrd(p, e, d) of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray: diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 3e077f732..6cf6f9acd 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2119,14 +2119,7 @@ proc semSizeof(c: PContext, n: PNode): PNode = n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType}) #restoreOldStyleType(n.sons[1]) n.typ = getSysType(c.graph, n.info, tyInt) - - let size = getSize(c.config, n[1].typ) - if size >= 0: - result = newIntNode(nkIntLit, size) - result.info = n.info - result.typ = n.typ - else: - result = n + result = foldSizeOf(c.config, n, n) proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = # this is a hotspot in the compiler! @@ -2215,7 +2208,8 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = result = setMs(n, s) else: result = c.graph.emptyNode - of mSizeOf: result = semSizeof(c, setMs(n, s)) + of mSizeOf: result = + semSizeof(c, setMs(n, s)) else: result = semDirectOp(c, n, flags) diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 8c4e6431f..9323f1cee 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -648,13 +648,11 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode = # This fixes bug #544. result = newIntNodeT(lengthOrd(g.config, n.sons[1].typ), n, g) of mSizeOf: - let size = getSize(g.config, n[1].typ) - if size >= 0: - result = newIntNode(nkIntLit, size) - result.info = n.info - result.typ = getSysType(g, n.info, tyInt) - else: - result = nil + result = foldSizeOf(g.config, n, nil) + of mAlignOf: + result = foldAlignOf(g.config, n, nil) + of mOffsetOf: + result = foldOffsetOf(g.config, n, nil) of mAstToStr: result = newStrNodeT(renderTree(n[1], {renderNoComments}), n, g) of mConStrStr: diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 1472cd2f3..6956e9eca 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -375,52 +375,11 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, of mTypeOf: result = semTypeOf(c, n) of mSizeOf: - # TODO there is no proper way to find out if a type cannot be queried for the size. - let size = getSize(c.config, n[1].typ) - # We just assume here that the type might come from the c backend - if size == szUnknownSize: - # Forward to the c code generation to emit a `sizeof` in the C code. - result = n - elif size >= 0: - result = newIntNode(nkIntLit, size) - result.info = n.info - result.typ = n.typ - else: - localError(c.config, n.info, "cannot evaluate 'sizeof' because its type is not defined completely, type: " & n[1].typ.typeToString) - result = n + result = foldSizeOf(c.config, n, n) of mAlignOf: - # this is 100% analog to mSizeOf, could be made more dry. - let align = getAlign(c.config, n[1].typ) - if align == szUnknownSize: - result = n - elif align >= 0: - result = newIntNode(nkIntLit, align) - result.info = n.info - result.typ = n.typ - else: - localError(c.config, n.info, "cannot evaluate 'alignof' because its type is not defined completely, type: " & n[1].typ.typeToString) - result = n + result = foldAlignOf(c.config, n, n) of mOffsetOf: - var dotExpr: PNode - - block findDotExpr: - if n[1].kind == nkDotExpr: - dotExpr = n[1] - elif n[1].kind == nkCheckedFieldExpr: - dotExpr = n[1][0] - else: - illFormedAst(n, c.config) - - assert dotExpr != nil - - let value = dotExpr[0] - let member = dotExpr[1] - - discard computeSize(c.config, value.typ) - - result = newIntNode(nkIntLit, member.sym.offset) - result.info = n.info - result.typ = n.typ + result = foldOffsetOf(c.config, n, n) of mArrGet: result = semArrGet(c, n, flags) of mArrPut: diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index cf8f296b4..641b823a3 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -11,6 +11,10 @@ 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++. @@ -18,6 +22,38 @@ const 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 = @@ -49,6 +85,14 @@ proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt = 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 @@ -65,7 +109,7 @@ proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, assert(n.sons[0].kind == nkSym) let (kindOffset, kindAlign) = computeObjectOffsetsFoldFunction(conf, n.sons[0], initialOffset) - var maxChildAlign: BiggestInt = 0 + var maxChildAlign: BiggestInt = if initialOffset == szUnknownSize: szUnknownSize else: 0 for i in 1 ..< sonsLen(n): let child = n.sons[i] case child.kind @@ -83,6 +127,7 @@ proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, else: internalError(conf, "computeObjectOffsetsFoldFunction(record case branch)") if maxChildAlign == szUnknownSize: + setOffsetsToUnknown(n) result.align = szUnknownSize result.offset = szUnknownSize else: @@ -145,10 +190,12 @@ proc computePackedObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOf var maxChildOffset: BiggestInt = kindUnionOffset for i in 1 ..< sonsLen(n): let offset = computePackedObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset, debug) - if offset < 0: - result = offset - break - maxChildOffset = max(maxChildOffset, offset) + if offset == szIllegalRecursion: + return szIllegalRecursion + if offset == szUnknownSize or maxChildOffset == szUnknownSize: + maxChildOffset = szUnknownSize + else: + maxChildOffset = max(maxChildOffset, offset) result = maxChildOffset of nkRecList: result = initialOffset @@ -335,19 +382,22 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = typ.size = typ.sons[0].size typ.align = typ.sons[0].align of tyTuple: - maxAlign = 1 - sizeAccum = 0 - for i in 0 ..< sonsLen(typ): - let child = typ.sons[i] - computeSizeAlign(conf, child) - if child.size < 0: - typ.size = child.size - typ.align = child.align - return - maxAlign = max(maxAlign, child.align) - sizeAccum = align(sizeAccum, child.align) + child.size - typ.size = align(sizeAccum, maxAlign) - typ.align = int16(maxAlign) + 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 @@ -448,3 +498,58 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = 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 diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index e221929a2..fe9772d08 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1310,8 +1310,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = # produces a value else: globalError(c.config, n.info, "expandToAst requires a call expression") - of mSizeOf, mAlignOf: - globalError(c.config, n.info, "cannot evaluate 'sizeof/alignof' because its type is not defined completely") + of mSizeOf: + globalError(c.config, n.info, "cannot evaluate 'sizeof' because its type is not defined completely") + of mAlignOf: + globalError(c.config, n.info, "cannot evaluate 'alignof' because its type is not defined completely") + of mOffsetOf: + globalError(c.config, n.info, "cannot evaluate 'offsetof' because its type is not defined completely") of mRunnableExamples: discard "just ignore any call to runnableExamples" of mDestroy: discard "ignore calls to the default destructor" |