sizealignoffsetimpl.nim 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. #
  2. #
  3. # The Nim Compiler
  4. #
  5. # See the file "copying.txt", included in this
  6. # distribution, for details about the copyright.
  7. #
  8. ## code owner: Arne Döring
  9. ## e-mail: arne.doering@gmx.net
  10. proc align(address, alignment: BiggestInt): BiggestInt =
  11. result = (address + (alignment - 1)) and not (alignment - 1)
  12. proc align(address, alignment: int): int =
  13. result = (address + (alignment - 1)) and not (alignment - 1)
  14. const
  15. ## a size is concidered "unknown" when it is an imported type from C
  16. ## or C++.
  17. szUnknownSize* = -3
  18. szIllegalRecursion* = -2
  19. szUncomputedSize* = -1
  20. type IllegalTypeRecursionError = object of Exception
  21. proc raiseIllegalTypeRecursion() =
  22. raise newException(IllegalTypeRecursionError, "illegal type recursion")
  23. type
  24. OffsetAccum = object
  25. maxAlign: int
  26. offset: int
  27. proc inc(arg: var OffsetAccum; value: int) =
  28. if unlikely(value == szIllegalRecursion): raiseIllegalTypeRecursion()
  29. if value == szUnknownSize or arg.offset == szUnknownSize:
  30. arg.offset = szUnknownSize
  31. else:
  32. arg.offset += value
  33. proc align(arg: var OffsetAccum; value: int) =
  34. if unlikely(value == szIllegalRecursion): raiseIllegalTypeRecursion()
  35. if value == szUnknownSize or arg.maxAlign == szUnknownSize or arg.offset == szUnknownSize:
  36. arg.maxAlign = szUnknownSize
  37. arg.offset = szUnknownSize
  38. else:
  39. arg.maxAlign = max(value, arg.maxAlign)
  40. arg.offset = align(arg.offset, value)
  41. proc finish(arg: var OffsetAccum) =
  42. if arg.maxAlign == szUnknownSize or arg.offset == szUnknownSize:
  43. arg.offset = szUnknownSize
  44. else:
  45. arg.offset = align(arg.offset, arg.maxAlign)
  46. proc computeSizeAlign(conf: ConfigRef; typ: PType)
  47. proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt =
  48. ## returns object alignment
  49. case n.kind
  50. of nkRecCase:
  51. assert(n.sons[0].kind == nkSym)
  52. result = computeSubObjectAlign(conf, n.sons[0])
  53. for i in 1 ..< sonsLen(n):
  54. let child = n.sons[i]
  55. case child.kind
  56. of nkOfBranch, nkElse:
  57. let align = computeSubObjectAlign(conf, child.lastSon)
  58. if align < 0:
  59. return align
  60. result = max(result, align)
  61. else:
  62. internalError(conf, "computeSubObjectAlign")
  63. of nkRecList:
  64. result = 1
  65. for i, child in n.sons:
  66. let align = computeSubObjectAlign(conf, n.sons[i])
  67. if align < 0:
  68. return align
  69. result = max(result, align)
  70. of nkSym:
  71. computeSizeAlign(conf, n.sym.typ)
  72. result = n.sym.typ.align
  73. else:
  74. result = 1
  75. proc setOffsetsToUnknown(n: PNode) =
  76. if n.kind == nkSym and n.sym.kind == skField:
  77. n.sym.offset = szUnknownSize
  78. else:
  79. for i in 0 ..< safeLen(n):
  80. setOffsetsToUnknown(n[i])
  81. proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode,
  82. initialOffset: BiggestInt): tuple[offset, align: BiggestInt] =
  83. ## ``offset`` is the offset within the object, after the node has been written, no padding bytes added
  84. ## ``align`` maximum alignment from all sub nodes
  85. assert n != nil
  86. if n.typ != nil and n.typ.size == szIllegalRecursion:
  87. result.offset = szIllegalRecursion
  88. result.align = szIllegalRecursion
  89. return
  90. result.align = 1
  91. case n.kind
  92. of nkRecCase:
  93. assert(n.sons[0].kind == nkSym)
  94. let (kindOffset, kindAlign) = computeObjectOffsetsFoldFunction(conf, n.sons[0], initialOffset)
  95. var maxChildAlign: BiggestInt = if initialOffset == szUnknownSize: szUnknownSize else: 0
  96. for i in 1 ..< sonsLen(n):
  97. let child = n.sons[i]
  98. case child.kind
  99. of nkOfBranch, nkElse:
  100. # offset parameter cannot be known yet, it needs to know the alignment first
  101. let align = computeSubObjectAlign(conf, n.sons[i].lastSon)
  102. if align == szIllegalRecursion:
  103. result.offset = szIllegalRecursion
  104. result.align = szIllegalRecursion
  105. return
  106. if align == szUnknownSize or maxChildAlign == szUnknownSize:
  107. maxChildAlign = szUnknownSize
  108. else:
  109. maxChildAlign = max(maxChildAlign, align)
  110. else:
  111. internalError(conf, "computeObjectOffsetsFoldFunction(record case branch)")
  112. if maxChildAlign == szUnknownSize:
  113. setOffsetsToUnknown(n)
  114. result.align = szUnknownSize
  115. result.offset = szUnknownSize
  116. else:
  117. # the union neds to be aligned first, before the offsets can be assigned
  118. let kindUnionOffset = align(kindOffset, maxChildAlign)
  119. var maxChildOffset: BiggestInt = 0
  120. for i in 1 ..< sonsLen(n):
  121. let (offset, align) = computeObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset)
  122. maxChildOffset = max(maxChildOffset, offset)
  123. result.align = max(kindAlign, maxChildAlign)
  124. result.offset = maxChildOffset
  125. of nkRecList:
  126. result.align = 1 # maximum of all member alignments
  127. var offset = initialOffset
  128. for i, child in n.sons:
  129. let (newOffset, align) = computeObjectOffsetsFoldFunction(conf, child, offset)
  130. if newOffset == szIllegalRecursion:
  131. result.offset = szIllegalRecursion
  132. result.align = szIllegalRecursion
  133. return
  134. elif newOffset == szUnknownSize or offset == szUnknownSize:
  135. # if anything is unknown, the rest becomes unknown as well
  136. offset = szUnknownSize
  137. result.align = szUnknownSize
  138. else:
  139. offset = newOffset
  140. result.align = max(result.align, align)
  141. # final alignment
  142. if offset == szUnknownSize:
  143. result.offset = szUnknownSize
  144. else:
  145. result.offset = align(offset, result.align)
  146. of nkSym:
  147. var size = szUnknownSize
  148. var align = szUnknownSize
  149. if n.sym.bitsize == 0: # 0 represents bitsize not set
  150. computeSizeAlign(conf, n.sym.typ)
  151. size = n.sym.typ.size.int
  152. align = n.sym.typ.align.int
  153. result.align = align
  154. if initialOffset == szUnknownSize or size == szUnknownSize:
  155. n.sym.offset = szUnknownSize
  156. result.offset = szUnknownSize
  157. else:
  158. n.sym.offset = align(initialOffset, align).int
  159. result.offset = n.sym.offset + n.sym.typ.size
  160. else:
  161. result.align = szUnknownSize
  162. result.offset = szUnknownSize
  163. proc computePackedObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOffset: BiggestInt, debug: bool): BiggestInt =
  164. ## ``result`` is the offset within the object, after the node has been written, no padding bytes added
  165. case n.kind
  166. of nkRecCase:
  167. assert(n.sons[0].kind == nkSym)
  168. let kindOffset = computePackedObjectOffsetsFoldFunction(conf, n.sons[0], initialOffset, debug)
  169. # the union neds to be aligned first, before the offsets can be assigned
  170. let kindUnionOffset = kindOffset
  171. var maxChildOffset: BiggestInt = kindUnionOffset
  172. for i in 1 ..< sonsLen(n):
  173. let offset = computePackedObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset, debug)
  174. if offset == szIllegalRecursion:
  175. return szIllegalRecursion
  176. if offset == szUnknownSize or maxChildOffset == szUnknownSize:
  177. maxChildOffset = szUnknownSize
  178. else:
  179. maxChildOffset = max(maxChildOffset, offset)
  180. result = maxChildOffset
  181. of nkRecList:
  182. result = initialOffset
  183. for i, child in n.sons:
  184. result = computePackedObjectOffsetsFoldFunction(conf, child, result, debug)
  185. if result == szIllegalRecursion:
  186. break
  187. of nkSym:
  188. var size = szUnknownSize
  189. if n.sym.bitsize == 0:
  190. computeSizeAlign(conf, n.sym.typ)
  191. size = n.sym.typ.size.int
  192. if initialOffset == szUnknownSize or size == szUnknownSize:
  193. n.sym.offset = szUnknownSize
  194. result = szUnknownSize
  195. else:
  196. n.sym.offset = int(initialOffset)
  197. result = initialOffset + n.sym.typ.size
  198. else:
  199. result = szUnknownSize
  200. proc computeUnionObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, debug: bool): tuple[offset, align: BiggestInt] =
  201. ## ``result`` is the offset from the larget member of the union.
  202. case n.kind
  203. of nkRecCase:
  204. result.offset = szUnknownSize
  205. result.align = szUnknownSize
  206. localError(conf, n.info, "Illegal use of ``case`` in union type.")
  207. #internalError(conf, "Illegal use of ``case`` in union type.")
  208. of nkRecList:
  209. var maxChildOffset: BiggestInt = 0
  210. for i, child in n.sons:
  211. let (offset, align) = computeUnionObjectOffsetsFoldFunction(conf, child, debug)
  212. if offset == szIllegalRecursion or align == szIllegalRecursion:
  213. result.offset = szIllegalRecursion
  214. result.align = szIllegalRecursion
  215. elif offset == szUnknownSize or align == szUnknownSize:
  216. result.offset = szUnknownSize
  217. result.align = szUnknownSize
  218. else:
  219. assert offset != szUncomputedSize
  220. assert align != szUncomputedSize
  221. result.offset = max(result.offset, offset)
  222. result.align = max(result.align, align)
  223. of nkSym:
  224. var size = szUnknownSize
  225. var align = szUnknownSize
  226. if n.sym.bitsize == 0: # 0 represents bitsize not set
  227. computeSizeAlign(conf, n.sym.typ)
  228. size = n.sym.typ.size.int
  229. align = n.sym.typ.align.int
  230. result.align = align
  231. if size == szUnknownSize:
  232. n.sym.offset = szUnknownSize
  233. result.offset = szUnknownSize
  234. else:
  235. n.sym.offset = 0
  236. result.offset = n.sym.typ.size
  237. else:
  238. result.offset = szUnknownSize
  239. result.align = szUnknownSize
  240. proc computeSizeAlign(conf: ConfigRef; typ: PType) =
  241. ## computes and sets ``size`` and ``align`` members of ``typ``
  242. assert typ != nil
  243. let hasSize = typ.size != szUncomputedSize
  244. let hasAlign = typ.align != szUncomputedSize
  245. if hasSize and hasAlign:
  246. # nothing to do, size and align already computed
  247. return
  248. # This function can only calculate both, size and align at the same time.
  249. # If one of them is already set this value is stored here and reapplied
  250. let revertSize = typ.size
  251. let revertAlign = typ.align
  252. defer:
  253. if hasSize:
  254. typ.size = revertSize
  255. if hasAlign:
  256. typ.align = revertAlign
  257. if typ.size == szIllegalRecursion or typ.align == szIllegalRecursion:
  258. # we are already computing the size of the type
  259. # --> illegal recursion in type
  260. return
  261. # mark computation in progress
  262. typ.size = szIllegalRecursion
  263. typ.align = szIllegalRecursion
  264. var maxAlign, sizeAccum, length: BiggestInt
  265. var tk = typ.kind
  266. case tk
  267. of tyProc:
  268. if typ.callConv == ccClosure:
  269. typ.size = 2 * conf.target.ptrSize
  270. else:
  271. typ.size = conf.target.ptrSize
  272. typ.align = int16(conf.target.ptrSize)
  273. of tyNil:
  274. typ.size = conf.target.ptrSize
  275. typ.align = int16(conf.target.ptrSize)
  276. of tyString:
  277. if conf.selectedGC == gcDestructors:
  278. typ.size = conf.target.ptrSize * 2
  279. else:
  280. typ.size = conf.target.ptrSize
  281. typ.align = int16(conf.target.ptrSize)
  282. of tyCString, tySequence, tyPtr, tyRef, tyVar, tyLent, tyOpenArray:
  283. let base = typ.lastSon
  284. if base == typ:
  285. # this is not the correct location to detect ``type A = ptr A``
  286. typ.size = szIllegalRecursion
  287. typ.align = szIllegalRecursion
  288. return
  289. typ.align = int16(conf.target.ptrSize)
  290. if typ.kind == tySequence and conf.selectedGC == gcDestructors:
  291. typ.size = conf.target.ptrSize * 2
  292. else:
  293. typ.size = conf.target.ptrSize
  294. of tyArray:
  295. computeSizeAlign(conf, typ.sons[1])
  296. let elemSize = typ.sons[1].size
  297. if elemSize < 0:
  298. typ.size = elemSize
  299. typ.align = int16(elemSize)
  300. else:
  301. typ.size = lengthOrd(conf, typ.sons[0]) * elemSize
  302. typ.align = typ.sons[1].align
  303. of tyUncheckedArray:
  304. let base = typ.lastSon
  305. computeSizeAlign(conf, base)
  306. typ.size = 0
  307. typ.align = base.align
  308. of tyEnum:
  309. if firstOrd(conf, typ) < 0:
  310. typ.size = 4 # use signed int32
  311. typ.align = 4
  312. else:
  313. length = lastOrd(conf, typ) # BUGFIX: use lastOrd!
  314. if length + 1 < `shl`(1, 8):
  315. typ.size = 1
  316. typ.align = 1
  317. elif length + 1 < `shl`(1, 16):
  318. typ.size = 2
  319. typ.align = 2
  320. elif length + 1 < `shl`(BiggestInt(1), 32):
  321. typ.size = 4
  322. typ.align = 4
  323. else:
  324. typ.size = 8
  325. typ.align = 8
  326. of tySet:
  327. if typ.sons[0].kind == tyGenericParam:
  328. typ.size = szUncomputedSize
  329. typ.align = szUncomputedSize # in original version this was 1
  330. else:
  331. length = lengthOrd(conf, typ.sons[0])
  332. if length <= 8:
  333. typ.size = 1
  334. elif length <= 16:
  335. typ.size = 2
  336. elif length <= 32:
  337. typ.size = 4
  338. elif length <= 64:
  339. typ.size = 8
  340. elif align(length, 8) mod 8 == 0:
  341. typ.size = align(length, 8) div 8
  342. else:
  343. typ.size = align(length, 8) div 8 + 1
  344. typ.align = int16(typ.size)
  345. of tyRange:
  346. computeSizeAlign(conf, typ.sons[0])
  347. typ.size = typ.sons[0].size
  348. typ.align = typ.sons[0].align
  349. of tyTuple:
  350. try:
  351. var accum = OffsetAccum(maxAlign: 1)
  352. for i in 0 ..< sonsLen(typ):
  353. let child = typ.sons[i]
  354. computeSizeAlign(conf, child)
  355. accum.align(child.align)
  356. if typ.n != nil: # is named tuple (has field symbols)?
  357. let sym = typ.n[i].sym
  358. sym.offset = accum.offset
  359. accum.inc(int(child.size))
  360. accum.finish
  361. typ.size = accum.offset
  362. typ.align = int16(accum.maxAlign)
  363. except IllegalTypeRecursionError:
  364. typ.size = szIllegalRecursion
  365. typ.align = szIllegalRecursion
  366. of tyObject:
  367. var headerSize: BiggestInt
  368. var headerAlign: int16
  369. if typ.sons[0] != nil:
  370. # compute header size
  371. if conf.cmd == cmdCompileToCpp:
  372. # if the target is C++ the members of this type are written
  373. # into the padding byets at the end of the parent type. At the
  374. # moment it is not supported to calculate that.
  375. headerSize = szUnknownSize
  376. headerAlign = szUncomputedSize
  377. else:
  378. var st = typ.sons[0]
  379. while st.kind in skipPtrs:
  380. st = st.sons[^1]
  381. computeSizeAlign(conf, st)
  382. if st.size == szIllegalRecursion:
  383. typ.size = st.size
  384. typ.align = st.align
  385. return
  386. headerSize = st.size
  387. headerAlign = st.align
  388. elif isObjectWithTypeFieldPredicate(typ):
  389. # this branch is taken for RootObj
  390. headerSize = conf.target.intSize
  391. headerAlign = conf.target.intSize.int16
  392. else:
  393. headerSize = 0
  394. headerAlign = 1
  395. let (offset, align) =
  396. if tfUnion in typ.flags:
  397. if tfPacked in typ.flags:
  398. let info = if typ.sym != nil: typ.sym.info else: unknownLineInfo()
  399. localError(conf, info, "type may not be packed and union at the same time.")
  400. (BiggestInt(szUnknownSize), BiggestInt(szUnknownSize))
  401. else:
  402. computeUnionObjectOffsetsFoldFunction(conf, typ.n, false)
  403. elif tfPacked in typ.flags:
  404. (computePackedObjectOffsetsFoldFunction(conf, typ.n, headerSize, false), BiggestInt(1))
  405. else:
  406. computeObjectOffsetsFoldFunction(conf, typ.n, headerSize)
  407. if offset == szIllegalRecursion:
  408. typ.size = szIllegalRecursion
  409. typ.align = szIllegalRecursion
  410. return
  411. if offset == szUnknownSize or (
  412. typ.sym != nil and
  413. typ.sym.flags * {sfCompilerProc, sfImportc} == {sfImportc}):
  414. typ.size = szUnknownSize
  415. typ.align = szUnknownSize
  416. return
  417. # header size is already in size from computeObjectOffsetsFoldFunction
  418. # maxAlign is probably not changed at all from headerAlign
  419. if tfPacked in typ.flags:
  420. typ.size = offset
  421. typ.align = 1
  422. else:
  423. typ.align = int16(max(align, headerAlign))
  424. typ.size = align(offset, typ.align)
  425. of tyInferred:
  426. if typ.len > 1:
  427. computeSizeAlign(conf, typ.lastSon)
  428. typ.size = typ.lastSon.size
  429. typ.align = typ.lastSon.align
  430. of tyGenericInst, tyDistinct, tyGenericBody, tyAlias, tySink, tyOwned:
  431. computeSizeAlign(conf, typ.lastSon)
  432. typ.size = typ.lastSon.size
  433. typ.align = typ.lastSon.align
  434. of tyTypeClasses:
  435. if typ.isResolvedUserTypeClass:
  436. computeSizeAlign(conf, typ.lastSon)
  437. typ.size = typ.lastSon.size
  438. typ.align = typ.lastSon.align
  439. else:
  440. typ.size = szUncomputedSize
  441. typ.align = szUncomputedSize
  442. of tyTypeDesc:
  443. computeSizeAlign(conf, typ.base)
  444. typ.size = typ.base.size
  445. typ.align = typ.base.align
  446. of tyForward:
  447. # is this really illegal recursion, or maybe just unknown?
  448. typ.size = szIllegalRecursion
  449. typ.align = szIllegalRecursion
  450. of tyStatic:
  451. if typ.n != nil:
  452. computeSizeAlign(conf, typ.lastSon)
  453. typ.size = typ.lastSon.size
  454. typ.align = typ.lastSon.align
  455. else:
  456. typ.size = szUncomputedSize
  457. typ.align = szUncomputedSize
  458. else:
  459. typ.size = szUncomputedSize
  460. typ.align = szUncomputedSize
  461. template foldSizeOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
  462. let config = conf
  463. let node = n
  464. let typ = node[1].typ
  465. computeSizeAlign(config, typ)
  466. let size = typ.size
  467. if size >= 0:
  468. let res = newIntNode(nkIntLit, size)
  469. res.info = node.info
  470. res.typ = node.typ
  471. res
  472. else:
  473. fallback
  474. template foldAlignOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
  475. let config = conf
  476. let node = n
  477. let typ = node[1].typ
  478. computeSizeAlign(config, typ)
  479. let align = typ.align
  480. if align >= 0:
  481. let res = newIntNode(nkIntLit, align)
  482. res.info = node.info
  483. res.typ = node.typ
  484. res
  485. else:
  486. fallback
  487. template foldOffsetOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
  488. ## Returns an int literal node of the given offsetof expression in `n`.
  489. ## Falls back to `fallback`, if the `offsetof` expression can't be processed.
  490. let config = conf
  491. let node : PNode = n
  492. var dotExpr: PNode
  493. block findDotExpr:
  494. if node[1].kind == nkDotExpr:
  495. dotExpr = node[1]
  496. elif node[1].kind == nkCheckedFieldExpr:
  497. dotExpr = node[1][0]
  498. else:
  499. localError(config, node.info, "can't compute offsetof on this ast")
  500. assert dotExpr != nil
  501. let value = dotExpr[0]
  502. let member = dotExpr[1]
  503. computeSizeAlign(config, value.typ)
  504. let offset = member.sym.offset
  505. if offset >= 0:
  506. let tmp = newIntNode(nkIntLit, offset)
  507. tmp.info = node.info
  508. tmp.typ = node.typ
  509. tmp
  510. else:
  511. fallback