nirinsts.nim 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2023 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## NIR instructions. Somewhat inspired by LLVM's instructions.
  10. import std / [assertions, hashes]
  11. import .. / ic / [bitabs, rodfiles]
  12. import nirlineinfos, nirtypes
  13. const
  14. NirVersion = 1
  15. nirCookie* = [byte(0), byte('N'), byte('I'), byte('R'),
  16. byte(sizeof(int)*8), byte(system.cpuEndian), byte(0), byte(NirVersion)]
  17. type
  18. SymId* = distinct int
  19. proc `$`*(s: SymId): string {.borrow.}
  20. proc hash*(s: SymId): Hash {.borrow.}
  21. proc `==`*(a, b: SymId): bool {.borrow.}
  22. type
  23. Opcode* = enum
  24. Nop,
  25. ImmediateVal,
  26. IntVal,
  27. StrVal,
  28. SymDef,
  29. SymUse,
  30. Typed, # with type ID
  31. PragmaId, # with Pragma ID, possible values: see PragmaKey enum
  32. NilVal,
  33. Label,
  34. Goto,
  35. CheckedGoto,
  36. LoopLabel,
  37. GotoLoop, # last atom
  38. ModuleSymUse, # `"module".x`
  39. ArrayConstr,
  40. ObjConstr,
  41. Ret,
  42. Yld,
  43. Select,
  44. SelectPair, # ((values...), Label)
  45. SelectList, # (values...)
  46. SelectValue, # (value)
  47. SelectRange, # (valueA..valueB)
  48. ForeignDecl, # Can wrap SummonGlobal, SummonThreadLocal, SummonConst
  49. SummonGlobal,
  50. SummonThreadLocal,
  51. Summon, # x = Summon Typed <Type ID>; x begins to live
  52. SummonResult,
  53. SummonParam,
  54. SummonConst,
  55. Kill, # `Kill x`: scope end for `x`
  56. AddrOf,
  57. ArrayAt, # a[i]
  58. DerefArrayAt, # a[i] where `a` is a PtrArray; `a[][i]`
  59. FieldAt, # obj.field
  60. DerefFieldAt, # obj[].field
  61. Load, # a[]
  62. Store, # a[] = b
  63. Asgn, # a = b
  64. SetExc,
  65. TestExc,
  66. CheckedRange,
  67. CheckedIndex,
  68. Call,
  69. IndirectCall,
  70. CheckedCall, # call that can raise
  71. CheckedIndirectCall, # call that can raise
  72. CheckedAdd, # with overflow checking etc.
  73. CheckedSub,
  74. CheckedMul,
  75. CheckedDiv,
  76. CheckedMod,
  77. Add,
  78. Sub,
  79. Mul,
  80. Div,
  81. Mod,
  82. BitShl,
  83. BitShr,
  84. BitAnd,
  85. BitOr,
  86. BitXor,
  87. BitNot,
  88. BoolNot,
  89. Eq,
  90. Le,
  91. Lt,
  92. Cast,
  93. NumberConv,
  94. CheckedObjConv,
  95. ObjConv,
  96. TestOf,
  97. Emit,
  98. ProcDecl,
  99. ForeignProcDecl,
  100. PragmaPair
  101. type
  102. PragmaKey* = enum
  103. FastCall, StdCall, CDeclCall, SafeCall, SysCall, InlineCall, NoinlineCall, ThisCall, NoCall,
  104. CoreName,
  105. ExternName,
  106. HeaderImport,
  107. DllImport,
  108. DllExport,
  109. ObjExport
  110. const
  111. LastAtomicValue = GotoLoop
  112. OpcodeBits = 8'u32
  113. OpcodeMask = (1'u32 shl OpcodeBits) - 1'u32
  114. ValueProducingAtoms = {ImmediateVal, IntVal, StrVal, SymUse, NilVal}
  115. ValueProducing* = {
  116. ImmediateVal,
  117. IntVal,
  118. StrVal,
  119. SymUse,
  120. NilVal,
  121. ModuleSymUse,
  122. ArrayConstr,
  123. ObjConstr,
  124. CheckedAdd,
  125. CheckedSub,
  126. CheckedMul,
  127. CheckedDiv,
  128. CheckedMod,
  129. Add,
  130. Sub,
  131. Mul,
  132. Div,
  133. Mod,
  134. BitShl,
  135. BitShr,
  136. BitAnd,
  137. BitOr,
  138. BitXor,
  139. BitNot,
  140. BoolNot,
  141. Eq,
  142. Le,
  143. Lt,
  144. Cast,
  145. NumberConv,
  146. CheckedObjConv,
  147. ObjConv,
  148. AddrOf,
  149. Load,
  150. ArrayAt,
  151. DerefArrayAt,
  152. FieldAt,
  153. DerefFieldAt,
  154. TestOf
  155. }
  156. type
  157. Instr* = object # 8 bytes
  158. x: uint32
  159. info*: PackedLineInfo
  160. template kind*(n: Instr): Opcode = Opcode(n.x and OpcodeMask)
  161. template operand(n: Instr): uint32 = (n.x shr OpcodeBits)
  162. template rawOperand*(n: Instr): uint32 = (n.x shr OpcodeBits)
  163. template toX(k: Opcode; operand: uint32): uint32 =
  164. uint32(k) or (operand shl OpcodeBits)
  165. template toX(k: Opcode; operand: LitId): uint32 =
  166. uint32(k) or (operand.uint32 shl OpcodeBits)
  167. type
  168. Tree* = object
  169. nodes: seq[Instr]
  170. Values* = object
  171. numbers: BiTable[int64]
  172. strings: BiTable[string]
  173. type
  174. PatchPos* = distinct int
  175. NodePos* = distinct int
  176. const
  177. InvalidPatchPos* = PatchPos(-1)
  178. proc isValid(p: PatchPos): bool {.inline.} = p.int != -1
  179. proc prepare*(tree: var Tree; info: PackedLineInfo; kind: Opcode): PatchPos =
  180. result = PatchPos tree.nodes.len
  181. tree.nodes.add Instr(x: toX(kind, 1'u32), info: info)
  182. proc isAtom(tree: Tree; pos: int): bool {.inline.} = tree.nodes[pos].kind <= LastAtomicValue
  183. proc isAtom(tree: Tree; pos: NodePos): bool {.inline.} = tree.nodes[pos.int].kind <= LastAtomicValue
  184. proc patch*(tree: var Tree; pos: PatchPos) =
  185. let pos = pos.int
  186. let k = tree.nodes[pos].kind
  187. assert k > LastAtomicValue
  188. let distance = int32(tree.nodes.len - pos)
  189. assert distance > 0
  190. tree.nodes[pos].x = toX(k, cast[uint32](distance))
  191. template build*(tree: var Tree; info: PackedLineInfo; kind: Opcode; body: untyped) =
  192. let pos = prepare(tree, info, kind)
  193. body
  194. patch(tree, pos)
  195. template buildTyped*(tree: var Tree; info: PackedLineInfo; kind: Opcode; typ: TypeId; body: untyped) =
  196. let pos = prepare(tree, info, kind)
  197. tree.addTyped info, typ
  198. body
  199. patch(tree, pos)
  200. proc len*(tree: Tree): int {.inline.} = tree.nodes.len
  201. template rawSpan(n: Instr): int = int(operand(n))
  202. proc nextChild(tree: Tree; pos: var int) {.inline.} =
  203. if tree.nodes[pos].kind > LastAtomicValue:
  204. assert tree.nodes[pos].operand > 0'u32
  205. inc pos, tree.nodes[pos].rawSpan
  206. else:
  207. inc pos
  208. proc next*(tree: Tree; pos: var NodePos) {.inline.} = nextChild tree, int(pos)
  209. template firstSon*(n: NodePos): NodePos = NodePos(n.int+1)
  210. template skipTyped*(n: NodePos): NodePos = NodePos(n.int+2)
  211. iterator sons*(tree: Tree; n: NodePos): NodePos =
  212. var pos = n.int
  213. assert tree.nodes[pos].kind > LastAtomicValue
  214. let last = pos + tree.nodes[pos].rawSpan
  215. inc pos
  216. while pos < last:
  217. yield NodePos pos
  218. nextChild tree, pos
  219. iterator sonsFrom1*(tree: Tree; n: NodePos): NodePos =
  220. var pos = n.int
  221. assert tree.nodes[pos].kind > LastAtomicValue
  222. let last = pos + tree.nodes[pos].rawSpan
  223. inc pos
  224. nextChild tree, pos
  225. while pos < last:
  226. yield NodePos pos
  227. nextChild tree, pos
  228. iterator sonsFromN*(tree: Tree; n: NodePos; toSkip = 2): NodePos =
  229. var pos = n.int
  230. assert tree.nodes[pos].kind > LastAtomicValue
  231. let last = pos + tree.nodes[pos].rawSpan
  232. inc pos
  233. for i in 1..toSkip:
  234. nextChild tree, pos
  235. while pos < last:
  236. yield NodePos pos
  237. nextChild tree, pos
  238. template `[]`*(t: Tree; n: NodePos): Instr = t.nodes[n.int]
  239. iterator sonsRest*(tree: Tree; parent, n: NodePos): NodePos =
  240. var pos = n.int
  241. assert tree[parent].kind > LastAtomicValue
  242. let last = parent.int + tree[parent].rawSpan
  243. while pos < last:
  244. yield NodePos pos
  245. nextChild tree, pos
  246. proc span(tree: Tree; pos: int): int {.inline.} =
  247. if tree.nodes[pos].kind <= LastAtomicValue: 1 else: int(tree.nodes[pos].operand)
  248. proc copyTree*(dest: var Tree; src: Tree) =
  249. let pos = 0
  250. let L = span(src, pos)
  251. let d = dest.nodes.len
  252. dest.nodes.setLen(d + L)
  253. assert L > 0
  254. for i in 0..<L:
  255. dest.nodes[d+i] = src.nodes[pos+i]
  256. proc sons2*(tree: Tree; n: NodePos): (NodePos, NodePos) {.inline.} =
  257. assert(not isAtom(tree, n.int))
  258. let a = n.int+1
  259. let b = a + span(tree, a)
  260. result = (NodePos a, NodePos b)
  261. proc sons3*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos) {.inline.} =
  262. assert(not isAtom(tree, n.int))
  263. let a = n.int+1
  264. let b = a + span(tree, a)
  265. let c = b + span(tree, b)
  266. result = (NodePos a, NodePos b, NodePos c)
  267. proc sons4*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos, NodePos) {.inline.} =
  268. assert(not isAtom(tree, n.int))
  269. let a = n.int+1
  270. let b = a + span(tree, a)
  271. let c = b + span(tree, b)
  272. let d = c + span(tree, c)
  273. result = (NodePos a, NodePos b, NodePos c, NodePos d)
  274. proc sons5*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos, NodePos, NodePos) {.inline.} =
  275. assert(not isAtom(tree, n.int))
  276. let a = n.int+1
  277. let b = a + span(tree, a)
  278. let c = b + span(tree, b)
  279. let d = c + span(tree, c)
  280. let e = d + span(tree, d)
  281. result = (NodePos a, NodePos b, NodePos c, NodePos d, NodePos e)
  282. proc typeId*(ins: Instr): TypeId {.inline.} =
  283. assert ins.kind == Typed
  284. result = TypeId(ins.operand)
  285. proc symId*(ins: Instr): SymId {.inline.} =
  286. assert ins.kind in {SymUse, SymDef}
  287. result = SymId(ins.operand)
  288. proc immediateVal*(ins: Instr): int {.inline.} =
  289. assert ins.kind == ImmediateVal
  290. result = cast[int](ins.operand)
  291. proc litId*(ins: Instr): LitId {.inline.} =
  292. assert ins.kind in {StrVal, IntVal}
  293. result = LitId(ins.operand)
  294. type
  295. LabelId* = distinct int
  296. proc `==`*(a, b: LabelId): bool {.borrow.}
  297. proc hash*(a: LabelId): Hash {.borrow.}
  298. proc label*(ins: Instr): LabelId {.inline.} =
  299. assert ins.kind in {Label, LoopLabel, Goto, GotoLoop, CheckedGoto}
  300. result = LabelId(ins.operand)
  301. proc newLabel*(labelGen: var int): LabelId {.inline.} =
  302. result = LabelId labelGen
  303. inc labelGen
  304. proc newLabels*(labelGen: var int; n: int): LabelId {.inline.} =
  305. result = LabelId labelGen
  306. inc labelGen, n
  307. proc addNewLabel*(t: var Tree; labelGen: var int; info: PackedLineInfo; k: Opcode): LabelId =
  308. assert k in {Label, LoopLabel}
  309. result = LabelId labelGen
  310. t.nodes.add Instr(x: toX(k, uint32(result)), info: info)
  311. inc labelGen
  312. proc gotoLabel*(t: var Tree; info: PackedLineInfo; k: Opcode; L: LabelId) =
  313. assert k in {Goto, GotoLoop, CheckedGoto}
  314. t.nodes.add Instr(x: toX(k, uint32(L)), info: info)
  315. proc addLabel*(t: var Tree; info: PackedLineInfo; k: Opcode; L: LabelId) {.inline.} =
  316. assert k in {Label, LoopLabel, Goto, GotoLoop, CheckedGoto}
  317. t.nodes.add Instr(x: toX(k, uint32(L)), info: info)
  318. proc addSymUse*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} =
  319. t.nodes.add Instr(x: toX(SymUse, uint32(s)), info: info)
  320. proc addSymDef*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} =
  321. t.nodes.add Instr(x: toX(SymDef, uint32(s)), info: info)
  322. proc addNop*(t: var Tree; info: PackedLineInfo) {.inline.} =
  323. t.nodes.add Instr(x: toX(Nop, 0'u32), info: info)
  324. proc addTyped*(t: var Tree; info: PackedLineInfo; typ: TypeId) {.inline.} =
  325. assert typ.int >= 0
  326. t.nodes.add Instr(x: toX(Typed, uint32(typ)), info: info)
  327. proc addSummon*(t: var Tree; info: PackedLineInfo; s: SymId; typ: TypeId; opc = Summon) {.inline.} =
  328. assert typ.int >= 0
  329. assert opc in {Summon, SummonConst, SummonGlobal, SummonThreadLocal, SummonParam, SummonResult}
  330. let x = prepare(t, info, opc)
  331. t.nodes.add Instr(x: toX(Typed, uint32(typ)), info: info)
  332. t.nodes.add Instr(x: toX(SymDef, uint32(s)), info: info)
  333. patch t, x
  334. proc addImmediateVal*(t: var Tree; info: PackedLineInfo; x: int) =
  335. assert x >= 0 and x < ((1 shl 32) - OpcodeBits.int)
  336. t.nodes.add Instr(x: toX(ImmediateVal, uint32(x)), info: info)
  337. proc addPragmaId*(t: var Tree; info: PackedLineInfo; x: PragmaKey) =
  338. t.nodes.add Instr(x: toX(PragmaId, uint32(x)), info: info)
  339. proc addIntVal*(t: var Tree; integers: var BiTable[int64]; info: PackedLineInfo; typ: TypeId; x: int64) =
  340. buildTyped t, info, NumberConv, typ:
  341. t.nodes.add Instr(x: toX(IntVal, uint32(integers.getOrIncl(x))), info: info)
  342. proc boolVal*(t: var Tree; integers: var BiTable[int64]; info: PackedLineInfo; b: bool) =
  343. buildTyped t, info, NumberConv, Bool8Id:
  344. t.nodes.add Instr(x: toX(IntVal, uint32(integers.getOrIncl(ord b))), info: info)
  345. proc addStrVal*(t: var Tree; strings: var BiTable[string]; info: PackedLineInfo; s: string) =
  346. t.nodes.add Instr(x: toX(StrVal, uint32(strings.getOrIncl(s))), info: info)
  347. proc addStrLit*(t: var Tree; info: PackedLineInfo; s: LitId) =
  348. t.nodes.add Instr(x: toX(StrVal, uint32(s)), info: info)
  349. proc addNilVal*(t: var Tree; info: PackedLineInfo; typ: TypeId) =
  350. buildTyped t, info, NumberConv, typ:
  351. t.nodes.add Instr(x: toX(NilVal, uint32(0)), info: info)
  352. proc store*(r: var RodFile; t: Tree) = storeSeq r, t.nodes
  353. proc load*(r: var RodFile; t: var Tree) = loadSeq r, t.nodes
  354. proc escapeToNimLit*(s: string; result: var string) =
  355. result.add '"'
  356. for c in items s:
  357. if c < ' ' or int(c) >= 128:
  358. result.add '\\'
  359. result.addInt int(c)
  360. elif c == '\\':
  361. result.add r"\\"
  362. elif c == '\n':
  363. result.add r"\n"
  364. elif c == '\r':
  365. result.add r"\r"
  366. elif c == '\t':
  367. result.add r"\t"
  368. else:
  369. result.add c
  370. result.add '"'
  371. type
  372. SymNames* = object
  373. s: seq[LitId]
  374. proc `[]=`*(t: var SymNames; key: SymId; val: LitId) =
  375. let k = int(key)
  376. if k >= t.s.len: t.s.setLen k+1
  377. t.s[k] = val
  378. proc `[]`*(t: SymNames; key: SymId): LitId =
  379. let k = int(key)
  380. if k < t.s.len: result = t.s[k]
  381. else: result = LitId(0)
  382. template localName(s: SymId): string =
  383. let name = names[s]
  384. if name != LitId(0):
  385. strings[name]
  386. else:
  387. $s.int
  388. proc store*(r: var RodFile; t: SymNames) = storeSeq(r, t.s)
  389. proc load*(r: var RodFile; t: var SymNames) = loadSeq(r, t.s)
  390. proc toString*(t: Tree; pos: NodePos; strings: BiTable[string]; integers: BiTable[int64];
  391. names: SymNames;
  392. r: var string; nesting = 0) =
  393. if r.len > 0 and r[r.len-1] notin {' ', '\n', '(', '[', '{'}:
  394. r.add ' '
  395. case t[pos].kind
  396. of Nop: r.add "Nop"
  397. of ImmediateVal:
  398. r.add $t[pos].operand
  399. of IntVal:
  400. r.add "IntVal "
  401. r.add $integers[LitId t[pos].operand]
  402. of StrVal:
  403. escapeToNimLit(strings[LitId t[pos].operand], r)
  404. of SymDef:
  405. r.add "SymDef "
  406. r.add localName(SymId t[pos].operand)
  407. of SymUse:
  408. r.add "SymUse "
  409. r.add localName(SymId t[pos].operand)
  410. of PragmaId:
  411. r.add $cast[PragmaKey](t[pos].operand)
  412. of Typed:
  413. r.add "T<"
  414. r.add $t[pos].operand
  415. r.add ">"
  416. of NilVal:
  417. r.add "NilVal"
  418. of Label:
  419. # undo the nesting:
  420. var spaces = r.len-1
  421. while spaces >= 0 and r[spaces] == ' ': dec spaces
  422. r.setLen spaces+1
  423. r.add "\n L"
  424. r.add $t[pos].operand
  425. of Goto, CheckedGoto, LoopLabel, GotoLoop:
  426. r.add $t[pos].kind
  427. r.add " L"
  428. r.add $t[pos].operand
  429. else:
  430. r.add $t[pos].kind
  431. r.add "{\n"
  432. for i in 0..<(nesting+1)*2: r.add ' '
  433. for p in sons(t, pos):
  434. toString t, p, strings, integers, names, r, nesting+1
  435. r.add "\n"
  436. for i in 0..<nesting*2: r.add ' '
  437. r.add "}"
  438. proc allTreesToString*(t: Tree; strings: BiTable[string]; integers: BiTable[int64];
  439. names: SymNames;
  440. r: var string) =
  441. var i = 0
  442. while i < t.len:
  443. toString t, NodePos(i), strings, integers, names, r
  444. nextChild t, i
  445. type
  446. Value* = distinct Tree
  447. proc prepare*(dest: var Value; info: PackedLineInfo; k: Opcode): PatchPos {.inline.} =
  448. assert k in ValueProducing - ValueProducingAtoms
  449. result = prepare(Tree(dest), info, k)
  450. proc patch*(dest: var Value; pos: PatchPos) {.inline.} =
  451. patch(Tree(dest), pos)
  452. proc localToValue*(info: PackedLineInfo; s: SymId): Value =
  453. result = Value(Tree())
  454. Tree(result).addSymUse info, s
  455. proc hasValue*(v: Value): bool {.inline.} = Tree(v).len > 0
  456. proc isEmpty*(v: Value): bool {.inline.} = Tree(v).len == 0
  457. proc extractTemp*(v: Value): SymId =
  458. if hasValue(v) and Tree(v)[NodePos 0].kind == SymUse:
  459. result = SymId(Tree(v)[NodePos 0].operand)
  460. else:
  461. result = SymId(-1)
  462. proc copyTree*(dest: var Tree; src: Value) = copyTree dest, Tree(src)
  463. proc addImmediateVal*(t: var Value; info: PackedLineInfo; x: int) =
  464. assert x >= 0 and x < ((1 shl 32) - OpcodeBits.int)
  465. Tree(t).nodes.add Instr(x: toX(ImmediateVal, uint32(x)), info: info)
  466. template build*(tree: var Value; info: PackedLineInfo; kind: Opcode; body: untyped) =
  467. let pos = prepare(Tree(tree), info, kind)
  468. body
  469. patch(tree, pos)
  470. proc addTyped*(t: var Value; info: PackedLineInfo; typ: TypeId) {.inline.} =
  471. addTyped(Tree(t), info, typ)
  472. template buildTyped*(tree: var Value; info: PackedLineInfo; kind: Opcode; typ: TypeId; body: untyped) =
  473. let pos = prepare(tree, info, kind)
  474. tree.addTyped info, typ
  475. body
  476. patch(tree, pos)
  477. proc addStrVal*(t: var Value; strings: var BiTable[string]; info: PackedLineInfo; s: string) =
  478. addStrVal(Tree(t), strings, info, s)
  479. proc addNilVal*(t: var Value; info: PackedLineInfo; typ: TypeId) =
  480. addNilVal Tree(t), info, typ
  481. proc addIntVal*(t: var Value; integers: var BiTable[int64]; info: PackedLineInfo; typ: TypeId; x: int64) =
  482. addIntVal Tree(t), integers, info, typ, x