sizealignoffsetimpl.nim 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  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 neds 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 = accumRoot
  130. computeObjectOffsetsFoldFunction(conf, n[i].lastSon, packed, branchAccum)
  131. accum.mergeBranch(branchAccum)
  132. of nkRecList:
  133. for i, child in n.sons:
  134. computeObjectOffsetsFoldFunction(conf, child, packed, accum)
  135. of nkSym:
  136. var size = szUnknownSize
  137. var align = szUnknownSize
  138. if n.sym.bitsize == 0: # 0 represents bitsize not set
  139. computeSizeAlign(conf, n.sym.typ)
  140. size = n.sym.typ.size.int
  141. align = if packed: 1 else: n.sym.typ.align.int
  142. accum.align(align)
  143. if n.sym.alignment > 0:
  144. accum.align(n.sym.alignment)
  145. n.sym.offset = accum.offset
  146. accum.inc(size)
  147. else:
  148. accum.maxAlign = szUnknownSize
  149. accum.offset = szUnknownSize
  150. proc computeUnionObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode; packed: bool; accum: var OffsetAccum) =
  151. ## ``accum.offset`` will the offset from the larget member of the union.
  152. case n.kind
  153. of nkRecCase:
  154. accum.offset = szUnknownSize
  155. accum.maxAlign = szUnknownSize
  156. localError(conf, n.info, "Illegal use of ``case`` in union type.")
  157. of nkRecList:
  158. let accumRoot = accum # copy, because each branch should start af the same offset
  159. for i, child in n.sons:
  160. var branchAccum = accumRoot
  161. computeUnionObjectOffsetsFoldFunction(conf, child, packed, branchAccum)
  162. accum.mergeBranch(branchAccum)
  163. of nkSym:
  164. var size = szUnknownSize
  165. var align = szUnknownSize
  166. if n.sym.bitsize == 0: # 0 represents bitsize not set
  167. computeSizeAlign(conf, n.sym.typ)
  168. size = n.sym.typ.size.int
  169. align = if packed: 1 else: n.sym.typ.align.int
  170. accum.align(align)
  171. if n.sym.alignment > 0:
  172. accum.align(n.sym.alignment)
  173. n.sym.offset = accum.offset
  174. accum.inc(size)
  175. else:
  176. accum.maxAlign = szUnknownSize
  177. accum.offset = szUnknownSize
  178. proc computeSizeAlign(conf: ConfigRef; typ: PType) =
  179. ## computes and sets ``size`` and ``align`` members of ``typ``
  180. assert typ != nil
  181. let hasSize = typ.size != szUncomputedSize
  182. let hasAlign = typ.align != szUncomputedSize
  183. if hasSize and hasAlign:
  184. # nothing to do, size and align already computed
  185. return
  186. # This function can only calculate both, size and align at the same time.
  187. # If one of them is already set this value is stored here and reapplied
  188. let revertSize = typ.size
  189. let revertAlign = typ.align
  190. defer:
  191. if hasSize:
  192. typ.size = revertSize
  193. if hasAlign:
  194. typ.align = revertAlign
  195. if typ.size == szIllegalRecursion or typ.align == szIllegalRecursion:
  196. # we are already computing the size of the type
  197. # --> illegal recursion in type
  198. return
  199. # mark computation in progress
  200. typ.size = szIllegalRecursion
  201. typ.align = szIllegalRecursion
  202. typ.paddingAtEnd = 0
  203. var tk = typ.kind
  204. case tk
  205. of tyProc:
  206. if typ.callConv == ccClosure:
  207. typ.size = 2 * conf.target.ptrSize
  208. else:
  209. typ.size = conf.target.ptrSize
  210. typ.align = int16(conf.target.ptrSize)
  211. of tyNil:
  212. typ.size = conf.target.ptrSize
  213. typ.align = int16(conf.target.ptrSize)
  214. of tyString:
  215. if optSeqDestructors in conf.globalOptions:
  216. typ.size = conf.target.ptrSize * 2
  217. else:
  218. typ.size = conf.target.ptrSize
  219. typ.align = int16(conf.target.ptrSize)
  220. of tyCString, tySequence, tyPtr, tyRef, tyVar, tyLent, tyOpenArray:
  221. let base = typ.lastSon
  222. if base == typ:
  223. # this is not the correct location to detect ``type A = ptr A``
  224. typ.size = szIllegalRecursion
  225. typ.align = szIllegalRecursion
  226. typ.paddingAtEnd = szIllegalRecursion
  227. return
  228. typ.align = int16(conf.target.ptrSize)
  229. if typ.kind == tySequence and optSeqDestructors in conf.globalOptions:
  230. typ.size = conf.target.ptrSize * 2
  231. else:
  232. typ.size = conf.target.ptrSize
  233. of tyArray:
  234. computeSizeAlign(conf, typ[1])
  235. let elemSize = typ[1].size
  236. if elemSize < 0:
  237. typ.size = elemSize
  238. typ.align = int16(elemSize)
  239. else:
  240. typ.size = toInt64Checked(lengthOrd(conf, typ[0]) * int32(elemSize), szTooBigSize)
  241. typ.align = typ[1].align
  242. of tyUncheckedArray:
  243. let base = typ.lastSon
  244. computeSizeAlign(conf, base)
  245. typ.size = 0
  246. typ.align = base.align
  247. of tyEnum:
  248. if firstOrd(conf, typ) < Zero:
  249. typ.size = 4 # use signed int32
  250. typ.align = 4
  251. else:
  252. let length = toInt64(lastOrd(conf, typ)) # BUGFIX: use lastOrd!
  253. if length + 1 < `shl`(1, 8):
  254. typ.size = 1
  255. typ.align = 1
  256. elif length + 1 < `shl`(1, 16):
  257. typ.size = 2
  258. typ.align = 2
  259. elif length + 1 < `shl`(BiggestInt(1), 32):
  260. typ.size = 4
  261. typ.align = 4
  262. else:
  263. typ.size = 8
  264. typ.align = int16(conf.floatInt64Align)
  265. of tySet:
  266. if typ[0].kind == tyGenericParam:
  267. typ.size = szUncomputedSize
  268. typ.align = szUncomputedSize
  269. else:
  270. let length = toInt64(lengthOrd(conf, typ[0]))
  271. if length <= 8:
  272. typ.size = 1
  273. typ.align = 1
  274. elif length <= 16:
  275. typ.size = 2
  276. typ.align = 2
  277. elif length <= 32:
  278. typ.size = 4
  279. typ.align = 4
  280. elif length <= 64:
  281. typ.size = 8
  282. typ.align = int16(conf.floatInt64Align)
  283. elif align(length, 8) mod 8 == 0:
  284. typ.size = align(length, 8) div 8
  285. typ.align = int16(conf.floatInt64Align)
  286. else:
  287. typ.size = align(length, 8) div 8 + 1
  288. typ.align = int16(conf.floatInt64Align)
  289. of tyRange:
  290. computeSizeAlign(conf, typ[0])
  291. typ.size = typ[0].size
  292. typ.align = typ[0].align
  293. typ.paddingAtEnd = typ[0].paddingAtEnd
  294. of tyTuple:
  295. try:
  296. var accum = OffsetAccum(maxAlign: 1)
  297. for i in 0..<typ.len:
  298. let child = typ[i]
  299. computeSizeAlign(conf, child)
  300. accum.align(child.align)
  301. if typ.n != nil: # is named tuple (has field symbols)?
  302. let sym = typ.n[i].sym
  303. sym.offset = accum.offset
  304. accum.inc(int(child.size))
  305. typ.paddingAtEnd = int16(accum.finish())
  306. typ.size = if accum.offset == 0: 1 else: accum.offset
  307. typ.align = int16(accum.maxAlign)
  308. except IllegalTypeRecursionError:
  309. typ.paddingAtEnd = szIllegalRecursion
  310. typ.size = szIllegalRecursion
  311. typ.align = szIllegalRecursion
  312. of tyObject:
  313. try:
  314. var accum =
  315. if typ[0] != nil:
  316. # compute header size
  317. var st = typ[0]
  318. while st.kind in skipPtrs:
  319. st = st[^1]
  320. computeSizeAlign(conf, st)
  321. if conf.backend == backendCpp:
  322. OffsetAccum(
  323. offset: int(st.size) - int(st.paddingAtEnd),
  324. maxAlign: st.align
  325. )
  326. else:
  327. OffsetAccum(
  328. offset: int(st.size),
  329. maxAlign: st.align
  330. )
  331. elif isObjectWithTypeFieldPredicate(typ):
  332. # this branch is taken for RootObj
  333. OffsetAccum(
  334. offset: conf.target.intSize,
  335. maxAlign: conf.target.intSize
  336. )
  337. else:
  338. OffsetAccum(maxAlign: 1)
  339. if tfUnion in typ.flags:
  340. if accum.offset != 0:
  341. let info = if typ.sym != nil: typ.sym.info else: unknownLineInfo
  342. localError(conf, info, "union type may not have an object header")
  343. accum = OffsetAccum(offset: szUnknownSize, maxAlign: szUnknownSize)
  344. elif tfPacked in typ.flags:
  345. computeUnionObjectOffsetsFoldFunction(conf, typ.n, true, accum)
  346. else:
  347. computeUnionObjectOffsetsFoldFunction(conf, typ.n, false, accum)
  348. elif tfPacked in typ.flags:
  349. accum.maxAlign = 1
  350. computeObjectOffsetsFoldFunction(conf, typ.n, true, accum)
  351. else:
  352. computeObjectOffsetsFoldFunction(conf, typ.n, false, accum)
  353. let paddingAtEnd = int16(accum.finish())
  354. if typ.sym != nil and
  355. typ.sym.flags * {sfCompilerProc, sfImportc} == {sfImportc} and
  356. tfCompleteStruct notin typ.flags:
  357. typ.size = szUnknownSize
  358. typ.align = szUnknownSize
  359. typ.paddingAtEnd = szUnknownSize
  360. else:
  361. typ.size = if accum.offset == 0: 1 else: accum.offset
  362. typ.align = int16(accum.maxAlign)
  363. typ.paddingAtEnd = paddingAtEnd
  364. except IllegalTypeRecursionError:
  365. typ.size = szIllegalRecursion
  366. typ.align = szIllegalRecursion
  367. typ.paddingAtEnd = szIllegalRecursion
  368. of tyInferred:
  369. if typ.len > 1:
  370. computeSizeAlign(conf, typ.lastSon)
  371. typ.size = typ.lastSon.size
  372. typ.align = typ.lastSon.align
  373. typ.paddingAtEnd = typ.lastSon.paddingAtEnd
  374. of tyGenericInst, tyDistinct, tyGenericBody, tyAlias, tySink, tyOwned:
  375. computeSizeAlign(conf, typ.lastSon)
  376. typ.size = typ.lastSon.size
  377. typ.align = typ.lastSon.align
  378. typ.paddingAtEnd = typ.lastSon.paddingAtEnd
  379. of tyTypeClasses:
  380. if typ.isResolvedUserTypeClass:
  381. computeSizeAlign(conf, typ.lastSon)
  382. typ.size = typ.lastSon.size
  383. typ.align = typ.lastSon.align
  384. typ.paddingAtEnd = typ.lastSon.paddingAtEnd
  385. else:
  386. typ.size = szUnknownSize
  387. typ.align = szUnknownSize
  388. typ.paddingAtEnd = szUnknownSize
  389. of tyTypeDesc:
  390. computeSizeAlign(conf, typ.base)
  391. typ.size = typ.base.size
  392. typ.align = typ.base.align
  393. typ.paddingAtEnd = typ.base.paddingAtEnd
  394. of tyForward:
  395. # is this really illegal recursion, or maybe just unknown?
  396. typ.size = szIllegalRecursion
  397. typ.align = szIllegalRecursion
  398. typ.paddingAtEnd = szIllegalRecursion
  399. of tyStatic:
  400. if typ.n != nil:
  401. computeSizeAlign(conf, typ.lastSon)
  402. typ.size = typ.lastSon.size
  403. typ.align = typ.lastSon.align
  404. typ.paddingAtEnd = typ.lastSon.paddingAtEnd
  405. else:
  406. typ.size = szUnknownSize
  407. typ.align = szUnknownSize
  408. typ.paddingAtEnd = szUnknownSize
  409. else:
  410. typ.size = szUnknownSize
  411. typ.align = szUnknownSize
  412. typ.paddingAtEnd = szUnknownSize
  413. template foldSizeOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
  414. let config = conf
  415. let node = n
  416. let typ = node[1].typ
  417. computeSizeAlign(config, typ)
  418. let size = typ.size
  419. if size >= 0:
  420. let res = newIntNode(nkIntLit, size)
  421. res.info = node.info
  422. res.typ = node.typ
  423. res
  424. else:
  425. fallback
  426. template foldAlignOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
  427. let config = conf
  428. let node = n
  429. let typ = node[1].typ
  430. computeSizeAlign(config, typ)
  431. let align = typ.align
  432. if align >= 0:
  433. let res = newIntNode(nkIntLit, align)
  434. res.info = node.info
  435. res.typ = node.typ
  436. res
  437. else:
  438. fallback
  439. template foldOffsetOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
  440. ## Returns an int literal node of the given offsetof expression in `n`.
  441. ## Falls back to `fallback`, if the `offsetof` expression can't be processed.
  442. let config = conf
  443. let node : PNode = n
  444. var dotExpr: PNode
  445. block findDotExpr:
  446. if node[1].kind == nkDotExpr:
  447. dotExpr = node[1]
  448. elif node[1].kind == nkCheckedFieldExpr:
  449. dotExpr = node[1][0]
  450. else:
  451. localError(config, node.info, "can't compute offsetof on this ast")
  452. assert dotExpr != nil
  453. let value = dotExpr[0]
  454. let member = dotExpr[1]
  455. computeSizeAlign(config, value.typ)
  456. let offset = member.sym.offset
  457. if offset >= 0:
  458. let tmp = newIntNode(nkIntLit, offset)
  459. tmp.info = node.info
  460. tmp.typ = node.typ
  461. tmp
  462. else:
  463. fallback