NIR: store sizes, alignments and offsets in the type graph; beginning… (#22822)

…s of a patent-pending new VM
This commit is contained in:
Andreas Rumpf
2023-10-16 00:01:33 +02:00
committed by GitHub
parent 5f400983d5
commit 10c3ab6269
22 changed files with 1389 additions and 387 deletions

View File

@@ -924,7 +924,7 @@ type
# for variables a slot index for the evaluator
offset*: int32 # offset of record field
disamb*: int32 # disambiguation number; the basic idea is that
# `<procname>__<module>_<disamb>`
# `<procname>__<module>_<disamb>` is unique
loc*: TLoc
annex*: PLib # additional fields (seldom used, so we use a
# reference to another object to save space)

View File

@@ -460,6 +460,7 @@ proc handleCmdInput*(conf: ConfigRef) =
proc parseCommand*(command: string): Command =
case command.normalize
of "c", "cc", "compile", "compiletoc": cmdCompileToC
of "nir": cmdCompileToNir
of "cpp", "compiletocpp": cmdCompileToCpp
of "objc", "compiletooc": cmdCompileToOC
of "js", "compiletojs": cmdCompileToJS
@@ -496,6 +497,7 @@ proc setCmd*(conf: ConfigRef, cmd: Command) =
of cmdCompileToCpp: conf.backend = backendCpp
of cmdCompileToOC: conf.backend = backendObjc
of cmdCompileToJS: conf.backend = backendJs
of cmdCompileToNir: conf.backend = backendNir
else: discard
proc setCommandEarly*(conf: ConfigRef, command: string) =
@@ -794,7 +796,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
if conf.backend == backendJs or conf.cmd == cmdNimscript: discard
else: processOnOffSwitchG(conf, {optThreads}, arg, pass, info)
#if optThreads in conf.globalOptions: conf.setNote(warnGcUnsafe)
of "tlsemulation":
of "tlsemulation":
processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info)
if optTlsEmulation in conf.globalOptions:
conf.legacyFeatures.incl emitGenerics

View File

@@ -319,7 +319,7 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
var fullSuffix = suffix
case conf.backend
of backendCpp, backendJs, backendObjc: fullSuffix = "." & $conf.backend & suffix
of backendC: discard
of backendC, backendNir: discard
of backendInvalid:
# during parsing of cfg files; we don't know the backend yet, no point in
# guessing wrong thing

View File

