mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-21 14:55:24 +00:00
NIR: store sizes, alignments and offsets in the type graph; beginning… (#22822)
…s of a patent-pending new VM
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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) =
|
||||
|
||||
@@ -65,6 +65,7 @@ type
|
||||
CgenPass
|
||||
EvalPass
|
||||
InterpreterPass
|
||||
NirPass
|
||||
NirReplPass
|
||||
GenDependPass
|
||||
Docgen2TexPass
|
||||
|
||||
@@ -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
@@ -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
48
compiler/nir/nirc.nim
Normal 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)
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
467
compiler/nir/nirvm.nim
Normal 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)
|
||||
200
compiler/nir/stringcases.nim
Normal file
200
compiler/nir/stringcases.nim
Normal 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
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user