sizealignoffsetimpl.nim 16 KB

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