baby steps

This commit is contained in:
araq
2023-10-02 14:23:42 +02:00
parent f496c0e14c
commit df71f4602e
7 changed files with 253 additions and 62 deletions

View File

@@ -22,6 +22,8 @@ import
modules,
modulegraphs, lineinfos, pathutils, vmprofiler
# ensure NIR compiles:
import nir / nir
when defined(nimPreviewSlimSystem):
import std/[syncio, assertions]

View File

@@ -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

View File

@@ -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]

View File

@@ -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 <Type ID>; 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

84
compiler/nir/nirslots.nim Normal file
View File

@@ -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

View File

@@ -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]

View File

@@ -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..<n.len:
@@ -47,7 +50,7 @@ proc collectFieldTypes(c: var Context; n: PNode; dest: var Table[ItemId, TypeId]
else:
assert false, "unknown node kind: " & $n.kind
proc objectToIr(c: var Context; n: PNode; fieldTypes: Table[ItemId, TypeId]; unionId: var int) =
proc objectToIr(c: var TypesCon; n: PNode; fieldTypes: Table[ItemId, TypeId]; unionId: var int) =
case n.kind
of nkRecList:
for i in 0..<n.len:
@@ -72,7 +75,7 @@ proc objectToIr(c: var Context; n: PNode; fieldTypes: Table[ItemId, TypeId]; uni
else:
assert false, "unknown node kind: " & $n.kind
proc objectToIr(c: var Context; t: PType): TypeId =
proc objectToIr(c: var TypesCon; t: PType): TypeId =
if t[0] != nil:
# ensure we emitted the base type:
discard typeToIr(c, t[0])
@@ -97,10 +100,10 @@ proc objectToIr(c: var Context; t: PType): TypeId =
objectToIr c, t.n, fieldTypes, unionId
result = sealType(c.g, obj)
proc objectHeaderToIr(c: var Context; t: PType): TypeId =
proc objectHeaderToIr(c: var TypesCon; t: PType): TypeId =
result = c.g.nominalType(ObjectTy, mangle(c, t))
proc tupleToIr(c: var Context; t: PType): TypeId =
proc tupleToIr(c: var TypesCon; t: PType): TypeId =
var fieldTypes = newSeq[TypeId](t.len)
for i in 0..<t.len:
fieldTypes[i] = typeToIr(c, t[i])
@@ -110,7 +113,7 @@ proc tupleToIr(c: var Context; t: PType): TypeId =
c.g.addField "f_" & $i, fieldTypes[i]
result = sealType(c.g, obj)
proc procToIr(c: var Context; t: PType; addEnv = false): TypeId =
proc procToIr(c: var TypesCon; t: PType; addEnv = false): TypeId =
var fieldTypes = newSeq[TypeId](0)
for i in 0..<t.len:
if not isCompileTimeOnly(t[i]):
@@ -140,13 +143,13 @@ proc procToIr(c: var Context; t: PType; addEnv = false): TypeId =
c.g.addVarargs()
result = sealType(c.g, obj)
proc nativeInt(c: Context): TypeId =
proc nativeInt(c: TypesCon): TypeId =
case c.conf.target.intSize
of 2: result = Int16Id
of 4: result = Int32Id
else: result = Int64Id
proc openArrayToIr(c: var Context; t: PType): TypeId =
proc openArrayToIr(c: var TypesCon; t: PType): TypeId =
# object (a: ArrayPtr[T], len: int)
let e = lastSon(t)
let mangledBase = mangle(c, e)
@@ -169,7 +172,7 @@ proc openArrayToIr(c: var Context; t: PType): TypeId =
result = sealType(c.g, p) # ObjectDecl
proc stringToIr(c: var Context; t: PType): TypeId =
proc stringToIr(c: var TypesCon; t: PType): TypeId =
#[
NimStrPayload = object
@@ -207,7 +210,7 @@ proc stringToIr(c: var Context; t: PType): TypeId =
result = sealType(c.g, str) # ObjectDecl
proc seqToIr(c: var Context; t: PType): TypeId =
proc seqToIr(c: var TypesCon; t: PType): TypeId =
#[
NimSeqPayload[T] = object
cap: int
@@ -251,7 +254,7 @@ proc seqToIr(c: var Context; t: PType): TypeId =
result = sealType(c.g, sq) # ObjectDecl
proc closureToIr(c: var Context; t: PType): TypeId =
proc closureToIr(c: var TypesCon; t: PType): TypeId =
# struct {fn(args, void* env), env}
# typedef struct {$n" &
# "N_NIMCALL_PTR($2, ClP_0) $3;$n" &
@@ -279,7 +282,7 @@ proc closureToIr(c: var Context; t: PType): TypeId =
result = sealType(c.g, p) # ObjectDecl
proc typeToIr*(c: var Context; t: PType): TypeId =
proc typeToIr*(c: var TypesCon; t: PType): TypeId =
case t.kind
of tyInt:
case int(getSize(c.conf, t))