sizealignoffsetimpl.nim 16 KB

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