sizealignoffsetimpl.nim 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. proc align(address, alignment: BiggestInt): BiggestInt =
  2. result = (address + (alignment - 1)) and not (alignment - 1)
  3. const
  4. ## a size is concidered "unknown" when it is an imported type from C
  5. ## or C++.
  6. szUnknownSize* = -3
  7. szIllegalRecursion* = -2
  8. szUncomputedSize* = -1
  9. proc computeSizeAlign(conf: ConfigRef; typ: PType): void
  10. proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt =
  11. ## returns object alignment
  12. case n.kind
  13. of nkRecCase:
  14. assert(n.sons[0].kind == nkSym)
  15. result = computeSubObjectAlign(conf, n.sons[0])
  16. for i in 1 ..< sonsLen(n):
  17. let child = n.sons[i]
  18. case child.kind
  19. of nkOfBranch, nkElse:
  20. let align = computeSubObjectAlign(conf, child.lastSon)
  21. if align < 0:
  22. return align
  23. result = max(result, align)
  24. else:
  25. internalError(conf, "computeSubObjectAlign")
  26. of nkRecList:
  27. result = 1
  28. for i, child in n.sons:
  29. let align = computeSubObjectAlign(conf, n.sons[i])
  30. if align < 0:
  31. return align
  32. result = max(result, align)
  33. of nkSym:
  34. computeSizeAlign(conf, n.sym.typ)
  35. result = n.sym.typ.align
  36. else:
  37. result = 1
  38. proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOffset: BiggestInt): tuple[offset, align: BiggestInt] =
  39. ## ``offset`` is the offset within the object, after the node has been written, no padding bytes added
  40. ## ``align`` maximum alignment from all sub nodes
  41. if n.typ != nil and n.typ.size == szIllegalRecursion:
  42. result.offset = szIllegalRecursion
  43. result.align = szIllegalRecursion
  44. return
  45. result.align = 1
  46. case n.kind
  47. of nkRecCase:
  48. assert(n.sons[0].kind == nkSym)
  49. let (kindOffset, kindAlign) = computeObjectOffsetsFoldFunction(conf, n.sons[0], initialOffset)
  50. var maxChildAlign: BiggestInt = 0
  51. for i in 1 ..< sonsLen(n):
  52. let child = n.sons[i]
  53. case child.kind
  54. of nkOfBranch, nkElse:
  55. # offset parameter cannot be known yet, it needs to know the alignment first
  56. let align = computeSubObjectAlign(conf, n.sons[i].lastSon)
  57. if align == szIllegalRecursion:
  58. result.offset = szIllegalRecursion
  59. result.align = szIllegalRecursion
  60. return
  61. if align == szUnknownSize or maxChildAlign == szUnknownSize:
  62. maxChildAlign = szUnknownSize
  63. else:
  64. maxChildAlign = max(maxChildAlign, align)
  65. else:
  66. internalError(conf, "computeObjectOffsetsFoldFunction(record case branch)")
  67. if maxChildAlign == szUnknownSize:
  68. result.align = szUnknownSize
  69. result.offset = szUnknownSize
  70. else:
  71. # the union neds to be aligned first, before the offsets can be assigned
  72. let kindUnionOffset = align(kindOffset, maxChildAlign)
  73. var maxChildOffset: BiggestInt = 0
  74. for i in 1 ..< sonsLen(n):
  75. let (offset, align) = computeObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset)
  76. maxChildOffset = max(maxChildOffset, offset)
  77. result.align = max(kindAlign, maxChildAlign)
  78. result.offset = maxChildOffset
  79. of nkRecList:
  80. result.align = 1 # maximum of all member alignments
  81. var offset = initialOffset
  82. for i, child in n.sons:
  83. let (new_offset, align) = computeObjectOffsetsFoldFunction(conf, child, offset)
  84. if new_offset == szIllegalRecursion:
  85. result.offset = szIllegalRecursion
  86. result.align = szIllegalRecursion
  87. return
  88. elif new_offset == szUnknownSize or offset == szUnknownSize:
  89. # if anything is unknown, the rest becomes unknown as well
  90. offset = szUnknownSize
  91. result.align = szUnknownSize
  92. else:
  93. offset = new_offset
  94. result.align = max(result.align, align)
  95. # final alignment
  96. if offset == szUnknownSize:
  97. result.offset = szUnknownSize
  98. else:
  99. result.offset = align(offset, result.align)
  100. of nkSym:
  101. computeSizeAlign(conf, n.sym.typ)
  102. let size = n.sym.typ.size
  103. let align = n.sym.typ.align
  104. result.align = align
  105. if initialOffset == szUnknownSize:
  106. n.sym.offset = szUnknownSize
  107. result.offset = szUnknownSize
  108. else:
  109. n.sym.offset = align(initialOffset, align).int
  110. result.offset = n.sym.offset + n.sym.typ.size
  111. else:
  112. result.align = szUnknownSize
  113. result.offset = szUnknownSize
  114. proc computePackedObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOffset: BiggestInt, debug: bool): BiggestInt =
  115. ## ``result`` is the offset within the object, after the node has been written, no padding bytes added
  116. case n.kind
  117. of nkRecCase:
  118. assert(n.sons[0].kind == nkSym)
  119. let kindOffset = computePackedObjectOffsetsFoldFunction(conf, n.sons[0], initialOffset, debug)
  120. # the union neds to be aligned first, before the offsets can be assigned
  121. let kindUnionOffset = kindOffset
  122. var maxChildOffset: BiggestInt = kindUnionOffset
  123. for i in 1 ..< sonsLen(n):
  124. let offset = computePackedObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset, debug)
  125. maxChildOffset = max(maxChildOffset, offset)
  126. result = maxChildOffset
  127. of nkRecList:
  128. result = initialOffset
  129. for i, child in n.sons:
  130. result = computePackedObjectOffsetsFoldFunction(conf, child, result, debug)
  131. if result == szIllegalRecursion:
  132. break
  133. of nkSym:
  134. computeSizeAlign(conf, n.sym.typ)
  135. n.sym.offset = initialOffset.int
  136. result = n.sym.offset + n.sym.typ.size
  137. else:
  138. result = szUnknownSize
  139. # TODO this one needs an alignment map of the individual types
  140. proc computeSizeAlign(conf: ConfigRef; typ: PType) =
  141. ## computes and sets ``size`` and ``align`` members of ``typ``
  142. let hasSize = typ.size != szUncomputedSize
  143. let hasAlign = typ.align != szUncomputedSize
  144. if hasSize and hasAlign:
  145. # nothing to do, size and align already computed
  146. return
  147. # This function can only calculate both, size and align at the same time.
  148. # If one of them is already set this value is stored here and reapplied
  149. let revertSize = typ.size
  150. let revertAlign = typ.align
  151. defer:
  152. if hasSize:
  153. typ.size = revertSize
  154. if hasAlign:
  155. typ.align = revertAlign
  156. if typ.size == szIllegalRecursion or typ.align == szIllegalRecursion:
  157. # we are already computing the size of the type
  158. # --> illegal recursion in type
  159. return
  160. # mark computation in progress
  161. typ.size = szIllegalRecursion
  162. typ.align = szIllegalRecursion
  163. var maxAlign, sizeAccum, length: BiggestInt
  164. var tk = typ.kind
  165. case tk
  166. of tyProc:
  167. if typ.callConv == ccClosure:
  168. typ.size = 2 * conf.target.ptrSize
  169. else:
  170. typ.size = conf.target.ptrSize
  171. typ.align = int16(conf.target.ptrSize)
  172. of tyNil:
  173. typ.size = conf.target.ptrSize
  174. typ.align = int16(conf.target.ptrSize)
  175. of tyString:
  176. if tfHasAsgn in typ.flags:
  177. typ.size = conf.target.ptrSize * 2
  178. else:
  179. typ.size = conf.target.ptrSize
  180. typ.align = int16(conf.target.ptrSize)
  181. of tyCString, tySequence, tyPtr, tyRef, tyVar, tyLent, tyOpenArray:
  182. let base = typ.lastSon
  183. if base == typ:
  184. # this is not the correct location to detect ``type A = ptr A``
  185. typ.size = szIllegalRecursion
  186. typ.align = szIllegalRecursion
  187. return
  188. # recursive tuplers are not allowed and should be detected in the frontend
  189. if base.kind == tyTuple:
  190. computeSizeAlign(conf, base)
  191. if base.size == szIllegalRecursion:
  192. typ.size = szIllegalRecursion
  193. typ.align = szIllegalRecursion
  194. return
  195. typ.align = int16(conf.target.ptrSize)
  196. if typ.kind == tySequence and tfHasAsgn in typ.flags:
  197. typ.size = conf.target.ptrSize * 2
  198. else:
  199. typ.size = conf.target.ptrSize
  200. of tyArray:
  201. computeSizeAlign(conf, typ.sons[1])
  202. let elemSize = typ.sons[1].size
  203. if elemSize < 0:
  204. typ.size = elemSize
  205. typ.align = int16(elemSize)
  206. else:
  207. typ.size = lengthOrd(conf, typ.sons[0]) * elemSize
  208. typ.align = typ.sons[1].align
  209. of tyUncheckedArray:
  210. let base = typ.lastSon
  211. computeSizeAlign(conf, base)
  212. typ.size = szUnknownSize
  213. typ.align = base.align
  214. of tyEnum:
  215. if firstOrd(conf, typ) < 0:
  216. typ.size = 4 # use signed int32
  217. typ.align = 4
  218. else:
  219. length = lastOrd(conf, typ) # BUGFIX: use lastOrd!
  220. if length + 1 < `shl`(1, 8):
  221. typ.size = 1
  222. typ.align = 1
  223. elif length + 1 < `shl`(1, 16):
  224. typ.size = 2
  225. typ.align = 2
  226. elif length + 1 < `shl`(BiggestInt(1), 32):
  227. typ.size = 4
  228. typ.align = 4
  229. else:
  230. typ.size = 8
  231. typ.align = 8
  232. of tySet:
  233. if typ.sons[0].kind == tyGenericParam:
  234. typ.size = szUncomputedSize
  235. typ.align = szUncomputedSize # in original version this was 1
  236. else:
  237. length = lengthOrd(conf, typ.sons[0])
  238. if length <= 8:
  239. typ.size = 1
  240. elif length <= 16:
  241. typ.size = 2
  242. elif length <= 32:
  243. typ.size = 4
  244. elif length <= 64:
  245. typ.size = 8
  246. elif align(length, 8) mod 8 == 0:
  247. typ.size = align(length, 8) div 8
  248. else:
  249. typ.size = align(length, 8) div 8 + 1
  250. typ.align = int16(typ.size)
  251. of tyRange:
  252. computeSizeAlign(conf, typ.sons[0])
  253. typ.size = typ.sons[0].size
  254. typ.align = typ.sons[0].align
  255. of tyTuple:
  256. maxAlign = 1
  257. sizeAccum = 0
  258. for i in countup(0, sonsLen(typ) - 1):
  259. let child = typ.sons[i]
  260. computeSizeAlign(conf, child)
  261. if child.size == szIllegalRecursion:
  262. typ.size = szIllegalRecursion
  263. typ.align = szIllegalRecursion
  264. return
  265. maxAlign = max(maxAlign, child.align)
  266. sizeAccum = align(sizeAccum, child.align) + child.size
  267. typ.size = align(sizeAccum, maxAlign)
  268. typ.align = int16(maxAlign)
  269. of tyObject:
  270. var headerSize: BiggestInt
  271. var headerAlign: int16
  272. if typ.sons[0] != nil:
  273. # compute header size
  274. var st = typ.sons[0]
  275. while st.kind in skipPtrs:
  276. st = st.sons[^1]
  277. computeSizeAlign(conf, st)
  278. if st.size == szIllegalRecursion:
  279. typ.size = st.size
  280. typ.align = st.align
  281. return
  282. headerSize = st.size
  283. headerAlign = st.align
  284. elif isObjectWithTypeFieldPredicate(typ):
  285. # this branch is taken for RootObj
  286. headerSize = conf.target.intSize
  287. headerAlign = conf.target.intSize.int16
  288. else:
  289. headerSize = 0
  290. headerAlign = 1
  291. let (offset, align) =
  292. if tfPacked in typ.flags:
  293. (computePackedObjectOffsetsFoldFunction(conf, typ.n, headerSize, false), BiggestInt(1))
  294. else:
  295. computeObjectOffsetsFoldFunction(conf, typ.n, headerSize)
  296. if offset == szIllegalRecursion:
  297. typ.size = szIllegalRecursion
  298. typ.align = szIllegalRecursion
  299. return
  300. if offset == szUnknownSize or (
  301. typ.sym != nil and
  302. typ.sym.flags * {sfCompilerProc, sfImportc} == {sfImportc}):
  303. typ.size = szUnknownSize
  304. typ.align = szUnknownSize
  305. return
  306. # header size is already in size from computeObjectOffsetsFoldFunction
  307. # maxAlign is probably not changed at all from headerAlign
  308. if tfPacked in typ.flags:
  309. typ.size = offset
  310. typ.align = 1
  311. else:
  312. typ.align = int16(max(align, headerAlign))
  313. typ.size = align(offset, typ.align)
  314. of tyInferred:
  315. if typ.len > 1:
  316. computeSizeAlign(conf, typ.lastSon)
  317. typ.size = typ.lastSon.size
  318. typ.align = typ.lastSon.align
  319. of tyGenericInst, tyDistinct, tyGenericBody, tyAlias:
  320. computeSizeAlign(conf, typ.lastSon)
  321. typ.size = typ.lastSon.size
  322. typ.align = typ.lastSon.align
  323. of tyTypeClasses:
  324. if typ.isResolvedUserTypeClass:
  325. computeSizeAlign(conf, typ.lastSon)
  326. typ.size = typ.lastSon.size
  327. typ.align = typ.lastSon.align
  328. else:
  329. typ.size = szUncomputedSize
  330. typ.align = szUncomputedSize
  331. of tyTypeDesc:
  332. computeSizeAlign(conf, typ.base)
  333. typ.size = typ.base.size
  334. typ.align = typ.base.align
  335. of tyForward:
  336. # is this really illegal recursion, or maybe just unknown?
  337. typ.size = szIllegalRecursion
  338. typ.align = szIllegalRecursion
  339. of tyStatic:
  340. if typ.n != nil:
  341. computeSizeAlign(conf, typ.lastSon)
  342. typ.size = typ.lastSon.size
  343. typ.align = typ.lastSon.align
  344. else:
  345. typ.size = szUncomputedSize
  346. typ.align = szUncomputedSize
  347. else:
  348. typ.size = szUncomputedSize
  349. typ.align = szUncomputedSize