diff --git a/compiler/main.nim b/compiler/main.nim index 7118a253c4..38645b568b 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -22,6 +22,8 @@ import modules, modulegraphs, lineinfos, pathutils, vmprofiler +# ensure NIR compiles: +import nir / nir when defined(nimPreviewSlimSystem): import std/[syncio, assertions] diff --git a/compiler/nir/ast2ir.nim b/compiler/nir/ast2ir.nim index 67fd210c35..ef4d4d9a8f 100644 --- a/compiler/nir/ast2ir.nim +++ b/compiler/nir/ast2ir.nim @@ -7,40 +7,66 @@ # distribution, for details about the copyright. # -import std / [assertions, tables] +import std / [assertions, tables, sets] import ".." / [ast, types, options, lineinfos, msgs] import .. / ic / bitabs -import nirtypes, nirinsts, nirlineinfos +import nirtypes, nirinsts, nirlineinfos, nirslots, types2ir type - Context = object + ModuleCon = ref object + strings: BiTable[string] + man: LineInfoManager + types: TypesCon + slotGenerator: ref int + + LocInfo = object + inUse: bool + typ: TypeId + + ProcCon = object conf: ConfigRef lastFileKey: FileIndex lastFileVal: LitId - strings: BiTable[string] - man: LineInfoManager labelGen: int + scopes: seq[LabelId] + sm: SlotManager + locGen: int + m: ModuleCon -proc toLineInfo(c: var Context; i: TLineInfo): PackedLineInfo = +proc initModuleCon*(conf: ConfigRef): ModuleCon = + ModuleCon(types: initTypesCon(conf), slotGenerator: new(int)) + +proc initProcCon*(m: ModuleCon; ): ProcCon = + ProcCon(m: m, sm: initSlotManager({}, m.slotGenerator)) + +proc toLineInfo(c: var ProcCon; i: TLineInfo): PackedLineInfo = var val: LitId if c.lastFileKey == i.fileIndex: val = c.lastFileVal else: - val = c.strings.getOrIncl(toFullPath(c.conf, i.fileIndex)) + val = c.m.strings.getOrIncl(toFullPath(c.conf, i.fileIndex)) # remember the entry: c.lastFileKey = i.fileIndex c.lastFileVal = val - result = pack(c.man, val, int32 i.line, int32 i.col) + result = pack(c.m.man, val, int32 i.line, int32 i.col) -proc gen*(c: var Context; dest: var Tree; n: PNode) -proc genx*(c: var Context; dest: var Tree; n: PNode): Tree +proc gen*(c: var ProcCon; dest: var Tree; n: PNode) +proc genv*(c: var ProcCon; dest: var Tree; v: var Value; n: PNode) + +proc genx*(c: var ProcCon; dest: var Tree; n: PNode): SymId = + let info = toLineInfo(c, n.info) + let t = typeToIr(c.m.types, n.typ) + result = allocTemp(c.sm, t) + addSummon dest, info, result, t + var ex = localToValue(info, result) + genv(c, dest, ex, n) template withBlock(lab: LabelId; body: untyped) = body dest.addInstr(info, Label, lab) -proc genWhile(c: var Context; dest: var Tree; n: PNode) = +proc genWhile(c: var ProcCon; dest: var Tree; n: PNode) = # LoopLabel lab1: # cond, tmp # select cond @@ -61,10 +87,10 @@ proc genWhile(c: var Context; dest: var Tree; n: PNode) = c.gen(dest, n[1]) dest.gotoLabel info, GotoLoop, loopLab -proc genx*(c: var Context; dest: var Tree; n: PNode): Tree = +proc genv*(c: var ProcCon; dest: var Tree; v: var Value; n: PNode) = quit "too implement" -proc gen*(c: var Context; dest: var Tree; n: PNode) = +proc gen*(c: var ProcCon; dest: var Tree; n: PNode) = case n.kind of nkWhileStmt: genWhile c, dest, n diff --git a/compiler/nir/nir.nim b/compiler/nir/nir.nim index b1b8444934..5639fe5c65 100644 --- a/compiler/nir/nir.nim +++ b/compiler/nir/nir.nim @@ -10,13 +10,13 @@ ## Nim Intermediate Representation, designed to capture all of Nim's semantics without losing too much ## precious information. Can easily be translated into C. And to JavaScript, hopefully. -import nirtypes, nirinsts - -type - Module* = object - types: TypeGraph - data: seq[Tree] - init: seq[Tree] - procs: seq[Tree] +import nirtypes, nirinsts, ast2ir +when false: + type + Module* = object + types: TypeGraph + data: seq[Tree] + init: seq[Tree] + procs: seq[Tree] diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim index 192cf752e3..78b95e1980 100644 --- a/compiler/nir/nirinsts.nim +++ b/compiler/nir/nirinsts.nim @@ -9,27 +9,33 @@ ## NIR instructions. Somewhat inspired by LLVM's instructions. -import std / assertions +import std / [assertions, hashes] import .. / ic / bitabs -import nirlineinfos +import nirlineinfos, nirtypes type SymId* = distinct int - InstKind* = enum + +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, - ModuleId, # module ID + ModuleId, ModuleSymUse, # `module.x` + Typed, # with type ID + NilVal, Label, Goto, LoopLabel, - GotoLoop, - Typed, # with type ID - NilVal, # last atom + GotoLoop, # last atom ArrayConstr, ObjConstr, @@ -41,11 +47,15 @@ type SummonThreadLocal, Summon, # x = Summon Typed ; x begins to live Kill, # `Kill x`: scope end for `x` - Load, - Store, + + AddrOf, ArrayAt, # addr(a[i]) FieldAt, # addr(obj.field) + Load, # a[] + Store, # a[] = b + Asgn, # a = b + Call, IndirectCall, CheckedCall, # call that can raise @@ -77,24 +87,62 @@ type ProcDecl const - LastAtomicValue = NilVal + LastAtomicValue = GotoLoop - InstKindBits = 8'u32 - InstKindMask = (1'u32 shl InstKindBits) - 1'u32 + OpcodeBits = 8'u32 + OpcodeMask = (1'u32 shl OpcodeBits) - 1'u32 + + ValueProducing* = { + ImmediateVal, + IntVal, + StrVal, + SymUse, + ModuleSymUse, + NilVal, + ArrayConstr, + ObjConstr, + CheckedAdd, + CheckedSub, + CheckedMul, + CheckedDiv, + CheckedMod, + Add, + Sub, + Mul, + Div, + Mod, + BitShl, + BitShr, + BitAnd, + BitOr, + BitXor, + BitNot, + Eq, + Le, + Lt, + Cast, + NumberConv, + CheckedObjConv, + ObjConv, + AddrOf, + Load, + ArrayAt, + FieldAt + } type Instr* = object # 8 bytes x: uint32 info: PackedLineInfo -template kind*(n: Instr): InstKind = InstKind(n.x and InstKindMask) -template operand(n: Instr): uint32 = (n.x shr InstKindBits) +template kind*(n: Instr): Opcode = Opcode(n.x and OpcodeMask) +template operand(n: Instr): uint32 = (n.x shr OpcodeBits) -template toX(k: InstKind; operand: uint32): uint32 = - uint32(k) or (operand shl InstKindBits) +template toX(k: Opcode; operand: uint32): uint32 = + uint32(k) or (operand shl OpcodeBits) -template toX(k: InstKind; operand: LitId): uint32 = - uint32(k) or (operand.uint32 shl InstKindBits) +template toX(k: Opcode; operand: LitId): uint32 = + uint32(k) or (operand.uint32 shl OpcodeBits) type Tree* = object @@ -113,14 +161,14 @@ const proc isValid(p: PatchPos): bool {.inline.} = p.int != -1 -proc prepare(tree: var Tree; kind: InstKind): PatchPos = +proc prepare*(tree: var Tree; info: PackedLineInfo; kind: Opcode): PatchPos = result = PatchPos tree.nodes.len - tree.nodes.add Instr(x: toX(kind, 1'u32)) + 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) = +proc patch*(tree: var Tree; pos: PatchPos) = let pos = pos.int let k = tree.nodes[pos].kind assert k > LastAtomicValue @@ -169,16 +217,39 @@ proc newLabel*(labelGen: var int): LabelId {.inline.} = result = LabelId labelGen inc labelGen -proc addLabel*(t: var Tree; labelGen: var int; info: PackedLineInfo; k: InstKind): LabelId = +proc addLabel*(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: InstKind; L: LabelId) = +proc gotoLabel*(t: var Tree; info: PackedLineInfo; k: Opcode; L: LabelId) = assert k in {Goto, GotoLoop} t.nodes.add Instr(x: toX(k, uint32(L)), info: info) -proc addInstr*(t: var Tree; info: PackedLineInfo; k: InstKind; L: LabelId) {.inline.} = +proc addInstr*(t: var Tree; info: PackedLineInfo; k: Opcode; L: LabelId) {.inline.} = assert k in {Label, LoopLabel, Goto, GotoLoop} 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 addSummon*(t: var Tree; info: PackedLineInfo; s: SymId; typ: TypeId) {.inline.} = + let x = prepare(t, info, Summon) + t.nodes.add Instr(x: toX(SymDef, uint32(s)), info: info) + t.nodes.add Instr(x: toX(Typed, uint32(typ)), info: info) + patch t, x + +type + Value* = distinct Tree + +proc prepare*(dest: var Value; info: PackedLineInfo; k: Opcode): PatchPos {.inline.} = + assert k in ValueProducing + 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 diff --git a/compiler/nir/nirslots.nim b/compiler/nir/nirslots.nim new file mode 100644 index 0000000000..a5c0e93f4a --- /dev/null +++ b/compiler/nir/nirslots.nim @@ -0,0 +1,84 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Management of slots. Similar to "register allocation" +## in lower level languages. + +import std / [assertions, tables] +import nirtypes, nirinsts + +type + SlotManagerFlag* = enum + ReuseTemps, + ReuseVars + SlotManager* = object # "register allocator" + live: Table[SymId, TypeId] + dead: Table[TypeId, seq[SymId]] + flags: set[SlotManagerFlag] + locGen: ref int + +proc initSlotManager*(flags: set[SlotManagerFlag]; generator: ref int): SlotManager {.inline.} = + SlotManager(flags: flags, locGen: generator) + +proc allocRaw(m: var SlotManager; t: TypeId; f: SlotManagerFlag): SymId {.inline.} = + if f in m.flags and m.dead.hasKey(t) and m.dead[t].len > 0: + result = m.dead[t].pop() + else: + result = SymId(m.locGen[]) + inc m.locGen[] + m.live[result] = t + +proc allocTemp*(m: var SlotManager; t: TypeId): SymId {.inline.} = + result = allocRaw(m, t, ReuseTemps) + +proc allocVar*(m: var SlotManager; t: TypeId): SymId {.inline.} = + result = allocRaw(m, t, ReuseVars) + +proc freeLoc*(m: var SlotManager; s: SymId) = + let t = m.live.getOrDefault(s) + assert t.int != 0 + m.live.del s + m.dead.mgetOrPut(t, @[]).add s + +iterator stillAlive*(m: SlotManager): (SymId, TypeId) = + for k, v in pairs(m.live): + yield (k, v) + +proc getType*(m: SlotManager; s: SymId): TypeId {.inline.} = m.live[s] + +when isMainModule: + var m = initSlotManager({ReuseTemps}, new(int)) + + var g = initTypeGraph() + + let a = g.openType ArrayTy + g.addBuiltinType Int8Id + g.addArrayLen 5'u64 + let finalArrayType = sealType(g, a) + + let obj = g.openType ObjectDecl + g.addName "MyType" + + g.addField "p", finalArrayType + let objB = sealType(g, obj) + + let x = m.allocTemp(objB) + assert x.int == 0 + + let y = m.allocTemp(objB) + assert y.int == 1 + + let z = m.allocTemp(Int8Id) + assert z.int == 2 + + m.freeLoc y + let y2 = m.allocTemp(objB) + assert y2.int == 1 + + diff --git a/compiler/nir/nirtypes.nim b/compiler/nir/nirtypes.nim index 5c0d3c537a..981b891ec6 100644 --- a/compiler/nir/nirtypes.nim +++ b/compiler/nir/nirtypes.nim @@ -9,7 +9,7 @@ ## Type system for NIR. Close to C's type system but without its quirks. -import std / assertions +import std / [assertions, hashes] import .. / ic / bitabs type @@ -49,6 +49,11 @@ template toX(k: NirTypeKind; operand: LitId): uint32 = type TypeId* = distinct int + +proc `==`*(a, b: TypeId): bool {.borrow.} +proc hash*(a: TypeId): Hash {.borrow.} + +type TypeGraph* = object nodes: seq[TypeNode] names: BiTable[string] diff --git a/compiler/nir/types2ir.nim b/compiler/nir/types2ir.nim index 603ea1d254..b09ec59d26 100644 --- a/compiler/nir/types2ir.nim +++ b/compiler/nir/types2ir.nim @@ -12,24 +12,27 @@ import ".." / [ast, types, options, sighashes, modulegraphs] import nirtypes type - Context = object + TypesCon* = object processed: Table[ItemId, TypeId] recursionCheck: HashSet[ItemId] g: TypeGraph conf: ConfigRef -proc mangle(c: var Context; t: PType): string = +proc initTypesCon*(conf: ConfigRef): TypesCon = + TypesCon(g: initTypeGraph(), conf: conf) + +proc mangle(c: var TypesCon; t: PType): string = result = $sighashes.hashType(t, c.conf) -template cached(c: var Context; t: PType; body: untyped) = +template cached(c: var TypesCon; t: PType; body: untyped) = result = c.processed.getOrDefault(t.itemId) if result.int == 0: body c.processed[t.itemId] = result -proc typeToIr*(c: var Context; t: PType): TypeId +proc typeToIr*(c: var TypesCon; t: PType): TypeId -proc collectFieldTypes(c: var Context; n: PNode; dest: var Table[ItemId, TypeId]) = +proc collectFieldTypes(c: var TypesCon; n: PNode; dest: var Table[ItemId, TypeId]) = case n.kind of nkRecList: for i in 0..