sizealignoffsetimpl.nim 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  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:
  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. let len = lengthOrd(conf, typ[0])
  239. if elemSize < 0:
  240. typ.size = elemSize
  241. typ.align = int16(elemSize)
  242. elif len < 0:
  243. typ.size = szUnknownSize
  244. typ.align = szUnknownSize
  245. else:
  246. typ.size = toInt64Checked(len * int32(elemSize), szTooBigSize)
  247. typ.align = typ[1].align
  248. of tyUncheckedArray:
  249. let base = typ.lastSon
  250. computeSizeAlign(conf, base)
  251. typ.size = 0
  252. typ.align = base.align
  253. of tyEnum:
  254. if firstOrd(conf, typ) < Zero:
  255. typ.size = 4 # use signed int32
  256. typ.align = 4
  257. else:
  258. let lastOrd = toInt64(lastOrd(conf, typ)) # BUGFIX: use lastOrd!
  259. if lastOrd < `shl`(1, 8):
  260. typ.size = 1
  261. typ.align = 1
  262. elif lastOrd < `shl`(1, 16):
  263. typ.size = 2
  264. typ.align = 2
  265. elif lastOrd < `shl`(BiggestInt(1), 32):
  266. typ.size = 4
  267. typ.align = 4
  268. else:
  269. typ.size = 8
  270. typ.align = int16(conf.floatInt64Align)
  271. of tySet:
  272. if typ[0].kind == tyGenericParam:
  273. typ.size = szUncomputedSize
  274. typ.align = szUncomputedSize
  275. else:
  276. let length = toInt64(lengthOrd(conf, typ[0]))
  277. if length <= 8:
  278. typ.size = 1
  279. typ.align = 1
  280. elif length <= 16:
  281. typ.size = 2
  282. typ.align = 2
  283. elif length <= 32:
  284. typ.size = 4
  285. typ.align = 4
  286. elif length <= 64:
  287. typ.size = 8
  288. typ.align = int16(conf.floatInt64Align)
  289. elif align(length, 8) mod 8 == 0:
  290. typ.size = align(length, 8) div 8
  291. typ.align = int16(conf.floatInt64Align)
  292. else:
  293. typ.size = align(length, 8) div 8 + 1
  294. typ.align = int16(conf.floatInt64Align)
  295. of tyRange:
  296. computeSizeAlign(conf, typ[0])
  297. typ.size = typ[0].size
  298. typ.align = typ[0].align
  299. typ.paddingAtEnd = typ[0].paddingAtEnd
  300. of tyTuple:
  301. try:
  302. var accum = OffsetAccum(maxAlign: 1)
  303. for i in 0..<typ.len:
  304. let child = typ[i]
  305. computeSizeAlign(conf, child)
  306. accum.align(child.align)
  307. if typ.n != nil: # is named tuple (has field symbols)?
  308. let sym = typ.n[i].sym
  309. sym.offset = accum.offset
  310. accum.inc(int(child.size))
  311. typ.paddingAtEnd = int16(accum.finish())
  312. typ.size = if accum.offset == 0: 1 else: accum.offset
  313. typ.align = int16(accum.maxAlign)
  314. except IllegalTypeRecursionError:
  315. typ.paddingAtEnd = szIllegalRecursion
  316. typ.size = szIllegalRecursion
  317. typ.align = szIllegalRecursion
  318. of tyObject:
  319. try:
  320. var accum =
  321. if typ[0] != nil:
  322. # compute header size
  323. var st = typ[0]
  324. while st.kind in skipPtrs:
  325. st = st[^1]
  326. computeSizeAlign(conf, st)
  327. if conf.backend == backendCpp:
  328. OffsetAccum(
  329. offset: int(st.size) - int(st.paddingAtEnd),
  330. maxAlign: st.align
  331. )
  332. else:
  333. OffsetAccum(
  334. offset: int(st.size),
  335. maxAlign: st.align
  336. )
  337. elif isObjectWithTypeFieldPredicate(typ):
  338. # this branch is taken for RootObj
  339. OffsetAccum(
  340. offset: conf.target.intSize,
  341. maxAlign: conf.target.intSize
  342. )
  343. else:
  344. OffsetAccum(maxAlign: 1)
  345. if tfUnion in typ.flags:
  346. if accum.offset != 0:
  347. let info = if typ.sym != nil: typ.sym.info else: unknownLineInfo
  348. localError(conf, info, "union type may not have an object header")
  349. accum = OffsetAccum(offset: szUnknownSize, maxAlign: szUnknownSize)
  350. elif tfPacked in typ.flags:
  351. computeUnionObjectOffsetsFoldFunction(conf, typ.n, true, accum)
  352. else:
  353. computeUnionObjectOffsetsFoldFunction(conf, typ.n, false, accum)
  354. elif tfPacked in typ.flags:
  355. accum.maxAlign = 1
  356. computeObjectOffsetsFoldFunction(conf, typ.n, true, accum)
  357. else:
  358. computeObjectOffsetsFoldFunction(conf, typ.n, false, accum)
  359. let paddingAtEnd = int16(accum.finish())
  360. if typ.sym != nil and
  361. typ.sym.flags * {sfCompilerProc, sfImportc} == {sfImportc} and
  362. tfCompleteStruct notin typ.flags:
  363. typ.size = szUnknownSize
  364. typ.align = szUnknownSize
  365. typ.paddingAtEnd = szUnknownSize
  366. else:
  367. typ.size = if accum.offset == 0: 1 else: accum.offset
  368. typ.align = int16(accum.maxAlign)
  369. typ.paddingAtEnd = paddingAtEnd
  370. except IllegalTypeRecursionError:
  371. typ.size = szIllegalRecursion
  372. typ.align = szIllegalRecursion
  373. typ.paddingAtEnd = szIllegalRecursion
  374. of tyInferred:
  375. if typ.len > 1:
  376. computeSizeAlign(conf, typ.lastSon)
  377. typ.size = typ.lastSon.size
  378. typ.align = typ.lastSon.align
  379. typ.paddingAtEnd = typ.lastSon.paddingAtEnd
  380. of tyGenericInst, tyDistinct, tyGenericBody, tyAlias, tySink, tyOwned:
  381. computeSizeAlign(conf, typ.lastSon)
  382. typ.size = typ.lastSon.size
  383. typ.align = typ.lastSon.align
  384. typ.paddingAtEnd = typ.lastSon.paddingAtEnd
  385. of tyTypeClasses:
  386. if typ.isResolvedUserTypeClass:
  387. computeSizeAlign(conf, typ.lastSon)
  388. typ.size = typ.lastSon.size
  389. typ.align = typ.lastSon.align
  390. typ.paddingAtEnd = typ.lastSon.paddingAtEnd
  391. else:
  392. typ.size = szUnknownSize
  393. typ.align = szUnknownSize
  394. typ.paddingAtEnd = szUnknownSize
  395. of tyTypeDesc:
  396. computeSizeAlign(conf, typ.base)
  397. typ.size = typ.base.size
  398. typ.align = typ.base.align
  399. typ.paddingAtEnd = typ.base.paddingAtEnd
  400. of tyForward:
  401. # is this really illegal recursion, or maybe just unknown?
  402. typ.size = szIllegalRecursion
  403. typ.align = szIllegalRecursion
  404. typ.paddingAtEnd = szIllegalRecursion
  405. of tyStatic:
  406. if typ.n != nil:
  407. computeSizeAlign(conf, typ.lastSon)
  408. typ.size = typ.lastSon.size
  409. typ.align = typ.lastSon.align
  410. typ.paddingAtEnd = typ.lastSon.paddingAtEnd
  411. else:
  412. typ.size = szUnknownSize
  413. typ.align = szUnknownSize
  414. typ.paddingAtEnd = szUnknownSize
  415. else:
  416. typ.size = szUnknownSize
  417. typ.align = szUnknownSize
  418. typ.paddingAtEnd = szUnknownSize
  419. template foldSizeOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
  420. let config = conf
  421. let node = n
  422. let typ = node[1].typ
  423. computeSizeAlign(config, typ)
  424. let size = typ.size
  425. if size >= 0:
  426. let res = newIntNode(nkIntLit, size)
  427. res.info = node.info
  428. res.typ = node.typ
  429. res
  430. else:
  431. fallback
  432. template foldAlignOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
  433. let config = conf
  434. let node = n
  435. let typ = node[1].typ
  436. computeSizeAlign(config, typ)
  437. let align = typ.align
  438. if align >= 0:
  439. let res = newIntNode(nkIntLit, align)
  440. res.info = node.info
  441. res.typ = node.typ
  442. res
  443. else:
  444. fallback
  445. template foldOffsetOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
  446. ## Returns an int literal node of the given offsetof expression in `n`.
  447. ## Falls back to `fallback`, if the `offsetof` expression can't be processed.
  448. let config = conf
  449. let node : PNode = n
  450. var dotExpr: PNode
  451. block findDotExpr:
  452. if node[1].kind == nkDotExpr:
  453. dotExpr = node[1]
  454. elif node[1].kind == nkCheckedFieldExpr:
  455. dotExpr = node[1][0]
  456. else:
  457. localError(config, node.info, "can't compute offsetof on this ast")
  458. assert dotExpr != nil
  459. let value = dotExpr[0]
  460. let member = dotExpr[1]
  461. computeSizeAlign(config, value.typ)
  462. let offset = member.sym.offset
  463. if offset >= 0:
  464. let tmp = newIntNode(nkIntLit, offset)
  465. tmp.info = node.info
  466. tmp.typ = node.typ
  467. tmp
  468. else:
  469. fallback