sizealignoffsetimpl.nim 12 KB

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