mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
NIR: C codegen, WIP (#22903)
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -98,6 +98,7 @@ type
|
||||
backendFlagsSection
|
||||
aliveSymsSection # beware, this is stored in a `.alivesyms` file.
|
||||
sideChannelSection
|
||||
namespaceSection
|
||||
symnamesSection
|
||||
|
||||
RodFileError* = enum
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user