# # # 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..>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) # # s = "Hello " & name & ", how do you feel?" & 'z' # # # { # 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.elementType 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.elementType)) 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.elementType)) 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)..= 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.returnType) and not isCompileTimeOnly(prc.typ.returnType): # happens for procs without bodies: let t = typeToIr(c.m, prc.typ.returnType) let tmp = allocTemp(c, t) c.code.addSummon toLineInfo(c, params.info), tmp, t, SummonResult for i in 1.. 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.. 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")