@@ -97,6 +97,7 @@ type
typeInfoSection # required by the backend
backendFlagsSection
aliveSymsSection # beware, this is stored in a `.alivesyms` file.
sideChannelSection
RodFileError* = enum
ok, tooBig, cannotOpen, ioFailure, wrongHeader, wrongSection, configMismatch,
@@ -110,7 +111,7 @@ type
const
RodVersion = 1
cookie = [byte(0), byte('R'), byte('O'), byte('D'),
defaultCookie = [byte(0), byte('R'), byte('O'), byte('D'),
byte(sizeof(int)*8), byte(system.cpuEndian), byte(0), byte(RodVersion)]
proc setError(f: var RodFile; err: RodFileError) {.inline.} =
@@ -206,13 +207,13 @@ proc loadSeq*[T](f: var RodFile; s: var seq[T]) =
for i in 0..<lenPrefix:
loadPrim(f, s[i])
proc storeHeader*(f: var RodFile) =
proc storeHeader*(f: var RodFile; cookie = defaultCookie) =
## stores the header which is described by `cookie`.
if f.err != ok: return
if f.f.writeBytes(cookie, 0, cookie.len) != cookie.len:
setError f, ioFailure
proc loadHeader*(f: var RodFile) =
proc loadHeader*(f: var RodFile; cookie = defaultCookie) =
## Loads the header which is described by `cookie`.
if f.err != ok: return
var thisCookie: array[cookie.len, byte] = default(array[cookie.len, byte])

View File

@@ -49,6 +49,9 @@ proc writeDepsFile(g: ModuleGraph) =
f.writeLine(toFullPath(g.config, k))
f.close()
proc writeNinjaFile(g: ModuleGraph) =
discard "to implement"
proc writeCMakeDepsFile(conf: ConfigRef) =
## write a list of C files for build systems like CMake.
## only updated when the C file list changes.
@@ -159,6 +162,26 @@ proc commandCompileToC(graph: ModuleGraph) =
if optGenCDeps in graph.config.globalOptions:
writeCMakeDepsFile(conf)
proc commandCompileToNir(graph: ModuleGraph) =
let conf = graph.config
extccomp.initVars(conf)
if conf.symbolFiles == disabledSf:
if {optRun, optForceFullMake} * conf.globalOptions == {optRun}:
if not changeDetectedViaJsonBuildInstructions(conf, conf.jsonBuildInstructionsFile):
# nothing changed
graph.config.notes = graph.config.mainPackageNotes
return
if not extccomp.ccHasSaneOverflow(conf):
conf.symbols.defineSymbol("nimEmulateOverflowChecks")
if conf.symbolFiles == disabledSf:
setPipeLinePass(graph, NirPass)
else:
setPipeLinePass(graph, SemPass)
compilePipelineProject(graph)
writeNinjaFile(graph)
proc commandJsonScript(graph: ModuleGraph) =
extccomp.runJsonBuildInstructions(graph.config, graph.config.jsonBuildInstructionsFile)
@@ -270,6 +293,8 @@ proc mainCommand*(graph: ModuleGraph) =
# and it has added this define implictly, so we must undo that here.
# A better solution might be to fix system.nim
undefSymbol(conf.symbols, "useNimRtl")
of backendNir:
if conf.exc == excNone: conf.exc = excGoto
of backendInvalid: raiseAssert "unreachable"
proc compileToBackend() =
@@ -280,6 +305,7 @@ proc mainCommand*(graph: ModuleGraph) =
of backendCpp: commandCompileToC(graph)
of backendObjc: commandCompileToC(graph)
of backendJs: commandCompileToJS(graph)
of backendNir: commandCompileToNir(graph)
of backendInvalid: raiseAssert "unreachable"
template docLikeCmd(body) =

View File

@@ -65,6 +65,7 @@ type
CgenPass
EvalPass
InterpreterPass
NirPass
NirReplPass
GenDependPass
Docgen2TexPass

View File

@@ -116,7 +116,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
conf.backend = backendC
if conf.selectedGC == gcUnselected:
if conf.backend in {backendC, backendCpp, backendObjc} or
if conf.backend in {backendC, backendCpp, backendObjc, backendNir} or
(conf.cmd == cmdInteractive and isDefined(conf, "nir")):
initOrcDefines(conf)

File diff suppressed because it is too large Load Diff

View File

@@ -10,8 +10,12 @@
## 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 ".." / [ast, modulegraphs, renderer, transf]
import nirtypes, nirinsts, ast2ir
from os import addFileExt, `/`, createDir
import ".." / [ast, modulegraphs, renderer, transf, options, msgs, lineinfos]
import nirtypes, nirinsts, ast2ir, nirlineinfos
import ".." / ic / [rodfiles, bitabs]
type
PCtx* = ref object of TPassContext
@@ -45,8 +49,8 @@ proc evalStmt(c: PCtx; n: PNode) =
var res = ""
if pc < c.c.code.len:
toString c.c.code, NodePos(pc), c.m.strings, c.m.integers, res
#res.add "\n"
toString c.c.code, NodePos(pc), c.m.lit.strings, c.m.lit.numbers, res
#res.add "\n--------------------------\n"
#toString res, c.m.types.g
echo res
@@ -61,11 +65,51 @@ proc runCode*(c: PPassContext; n: PNode): PNode =
result = n
c.oldErrorCount = c.m.graph.config.errorCounter
when false:
type
Module* = object
types: TypeGraph
data: seq[Tree]
init: seq[Tree]
procs: seq[Tree]
type
NirPassContext* = ref object of TPassContext
m: ModuleCon
c: ProcCon
proc openNirBackend*(g: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
let m = initModuleCon(g, g.config, idgen, module)
NirPassContext(m: m, c: initProcCon(m, nil, g.config), idgen: idgen)
proc gen(c: NirPassContext; n: PNode) =
let n = transformExpr(c.m.graph, c.idgen, c.m.module, n)
let pc = genStmt(c.c, n)
proc nirBackend*(c: PPassContext; n: PNode): PNode =
gen(NirPassContext(c), n)
result = n
proc closeNirBackend*(c: PPassContext; finalNode: PNode) =
discard nirBackend(c, finalNode)
let c = NirPassContext(c)
let nimcache = getNimcacheDir(c.c.config).string
createDir nimcache
let outp = nimcache / c.m.module.name.s.addFileExt("nir")
var r = rodfiles.create(outp)
try:
r.storeHeader(nirCookie)
r.storeSection stringsSection
r.store c.m.lit.strings
r.storeSection numbersSection
r.store c.m.lit.numbers
r.storeSection bodiesSection
r.store c.c.code
r.storeSection typesSection
r.store c.m.types.g
r.storeSection sideChannelSection
r.store c.m.man
finally:
r.close()
if r.err != ok:
rawMessage(c.c.config, errFatal, "serialization failed: " & outp)
else:
echo "created: ", outp

48
compiler/nir/nirc.nim Normal file
View File

@@ -0,0 +1,48 @@
#
#
# The Nim Compiler
# (c) Copyright 2023 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Nir Compiler. Currently only supports a "view" command.
import ".." / ic / [bitabs, rodfiles]
import nirinsts, nirtypes, nirlineinfos
proc view(filename: string) =
var lit = Literals()
var r = rodfiles.open(filename)
var code = default Tree
var man = default LineInfoManager
var types = initTypeGraph(lit)
try:
r.loadHeader(nirCookie)
r.loadSection stringsSection
r.load lit.strings
r.loadSection numbersSection
r.load lit.numbers
r.loadSection bodiesSection
r.load code
r.loadSection typesSection
r.load types
r.loadSection sideChannelSection
r.load man
finally:
r.close()
var res = ""
allTreesToString code, lit.strings, lit.numbers, res
echo res
import std / os
view paramStr(1)

View File

@@ -10,9 +10,14 @@
## NIR instructions. Somewhat inspired by LLVM's instructions.
import std / [assertions, hashes]
import .. / ic / bitabs
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
@@ -167,6 +172,8 @@ type
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)
@@ -257,6 +264,10 @@ 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
@@ -281,9 +292,11 @@ proc addSymDef*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} =
t.nodes.add Instr(x: toX(SymDef, uint32(s)), 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}
let x = prepare(t, info, opc)
t.nodes.add Instr(x: toX(Typed, uint32(typ)), info: info)
@@ -304,10 +317,16 @@ proc addIntVal*(t: var Tree; integers: var BiTable[int64]; info: PackedLineInfo;
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:
@@ -370,6 +389,14 @@ proc toString*(t: Tree; pos: NodePos; strings: BiTable[string]; integers: BiTabl
for i in 0..<nesting*2: r.add ' '
r.add "}"
proc allTreesToString*(t: Tree; strings: BiTable[string]; integers: BiTable[int64];
r: var string) =
var i = 0
while i < t.len:
toString t, NodePos(i), strings, integers, r
nextChild t, i
type
Value* = distinct Tree
@@ -419,3 +446,6 @@ proc addStrVal*(t: var Value; strings: var BiTable[string]; info: PackedLineInfo
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

View File

@@ -34,13 +34,13 @@ const
static:
assert AsideBit + FileBits + LineBits + ColBits == 32
import .. / ic / bitabs # for LitId
import .. / ic / [bitabs, rodfiles] # for LitId
type
PackedLineInfo* = distinct uint32
LineInfoManager* = object
aside*: seq[(LitId, int32, int32)]
aside: seq[(LitId, int32, int32)]
proc pack*(m: var LineInfoManager; file: LitId; line, col: int32): PackedLineInfo =
if file.uint32 <= FileMax.uint32 and line <= LineMax and col <= ColMax:
@@ -66,6 +66,9 @@ proc unpack*(m: LineInfoManager; i: PackedLineInfo): (LitId, int32, int32) =
proc getFileId*(m: LineInfoManager; i: PackedLineInfo): LitId =
result = unpack(m, i)[0]
proc store*(r: var RodFile; m: LineInfoManager) = storeSeq(r, m.aside)
proc load*(r: var RodFile; m: var LineInfoManager) = loadSeq(r, m.aside)
when isMainModule:
var m = LineInfoManager(aside: @[])
for i in 0'i32..<16388'i32:

View File

@@ -76,7 +76,7 @@ proc closeScope*(m: var SlotManager) =
when isMainModule:
var m = initSlotManager({ReuseTemps}, new(int))
var g = initTypeGraph()
var g = initTypeGraph(Literals())
let a = g.openType ArrayTy
g.addBuiltinType Int8Id

View File

@@ -10,12 +10,15 @@
## Type system for NIR. Close to C's type system but without its quirks.
import std / [assertions, hashes]
import .. / ic / bitabs
import .. / ic / [bitabs, rodfiles]
type
NirTypeKind* = enum
VoidTy, IntTy, UIntTy, FloatTy, BoolTy, CharTy, NameVal, IntVal,
VoidTy, IntTy, UIntTy, FloatTy, BoolTy, CharTy, NameVal,
IntVal, SizeVal, AlignVal, OffsetVal,
AnnotationVal,
ObjectTy,
UnionTy,
VarargsTy, # the `...` in a C prototype; also the last "atom"
APtrTy, # pointer to aliasable memory
UPtrTy, # pointer to unique/unaliasable memory
@@ -23,8 +26,6 @@ type
UArrayPtrTy, # pointer to array of unique/unaliasable memory
ArrayTy,
LastArrayTy, # array of unspecified size as a last field inside an object
ObjectTy,
UnionTy,
ProcTy,
ObjectDecl,
UnionDecl,
@@ -54,10 +55,13 @@ proc `==`*(a, b: TypeId): bool {.borrow.}
proc hash*(a: TypeId): Hash {.borrow.}
type
Literals* = ref object
strings*: BiTable[string]
numbers*: BiTable[int64]
TypeGraph* = object
nodes: seq[TypeNode]
names: BiTable[string]
numbers: BiTable[uint64]
lit: Literals
const
VoidId* = TypeId 0
@@ -76,7 +80,7 @@ const
VoidPtrId* = TypeId 13
LastBuiltinId* = 13
proc initTypeGraph*(): TypeGraph =
proc initTypeGraph*(lit: Literals): TypeGraph =
result = TypeGraph(nodes: @[
TypeNode(x: toX(VoidTy, 0'u32)),
TypeNode(x: toX(BoolTy, 8'u32)),
@@ -93,7 +97,7 @@ proc initTypeGraph*(): TypeGraph =
TypeNode(x: toX(FloatTy, 64'u32)),
TypeNode(x: toX(APtrTy, 2'u32)),
TypeNode(x: toX(VoidTy, 0'u32))
])
], lit: lit)
assert result.nodes.len == LastBuiltinId+2
type
@@ -164,9 +168,9 @@ proc sons3(tree: TypeGraph; n: TypeId): (TypeId, TypeId, TypeId) =
let c = b + span(tree, b)
result = (TypeId a, TypeId b, TypeId c)
proc arrayLen*(tree: TypeGraph; n: TypeId): BiggestUInt =
proc arrayLen*(tree: TypeGraph; n: TypeId): BiggestInt =
assert tree[n].kind == ArrayTy
result = tree.numbers[LitId tree[n].operand]
result = tree.lit.numbers[LitId tree[n].operand]
proc openType*(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos =
assert kind in {APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy,
@@ -174,20 +178,54 @@ proc openType*(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos =
FieldDecl}
result = prepare(tree, kind)
proc sealType*(tree: var TypeGraph; p: TypePatchPos): TypeId =
# TODO: Search for an existing instance of this type in
# order to reduce memory consumption.
result = TypeId(p)
template typeInvariant(p: TypePatchPos) =
when false:
if tree[TypeId(p)].kind == FieldDecl:
var k = 0
for ch in sons(tree, TypeId(p)):
inc k
assert k > 2, "damn! " & $k
proc sealType*(tree: var TypeGraph; p: TypePatchPos) =
patch tree, p
typeInvariant(p)
proc finishType*(tree: var TypeGraph; p: TypePatchPos): TypeId =
# Search for an existing instance of this type in
# order to reduce memory consumption:
patch tree, p
typeInvariant(p)
let s = span(tree, p.int)
var i = 0
while i < p.int:
if tree.nodes[i].x == tree.nodes[p.int].x:
var isMatch = true
for j in 1..<s:
if tree.nodes[j+i].x == tree.nodes[j+p.int].x:
discard "still a match"
else:
isMatch = false
break
if isMatch:
if p.int+s == tree.len:
setLen tree.nodes, p.int
return TypeId(i)
nextChild tree, i
result = TypeId(p)
proc nominalType*(tree: var TypeGraph; kind: NirTypeKind; name: string): TypeId =
assert kind in {ObjectTy, UnionTy}
let content = TypeNode(x: toX(kind, tree.lit.strings.getOrIncl(name)))
for i in 0..<tree.len:
if tree.nodes[i].x == content.x:
return TypeId(i)
result = TypeId tree.nodes.len
tree.nodes.add TypeNode(x: toX(kind, tree.names.getOrIncl(name)))
tree.nodes.add content
proc addNominalType*(tree: var TypeGraph; kind: NirTypeKind; name: string) =
assert kind in {ObjectTy, UnionTy}
tree.nodes.add TypeNode(x: toX(kind, tree.names.getOrIncl(name)))
tree.nodes.add TypeNode(x: toX(kind, tree.lit.strings.getOrIncl(name)))
proc addVarargs*(tree: var TypeGraph) =
tree.nodes.add TypeNode(x: toX(VarargsTy, 0'u32))
@@ -219,30 +257,46 @@ proc addType*(g: var TypeGraph; t: TypeId) =
for i in 0..<L:
g.nodes[d+i] = g.nodes[pos+i]
proc addArrayLen*(g: var TypeGraph; len: uint64) =
g.nodes.add TypeNode(x: toX(IntVal, g.numbers.getOrIncl(len)))
proc addArrayLen*(g: var TypeGraph; len: int64) =
g.nodes.add TypeNode(x: toX(IntVal, g.lit.numbers.getOrIncl(len)))
proc addSize*(g: var TypeGraph; s: int64) =
g.nodes.add TypeNode(x: toX(SizeVal, g.lit.numbers.getOrIncl(s)))
proc addOffset*(g: var TypeGraph; offset: int64) =
g.nodes.add TypeNode(x: toX(OffsetVal, g.lit.numbers.getOrIncl(offset)))
proc addAlign*(g: var TypeGraph; a: int64) =
g.nodes.add TypeNode(x: toX(AlignVal, g.lit.numbers.getOrIncl(a)))
proc addName*(g: var TypeGraph; name: string) =
g.nodes.add TypeNode(x: toX(NameVal, g.names.getOrIncl(name)))
g.nodes.add TypeNode(x: toX(NameVal, g.lit.strings.getOrIncl(name)))
proc addAnnotation*(g: var TypeGraph; name: string) =
g.nodes.add TypeNode(x: toX(NameVal, g.names.getOrIncl(name)))
g.nodes.add TypeNode(x: toX(NameVal, g.lit.strings.getOrIncl(name)))
proc addField*(g: var TypeGraph; name: string; typ: TypeId) =
proc addField*(g: var TypeGraph; name: string; typ: TypeId; offset: int64) =
let f = g.openType FieldDecl
g.addType typ
g.addOffset offset
g.addName name
discard sealType(g, f)
sealType(g, f)
proc ptrTypeOf*(g: var TypeGraph; t: TypeId): TypeId =
let f = g.openType APtrTy
g.addType t
result = sealType(g, f)
result = finishType(g, f)
proc arrayPtrTypeOf*(g: var TypeGraph; t: TypeId): TypeId =
let f = g.openType AArrayPtrTy
g.addType t
result = sealType(g, f)
result = finishType(g, f)
proc store*(r: var RodFile; g: TypeGraph) =
storeSeq r, g.nodes
proc load*(r: var RodFile; g: var TypeGraph) =
loadSeq r, g.nodes
proc toString*(dest: var string; g: TypeGraph; i: TypeId) =
case g[i].kind
@@ -263,9 +317,11 @@ proc toString*(dest: var string; g: TypeGraph; i: TypeId) =
dest.add "c"
dest.addInt g[i].operand
of NameVal, AnnotationVal:
dest.add g.names[LitId g[i].operand]
of IntVal:
dest.add $g.numbers[LitId g[i].operand]
dest.add g.lit.strings[LitId g[i].operand]
of IntVal, SizeVal, AlignVal, OffsetVal:
dest.add $g[i].kind
dest.add ' '
dest.add $g.lit.numbers[LitId g[i].operand]
of VarargsTy:
dest.add "..."
of APtrTy:
@@ -298,10 +354,10 @@ proc toString*(dest: var string; g: TypeGraph; i: TypeId) =
dest.add "]"
of ObjectTy:
dest.add "object "
dest.add g.names[LitId g[i].operand]
dest.add g.lit.strings[LitId g[i].operand]
of UnionTy:
dest.add "union "
dest.add g.names[LitId g[i].operand]
dest.add g.lit.strings[LitId g[i].operand]
of ProcTy:
dest.add "proc["
for t in sons(g, i): toString(dest, g, t)
@@ -319,10 +375,19 @@ proc toString*(dest: var string; g: TypeGraph; i: TypeId) =
dest.add '\n'
dest.add "]"
of FieldDecl:
let (typ, name) = g.sons2(i)
toString(dest, g, typ)
dest.add ' '
toString(dest, g, name)
dest.add "field["
for t in sons(g, i):
toString(dest, g, t)
dest.add ' '
dest.add "]"
when false:
let (typ, offset, name) = g.sons3(i)
toString(dest, g, typ)
dest.add ' '
toString(dest, g, offset)
dest.add ' '
toString(dest, g, name)
proc toString*(dest: var string; g: TypeGraph) =
var i = 0
@@ -336,17 +401,17 @@ proc `$`(g: TypeGraph): string =
toString(result, g)
when isMainModule:
var g = initTypeGraph()
var g = initTypeGraph(Literals())
let a = g.openType ArrayTy
g.addBuiltinType Int8Id
g.addArrayLen 5'u64
let finalArrayType = sealType(g, a)
g.addArrayLen 5
let finalArrayType = finishType(g, a)
let obj = g.openType ObjectDecl
g.nodes.add TypeNode(x: toX(NameVal, g.names.getOrIncl("MyType")))
g.nodes.add TypeNode(x: toX(NameVal, g.lit.strings.getOrIncl("MyType")))
g.addField "p", finalArrayType
discard sealType(g, obj)
g.addField "p", finalArrayType, 0
sealType(g, obj)
echo g

467
compiler/nir/nirvm.nim Normal file
View File

@@ -0,0 +1,467 @@
#
#
# The Nim Compiler
# (c) Copyright 2023 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
##[ NIR is a little too high level to interpret it efficiently. Thus
we compute `addresses` for SymIds, labels and offsets for object fields
in a preprocessing step.
We also split the instruction stream into separate (code, debug) seqs while
we're at it.
]##
import std / [tables, intsets]
import ".." / ic / bitabs
import nirinsts, nirtypes
type
OpcodeM = enum
ImmediateValM,
IntValM,
StrValM,
LoadLocalM, # with local ID
TypedM, # with type ID
PragmaIdM, # with Pragma ID, possible values: see PragmaKey enum
NilValM,
GotoM,
CheckedGotoM, # last atom
LoadProcM,
LoadGlobalM, # `"module".x`
ArrayConstrM,
ObjConstrM,
RetM,
YldM,
SelectM,
SelectPairM, # ((values...), Label)
SelectListM, # (values...)
SelectValueM, # (value)
SelectRangeM, # (valueA..valueB)
SummonGlobalM,
SummonThreadLocalM,
SummonM, # x = Summon Typed <Type ID>; x begins to live
SummonParamM,
AddrOfM,
ArrayAtM, # addr(a[i])
FieldAtM, # addr(obj.field)
LoadM, # a[]
StoreM, # a[] = b
AsgnM, # a = b
SetExcM,
TestExcM,
CheckedRangeM,
CheckedIndexM,
CallM,
IndirectCallM,
CheckedCallM, # call that can raise
CheckedIndirectCallM, # call that can raise
CheckedAddM, # with overflow checking etc.
CheckedSubM,
CheckedMulM,
CheckedDivM,
CheckedModM,
AddM,
SubM,
MulM,
DivM,
ModM,
BitShlM,
BitShrM,
BitAndM,
BitOrM,
BitXorM,
BitNotM,
BoolNotM,
EqM,
LeM,
LtM,
CastM,
NumberConvM,
CheckedObjConvM,
ObjConvM,
TestOfM,
ProcDeclM,
PragmaPairM
const
LastAtomicValue = CheckedGotoM
OpcodeBits = 8'u32
OpcodeMask = (1'u32 shl OpcodeBits) - 1'u32
type
Instr = distinct uint32
template kind(n: Instr): OpcodeM = OpcodeM(n and OpcodeMask)
template operand(n: Instr): uint32 = (n shr OpcodeBits)
template toIns(k: OpcodeM; operand: uint32): Instr =
Instr(uint32(k) or (operand shl OpcodeBits))
template toIns(k: OpcodeM; operand: LitId): Instr =
Instr(uint32(k) or (operand.uint32 shl OpcodeBits))
type
PatchPos = distinct int
CodePos = distinct int
Unit = ref object ## a NIR module
procs: Table[SymId, CodePos]
globals: Table[SymId, uint32]
integers: BiTable[int64]
strings: BiTable[string]
globalsGen: uint32
Universe* = object ## all units: For interpretation we need that
units: Table[string, Unit]
Bytecode = object
code: seq[Instr]
debug: seq[PackedLineInfo]
u: Unit
const
InvalidPatchPos* = PatchPos(-1)
proc isValid(p: PatchPos): bool {.inline.} = p.int != -1
proc prepare(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM): PatchPos =
result = PatchPos bc.code.len
bc.code.add toIns(kind, 1'u32)
bc.debug.add info
proc add(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM; raw: uint32) =
bc.code.add toIns(kind, raw)
bc.debug.add info
proc add(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM; lit: LitId) =
add bc, info, kind, uint(lit)
proc isAtom(bc: Bytecode; pos: int): bool {.inline.} = bc.code[pos].kind <= LastAtomicValue
proc isAtom(bc: Bytecode; pos: CodePos): bool {.inline.} = bc.code[pos.int].kind <= LastAtomicValue
proc patch(bc: var Bytecode; pos: PatchPos) =
let pos = pos.int
let k = bc.code[pos].kind
assert k > LastAtomicValue
let distance = int32(bc.code.len - pos)
assert distance > 0
bc.code[pos] = toIns(k, cast[uint32](distance))
template build(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM; body: untyped) =
let pos = prepare(bc, info, kind)
body
patch(bc, pos)
proc len*(bc: Bytecode): int {.inline.} = bc.code.len
template rawSpan(n: Instr): int = int(operand(n))
proc nextChild(bc: Bytecode; pos: var int) {.inline.} =
if bc.code[pos].kind > LastAtomicValue:
assert bc.code[pos].operand > 0'u32
inc pos, bc.code[pos].rawSpan
else:
inc pos
iterator sons(bc: Bytecode; n: CodePos): CodePos =
var pos = n.int
assert bc.code[pos].kind > LastAtomicValue
let last = pos + bc.code[pos].rawSpan
inc pos
while pos < last:
yield CodePos pos
nextChild bc, pos
template `[]`*(t: Bytecode; n: CodePos): Instr = t.code[n.int]
proc span(bc: Bytecode; pos: int): int {.inline.} =
if bc.code[pos].kind <= LastAtomicValue: 1 else: int(bc.code[pos].operand)
type
Preprocessing = object
known: Table[LabelId, CodePos]
toPatch: Table[LabelId, seq[CodePos]]
locals: Table[SymId, uint32]
c: Bytecode # to be moved out
thisModule: LitId
markedWithLabel: IntSet
proc genGoto(c: var Preprocessing; lab: LabelId; opc: OpcodeM) =
let dest = c.known.getOrDefault(lab, CodePos(-1))
if dest.int >= 0:
c.bc.add info, opc, uint32 dest
else:
let here = CodePos(c.bc.code.len)
c.toPatch.mgetOrPut(lab, @[]).add here
c.bc.add info, opc, 1u32 # will be patched once we traversed the label
proc preprocess(c: var Preprocessing; u: var Universe; t: Tree; n: NodePos) =
let info = t[n].info
template recurse(opc) =
build c.bc, info, opc:
for c in sons(t, n): preprocess(c, u, t, c)
case t[n].kind
of Nop:
discard "don't use Nop"
of ImmediateVal:
c.bc.add info, ImmediateValM, t[n].rawOperand
of IntVal:
c.bc.add info, IntValM, t[n].rawOperand
of StrVal:
c.bc.add info, StrValM, t[n].rawOperand
of SymDef:
assert false, "SymDef outside of declaration context"
of SymUse:
let s = t[n].symId
if c.locals.hasKey(s):
c.bc.add info, LoadLocalM, c.locals[s]
elif c.bc.u.procs.hasKey(s):
build c.bc, info, LoadProcM:
c.bc.add info, StrValM, thisModule
c.bc.add info, LoadLocalM, uint32 c.bc.u.procs[s]
elif c.bc.u.globals.hasKey(s):
build c.bc, info, LoadGlobalM:
c.bc.add info, StrValM, thisModule
c.bc.add info, LoadLocalM, uint32 s
else:
assert false, "don't understand SymUse ID"
of ModuleSymUse:
let moduleName {.cursor.} = c.bc.u.strings[t[n.firstSon].litId]
let unit = u.units.getOrDefault(moduleName)
of Typed:
c.bc.add info, TypedM, t[n].rawOperand
of PragmaId:
c.bc.add info, TypedM, t[n].rawOperand
of NilVal:
c.bc.add info, NilValM, t[n].rawOperand
of LoopLabel, Label:
let lab = t[n].label
let here = CodePos(c.bc.code.len-1)
c.known[lab] = here
var p: seq[CodePos]
if c.toPatch.take(lab, p):
for x in p: c.bc.code[x] = toIns(c.bc.code[x].kind, here)
c.markedWithLabel.incl here.int # for toString()
of Goto, GotoLoop:
c.genGoto(t[n].label, GotoM)
of CheckedGoto:
c.genGoto(t[n].label, CheckedGotoM)
of ArrayConstr:
recurse ArrayConstrM
of ObjConstr:
recurse ObjConstrM
of Ret:
recurse RetM
of Yld:
recurse YldM
of Select:
recurse SelectM
of SelectPair:
recurse SelectPairM
of SelectList:
recurse SelectListM
of SelectValue:
recurse SelectValueM
of SelectRange:
recurse SelectRangeM
of SummonGlobal, SummonThreadLocal, SummonConst:
#let s =
discard "xxx"
of Summon, SummonParam:
# x = Summon Typed <Type ID>; x begins to live
discard "xxx"
of Kill:
discard "we don't care about Kill instructions"
of AddrOf:
recurse AddrOfM
of ArrayAt:
recurse ArrayAtM
of FieldAt:
recurse FieldAtM
of Load:
recurse LoadM
of Store:
recurse StoreM
of Asgn:
recurse AsgnM
of SetExc:
recurse SetExcM
of TestExc:
recurse TestExcM
of CheckedRange:
recurse CheckedRangeM
of CheckedIndex:
recurse CheckedIndexM
of Call:
recurse CallM
of IndirectCall:
recurse IndirectCallM
of CheckedCall:
recurse CheckedCallM
of CheckedIndirectCall:
recurse CheckedIndirectCallM
of CheckedAdd:
recurse CheckedAddM
of CheckedSub:
recurse CheckedSubM
of CheckedMul:
recurse CheckedMulM
of CheckedDiv:
recurse CheckedDivM
of CheckedMod:
recurse CheckedModM
of Add:
recurse AddM
of Sub:
recurse SubM
of Mul:
recurse MulM
of Div:
recurse DivM
of Mod:
recurse ModM
of BitShl:
recurse BitShlM
of BitShr:
recurse BitShrM
of BitAnd:
recurse BitAndM
of BitOr:
recurse BitOrM
of BitXor:
recurse BitXorM
of BitNot:
recurse BitNotM
of BoolNot:
recurse BoolNotM
of Eq:
recurse EqM
of Le:
recurse LeM
of Lt:
recurse LtM
of Cast:
recurse CastM
of NumberConv:
recurse NumberConvM
of CheckedObjConv:
recurse CheckedObjConvM
of ObjConv:
recurse ObjConvM
of TestOf:
recurse TestOfM
of Emit:
assert false, "cannot interpret: Emit"
of ProcDecl:
recurse ProcDeclM
of PragmaPair:
recurse PragmaPairM
const PayloadSize = 128
type
StackFrame = ref object
locals: pointer # usually points into `payload` if size is small enough, otherwise it's `alloc`'ed.
payload: array[PayloadSize, byte]
caller: StackFrame
returnAddr: CodePos
proc newStackFrame(size: int; caller: StackFrame; returnAddr: CodePos): StackFrame =
result = StackFrame(caller: caller, returnAddr: returnAddr)
if size <= PayloadSize:
result.locals = addr(result.payload)
else:
result.locals = alloc0(size)
proc popStackFrame(s: StackFrame): StackFrame =
if result.locals != addr(result.payload):
dealloc result.locals
result = s.caller
template `+!`(p: pointer; diff: uint): pointer = cast[pointer](cast[uint](p) + diff)
proc eval(c: seq[Instr]; pc: CodePos; s: StackFrame; result: pointer)
proc evalAddr(c: seq[Instr]; pc: CodePos; s: StackFrame): pointer =
case c[pc].kind
of LoadLocalM:
result = s.locals +! c[pc].operand
of FieldAtM:
result = eval(c, pc+1, s)
result = result +! c[pc+2].operand
of ArrayAtM:
let elemSize = c[pc+1].operand
result = eval(c, pc+2, s)
var idx: int
eval(c, pc+3, addr idx)
result = result +! (idx * elemSize)
proc eval(c: seq[Instr]; pc: CodePos; s: StackFrame; result: pointer) =
case c[pc].kind
of AddM:
# assume `int` here for now:
var x, y: int
eval c, pc+1, s, addr x
eval c, pc+2, s, addr y
cast[ptr int](res)[] = x + y
of StrValM:
cast[ptr StringDesc](res)[] = addr(c.strings[c[pc].litId])
of ObjConstrM:
for ch in sons(c, pc):
let offset = c[ch]
eval c, ch+2, s, result+!offset
of ArrayConstrM:
let elemSize = c[pc+1].operand
var r = result
for ch in sons(c, pc):
eval c, ch, s, r
r = r+!elemSize # can even do strength reduction here!
else:
assert false, "cannot happen"
proc exec(c: seq[Instr]; pc: CodePos) =
var pc = pc
var currentFrame: StackFrame = nil
while true:
case c[pc].kind
of GotoM:
pc = CodePos(c[pc].operand)
of Asgn:
let (size, a, b) = sons3(c, pc)
let dest = evalAddr(c, a, s)
eval(c, b, s, dest)
of CallM:
# No support for return values, these are mapped to `var T` parameters!
let prc = evalProc(c, pc+1)
# setup storage for the proc already:
let s2 = newStackFrame(prc.frameSize, currentFrame, pc)
var i = 0
for a in sons(c, pc):
eval(c, a, s2, paramAddr(s2, i))
inc i
currentFrame = s2
pc = pcOf(prc)
of RetM:
pc = currentFrame.returnAddr
currentFrame = popStackFrame(currentFrame)
of SelectM:
var x: bool
eval(c, b, addr x)
# follow the selection instructions...
pc = activeBranch(c, b, x)

View File

@@ -0,0 +1,200 @@
#
#
# The Nim Compiler
# (c) Copyright 2023 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## included from ast2ir.nim
#[
case s
of "abc", "abbd":
echo 1
of "hah":
echo 2
of "gah":
echo 3
# we produce code like this:
if s[0] <= 'a':
if s == "abc: goto L1
elif s == "abbd": goto L1
else:
if s[2] <= 'h':
if s == "hah": goto L2
elif s == "gah": goto L3
goto afterCase
L1:
echo 1
goto afterCase
L2:
echo 2
goto afterCase
L3:
echo 3
goto afterCase
afterCase: ...
]#
# We split the set of strings into 2 sets of roughly the same size.
# The condition used for splitting is a (position, char) tuple.
# Every string of length > position for which s[position] <= char is in one
# set else it is in the other set.
from sequtils import addUnique
type
Key = (LitId, LabelId)
proc splitValue(strings: BiTable[string]; a: openArray[Key]; position: int): (char, float) =
var cand: seq[char] = @[]
for t in items a:
let s = strings[t[0]]
if s.len > position: cand.addUnique s[position]
result = ('\0', -1.0)
for disc in items cand:
var hits = 0
for t in items a:
let s = strings[t[0]]
if s.len > position and s[position] <= disc:
inc hits
# the split is the better, the more `hits` is close to `a.len / 2`:
let grade = 100000.0 - abs(hits.float - a.len.float / 2.0)
if grade > result[1]:
result = (disc, grade)
proc tryAllPositions(strings: BiTable[string]; a: openArray[Key]): (char, int) =
var m = 0
for t in items a:
m = max(m, strings[t[0]].len)
result = ('\0', -1)
var best = -1.0
for i in 0 ..< m:
let current = splitValue(strings, a, i)
if current[1] > best:
best = current[1]
result = (current[0], i)
type
SearchKind = enum
LinearSearch, SplitSearch
SearchResult* = object
case kind: SearchKind
of LinearSearch:
a: seq[Key]
of SplitSearch:
span: int
best: (char, int)
proc emitLinearSearch(strings: BiTable[string]; a: openArray[Key]; dest: var seq[SearchResult]) =
var d = SearchResult(kind: LinearSearch, a: @[])
for x in a: d.a.add x
dest.add d
proc split(strings: BiTable[string]; a: openArray[Key]; dest: var seq[SearchResult]) =
if a.len <= 4:
emitLinearSearch strings, a, dest
else:
let best = tryAllPositions(strings, a)
var groupA: seq[Key] = @[]
var groupB: seq[Key] = @[]
for t in items a:
let s = strings[t[0]]
if s.len > best[1] and s[best[1]] <= best[0]:
groupA.add t
else:
groupB.add t
if groupA.len == 0 or groupB.len == 0:
emitLinearSearch strings, a, dest
else:
let toPatch = dest.len
dest.add SearchResult(kind: SplitSearch, span: 1, best: best)
split strings, groupA, dest
split strings, groupB, dest
let dist = dest.len - toPatch
assert dist > 0
dest[toPatch].span = dist
proc toProblemDescription(c: var ProcCon; n: PNode): (seq[Key], LabelId) =
result = (@[], newLabels(c.labelGen, n.len))
assert n.kind == nkCaseStmt
for i in 1..<n.len:
let it = n[i]
let thisBranch = LabelId(result[1].int + i - 1)
if it.kind == nkOfBranch:
for j in 0..<it.len-1:
assert it[j].kind in {nkStrLit..nkTripleStrLit}
result[0].add (c.lit.strings.getOrIncl(it[j].strVal), thisBranch)
proc decodeSolution(c: var ProcCon; dest: var Tree; s: seq[SearchResult]; i: int;
selector: Value; info: PackedLineInfo) =
case s[i].kind
of SplitSearch:
let thenA = i+1
let elseA = thenA + (if s[thenA].kind == LinearSearch: 1 else: s[thenA].span)
let best = s[i].best
let tmp = getTemp(c, Bool8Id, info)
buildTyped dest, info, Asgn, Bool8Id:
dest.copyTree tmp
buildTyped dest, info, Call, Bool8Id:
c.addUseCodegenProc dest, "nimStrAtLe", info
dest.copyTree selector
dest.addIntVal c.lit.numbers, info, c.m.nativeIntId, best[1]
dest.addIntVal c.lit.numbers, info, Char8Id, best[0].int
template then() =
c.decodeSolution dest, s, thenA, selector, info
template otherwise() =
c.decodeSolution dest, s, elseA, selector, info
buildIfThenElse tmp, then, otherwise
freeTemp c, tmp
of LinearSearch:
let tmp = getTemp(c, Bool8Id, info)
for x in s[i].a:
buildTyped dest, info, Asgn, Bool8Id:
dest.copyTree tmp
buildTyped dest, info, Call, Bool8Id:
c.addUseCodegenProc dest, "eqStrings", info
dest.copyTree selector
dest.addStrLit info, x[0]
buildIf tmp:
c.code.gotoLabel info, Goto, x[1]
freeTemp c, tmp
proc genStringCase(c: var ProcCon; n: PNode; d: var Value) =
let (problem, firstBranch) = toProblemDescription(c, n)
var solution: seq[SearchResult] = @[]
split c.lit.strings, problem, solution
# XXX Todo move complex case selector into a temporary.
let selector = c.genx(n[0])
let info = toLineInfo(c, n.info)
decodeSolution c, c.code, solution, 0, selector, info
let lend = newLabel(c.labelGen)
c.code.addLabel info, Goto, lend
for i in 1..<n.len:
let it = n[i]
let thisBranch = LabelId(firstBranch.int + i - 1)
c.code.addLabel info, Label, thisBranch
if it.kind == nkOfBranch:
gen(c, it.lastSon, d)
c.code.addLabel info, Goto, lend
else:
gen(c, it.lastSon, d)
c.code.addLabel info, Label, lend
freeTemp c, selector

View File

@@ -18,8 +18,8 @@ type
g*: TypeGraph
conf: ConfigRef
proc initTypesCon*(conf: ConfigRef): TypesCon =
TypesCon(g: initTypeGraph(), conf: conf)
proc initTypesCon*(conf: ConfigRef; lit: Literals): TypesCon =
TypesCon(g: initTypeGraph(lit), conf: conf)
proc mangle(c: var TypesCon; t: PType): string =
result = $sighashes.hashType(t, c.conf)
@@ -67,11 +67,11 @@ proc objectToIr(c: var TypesCon; n: PNode; fieldTypes: Table[ItemId, TypeId]; un
let subObj = openType(c.g, ObjectDecl)
c.g.addName "uo_" & $unionId & "_" & $i
objectToIr c, lastSon(n[i]), fieldTypes, unionId
discard sealType(c.g, subObj)
sealType(c.g, subObj)
else: discard
discard sealType(c.g, u)
sealType(c.g, u)
of nkSym:
c.g.addField n.sym.name.s & "_" & $n.sym.position, fieldTypes[n.sym.itemId]
c.g.addField n.sym.name.s & "_" & $n.sym.position, fieldTypes[n.sym.itemId], n.sym.offset
else:
assert false, "unknown node kind: " & $n.kind
@@ -85,6 +85,9 @@ proc objectToIr(c: var TypesCon; t: PType): TypeId =
collectFieldTypes c, t.n, fieldTypes
let obj = openType(c.g, ObjectDecl)
c.g.addName mangle(c, t)
c.g.addSize c.conf.getSize(t)
c.g.addAlign c.conf.getAlign(t)
if t[0] != nil:
c.g.addNominalType(ObjectTy, mangle(c, t[0]))
else:
@@ -93,12 +96,13 @@ proc objectToIr(c: var TypesCon; t: PType): TypeId =
let f2 = c.g.openType FieldDecl
let voidPtr = openType(c.g, APtrTy)
c.g.addBuiltinType(VoidId)
discard sealType(c.g, voidPtr)
sealType(c.g, voidPtr)
c.g.addOffset 0 # type field is always at offset 0
c.g.addName "m_type"
discard sealType(c.g, f2) # FieldDecl
sealType(c.g, f2) # FieldDecl
objectToIr c, t.n, fieldTypes, unionId
result = sealType(c.g, obj)
result = finishType(c.g, obj)
proc objectHeaderToIr(c: var TypesCon; t: PType): TypeId =
result = c.g.nominalType(ObjectTy, mangle(c, t))
@@ -109,9 +113,18 @@ proc tupleToIr(c: var TypesCon; t: PType): TypeId =
fieldTypes[i] = typeToIr(c, t[i])
let obj = openType(c.g, ObjectDecl)
c.g.addName mangle(c, t)
c.g.addSize c.conf.getSize(t)
c.g.addAlign c.conf.getAlign(t)
var accum = OffsetAccum(maxAlign: 1)
for i in 0..<t.len:
c.g.addField "f_" & $i, fieldTypes[i]
result = sealType(c.g, obj)
let child = t[i]
c.g.addField "f_" & $i, fieldTypes[i], accum.offset
computeSizeAlign(c.conf, child)
accum.align(child.align)
accum.inc(int32(child.size))
result = finishType(c.g, obj)
proc procToIr(c: var TypesCon; t: PType; addEnv = false): TypeId =
var fieldTypes = newSeq[TypeId](0)
@@ -137,11 +150,11 @@ proc procToIr(c: var TypesCon; t: PType; addEnv = false): TypeId =
if addEnv:
let a = openType(c.g, APtrTy)
c.g.addBuiltinType(VoidId)
discard sealType(c.g, a)
sealType(c.g, a)
if tfVarargs in t.flags:
c.g.addVarargs()
result = sealType(c.g, obj)
result = finishType(c.g, obj)
proc nativeInt(c: TypesCon): TypeId =
case c.conf.target.intSize
@@ -154,7 +167,7 @@ proc openArrayPayloadType*(c: var TypesCon; t: PType): TypeId =
let elementType = typeToIr(c, e)
let arr = c.g.openType AArrayPtrTy
c.g.addType elementType
result = sealType(c.g, arr) # LastArrayTy
result = finishType(c.g, arr) # LastArrayTy
proc openArrayToIr(c: var TypesCon; t: PType): TypeId =
# object (a: ArrayPtr[T], len: int)
@@ -167,38 +180,45 @@ proc openArrayToIr(c: var TypesCon; t: PType): TypeId =
let p = openType(c.g, ObjectDecl)
c.g.addName typeName
c.g.addSize c.conf.target.ptrSize*2
c.g.addAlign c.conf.target.ptrSize
let f = c.g.openType FieldDecl
let arr = c.g.openType AArrayPtrTy
c.g.addType elementType
discard sealType(c.g, arr) # LastArrayTy
sealType(c.g, arr) # LastArrayTy
c.g.addOffset 0
c.g.addName "data"
discard sealType(c.g, f) # FieldDecl
sealType(c.g, f) # FieldDecl
c.g.addField "len", c.nativeInt
c.g.addField "len", c.nativeInt, c.conf.target.ptrSize
result = sealType(c.g, p) # ObjectDecl
result = finishType(c.g, p) # ObjectDecl
proc strPayloadType(c: var TypesCon): string =
result = "NimStrPayload"
let p = openType(c.g, ObjectDecl)
c.g.addName result
c.g.addField "cap", c.nativeInt
c.g.addSize c.conf.target.ptrSize*2
c.g.addAlign c.conf.target.ptrSize
c.g.addField "cap", c.nativeInt, 0
let f = c.g.openType FieldDecl
let arr = c.g.openType LastArrayTy
c.g.addBuiltinType Char8Id
discard sealType(c.g, arr) # LastArrayTy
sealType(c.g, arr) # LastArrayTy
c.g.addOffset c.conf.target.ptrSize # comes after the len field
c.g.addName "data"
discard sealType(c.g, f) # FieldDecl
sealType(c.g, f) # FieldDecl
discard sealType(c.g, p)
sealType(c.g, p)
proc strPayloadPtrType*(c: var TypesCon): TypeId =
let mangled = strPayloadType(c)
let ffp = c.g.openType APtrTy
c.g.addNominalType ObjectTy, mangled
result = sealType(c.g, ffp) # APtrTy
result = finishType(c.g, ffp) # APtrTy
proc stringToIr(c: var TypesCon): TypeId =
#[
@@ -216,16 +236,20 @@ proc stringToIr(c: var TypesCon): TypeId =
let str = openType(c.g, ObjectDecl)
c.g.addName "NimStringV2"
c.g.addField "len", c.nativeInt
c.g.addSize c.conf.target.ptrSize*2
c.g.addAlign c.conf.target.ptrSize
c.g.addField "len", c.nativeInt, 0
let fp = c.g.openType FieldDecl
let ffp = c.g.openType APtrTy
c.g.addNominalType ObjectTy, "NimStrPayload"
discard sealType(c.g, ffp) # APtrTy
sealType(c.g, ffp) # APtrTy
c.g.addOffset c.conf.target.ptrSize # comes after 'len' field
c.g.addName "p"
discard sealType(c.g, fp) # FieldDecl
sealType(c.g, fp) # FieldDecl
result = sealType(c.g, str) # ObjectDecl
result = finishType(c.g, str) # ObjectDecl
proc seqPayloadType(c: var TypesCon; t: PType): string =
#[
@@ -241,21 +265,25 @@ proc seqPayloadType(c: var TypesCon; t: PType): string =
let p = openType(c.g, ObjectDecl)
c.g.addName payloadName
c.g.addField "cap", c.nativeInt
c.g.addSize c.conf.target.intSize
c.g.addAlign c.conf.target.intSize
c.g.addField "cap", c.nativeInt, 0
let f = c.g.openType FieldDecl
let arr = c.g.openType LastArrayTy
c.g.addType elementType
discard sealType(c.g, arr) # LastArrayTy
sealType(c.g, arr) # LastArrayTy
c.g.addOffset c.conf.target.ptrSize
c.g.addName "data"
discard sealType(c.g, f) # FieldDecl
discard sealType(c.g, p)
sealType(c.g, f) # FieldDecl
sealType(c.g, p)
proc seqPayloadPtrType*(c: var TypesCon; t: PType): TypeId =
let mangledBase = seqPayloadType(c, t)
let ffp = c.g.openType APtrTy
c.g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase
result = sealType(c.g, ffp) # APtrTy
result = finishType(c.g, ffp) # APtrTy
proc seqToIr(c: var TypesCon; t: PType): TypeId =
#[
@@ -267,16 +295,20 @@ proc seqToIr(c: var TypesCon; t: PType): TypeId =
let sq = openType(c.g, ObjectDecl)
c.g.addName "NimSeqV2" & mangledBase
c.g.addField "len", c.nativeInt
c.g.addSize c.conf.getSize(t)
c.g.addAlign c.conf.getAlign(t)
c.g.addField "len", c.nativeInt, 0
let fp = c.g.openType FieldDecl
let ffp = c.g.openType APtrTy
c.g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase
discard sealType(c.g, ffp) # APtrTy
sealType(c.g, ffp) # APtrTy
c.g.addOffset c.conf.target.ptrSize
c.g.addName "p"
discard sealType(c.g, fp) # FieldDecl
sealType(c.g, fp) # FieldDecl
result = sealType(c.g, sq) # ObjectDecl
result = finishType(c.g, sq) # ObjectDecl
proc closureToIr(c: var TypesCon; t: PType): TypeId =
@@ -291,21 +323,25 @@ proc closureToIr(c: var TypesCon; t: PType): TypeId =
let p = openType(c.g, ObjectDecl)
c.g.addName typeName
c.g.addSize c.conf.getSize(t)
c.g.addAlign c.conf.getAlign(t)
let f = c.g.openType FieldDecl
c.g.addType procType
c.g.addOffset 0
c.g.addName "ClP_0"
discard sealType(c.g, f) # FieldDecl
sealType(c.g, f) # FieldDecl
let f2 = c.g.openType FieldDecl
let voidPtr = openType(c.g, APtrTy)
c.g.addBuiltinType(VoidId)
discard sealType(c.g, voidPtr)
sealType(c.g, voidPtr)
c.g.addOffset c.conf.target.ptrSize
c.g.addName "ClE_0"
discard sealType(c.g, f2) # FieldDecl
sealType(c.g, f2) # FieldDecl
result = sealType(c.g, p) # ObjectDecl
result = finishType(c.g, p) # ObjectDecl
proc bitsetBasetype*(c: var TypesCon; t: PType): TypeId =
let s = int(getSize(c.conf, t))
@@ -376,8 +412,8 @@ proc typeToIr*(c: var TypesCon; t: PType): TypeId =
let elemType = typeToIr(c, t[1])
let a = openType(c.g, ArrayTy)
c.g.addType(elemType)
c.g.addArrayLen uint64(n)
result = sealType(c.g, a)
c.g.addArrayLen n
result = finishType(c.g, a)
of tyPtr, tyRef:
cached(c, t):
let e = t.lastSon
@@ -385,12 +421,12 @@ proc typeToIr*(c: var TypesCon; t: PType): TypeId =
let elemType = typeToIr(c, e.lastSon)
let a = openType(c.g, AArrayPtrTy)
c.g.addType(elemType)
result = sealType(c.g, a)
result = finishType(c.g, a)
else:
let elemType = typeToIr(c, t.lastSon)
let a = openType(c.g, APtrTy)
c.g.addType(elemType)
result = sealType(c.g, a)
result = finishType(c.g, a)
of tyVar, tyLent:
cached(c, t):
let e = t.lastSon
@@ -401,7 +437,7 @@ proc typeToIr*(c: var TypesCon; t: PType): TypeId =
let elemType = typeToIr(c, e)
let a = openType(c.g, APtrTy)
c.g.addType(elemType)
result = sealType(c.g, a)
result = finishType(c.g, a)
of tySet:
let s = int(getSize(c.conf, t))
case s
@@ -414,12 +450,13 @@ proc typeToIr*(c: var TypesCon; t: PType): TypeId =
cached(c, t):
let a = openType(c.g, ArrayTy)
c.g.addType(UInt8Id)
c.g.addArrayLen uint64(s)
result = sealType(c.g, a)
of tyPointer:
c.g.addArrayLen s
result = finishType(c.g, a)
of tyPointer, tyNil:
# tyNil can happen for code like: `const CRAP = nil` which we have in posix.nim
let a = openType(c.g, APtrTy)
c.g.addBuiltinType(VoidId)
result = sealType(c.g, a)
result = finishType(c.g, a)
of tyObject:
# Objects are special as they can be recursive in Nim. This is easily solvable.
# We check if we are already "processing" t. If so, we produce `ObjectTy`
@@ -451,20 +488,20 @@ proc typeToIr*(c: var TypesCon; t: PType): TypeId =
cached(c, t):
let a = openType(c.g, AArrayPtrTy)
c.g.addBuiltinType Char8Id
result = sealType(c.g, a)
result = finishType(c.g, a)
of tyUncheckedArray:
# We already handled the `ptr UncheckedArray` in a special way.
cached(c, t):
let elemType = typeToIr(c, t.lastSon)
let a = openType(c.g, LastArrayTy)
c.g.addType(elemType)
result = sealType(c.g, a)
result = finishType(c.g, a)
of tyUntyped, tyTyped:
# this avoids a special case for system.echo which is not a generic but
# uses `varargs[typed]`:
result = VoidId
of tyNone, tyEmpty, tyTypeDesc,
tyNil, tyGenericInvocation, tyProxy, tyBuiltInTypeClass,
tyGenericInvocation, tyProxy, tyBuiltInTypeClass,
tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass,
tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward:
result = TypeId(-1)

View File

@@ -137,13 +137,15 @@ type
backendCpp = "cpp"
backendJs = "js"
backendObjc = "objc"
backendNir = "nir"
# backendNimscript = "nimscript" # this could actually work
# backendLlvm = "llvm" # probably not well supported; was cmdCompileToLLVM
Command* = enum ## Nim's commands
cmdNone # not yet processed command
cmdUnknown # command unmapped
cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS
cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS,
cmdCompileToNir,
cmdCrun # compile and run in nimache
cmdTcc # run the project via TCC backend
cmdCheck # semantic checking for whole project
@@ -170,7 +172,8 @@ type
# old unused: cmdInterpret, cmdDef: def feature (find definition for IDEs)
const
cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS, cmdCrun}
cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
cmdCompileToJS, cmdCrun, cmdCompileToNir}
cmdDocLike* = {cmdDoc0, cmdDoc, cmdDoc2tex, cmdJsondoc0, cmdJsondoc,
cmdCtags, cmdBuildindex}
@@ -235,9 +238,9 @@ type
laxEffects
## Lax effects system prior to Nim 2.0.
verboseTypeMismatch
emitGenerics
## generics are emitted in the module that contains them.
## Useful for libraries that rely on local passC
emitGenerics
## generics are emitted in the module that contains them.
## Useful for libraries that rely on local passC
SymbolFilesOption* = enum
disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest

View File

@@ -45,6 +45,8 @@ proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext):
result = interpreterCode(bModule, semNode)
of NirReplPass:
result = runCode(bModule, semNode)
of NirPass:
result = nirBackend(bModule, semNode)
of NonePass:
raiseAssert "use setPipeLinePass to set a proper PipelinePass"
@@ -107,6 +109,8 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
case graph.pipelinePass
of CgenPass:
setupCgen(graph, module, idgen)
of NirPass:
openNirBackend(graph, module, idgen)
of JSgenPass:
when not defined(leanCompiler):
setupJSgen(graph, module, idgen)
@@ -203,6 +207,8 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
discard interpreterCode(bModule, finalNode)
of NirReplPass:
discard runCode(bModule, finalNode)
of NirPass:
closeNirBackend(bModule, finalNode)
of SemPass, GenDependPass:
discard
of Docgen2Pass, Docgen2TexPass:

View File

@@ -29,11 +29,11 @@ proc raiseIllegalTypeRecursion() =
raise newException(IllegalTypeRecursionError, "illegal type recursion")
type
OffsetAccum = object
maxAlign: int32
offset: int32
OffsetAccum* = object
maxAlign*: int32
offset*: int32
proc inc(arg: var OffsetAccum; value: int32) =
proc inc*(arg: var OffsetAccum; value: int32) =
if unlikely(value == szIllegalRecursion): raiseIllegalTypeRecursion()
if value == szUnknownSize or arg.offset == szUnknownSize:
arg.offset = szUnknownSize
@@ -47,7 +47,7 @@ proc alignmentMax(a, b: int32): int32 =
else:
max(a, b)
proc align(arg: var OffsetAccum; value: int32) =
proc align*(arg: var OffsetAccum; value: int32) =
if unlikely(value == szIllegalRecursion): raiseIllegalTypeRecursion()
if value == szUnknownSize or arg.maxAlign == szUnknownSize or arg.offset == szUnknownSize:
arg.maxAlign = szUnknownSize
@@ -73,7 +73,7 @@ proc finish(arg: var OffsetAccum): int32 =
result = align(arg.offset, arg.maxAlign) - arg.offset
arg.offset += result
proc computeSizeAlign(conf: ConfigRef; typ: PType)
proc computeSizeAlign*(conf: ConfigRef; typ: PType)
proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt =
## returns object alignment

View File

@@ -178,7 +178,7 @@ proc newSeq[T](s: var seq[T], len: Natural) =
shrink(s, 0)
setLen(s, len)
proc sameSeqPayload(x: pointer, y: pointer): bool {.compilerproc, inline.} =
proc sameSeqPayload(x: pointer, y: pointer): bool {.compilerRtl, inl.} =
result = cast[ptr NimRawSeq](x)[].p == cast[ptr NimRawSeq](y)[].p

View File

@@ -209,6 +209,9 @@ proc nimAddStrV1(s: var NimStringV2; src: NimStringV2) {.compilerRtl, inl.} =
proc nimDestroyStrV1(s: NimStringV2) {.compilerRtl, inl.} =
frees(s)
proc nimStrAtLe(s: string; idx: int; ch: char): bool {.compilerRtl, inl.} =
result = idx < s.len and s[idx] <= ch
func capacity*(self: string): int {.inline.} =
## Returns the current capacity of the string.
# See https://github.com/nim-lang/RFCs/issues/460