123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583 |
- #
- #
- # The Nim Compiler
- # (c) Copyright 2023 Andreas Rumpf
- #
- # See the file "copying.txt", included in this
- # distribution, for details about the copyright.
- #
- ## NIR instructions. Somewhat inspired by LLVM's instructions.
- import std / [assertions, hashes]
- import .. / ic / [bitabs, rodfiles]
- import nirlineinfos, nirtypes
- const
- NirVersion = 1
- nirCookie* = [byte(0), byte('N'), byte('I'), byte('R'),
- byte(sizeof(int)*8), byte(system.cpuEndian), byte(0), byte(NirVersion)]
- type
- SymId* = distinct int
- proc `$`*(s: SymId): string {.borrow.}
- proc hash*(s: SymId): Hash {.borrow.}
- proc `==`*(a, b: SymId): bool {.borrow.}
- type
- Opcode* = enum
- Nop,
- ImmediateVal,
- IntVal,
- StrVal,
- SymDef,
- SymUse,
- Typed, # with type ID
- PragmaId, # with Pragma ID, possible values: see PragmaKey enum
- NilVal,
- Label,
- Goto,
- CheckedGoto,
- LoopLabel,
- GotoLoop, # last atom
- ModuleSymUse, # `"module".x`
- ArrayConstr,
- ObjConstr,
- Ret,
- Yld,
- Select,
- SelectPair, # ((values...), Label)
- SelectList, # (values...)
- SelectValue, # (value)
- SelectRange, # (valueA..valueB)
- ForeignDecl, # Can wrap SummonGlobal, SummonThreadLocal, SummonConst
- SummonGlobal,
- SummonThreadLocal,
- Summon, # x = Summon Typed <Type ID>; x begins to live
- SummonResult,
- SummonParam,
- SummonConst,
- Kill, # `Kill x`: scope end for `x`
- AddrOf,
- ArrayAt, # a[i]
- DerefArrayAt, # a[i] where `a` is a PtrArray; `a[][i]`
- FieldAt, # obj.field
- DerefFieldAt, # obj[].field
- Load, # a[]
- Store, # a[] = b
- Asgn, # a = b
- SetExc,
- TestExc,
- CheckedRange,
- CheckedIndex,
- Call,
- IndirectCall,
- CheckedCall, # call that can raise
- CheckedIndirectCall, # call that can raise
- CheckedAdd, # with overflow checking etc.
- CheckedSub,
- CheckedMul,
- CheckedDiv,
- CheckedMod,
- Add,
- Sub,
- Mul,
- Div,
- Mod,
- BitShl,
- BitShr,
- BitAnd,
- BitOr,
- BitXor,
- BitNot,
- BoolNot,
- Eq,
- Le,
- Lt,
- Cast,
- NumberConv,
- CheckedObjConv,
- ObjConv,
- TestOf,
- Emit,
- ProcDecl,
- ForeignProcDecl,
- PragmaPair
- type
- PragmaKey* = enum
- FastCall, StdCall, CDeclCall, SafeCall, SysCall, InlineCall, NoinlineCall, ThisCall, NoCall,
- CoreName,
- ExternName,
- HeaderImport,
- DllImport,
- DllExport,
- ObjExport
- const
- LastAtomicValue = GotoLoop
- OpcodeBits = 8'u32
- OpcodeMask = (1'u32 shl OpcodeBits) - 1'u32
- ValueProducingAtoms = {ImmediateVal, IntVal, StrVal, SymUse, NilVal}
- ValueProducing* = {
- ImmediateVal,
- IntVal,
- StrVal,
- SymUse,
- NilVal,
- ModuleSymUse,
- ArrayConstr,
- ObjConstr,
- CheckedAdd,
- CheckedSub,
- CheckedMul,
- CheckedDiv,
- CheckedMod,
- Add,
- Sub,
- Mul,
- Div,
- Mod,
- BitShl,
- BitShr,
- BitAnd,
- BitOr,
- BitXor,
- BitNot,
- BoolNot,
- Eq,
- Le,
- Lt,
- Cast,
- NumberConv,
- CheckedObjConv,
- ObjConv,
- AddrOf,
- Load,
- ArrayAt,
- DerefArrayAt,
- FieldAt,
- DerefFieldAt,
- TestOf
- }
- type
- Instr* = object # 8 bytes
- x: uint32
- info*: PackedLineInfo
- template kind*(n: Instr): Opcode = Opcode(n.x and OpcodeMask)
- template operand(n: Instr): uint32 = (n.x shr OpcodeBits)
- template rawOperand*(n: Instr): uint32 = (n.x shr OpcodeBits)
- template toX(k: Opcode; operand: uint32): uint32 =
- uint32(k) or (operand shl OpcodeBits)
- template toX(k: Opcode; operand: LitId): uint32 =
- uint32(k) or (operand.uint32 shl OpcodeBits)
- type
- Tree* = object
- nodes: seq[Instr]
- Values* = object
- numbers: BiTable[int64]
- strings: BiTable[string]
- type
- PatchPos* = distinct int
- NodePos* = distinct int
- const
- InvalidPatchPos* = PatchPos(-1)
- proc isValid(p: PatchPos): bool {.inline.} = p.int != -1
- proc prepare*(tree: var Tree; info: PackedLineInfo; kind: Opcode): PatchPos =
- result = PatchPos tree.nodes.len
- tree.nodes.add Instr(x: toX(kind, 1'u32), info: info)
- proc isAtom(tree: Tree; pos: int): bool {.inline.} = tree.nodes[pos].kind <= LastAtomicValue
- proc isAtom(tree: Tree; pos: NodePos): bool {.inline.} = tree.nodes[pos.int].kind <= LastAtomicValue
- proc patch*(tree: var Tree; pos: PatchPos) =
- let pos = pos.int
- let k = tree.nodes[pos].kind
- assert k > LastAtomicValue
- let distance = int32(tree.nodes.len - pos)
- assert distance > 0
- tree.nodes[pos].x = toX(k, cast[uint32](distance))
- template build*(tree: var Tree; info: PackedLineInfo; kind: Opcode; body: untyped) =
- let pos = prepare(tree, info, kind)
- body
- patch(tree, pos)
- template buildTyped*(tree: var Tree; info: PackedLineInfo; kind: Opcode; typ: TypeId; body: untyped) =
- let pos = prepare(tree, info, kind)
- tree.addTyped info, typ
- body
- patch(tree, pos)
- proc len*(tree: Tree): int {.inline.} = tree.nodes.len
- template rawSpan(n: Instr): int = int(operand(n))
- proc nextChild(tree: Tree; pos: var int) {.inline.} =
- if tree.nodes[pos].kind > LastAtomicValue:
- assert tree.nodes[pos].operand > 0'u32
- inc pos, tree.nodes[pos].rawSpan
- else:
- inc pos
- proc next*(tree: Tree; pos: var NodePos) {.inline.} = nextChild tree, int(pos)
- template firstSon*(n: NodePos): NodePos = NodePos(n.int+1)
- template skipTyped*(n: NodePos): NodePos = NodePos(n.int+2)
- iterator sons*(tree: Tree; n: NodePos): NodePos =
- var pos = n.int
- assert tree.nodes[pos].kind > LastAtomicValue
- let last = pos + tree.nodes[pos].rawSpan
- inc pos
- while pos < last:
- yield NodePos pos
- nextChild tree, pos
- iterator sonsFrom1*(tree: Tree; n: NodePos): NodePos =
- var pos = n.int
- assert tree.nodes[pos].kind > LastAtomicValue
- let last = pos + tree.nodes[pos].rawSpan
- inc pos
- nextChild tree, pos
- while pos < last:
- yield NodePos pos
- nextChild tree, pos
- iterator sonsFromN*(tree: Tree; n: NodePos; toSkip = 2): NodePos =
- var pos = n.int
- assert tree.nodes[pos].kind > LastAtomicValue
- let last = pos + tree.nodes[pos].rawSpan
- inc pos
- for i in 1..toSkip:
- nextChild tree, pos
- while pos < last:
- yield NodePos pos
- nextChild tree, pos
- template `[]`*(t: Tree; n: NodePos): Instr = t.nodes[n.int]
- iterator sonsRest*(tree: Tree; parent, n: NodePos): NodePos =
- var pos = n.int
- assert tree[parent].kind > LastAtomicValue
- let last = parent.int + tree[parent].rawSpan
- while pos < last:
- yield NodePos pos
- nextChild tree, pos
- proc span(tree: Tree; pos: int): int {.inline.} =
- if tree.nodes[pos].kind <= LastAtomicValue: 1 else: int(tree.nodes[pos].operand)
- proc copyTree*(dest: var Tree; src: Tree) =
- let pos = 0
- let L = span(src, pos)
- let d = dest.nodes.len
- dest.nodes.setLen(d + L)
- assert L > 0
- for i in 0..<L:
- dest.nodes[d+i] = src.nodes[pos+i]
- proc sons2*(tree: Tree; n: NodePos): (NodePos, NodePos) {.inline.} =
- assert(not isAtom(tree, n.int))
- let a = n.int+1
- let b = a + span(tree, a)
- result = (NodePos a, NodePos b)
- proc sons3*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos) {.inline.} =
- assert(not isAtom(tree, n.int))
- let a = n.int+1
- let b = a + span(tree, a)
- let c = b + span(tree, b)
- result = (NodePos a, NodePos b, NodePos c)
- proc sons4*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos, NodePos) {.inline.} =
- assert(not isAtom(tree, n.int))
- let a = n.int+1
- let b = a + span(tree, a)
- let c = b + span(tree, b)
- let d = c + span(tree, c)
- result = (NodePos a, NodePos b, NodePos c, NodePos d)
- proc sons5*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos, NodePos, NodePos) {.inline.} =
- assert(not isAtom(tree, n.int))
- let a = n.int+1
- let b = a + span(tree, a)
- let c = b + span(tree, b)
- let d = c + span(tree, c)
- let e = d + span(tree, d)
- result = (NodePos a, NodePos b, NodePos c, NodePos d, NodePos e)
- proc typeId*(ins: Instr): TypeId {.inline.} =
- assert ins.kind == Typed
- result = TypeId(ins.operand)
- proc symId*(ins: Instr): SymId {.inline.} =
- assert ins.kind in {SymUse, SymDef}
- result = SymId(ins.operand)
- proc immediateVal*(ins: Instr): int {.inline.} =
- assert ins.kind == ImmediateVal
- result = cast[int](ins.operand)
- proc litId*(ins: Instr): LitId {.inline.} =
- assert ins.kind in {StrVal, IntVal}
- result = LitId(ins.operand)
- type
- LabelId* = distinct int
- proc `==`*(a, b: LabelId): bool {.borrow.}
- proc hash*(a: LabelId): Hash {.borrow.}
- proc label*(ins: Instr): LabelId {.inline.} =
- assert ins.kind in {Label, LoopLabel, Goto, GotoLoop, CheckedGoto}
- result = LabelId(ins.operand)
- proc newLabel*(labelGen: var int): LabelId {.inline.} =
- result = LabelId labelGen
- inc labelGen
- proc newLabels*(labelGen: var int; n: int): LabelId {.inline.} =
- result = LabelId labelGen
- inc labelGen, n
- proc addNewLabel*(t: var Tree; labelGen: var int; info: PackedLineInfo; k: Opcode): LabelId =
- assert k in {Label, LoopLabel}
- result = LabelId labelGen
- t.nodes.add Instr(x: toX(k, uint32(result)), info: info)
- inc labelGen
- proc gotoLabel*(t: var Tree; info: PackedLineInfo; k: Opcode; L: LabelId) =
- assert k in {Goto, GotoLoop, CheckedGoto}
- t.nodes.add Instr(x: toX(k, uint32(L)), info: info)
- proc addLabel*(t: var Tree; info: PackedLineInfo; k: Opcode; L: LabelId) {.inline.} =
- assert k in {Label, LoopLabel, Goto, GotoLoop, CheckedGoto}
- t.nodes.add Instr(x: toX(k, uint32(L)), info: info)
- proc addSymUse*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} =
- t.nodes.add Instr(x: toX(SymUse, uint32(s)), info: info)
- proc addSymDef*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} =
- t.nodes.add Instr(x: toX(SymDef, uint32(s)), info: info)
- proc addNop*(t: var Tree; info: PackedLineInfo) {.inline.} =
- t.nodes.add Instr(x: toX(Nop, 0'u32), info: info)
- proc addTyped*(t: var Tree; info: PackedLineInfo; typ: TypeId) {.inline.} =
- assert typ.int >= 0
- t.nodes.add Instr(x: toX(Typed, uint32(typ)), info: info)
- proc addSummon*(t: var Tree; info: PackedLineInfo; s: SymId; typ: TypeId; opc = Summon) {.inline.} =
- assert typ.int >= 0
- assert opc in {Summon, SummonConst, SummonGlobal, SummonThreadLocal, SummonParam, SummonResult}
- let x = prepare(t, info, opc)
- t.nodes.add Instr(x: toX(Typed, uint32(typ)), info: info)
- t.nodes.add Instr(x: toX(SymDef, uint32(s)), info: info)
- patch t, x
- proc addImmediateVal*(t: var Tree; info: PackedLineInfo; x: int) =
- assert x >= 0 and x < ((1 shl 32) - OpcodeBits.int)
- t.nodes.add Instr(x: toX(ImmediateVal, uint32(x)), info: info)
- proc addPragmaId*(t: var Tree; info: PackedLineInfo; x: PragmaKey) =
- t.nodes.add Instr(x: toX(PragmaId, uint32(x)), info: info)
- proc addIntVal*(t: var Tree; integers: var BiTable[int64]; info: PackedLineInfo; typ: TypeId; x: int64) =
- buildTyped t, info, NumberConv, typ:
- t.nodes.add Instr(x: toX(IntVal, uint32(integers.getOrIncl(x))), info: info)
- proc boolVal*(t: var Tree; integers: var BiTable[int64]; info: PackedLineInfo; b: bool) =
- buildTyped t, info, NumberConv, Bool8Id:
- t.nodes.add Instr(x: toX(IntVal, uint32(integers.getOrIncl(ord b))), info: info)
- proc addStrVal*(t: var Tree; strings: var BiTable[string]; info: PackedLineInfo; s: string) =
- t.nodes.add Instr(x: toX(StrVal, uint32(strings.getOrIncl(s))), info: info)
- proc addStrLit*(t: var Tree; info: PackedLineInfo; s: LitId) =
- t.nodes.add Instr(x: toX(StrVal, uint32(s)), info: info)
- proc addNilVal*(t: var Tree; info: PackedLineInfo; typ: TypeId) =
- buildTyped t, info, NumberConv, typ:
- t.nodes.add Instr(x: toX(NilVal, uint32(0)), info: info)
- proc store*(r: var RodFile; t: Tree) = storeSeq r, t.nodes
- proc load*(r: var RodFile; t: var Tree) = loadSeq r, t.nodes
- proc escapeToNimLit*(s: string; result: var string) =
- result.add '"'
- for c in items s:
- if c < ' ' or int(c) >= 128:
- result.add '\\'
- result.addInt int(c)
- elif c == '\\':
- result.add r"\\"
- elif c == '\n':
- result.add r"\n"
- elif c == '\r':
- result.add r"\r"
- elif c == '\t':
- result.add r"\t"
- else:
- result.add c
- result.add '"'
- type
- SymNames* = object
- s: seq[LitId]
- proc `[]=`*(t: var SymNames; key: SymId; val: LitId) =
- let k = int(key)
- if k >= t.s.len: t.s.setLen k+1
- t.s[k] = val
- proc `[]`*(t: SymNames; key: SymId): LitId =
- let k = int(key)
- if k < t.s.len: result = t.s[k]
- else: result = LitId(0)
- template localName(s: SymId): string =
- let name = names[s]
- if name != LitId(0):
- strings[name]
- else:
- $s.int
- proc store*(r: var RodFile; t: SymNames) = storeSeq(r, t.s)
- proc load*(r: var RodFile; t: var SymNames) = loadSeq(r, t.s)
- proc toString*(t: Tree; pos: NodePos; strings: BiTable[string]; integers: BiTable[int64];
- names: SymNames;
- r: var string; nesting = 0) =
- if r.len > 0 and r[r.len-1] notin {' ', '\n', '(', '[', '{'}:
- r.add ' '
- case t[pos].kind
- of Nop: r.add "Nop"
- of ImmediateVal:
- r.add $t[pos].operand
- of IntVal:
- r.add "IntVal "
- r.add $integers[LitId t[pos].operand]
- of StrVal:
- escapeToNimLit(strings[LitId t[pos].operand], r)
- of SymDef:
- r.add "SymDef "
- r.add localName(SymId t[pos].operand)
- of SymUse:
- r.add "SymUse "
- r.add localName(SymId t[pos].operand)
- of PragmaId:
- r.add $cast[PragmaKey](t[pos].operand)
- of Typed:
- r.add "T<"
- r.add $t[pos].operand
- r.add ">"
- of NilVal:
- r.add "NilVal"
- of Label:
- # undo the nesting:
- var spaces = r.len-1
- while spaces >= 0 and r[spaces] == ' ': dec spaces
- r.setLen spaces+1
- r.add "\n L"
- r.add $t[pos].operand
- of Goto, CheckedGoto, LoopLabel, GotoLoop:
- r.add $t[pos].kind
- r.add " L"
- r.add $t[pos].operand
- else:
- r.add $t[pos].kind
- r.add "{\n"
- for i in 0..<(nesting+1)*2: r.add ' '
- for p in sons(t, pos):
- toString t, p, strings, integers, names, r, nesting+1
- r.add "\n"
- for i in 0..<nesting*2: r.add ' '
- r.add "}"
- proc allTreesToString*(t: Tree; strings: BiTable[string]; integers: BiTable[int64];
- names: SymNames;
- r: var string) =
- var i = 0
- while i < t.len:
- toString t, NodePos(i), strings, integers, names, r
- nextChild t, i
- type
- Value* = distinct Tree
- proc prepare*(dest: var Value; info: PackedLineInfo; k: Opcode): PatchPos {.inline.} =
- assert k in ValueProducing - ValueProducingAtoms
- result = prepare(Tree(dest), info, k)
- proc patch*(dest: var Value; pos: PatchPos) {.inline.} =
- patch(Tree(dest), pos)
- proc localToValue*(info: PackedLineInfo; s: SymId): Value =
- result = Value(Tree())
- Tree(result).addSymUse info, s
- proc hasValue*(v: Value): bool {.inline.} = Tree(v).len > 0
- proc isEmpty*(v: Value): bool {.inline.} = Tree(v).len == 0
- proc extractTemp*(v: Value): SymId =
- if hasValue(v) and Tree(v)[NodePos 0].kind == SymUse:
- result = SymId(Tree(v)[NodePos 0].operand)
- else:
- result = SymId(-1)
- proc copyTree*(dest: var Tree; src: Value) = copyTree dest, Tree(src)
- proc addImmediateVal*(t: var Value; info: PackedLineInfo; x: int) =
- assert x >= 0 and x < ((1 shl 32) - OpcodeBits.int)
- Tree(t).nodes.add Instr(x: toX(ImmediateVal, uint32(x)), info: info)
- template build*(tree: var Value; info: PackedLineInfo; kind: Opcode; body: untyped) =
- let pos = prepare(Tree(tree), info, kind)
- body
- patch(tree, pos)
- proc addTyped*(t: var Value; info: PackedLineInfo; typ: TypeId) {.inline.} =
- addTyped(Tree(t), info, typ)
- template buildTyped*(tree: var Value; info: PackedLineInfo; kind: Opcode; typ: TypeId; body: untyped) =
- let pos = prepare(tree, info, kind)
- tree.addTyped info, typ
- body
- patch(tree, pos)
- proc addStrVal*(t: var Value; strings: var BiTable[string]; info: PackedLineInfo; s: string) =
- addStrVal(Tree(t), strings, info, s)
- proc addNilVal*(t: var Value; info: PackedLineInfo; typ: TypeId) =
- addNilVal Tree(t), info, typ
- proc addIntVal*(t: var Value; integers: var BiTable[int64]; info: PackedLineInfo; typ: TypeId; x: int64) =
- addIntVal Tree(t), integers, info, typ, x
|