NIR: C codegen, WIP (#22903)

This commit is contained in:
Andreas Rumpf
2023-11-05 20:25:25 +01:00
committed by GitHub
parent f0e5bdd7d8
commit eb8824d71c
10 changed files with 1071 additions and 91 deletions

View File

@@ -36,9 +36,7 @@ proc mustRehash(length, counter: int): bool {.inline.} =
result = (length * 2 < counter * 3) or (length - counter < 4)
const
idStart = 256 ##
## Ids do not start with 0 but with this value. The IR needs it.
## TODO: explain why
idStart = 1
template idToIdx(x: LitId): int = x.int - idStart

View File

@@ -98,6 +98,7 @@ type
backendFlagsSection
aliveSymsSection # beware, this is stored in a `.alivesyms` file.
sideChannelSection
namespaceSection
symnamesSection
RodFileError* = enum

View File

@@ -31,6 +31,8 @@ type
idgen: IdGenerator
processedProcs, pendingProcsAsSet: HashSet[ItemId]
pendingProcs: seq[PSym] # procs we still need to generate code for
pendingVarsAsSet: HashSet[ItemId]
pendingVars: seq[PSym]
noModularity*: bool
inProc: int
toSymId: Table[ItemId, SymId]
@@ -69,6 +71,8 @@ proc initModuleCon*(graph: ModuleGraph; config: ConfigRef; idgen: IdGenerator; m
result.nativeIntId = Int64Id
result.nativeUIntId = UInt16Id
result.strPayloadId = strPayloadPtrType(result.types, result.nirm.types)
nirm.namespace = nirm.lit.strings.getOrIncl(customPath(toFullPath(config, module.info)))
nirm.intbits = uint32(config.target.intSize * 8)
proc initProcCon*(m: ModuleCon; prc: PSym; config: ConfigRef): ProcCon =
result = ProcCon(m: m, sm: initSlotManager({}), prc: prc, config: config,
@@ -162,11 +166,6 @@ proc getTemp(c: var ProcCon; t: TypeId; info: PackedLineInfo): Value =
c.code.addSummon info, tmp, t
result = localToValue(info, tmp)
template withTemp(tmp, n, body: untyped) {.dirty.} =
var tmp = getTemp(c, n)
body
c.freeTemp(tmp)
proc gen(c: var ProcCon; n: PNode; flags: GenFlags = {}) =
var tmp = default(Value)
gen(c, n, tmp, flags)
@@ -281,14 +280,15 @@ proc genIf(c: var ProcCon; n: PNode; d: var Value) =
var it = n[i]
if it.len == 2:
let info = toLineInfo(c, it[0].info)
withTemp(tmp, it[0]):
var elsePos: LabelId
if isNotOpr(it[0]):
c.gen(it[0][1], tmp)
elsePos = c.xjmp(it[0][1], opcTJmp, tmp) # if true
else:
c.gen(it[0], tmp)
elsePos = c.xjmp(it[0], opcFJmp, tmp) # if false
var elsePos: LabelId
if isNotOpr(it[0]):
let tmp = c.genx(it[0][1])
elsePos = c.xjmp(it[0][1], opcTJmp, tmp) # if true
c.freeTemp tmp
else:
let tmp = c.genx(it[0])
elsePos = c.xjmp(it[0], opcFJmp, tmp) # if false
c.freeTemp tmp
c.clearDest(n, d)
if isEmptyType(it[1].typ): # maybe noreturn call, don't touch `d`
c.genScope(it[1])
@@ -404,22 +404,23 @@ proc genCase(c: var ProcCon; n: PNode; d: var Value) =
var sections = newSeqOfCap[LabelId](n.len-1)
let ending = newLabel(c.labelGen)
let info = toLineInfo(c, n.info)
withTemp(tmp, n[0]):
buildTyped c.code, info, Select, typeToIr(c.m, n[0].typ):
c.gen(n[0], tmp)
for i in 1..<n.len:
let section = newLabel(c.labelGen)
sections.add section
let it = n[i]
let itinfo = toLineInfo(c, it.info)
build c.code, itinfo, SelectPair:
build c.code, itinfo, SelectList:
for j in 0..<it.len-1:
if it[j].kind == nkRange:
caseRange c, it[j]
else:
caseValue c, it[j]
c.code.addLabel itinfo, Goto, section
let tmp = c.genx(n[0])
buildTyped c.code, info, Select, typeToIr(c.m, n[0].typ):
c.code.copyTree tmp
for i in 1..<n.len:
let section = newLabel(c.labelGen)
sections.add section
let it = n[i]
let itinfo = toLineInfo(c, it.info)
build c.code, itinfo, SelectPair:
build c.code, itinfo, SelectList:
for j in 0..<it.len-1:
if it[j].kind == nkRange:
caseRange c, it[j]
else:
caseValue c, it[j]
c.code.addLabel itinfo, Goto, section
c.freeTemp tmp
for i in 1..<n.len:
let it = n[i]
let itinfo = toLineInfo(c, it.info)
@@ -2131,6 +2132,30 @@ proc genAsgn2(c: var ProcCon; a, b: PNode) =
var d = c.genx(a)
c.gen b, d
proc irModule(c: var ProcCon; owner: PSym): string =
#if owner == c.m.module: "" else:
customPath(toFullPath(c.config, owner.info))
proc fromForeignModule(c: ProcCon; s: PSym): bool {.inline.} =
result = ast.originatingModule(s) != c.m.module and not c.m.noModularity
proc genForeignVar(c: var ProcCon; s: PSym) =
var opc: Opcode
if s.kind == skConst:
opc = SummonConst
elif sfThread in s.flags:
opc = SummonThreadLocal
else:
assert sfGlobal in s.flags
opc = SummonGlobal
let t = typeToIr(c.m, s.typ)
let info = toLineInfo(c, s.info)
build c.code, info, ForeignDecl:
buildTyped c.code, info, opc, t:
build c.code, info, ModuleSymUse:
c.code.addStrVal c.lit.strings, info, irModule(c, ast.originatingModule(s))
c.code.addImmediateVal info, s.itemId.item.int
proc genVarSection(c: var ProcCon; n: PNode) =
for a in n:
if a.kind == nkCommentStmt: continue
@@ -2143,7 +2168,10 @@ proc genVarSection(c: var ProcCon; n: PNode) =
if vn.kind == nkSym:
let s = vn.sym
var opc: Opcode
if sfThread in s.flags:
if s.kind == skConst:
opc = SummonConst
if dontInlineConstant(n, s.astdef): continue
elif sfThread in s.flags:
opc = SummonThreadLocal
elif sfGlobal in s.flags:
opc = SummonGlobal
@@ -2172,17 +2200,13 @@ proc convStrToCStr(c: var ProcCon; n: PNode; d: var Value) =
proc convCStrToStr(c: var ProcCon; n: PNode; d: var Value) =
genUnaryCp(c, n, d, "cstrToNimstr", argAt = 0)
proc irModule(c: var ProcCon; owner: PSym): string =
#if owner == c.m.module: "" else:
customPath(toFullPath(c.config, owner.info))
proc fromForeignModule(c: ProcCon; s: PSym): bool {.inline.} =
result = ast.originatingModule(s) != c.m.module and not c.m.noModularity
proc genRdVar(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
let info = toLineInfo(c, n.info)
let s = n.sym
if fromForeignModule(c, s):
if s.kind in {skVar, skConst, skLet} and not c.m.pendingVarsAsSet.containsOrIncl(s.itemId):
c.m.pendingVars.add s
template body(target) =
build target, info, ModuleSymUse:
target.addStrVal c.lit.strings, info, irModule(c, ast.originatingModule(s))
@@ -2197,10 +2221,15 @@ proc genRdVar(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
proc genSym(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) =
let s = n.sym
case s.kind
of skVar, skForVar, skTemp, skLet, skResult, skParam, skConst:
of skConst:
if dontInlineConstant(n, s.astdef):
genRdVar(c, n, d, flags)
else:
gen(c, s.astdef, d, flags)
of skVar, skForVar, skTemp, skLet, skResult, skParam:
genRdVar(c, n, d, flags)
of skProc, skFunc, skConverter, skMethod, skIterator:
if not fromForeignModule(c, s):
if not c.m.noModularity:
# anon and generic procs have no AST so we need to remember not to forget
# to emit these:
if not c.m.processedProcs.contains(s.itemId):
@@ -2354,12 +2383,13 @@ proc genObjAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
valueIntoDest c, info, d, n.typ, body
freeTemp c, a
proc genParams(c: var ProcCon; params: PNode; prc: PSym) =
proc genParams(c: var ProcCon; params: PNode; prc: PSym): PSym =
result = nil
if params.len > 0 and resultPos < prc.ast.len:
let resNode = prc.ast[resultPos]
let res = resNode.sym # get result symbol
c.code.addSummon toLineInfo(c, res.info), toSymId(c, res),
typeToIr(c.m, res.typ), SummonResult
result = resNode.sym # get result symbol
c.code.addSummon toLineInfo(c, result.info), toSymId(c, result),
typeToIr(c.m, result.typ), SummonResult
elif prc.typ.len > 0 and not isEmptyType(prc.typ[0]) and not isCompileTimeOnly(prc.typ[0]):
# happens for procs without bodies:
let t = typeToIr(c.m, prc.typ[0])
@@ -2389,6 +2419,7 @@ proc addCallConv(c: var ProcCon; info: PackedLineInfo; callConv: TCallingConvent
of ccNoConvention: ann NoCall
proc genProc(cOuter: var ProcCon; prc: PSym) =
if prc.magic notin generatedMagics: return
if cOuter.m.processedProcs.containsOrIncl(prc.itemId):
return
#assert cOuter.m.inProc == 0, " in nested proc! " & prc.name.s
@@ -2399,14 +2430,22 @@ proc genProc(cOuter: var ProcCon; prc: PSym) =
inc cOuter.m.inProc
var c = initProcCon(cOuter.m, prc, cOuter.m.graph.config)
let body =
if not fromForeignModule(c, prc):
transformBody(c.m.graph, c.m.idgen, prc, {useCache, keepOpenArrayConversions})
else:
nil
let body = transformBody(c.m.graph, c.m.idgen, prc, {useCache, keepOpenArrayConversions})
let info = toLineInfo(c, body.info)
build c.code, info, ProcDecl:
let symId = toSymId(c, prc)
addSymDef c.code, info, symId
c.m.nirm.symnames[symId] = c.lit.strings.getOrIncl(prc.name.s)
let info = toLineInfo(c, prc.info)
build c.code, info, (if body != nil: ProcDecl else: ForeignProcDecl):
if body != nil:
let symId = toSymId(c, prc)
addSymDef c.code, info, symId
c.m.nirm.symnames[symId] = c.lit.strings.getOrIncl(prc.name.s)
else:
build c.code, info, ModuleSymUse:
c.code.addStrVal c.lit.strings, info, irModule(c, ast.originatingModule(prc))
c.code.addImmediateVal info, prc.itemId.item.int
addCallConv c, info, prc.typ.callConv
if sfCompilerProc in prc.flags:
build c.code, info, PragmaPair:
@@ -2435,11 +2474,16 @@ proc genProc(cOuter: var ProcCon; prc: PSym) =
else:
c.code.addPragmaId info, ObjExport
genParams(c, prc.typ.n, prc)
gen(c, body)
patch c, body, c.exitLabel
build c.code, info, Ret:
discard
let resultSym = genParams(c, prc.typ.n, prc)
if body != nil:
gen(c, body)
patch c, body, c.exitLabel
if resultSym != nil:
build c.code, info, Ret:
c.code.addSymUse info, toSymId(c, resultSym)
else:
build c.code, info, Ret:
c.code.addNop info
#copyTree cOuter.code, c.code
dec cOuter.m.inProc
@@ -2569,10 +2613,13 @@ proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) =
localError(c.config, n.info, "cannot generate IR code for " & $n)
proc genPendingProcs(c: var ProcCon) =
while c.m.pendingProcs.len > 0:
while c.m.pendingProcs.len > 0 or c.m.pendingVars.len > 0:
let procs = move(c.m.pendingProcs)
for v in procs:
genProc(c, v)
let vars = move(c.m.pendingVars)
for v in vars:
genForeignVar(c, v)
proc genStmt*(c: var ProcCon; n: PNode): NodePos =
result = NodePos c.code.len

View File

@@ -9,8 +9,11 @@
# We produce C code as a list of tokens.
import std / assertions
import .. / ic / bitabs
import std / [assertions, syncio, tables, intsets]
from std / strutils import toOctal
import .. / ic / [bitabs, rodfiles]
import nirtypes, nirinsts, nirfiles
import ../../dist/checksums/src/checksums/md5
type
Token = LitId # indexing into the tokens BiTable[string]
@@ -18,7 +21,6 @@ type
PredefinedToken = enum
IgnoreMe = "<unused>"
EmptyToken = ""
DeclPrefix = "" # the next token is the name of a definition
CurlyLe = "{"
CurlyRi = "}"
ParLe = "("
@@ -29,7 +31,7 @@ type
Semicolon = ";"
Comma = ", "
Space = " "
Colon = ":"
Colon = ": "
Dot = "."
Arrow = "->"
Star = "*"
@@ -38,36 +40,41 @@ type
ScopeOpr = "::"
ConstKeyword = "const "
StaticKeyword = "static "
NimString = "NimString"
StrLitPrefix = "(NimChar*)"
StrLitNamePrefix = "Qstr"
LoopKeyword = "while (true) "
WhileKeyword = "while ("
ExternKeyword = "extern "
WhileKeyword = "while "
IfKeyword = "if ("
ElseKeyword = "else "
SwitchKeyword = "switch ("
SwitchKeyword = "switch "
CaseKeyword = "case "
DefaultKeyword = "default:"
BreakKeyword = "break"
NullPtr = "nullptr"
IfNot = "if (!("
ReturnKeyword = "return "
const
ModulePrefix = Token(int(ReturnKeyword)+1)
TypedefStruct = "typedef struct "
TypedefUnion = "typedef union "
IncludeKeyword = "#include "
proc fillTokenTable(tab: var BiTable[string]) =
for e in EmptyToken..high(PredefinedToken):
let id = tab.getOrIncl $e
assert id == LitId(e)
assert id == LitId(e), $(id, " ", ord(e))
type
GeneratedCode* = object
m: NirModule
includes: seq[LitId]
includedHeaders: IntSet
data: seq[LitId]
protos: seq[LitId]
code: seq[LitId]
tokens: BiTable[string]
emittedStrings: IntSet
needsPrefix: IntSet
mangledModules: Table[LitId, LitId]
proc initGeneratedCode*(): GeneratedCode =
result = GeneratedCode(code: @[], tokens: initBiTable[string]())
proc initGeneratedCode*(m: sink NirModule): GeneratedCode =
result = GeneratedCode(m: m, code: @[], tokens: initBiTable[string]())
fillTokenTable(result.tokens)
proc add*(g: var GeneratedCode; t: PredefinedToken) {.inline.} =
@@ -76,3 +83,845 @@ proc add*(g: var GeneratedCode; t: PredefinedToken) {.inline.} =
proc add*(g: var GeneratedCode; s: string) {.inline.} =
g.code.add g.tokens.getOrIncl(s)
proc mangleModuleName(c: var GeneratedCode; key: LitId): LitId =
result = c.mangledModules.getOrDefault(key, LitId(0))
if result == LitId(0):
let u {.cursor.} = c.m.lit.strings[key]
var last = u.len - len(".nim") - 1
var start = last
while start >= 0 and u[start] != '/': dec start
var sum = getMD5(u)
sum.setLen(8)
let dest = u.substr(start+1, last) & sum
result = c.tokens.getOrIncl(dest)
c.mangledModules[key] = result
type
CppFile = object
f: File
proc write(f: var CppFile; s: string) = write(f.f, s)
proc write(f: var CppFile; c: char) = write(f.f, c)
proc writeTokenSeq(f: var CppFile; s: seq[Token]; c: GeneratedCode) =
var indent = 0
for i in 0..<s.len:
let x = s[i]
case x
of Token(CurlyLe):
inc indent
write f, c.tokens[x]
write f, "\n"
for i in 1..indent*2: write f, ' '
of Token(CurlyRi):
dec indent
write f, c.tokens[x]
if i+1 < s.len and s[i+1] == Token(CurlyRi):
discard
else:
write f, "\n"
for i in 1..indent*2: write f, ' '
of Token(Semicolon):
write f, c.tokens[x]
if i+1 < s.len and s[i+1] == Token(CurlyRi):
discard "no newline before }"
else:
write f, "\n"
for i in 1..indent*2: write f, ' '
of Token(NewLine):
write f, c.tokens[x]
for i in 1..indent*2: write f, ' '
else:
write f, c.tokens[x]
# Type graph
type
TypeList = object
processed: IntSet
s: seq[(TypeId, PredefinedToken)]
proc add(dest: var TypeList; elem: TypeId; decl: PredefinedToken) =
if not containsOrIncl(dest.processed, int(elem)):
dest.s.add (elem, decl)
type
TypeOrder = object
forwardedDecls, ordered: TypeList
typeImpls: Table[string, TypeId]
lookedAt: IntSet
proc traverseObject(types: TypeGraph; lit: Literals; c: var TypeOrder; t: TypeId)
proc recordDependency(types: TypeGraph; lit: Literals; c: var TypeOrder; parent, child: TypeId) =
var ch = child
var viaPointer = false
while true:
case types[ch].kind
of APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy:
viaPointer = true
ch = elementType(types, ch)
of LastArrayTy:
ch = elementType(types, ch)
else:
break
case types[ch].kind
of ObjectTy, UnionTy:
let decl = if types[ch].kind == ObjectTy: TypedefStruct else: TypedefUnion
let obj = c.typeImpls.getOrDefault(lit.strings[types[ch].litId])
if viaPointer:
c.forwardedDecls.add obj, decl
else:
if not containsOrIncl(c.lookedAt, obj.int):
traverseObject(types, lit, c, obj)
c.ordered.add obj, decl
of ArrayTy:
if viaPointer:
c.forwardedDecls.add ch, TypedefStruct
else:
if not containsOrIncl(c.lookedAt, ch.int):
traverseObject(types, lit, c, ch)
c.ordered.add ch, TypedefStruct
else:
discard "uninteresting type as we only focus on the required struct declarations"
proc traverseObject(types: TypeGraph; lit: Literals; c: var TypeOrder; t: TypeId) =
for x in sons(types, t):
case types[x].kind
of FieldDecl:
recordDependency types, lit, c, t, x.firstSon
of ObjectTy:
# inheritance
recordDependency types, lit, c, t, x
else: discard
proc traverseTypes(types: TypeGraph; lit: Literals; c: var TypeOrder) =
for t in allTypes(types):
if types[t].kind in {ObjectDecl, UnionDecl}:
assert types[t.firstSon].kind == NameVal
c.typeImpls[lit.strings[types[t.firstSon].litId]] = t
for t in allTypesIncludingInner(types):
case types[t].kind
of ObjectDecl, UnionDecl:
traverseObject types, lit, c, t
let decl = if types[t].kind == ObjectDecl: TypedefStruct else: TypedefUnion
c.ordered.add t, decl
of ArrayTy:
traverseObject types, lit, c, t
c.ordered.add t, TypedefStruct
else: discard
when false:
template emitType(s: string) = c.types.add c.tokens.getOrIncl(s)
template emitType(t: Token) = c.types.add t
template emitType(t: PredefinedToken) = c.types.add Token(t)
proc genType(g: var GeneratedCode; types: TypeGraph; lit: Literals; t: TypeId; name = "") =
template maybeAddName =
if name != "":
g.add Space
g.add name
template atom(s: string) =
g.add s
maybeAddName()
case types[t].kind
of VoidTy: atom "void"
of IntTy: atom "NI" & $types[t].integralBits
of UIntTy: atom "NU" & $types[t].integralBits
of FloatTy: atom "NF" & $types[t].integralBits
of BoolTy: atom "NB" & $types[t].integralBits
of CharTy: atom "NC" & $types[t].integralBits
of ObjectTy, UnionTy, NameVal, AnnotationVal:
atom lit.strings[types[t].litId]
of VarargsTy:
g.add "..."
of APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy:
genType g, types, lit, elementType(types, t)
g.add Star
maybeAddName()
of ArrayTy:
genType g, types, lit, arrayName(types, t)
maybeAddName()
of LastArrayTy:
genType g, types, lit, elementType(types, t)
maybeAddName()
g.add BracketLe
g.add BracketRi
of ProcTy:
let (retType, callConv) = returnType(types, t)
genType g, types, lit, retType
genType g, types, lit, callConv
g.add ParLe
g.add Star # "(*fn)"
maybeAddName()
g.add ParRi
g.add ParLe
var i = 0
for ch in params(types, t):
if i > 0: g.add Comma
genType g, types, lit, ch
inc i
g.add ParRi
of ObjectDecl, UnionDecl:
atom lit.strings[types[t.firstSon].litId]
of IntVal, SizeVal, AlignVal, OffsetVal, FieldDecl:
#raiseAssert "did not expect: " & $types[t].kind
g.add "BUG "
atom $types[t].kind
proc generateTypes(g: var GeneratedCode; types: TypeGraph; lit: Literals; c: TypeOrder) =
for (t, declKeyword) in c.forwardedDecls.s:
let name = if types[t].kind == ArrayTy: arrayName(types, t) else: t.firstSon
let s {.cursor.} = lit.strings[types[name].litId]
g.add declKeyword
g.add s
g.add Space
g.add s
g.add Semicolon
for (t, declKeyword) in c.ordered.s:
let name = if types[t].kind == ArrayTy: arrayName(types, t) else: t.firstSon
let s {.cursor.} = lit.strings[types[name].litId]
g.add declKeyword
g.add CurlyLe
if types[t].kind == ArrayTy:
#let name = arrayName(types, t)
genType g, types, lit, elementType(types, t), "a"
g.add BracketLe
g.add $arrayLen(types, t)
g.add BracketRi
g.add Semicolon
else:
var i = 0
for x in sons(types, t):
case types[x].kind
of FieldDecl:
genType g, types, lit, x.firstSon, "F" & $i
g.add Semicolon
inc i
of ObjectTy:
genType g, types, lit, x, "P"
g.add Semicolon
else: discard
g.add CurlyRi
g.add s
g.add Semicolon
# Procs
proc toCChar*(c: char; result: var string) {.inline.} =
case c
of '\0'..'\x1F', '\x7F'..'\xFF':
result.add '\\'
result.add toOctal(c)
of '\'', '\"', '\\', '?':
result.add '\\'
result.add c
else:
result.add c
proc makeCString(s: string): string =
result = newStringOfCap(s.len + 10)
result.add('"')
for c in s: toCChar(c, result)
result.add('"')
template emitData(s: string) = c.data.add c.tokens.getOrIncl(s)
template emitData(t: Token) = c.data.add t
template emitData(t: PredefinedToken) = c.data.add Token(t)
proc genStrLit(c: var GeneratedCode; lit: Literals; litId: LitId): Token =
result = Token(c.tokens.getOrIncl "QStr" & $litId)
if not containsOrIncl(c.emittedStrings, int(litId)):
let s {.cursor.} = lit.strings[litId]
emitData "static const struct "
emitData CurlyLe
emitData " NI cap"
emitData Semicolon
emitData "NC8 data"
emitData BracketLe
emitData $s.len
emitData "+1"
emitData BracketRi
emitData Semicolon
emitData CurlyRi
emitData result
emitData AsgnOpr
emitData CurlyLe
emitData $s.len
emitData " | NIM_STRLIT_FLAG"
emitData Comma
emitData makeCString(s)
emitData CurlyRi
emitData Semicolon
proc genIntLit(c: var GeneratedCode; lit: Literals; litId: LitId) =
let i = lit.numbers[litId]
if i > low(int32) and i <= high(int32):
c.add $i
elif i == low(int32):
# Nim has the same bug for the same reasons :-)
c.add "(-2147483647 -1)"
elif i > low(int64):
c.add "IL64("
c.add $i
c.add ")"
else:
c.add "(IL64(-9223372036854775807) - IL64(1))"
proc gen(c: var GeneratedCode; t: Tree; n: NodePos)
proc genSymDef(c: var GeneratedCode; t: Tree; n: NodePos) =
if t[n].kind == SymDef:
c.needsPrefix.incl t[n].symId.int
gen c, t, n
proc genGlobal(c: var GeneratedCode; t: Tree; name, typ: NodePos; annotation: string) =
c.add annotation
let m: string
if t[name].kind == SymDef:
m = c.tokens[mangleModuleName(c, c.m.namespace)] & "__" & $t[name].symId
else:
assert t[name].kind == ModuleSymUse
let (x, y) = sons2(t, name)
m = c.tokens[mangleModuleName(c, t[x].litId)] & "__" & $t[y].immediateVal
genType c, c.m.types, c.m.lit, t[typ].typeId, m
proc genLocal(c: var GeneratedCode; t: Tree; name, typ: NodePos; annotation: string) =
assert t[name].kind == SymDef
c.add annotation
genType c, c.m.types, c.m.lit, t[typ].typeId, "q" & $t[name].symId
# XXX Use proper names here
proc genProcDecl(c: var GeneratedCode; t: Tree; n: NodePos; isExtern: bool) =
let signatureBegin = c.code.len
let name = n.firstSon
var prc = n.firstSon
next t, prc
while true:
case t[prc].kind
of PragmaPair:
let (x, y) = sons2(t, prc)
let key = cast[PragmaKey](t[x].rawOperand)
case key
of HeaderImport:
let lit = t[y].litId
let headerAsStr {.cursor.} = c.m.lit.strings[lit]
let header = c.tokens.getOrIncl(headerAsStr)
# headerAsStr can be empty, this has the semantics of the `nodecl` pragma:
if headerAsStr.len > 0 and not c.includedHeaders.containsOrIncl(int header):
if headerAsStr[0] == '#':
discard "skip the #include"
else:
c.includes.add Token(IncludeKeyword)
c.includes.add header
c.includes.add Token NewLine
# do not generate code for importc'ed procs:
return
of DllImport:
let lit = t[y].litId
raiseAssert "cannot eval: " & c.m.lit.strings[lit]
else: discard
of PragmaId: discard
else: break
next t, prc
var resultDeclPos = NodePos(-1)
if t[prc].kind == SummonResult:
resultDeclPos = prc
gen c, t, prc.firstSon
next t, prc
else:
c.add "void"
c.add Space
genSymDef c, t, name
c.add ParLe
var params = 0
while t[prc].kind == SummonParam:
if params > 0: c.add Comma
let (typ, sym) = sons2(t, prc)
genLocal c, t, sym, typ, ""
next t, prc
inc params
if params == 0:
c.add "void"
c.add ParRi
for i in signatureBegin ..< c.code.len:
c.protos.add c.code[i]
c.protos.add Token Semicolon
if isExtern:
c.code.setLen signatureBegin
else:
c.add CurlyLe
if resultDeclPos.int >= 0:
gen c, t, resultDeclPos
for ch in sonsRest(t, n, prc):
gen c, t, ch
c.add CurlyRi
template triop(opr) =
let (typ, a, b) = sons3(t, n)
c.add ParLe
c.add ParLe
gen c, t, typ
c.add ParRi
gen c, t, a
c.add opr
gen c, t, b
c.add ParRi
template cmpop(opr) =
let (_, a, b) = sons3(t, n)
c.add ParLe
gen c, t, a
c.add opr
gen c, t, b
c.add ParRi
template binaryop(opr) =
let (typ, a) = sons2(t, n)
c.add ParLe
c.add ParLe
gen c, t, typ
c.add ParRi
c.add opr
gen c, t, a
c.add ParRi
template checkedBinaryop(opr) =
let (typ, labIdx, a, b) = sons4(t, n)
let bits = integralBits(c.m.types[t[typ].typeId])
let lab = t[labIdx].label
c.add (opr & $bits)
c.add ParLe
c.gen t, a
c.add Comma
c.gen t, b
c.add Comma
c.add "L" & $lab.int
c.add ParRi
template moveToDataSection(body: untyped) =
let oldLen = c.code.len
body
for i in oldLen ..< c.code.len:
c.data.add c.code[i]
setLen c.code, oldLen
proc gen(c: var GeneratedCode; t: Tree; n: NodePos) =
case t[n].kind
of Nop:
discard "nothing to emit"
of ImmediateVal:
c.add "BUG: " & $t[n].kind
of IntVal:
genIntLit c, c.m.lit, t[n].litId
of StrVal:
c.code.add genStrLit(c, c.m.lit, t[n].litId)
of Typed:
genType c, c.m.types, c.m.lit, t[n].typeId
of SymDef, SymUse:
let s = t[n].symId
if c.needsPrefix.contains(s.int):
c.code.add mangleModuleName(c, c.m.namespace)
c.add "__"
c.add $s
else:
# XXX Use proper names here
c.add "q"
c.add $s
of ModuleSymUse:
let (x, y) = sons2(t, n)
let u = mangleModuleName(c, t[x].litId)
let s = t[y].immediateVal
c.code.add u
c.add "__"
c.add $s
of NilVal:
c.add NullPtr
of LoopLabel:
c.add WhileKeyword
c.add ParLe
c.add "1"
c.add ParRi
c.add CurlyLe
of GotoLoop:
c.add CurlyRi
of Label:
let lab = t[n].label
c.add "L"
c.add $lab.int
c.add Colon
c.add Semicolon
of Goto:
let lab = t[n].label
c.add "goto L"
c.add $lab.int
c.add Semicolon
of CheckedGoto:
discard "XXX todo"
of ArrayConstr:
c.add CurlyLe
var i = 0
for ch in sonsFrom1(t, n):
if i > 0: c.add Comma
c.gen t, ch
inc i
c.add CurlyRi
of ObjConstr:
c.add CurlyLe
var i = 0
for ch in sonsFrom1(t, n):
if i mod 2 == 0:
if i > 0: c.add Comma
c.add ".F" & $t[ch].immediateVal
c.add AsgnOpr
else:
c.gen t, ch
inc i
c.add CurlyRi
of Ret:
c.add ReturnKeyword
c.gen t, n.firstSon
c.add Semicolon
of Select:
c.add SwitchKeyword
c.add ParLe
let (_, selector) = sons2(t, n)
c.gen t, selector
c.add ParRi
c.add CurlyLe
for ch in sonsFromN(t, n, 2):
c.gen t, ch
c.add CurlyRi
of SelectPair:
let (le, ri) = sons2(t, n)
c.gen t, le
c.gen t, ri
of SelectList:
for ch in sons(t, n):
c.gen t, ch
of SelectValue:
c.add CaseKeyword
c.gen t, n.firstSon
c.add Colon
of SelectRange:
let (le, ri) = sons2(t, n)
c.add CaseKeyword
c.gen t, le
c.add " ... "
c.gen t, ri
c.add Colon
of ForeignDecl:
c.data.add LitId(ExternKeyword)
c.gen t, n.firstSon
of SummonGlobal:
moveToDataSection:
let (typ, sym) = sons2(t, n)
c.genGlobal t, sym, typ, ""
c.add Semicolon
of SummonThreadLocal:
moveToDataSection:
let (typ, sym) = sons2(t, n)
c.genGlobal t, sym, typ, "__thread "
c.add Semicolon
of SummonConst:
moveToDataSection:
let (typ, sym) = sons2(t, n)
c.genGlobal t, sym, typ, "const "
c.add Semicolon
of Summon, SummonResult:
let (typ, sym) = sons2(t, n)
c.genLocal t, sym, typ, ""
c.add Semicolon
of SummonParam:
raiseAssert "SummonParam should have been handled in genProc"
of Kill:
discard "we don't care about Kill instructions"
of AddrOf:
let (_, arg) = sons2(t, n)
c.add "&"
gen c, t, arg
of DerefArrayAt:
let (_, a, i) = sons3(t, n)
gen c, t, a
c.add BracketLe
gen c, t, i
c.add BracketRi
of ArrayAt:
let (_, a, i) = sons3(t, n)
gen c, t, a
c.add Dot
c.add "a"
c.add BracketLe
gen c, t, i
c.add BracketRi
of FieldAt:
let (_, a, b) = sons3(t, n)
gen c, t, a
let field = t[b].immediateVal
c.add Dot
c.add "F" & $field
of DerefFieldAt:
let (_, a, b) = sons3(t, n)
gen c, t, a
let field = t[b].immediateVal
c.add Arrow
c.add "F" & $field
of Load:
let (_, arg) = sons2(t, n)
c.add ParLe
c.add "*"
gen c, t, arg
c.add ParRi
of Store:
raiseAssert "Assumption was that Store is unused!"
of Asgn:
let (_, dest, src) = sons3(t, n)
gen c, t, dest
c.add AsgnOpr
gen c, t, src
c.add Semicolon
of CheckedRange:
c.add "nimCheckRange"
c.add ParLe
let (_, gotoInstr, x, a, b) = sons5(t, n)
gen c, t, x
c.add Comma
gen c, t, a
c.add Comma
gen c, t, b
c.add Comma
c.add "L" & $t[gotoInstr].label.int
c.add ParRi
of CheckedIndex:
c.add "nimCheckIndex"
c.add ParLe
let (gotoInstr, x, a) = sons3(t, n)
gen c, t, x
c.add Comma
gen c, t, a
c.add Comma
c.add "L" & $t[gotoInstr].label.int
c.add ParRi
of Call, IndirectCall:
let (typ, fn) = sons2(t, n)
gen c, t, fn
c.add ParLe
var i = 0
for ch in sonsFromN(t, n, 2):
if i > 0: c.add Comma
gen c, t, ch
inc i
c.add ParRi
if c.m.types[t[typ].typeId].kind == VoidTy:
c.add Semicolon
of CheckedCall, CheckedIndirectCall:
let (typ, gotoInstr, fn) = sons3(t, n)
gen c, t, fn
c.add ParLe
var i = 0
for ch in sonsFromN(t, n, 3):
if i > 0: c.add Comma
gen c, t, ch
inc i
c.add ParRi
if c.m.types[t[typ].typeId].kind == VoidTy:
c.add Semicolon
of CheckedAdd: checkedBinaryop "nimAddInt"
of CheckedSub: checkedBinaryop "nimSubInt"
of CheckedMul: checkedBinaryop "nimMulInt"
of CheckedDiv: checkedBinaryop "nimDivInt"
of CheckedMod: checkedBinaryop "nimModInt"
of Add: triop " + "
of Sub: triop " - "
of Mul: triop " * "
of Div: triop " / "
of Mod: triop " % "
of BitShl: triop " << "
of BitShr: triop " >> "
of BitAnd: triop " & "
of BitOr: triop " | "
of BitXor: triop " ^ "
of BitNot: binaryop " ~ "
of BoolNot: binaryop " !"
of Eq: cmpop " == "
of Le: cmpop " <= "
of Lt: cmpop " < "
of Cast: binaryop ""
of NumberConv: binaryop ""
of CheckedObjConv: binaryop ""
of ObjConv: binaryop ""
of Emit: raiseAssert "cannot interpret: Emit"
of ProcDecl: genProcDecl c, t, n, false
of ForeignProcDecl: genProcDecl c, t, n, true
of PragmaPair, PragmaId, TestOf, Yld, SetExc, TestExc:
c.add "cannot interpret: " & $t[n].kind
const
Prelude = """
/* GENERATED CODE. DO NOT EDIT. */
#ifdef __cplusplus
#define NB8 bool
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901)
// see #13798: to avoid conflicts for code emitting `#include <stdbool.h>`
#define NB8 _Bool
#else
typedef unsigned char NB8; // best effort
#endif
typedef unsigned char NC8;
typedef float NF32;
typedef double NF64;
#if defined(__BORLANDC__) || defined(_MSC_VER)
typedef signed char NI8;
typedef signed short int NI16;
typedef signed int NI32;
typedef __int64 NI64;
/* XXX: Float128? */
typedef unsigned char NU8;
typedef unsigned short int NU16;
typedef unsigned int NU32;
typedef unsigned __int64 NU64;
#elif defined(HAVE_STDINT_H)
#ifndef USE_NIM_NAMESPACE
# include <stdint.h>
#endif
typedef int8_t NI8;
typedef int16_t NI16;
typedef int32_t NI32;
typedef int64_t NI64;
typedef uint8_t NU8;
typedef uint16_t NU16;
typedef uint32_t NU32;
typedef uint64_t NU64;
#elif defined(HAVE_CSTDINT)
#ifndef USE_NIM_NAMESPACE
# include <cstdint>
#endif
typedef std::int8_t NI8;
typedef std::int16_t NI16;
typedef std::int32_t NI32;
typedef std::int64_t NI64;
typedef std::uint8_t NU8;
typedef std::uint16_t NU16;
typedef std::uint32_t NU32;
typedef std::uint64_t NU64;
#else
/* Unknown compiler/version, do our best */
#ifdef __INT8_TYPE__
typedef __INT8_TYPE__ NI8;
#else
typedef signed char NI8;
#endif
#ifdef __INT16_TYPE__
typedef __INT16_TYPE__ NI16;
#else
typedef signed short int NI16;
#endif
#ifdef __INT32_TYPE__
typedef __INT32_TYPE__ NI32;
#else
typedef signed int NI32;
#endif
#ifdef __INT64_TYPE__
typedef __INT64_TYPE__ NI64;
#else
typedef long long int NI64;
#endif
/* XXX: Float128? */
#ifdef __UINT8_TYPE__
typedef __UINT8_TYPE__ NU8;
#else
typedef unsigned char NU8;
#endif
#ifdef __UINT16_TYPE__
typedef __UINT16_TYPE__ NU16;
#else
typedef unsigned short int NU16;
#endif
#ifdef __UINT32_TYPE__
typedef __UINT32_TYPE__ NU32;
#else
typedef unsigned int NU32;
#endif
#ifdef __UINT64_TYPE__
typedef __UINT64_TYPE__ NU64;
#else
typedef unsigned long long int NU64;
#endif
#endif
#ifdef NIM_INTBITS
# if NIM_INTBITS == 64
typedef NI64 NI;
typedef NU64 NU;
# elif NIM_INTBITS == 32
typedef NI32 NI;
typedef NU32 NU;
# elif NIM_INTBITS == 16
typedef NI16 NI;
typedef NU16 NU;
# elif NIM_INTBITS == 8
typedef NI8 NI;
typedef NU8 NU;
# else
# error "invalid bit width for int"
# endif
#endif
#define NIM_STRLIT_FLAG ((NU64)(1) << ((NIM_INTBITS) - 2)) /* This has to be the same as system.strlitFlag! */
#define nimAddInt64(a, b, L) ({long long int res; if(__builtin_saddll_overflow(a, b, &res)) goto L; res})
#define nimSubInt64(a, b, L) ({long long int res; if(__builtin_ssubll_overflow(a, b, &res)) goto L; res})
#define nimMulInt64(a, b, L) ({long long int res; if(__builtin_smulll_overflow(a, b, &res)) goto L; res})
#define nimAddInt32(a, b, L) ({long int res; if(__builtin_sadd_overflow(a, b, &res)) goto L; res})
#define nimSubInt32(a, b, L) ({long int res; if(__builtin_ssub_overflow(a, b, &res)) goto L; res})
#define nimMulInt32(a, b, L) ({long int res; if(__builtin_smul_overflow(a, b, &res)) goto L; res})
#define nimCheckRange(x, a, b, L) ({if (x < a || x > b) goto L; x})
#define nimCheckIndex(x, a, L) ({if (x >= a) goto L; x})
"""
proc generateCode*(inp, outp: string) =
var c = initGeneratedCode(load(inp))
var co = TypeOrder()
traverseTypes(c.m.types, c.m.lit, co)
generateTypes(c, c.m.types, c.m.lit, co)
let typeDecls = move c.code
var i = NodePos(0)
while i.int < c.m.code.len:
gen c, c.m.code, NodePos(i)
next c.m.code, i
var f = CppFile(f: open(outp, fmWrite))
f.write "#define NIM_INTBITS " & $c.m.intbits & "\n"
f.write Prelude
writeTokenSeq f, c.includes, c
writeTokenSeq f, typeDecls, c
writeTokenSeq f, c.data, c
writeTokenSeq f, c.protos, c
writeTokenSeq f, c.code, c
f.f.close

View File

@@ -7,10 +7,10 @@
# distribution, for details about the copyright.
#
## Nir Compiler. Currently only supports a "view" command.
## Nir Compiler.
import ".." / ic / [bitabs, rodfiles]
import nirinsts, nirtypes, nirlineinfos, nirfiles #, nir2gcc
import nirinsts, nirtypes, nirlineinfos, nirfiles, cir
proc view(filename: string) =
let m = load(filename)
@@ -20,14 +20,10 @@ proc view(filename: string) =
nirtypes.toString res, m.types
echo res
proc libgcc(filename: string) =
let m = load(filename)
#gcc m, filename
import std / [syncio, parseopt]
proc writeHelp =
echo """Usage: nirc view|gcc <file.nir>"""
echo """Usage: nirc view|c <file.nir>"""
quit 0
proc main =
@@ -49,7 +45,8 @@ proc main =
case cmd
of "", "view":
view inp
of "gcc":
libgcc inp
of "c":
let outp = inp & ".c"
cir.generateCode inp, outp
main()

View File

@@ -16,6 +16,8 @@ type
man*: LineInfoManager
types*: TypeGraph
lit*: Literals
namespace*: LitId
intbits*: uint32
symnames*: SymNames
proc load*(filename: string): NirModule =
@@ -39,6 +41,10 @@ proc load*(filename: string): NirModule =
r.loadSection sideChannelSection
r.load result.man
r.loadSection namespaceSection
r.loadPrim result.namespace
r.loadPrim result.intbits
r.loadSection symnamesSection
r.load result.symnames
@@ -64,6 +70,10 @@ proc store*(m: NirModule; outp: string) =
r.storeSection sideChannelSection
r.store m.man
r.storeSection namespaceSection
r.storePrim m.namespace
r.storePrim m.intbits
r.storeSection symnamesSection
r.store m.symnames

View File

@@ -54,6 +54,7 @@ type
SelectList, # (values...)
SelectValue, # (value)
SelectRange, # (valueA..valueB)
ForeignDecl, # Can wrap SummonGlobal, SummonThreadLocal, SummonConst
SummonGlobal,
SummonThreadLocal,
Summon, # x = Summon Typed <Type ID>; x begins to live
@@ -108,6 +109,7 @@ type
TestOf,
Emit,
ProcDecl,
ForeignProcDecl,
PragmaPair
type
@@ -278,6 +280,14 @@ iterator sonsFromN*(tree: Tree; n: NodePos; toSkip = 2): NodePos =
template `[]`*(t: Tree; n: NodePos): Instr = t.nodes[n.int]
iterator sonsRest*(tree: Tree; parent, n: NodePos): NodePos =
var pos = n.int
assert tree[parent].kind > LastAtomicValue
let last = parent.int + tree[parent].rawSpan
while pos < last:
yield NodePos pos
nextChild tree, pos
proc span(tree: Tree; pos: int): int {.inline.} =
if tree.nodes[pos].kind <= LastAtomicValue: 1 else: int(tree.nodes[pos].operand)
@@ -290,19 +300,36 @@ proc copyTree*(dest: var Tree; src: Tree) =
for i in 0..<L:
dest.nodes[d+i] = src.nodes[pos+i]
proc sons2*(tree: Tree; n: NodePos): (NodePos, NodePos) =
proc sons2*(tree: Tree; n: NodePos): (NodePos, NodePos) {.inline.} =
assert(not isAtom(tree, n.int))
let a = n.int+1
let b = a + span(tree, a)
result = (NodePos a, NodePos b)
proc sons3*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos) =
proc sons3*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos) {.inline.} =
assert(not isAtom(tree, n.int))
let a = n.int+1
let b = a + span(tree, a)
let c = b + span(tree, b)
result = (NodePos a, NodePos b, NodePos c)
proc sons4*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos, NodePos) {.inline.} =
assert(not isAtom(tree, n.int))
let a = n.int+1
let b = a + span(tree, a)
let c = b + span(tree, b)
let d = c + span(tree, c)
result = (NodePos a, NodePos b, NodePos c, NodePos d)
proc sons5*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos, NodePos, NodePos) {.inline.} =
assert(not isAtom(tree, n.int))
let a = n.int+1
let b = a + span(tree, a)
let c = b + span(tree, b)
let d = c + span(tree, c)
let e = d + span(tree, d)
result = (NodePos a, NodePos b, NodePos c, NodePos d, NodePos e)
proc typeId*(ins: Instr): TypeId {.inline.} =
assert ins.kind == Typed
result = TypeId(ins.operand)
@@ -358,6 +385,9 @@ proc addSymUse*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} =
proc addSymDef*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} =
t.nodes.add Instr(x: toX(SymDef, uint32(s)), info: info)
proc addNop*(t: var Tree; info: PackedLineInfo) {.inline.} =
t.nodes.add Instr(x: toX(Nop, 0'u32), info: info)
proc addTyped*(t: var Tree; info: PackedLineInfo; typ: TypeId) {.inline.} =
assert typ.int >= 0
t.nodes.add Instr(x: toX(Typed, uint32(typ)), info: info)

View File

@@ -177,11 +177,35 @@ proc sons3(tree: TypeGraph; n: TypeId): (TypeId, TypeId, TypeId) =
let c = b + span(tree, b)
result = (TypeId a, TypeId b, TypeId c)
proc arrayName*(tree: TypeGraph; n: TypeId): TypeId {.inline.} =
assert tree[n].kind == ArrayTy
let (_, _, c) = sons3(tree, n)
result = c
proc arrayLen*(tree: TypeGraph; n: TypeId): BiggestInt =
assert tree[n].kind == ArrayTy
let (_, b) = sons2(tree, n)
result = tree.lit.numbers[LitId tree[b].operand]
proc returnType*(tree: TypeGraph; n: TypeId): (TypeId, TypeId) =
# Returns the positions of the return type + calling convention.
var pos = n.int
assert tree.nodes[pos].kind == ProcTy
let a = n.int+1
let b = a + span(tree, a)
result = (TypeId b, TypeId a) # not a typo, order is reversed
iterator params*(tree: TypeGraph; n: TypeId): TypeId =
var pos = n.int
assert tree.nodes[pos].kind == ProcTy
let last = pos + tree.nodes[pos].rawSpan
inc pos
nextChild tree, pos
nextChild tree, pos
while pos < last:
yield TypeId pos
nextChild tree, pos
proc openType*(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos =
assert kind in {APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy,
ArrayTy, LastArrayTy, ProcTy, ObjectDecl, UnionDecl,
@@ -356,10 +380,12 @@ proc toString*(dest: var string; g: TypeGraph; i: TypeId) =
dest.add "]"
of ArrayTy:
dest.add "Array["
let (elems, len) = g.sons2(i)
let (elems, len, name) = g.sons3(i)
toString(dest, g, elems)
dest.add ", "
toString(dest, g, len)
dest.add ", "
toString(dest, g, name)
dest.add "]"
of LastArrayTy:
# array of unspecified size as a last field inside an object
@@ -421,6 +447,12 @@ iterator allTypes*(g: TypeGraph; start = 0): TypeId =
yield TypeId i
nextChild g, i
iterator allTypesIncludingInner*(g: TypeGraph; start = 0): TypeId =
var i = start
while i < g.len:
yield TypeId i
inc i
proc `$`(g: TypeGraph): string =
result = ""
toString(result, g)
@@ -431,6 +463,7 @@ when isMainModule:
let a = g.openType ArrayTy
g.addBuiltinType Int8Id
g.addArrayLen 5
g.addName "SomeArray"
let finalArrayType = finishType(g, a)
let obj = g.openType ObjectDecl

View File

@@ -421,7 +421,7 @@ proc preprocess(c: var Preprocessing; bc: var Bytecode; t: Tree; n: NodePos; fla
for ch in sons(t, n): preprocess(c, bc, t, ch, {WantAddr})
case t[n].kind
of Nop:
of Nop, ForeignDecl, ForeignProcDecl:
discard "don't use Nop"
of ImmediateVal:
bc.add info, ImmediateValM, t[n].rawOperand

View File

@@ -14,6 +14,7 @@ import nirtypes
type
TypesCon* = object
processed: Table[ItemId, TypeId]
processedByName: Table[string, TypeId]
recursionCheck: HashSet[ItemId]
conf: ConfigRef
stringType: TypeId
@@ -30,6 +31,13 @@ template cached(c: var TypesCon; t: PType; body: untyped) =
body
c.processed[t.itemId] = result
template cachedByName(c: var TypesCon; t: PType; body: untyped) =
let key = mangle(c, t)
result = c.processedByName.getOrDefault(key)
if result.int == 0:
body
c.processedByName[key] = result
proc typeToIr*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId
proc collectFieldTypes(c: var TypesCon; g: var TypeGraph; n: PNode; dest: var Table[ItemId, TypeId]) =
@@ -273,10 +281,15 @@ proc seqPayloadType(c: var TypesCon; g: var TypeGraph; t: PType): (string, TypeI
let f = g.openType FieldDecl
let arr = g.openType LastArrayTy
g.addType elementType
result[1] = finishType(g, arr) # LastArrayTy
# DO NOT USE `finishType` here as it is an inner type. This is subtle and we
# probably need an even better API here.
sealType(g, arr)
result[1] = TypeId(arr)
g.addOffset c.conf.target.ptrSize
g.addName "data"
sealType(g, f) # FieldDecl
sealType(g, p)
proc seqPayloadPtrType*(c: var TypesCon; g: var TypeGraph; t: PType): (TypeId, TypeId) =
@@ -413,6 +426,7 @@ proc typeToIr*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
let a = openType(g, ArrayTy)
g.addType(elemType)
g.addArrayLen n
g.addName mangle(c, t)
result = finishType(g, a)
of tyPtr, tyRef:
cached(c, t):
@@ -451,6 +465,7 @@ proc typeToIr*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
let a = openType(g, ArrayTy)
g.addType(UInt8Id)
g.addArrayLen s
g.addName mangle(c, t)
result = finishType(g, a)
of tyPointer, tyNil:
# tyNil can happen for code like: `const CRAP = nil` which we have in posix.nim
@@ -467,7 +482,7 @@ proc typeToIr*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
else:
result = objectHeaderToIr(c, g, t)
of tyTuple:
cached(c, t):
cachedByName(c, t):
result = tupleToIr(c, g, t)
of tyProc:
cached(c, t):
@@ -485,7 +500,7 @@ proc typeToIr*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
else:
result = c.stringType
of tySequence:
cached(c, t):
cachedByName(c, t):
result = seqToIr(c, g, t)
of tyCstring:
cached(c, t):