sizealignoffsetimpl.nim 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  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: int32): int32 =
  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*: int32
  28. offset*: int32
  29. proc inc*(arg: var OffsetAccum; value: int32) =
  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: int32): int32 =
  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: int32) =
  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): int32 =
  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 = if accum.offset == szUnknownSize: szUnknownSize.int32 else: 1'i32
  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 = int32(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.int32
  138. var align = szUnknownSize.int32
  139. if n.sym.bitsize == 0: # 0 represents bitsize not set
  140. computeSizeAlign(conf, n.sym.typ)
  141. size = n.sym.typ.size.int32
  142. align = if packed: 1 else: n.sym.typ.align.int32
  143. accum.align(align)
  144. if n.sym.alignment > 0:
  145. accum.align(n.sym.alignment.int32)
  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.int32
  167. var align = szUnknownSize.int32
  168. if n.sym.bitsize == 0: # 0 represents bitsize not set
  169. computeSizeAlign(conf, n.sym.typ)
  170. size = n.sym.typ.size.int32
  171. align = if packed: 1 else: n.sym.typ.align.int32
  172. accum.align(align)
  173. if n.sym.alignment > 0:
  174. accum.align(n.sym.alignment.int32)
  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.last
  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.elementType)
  241. let elemSize = typ.elementType.size
  242. let len = lengthOrd(conf, typ.indexType)
  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.elementType.align
  252. of tyUncheckedArray:
  253. let base = typ.last
  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.elementType.kind == tyGenericParam:
  277. typ.size = szUncomputedSize
  278. typ.align = szUncomputedSize
  279. else:
  280. let length = toInt64(lengthOrd(conf, typ.elementType))
  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.elementType)
  301. typ.size = typ.elementType.size
  302. typ.align = typ.elementType.align
  303. typ.paddingAtEnd = typ.elementType.paddingAtEnd
  304. of tyTuple:
  305. try:
  306. var accum = OffsetAccum(maxAlign: 1)
  307. for i, child in typ.ikids:
  308. computeSizeAlign(conf, child)
  309. accum.align(child.align)
  310. if typ.n != nil: # is named tuple (has field symbols)?
  311. let sym = typ.n[i].sym
  312. sym.offset = accum.offset
  313. accum.inc(int32(child.size))
  314. typ.paddingAtEnd = int16(accum.finish())
  315. typ.size = if accum.offset == 0: 1 else: accum.offset
  316. typ.align = int16(accum.maxAlign)
  317. except IllegalTypeRecursionError:
  318. typ.paddingAtEnd = szIllegalRecursion
  319. typ.size = szIllegalRecursion
  320. typ.align = szIllegalRecursion
  321. of tyObject:
  322. try:
  323. var accum =
  324. if typ.baseClass != nil:
  325. # compute header size
  326. var st = typ.baseClass
  327. while st.kind in skipPtrs:
  328. st = st.skipModifier
  329. computeSizeAlign(conf, st)
  330. if conf.backend == backendCpp:
  331. OffsetAccum(
  332. offset: int32(st.size) - int32(st.paddingAtEnd),
  333. maxAlign: st.align
  334. )
  335. else:
  336. OffsetAccum(
  337. offset: int32(st.size),
  338. maxAlign: st.align
  339. )
  340. elif isObjectWithTypeFieldPredicate(typ):
  341. # this branch is taken for RootObj
  342. OffsetAccum(
  343. offset: conf.target.intSize.int32,
  344. maxAlign: conf.target.intSize.int32
  345. )
  346. else:
  347. OffsetAccum(maxAlign: 1)
  348. if tfUnion in typ.flags:
  349. if accum.offset != 0:
  350. let info = if typ.sym != nil: typ.sym.info else: unknownLineInfo
  351. localError(conf, info, "union type may not have an object header")
  352. accum = OffsetAccum(offset: szUnknownSize, maxAlign: szUnknownSize)
  353. else:
  354. computeUnionObjectOffsetsFoldFunction(conf, typ.n, tfPacked in typ.flags, accum)
  355. elif tfPacked in typ.flags:
  356. accum.maxAlign = 1
  357. computeObjectOffsetsFoldFunction(conf, typ.n, true, accum)
  358. else:
  359. if typ.baseClass == nil and lacksMTypeField(typ) and typ.n.len == 1 and
  360. typ.n[0].kind == nkSym and
  361. typ.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
  362. # a dummy field is generated for an object with a single field
  363. # with an UncheckedArray type
  364. assert accum.offset == 0
  365. accum.offset = 1
  366. computeObjectOffsetsFoldFunction(conf, typ.n, false, accum)
  367. let paddingAtEnd = int16(accum.finish())
  368. if typ.sym != nil and
  369. typ.sym.flags * {sfCompilerProc, sfImportc} == {sfImportc} and
  370. tfCompleteStruct notin typ.flags:
  371. typ.size = szUnknownSize
  372. typ.align = szUnknownSize
  373. typ.paddingAtEnd = szUnknownSize
  374. else:
  375. typ.size = if accum.offset == 0: 1 else: accum.offset
  376. typ.align = int16(accum.maxAlign)
  377. typ.paddingAtEnd = paddingAtEnd
  378. except IllegalTypeRecursionError:
  379. typ.size = szIllegalRecursion
  380. typ.align = szIllegalRecursion
  381. typ.paddingAtEnd = szIllegalRecursion
  382. of tyInferred:
  383. if typ.hasElementType:
  384. computeSizeAlign(conf, typ.last)
  385. typ.size = typ.last.size
  386. typ.align = typ.last.align
  387. typ.paddingAtEnd = typ.last.paddingAtEnd
  388. of tyGenericInst, tyDistinct, tyGenericBody, tyAlias, tySink, tyOwned:
  389. computeSizeAlign(conf, typ.skipModifier)
  390. typ.size = typ.skipModifier.size
  391. typ.align = typ.skipModifier.align
  392. typ.paddingAtEnd = typ.last.paddingAtEnd
  393. of tyTypeClasses:
  394. if typ.isResolvedUserTypeClass:
  395. computeSizeAlign(conf, typ.last)
  396. typ.size = typ.last.size
  397. typ.align = typ.last.align
  398. typ.paddingAtEnd = typ.last.paddingAtEnd
  399. else:
  400. typ.size = szUnknownSize
  401. typ.align = szUnknownSize
  402. typ.paddingAtEnd = szUnknownSize
  403. of tyTypeDesc:
  404. computeSizeAlign(conf, typ.base)
  405. typ.size = typ.base.size
  406. typ.align = typ.base.align
  407. typ.paddingAtEnd = typ.base.paddingAtEnd
  408. of tyForward:
  409. typ.size = szUnknownSize
  410. typ.align = szUnknownSize
  411. typ.paddingAtEnd = szUnknownSize
  412. of tyStatic:
  413. if typ.n != nil:
  414. computeSizeAlign(conf, typ.last)
  415. typ.size = typ.last.size
  416. typ.align = typ.last.align
  417. typ.paddingAtEnd = typ.last.paddingAtEnd
  418. else:
  419. typ.size = szUnknownSize
  420. typ.align = szUnknownSize
  421. typ.paddingAtEnd = szUnknownSize
  422. of tyInt, tyUInt:
  423. setSize typ, conf.target.intSize.int16
  424. of tyBool, tyChar, tyUInt8, tyInt8:
  425. setSize typ, 1
  426. of tyInt16, tyUInt16:
  427. setSize typ, 2
  428. of tyInt32, tyUInt32, tyFloat32:
  429. setSize typ, 4
  430. of tyInt64, tyUInt64, tyFloat64, tyFloat:
  431. setSize typ, 8
  432. else:
  433. typ.size = szUnknownSize
  434. typ.align = szUnknownSize
  435. typ.paddingAtEnd = szUnknownSize
  436. template foldSizeOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
  437. let config = conf
  438. let node = n
  439. let typ = node[1].typ
  440. computeSizeAlign(config, typ)
  441. let size = typ.size
  442. if size >= 0:
  443. let res = newIntNode(nkIntLit, size)
  444. res.info = node.info
  445. res.typ() = node.typ
  446. res
  447. else:
  448. fallback
  449. template foldAlignOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
  450. let config = conf
  451. let node = n
  452. let typ = node[1].typ
  453. computeSizeAlign(config, typ)
  454. let align = typ.align
  455. if align >= 0:
  456. let res = newIntNode(nkIntLit, align)
  457. res.info = node.info
  458. res.typ() = node.typ
  459. res
  460. else:
  461. fallback
  462. template foldOffsetOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
  463. ## Returns an int literal node of the given offsetof expression in `n`.
  464. ## Falls back to `fallback`, if the `offsetof` expression can't be processed.
  465. let config = conf
  466. let node = n
  467. var dotExpr: PNode
  468. block findDotExpr:
  469. if node[1].kind == nkDotExpr:
  470. dotExpr = node[1]
  471. elif node[1].kind == nkCheckedFieldExpr:
  472. dotExpr = node[1][0]
  473. else:
  474. dotExpr = nil
  475. localError(config, node.info, "can't compute offsetof on this ast")
  476. assert dotExpr != nil
  477. let value = dotExpr[0]
  478. let member = dotExpr[1]
  479. computeSizeAlign(config, value.typ)
  480. let offset = member.sym.offset
  481. if offset >= 0:
  482. let tmp = newIntNode(nkIntLit, offset)
  483. tmp.info = node.info
  484. tmp.typ() = node.typ
  485. tmp
  486. else:
  487. fallback