Files
Nim/compiler/nir/ast2ir.nim
2023-11-06 18:33:28 +01:00

2646 lines
89 KiB
Nim

#
#
# The Nim Compiler
# (c) Copyright 2023 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import std / [assertions, tables, sets]
import ".." / [ast, astalgo, types, options, lineinfos, msgs, magicsys,
modulegraphs, renderer, transf, bitsets, trees, nimsets,
expanddefaults]
from ".." / lowerings import lowerSwap, lowerTupleUnpacking
from ".." / pathutils import customPath
import .. / ic / bitabs
import nirtypes, nirinsts, nirlineinfos, nirslots, types2ir, nirfiles
when defined(nimCompilerStacktraceHints):
import std/stackframes
type
ModuleCon* = ref object
nirm*: ref NirModule
types: TypesCon
module*: PSym
graph*: ModuleGraph
nativeIntId, nativeUIntId: TypeId
strPayloadId: (TypeId, TypeId)
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]
symIdCounter: int32
ProcCon* = object
config*: ConfigRef
lit: Literals
lastFileKey: FileIndex
lastFileVal: LitId
labelGen: int
exitLabel: LabelId
#code*: Tree
blocks: seq[(PSym, LabelId)]
sm: SlotManager
idgen: IdGenerator
m: ModuleCon
prc: PSym
options: TOptions
template code(c: ProcCon): Tree = c.m.nirm.code
proc initModuleCon*(graph: ModuleGraph; config: ConfigRef; idgen: IdGenerator; module: PSym;
nirm: ref NirModule): ModuleCon =
#let lit = Literals() # must be shared
result = ModuleCon(graph: graph, types: initTypesCon(config), nirm: nirm,
idgen: idgen, module: module)
case config.target.intSize
of 2:
result.nativeIntId = Int16Id
result.nativeUIntId = UInt16Id
of 4:
result.nativeIntId = Int32Id
result.nativeUIntId = UInt16Id
else:
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,
lit: m.nirm.lit, idgen: m.idgen,
options: if prc != nil: prc.options
else: config.options)
result.exitLabel = newLabel(result.labelGen)
proc toLineInfo(c: var ProcCon; i: TLineInfo): PackedLineInfo =
var val: LitId
if c.lastFileKey == i.fileIndex:
val = c.lastFileVal
else:
val = c.lit.strings.getOrIncl(toFullPath(c.config, i.fileIndex))
# remember the entry:
c.lastFileKey = i.fileIndex
c.lastFileVal = val
result = pack(c.m.nirm.man, val, int32 i.line, int32 i.col)
proc bestEffort(c: ProcCon): TLineInfo =
if c.prc != nil:
c.prc.info
else:
c.m.module.info
proc popBlock(c: var ProcCon; oldLen: int) =
c.blocks.setLen(oldLen)
template withBlock(labl: PSym; info: PackedLineInfo; asmLabl: LabelId; body: untyped) {.dirty.} =
var oldLen {.gensym.} = c.blocks.len
c.blocks.add (labl, asmLabl)
body
popBlock(c, oldLen)
type
GenFlag = enum
gfAddrOf # load the address of the expression
gfToOutParam # the expression is passed to an `out` parameter
GenFlags = set[GenFlag]
proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {})
proc genScope(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) =
openScope c.sm
gen c, n, d, flags
closeScope c.sm
proc freeTemp(c: var ProcCon; tmp: Value) =
let s = extractTemp(tmp)
if s != SymId(-1):
freeTemp(c.sm, s)
proc freeTemps(c: var ProcCon; tmps: openArray[Value]) =
for t in tmps: freeTemp(c, t)
proc typeToIr(m: ModuleCon; t: PType): TypeId =
typeToIr(m.types, m.nirm.types, t)
proc allocTemp(c: var ProcCon; t: TypeId): SymId =
if c.m.noModularity:
result = allocTemp(c.sm, t, c.m.symIdCounter)
else:
result = allocTemp(c.sm, t, c.idgen.symId)
const
ListSymId = -1
proc toSymId(c: var ProcCon; s: PSym): SymId =
if c.m.noModularity:
result = c.m.toSymId.getOrDefault(s.itemId, SymId(-1))
if result.int < 0:
inc c.m.symIdCounter
result = SymId(c.m.symIdCounter)
c.m.toSymId[s.itemId] = result
when ListSymId != -1:
if result.int == ListSymId or s.name.s == "echoBinSafe":
echo result.int, " is ", s.name.s, " ", c.m.graph.config $ s.info, " ", s.flags
writeStackTrace()
else:
result = SymId(s.itemId.item)
proc getTemp(c: var ProcCon; n: PNode): Value =
let info = toLineInfo(c, n.info)
let t = typeToIr(c.m, n.typ)
let tmp = allocTemp(c, t)
c.code.addSummon info, tmp, t
result = localToValue(info, tmp)
proc getTemp(c: var ProcCon; t: TypeId; info: PackedLineInfo): Value =
let tmp = allocTemp(c, t)
c.code.addSummon info, tmp, t
result = localToValue(info, tmp)
proc gen(c: var ProcCon; n: PNode; flags: GenFlags = {}) =
var tmp = default(Value)
gen(c, n, tmp, flags)
freeTemp c, tmp
proc genScope(c: var ProcCon; n: PNode; flags: GenFlags = {}) =
openScope c.sm
gen c, n, flags
closeScope c.sm
proc genx(c: var ProcCon; n: PNode; flags: GenFlags = {}): Value =
result = default(Value)
gen(c, n, result, flags)
assert Tree(result).len > 0, $n
proc clearDest(c: var ProcCon; n: PNode; d: var Value) {.inline.} =
when false:
if n.typ.isNil or n.typ.kind == tyVoid:
let s = extractTemp(d)
if s != SymId(-1):
freeLoc(c.sm, s)
proc isNotOpr(n: PNode): bool =
n.kind in nkCallKinds and n[0].kind == nkSym and n[0].sym.magic == mNot
proc jmpBack(c: var ProcCon; n: PNode; lab: LabelId) =
c.code.gotoLabel toLineInfo(c, n.info), GotoLoop, lab
type
JmpKind = enum opcFJmp, opcTJmp
proc xjmp(c: var ProcCon; n: PNode; jk: JmpKind; v: Value): LabelId =
result = newLabel(c.labelGen)
let info = toLineInfo(c, n.info)
buildTyped c.code, info, Select, Bool8Id:
c.code.copyTree Tree(v)
build c.code, info, SelectPair:
build c.code, info, SelectValue:
c.code.boolVal(c.lit.numbers, info, jk == opcTJmp)
c.code.gotoLabel info, Goto, result
proc patch(c: var ProcCon; n: PNode; L: LabelId) =
addLabel c.code, toLineInfo(c, n.info), Label, L
proc genWhile(c: var ProcCon; n: PNode) =
# lab1:
# cond, tmp
# fjmp tmp, lab2
# body
# jmp lab1
# lab2:
let info = toLineInfo(c, n.info)
let lab1 = c.code.addNewLabel(c.labelGen, info, LoopLabel)
withBlock(nil, info, lab1):
if isTrue(n[0]):
c.gen(n[1])
c.jmpBack(n, lab1)
elif isNotOpr(n[0]):
var tmp = c.genx(n[0][1])
let lab2 = c.xjmp(n, opcTJmp, tmp)
c.freeTemp(tmp)
c.gen(n[1])
c.jmpBack(n, lab1)
c.patch(n, lab2)
else:
var tmp = c.genx(n[0])
let lab2 = c.xjmp(n, opcFJmp, tmp)
c.freeTemp(tmp)
c.gen(n[1])
c.jmpBack(n, lab1)
c.patch(n, lab2)
proc genBlock(c: var ProcCon; n: PNode; d: var Value) =
openScope c.sm
let info = toLineInfo(c, n.info)
let lab1 = newLabel(c.labelGen)
withBlock(n[0].sym, info, lab1):
c.gen(n[1], d)
c.code.addLabel(info, Label, lab1)
closeScope c.sm
c.clearDest(n, d)
proc jumpTo(c: var ProcCon; n: PNode; L: LabelId) =
c.code.addLabel(toLineInfo(c, n.info), Goto, L)
proc genBreak(c: var ProcCon; n: PNode) =
if n[0].kind == nkSym:
for i in countdown(c.blocks.len-1, 0):
if c.blocks[i][0] == n[0].sym:
c.jumpTo n, c.blocks[i][1]
return
localError(c.config, n.info, "NIR problem: cannot find 'break' target")
else:
c.jumpTo n, c.blocks[c.blocks.high][1]
proc genIf(c: var ProcCon; n: PNode; d: var Value) =
# if (!expr1) goto lab1;
# thenPart
# goto LEnd
# lab1:
# if (!expr2) goto lab2;
# thenPart2
# goto LEnd
# lab2:
# elsePart
# Lend:
if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n)
var ending = newLabel(c.labelGen)
for i in 0..<n.len:
var it = n[i]
if it.len == 2:
let info = toLineInfo(c, it[0].info)
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])
else:
c.genScope(it[1], d) # then part
if i < n.len-1:
c.jumpTo it[1], ending
c.patch(it, elsePos)
else:
c.clearDest(n, d)
if isEmptyType(it[0].typ): # maybe noreturn call, don't touch `d`
c.genScope(it[0])
else:
c.genScope(it[0], d)
c.patch(n, ending)
c.clearDest(n, d)
proc tempToDest(c: var ProcCon; n: PNode; d: var Value; tmp: Value) =
if isEmpty(d):
d = tmp
else:
let info = toLineInfo(c, n.info)
buildTyped c.code, info, Asgn, typeToIr(c.m, n.typ):
c.code.copyTree d
c.code.copyTree tmp
freeTemp(c, tmp)
proc genAndOr(c: var ProcCon; n: PNode; opc: JmpKind; d: var Value) =
# asgn d, a
# tjmp|fjmp lab1
# asgn d, b
# lab1:
var tmp = getTemp(c, n)
c.gen(n[1], tmp)
let lab1 = c.xjmp(n, opc, tmp)
c.gen(n[2], tmp)
c.patch(n, lab1)
tempToDest c, n, d, tmp
proc unused(c: var ProcCon; n: PNode; x: Value) {.inline.} =
if hasValue(x):
#debug(n)
localError(c.config, n.info, "not unused")
proc caseValue(c: var ProcCon; n: PNode) =
let info = toLineInfo(c, n.info)
build c.code, info, SelectValue:
let x = genx(c, n)
c.code.copyTree x
freeTemp(c, x)
proc caseRange(c: var ProcCon; n: PNode) =
let info = toLineInfo(c, n.info)
build c.code, info, SelectRange:
let x = genx(c, n[0])
let y = genx(c, n[1])
c.code.copyTree x
c.code.copyTree y
freeTemp(c, y)
freeTemp(c, x)
proc addUseCodegenProc(c: var ProcCon; dest: var Tree; name: string; info: PackedLineInfo) =
let cp = getCompilerProc(c.m.graph, name)
let theProc = c.genx newSymNode(cp)
copyTree c.code, theProc
template buildCond(useNegation: bool; cond: typed; body: untyped) =
let lab = newLabel(c.labelGen)
buildTyped c.code, info, Select, Bool8Id:
c.code.copyTree cond
build c.code, info, SelectPair:
build c.code, info, SelectValue:
c.code.boolVal(c.lit.numbers, info, useNegation)
c.code.gotoLabel info, Goto, lab
body
c.code.addLabel info, Label, lab
template buildIf(cond: typed; body: untyped) =
buildCond false, cond, body
template buildIfNot(cond: typed; body: untyped) =
buildCond true, cond, body
template buildIfThenElse(cond: typed; then, otherwise: untyped) =
let lelse = newLabel(c.labelGen)
let lend = newLabel(c.labelGen)
buildTyped c.code, info, Select, Bool8Id:
c.code.copyTree cond
build c.code, info, SelectPair:
build c.code, info, SelectValue:
c.code.boolVal(c.lit.numbers, info, false)
c.code.gotoLabel info, Goto, lelse
then()
c.code.gotoLabel info, Goto, lend
c.code.addLabel info, Label, lelse
otherwise()
c.code.addLabel info, Label, lend
include stringcases
proc genCase(c: var ProcCon; n: PNode; d: var Value) =
if not isEmptyType(n.typ):
if isEmpty(d): d = getTemp(c, n)
else:
unused(c, n, d)
if n[0].typ.skipTypes(abstractInst).kind == tyString:
genStringCase(c, n, d)
return
var sections = newSeqOfCap[LabelId](n.len-1)
let ending = newLabel(c.labelGen)
let info = toLineInfo(c, n.info)
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)
c.code.addLabel itinfo, Label, sections[i-1]
c.gen it.lastSon
if i != n.len-1:
c.code.addLabel itinfo, Goto, ending
c.code.addLabel info, Label, ending
proc rawCall(c: var ProcCon; info: PackedLineInfo; opc: Opcode; t: TypeId; args: var openArray[Value]) =
buildTyped c.code, info, opc, t:
if opc in {CheckedCall, CheckedIndirectCall}:
c.code.addLabel info, CheckedGoto, c.exitLabel
for a in mitems(args):
c.code.copyTree a
freeTemp c, a
proc canRaiseDisp(c: ProcCon; n: PNode): bool =
# we assume things like sysFatal cannot raise themselves
if n.kind == nkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}:
result = false
elif optPanics in c.config.globalOptions or
(n.kind == nkSym and sfSystemModule in getModule(n.sym).flags and
sfSystemRaisesDefect notin n.sym.flags):
# we know we can be strict:
result = canRaise(n)
else:
# we have to be *very* conservative:
result = canRaiseConservative(n)
proc genCall(c: var ProcCon; n: PNode; d: var Value) =
let canRaise = canRaiseDisp(c, n[0])
let opc = if n[0].kind == nkSym and n[0].sym.kind in routineKinds:
(if canRaise: CheckedCall else: Call)
else:
(if canRaise: CheckedIndirectCall else: IndirectCall)
let info = toLineInfo(c, n.info)
# In the IR we cannot nest calls. Thus we use two passes:
var args: seq[Value] = @[]
var t = n[0].typ
if t != nil: t = t.skipTypes(abstractInst)
args.add genx(c, n[0])
for i in 1..<n.len:
if t != nil and i < t.len:
if isCompileTimeOnly(t[i]): discard
elif isOutParam(t[i]): args.add genx(c, n[i], {gfToOutParam})
else: args.add genx(c, n[i])
else:
args.add genx(c, n[i])
let tb = typeToIr(c.m, n.typ)
if not isEmptyType(n.typ):
if isEmpty(d): d = getTemp(c, n)
# XXX Handle problematic aliasing here: `a = f_canRaise(a)`.
buildTyped c.code, info, Asgn, tb:
c.code.copyTree d
rawCall c, info, opc, tb, args
else:
rawCall c, info, opc, tb, args
freeTemps c, args
proc genRaise(c: var ProcCon; n: PNode) =
let info = toLineInfo(c, n.info)
let tb = typeToIr(c.m, n[0].typ)
let d = genx(c, n[0])
buildTyped c.code, info, SetExc, tb:
c.code.copyTree d
c.freeTemp(d)
c.code.addLabel info, Goto, c.exitLabel
proc genReturn(c: var ProcCon; n: PNode) =
if n[0].kind != nkEmpty:
gen(c, n[0])
# XXX Block leave actions?
let info = toLineInfo(c, n.info)
c.code.addLabel info, Goto, c.exitLabel
proc genTry(c: var ProcCon; n: PNode; d: var Value) =
if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n)
var endings: seq[LabelId] = @[]
let ehPos = newLabel(c.labelGen)
let oldExitLab = c.exitLabel
c.exitLabel = ehPos
if isEmptyType(n[0].typ): # maybe noreturn call, don't touch `d`
c.gen(n[0])
else:
c.gen(n[0], d)
c.clearDest(n, d)
# Add a jump past the exception handling code
let jumpToFinally = newLabel(c.labelGen)
c.jumpTo n, jumpToFinally
# This signals where the body ends and where the exception handling begins
c.patch(n, ehPos)
c.exitLabel = oldExitLab
for i in 1..<n.len:
let it = n[i]
if it.kind != nkFinally:
# first opcExcept contains the end label of the 'except' block:
let endExcept = newLabel(c.labelGen)
for j in 0..<it.len - 1:
assert(it[j].kind == nkType)
let typ = it[j].typ.skipTypes(abstractPtrs-{tyTypeDesc})
let itinfo = toLineInfo(c, it[j].info)
build c.code, itinfo, TestExc:
c.code.addTyped itinfo, typeToIr(c.m, typ)
if it.len == 1:
let itinfo = toLineInfo(c, it.info)
build c.code, itinfo, TestExc:
c.code.addTyped itinfo, VoidId
let body = it.lastSon
if isEmptyType(body.typ): # maybe noreturn call, don't touch `d`
c.gen(body)
else:
c.gen(body, d)
c.clearDest(n, d)
if i < n.len:
endings.add newLabel(c.labelGen)
c.patch(it, endExcept)
let fin = lastSon(n)
# we always generate an 'opcFinally' as that pops the safepoint
# from the stack if no exception is raised in the body.
c.patch(fin, jumpToFinally)
#c.gABx(fin, opcFinally, 0, 0)
for endPos in endings: c.patch(n, endPos)
if fin.kind == nkFinally:
c.gen(fin[0])
c.clearDest(n, d)
#c.gABx(fin, opcFinallyEnd, 0, 0)
template isGlobal(s: PSym): bool = sfGlobal in s.flags and s.kind != skForVar
proc isGlobal(n: PNode): bool = n.kind == nkSym and isGlobal(n.sym)
proc genField(c: var ProcCon; n: PNode; d: var Value) =
var pos: int
if n.kind != nkSym or n.sym.kind != skField:
localError(c.config, n.info, "no field symbol")
pos = 0
else:
pos = n.sym.position
d.addImmediateVal toLineInfo(c, n.info), pos
proc genIndex(c: var ProcCon; n: PNode; arr: PType; d: var Value) =
let info = toLineInfo(c, n.info)
if arr.skipTypes(abstractInst).kind == tyArray and
(let offset = firstOrd(c.config, arr); offset != Zero):
let x = c.genx(n)
buildTyped d, info, Sub, c.m.nativeIntId:
copyTree d.Tree, x
d.addImmediateVal toLineInfo(c, n.info), toInt(offset)
else:
c.gen(n, d)
if optBoundsCheck in c.options:
let idx = move d
build d, info, CheckedIndex:
d.Tree.addLabel info, CheckedGoto, c.exitLabel
copyTree d.Tree, idx
let x = toInt64 lengthOrd(c.config, arr)
d.addIntVal c.lit.numbers, info, c.m.nativeIntId, x
proc rawGenNew(c: var ProcCon; d: Value; refType: PType; ninfo: TLineInfo; needsInit: bool) =
assert refType.kind == tyRef
let baseType = refType.lastSon
let info = toLineInfo(c, ninfo)
let codegenProc = magicsys.getCompilerProc(c.m.graph,
if needsInit: "nimNewObj" else: "nimNewObjUninit")
let refTypeIr = typeToIr(c.m, refType)
buildTyped c.code, info, Asgn, refTypeIr:
copyTree c.code, d
buildTyped c.code, info, Cast, refTypeIr:
buildTyped c.code, info, Call, VoidPtrId:
let theProc = c.genx newSymNode(codegenProc, ninfo)
copyTree c.code, theProc
c.code.addImmediateVal info, int(getSize(c.config, baseType))
c.code.addImmediateVal info, int(getAlign(c.config, baseType))
proc genNew(c: var ProcCon; n: PNode; needsInit: bool) =
# If in doubt, always follow the blueprint of the C code generator for `mm:orc`.
let refType = n[1].typ.skipTypes(abstractInstOwned)
let d = genx(c, n[1])
rawGenNew c, d, refType, n.info, needsInit
freeTemp c, d
proc genNewSeqOfCap(c: var ProcCon; n: PNode; d: var Value) =
let info = toLineInfo(c, n.info)
let seqtype = skipTypes(n.typ, abstractVarRange)
let baseType = seqtype.lastSon
var a = c.genx(n[1])
if isEmpty(d): d = getTemp(c, n)
# $1.len = 0
buildTyped c.code, info, Asgn, c.m.nativeIntId:
buildTyped c.code, info, FieldAt, typeToIr(c.m, seqtype):
copyTree c.code, d
c.code.addImmediateVal info, 0
c.code.addImmediateVal info, 0
# $1.p = ($4*) #newSeqPayloadUninit($2, sizeof($3), NIM_ALIGNOF($3))
let payloadPtr = seqPayloadPtrType(c.m.types, c.m.nirm.types, seqtype)[0]
buildTyped c.code, info, Asgn, payloadPtr:
# $1.p
buildTyped c.code, info, FieldAt, typeToIr(c.m, seqtype):
copyTree c.code, d
c.code.addImmediateVal info, 1
# ($4*) #newSeqPayloadUninit($2, sizeof($3), NIM_ALIGNOF($3))
buildTyped c.code, info, Cast, payloadPtr:
buildTyped c.code, info, Call, VoidPtrId:
let codegenProc = magicsys.getCompilerProc(c.m.graph, "newSeqPayloadUninit")
let theProc = c.genx newSymNode(codegenProc, n.info)
copyTree c.code, theProc
copyTree c.code, a
c.code.addImmediateVal info, int(getSize(c.config, baseType))
c.code.addImmediateVal info, int(getAlign(c.config, baseType))
freeTemp c, a
proc genNewSeqPayload(c: var ProcCon; info: PackedLineInfo; d, b: Value; seqtype: PType) =
let baseType = seqtype.lastSon
# $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3))
let payloadPtr = seqPayloadPtrType(c.m.types, c.m.nirm.types, seqtype)[0]
# $1.len = $2
buildTyped c.code, info, Asgn, c.m.nativeIntId:
buildTyped c.code, info, FieldAt, typeToIr(c.m, seqtype):
copyTree c.code, d
c.code.addImmediateVal info, 0
copyTree c.code, b
buildTyped c.code, info, Asgn, payloadPtr:
# $1.p
buildTyped c.code, info, FieldAt, typeToIr(c.m, seqtype):
copyTree c.code, d
c.code.addImmediateVal info, 1
# ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3))
buildTyped c.code, info, Cast, payloadPtr:
buildTyped c.code, info, Call, VoidPtrId:
let codegenProc = magicsys.getCompilerProc(c.m.graph, "newSeqPayload")
let theProc = c.genx newSymNode(codegenProc)
copyTree c.code, theProc
copyTree c.code, b
c.code.addImmediateVal info, int(getSize(c.config, baseType))
c.code.addImmediateVal info, int(getAlign(c.config, baseType))
proc genNewSeq(c: var ProcCon; n: PNode) =
let info = toLineInfo(c, n.info)
let seqtype = skipTypes(n[1].typ, abstractVarRange)
var d = c.genx(n[1])
var b = c.genx(n[2])
genNewSeqPayload(c, info, d, b, seqtype)
freeTemp c, b
freeTemp c, d
template intoDest*(d: var Value; info: PackedLineInfo; typ: TypeId; body: untyped) =
if typ == VoidId:
body(c.code)
elif isEmpty(d):
body(Tree(d))
else:
buildTyped c.code, info, Asgn, typ:
copyTree c.code, d
body(c.code)
template valueIntoDest(c: var ProcCon; info: PackedLineInfo; d: var Value; typ: PType; body: untyped) =
if isEmpty(d):
body(Tree d)
else:
buildTyped c.code, info, Asgn, typeToIr(c.m, typ):
copyTree c.code, d
body(c.code)
template constrIntoDest(c: var ProcCon; info: PackedLineInfo; d: var Value; typ: PType; body: untyped) =
var tmp = default(Value)
body(Tree tmp)
if isEmpty(d):
d = tmp
else:
buildTyped c.code, info, Asgn, typeToIr(c.m, typ):
copyTree c.code, d
copyTree c.code, tmp
proc genBinaryOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) =
let info = toLineInfo(c, n.info)
let tmp = c.genx(n[1])
let tmp2 = c.genx(n[2])
let t = typeToIr(c.m, n.typ)
template body(target) =
buildTyped target, info, opc, t:
if opc in {CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, CheckedMod}:
target.addLabel info, CheckedGoto, c.exitLabel
copyTree target, tmp
copyTree target, tmp2
intoDest d, info, t, body
c.freeTemp(tmp)
c.freeTemp(tmp2)
proc genCmpOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) =
let info = toLineInfo(c, n.info)
let tmp = c.genx(n[1])
let tmp2 = c.genx(n[2])
let t = typeToIr(c.m, n[1].typ)
template body(target) =
buildTyped target, info, opc, t:
copyTree target, tmp
copyTree target, tmp2
intoDest d, info, Bool8Id, body
c.freeTemp(tmp)
c.freeTemp(tmp2)
proc genUnaryOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) =
let info = toLineInfo(c, n.info)
let tmp = c.genx(n[1])
let t = typeToIr(c.m, n.typ)
template body(target) =
buildTyped target, info, opc, t:
copyTree target, tmp
intoDest d, info, t, body
c.freeTemp(tmp)
proc genIncDec(c: var ProcCon; n: PNode; opc: Opcode) =
let info = toLineInfo(c, n.info)
let t = typeToIr(c.m, skipTypes(n[1].typ, abstractVar))
let d = c.genx(n[1])
let tmp = c.genx(n[2])
# we produce code like: i = i + 1
buildTyped c.code, info, Asgn, t:
copyTree c.code, d
buildTyped c.code, info, opc, t:
if opc in {CheckedAdd, CheckedSub}:
c.code.addLabel info, CheckedGoto, c.exitLabel
copyTree c.code, d
copyTree c.code, tmp
c.freeTemp(tmp)
#c.genNarrow(n[1], d)
c.freeTemp(d)
proc genArrayLen(c: var ProcCon; n: PNode; d: var Value) =
#echo c.m.graph.config $ n.info, " ", n
let info = toLineInfo(c, n.info)
var a = n[1]
#if a.kind == nkHiddenAddr: a = a[0]
var typ = skipTypes(a.typ, abstractVar + tyUserTypeClasses)
case typ.kind
of tyOpenArray, tyVarargs:
let xa = c.genx(a)
template body(target) =
buildTyped target, info, FieldAt, typeToIr(c.m, typ):
copyTree target, xa
target.addImmediateVal info, 1 # (p, len)-pair so len is at index 1
intoDest d, info, c.m.nativeIntId, body
of tyCstring:
let xa = c.genx(a)
if isEmpty(d): d = getTemp(c, n)
buildTyped c.code, info, Call, c.m.nativeIntId:
let codegenProc = magicsys.getCompilerProc(c.m.graph, "nimCStrLen")
assert codegenProc != nil
let theProc = c.genx newSymNode(codegenProc, n.info)
copyTree c.code, theProc
copyTree c.code, xa
of tyString, tySequence:
let xa = c.genx(a)
if typ.kind == tySequence:
# we go through a temporary here because people write bullshit code.
if isEmpty(d): d = getTemp(c, n)
template body(target) =
buildTyped target, info, FieldAt, typeToIr(c.m, typ):
copyTree target, xa
target.addImmediateVal info, 0 # (len, p)-pair so len is at index 0
intoDest d, info, c.m.nativeIntId, body
of tyArray:
template body(target) =
target.addIntVal(c.lit.numbers, info, c.m.nativeIntId, toInt lengthOrd(c.config, typ))
intoDest d, info, c.m.nativeIntId, body
else: internalError(c.config, n.info, "genArrayLen()")
proc genUnaryMinus(c: var ProcCon; n: PNode; d: var Value) =
let info = toLineInfo(c, n.info)
let tmp = c.genx(n[1])
let t = typeToIr(c.m, n.typ)
template body(target) =
buildTyped target, info, Sub, t:
# Little hack: This works because we know that `0.0` is all 0 bits:
target.addIntVal(c.lit.numbers, info, t, 0)
copyTree target, tmp
intoDest d, info, t, body
c.freeTemp(tmp)
proc genHigh(c: var ProcCon; n: PNode; d: var Value) =
let info = toLineInfo(c, n.info)
let t = typeToIr(c.m, n.typ)
var x = default(Value)
genArrayLen(c, n, x)
template body(target) =
buildTyped target, info, Sub, t:
copyTree target, x
target.addIntVal(c.lit.numbers, info, t, 1)
intoDest d, info, t, body
c.freeTemp x
proc genBinaryCp(c: var ProcCon; n: PNode; d: var Value; compilerProc: string) =
let info = toLineInfo(c, n.info)
let xa = c.genx(n[1])
let xb = c.genx(n[2])
if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n)
let t = typeToIr(c.m, n.typ)
template body(target) =
buildTyped target, info, Call, t:
let codegenProc = magicsys.getCompilerProc(c.m.graph, compilerProc)
#assert codegenProc != nil, $n & " " & (c.m.graph.config $ n.info)
let theProc = c.genx newSymNode(codegenProc, n.info)
copyTree target, theProc
copyTree target, xa
copyTree target, xb
intoDest d, info, t, body
c.freeTemp xb
c.freeTemp xa
proc genUnaryCp(c: var ProcCon; n: PNode; d: var Value; compilerProc: string; argAt = 1) =
let info = toLineInfo(c, n.info)
let xa = c.genx(n[argAt])
if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n)
let t = typeToIr(c.m, n.typ)
template body(target) =
buildTyped target, info, Call, t:
let codegenProc = magicsys.getCompilerProc(c.m.graph, compilerProc)
let theProc = c.genx newSymNode(codegenProc, n.info)
copyTree target, theProc
copyTree target, xa
intoDest d, info, t, body
c.freeTemp xa
proc genEnumToStr(c: var ProcCon; n: PNode; d: var Value) =
let t = n[1].typ.skipTypes(abstractInst+{tyRange})
let toStrProc = getToStringProc(c.m.graph, t)
# XXX need to modify this logic for IC.
var nb = copyTree(n)
nb[0] = newSymNode(toStrProc)
gen(c, nb, d)
proc genOf(c: var ProcCon; n: PNode; d: var Value) =
genUnaryOp c, n, d, TestOf
template sizeOfLikeMsg(name): string =
"'" & name & "' requires '.importc' types to be '.completeStruct'"
proc genIsNil(c: var ProcCon; n: PNode; d: var Value) =
let info = toLineInfo(c, n.info)
let tmp = c.genx(n[1])
let t = typeToIr(c.m, n[1].typ)
template body(target) =
buildTyped target, info, Eq, t:
copyTree target, tmp
addNilVal target, info, t
intoDest d, info, Bool8Id, body
c.freeTemp(tmp)
proc fewCmps(conf: ConfigRef; s: PNode): bool =
# this function estimates whether it is better to emit code
# for constructing the set or generating a bunch of comparisons directly
if s.kind != nkCurly:
result = false
elif (getSize(conf, s.typ) <= conf.target.intSize) and (nfAllConst in s.flags):
result = false # it is better to emit the set generation code
elif elemType(s.typ).kind in {tyInt, tyInt16..tyInt64}:
result = true # better not emit the set if int is basetype!
else:
result = s.len <= 8 # 8 seems to be a good value
proc genInBitset(c: var ProcCon; n: PNode; d: var Value) =
let info = toLineInfo(c, n.info)
let a = c.genx(n[1])
let b = c.genx(n[2])
let t = bitsetBasetype(c.m.types, c.m.nirm.types, n[1].typ)
let setType = typeToIr(c.m, n[1].typ)
let mask =
case t
of UInt8Id: 7
of UInt16Id: 15
of UInt32Id: 31
else: 63
let expansion = if t == UInt64Id: UInt64Id else: c.m.nativeUIntId
# "(($1 &(1U<<((NU)($2)&7U)))!=0)" - or -
# "(($1[(NU)($2)>>3] &(1U<<((NU)($2)&7U)))!=0)"
template body(target) =
buildTyped target, info, BoolNot, Bool8Id:
buildTyped target, info, Eq, t:
buildTyped target, info, BitAnd, t:
if c.m.nirm.types[setType].kind != ArrayTy:
copyTree target, a
else:
buildTyped target, info, ArrayAt, setType:
copyTree target, a
buildTyped target, info, BitShr, t:
buildTyped target, info, Cast, expansion:
copyTree target, b
addIntVal target, c.lit.numbers, info, expansion, 3
buildTyped target, info, BitShl, t:
addIntVal target, c.lit.numbers, info, t, 1
buildTyped target, info, BitAnd, t:
buildTyped target, info, Cast, expansion:
copyTree target, b
addIntVal target, c.lit.numbers, info, expansion, mask
addIntVal target, c.lit.numbers, info, t, 0
intoDest d, info, t, body
c.freeTemp(b)
c.freeTemp(a)
proc genInSet(c: var ProcCon; n: PNode; d: var Value) =
let g {.cursor.} = c.m.graph
if n[1].kind == nkCurly and fewCmps(g.config, n[1]):
# a set constructor but not a constant set:
# do not emit the set, but generate a bunch of comparisons; and if we do
# so, we skip the unnecessary range check: This is a semantical extension
# that code now relies on. :-/ XXX
let elem = if n[2].kind in {nkChckRange, nkChckRange64}: n[2][0]
else: n[2]
let curly = n[1]
var ex: PNode = nil
for it in curly:
var test: PNode
if it.kind == nkRange:
test = newTree(nkCall, g.operators.opAnd.newSymNode,
newTree(nkCall, g.operators.opLe.newSymNode, it[0], elem), # a <= elem
newTree(nkCall, g.operators.opLe.newSymNode, elem, it[1])
)
else:
test = newTree(nkCall, g.operators.opEq.newSymNode, elem, it)
test.typ = getSysType(g, it.info, tyBool)
if ex == nil: ex = test
else: ex = newTree(nkCall, g.operators.opOr.newSymNode, ex, test)
if ex == nil:
let info = toLineInfo(c, n.info)
template body(target) =
boolVal target, c.lit.numbers, info, false
intoDest d, info, Bool8Id, body
else:
gen c, ex, d
else:
genInBitset c, n, d
proc genCard(c: var ProcCon; n: PNode; d: var Value) =
let info = toLineInfo(c, n.info)
let a = c.genx(n[1])
let t = typeToIr(c.m, n.typ)
let setType = typeToIr(c.m, n[1].typ)
if isEmpty(d): d = getTemp(c, n)
buildTyped c.code, info, Asgn, t:
copyTree c.code, d
buildTyped c.code, info, Call, t:
if c.m.nirm.types[setType].kind == ArrayTy:
let codegenProc = magicsys.getCompilerProc(c.m.graph, "cardSet")
let theProc = c.genx newSymNode(codegenProc, n.info)
copyTree c.code, theProc
buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, setType):
copyTree c.code, a
c.code.addImmediateVal info, int(getSize(c.config, n[1].typ))
elif t == UInt64Id:
let codegenProc = magicsys.getCompilerProc(c.m.graph, "countBits64")
let theProc = c.genx newSymNode(codegenProc, n.info)
copyTree c.code, theProc
copyTree c.code, a
else:
let codegenProc = magicsys.getCompilerProc(c.m.graph, "countBits32")
let theProc = c.genx newSymNode(codegenProc, n.info)
copyTree c.code, theProc
buildTyped c.code, info, Cast, UInt32Id:
copyTree c.code, a
freeTemp c, a
proc genEqSet(c: var ProcCon; n: PNode; d: var Value) =
let info = toLineInfo(c, n.info)
let a = c.genx(n[1])
let b = c.genx(n[2])
let t = typeToIr(c.m, n.typ)
let setType = typeToIr(c.m, n[1].typ)
if c.m.nirm.types[setType].kind == ArrayTy:
if isEmpty(d): d = getTemp(c, n)
buildTyped c.code, info, Asgn, t:
copyTree c.code, d
buildTyped c.code, info, Eq, t:
buildTyped c.code, info, Call, t:
let codegenProc = magicsys.getCompilerProc(c.m.graph, "nimCmpMem")
let theProc = c.genx newSymNode(codegenProc, n.info)
copyTree c.code, theProc
buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, setType):
copyTree c.code, a
buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, setType):
copyTree c.code, b
c.code.addImmediateVal info, int(getSize(c.config, n[1].typ))
c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0
else:
template body(target) =
buildTyped target, info, Eq, setType:
copyTree target, a
copyTree target, b
intoDest d, info, Bool8Id, body
freeTemp c, b
freeTemp c, a
proc beginCountLoop(c: var ProcCon; info: PackedLineInfo; first, last: int): (SymId, LabelId, LabelId) =
let tmp = allocTemp(c, c.m.nativeIntId)
c.code.addSummon info, tmp, c.m.nativeIntId
buildTyped c.code, info, Asgn, c.m.nativeIntId:
c.code.addSymUse info, tmp
c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, first
let lab1 = c.code.addNewLabel(c.labelGen, info, LoopLabel)
result = (tmp, lab1, newLabel(c.labelGen))
buildTyped c.code, info, Select, Bool8Id:
buildTyped c.code, info, Lt, c.m.nativeIntId:
c.code.addSymUse info, tmp
c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, last
build c.code, info, SelectPair:
build c.code, info, SelectValue:
c.code.boolVal(c.lit.numbers, info, false)
c.code.gotoLabel info, Goto, result[2]
proc beginCountLoop(c: var ProcCon; info: PackedLineInfo; first, last: Value): (SymId, LabelId, LabelId) =
let tmp = allocTemp(c, c.m.nativeIntId)
c.code.addSummon info, tmp, c.m.nativeIntId
buildTyped c.code, info, Asgn, c.m.nativeIntId:
c.code.addSymUse info, tmp
copyTree c.code, first
let lab1 = c.code.addNewLabel(c.labelGen, info, LoopLabel)
result = (tmp, lab1, newLabel(c.labelGen))
buildTyped c.code, info, Select, Bool8Id:
buildTyped c.code, info, Le, c.m.nativeIntId:
c.code.addSymUse info, tmp
copyTree c.code, last
build c.code, info, SelectPair:
build c.code, info, SelectValue:
c.code.boolVal(c.lit.numbers, info, false)
c.code.gotoLabel info, Goto, result[2]
proc endLoop(c: var ProcCon; info: PackedLineInfo; s: SymId; back, exit: LabelId) =
buildTyped c.code, info, Asgn, c.m.nativeIntId:
c.code.addSymUse info, s
buildTyped c.code, info, Add, c.m.nativeIntId:
c.code.addSymUse info, s
c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, 1
c.code.addLabel info, GotoLoop, back
c.code.addLabel info, Label, exit
freeTemp(c.sm, s)
proc genLeSet(c: var ProcCon; n: PNode; d: var Value) =
let info = toLineInfo(c, n.info)
let a = c.genx(n[1])
let b = c.genx(n[2])
let t = typeToIr(c.m, n.typ)
let setType = typeToIr(c.m, n[1].typ)
if c.m.nirm.types[setType].kind == ArrayTy:
let elemType = bitsetBasetype(c.m.types, c.m.nirm.types, n[1].typ)
if isEmpty(d): d = getTemp(c, n)
# "for ($1 = 0; $1 < $2; $1++):"
# " $3 = (($4[$1] & ~ $5[$1]) == 0)"
# " if (!$3) break;"
let (idx, backLabel, endLabel) = beginCountLoop(c, info, 0, int(getSize(c.config, n[1].typ)))
buildTyped c.code, info, Asgn, Bool8Id:
copyTree c.code, d
buildTyped c.code, info, Eq, elemType:
buildTyped c.code, info, BitAnd, elemType:
buildTyped c.code, info, ArrayAt, setType:
copyTree c.code, a
c.code.addSymUse info, idx
buildTyped c.code, info, BitNot, elemType:
buildTyped c.code, info, ArrayAt, setType:
copyTree c.code, b
c.code.addSymUse info, idx
c.code.addIntVal c.lit.numbers, info, elemType, 0
# if !$3: break
buildTyped c.code, info, Select, Bool8Id:
c.code.copyTree d
build c.code, info, SelectPair:
build c.code, info, SelectValue:
c.code.boolVal(c.lit.numbers, info, false)
c.code.gotoLabel info, Goto, endLabel
endLoop(c, info, idx, backLabel, endLabel)
else:
# "(($1 & ~ $2)==0)"
template body(target) =
buildTyped target, info, Eq, setType:
buildTyped target, info, BitAnd, setType:
copyTree target, a
buildTyped target, info, BitNot, setType:
copyTree target, b
target.addIntVal c.lit.numbers, info, setType, 0
intoDest d, info, Bool8Id, body
freeTemp c, b
freeTemp c, a
proc genLtSet(c: var ProcCon; n: PNode; d: var Value) =
localError(c.m.graph.config, n.info, "`<` for sets not implemented")
proc genBinarySet(c: var ProcCon; n: PNode; d: var Value; m: TMagic) =
let info = toLineInfo(c, n.info)
let a = c.genx(n[1])
let b = c.genx(n[2])
let t = typeToIr(c.m, n.typ)
let setType = typeToIr(c.m, n[1].typ)
if c.m.nirm.types[setType].kind == ArrayTy:
let elemType = bitsetBasetype(c.m.types, c.m.nirm.types, n[1].typ)
if isEmpty(d): d = getTemp(c, n)
# "for ($1 = 0; $1 < $2; $1++):"
# " $3 = (($4[$1] & ~ $5[$1]) == 0)"
# " if (!$3) break;"
let (idx, backLabel, endLabel) = beginCountLoop(c, info, 0, int(getSize(c.config, n[1].typ)))
buildTyped c.code, info, Asgn, elemType:
buildTyped c.code, info, ArrayAt, setType:
copyTree c.code, d
c.code.addSymUse info, idx
buildTyped c.code, info, (if m == mPlusSet: BitOr else: BitAnd), elemType:
buildTyped c.code, info, ArrayAt, setType:
copyTree c.code, a
c.code.addSymUse info, idx
if m == mMinusSet:
buildTyped c.code, info, BitNot, elemType:
buildTyped c.code, info, ArrayAt, setType:
copyTree c.code, b
c.code.addSymUse info, idx
else:
buildTyped c.code, info, ArrayAt, setType:
copyTree c.code, b
c.code.addSymUse info, idx
endLoop(c, info, idx, backLabel, endLabel)
else:
# "(($1 & ~ $2)==0)"
template body(target) =
buildTyped target, info, (if m == mPlusSet: BitOr else: BitAnd), setType:
copyTree target, a
if m == mMinusSet:
buildTyped target, info, BitNot, setType:
copyTree target, b
else:
copyTree target, b
intoDest d, info, setType, body
freeTemp c, b
freeTemp c, a
proc genInclExcl(c: var ProcCon; n: PNode; m: TMagic) =
let info = toLineInfo(c, n.info)
let a = c.genx(n[1])
let b = c.genx(n[2])
let setType = typeToIr(c.m, n[1].typ)
let t = bitsetBasetype(c.m.types, c.m.nirm.types, n[1].typ)
let mask =
case t
of UInt8Id: 7
of UInt16Id: 15
of UInt32Id: 31
else: 63
buildTyped c.code, info, Asgn, setType:
if c.m.nirm.types[setType].kind == ArrayTy:
if m == mIncl:
# $1[(NU)($2)>>3] |=(1U<<($2&7U))
buildTyped c.code, info, ArrayAt, setType:
copyTree c.code, a
buildTyped c.code, info, BitShr, t:
buildTyped c.code, info, Cast, c.m.nativeUIntId:
copyTree c.code, b
addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3
buildTyped c.code, info, BitOr, t:
buildTyped c.code, info, ArrayAt, setType:
copyTree c.code, a
buildTyped c.code, info, BitShr, t:
buildTyped c.code, info, Cast, c.m.nativeUIntId:
copyTree c.code, b
addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3
buildTyped c.code, info, BitShl, t:
c.code.addIntVal c.lit.numbers, info, t, 1
buildTyped c.code, info, BitAnd, t:
copyTree c.code, b
c.code.addIntVal c.lit.numbers, info, t, 7
else:
# $1[(NU)($2)>>3] &= ~(1U<<($2&7U))
buildTyped c.code, info, ArrayAt, setType:
copyTree c.code, a
buildTyped c.code, info, BitShr, t:
buildTyped c.code, info, Cast, c.m.nativeUIntId:
copyTree c.code, b
addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3
buildTyped c.code, info, BitAnd, t:
buildTyped c.code, info, ArrayAt, setType:
copyTree c.code, a
buildTyped c.code, info, BitShr, t:
buildTyped c.code, info, Cast, c.m.nativeUIntId:
copyTree c.code, b
addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3
buildTyped c.code, info, BitNot, t:
buildTyped c.code, info, BitShl, t:
c.code.addIntVal c.lit.numbers, info, t, 1
buildTyped c.code, info, BitAnd, t:
copyTree c.code, b
c.code.addIntVal c.lit.numbers, info, t, 7
else:
copyTree c.code, a
if m == mIncl:
# $1 |= ((NU8)1)<<(($2) & 7)
buildTyped c.code, info, BitOr, setType:
copyTree c.code, a
buildTyped c.code, info, BitShl, t:
c.code.addIntVal c.lit.numbers, info, t, 1
buildTyped c.code, info, BitAnd, t:
copyTree c.code, b
c.code.addIntVal c.lit.numbers, info, t, mask
else:
# $1 &= ~(((NU8)1) << (($2) & 7))
buildTyped c.code, info, BitAnd, setType:
copyTree c.code, a
buildTyped c.code, info, BitNot, t:
buildTyped c.code, info, BitShl, t:
c.code.addIntVal c.lit.numbers, info, t, 1
buildTyped c.code, info, BitAnd, t:
copyTree c.code, b
c.code.addIntVal c.lit.numbers, info, t, mask
freeTemp c, b
freeTemp c, a
proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) =
# example: { a..b, c, d, e, f..g }
# we have to emit an expression of the form:
# nimZeroMem(tmp, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c);
# incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g);
let info = toLineInfo(c, n.info)
let setType = typeToIr(c.m, n.typ)
let size = int(getSize(c.config, n.typ))
let t = bitsetBasetype(c.m.types, c.m.nirm.types, n.typ)
let mask =
case t
of UInt8Id: 7
of UInt16Id: 15
of UInt32Id: 31
else: 63
if isEmpty(d): d = getTemp(c, n)
if c.m.nirm.types[setType].kind != ArrayTy:
buildTyped c.code, info, Asgn, setType:
copyTree c.code, d
c.code.addIntVal c.lit.numbers, info, t, 0
for it in n:
if it.kind == nkRange:
let a = genx(c, it[0])
let b = genx(c, it[1])
let (idx, backLabel, endLabel) = beginCountLoop(c, info, a, b)
buildTyped c.code, info, Asgn, setType:
copyTree c.code, d
buildTyped c.code, info, BitAnd, setType:
copyTree c.code, d
buildTyped c.code, info, BitNot, t:
buildTyped c.code, info, BitShl, t:
c.code.addIntVal c.lit.numbers, info, t, 1
buildTyped c.code, info, BitAnd, t:
c.code.addSymUse info, idx
c.code.addIntVal c.lit.numbers, info, t, mask
endLoop(c, info, idx, backLabel, endLabel)
freeTemp c, b
freeTemp c, a
else:
let a = genx(c, it)
buildTyped c.code, info, Asgn, setType:
copyTree c.code, d
buildTyped c.code, info, BitAnd, setType:
copyTree c.code, d
buildTyped c.code, info, BitNot, t:
buildTyped c.code, info, BitShl, t:
c.code.addIntVal c.lit.numbers, info, t, 1
buildTyped c.code, info, BitAnd, t:
copyTree c.code, a
c.code.addIntVal c.lit.numbers, info, t, mask
freeTemp c, a
else:
# init loop:
let (idx, backLabel, endLabel) = beginCountLoop(c, info, 0, size)
buildTyped c.code, info, Asgn, t:
copyTree c.code, d
c.code.addIntVal c.lit.numbers, info, t, 0
endLoop(c, info, idx, backLabel, endLabel)
# incl elements:
for it in n:
if it.kind == nkRange:
let a = genx(c, it[0])
let b = genx(c, it[1])
let (idx, backLabel, endLabel) = beginCountLoop(c, info, a, b)
buildTyped c.code, info, Asgn, t:
buildTyped c.code, info, ArrayAt, setType:
copyTree c.code, d
buildTyped c.code, info, BitShr, t:
buildTyped c.code, info, Cast, c.m.nativeUIntId:
c.code.addSymUse info, idx
addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3
buildTyped c.code, info, BitOr, t:
buildTyped c.code, info, ArrayAt, setType:
copyTree c.code, d
buildTyped c.code, info, BitShr, t:
buildTyped c.code, info, Cast, c.m.nativeUIntId:
c.code.addSymUse info, idx
addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3
buildTyped c.code, info, BitShl, t:
c.code.addIntVal c.lit.numbers, info, t, 1
buildTyped c.code, info, BitAnd, t:
c.code.addSymUse info, idx
c.code.addIntVal c.lit.numbers, info, t, 7
endLoop(c, info, idx, backLabel, endLabel)
freeTemp c, b
freeTemp c, a
else:
let a = genx(c, it)
# $1[(NU)($2)>>3] |=(1U<<($2&7U))
buildTyped c.code, info, Asgn, t:
buildTyped c.code, info, ArrayAt, setType:
copyTree c.code, d
buildTyped c.code, info, BitShr, t:
buildTyped c.code, info, Cast, c.m.nativeUIntId:
copyTree c.code, a
addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3
buildTyped c.code, info, BitOr, t:
buildTyped c.code, info, ArrayAt, setType:
copyTree c.code, d
buildTyped c.code, info, BitShr, t:
buildTyped c.code, info, Cast, c.m.nativeUIntId:
copyTree c.code, a
addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3
buildTyped c.code, info, BitShl, t:
c.code.addIntVal c.lit.numbers, info, t, 1
buildTyped c.code, info, BitAnd, t:
copyTree c.code, a
c.code.addIntVal c.lit.numbers, info, t, 7
freeTemp c, a
proc genSetConstr(c: var ProcCon; n: PNode; d: var Value) =
if isDeepConstExpr(n):
let info = toLineInfo(c, n.info)
let setType = typeToIr(c.m, n.typ)
let size = int(getSize(c.config, n.typ))
let cs = toBitSet(c.config, n)
if c.m.nirm.types[setType].kind != ArrayTy:
template body(target) =
target.addIntVal c.lit.numbers, info, setType, cast[BiggestInt](bitSetToWord(cs, size))
intoDest d, info, setType, body
else:
let t = bitsetBasetype(c.m.types, c.m.nirm.types, n.typ)
template body(target) =
buildTyped target, info, ArrayConstr, setType:
for i in 0..high(cs):
target.addIntVal c.lit.numbers, info, t, int64 cs[i]
intoDest d, info, setType, body
else:
genSetConstrDyn c, n, d
proc genStrConcat(c: var ProcCon; n: PNode; d: var Value) =
let info = toLineInfo(c, n.info)
# <Nim code>
# s = "Hello " & name & ", how do you feel?" & 'z'
#
# <generated code>
# {
# string tmp0;
# ...
# tmp0 = rawNewString(6 + 17 + 1 + s2->len);
# // we cannot generate s = rawNewString(...) here, because
# // ``s`` may be used on the right side of the expression
# appendString(tmp0, strlit_1);
# appendString(tmp0, name);
# appendString(tmp0, strlit_2);
# appendChar(tmp0, 'z');
# asgn(s, tmp0);
# }
var args: seq[Value] = @[]
var argsRuntimeLen: seq[Value] = @[]
var precomputedLen = 0
for i in 1 ..< n.len:
let it = n[i]
args.add genx(c, it)
if skipTypes(it.typ, abstractVarRange).kind == tyChar:
inc precomputedLen
elif it.kind in {nkStrLit..nkTripleStrLit}:
inc precomputedLen, it.strVal.len
else:
argsRuntimeLen.add args[^1]
# generate length computation:
var tmpLen = allocTemp(c, c.m.nativeIntId)
buildTyped c.code, info, Asgn, c.m.nativeIntId:
c.code.addSymUse info, tmpLen
c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, precomputedLen
for a in mitems(argsRuntimeLen):
buildTyped c.code, info, Asgn, c.m.nativeIntId:
c.code.addSymUse info, tmpLen
buildTyped c.code, info, CheckedAdd, c.m.nativeIntId:
c.code.addLabel info, CheckedGoto, c.exitLabel
c.code.addSymUse info, tmpLen
buildTyped c.code, info, FieldAt, typeToIr(c.m, n.typ):
copyTree c.code, a
c.code.addImmediateVal info, 0 # (len, p)-pair so len is at index 0
var tmpStr = getTemp(c, n)
# ^ because of aliasing, we always go through a temporary
let t = typeToIr(c.m, n.typ)
buildTyped c.code, info, Asgn, t:
copyTree c.code, tmpStr
buildTyped c.code, info, Call, t:
let codegenProc = magicsys.getCompilerProc(c.m.graph, "rawNewString")
#assert codegenProc != nil, $n & " " & (c.m.graph.config $ n.info)
let theProc = c.genx newSymNode(codegenProc, n.info)
copyTree c.code, theProc
c.code.addSymUse info, tmpLen
freeTemp c.sm, tmpLen
for i in 1 ..< n.len:
let it = n[i]
let isChar = skipTypes(it.typ, abstractVarRange).kind == tyChar
buildTyped c.code, info, Call, VoidId:
let codegenProc = magicsys.getCompilerProc(c.m.graph,
(if isChar: "appendChar" else: "appendString"))
#assert codegenProc != nil, $n & " " & (c.m.graph.config $ n.info)
let theProc = c.genx newSymNode(codegenProc, n.info)
copyTree c.code, theProc
buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, t):
copyTree c.code, tmpStr
copyTree c.code, args[i-1]
freeTemp c, args[i-1]
if isEmpty(d):
d = tmpStr
else:
# XXX Test that this does not cause memory leaks!
buildTyped c.code, info, Asgn, t:
copyTree c.code, d
copyTree c.code, tmpStr
proc genDefault(c: var ProcCon; n: PNode; d: var Value) =
let m = expandDefault(n.typ, n.info)
gen c, m, d
proc genWasMoved(c: var ProcCon; n: PNode) =
let n1 = n[1].skipAddr
# XXX We need a way to replicate this logic or better yet a better
# solution for injectdestructors.nim:
#if c.withinBlockLeaveActions > 0 and notYetAlive(n1):
var d = c.genx(n1)
assert not isEmpty(d)
let m = expandDefault(n1.typ, n1.info)
gen c, m, d
proc genMove(c: var ProcCon; n: PNode; d: var Value) =
let info = toLineInfo(c, n.info)
let n1 = n[1].skipAddr
var a = c.genx(n1)
if n.len == 4:
# generated by liftdestructors:
let src = c.genx(n[2])
# if ($1.p == $2.p) goto lab1
let lab1 = newLabel(c.labelGen)
let n1t = typeToIr(c.m, n1.typ)
let payloadType = seqPayloadPtrType(c.m.types, c.m.nirm.types, n1.typ)[0]
buildTyped c.code, info, Select, Bool8Id:
buildTyped c.code, info, Eq, payloadType:
buildTyped c.code, info, FieldAt, n1t:
copyTree c.code, a
c.code.addImmediateVal info, 1 # (len, p)-pair
buildTyped c.code, info, FieldAt, n1t:
copyTree c.code, src
c.code.addImmediateVal info, 1 # (len, p)-pair
build c.code, info, SelectPair:
build c.code, info, SelectValue:
c.code.boolVal(c.lit.numbers, info, true)
c.code.gotoLabel info, Goto, lab1
gen(c, n[3])
c.patch n, lab1
buildTyped c.code, info, Asgn, typeToIr(c.m, n1.typ):
copyTree c.code, a
copyTree c.code, src
else:
if isEmpty(d): d = getTemp(c, n)
buildTyped c.code, info, Asgn, typeToIr(c.m, n1.typ):
copyTree c.code, d
copyTree c.code, a
var op = getAttachedOp(c.m.graph, n.typ, attachedWasMoved)
if op == nil or skipTypes(n1.typ, abstractVar+{tyStatic}).kind in {tyOpenArray, tyVarargs}:
let m = expandDefault(n1.typ, n1.info)
gen c, m, a
else:
var opB = c.genx(newSymNode(op))
buildTyped c.code, info, Call, typeToIr(c.m, n.typ):
copyTree c.code, opB
buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, typeToIr(c.m, n1.typ)):
copyTree c.code, a
template fieldAt(x: Value; i: int; t: TypeId): Tree =
var result = default(Tree)
buildTyped result, info, FieldAt, t:
copyTree result, x
result.addImmediateVal info, i
result
template eqNil(x: Tree; t: TypeId): Tree =
var result = default(Tree)
buildTyped result, info, Eq, t:
copyTree result, x
result.addNilVal info, t
result
template eqZero(x: Tree): Tree =
var result = default(Tree)
buildTyped result, info, Eq, c.m.nativeIntId:
copyTree result, x
result.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0
result
template bitOp(x: Tree; opc: Opcode; y: int): Tree =
var result = default(Tree)
buildTyped result, info, opc, c.m.nativeIntId:
copyTree result, x
result.addIntVal c.lit.numbers, info, c.m.nativeIntId, y
result
proc genDestroySeq(c: var ProcCon; n: PNode; t: PType) =
let info = toLineInfo(c, n.info)
let strLitFlag = 1 shl (c.m.graph.config.target.intSize * 8 - 2) # see also NIM_STRLIT_FLAG
let x = c.genx(n[1])
let baseType = t.lastSon
let seqType = typeToIr(c.m, t)
let p = fieldAt(x, 0, seqType)
# if $1.p != nil and ($1.p.cap and NIM_STRLIT_FLAG) == 0:
# alignedDealloc($1.p, NIM_ALIGNOF($2))
buildIfNot p.eqNil(seqType):
buildIf fieldAt(Value(p), 0, seqPayloadPtrType(c.m.types, c.m.nirm.types, t)[0]).bitOp(BitAnd, 0).eqZero():
let codegenProc = getCompilerProc(c.m.graph, "alignedDealloc")
buildTyped c.code, info, Call, VoidId:
let theProc = c.genx newSymNode(codegenProc, n.info)
copyTree c.code, theProc
copyTree c.code, p
c.code.addImmediateVal info, int(getAlign(c.config, baseType))
freeTemp c, x
proc genDestroy(c: var ProcCon; n: PNode) =
let t = n[1].typ.skipTypes(abstractInst)
case t.kind
of tyString:
var unused = default(Value)
genUnaryCp(c, n, unused, "nimDestroyStrV1")
of tySequence:
genDestroySeq(c, n, t)
else: discard "nothing to do"
type
IndexFor = enum
ForSeq, ForStr, ForOpenArray, ForArray
proc genIndexCheck(c: var ProcCon; n: PNode; a: Value; kind: IndexFor; arr: PType): Value =
if optBoundsCheck in c.options:
let info = toLineInfo(c, n.info)
result = default(Value)
let idx = genx(c, n)
build result, info, CheckedIndex:
result.Tree.addLabel info, CheckedGoto, c.exitLabel
copyTree result.Tree, idx
case kind
of ForSeq, ForStr:
buildTyped result, info, FieldAt, typeToIr(c.m, arr):
copyTree result.Tree, a
result.addImmediateVal info, 0 # (len, p)-pair
of ForOpenArray:
buildTyped result, info, FieldAt, typeToIr(c.m, arr):
copyTree result.Tree, a
result.addImmediateVal info, 1 # (p, len)-pair
of ForArray:
let x = toInt64 lengthOrd(c.config, arr)
result.addIntVal c.lit.numbers, info, c.m.nativeIntId, x
freeTemp c, idx
else:
result = genx(c, n)
proc addSliceFields(c: var ProcCon; target: var Tree; info: PackedLineInfo;
x: Value; n: PNode; arrType: PType) =
let elemType = arrayPtrTypeOf(c.m.nirm.types, typeToIr(c.m, arrType.lastSon))
case arrType.kind
of tyString, tySequence:
let checkKind = if arrType.kind == tyString: ForStr else: ForSeq
let pay = if checkKind == ForStr: c.m.strPayloadId
else: seqPayloadPtrType(c.m.types, c.m.nirm.types, arrType)
let y = genIndexCheck(c, n[2], x, checkKind, arrType)
let z = genIndexCheck(c, n[3], x, checkKind, arrType)
buildTyped target, info, ObjConstr, typeToIr(c.m, n.typ):
target.addImmediateVal info, 0
buildTyped target, info, AddrOf, elemType:
buildTyped target, info, DerefArrayAt, pay[1]:
buildTyped target, info, FieldAt, typeToIr(c.m, arrType):
copyTree target, x
target.addImmediateVal info, 1 # (len, p)-pair
copyTree target, y
# len:
target.addImmediateVal info, 1
buildTyped target, info, Add, c.m.nativeIntId:
buildTyped target, info, Sub, c.m.nativeIntId:
copyTree target, z
copyTree target, y
target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 1
freeTemp c, z
freeTemp c, y
of tyArray:
# XXX This evaluates the index check for `y` twice.
# This check is also still insufficient for non-zero based arrays.
let y = genIndexCheck(c, n[2], x, ForArray, arrType)
let z = genIndexCheck(c, n[3], x, ForArray, arrType)
buildTyped target, info, ObjConstr, typeToIr(c.m, n.typ):
target.addImmediateVal info, 0
buildTyped target, info, AddrOf, elemType:
buildTyped target, info, ArrayAt, typeToIr(c.m, arrType):
copyTree target, x
copyTree target, y
target.addImmediateVal info, 1
buildTyped target, info, Add, c.m.nativeIntId:
buildTyped target, info, Sub, c.m.nativeIntId:
copyTree target, z
copyTree target, y
target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 1
freeTemp c, z
freeTemp c, y
of tyOpenArray:
# XXX This evaluates the index check for `y` twice.
let y = genIndexCheck(c, n[2], x, ForOpenArray, arrType)
let z = genIndexCheck(c, n[3], x, ForOpenArray, arrType)
let pay = openArrayPayloadType(c.m.types, c.m.nirm.types, arrType)
buildTyped target, info, ObjConstr, typeToIr(c.m, n.typ):
target.addImmediateVal info, 0
buildTyped target, info, AddrOf, elemType:
buildTyped target, info, DerefArrayAt, pay:
buildTyped target, info, FieldAt, typeToIr(c.m, arrType):
copyTree target, x
target.addImmediateVal info, 0 # (p, len)-pair
copyTree target, y
target.addImmediateVal info, 1
buildTyped target, info, Add, c.m.nativeIntId:
buildTyped target, info, Sub, c.m.nativeIntId:
copyTree target, z
copyTree target, y
target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 1
freeTemp c, z
freeTemp c, y
else:
raiseAssert "addSliceFields: " & typeToString(arrType)
proc genSlice(c: var ProcCon; n: PNode; d: var Value) =
let info = toLineInfo(c, n.info)
let x = c.genx(n[1])
let arrType = n[1].typ.skipTypes(abstractVar)
template body(target) =
c.addSliceFields target, info, x, n, arrType
valueIntoDest c, info, d, arrType, body
freeTemp c, x
proc genMagic(c: var ProcCon; n: PNode; d: var Value; m: TMagic) =
case m
of mAnd: c.genAndOr(n, opcFJmp, d)
of mOr: c.genAndOr(n, opcTJmp, d)
of mPred, mSubI: c.genBinaryOp(n, d, if optOverflowCheck in c.options: CheckedSub else: Sub)
of mSucc, mAddI: c.genBinaryOp(n, d, if optOverflowCheck in c.options: CheckedAdd else: Add)
of mInc:
unused(c, n, d)
c.genIncDec(n, if optOverflowCheck in c.options: CheckedAdd else: Add)
of mDec:
unused(c, n, d)
c.genIncDec(n, if optOverflowCheck in c.options: CheckedSub else: Sub)
of mOrd, mChr, mUnown:
c.gen(n[1], d)
of generatedMagics:
genCall(c, n, d)
of mNew, mNewFinalize:
unused(c, n, d)
c.genNew(n, needsInit = true)
of mNewSeq:
unused(c, n, d)
c.genNewSeq(n)
of mNewSeqOfCap: c.genNewSeqOfCap(n, d)
of mNewString, mNewStringOfCap, mExit: c.genCall(n, d)
of mLengthOpenArray, mLengthArray, mLengthSeq, mLengthStr:
genArrayLen(c, n, d)
of mMulI: genBinaryOp(c, n, d, if optOverflowCheck in c.options: CheckedMul else: Mul)
of mDivI: genBinaryOp(c, n, d, if optOverflowCheck in c.options: CheckedDiv else: Div)
of mModI: genBinaryOp(c, n, d, if optOverflowCheck in c.options: CheckedMod else: Mod)
of mAddF64: genBinaryOp(c, n, d, Add)
of mSubF64: genBinaryOp(c, n, d, Sub)
of mMulF64: genBinaryOp(c, n, d, Mul)
of mDivF64: genBinaryOp(c, n, d, Div)
of mShrI: genBinaryOp(c, n, d, BitShr)
of mShlI: genBinaryOp(c, n, d, BitShl)
of mAshrI: genBinaryOp(c, n, d, BitShr)
of mBitandI: genBinaryOp(c, n, d, BitAnd)
of mBitorI: genBinaryOp(c, n, d, BitOr)
of mBitxorI: genBinaryOp(c, n, d, BitXor)
of mAddU: genBinaryOp(c, n, d, Add)
of mSubU: genBinaryOp(c, n, d, Sub)
of mMulU: genBinaryOp(c, n, d, Mul)
of mDivU: genBinaryOp(c, n, d, Div)
of mModU: genBinaryOp(c, n, d, Mod)
of mEqI, mEqB, mEqEnum, mEqCh:
genCmpOp(c, n, d, Eq)
of mLeI, mLeEnum, mLeCh, mLeB:
genCmpOp(c, n, d, Le)
of mLtI, mLtEnum, mLtCh, mLtB:
genCmpOp(c, n, d, Lt)
of mEqF64: genCmpOp(c, n, d, Eq)
of mLeF64: genCmpOp(c, n, d, Le)
of mLtF64: genCmpOp(c, n, d, Lt)
of mLePtr, mLeU: genCmpOp(c, n, d, Le)
of mLtPtr, mLtU: genCmpOp(c, n, d, Lt)
of mEqProc, mEqRef:
genCmpOp(c, n, d, Eq)
of mXor: genBinaryOp(c, n, d, BitXor)
of mNot: genUnaryOp(c, n, d, BoolNot)
of mUnaryMinusI, mUnaryMinusI64:
genUnaryMinus(c, n, d)
#genNarrow(c, n, d)
of mUnaryMinusF64: genUnaryMinus(c, n, d)
of mUnaryPlusI, mUnaryPlusF64: gen(c, n[1], d)
of mBitnotI:
genUnaryOp(c, n, d, BitNot)
when false:
# XXX genNarrowU modified, do not narrow signed types
let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
let size = getSize(c.config, t)
if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8):
c.gABC(n, opcNarrowU, d, TRegister(size*8))
of mStrToStr, mEnsureMove: c.gen n[1], d
of mIntToStr: genUnaryCp(c, n, d, "nimIntToStr")
of mInt64ToStr: genUnaryCp(c, n, d, "nimInt64ToStr")
of mBoolToStr: genUnaryCp(c, n, d, "nimBoolToStr")
of mCharToStr: genUnaryCp(c, n, d, "nimCharToStr")
of mFloatToStr:
if n[1].typ.skipTypes(abstractInst).kind == tyFloat32:
genUnaryCp(c, n, d, "nimFloat32ToStr")
else:
genUnaryCp(c, n, d, "nimFloatToStr")
of mCStrToStr: genUnaryCp(c, n, d, "cstrToNimstr")
of mEnumToStr: genEnumToStr(c, n, d)
of mEqStr: genBinaryCp(c, n, d, "eqStrings")
of mEqCString: genCall(c, n, d)
of mLeStr: genBinaryCp(c, n, d, "leStrings")
of mLtStr: genBinaryCp(c, n, d, "ltStrings")
of mSetLengthStr:
unused(c, n, d)
let nb = copyTree(n)
nb[1] = makeAddr(nb[1], c.m.idgen)
genBinaryCp(c, nb, d, "setLengthStrV2")
of mSetLengthSeq:
unused(c, n, d)
let nb = copyTree(n)
nb[1] = makeAddr(nb[1], c.m.idgen)
genCall(c, nb, d)
of mSwap:
unused(c, n, d)
c.gen(lowerSwap(c.m.graph, n, c.m.idgen,
if c.prc == nil: c.m.module else: c.prc), d)
of mParseBiggestFloat:
genCall c, n, d
of mHigh:
c.genHigh n, d
of mEcho:
unused(c, n, d)
genUnaryCp c, n, d, "echoBinSafe"
of mAppendStrCh:
unused(c, n, d)
let nb = copyTree(n)
nb[1] = makeAddr(nb[1], c.m.idgen)
genBinaryCp(c, nb, d, "nimAddCharV1")
of mMinI, mMaxI, mAbsI, mDotDot:
c.genCall(n, d)
of mSizeOf:
localError(c.config, n.info, sizeOfLikeMsg("sizeof"))
of mAlignOf:
localError(c.config, n.info, sizeOfLikeMsg("alignof"))
of mOffsetOf:
localError(c.config, n.info, sizeOfLikeMsg("offsetof"))
of mRunnableExamples:
discard "just ignore any call to runnableExamples"
of mOf: genOf(c, n, d)
of mAppendStrStr:
unused(c, n, d)
let nb = copyTree(n)
nb[1] = makeAddr(nb[1], c.m.idgen)
genBinaryCp(c, nb, d, "nimAddStrV1")
of mAppendSeqElem:
unused(c, n, d)
let nb = copyTree(n)
nb[1] = makeAddr(nb[1], c.m.idgen)
genCall(c, nb, d)
of mIsNil: genIsNil(c, n, d)
of mInSet: genInSet(c, n, d)
of mCard: genCard(c, n, d)
of mEqSet: genEqSet(c, n, d)
of mLeSet: genLeSet(c, n, d)
of mLtSet: genLtSet(c, n, d)
of mMulSet: genBinarySet(c, n, d, m)
of mPlusSet: genBinarySet(c, n, d, m)
of mMinusSet: genBinarySet(c, n, d, m)
of mIncl, mExcl:
unused(c, n, d)
genInclExcl(c, n, m)
of mConStrStr: genStrConcat(c, n, d)
of mDefault, mZeroDefault:
genDefault c, n, d
of mMove: genMove(c, n, d)
of mWasMoved, mReset:
unused(c, n, d)
genWasMoved(c, n)
of mDestroy: genDestroy(c, n)
#of mAccessEnv: unaryExpr(d, n, d, "$1.ClE_0")
#of mAccessTypeField: genAccessTypeField(c, n, d)
of mSlice: genSlice(c, n, d)
of mTrace: discard "no code to generate"
else:
# mGCref, mGCunref: unused by ORC
globalError(c.config, n.info, "cannot generate code for: " & $m)
proc canElimAddr(n: PNode; idgen: IdGenerator): PNode =
result = nil
case n[0].kind
of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
var m = n[0][0]
if m.kind in {nkDerefExpr, nkHiddenDeref}:
# addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
result = copyNode(n[0])
result.add m[0]
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
result.typ = n.typ
elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen)
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
var m = n[0][1]
if m.kind in {nkDerefExpr, nkHiddenDeref}:
# addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
result = copyNode(n[0])
result.add n[0][0]
result.add m[0]
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
result.typ = n.typ
elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen)
else:
if n[0].kind in {nkDerefExpr, nkHiddenDeref}:
# addr ( deref ( x )) --> x
result = n[0][0]
proc genAddr(c: var ProcCon; n: PNode; d: var Value, flags: GenFlags) =
if (let m = canElimAddr(n, c.m.idgen); m != nil):
gen(c, m, d, flags)
return
let info = toLineInfo(c, n.info)
let tmp = c.genx(n[0], flags)
template body(target) =
buildTyped target, info, AddrOf, typeToIr(c.m, n.typ):
copyTree target, tmp
valueIntoDest c, info, d, n.typ, body
freeTemp c, tmp
proc genDeref(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
let info = toLineInfo(c, n.info)
let tmp = c.genx(n[0], flags)
template body(target) =
buildTyped target, info, Load, typeToIr(c.m, n.typ):
copyTree target, tmp
valueIntoDest c, info, d, n.typ, body
freeTemp c, tmp
proc addAddrOfFirstElem(c: var ProcCon; target: var Tree; info: PackedLineInfo; tmp: Value; typ: PType) =
let arrType = typ.skipTypes(abstractVar)
let elemType = arrayPtrTypeOf(c.m.nirm.types, typeToIr(c.m, arrType.lastSon))
case arrType.kind
of tyString:
let t = typeToIr(c.m, typ)
target.addImmediateVal info, 0
buildTyped target, info, AddrOf, elemType:
buildTyped target, info, DerefArrayAt, c.m.strPayloadId[1]:
buildTyped target, info, FieldAt, typeToIr(c.m, arrType):
copyTree target, tmp
target.addImmediateVal info, 1 # (len, p)-pair
target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0
# len:
target.addImmediateVal info, 1
buildTyped target, info, FieldAt, typeToIr(c.m, arrType):
copyTree target, tmp
target.addImmediateVal info, 0 # (len, p)-pair so len is at index 0
of tySequence:
let t = typeToIr(c.m, typ)
target.addImmediateVal info, 0
buildTyped target, info, AddrOf, elemType:
buildTyped target, info, DerefArrayAt, seqPayloadPtrType(c.m.types, c.m.nirm.types, typ)[1]:
buildTyped target, info, FieldAt, typeToIr(c.m, arrType):
copyTree target, tmp
target.addImmediateVal info, 1 # (len, p)-pair
target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0
# len:
target.addImmediateVal info, 1
buildTyped target, info, FieldAt, typeToIr(c.m, arrType):
copyTree target, tmp
target.addImmediateVal info, 0 # (len, p)-pair so len is at index 0
of tyArray:
let t = typeToIr(c.m, arrType)
target.addImmediateVal info, 0
buildTyped target, info, AddrOf, elemType:
buildTyped target, info, ArrayAt, t:
copyTree target, tmp
target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0
target.addImmediateVal info, 1
target.addIntVal(c.lit.numbers, info, c.m.nativeIntId, toInt lengthOrd(c.config, arrType))
else:
raiseAssert "addAddrOfFirstElem: " & typeToString(typ)
proc genToOpenArrayConv(c: var ProcCon; arg: PNode; d: var Value; flags: GenFlags; destType: PType) =
let info = toLineInfo(c, arg.info)
let tmp = c.genx(arg, flags)
let arrType = destType.skipTypes(abstractVar)
template body(target) =
buildTyped target, info, ObjConstr, typeToIr(c.m, arrType):
c.addAddrOfFirstElem target, info, tmp, arg.typ
valueIntoDest c, info, d, arrType, body
freeTemp c, tmp
proc genConv(c: var ProcCon; n, arg: PNode; d: var Value; flags: GenFlags; opc: Opcode) =
let targetType = n.typ.skipTypes({tyDistinct})
let argType = arg.typ.skipTypes({tyDistinct})
if sameBackendType(targetType, argType) or (
argType.kind == tyProc and targetType.kind == argType.kind):
# don't do anything for lambda lifting conversions:
gen c, arg, d
return
if opc != Cast and targetType.skipTypes({tyVar, tyLent}).kind in {tyOpenArray, tyVarargs} and
argType.skipTypes({tyVar, tyLent}).kind notin {tyOpenArray, tyVarargs}:
genToOpenArrayConv c, arg, d, flags, n.typ
return
let info = toLineInfo(c, n.info)
let tmp = c.genx(arg, flags)
template body(target) =
buildTyped target, info, opc, typeToIr(c.m, n.typ):
if opc == CheckedObjConv:
target.addLabel info, CheckedGoto, c.exitLabel
copyTree target, tmp
valueIntoDest c, info, d, n.typ, body
freeTemp c, tmp
proc genObjOrTupleConstr(c: var ProcCon; n: PNode; d: var Value; t: PType) =
# XXX x = (x.old, 22) produces wrong code ... stupid self assignments
let info = toLineInfo(c, n.info)
template body(target) =
buildTyped target, info, ObjConstr, typeToIr(c.m, t):
for i in ord(n.kind == nkObjConstr)..<n.len:
let it = n[i]
if it.kind == nkExprColonExpr:
genField(c, it[0], Value target)
let tmp = c.genx(it[1])
copyTree target, tmp
c.freeTemp(tmp)
else:
let tmp = c.genx(it)
target.addImmediateVal info, i
copyTree target, tmp
c.freeTemp(tmp)
if isException(t):
target.addImmediateVal info, 1 # "name" field is at position after the "parent". See system.nim
target.addStrVal c.lit.strings, info, t.skipTypes(abstractInst).sym.name.s
constrIntoDest c, info, d, t, body
proc genRefObjConstr(c: var ProcCon; n: PNode; d: var Value) =
if isEmpty(d): d = getTemp(c, n)
let info = toLineInfo(c, n.info)
let refType = n.typ.skipTypes(abstractInstOwned)
let objType = refType.lastSon
rawGenNew(c, d, refType, n.info, needsInit = nfAllFieldsSet notin n.flags)
var deref = default(Value)
deref.buildTyped info, Load, typeToIr(c.m, objType):
deref.Tree.copyTree d
genObjOrTupleConstr c, n, deref, objType
proc genSeqConstr(c: var ProcCon; n: PNode; d: var Value) =
if isEmpty(d): d = getTemp(c, n)
let info = toLineInfo(c, n.info)
let seqtype = skipTypes(n.typ, abstractVarRange)
let baseType = seqtype.lastSon
var b = default(Value)
b.addIntVal c.lit.numbers, info, c.m.nativeIntId, n.len
genNewSeqPayload(c, info, d, b, seqtype)
for i in 0..<n.len:
var dd = default(Value)
buildTyped dd, info, DerefArrayAt, seqPayloadPtrType(c.m.types, c.m.nirm.types, seqtype)[1]:
buildTyped dd, info, FieldAt, typeToIr(c.m, seqtype):
copyTree Tree(dd), d
dd.addImmediateVal info, 1 # (len, p)-pair
dd.addIntVal c.lit.numbers, info, c.m.nativeIntId, i
gen(c, n[i], dd)
freeTemp c, d
proc genArrayConstr(c: var ProcCon; n: PNode, d: var Value) =
let seqType = n.typ.skipTypes(abstractVar-{tyTypeDesc})
if seqType.kind == tySequence:
genSeqConstr(c, n, d)
return
let info = toLineInfo(c, n.info)
template body(target) =
buildTyped target, info, ArrayConstr, typeToIr(c.m, n.typ):
for i in 0..<n.len:
let tmp = c.genx(n[i])
copyTree target, tmp
c.freeTemp(tmp)
constrIntoDest c, info, d, n.typ, body
proc genAsgn2(c: var ProcCon; a, b: PNode) =
assert a != nil
assert b != nil
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
#assert(a[0].kind == nkSym) can happen for transformed vars
if a.kind == nkVarTuple:
c.gen(lowerTupleUnpacking(c.m.graph, a, c.m.idgen, c.prc))
else:
var vn = a[0]
if vn.kind == nkPragmaExpr: vn = vn[0]
if vn.kind == nkSym:
let s = vn.sym
if s.kind == skConst:
if dontInlineConstant(n, s.astdef):
let symId = toSymId(c, s)
c.m.nirm.symnames[symId] = c.lit.strings.getOrIncl(s.name.s)
let val = c.genx(s.astdef)
let info = toLineInfo(c, a.info)
buildTyped c.code, info, SummonConst, typeToIr(c.m, s.typ):
c.code.addSymDef info, symId
c.code.copyTree val
freeTemp c, val
else:
var opc: Opcode
if sfThread in s.flags:
opc = SummonThreadLocal
elif sfGlobal in s.flags:
opc = SummonGlobal
else:
opc = Summon
#assert t.int >= 0, typeToString(s.typ) & (c.config $ n.info)
let symId = toSymId(c, s)
c.code.addSummon toLineInfo(c, a.info), symId, typeToIr(c.m, s.typ), opc
c.m.nirm.symnames[symId] = c.lit.strings.getOrIncl(s.name.s)
if a[2].kind != nkEmpty:
genAsgn2(c, vn, a[2])
else:
if a[2].kind == nkEmpty:
genAsgn2(c, vn, expandDefault(vn.typ, vn.info))
else:
genAsgn2(c, vn, a[2])
proc genAsgn(c: var ProcCon; n: PNode) =
var d = c.genx(n[0])
c.gen n[1], d
proc convStrToCStr(c: var ProcCon; n: PNode; d: var Value) =
genUnaryCp(c, n, d, "nimToCStringConv", argAt = 0)
proc convCStrToStr(c: var ProcCon; n: PNode; d: var Value) =
genUnaryCp(c, n, d, "cstrToNimstr", argAt = 0)
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))
target.addImmediateVal info, s.itemId.item.int
valueIntoDest c, info, d, s.typ, body
else:
template body(target) =
target.addSymUse info, toSymId(c, s)
valueIntoDest c, info, d, s.typ, body
proc genSym(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) =
let s = n.sym
case s.kind
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 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):
if not c.m.pendingProcsAsSet.containsOrIncl(s.itemId):
c.m.pendingProcs.add s
genRdVar(c, n, d, flags)
of skEnumField:
let info = toLineInfo(c, n.info)
template body(target) =
target.addIntVal c.lit.numbers, info, typeToIr(c.m, n.typ), s.position
valueIntoDest c, info, d, n.typ, body
else:
localError(c.config, n.info, "cannot generate code for: " & s.name.s)
proc genNumericLit(c: var ProcCon; n: PNode; d: var Value; bits: int64) =
let info = toLineInfo(c, n.info)
template body(target) =
target.addIntVal c.lit.numbers, info, typeToIr(c.m, n.typ), bits
valueIntoDest c, info, d, n.typ, body
proc genStringLit(c: var ProcCon; n: PNode; d: var Value) =
let info = toLineInfo(c, n.info)
template body(target) =
target.addStrVal c.lit.strings, info, n.strVal
valueIntoDest c, info, d, n.typ, body
proc genNilLit(c: var ProcCon; n: PNode; d: var Value) =
let info = toLineInfo(c, n.info)
template body(target) =
target.addNilVal info, typeToIr(c.m, n.typ)
valueIntoDest c, info, d, n.typ, body
proc genRangeCheck(c: var ProcCon; n: PNode; d: var Value) =
if optRangeCheck in c.options:
let info = toLineInfo(c, n.info)
let tmp = c.genx n[0]
let a = c.genx n[1]
let b = c.genx n[2]
template body(target) =
buildTyped target, info, CheckedRange, typeToIr(c.m, n.typ):
target.addLabel info, CheckedGoto, c.exitLabel
copyTree target, tmp
copyTree target, a
copyTree target, b
valueIntoDest c, info, d, n.typ, body
freeTemp c, tmp
freeTemp c, a
freeTemp c, b
else:
gen c, n[0], d
proc genArrAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
let arrayType = n[0].typ.skipTypes(abstractVarRange-{tyTypeDesc})
let arrayKind = arrayType.kind
let info = toLineInfo(c, n.info)
case arrayKind
of tyString:
let a = genx(c, n[0], flags)
let b = genIndexCheck(c, n[1], a, ForStr, arrayType)
let t = typeToIr(c.m, n.typ)
template body(target) =
buildTyped target, info, DerefArrayAt, c.m.strPayloadId[1]:
buildTyped target, info, FieldAt, typeToIr(c.m, arrayType):
copyTree target, a
target.addImmediateVal info, 1 # (len, p)-pair
copyTree target, b
intoDest d, info, t, body
freeTemp c, b
freeTemp c, a
of tyCstring, tyPtr, tyUncheckedArray:
let a = genx(c, n[0], flags)
let b = genx(c, n[1])
template body(target) =
buildTyped target, info, DerefArrayAt, typeToIr(c.m, arrayType):
copyTree target, a
copyTree target, b
valueIntoDest c, info, d, n.typ, body
freeTemp c, b
freeTemp c, a
of tyTuple:
let a = genx(c, n[0], flags)
let b = int n[1].intVal
template body(target) =
buildTyped target, info, FieldAt, typeToIr(c.m, arrayType):
copyTree target, a
target.addImmediateVal info, b
valueIntoDest c, info, d, n.typ, body
freeTemp c, a
of tyOpenArray, tyVarargs:
let a = genx(c, n[0], flags)
let b = genIndexCheck(c, n[1], a, ForOpenArray, arrayType)
let t = typeToIr(c.m, n.typ)
template body(target) =
buildTyped target, info, DerefArrayAt, openArrayPayloadType(c.m.types, c.m.nirm.types, n[0].typ):
buildTyped target, info, FieldAt, typeToIr(c.m, arrayType):
copyTree target, a
target.addImmediateVal info, 0 # (p, len)-pair
copyTree target, b
intoDest d, info, t, body
freeTemp c, b
freeTemp c, a
of tyArray:
let a = genx(c, n[0], flags)
var b = default(Value)
genIndex(c, n[1], n[0].typ, b)
template body(target) =
buildTyped target, info, ArrayAt, typeToIr(c.m, arrayType):
copyTree target, a
copyTree target, b
valueIntoDest c, info, d, n.typ, body
freeTemp c, b
freeTemp c, a
of tySequence:
let a = genx(c, n[0], flags)
let b = genIndexCheck(c, n[1], a, ForSeq, arrayType)
let t = typeToIr(c.m, n.typ)
template body(target) =
buildTyped target, info, DerefArrayAt, seqPayloadPtrType(c.m.types, c.m.nirm.types, n[0].typ)[1]:
buildTyped target, info, FieldAt, t:
copyTree target, a
target.addImmediateVal info, 1 # (len, p)-pair
copyTree target, b
intoDest d, info, t, body
freeTemp c, b
freeTemp c, a
else:
localError c.config, n.info, "invalid type for nkBracketExpr: " & $arrayKind
proc genObjAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
let info = toLineInfo(c, n.info)
var n0 = n[0]
var opc = FieldAt
if n0.kind == nkDotExpr:
# obj[].a --> DerefFieldAt instead of FieldAt:
n0 = n[0]
opc = DerefFieldAt
let a = genx(c, n0, flags)
template body(target) =
buildTyped target, info, opc, typeToIr(c.m, n0.typ):
copyTree target, a
genField c, n[1], Value(target)
valueIntoDest c, info, d, n.typ, body
freeTemp c, a
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]
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])
let tmp = allocTemp(c, t)
c.code.addSummon toLineInfo(c, params.info), tmp, t, SummonResult
for i in 1..<params.len:
let s = params[i].sym
if not isCompileTimeOnly(s.typ):
let t = typeToIr(c.m, s.typ)
assert t.int != -1, typeToString(s.typ)
let symId = toSymId(c, s)
c.code.addSummon toLineInfo(c, params[i].info), symId, t, SummonParam
c.m.nirm.symnames[symId] = c.lit.strings.getOrIncl(s.name.s)
proc addCallConv(c: var ProcCon; info: PackedLineInfo; callConv: TCallingConvention) =
template ann(s: untyped) = c.code.addPragmaId info, s
case callConv
of ccNimCall, ccFastCall, ccClosure: ann FastCall
of ccStdCall: ann StdCall
of ccCDecl: ann CDeclCall
of ccSafeCall: ann SafeCall
of ccSysCall: ann SysCall
of ccInline: ann InlineCall
of ccNoInline: ann NoinlineCall
of ccThisCall: ann ThisCall
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
if cOuter.m.inProc > 0:
if not cOuter.m.pendingProcsAsSet.containsOrIncl(prc.itemId):
cOuter.m.pendingProcs.add prc
return
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 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:
c.code.addPragmaId info, CoreName
c.code.addStrVal c.lit.strings, info, prc.name.s
if {sfImportc, sfExportc} * prc.flags != {}:
build c.code, info, PragmaPair:
c.code.addPragmaId info, ExternName
c.code.addStrVal c.lit.strings, info, prc.loc.r
if sfImportc in prc.flags:
if lfHeader in prc. loc.flags:
assert(prc. annex != nil)
let str = getStr(prc. annex.path)
build c.code, info, PragmaPair:
c.code.addPragmaId info, HeaderImport
c.code.addStrVal c.lit.strings, info, str
elif lfDynamicLib in prc. loc.flags:
assert(prc. annex != nil)
let str = getStr(prc. annex.path)
build c.code, info, PragmaPair:
c.code.addPragmaId info, DllImport
c.code.addStrVal c.lit.strings, info, str
elif sfExportc in prc.flags:
if lfDynamicLib in prc. loc.flags:
c.code.addPragmaId info, DllExport
else:
c.code.addPragmaId info, ObjExport
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
proc genProc(cOuter: var ProcCon; n: PNode) =
if n.len == 0 or n[namePos].kind != nkSym: return
let prc = n[namePos].sym
if isGenericRoutineStrict(prc) or isCompileTimeProc(prc) or sfForward in prc.flags: return
genProc cOuter, prc
proc genClosureCall(c: var ProcCon; n: PNode; d: var Value) =
let typ = skipTypes(n[0].typ, abstractInstOwned)
if tfIterator in typ.flags:
const PatIter = "$1.ClP_0($3, $1.ClE_0)" # we know the env exists
else:
const PatProc = "$1.ClE_0? $1.ClP_0($3, $1.ClE_0):(($4)($1.ClP_0))($2)"
proc genComplexCall(c: var ProcCon; n: PNode; d: var Value) =
if n[0].typ != nil and n[0].typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).callConv == ccClosure:
# XXX genClosureCall p, n, d
genCall c, n, d
else:
genCall c, n, d
proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) =
when defined(nimCompilerStacktraceHints):
setFrameMsg c.config$n.info & " " & $n.kind & " " & $flags
case n.kind
of nkSym: genSym(c, n, d, flags)
of nkCallKinds:
if n[0].kind == nkSym:
let s = n[0].sym
if s.magic != mNone:
genMagic(c, n, d, s.magic)
elif s.kind == skMethod:
localError(c.config, n.info, "cannot call method " & s.name.s &
" at compile time")
else:
genComplexCall(c, n, d)
else:
genComplexCall(c, n, d)
of nkCharLit..nkInt64Lit, nkUIntLit..nkUInt64Lit:
genNumericLit(c, n, d, n.intVal)
of nkFloatLit..nkFloat128Lit:
genNumericLit(c, n, d, cast[int64](n.floatVal))
of nkStrLit..nkTripleStrLit:
genStringLit(c, n, d)
of nkNilLit:
if not n.typ.isEmptyType: genNilLit(c, n, d)
else: unused(c, n, d)
of nkAsgn, nkFastAsgn, nkSinkAsgn:
unused(c, n, d)
genAsgn(c, n)
of nkDotExpr: genObjAccess(c, n, d, flags)
of nkCheckedFieldExpr: genObjAccess(c, n[0], d, flags)
of nkBracketExpr: genArrAccess(c, n, d, flags)
of nkDerefExpr, nkHiddenDeref: genDeref(c, n, d, flags)
of nkAddr, nkHiddenAddr: genAddr(c, n, d, flags)
of nkIfStmt, nkIfExpr: genIf(c, n, d)
of nkWhenStmt:
# This is "when nimvm" node. Chose the first branch.
gen(c, n[0][1], d)
of nkCaseStmt: genCase(c, n, d)
of nkWhileStmt:
unused(c, n, d)
genWhile(c, n)
of nkBlockExpr, nkBlockStmt: genBlock(c, n, d)
of nkReturnStmt: genReturn(c, n)
of nkRaiseStmt: genRaise(c, n)
of nkBreakStmt: genBreak(c, n)
of nkTryStmt, nkHiddenTryStmt: genTry(c, n, d)
of nkStmtList:
#unused(c, n, d)
# XXX Fix this bug properly, lexim triggers it
for x in n: gen(c, x)
of nkStmtListExpr:
for i in 0..<n.len-1: gen(c, n[i])
gen(c, n[^1], d, flags)
of nkPragmaBlock:
gen(c, n.lastSon, d, flags)
of nkDiscardStmt:
unused(c, n, d)
gen(c, n[0], d)
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
genConv(c, n, n[1], d, flags, NumberConv) # misnomer?
of nkObjDownConv:
genConv(c, n, n[0], d, flags, ObjConv)
of nkObjUpConv:
genConv(c, n, n[0], d, flags, CheckedObjConv)
of nkVarSection, nkLetSection, nkConstSection:
unused(c, n, d)
genVarSection(c, n)
of nkLambdaKinds:
#let s = n[namePos].sym
#discard genProc(c, s)
gen(c, newSymNode(n[namePos].sym), d)
of nkChckRangeF, nkChckRange64, nkChckRange:
genRangeCheck(c, n, d)
of declarativeDefs - {nkIteratorDef}:
unused(c, n, d)
genProc(c, n)
of nkEmpty, nkCommentStmt, nkTypeSection, nkPragma,
nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt,
nkMixinStmt, nkBindStmt, nkMacroDef, nkIteratorDef:
unused(c, n, d)
of nkStringToCString: convStrToCStr(c, n, d)
of nkCStringToString: convCStrToStr(c, n, d)
of nkBracket: genArrayConstr(c, n, d)
of nkCurly: genSetConstr(c, n, d)
of nkObjConstr:
if n.typ.skipTypes(abstractInstOwned).kind == tyRef:
genRefObjConstr(c, n, d)
else:
genObjOrTupleConstr(c, n, d, n.typ)
of nkPar, nkClosure, nkTupleConstr:
genObjOrTupleConstr(c, n, d, n.typ)
of nkCast:
genConv(c, n, n[1], d, flags, Cast)
of nkComesFrom:
discard "XXX to implement for better stack traces"
#of nkState: genState(c, n)
#of nkGotoState: genGotoState(c, n)
#of nkBreakState: genBreakState(c, n, d)
else:
localError(c.config, n.info, "cannot generate IR code for " & $n)
proc genPendingProcs(c: var ProcCon) =
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
var d = default(Value)
c.gen(n, d)
unused c, n, d
genPendingProcs c
proc genExpr*(c: var ProcCon; n: PNode, requiresValue = true): int =
result = c.code.len
var d = default(Value)
c.gen(n, d)
genPendingProcs c
if isEmpty d:
if requiresValue:
globalError(c.config, n.info, "VM problem: d register is not set")