# # # The Nimrod Compiler # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # ## This module implements the code generator for the VM. import unsigned, strutils, ast, astalgo, types, msgs, renderer, vmdef, trees, intsets, rodread, magicsys, options, lowerings from os import splitFile when hasFFI: import evalffi type TGenFlag = enum gfNone, gfAddrOf TGenFlags = set[TGenFlag] proc debugInfo(info: TLineInfo): string = result = info.toFilename.splitFile.name & ":" & $info.line proc codeListing(c: PCtx, result: var string, start=0) = # first iteration: compute all necessary labels: var jumpTargets = initIntSet() for i in start.. < c.code.len: let x = c.code[i] if x.opcode in relativeJumps: jumpTargets.incl(i+x.regBx-wordExcess) # for debugging purposes var i = start while i < c.code.len: if i in jumpTargets: result.addf("L$1:\n", i) let x = c.code[i] let opc = opcode(x) if opc in {opcConv, opcCast}: let y = c.code[i+1] let z = c.code[i+2] result.addf("\t$#\tr$#, r$#, $#, $#", ($opc).substr(3), x.regA, x.regB, c.types[y.regBx-wordExcess].typeToString, c.types[z.regBx-wordExcess].typeToString) inc i, 2 elif opc < firstABxInstr: result.addf("\t$#\tr$#, r$#, r$#", ($opc).substr(3), x.regA, x.regB, x.regC) elif opc in relativeJumps: result.addf("\t$#\tr$#, L$#", ($opc).substr(3), x.regA, i+x.regBx-wordExcess) elif opc in {opcLdConst, opcAsgnConst}: result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, c.constants[x.regBx-wordExcess].renderTree) else: result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, x.regBx-wordExcess) result.add("\t#") result.add(debugInfo(c.debug[i])) result.add("\n") inc i proc echoCode*(c: PCtx, start=0) {.deprecated.} = var buf = "" codeListing(c, buf, start) echo buf proc gABC(ctx: PCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) = ## Takes the registers `b` and `c`, applies the operation `opc` to them, and ## stores the result into register `a` ## The node is needed for debug information assert opc.ord < 255 let ins = (opc.uint32 or (a.uint32 shl 8'u32) or (b.uint32 shl 16'u32) or (c.uint32 shl 24'u32)).TInstr ctx.code.add(ins) ctx.debug.add(n.info) proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) = # Takes the `b` register and the immediate `imm`, appies the operation `opc`, # and stores the output value into `a`. # `imm` is signed and must be within [-127, 128] assert(imm >= -127 and imm <= 128) let ins = (opc.uint32 or (a.uint32 shl 8'u32) or (b.uint32 shl 16'u32) or (imm+byteExcess).uint32 shl 24'u32).TInstr c.code.add(ins) c.debug.add(n.info) proc gABx(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) = # Applies `opc` to `bx` and stores it into register `a` # `bx` must be signed and in the range [-32767, 32768] assert(bx >= -32767 and bx <= 32768) let ins = (opc.uint32 or a.uint32 shl 8'u32 or (bx+wordExcess).uint32 shl 16'u32).TInstr c.code.add(ins) c.debug.add(n.info) proc xjmp(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0): TPosition = #assert opc in {opcJmp, opcFJmp, opcTJmp} result = TPosition(c.code.len) gABx(c, n, opc, a, 0) proc genLabel(c: PCtx): TPosition = result = TPosition(c.code.len) #c.jumpTargets.incl(c.code.len) proc jmpBack(c: PCtx, n: PNode, p = TPosition(0)) = let dist = p.int - c.code.len internalAssert(-0x7fff < dist and dist < 0x7fff) gABx(c, n, opcJmpBack, 0, dist) proc patch(c: PCtx, p: TPosition) = # patch with current index let p = p.int let diff = c.code.len - p #c.jumpTargets.incl(c.code.len) internalAssert(-0x7fff < diff and diff < 0x7fff) let oldInstr = c.code[p] # opcode and regA stay the same: c.code[p] = ((oldInstr.uint32 and 0xffff'u32).uint32 or uint32(diff+wordExcess) shl 16'u32).TInstr proc getSlotKind(t: PType): TSlotKind = case t.skipTypes(abstractRange-{tyTypeDesc}).kind of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64: slotTempInt of tyString, tyCString: slotTempStr of tyFloat..tyFloat128: slotTempFloat else: slotTempComplex const HighRegisterPressure = 40 proc getTemp(c: PCtx; typ: PType): TRegister = let c = c.prc # we prefer the same slot kind here for efficiency. Unfortunately for # discardable return types we may not know the desired type. This can happen # for e.g. mNAdd[Multiple]: let k = if typ.isNil: slotTempComplex else: typ.getSlotKind for i in 0 .. c.maxSlots-1: if c.slots[i].kind == k and not c.slots[i].inUse: c.slots[i].inUse = true return TRegister(i) # if register pressure is high, we re-use more aggressively: if c.maxSlots >= HighRegisterPressure: for i in 0 .. c.maxSlots-1: if not c.slots[i].inUse: c.slots[i] = (inUse: true, kind: k) return TRegister(i) if c.maxSlots >= high(TRegister): internalError("cannot generate code; too many registers required") result = TRegister(c.maxSlots) c.slots[c.maxSlots] = (inUse: true, kind: k) inc c.maxSlots proc freeTemp(c: PCtx; r: TRegister) = let c = c.prc if c.slots[r].kind >= slotSomeTemp: c.slots[r].inUse = false proc getTempRange(c: PCtx; n: int; kind: TSlotKind): TRegister = # if register pressure is high, we re-use more aggressively: let c = c.prc if c.maxSlots >= HighRegisterPressure or c.maxSlots+n >= high(TRegister): for i in 0 .. c.maxSlots-n: if not c.slots[i].inUse: block search: for j in i+1 .. i+n-1: if c.slots[j].inUse: break search result = TRegister(i) for k in result .. result+n-1: c.slots[k] = (inUse: true, kind: kind) return if c.maxSlots+n >= high(TRegister): internalError("cannot generate code; too many registers required") result = TRegister(c.maxSlots) inc c.maxSlots, n for k in result .. result+n-1: c.slots[k] = (inUse: true, kind: kind) proc freeTempRange(c: PCtx; start: TRegister, n: int) = for i in start .. start+n-1: c.freeTemp(TRegister(i)) template withTemp(tmp, typ: expr, body: stmt) {.immediate, dirty.} = var tmp = getTemp(c, typ) body c.freeTemp(tmp) proc popBlock(c: PCtx; oldLen: int) = for f in c.prc.blocks[oldLen].fixups: c.patch(f) c.prc.blocks.setLen(oldLen) template withBlock(labl: PSym; body: stmt) {.immediate, dirty.} = var oldLen {.gensym.} = c.prc.blocks.len c.prc.blocks.add TBlock(label: labl, fixups: @[]) body popBlock(c, oldLen) proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) proc gen(c: PCtx; n: PNode; dest: TRegister; flags: TGenFlags = {}) = var d: TDest = dest gen(c, n, d, flags) internalAssert d == dest proc gen(c: PCtx; n: PNode; flags: TGenFlags = {}) = var tmp: TDest = -1 gen(c, n, tmp, flags) #if n.typ.isEmptyType: InternalAssert tmp < 0 proc genx(c: PCtx; n: PNode; flags: TGenFlags = {}): TRegister = var tmp: TDest = -1 gen(c, n, tmp, flags) internalAssert tmp >= 0 result = TRegister(tmp) proc clearDest(c: PCtx; n: PNode; dest: var TDest) {.inline.} = # stmt is different from 'void' in meta programming contexts. # So we only set dest to -1 if 'void': if dest >= 0 and (n.typ.isNil or n.typ.kind == tyEmpty): c.freeTemp(dest) dest = -1 proc isNotOpr(n: PNode): bool = n.kind in nkCallKinds and n.sons[0].kind == nkSym and n.sons[0].sym.magic == mNot proc isTrue(n: PNode): bool = n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or n.kind == nkIntLit and n.intVal != 0 proc genWhile(c: PCtx; n: PNode) = # L1: # cond, tmp # fjmp tmp, L2 # body # jmp L1 # L2: let L1 = c.genLabel withBlock(nil): if isTrue(n.sons[0]): c.gen(n.sons[1]) c.jmpBack(n, L1) elif isNotOpr(n.sons[0]): var tmp = c.genx(n.sons[0].sons[1]) let L2 = c.xjmp(n, opcTJmp, tmp) c.freeTemp(tmp) c.gen(n.sons[1]) c.jmpBack(n, L1) c.patch(L2) else: var tmp = c.genx(n.sons[0]) let L2 = c.xjmp(n, opcFJmp, tmp) c.freeTemp(tmp) c.gen(n.sons[1]) c.jmpBack(n, L1) c.patch(L2) proc genBlock(c: PCtx; n: PNode; dest: var TDest) = withBlock(n.sons[0].sym): c.gen(n.sons[1], dest) c.clearDest(n, dest) proc genBreak(c: PCtx; n: PNode) = let L1 = c.xjmp(n, opcJmp) if n.sons[0].kind == nkSym: #echo cast[int](n.sons[0].sym) for i in countdown(c.prc.blocks.len-1, 0): if c.prc.blocks[i].label == n.sons[0].sym: c.prc.blocks[i].fixups.add L1 return internalError(n.info, "cannot find 'break' target") else: c.prc.blocks[c.prc.blocks.high].fixups.add L1 proc genIf(c: PCtx, n: PNode; dest: var TDest) = # if (!expr1) goto L1; # thenPart # goto LEnd # L1: # if (!expr2) goto L2; # thenPart2 # goto LEnd # L2: # elsePart # Lend: if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ) var endings: seq[TPosition] = @[] for i in countup(0, len(n) - 1): var it = n.sons[i] if it.len == 2: withTemp(tmp, it.sons[0].typ): var elsePos: TPosition if isNotOpr(it.sons[0]): c.gen(it.sons[0].sons[1], tmp) elsePos = c.xjmp(it.sons[0].sons[1], opcTJmp, tmp) # if true else: c.gen(it.sons[0], tmp) elsePos = c.xjmp(it.sons[0], opcFJmp, tmp) # if false c.clearDest(n, dest) c.gen(it.sons[1], dest) # then part if i < sonsLen(n)-1: endings.add(c.xjmp(it.sons[1], opcJmp, 0)) c.patch(elsePos) else: c.clearDest(n, dest) c.gen(it.sons[0], dest) for endPos in endings: c.patch(endPos) c.clearDest(n, dest) proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) = # asgn dest, a # tjmp|fjmp L1 # asgn dest, b # L1: if dest < 0: dest = getTemp(c, n.typ) c.gen(n.sons[1], dest) let L1 = c.xjmp(n, opc, dest) c.gen(n.sons[2], dest) c.patch(L1) proc canonValue*(n: PNode): PNode = result = n proc rawGenLiteral(c: PCtx; n: PNode): int = result = c.constants.len assert(n.kind != nkCall) c.constants.add n.canonValue internalAssert result < 0x7fff proc sameConstant*(a, b: PNode): bool = result = false if a == b: result = true elif a != nil and b != nil and a.kind == b.kind: case a.kind of nkSym: result = a.sym == b.sym of nkIdent: result = a.ident.id == b.ident.id of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal of nkType: result = a.typ == b.typ of nkEmpty, nkNilLit: result = true else: if sonsLen(a) == sonsLen(b): for i in countup(0, sonsLen(a) - 1): if not sameConstant(a.sons[i], b.sons[i]): return result = true proc genLiteral(c: PCtx; n: PNode): int = # types do not matter here: for i in 0 .. = 0: #debug(n) internalError(n.info, "not unused") proc genCase(c: PCtx; n: PNode; dest: var TDest) = # if (!expr1) goto L1; # thenPart # goto LEnd # L1: # if (!expr2) goto L2; # thenPart2 # goto LEnd # L2: # elsePart # Lend: if not isEmptyType(n.typ): if dest < 0: dest = getTemp(c, n.typ) else: unused(n, dest) var endings: seq[TPosition] = @[] withTemp(tmp, n.sons[0].typ): c.gen(n.sons[0], tmp) # branch tmp, codeIdx # fjmp elseLabel for i in 1 .. = fntyp.len: internalAssert tfVarargs in fntyp.flags c.gABx(n, opcSetType, r, c.genType(n.sons[i].typ)) if dest < 0: c.gABC(n, opcIndCall, 0, x, n.len) else: c.gABC(n, opcIndCallAsgn, dest, x, n.len) c.freeTempRange(x, n.len) template isGlobal(s: PSym): bool = sfGlobal in s.flags and s.kind != skForVar proc needsAsgnPatch(n: PNode): bool = n.kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr, nkDerefExpr, nkHiddenDeref} or (n.kind == nkSym and n.sym.isGlobal) proc genField(n: PNode): TRegister = if n.kind != nkSym or n.sym.kind != skField: internalError(n.info, "no field symbol") let s = n.sym if s.position > high(result): internalError(n.info, "too large offset! cannot generate code for: " & s.name.s) result = s.position proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) = case le.kind of nkBracketExpr: let dest = c.genx(le.sons[0], {gfAddrOf}) let idx = c.genx(le.sons[1]) c.gABC(le, opcWrArr, dest, idx, value) c.freeTemp(dest) c.freeTemp(idx) of nkDotExpr, nkCheckedFieldExpr: # XXX field checks here let left = if le.kind == nkDotExpr: le else: le.sons[0] let dest = c.genx(left.sons[0], {gfAddrOf}) let idx = genField(left.sons[1]) c.gABC(left, opcWrObj, dest, idx, value) c.freeTemp(dest) of nkDerefExpr, nkHiddenDeref: let dest = c.genx(le.sons[0], {gfAddrOf}) c.gABC(le, opcWrDeref, dest, value) c.freeTemp(dest) of nkSym: if le.sym.isGlobal: let dest = c.genx(le, {gfAddrOf}) c.gABC(le, opcWrDeref, dest, value) c.freeTemp(dest) else: discard proc genNew(c: PCtx; n: PNode) = let dest = if needsAsgnPatch(n.sons[1]): c.getTemp(n.sons[1].typ) else: c.genx(n.sons[1]) # we use the ref's base type here as the VM conflates 'ref object' # and 'object' since internally we already have a pointer. c.gABx(n, opcNew, dest, c.genType(n.sons[1].typ.skipTypes(abstractVar-{tyTypeDesc}).sons[0])) c.genAsgnPatch(n.sons[1], dest) c.freeTemp(dest) proc genNewSeq(c: PCtx; n: PNode) = let dest = if needsAsgnPatch(n.sons[1]): c.getTemp(n.sons[1].typ) else: c.genx(n.sons[1]) let tmp = c.genx(n.sons[2]) c.gABx(n, opcNewSeq, dest, c.genType(n.sons[1].typ.skipTypes( abstractVar-{tyTypeDesc}))) c.gABx(n, opcNewSeq, tmp, 0) c.freeTemp(tmp) c.genAsgnPatch(n.sons[1], dest) c.freeTemp(dest) proc genUnaryABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = let tmp = c.genx(n.sons[1]) if dest < 0: dest = c.getTemp(n.typ) c.gABC(n, opc, dest, tmp) c.freeTemp(tmp) proc genUnaryABI(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = let tmp = c.genx(n.sons[1]) if dest < 0: dest = c.getTemp(n.typ) c.gABI(n, opc, dest, tmp, 0) c.freeTemp(tmp) proc genBinaryABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = let tmp = c.genx(n.sons[1]) tmp2 = c.genx(n.sons[2]) if dest < 0: dest = c.getTemp(n.typ) c.gABC(n, opc, dest, tmp, tmp2) c.freeTemp(tmp) c.freeTemp(tmp2) proc genNarrow(c: PCtx; n: PNode; dest: TDest) = let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) # uint is uint64 in the VM, we we only need to mask the result for # other unsigned types: if t.kind in {tyUInt8..tyUInt32}: c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) elif t.kind in {tyInt8..tyInt32}: c.gABC(n, opcNarrowS, dest, TRegister(t.size*8)) proc genNarrowU(c: PCtx; n: PNode; dest: TDest) = let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) # uint is uint64 in the VM, we we only need to mask the result for # other unsigned types: if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32}: c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) proc genBinaryABCnarrow(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = genBinaryABC(c, n, dest, opc) genNarrow(c, n, dest) proc genBinaryABCnarrowU(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = genBinaryABC(c, n, dest, opc) genNarrowU(c, n, dest) proc genSetType(c: PCtx; n: PNode; dest: TRegister) = let t = skipTypes(n.typ, abstractInst-{tyTypeDesc}) if t.kind == tySet: c.gABx(n, opcSetType, dest, c.genType(t)) proc genBinarySet(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = let tmp = c.genx(n.sons[1]) tmp2 = c.genx(n.sons[2]) if dest < 0: dest = c.getTemp(n.typ) c.genSetType(n.sons[1], tmp) c.genSetType(n.sons[2], tmp2) c.gABC(n, opc, dest, tmp, tmp2) c.freeTemp(tmp) c.freeTemp(tmp2) proc genBinaryStmt(c: PCtx; n: PNode; opc: TOpcode) = let dest = c.genx(n.sons[1]) tmp = c.genx(n.sons[2]) c.gABC(n, opc, dest, tmp, 0) c.freeTemp(tmp) proc genBinaryStmtVar(c: PCtx; n: PNode; opc: TOpcode) = let dest = c.genx(n.sons[1], {gfAddrOf}) tmp = c.genx(n.sons[2]) c.gABC(n, opc, dest, tmp, 0) #c.genAsgnPatch(n.sons[1], dest) c.freeTemp(tmp) proc genUnaryStmt(c: PCtx; n: PNode; opc: TOpcode) = let tmp = c.genx(n.sons[1]) c.gABC(n, opc, tmp, 0, 0) c.freeTemp(tmp) proc genVarargsABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = if dest < 0: dest = getTemp(c, n.typ) var x = c.getTempRange(n.len-1, slotTempStr) for i in 1..n.len-1: var r: TRegister = x+i-1 c.gen(n.sons[i], r) c.gABC(n, opc, dest, x, n.len-1) c.freeTempRange(x, n.len) proc isInt8Lit(n: PNode): bool = if n.kind in {nkCharLit..nkUInt64Lit}: result = n.intVal >= low(int8) and n.intVal <= high(int8) proc isInt16Lit(n: PNode): bool = if n.kind in {nkCharLit..nkUInt64Lit}: result = n.intVal >= low(int16) and n.intVal <= high(int16) proc genAddSubInt(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = if n.sons[2].isInt8Lit: let tmp = c.genx(n.sons[1]) if dest < 0: dest = c.getTemp(n.typ) c.gABI(n, succ(opc), dest, tmp, n.sons[2].intVal) c.freeTemp(tmp) else: genBinaryABC(c, n, dest, opc) c.genNarrow(n, dest) proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) = let tmp = c.genx(arg) if dest < 0: dest = c.getTemp(n.typ) c.gABC(n, opc, dest, tmp) c.gABx(n, opc, 0, genType(c, n.typ)) c.gABx(n, opc, 0, genType(c, arg.typ)) c.freeTemp(tmp) proc genCard(c: PCtx; n: PNode; dest: var TDest) = let tmp = c.genx(n.sons[1]) if dest < 0: dest = c.getTemp(n.typ) c.genSetType(n.sons[1], tmp) c.gABC(n, opcCard, dest, tmp) c.freeTemp(tmp) proc genMagic(c: PCtx; n: PNode; dest: var TDest) = let m = n.sons[0].sym.magic case m of mAnd: c.genAndOr(n, opcFJmp, dest) of mOr: c.genAndOr(n, opcTJmp, dest) of mUnaryLt: let tmp = c.genx(n.sons[1]) if dest < 0: dest = c.getTemp(n.typ) c.gABI(n, opcSubImmInt, dest, tmp, 1) c.freeTemp(tmp) of mPred, mSubI, mSubI64: c.genAddSubInt(n, dest, opcSubInt) of mSucc, mAddI, mAddI64: c.genAddSubInt(n, dest, opcAddInt) of mInc, mDec: unused(n, dest) let opc = if m == mInc: opcAddInt else: opcSubInt let d = c.genx(n.sons[1]) if n.sons[2].isInt8Lit: c.gABI(n, succ(opc), d, d, n.sons[2].intVal) else: let tmp = c.genx(n.sons[2]) c.gABC(n, opc, d, d, tmp) c.freeTemp(tmp) c.genNarrow(n.sons[1], d) c.genAsgnPatch(n.sons[1], d) c.freeTemp(d) of mOrd, mChr, mArrToSeq: c.gen(n.sons[1], dest) of mNew, mNewFinalize: unused(n, dest) c.genNew(n) of mNewSeq: unused(n, dest) c.genNewSeq(n) of mNewString: genUnaryABC(c, n, dest, opcNewStr) # XXX buggy of mNewStringOfCap: # we ignore the 'cap' argument and translate it as 'newString(0)'. # eval n.sons[1] for possible side effects: var tmp = c.genx(n.sons[1]) c.gABx(n, opcLdImmInt, tmp, 0) if dest < 0: dest = c.getTemp(n.typ) c.gABC(n, opcNewStr, dest, tmp) c.freeTemp(tmp) # XXX buggy of mLengthOpenArray, mLengthArray, mLengthSeq: genUnaryABI(c, n, dest, opcLenSeq) of mLengthStr: genUnaryABI(c, n, dest, opcLenStr) of mIncl, mExcl: unused(n, dest) var d = c.genx(n.sons[1]) var tmp = c.genx(n.sons[2]) c.genSetType(n.sons[1], d) c.gABC(n, if m == mIncl: opcIncl else: opcExcl, d, tmp) c.freeTemp(d) c.freeTemp(tmp) of mCard: genCard(c, n, dest) of mMulI, mMulI64: genBinaryABCnarrow(c, n, dest, opcMulInt) of mDivI, mDivI64: genBinaryABCnarrow(c, n, dest, opcDivInt) of mModI, mModI64: genBinaryABCnarrow(c, n, dest, opcModInt) of mAddF64: genBinaryABC(c, n, dest, opcAddFloat) of mSubF64: genBinaryABC(c, n, dest, opcSubFloat) of mMulF64: genBinaryABC(c, n, dest, opcMulFloat) of mDivF64: genBinaryABC(c, n, dest, opcDivFloat) of mShrI, mShrI64: genBinaryABCnarrowU(c, n, dest, opcShrInt) of mShlI, mShlI64: genBinaryABCnarrowU(c, n, dest, opcShlInt) of mBitandI, mBitandI64: genBinaryABCnarrowU(c, n, dest, opcBitandInt) of mBitorI, mBitorI64: genBinaryABCnarrowU(c, n, dest, opcBitorInt) of mBitxorI, mBitxorI64: genBinaryABCnarrowU(c, n, dest, opcBitxorInt) of mAddU: genBinaryABCnarrowU(c, n, dest, opcAddu) of mSubU: genBinaryABCnarrowU(c, n, dest, opcSubu) of mMulU: genBinaryABCnarrowU(c, n, dest, opcMulu) of mDivU: genBinaryABCnarrowU(c, n, dest, opcDivu) of mModU: genBinaryABCnarrowU(c, n, dest, opcModu) of mEqI, mEqI64, mEqB, mEqEnum, mEqCh: genBinaryABC(c, n, dest, opcEqInt) of mLeI, mLeI64, mLeEnum, mLeCh, mLeB: genBinaryABC(c, n, dest, opcLeInt) of mLtI, mLtI64, mLtEnum, mLtCh, mLtB: genBinaryABC(c, n, dest, opcLtInt) of mEqF64: genBinaryABC(c, n, dest, opcEqFloat) of mLeF64: genBinaryABC(c, n, dest, opcLeFloat) of mLtF64: genBinaryABC(c, n, dest, opcLtFloat) of mLePtr, mLeU, mLeU64: genBinaryABC(c, n, dest, opcLeu) of mLtPtr, mLtU, mLtU64: genBinaryABC(c, n, dest, opcLtu) of mEqProc, mEqRef, mEqUntracedRef, mEqCString: genBinaryABC(c, n, dest, opcEqRef) of mXor: genBinaryABCnarrowU(c, n, dest, opcXor) of mNot: genUnaryABC(c, n, dest, opcNot) of mUnaryMinusI, mUnaryMinusI64: genUnaryABC(c, n, dest, opcUnaryMinusInt) genNarrow(c, n, dest) of mUnaryMinusF64: genUnaryABC(c, n, dest, opcUnaryMinusFloat) of mUnaryPlusI, mUnaryPlusI64, mUnaryPlusF64: gen(c, n.sons[1], dest) of mBitnotI, mBitnotI64: genUnaryABC(c, n, dest, opcBitnotInt) genNarrowU(c, n, dest) of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32, mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr: genConv(c, n, n.sons[1], dest) of mEqStr: genBinaryABC(c, n, dest, opcEqStr) of mLeStr: genBinaryABC(c, n, dest, opcLeStr) of mLtStr: genBinaryABC(c, n, dest, opcLtStr) of mEqSet: genBinarySet(c, n, dest, opcEqSet) of mLeSet: genBinarySet(c, n, dest, opcLeSet) of mLtSet: genBinarySet(c, n, dest, opcLtSet) of mMulSet: genBinarySet(c, n, dest, opcMulSet) of mPlusSet: genBinarySet(c, n, dest, opcPlusSet) of mMinusSet: genBinarySet(c, n, dest, opcMinusSet) of mSymDiffSet: genBinarySet(c, n, dest, opcSymdiffSet) of mConStrStr: genVarargsABC(c, n, dest, opcConcatStr) of mInSet: genBinarySet(c, n, dest, opcContainsSet) of mRepr: genUnaryABC(c, n, dest, opcRepr) of mExit: unused(n, dest) var tmp = c.genx(n.sons[1]) c.gABC(n, opcQuit, tmp) c.freeTemp(tmp) of mSetLengthStr, mSetLengthSeq: unused(n, dest) var d = c.genx(n.sons[1]) var tmp = c.genx(n.sons[2]) c.gABC(n, if m == mSetLengthStr: opcSetLenStr else: opcSetLenSeq, d, tmp) c.genAsgnPatch(n.sons[1], d) c.freeTemp(tmp) of mSwap: unused(n, dest) var d = c.genx(n.sons[1]) var tmp = c.genx(n.sons[2]) c.gABC(n, opcSwap, d, tmp) c.freeTemp(tmp) of mIsNil: genUnaryABC(c, n, dest, opcIsNil) of mCopyStr: if dest < 0: dest = c.getTemp(n.typ) var tmp1 = c.genx(n.sons[1]) tmp2 = c.genx(n.sons[2]) tmp3 = c.getTemp(n.sons[2].typ) c.gABC(n, opcLenStr, tmp3, tmp1) c.gABC(n, opcSubStr, dest, tmp1, tmp2) c.gABC(n, opcSubStr, tmp3) c.freeTemp(tmp1) c.freeTemp(tmp2) c.freeTemp(tmp3) of mCopyStrLast: if dest < 0: dest = c.getTemp(n.typ) var tmp1 = c.genx(n.sons[1]) tmp2 = c.genx(n.sons[2]) tmp3 = c.genx(n.sons[3]) c.gABC(n, opcSubStr, dest, tmp1, tmp2) c.gABC(n, opcSubStr, tmp3) c.freeTemp(tmp1) c.freeTemp(tmp2) c.freeTemp(tmp3) of mReset: unused(n, dest) var d = c.genx(n.sons[1]) c.gABC(n, opcReset, d) of mOf, mIs: if dest < 0: dest = c.getTemp(n.typ) var tmp = c.genx(n.sons[1]) var idx = c.getTemp(getSysType(tyInt)) var typ = n.sons[2].typ if m == mOf: typ = typ.skipTypes(abstractPtrs-{tyTypeDesc}) c.gABx(n, opcLdImmInt, idx, c.genType(typ)) c.gABC(n, if m == mOf: opcOf else: opcIs, dest, tmp, idx) c.freeTemp(tmp) c.freeTemp(idx) of mSizeOf: globalError(n.info, errCannotInterpretNodeX, renderTree(n)) of mHigh: if dest < 0: dest = c.getTemp(n.typ) let tmp = c.genx(n.sons[1]) if n.sons[1].typ.skipTypes(abstractVar-{tyTypeDesc}).kind == tyString: c.gABI(n, opcLenStr, dest, tmp, 1) else: c.gABI(n, opcLenSeq, dest, tmp, 1) c.freeTemp(tmp) of mEcho: unused(n, dest) let x = c.getTempRange(n.len-1, slotTempUnknown) for i in 1.. = slotTempUnknown: gABC(c, n, opcAddrNode, dest, tmp) else: gABC(c, n, opcAddrReg, dest, tmp) c.freeTemp(tmp) proc whichAsgnOpc(n: PNode): TOpcode = case n.typ.skipTypes(abstractRange-{tyTypeDesc}).kind of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64: opcAsgnInt of tyString, tyCString: opcAsgnStr of tyFloat..tyFloat128: opcAsgnFloat of tyRef, tyNil, tyVar: opcAsgnRef else: opcAsgnComplex proc isRef(t: PType): bool = t.skipTypes(abstractRange-{tyTypeDesc}).kind == tyRef proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode = opc proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) = let tmp = c.genx(ri) assert dest >= 0 gABC(c, ri, whichAsgnOpc(ri), dest, tmp) c.freeTemp(tmp) proc setSlot(c: PCtx; v: PSym) = # XXX generate type initialization here? if v.position == 0: if c.prc.maxSlots == 0: c.prc.maxSlots = 1 v.position = c.prc.maxSlots c.prc.slots[v.position] = (inUse: true, kind: if v.kind == skLet: slotFixedLet else: slotFixedVar) inc c.prc.maxSlots proc cannotEval(n: PNode) {.noinline.} = globalError(n.info, errGenerated, "cannot evaluate at compile time: " & n.renderTree) proc isOwnedBy(a, b: PSym): bool = var a = a.owner while a != nil and a.kind != skModule: if a == b: return true a = a.owner proc getOwner(c: PCtx): PSym = result = c.prc.sym if result.isNil: result = c.module proc checkCanEval(c: PCtx; n: PNode) = # we need to ensure that we don't evaluate 'x' here: # proc foo() = var x ... let s = n.sym if s.position == 0: if s.kind in {skVar, skTemp, skLet, skParam, skResult} and not s.isOwnedBy(c.prc.sym) and s.owner != c.module: cannotEval(n) proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = case le.kind of nkBracketExpr: let dest = c.genx(le.sons[0], {gfAddrOf}) let idx = c.genx(le.sons[1]) let tmp = c.genx(ri) if le.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in { tyString, tyCString}: c.gABC(le, opcWrStrIdx, dest, idx, tmp) else: c.gABC(le, opcWrArr, dest, idx, tmp) c.freeTemp(tmp) of nkDotExpr, nkCheckedFieldExpr: # XXX field checks here let left = if le.kind == nkDotExpr: le else: le.sons[0] let dest = c.genx(left.sons[0], {gfAddrOf}) let idx = genField(left.sons[1]) let tmp = c.genx(ri) c.gABC(left, opcWrObj, dest, idx, tmp) c.freeTemp(tmp) of nkDerefExpr, nkHiddenDeref: let dest = c.genx(le.sons[0], {gfAddrOf}) let tmp = c.genx(ri) c.gABC(le, opcWrDeref, dest, tmp) c.freeTemp(tmp) of nkSym: let s = le.sym checkCanEval(c, le) if s.isGlobal: withTemp(tmp, le.typ): c.gen(le, tmp, {gfAddrOf}) let val = c.genx(ri) c.gABC(le, opcWrDeref, tmp, val) c.freeTemp(val) else: if s.kind == skForVar: c.setSlot s internalAssert s.position > 0 or (s.position == 0 and s.kind in {skParam,skResult}) var dest: TRegister = s.position + ord(s.kind == skParam) gen(c, ri, dest) else: let dest = c.genx(le, {gfAddrOf}) genAsgn(c, dest, ri, requiresCopy) proc genLit(c: PCtx; n: PNode; dest: var TDest) = var opc = opcLdConst if dest < 0: dest = c.getTemp(n.typ) elif c.prc.slots[dest].kind == slotFixedVar: opc = opcAsgnConst let lit = genLiteral(c, n) c.gABx(n, opc, dest, lit) proc genTypeLit(c: PCtx; t: PType; dest: var TDest) = var n = newNode(nkType) n.typ = t genLit(c, n, dest) proc importcSym(c: PCtx; info: TLineInfo; s: PSym) = when hasFFI: if allowFFI in c.features: c.globals.add(importcSymbol(s)) s.position = c.globals.len else: localError(info, errGenerated, "VM is not allowed to 'importc'") else: localError(info, errGenerated, "cannot 'importc' variable at compile time") proc getNullValue*(typ: PType, info: TLineInfo): PNode proc genGlobalInit(c: PCtx; n: PNode; s: PSym) = c.globals.add(getNullValue(s.typ, n.info)) s.position = c.globals.len # This is rather hard to support, due to the laziness of the VM code # generator. See tests/compile/tmacro2 for why this is necesary: # var decls{.compileTime.}: seq[PNimrodNode] = @[] let dest = c.getTemp(s.typ) c.gABx(n, opcLdGlobal, dest, s.position) let tmp = c.genx(s.ast) c.gABC(n, opcWrDeref, dest, tmp) c.freeTemp(dest) c.freeTemp(tmp) proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = let s = n.sym if s.isGlobal: if sfCompileTime in s.flags or c.mode == emRepl: discard elif s.position == 0: cannotEval(n) if s.position == 0: if sfImportc in s.flags: c.importcSym(n.info, s) else: genGlobalInit(c, n, s) if dest < 0: dest = c.getTemp(n.typ) if gfAddrOf notin flags and fitsRegister(s.typ): var cc = c.getTemp(n.typ) c.gABx(n, opcLdGlobal, cc, s.position) c.gABC(n, opcNodeToReg, dest, cc) c.freeTemp(cc) else: c.gABx(n, opcLdGlobal, dest, s.position) else: if s.kind == skForVar and c.mode == emRepl: c.setSlot(s) if s.position > 0 or (s.position == 0 and s.kind in {skParam,skResult}): if dest < 0: dest = s.position + ord(s.kind == skParam) internalAssert(c.prc.slots[dest].kind < slotSomeTemp) else: # we need to generate an assignment: genAsgn(c, dest, n, c.prc.slots[dest].kind >= slotSomeTemp) else: # see tests/t99bott for an example that triggers it: cannotEval(n) proc genArrAccess2(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; flags: TGenFlags) = let a = c.genx(n.sons[0], flags) let b = c.genx(n.sons[1], {}) if dest < 0: dest = c.getTemp(n.typ) if gfAddrOf notin flags and fitsRegister(n.typ): var cc = c.getTemp(n.typ) c.gABC(n, opc, cc, a, b) c.gABC(n, opcNodeToReg, dest, cc) c.freeTemp(cc) else: c.gABC(n, opc, dest, a, b) c.freeTemp(a) c.freeTemp(b) proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = let a = c.genx(n.sons[0], flags) let b = genField(n.sons[1]) if dest < 0: dest = c.getTemp(n.typ) if gfAddrOf notin flags and fitsRegister(n.typ.skipTypes({tyVar})): var cc = c.getTemp(n.typ) c.gABC(n, opcLdObj, cc, a, b) c.gABC(n, opcNodeToReg, dest, cc) c.freeTemp(cc) else: c.gABC(n, opcLdObj, dest, a, b) c.freeTemp(a) proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = # XXX implement field checks! genObjAccess(c, n.sons[0], dest, flags) proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = if n.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in { tyString, tyCString}: genArrAccess2(c, n, dest, opcLdStrIdx, {}) else: genArrAccess2(c, n, dest, opcLdArr, flags) proc getNullValueAux(obj: PNode, result: PNode) = case obj.kind of nkRecList: for i in countup(0, sonsLen(obj) - 1): getNullValueAux(obj.sons[i], result) of nkRecCase: getNullValueAux(obj.sons[0], result) for i in countup(1, sonsLen(obj) - 1): getNullValueAux(lastSon(obj.sons[i]), result) of nkSym: addSon(result, getNullValue(obj.sym.typ, result.info)) else: internalError(result.info, "getNullValueAux") proc getNullValue(typ: PType, info: TLineInfo): PNode = var t = skipTypes(typ, abstractRange-{tyTypeDesc}) result = emptyNode case t.kind of tyBool, tyEnum, tyChar, tyInt..tyInt64: result = newNodeIT(nkIntLit, info, t) of tyUInt..tyUInt64: result = newNodeIT(nkUIntLit, info, t) of tyFloat..tyFloat128: result = newNodeIT(nkFloatLit, info, t) of tyVar, tyPointer, tyPtr, tyCString, tySequence, tyString, tyExpr, tyStmt, tyTypeDesc, tyStatic, tyRef: result = newNodeIT(nkNilLit, info, t) of tyProc: if t.callConv != ccClosure: result = newNodeIT(nkNilLit, info, t) else: result = newNodeIT(nkPar, info, t) result.add(newNodeIT(nkNilLit, info, t)) result.add(newNodeIT(nkNilLit, info, t)) of tyObject: result = newNodeIT(nkPar, info, t) getNullValueAux(t.n, result) # initialize inherited fields: var base = t.sons[0] while base != nil: getNullValueAux(skipTypes(base, skipPtrs).n, result) base = base.sons[0] of tyArray, tyArrayConstr: result = newNodeIT(nkBracket, info, t) for i in countup(0, int(lengthOrd(t)) - 1): addSon(result, getNullValue(elemType(t), info)) of tyTuple: result = newNodeIT(nkPar, info, t) for i in countup(0, sonsLen(t) - 1): addSon(result, getNullValue(t.sons[i], info)) of tySet: result = newNodeIT(nkCurly, info, t) else: internalError("getNullValue: " & $t.kind) proc ldNullOpcode(t: PType): TOpcode = if fitsRegister(t): opcLdNullReg else: opcLdNull proc genVarSection(c: PCtx; n: PNode) = for a in n: if a.kind == nkCommentStmt: continue #assert(a.sons[0].kind == nkSym) can happen for transformed vars if a.kind == nkVarTuple: for i in 0 .. a.len-3: setSlot(c, a[i].sym) checkCanEval(c, a[i]) c.gen(lowerTupleUnpacking(a, c.getOwner)) elif a.sons[0].kind == nkSym: let s = a.sons[0].sym checkCanEval(c, a.sons[0]) if s.isGlobal: if s.position == 0: if sfImportc in s.flags: c.importcSym(a.info, s) else: let sa = getNullValue(s.typ, a.info) #if s.ast.isNil: getNullValue(s.typ, a.info) #else: canonValue(s.ast) assert sa.kind != nkCall c.globals.add(sa) s.position = c.globals.len if a.sons[2].kind != nkEmpty: let tmp = c.genx(a.sons[0], {gfAddrOf}) let val = c.genx(a.sons[2]) c.gABC(a, opcWrDeref, tmp, val) c.freeTemp(val) c.freeTemp(tmp) else: setSlot(c, s) if a.sons[2].kind == nkEmpty: c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ)) else: gen(c, a.sons[2], s.position.TRegister) else: # assign to a.sons[0]; happens for closures if a.sons[2].kind == nkEmpty: let tmp = genx(c, a.sons[0]) c.gABx(a, ldNullOpcode(a[0].typ), tmp, c.genType(a.sons[0].typ)) c.freeTemp(tmp) else: genAsgn(c, a.sons[0], a.sons[2], true) proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) = if dest < 0: dest = c.getTemp(n.typ) c.gABx(n, opcLdNull, dest, c.genType(n.typ)) let intType = getSysType(tyInt) let seqType = n.typ.skipTypes(abstractVar-{tyTypeDesc}) if seqType.kind == tySequence: var tmp = c.getTemp(intType) c.gABx(n, opcLdImmInt, tmp, n.len) c.gABx(n, opcNewSeq, dest, c.genType(seqType)) c.gABx(n, opcNewSeq, tmp, 0) c.freeTemp(tmp) if n.len > 0: var tmp = getTemp(c, intType) c.gABx(n, opcLdNullReg, tmp, c.genType(intType)) for x in n: let a = c.genx(x) c.gABC(n, whichAsgnOpc(x, opcWrArr), dest, tmp, a) c.gABI(n, opcAddImmInt, tmp, tmp, 1) c.freeTemp(a) c.freeTemp(tmp) proc genSetConstr(c: PCtx, n: PNode, dest: var TDest) = if dest < 0: dest = c.getTemp(n.typ) c.gABx(n, opcLdNull, dest, c.genType(n.typ)) for x in n: if x.kind == nkRange: let a = c.genx(x.sons[0]) let b = c.genx(x.sons[1]) c.gABC(n, opcInclRange, dest, a, b) c.freeTemp(b) c.freeTemp(a) else: let a = c.genx(x) c.gABC(n, opcIncl, dest, a) c.freeTemp(a) proc genObjConstr(c: PCtx, n: PNode, dest: var TDest) = if dest < 0: dest = c.getTemp(n.typ) let t = n.typ.skipTypes(abstractRange-{tyTypeDesc}) if t.kind == tyRef: c.gABx(n, opcNew, dest, c.genType(t.sons[0])) else: c.gABx(n, opcLdNull, dest, c.genType(n.typ)) for i in 1.. = low(int16) and s.position <= high(int16): c.gABx(n, opcLdImmInt, dest, s.position) else: var lit = genLiteral(c, newIntNode(nkIntLit, s.position)) c.gABx(n, opcLdConst, dest, lit) of skType: genTypeLit(c, s.typ, dest) else: internalError(n.info, "cannot generate code for: " & s.name.s) of nkCallKinds: if n.sons[0].kind == nkSym and n.sons[0].sym.magic != mNone: genMagic(c, n, dest) else: genCall(c, n, dest) clearDest(c, n, dest) of nkCharLit..nkInt64Lit: if isInt16Lit(n): if dest < 0: dest = c.getTemp(n.typ) c.gABx(n, opcLdImmInt, dest, n.intVal.int) else: genLit(c, n, dest) of nkUIntLit..pred(nkNilLit): genLit(c, n, dest) of nkNilLit: if not n.typ.isEmptyType: genLit(c, n, dest) else: unused(n, dest) of nkAsgn, nkFastAsgn: unused(n, dest) genAsgn(c, n.sons[0], n.sons[1], n.kind == nkAsgn) of nkDotExpr: genObjAccess(c, n, dest, flags) of nkCheckedFieldExpr: genCheckedObjAccess(c, n, dest, flags) of nkBracketExpr: genArrAccess(c, n, dest, flags) of nkDerefExpr, nkHiddenDeref: genAddrDeref(c, n, dest, opcLdDeref, flags) of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddrNode, flags) of nkWhenStmt, nkIfStmt, nkIfExpr: genIf(c, n, dest) of nkCaseStmt: genCase(c, n, dest) of nkWhileStmt: unused(n, dest) genWhile(c, n) of nkBlockExpr, nkBlockStmt: genBlock(c, n, dest) of nkReturnStmt: unused(n, dest) genReturn(c, n) of nkRaiseStmt: unused(n, dest) genRaise(c, n) of nkBreakStmt: unused(n, dest) genBreak(c, n) of nkTryStmt: genTry(c, n, dest) of nkStmtList: unused(n, dest) for x in n: gen(c, x) of nkStmtListExpr: let L = n.len-1 for i in 0 .. = 0: gABC(c, n, whichAsgnOpc(n), dest, tmp0) c.freeTemp(tmp0) else: dest = tmp0 of nkEmpty, nkCommentStmt, nkTypeSection, nkConstSection, nkPragma, nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt: unused(n, dest) of nkStringToCString, nkCStringToString: gen(c, n.sons[0], dest) of nkBracket: genArrayConstr(c, n, dest) of nkCurly: genSetConstr(c, n, dest) of nkObjConstr: genObjConstr(c, n, dest) of nkPar, nkClosure: genTupleConstr(c, n, dest) of nkCast: if allowCast in c.features: genConv(c, n, n.sons[1], dest, opcCast) else: localError(n.info, errGenerated, "VM is not allowed to 'cast'") else: internalError n.info, "cannot generate VM code for " & n.renderTree proc removeLastEof(c: PCtx) = let last = c.code.len-1 if last >= 0 and c.code[last].opcode == opcEof: # overwrite last EOF: assert c.code.len == c.debug.len c.code.setLen(last) c.debug.setLen(last) proc genStmt*(c: PCtx; n: PNode): int = c.removeLastEof result = c.code.len var d: TDest = -1 c.gen(n, d) c.gABC(n, opcEof) if d >= 0: internalError(n.info, "some destination set") proc genExpr*(c: PCtx; n: PNode, requiresValue = true): int = c.removeLastEof result = c.code.len var d: TDest = -1 c.gen(n, d) if d < 0: if requiresValue: internalError(n.info, "no destination set") d = 0 c.gABC(n, opcEof, d) proc genParams(c: PCtx; params: PNode) = # res.sym.position is already 0 c.prc.slots[0] = (inUse: true, kind: slotFixedVar) for i in 1.. 0: d = d + c.code[d].jmpDiff dec iters if c.code[d].opcode == opcRet: # optimize 'jmp to ret' to 'ret' here c.code[i] = c.code[d] elif d != i + c.code[i].jmpDiff: c.finalJumpTarget(i, d - i) else: discard proc genProc(c: PCtx; s: PSym): int = let x = s.ast.sons[optimizedCodePos] if x.kind == nkEmpty: #if s.name.s == "outterMacro" or s.name.s == "innerProc": # echo "GENERATING CODE FOR ", s.name.s let last = c.code.len-1 var eofInstr: TInstr if last >= 0 and c.code[last].opcode == opcEof: eofInstr = c.code[last] c.code.setLen(last) c.debug.setLen(last) #c.removeLastEof result = c.code.len+1 # skip the jump instruction s.ast.sons[optimizedCodePos] = newIntNode(nkIntLit, result) # thanks to the jmp we can add top level statements easily and also nest # procs easily: let body = s.getBody let procStart = c.xjmp(body, opcJmp, 0) var p = PProc(blocks: @[], sym: s) let oldPrc = c.prc c.prc = p # iterate over the parameters and allocate space for them: genParams(c, s.typ.n) if tfCapturesEnv in s.typ.flags: #let env = s.ast.sons[paramsPos].lastSon.sym #assert env.position == 2 c.prc.slots[c.prc.maxSlots] = (inUse: true, kind: slotFixedLet) inc c.prc.maxSlots gen(c, body) # generate final 'return' statement: c.gABC(body, opcRet) c.patch(procStart) c.gABC(body, opcEof, eofInstr.regA) c.optimizeJumps(result) s.offset = c.prc.maxSlots #if s.name.s == "addStuff": # echo renderTree(body) # c.echoCode(result) c.prc = oldPrc else: c.prc.maxSlots = s.offset result = x.intVal.int