diff options
Diffstat (limited to 'compiler/vmgen.nim')
-rw-r--r-- | compiler/vmgen.nim | 1002 |
1 files changed, 1002 insertions, 0 deletions
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim new file mode 100644 index 000000000..048a89b8c --- /dev/null +++ b/compiler/vmgen.nim @@ -0,0 +1,1002 @@ +# +# +# The Nimrod Compiler +# (c) Copyright 2013 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 + +proc codeListing(c: PCtx, result: var string) = + # for debugging purposes + var i = 0 + while i < c.code.len: + if i in c.jumpTargets: result.addf("L$1:\n", i) + let x = c.code[i] + + let opc = opcode(x) + if opc < firstABxInstr: + result.addf("\t$#\tr$#, r$#, r$#", ($opc).substr(3), x.regA, + x.regB, x.regC) + else: + result.addf("\t$#\tr$#, r$#", ($opc).substr(3), x.regA, x.regBx) + result.add(" #") + result.add(toFileLine(c.debug[i])) + result.add("\n") + inc i + +proc gABC(ctx: PCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) = + 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) = + let ins = (opc.ord or a shl 8 or b shl 16 or (imm+byteExcess) shl 24).TInstr + c.code.add(ins) + c.debug.add(n.info) + +proc gABx(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) = + let ins = (opc.ord or a shl 8 or (bx+wordExcess) shl 16).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} + gABx(c, n, opc, a, 0) + result = TPosition(c.code.len) + +proc genLabel(c: PCtx): TPosition = + result = TPosition(c.code.len) + c.jumpTargets.incl(c.code.len) + +proc jmp(c: PCtx, n: PNode, opc: TOpcode, p = TPosition(0)) = + let dist = p.int - c.code.len + InternalAssert(-0x7fff < dist and dist < 0x7fff) + gABx(c, n, opc, 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.int and 0xffff) or (diff+wordExcess)).TInstr + +proc getSlotKind(t: PType): TSlotKind = + case t.skipTypes(abstractRange).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: + let k = 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) + 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-1: + block search: + if not c.slots[i].inUse: + 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) +proc gen(c: PCtx; n: PNode; dest: TRegister) = + var d: TDest = dest + gen(c, n, d) + InternalAssert d == dest + +proc gen(c: PCtx; n: PNode) = + var tmp: TDest = -1 + gen(c, n, tmp) + InternalAssert tmp < 0 + +proc genx(c: PCtx; n: PNode): TRegister = + var tmp: TDest = -1 + gen(c, n, tmp) + result = TRegister(tmp) + +proc genWhile(c: PCtx; n: PNode) = + # L1: + # cond, tmp + # fjmp tmp, L2 + # body + # jmp L1 + # L2: + let L1 = c.genLabel + withBlock(nil): + var tmp = c.genx(n.sons[0]) + let L2 = c.xjmp(n, opcFJmp, tmp) + c.freeTemp(tmp) + c.gen(n.sons[1]) + c.jmp(n, opcJmp, L1) + c.patch(L2) + +proc genBlock(c: PCtx; n: PNode; dest: var TDest) = + withBlock(n.sons[0].sym): + c.gen(n.sons[1], dest) + +proc genBreak(c: PCtx; n: PNode) = + let L1 = c.xjmp(n, opcJmp) + if n.sons[0].kind == nkSym: + 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 + break + 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): + c.gen(it.sons[0], tmp) + let elsePos = c.xjmp(it.sons[0], opcFJmp, tmp) # if false + c.gen(n.sons[1], dest) # then part + if i < sonsLen(n)-1: + endings.add(c.xjmp(it.sons[1], opcJmp, 0)) + c.patch(elsePos) + else: + c.gen(it.sons[0], dest) + for endPos in endings: c.patch(endPos) + +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[0], dest) + let L1 = c.xjmp(n, opc) + c.gen(n.sons[1], dest) + c.patch(L1) + +proc rawGenLiteral(c: PCtx; n: PNode): int = + result = c.constants.len + c.constants.add n + 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 nkEmpty, nkNilLit, nkType: 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 .. <c.constants.len: + if sameConstant(c.constants[i], n): return i + result = rawGenLiteral(c, n) + +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 dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ) + 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 .. <n.len: + let it = n.sons[i] + if it.len == 1: + # else stmt: + c.gen(it.sons[0], dest) + else: + let b = rawGenLiteral(c, it) + c.gABx(it, opcBranch, tmp, b) + let elsePos = c.xjmp(it.lastSon, opcFJmp, tmp) + c.gen(it.lastSon, dest) + if i < sonsLen(n)-1: + endings.add(c.xjmp(it.lastSon, opcJmp, 0)) + c.patch(elsePos) + for endPos in endings: c.patch(endPos) + +proc genType(c: PCtx; typ: PType): int = + for i, t in c.types: + if sameType(t, typ): return i + result = c.types.len + c.types.add(typ) + internalAssert(result <= 0x7fff) + +proc genTry(c: PCtx; n: PNode; dest: var TDest) = + if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ) + var endings: seq[TPosition] = @[] + let elsePos = c.xjmp(n, opcTry, 0) + c.gen(n.sons[0], dest) + c.patch(elsePos) + for i in 1 .. <n.len: + let it = n.sons[i] + if it.kind != nkFinally: + var blen = len(it) + # first opcExcept contains the end label of the 'except' block: + let endExcept = c.xjmp(it, opcExcept, 0) + for j in countup(0, blen - 2): + assert(it.sons[j].kind == nkType) + let typ = it.sons[j].typ.skipTypes(abstractPtrs) + c.gABx(it, opcExcept, 0, c.genType(typ)) + if blen == 1: + # general except section: + c.gABx(it, opcExcept, 0, 0) + c.gen(it.lastSon, dest) + if i < sonsLen(n)-1: + endings.add(c.xjmp(it, opcJmp, 0)) + c.patch(endExcept) + for endPos in endings: c.patch(endPos) + let fin = lastSon(n) + # we always generate an 'opcFinally' as that pops the safepoint + # from the stack + c.gABx(fin, opcFinally, 0, 0) + if fin.kind == nkFinally: + c.gen(fin.sons[0], dest) + c.gABx(fin, opcFinallyEnd, 0, 0) + +proc genRaise(c: PCtx; n: PNode) = + let dest = genx(c, n.sons[0]) + c.gABC(n, opcRaise, dest) + c.freeTemp(dest) + +proc genReturn(c: PCtx; n: PNode) = + if n.sons[0].kind != nkEmpty: + gen(c, n.sons[0]) + c.gABC(n, opcRet) + +proc genCall(c: PCtx; n: PNode; dest: var TDest) = + if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ) + let x = c.getTempRange(n.len, slotTempUnknown) + for i in 0.. <n.len: + var r: TRegister = x+i + c.gen(n.sons[i], r) + 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) + +proc genNew(c: PCtx; n: PNode) = + let dest = c.genx(n.sons[1]) + c.gABx(n, opcNew, dest, c.genType(n.sons[1].typ.skipTypes(abstractVar))) + c.freeTemp(dest) + +proc genNewSeq(c: PCtx; n: PNode) = + let dest = c.genx(n.sons[1]) + c.gABx(n, opcNewSeq, dest, c.genType(n.sons[1].typ.skipTypes(abstractVar))) + let tmp = c.genx(n.sons[2]) + c.gABx(n, opcNewSeq, tmp, 0) + c.freeTemp(dest) + c.freeTemp(tmp) + +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 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 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) + +proc unused(n: PNode; x: TDest) {.inline.} = + if x >= 0: InternalError(n.info, "not unused") + +proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) = + let tmp = c.genx(arg) + let t = genType(c, n.typ) + if dest < 0: dest = c.getTemp(n.typ) + c.gABC(n, opc, dest, tmp) + c.gABx(n, opc, 0, t) + 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) + var d = c.genx(n.sons[1]).TDest + c.genAddSubInt(n, d, if m == mInc: opcAddInt else: opcSubInt) + c.freeTemp(d.TRegister) + 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) + 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) + of mLengthOpenArray, mLengthArray, mLengthSeq: + genUnaryABC(c, n, dest, opcLenSeq) + of mLengthStr: + genUnaryABC(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.gABC(n, if m == mIncl: opcIncl else: opcExcl, d, tmp) + c.freeTemp(d) + c.freeTemp(tmp) + of mCard: genUnaryABC(c, n, dest, opcCard) + of mMulI, mMulI64: genBinaryABC(c, n, dest, opcMulInt) + of mDivI, mDivI64: genBinaryABC(c, n, dest, opcDivInt) + of mModI, mModI64: genBinaryABC(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: genBinaryABC(c, n, dest, opcShrInt) + of mShlI, mShlI64: genBinaryABC(c, n, dest, opcShlInt) + of mBitandI, mBitandI64: genBinaryABC(c, n, dest, opcBitandInt) + of mBitorI, mBitorI64: genBinaryABC(c, n, dest, opcBitorInt) + of mBitxorI, mBitxorI64: genBinaryABC(c, n, dest, opcBitxorInt) + of mAddU: genBinaryABC(c, n, dest, opcAddu) + of mSubU: genBinaryABC(c, n, dest, opcSubu) + of mMulU: genBinaryABC(c, n, dest, opcMulu) + of mDivU: genBinaryABC(c, n, dest, opcDivu) + of mModU: genBinaryABC(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: genBinaryABC(c, n, dest, opcEqRef) + of mXor: genBinaryABC(c, n, dest, opcXor) + of mNot: genUnaryABC(c, n, dest, opcNot) + of mUnaryMinusI, mUnaryMinusI64: genUnaryABC(c, n, dest, opcUnaryMinusInt) + 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) + 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: genBinaryABC(c, n, dest, opcEqSet) + of mLeSet: genBinaryABC(c, n, dest, opcLeSet) + of mLtSet: genBinaryABC(c, n, dest, opcLtSet) + of mMulSet: genBinaryABC(c, n, dest, opcMulSet) + of mPlusSet: genBinaryABC(c, n, dest, opcPlusSet) + of mMinusSet: genBinaryABC(c, n, dest, opcMinusSet) + of mSymDiffSet: genBinaryABC(c, n, dest, opcSymdiffSet) + of mConStrStr: genVarargsABC(c, n, dest, opcConcatStr) + of mInSet: genBinaryABC(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.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: + if dest < 0: dest = c.getTemp(n.typ) + var tmp = c.genx(n.sons[1]) + c.gABC(n, opcOf, dest, tmp) + c.gABx(n, opcOf, 0, c.genType(n.sons[2].typ.skipTypes(abstractPtrs))) + c.freeTemp(tmp) + 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).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) + for i in 1.. <n.len: + var d = c.genx(n.sons[i]) + c.gABC(n, opcEcho, d) + c.freeTemp(d) + of mAppendStrCh: InternalError(n.info, "cannot generate code for: " & $m) + of mAppendStrStr: InternalError(n.info, "cannot generate code for: " & $m) + of mAppendSeqElem: InternalError(n.info, "cannot generate code for: " & $m) + of mParseExprToAst: InternalError(n.info, "cannot generate code for: " & $m) + of mParseStmtToAst: InternalError(n.info, "cannot generate code for: " & $m) + of mExpandToAst: InternalError(n.info, "cannot generate code for: " & $m) + of mTypeTrait: InternalError(n.info, "cannot generate code for: " & $m) + of mIs: InternalError(n.info, "cannot generate code for: " & $m) + of mSlurp: InternalError(n.info, "cannot generate code for: " & $m) + of mStaticExec: InternalError(n.info, "cannot generate code for: " & $m) + of mNLen: InternalError(n.info, "cannot generate code for: " & $m) + of mNChild: InternalError(n.info, "cannot generate code for: " & $m) + of mNSetChild: InternalError(n.info, "cannot generate code for: " & $m) + of mNAdd: InternalError(n.info, "cannot generate code for: " & $m) + of mNAddMultiple: InternalError(n.info, "cannot generate code for: " & $m) + of mNDel: InternalError(n.info, "cannot generate code for: " & $m) + of mNKind: InternalError(n.info, "cannot generate code for: " & $m) + of mNIntVal: InternalError(n.info, "cannot generate code for: " & $m) + of mNFloatVal: InternalError(n.info, "cannot generate code for: " & $m) + of mNSymbol: InternalError(n.info, "cannot generate code for: " & $m) + of mNIdent: InternalError(n.info, "cannot generate code for: " & $m) + of mNGetType: InternalError(n.info, "cannot generate code for: " & $m) + of mNStrVal: InternalError(n.info, "cannot generate code for: " & $m) + of mNSetIntVal: InternalError(n.info, "cannot generate code for: " & $m) + of mNSetFloatVal: InternalError(n.info, "cannot generate code for: " & $m) + of mNSetSymbol: InternalError(n.info, "cannot generate code for: " & $m) + of mNSetIdent: InternalError(n.info, "cannot generate code for: " & $m) + of mNSetType: InternalError(n.info, "cannot generate code for: " & $m) + of mNSetStrVal: InternalError(n.info, "cannot generate code for: " & $m) + of mNNewNimNode: InternalError(n.info, "cannot generate code for: " & $m) + of mNCopyNimNode: InternalError(n.info, "cannot generate code for: " & $m) + of mNCopyNimTree: InternalError(n.info, "cannot generate code for: " & $m) + of mNBindSym: InternalError(n.info, "cannot generate code for: " & $m) + of mStrToIdent: InternalError(n.info, "cannot generate code for: " & $m) + of mIdentToStr: InternalError(n.info, "cannot generate code for: " & $m) + of mEqIdent: InternalError(n.info, "cannot generate code for: " & $m) + of mEqNimrodNode: InternalError(n.info, "cannot generate code for: " & $m) + of mNLineInfo: InternalError(n.info, "cannot generate code for: " & $m) + of mNHint: InternalError(n.info, "cannot generate code for: " & $m) + of mNWarning: InternalError(n.info, "cannot generate code for: " & $m) + of mNError: InternalError(n.info, "cannot generate code for: " & $m) + of mNCallSite: InternalError(n.info, "cannot generate code for: " & $m) + else: + # XXX get rid of these: mMinI, mMaxI, mMinI64, mMaxI64, mMinF64, mMaxF64 + # mGCref, mGCunref, mEqCString, mAbsI, mAbsI64, mAbsF64 + InternalError(n.info, "cannot generate code for: " & $m) + +const + atomicTypes = {tyBool, tyChar, + tyExpr, tyStmt, tyTypeDesc, + tyEnum, + tyOrdinal, + tyRange, + tyProc, + tyPointer, tyOpenArray, + tyString, tyCString, + tyInt, tyInt8, tyInt16, tyInt32, tyInt64, + tyFloat, tyFloat32, tyFloat64, tyFloat128, + tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64} + +proc requiresCopy(n: PNode): bool = + if n.typ.skipTypes(abstractInst).kind in atomicTypes: + result = false + elif n.kind in ({nkCurly, nkBracket, nkPar, nkObjConstr}+nkCallKinds): + result = false + else: + result = true + +proc unnecessaryIndirection(n: PNode): bool = + n.typ.skipTypes(abstractInst).kind == tyRef + +proc skipDeref(n: PNode): PNode = + if n.kind in {nkDerefExpr, nkHiddenDeref} and unnecessaryIndirection(n): + result = n.sons[0] + else: + result = n + +proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = + # a nop for certain types + if unnecessaryIndirection(n): + gen(c, n.sons[0], dest) + else: + let tmp = c.genx(n.sons[0]) + if dest < 0: dest = c.getTemp(n.typ) + gABC(c, n, opc, dest, tmp) + c.freeTemp(tmp) + +proc whichAsgnOpc(n: PNode): TOpcode = + case n.typ.skipTypes(abstractRange).kind + of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64: + opcAsgnInt + of tyString, tyCString: + opcAsgnStr + of tyFloat..tyFloat128: + opcAsgnFloat + of tyRef, tyNil: + opcAsgnRef + else: + opcAsgnComplex + +proc isRef(t: PType): bool = t.skipTypes(abstractRange).kind == tyRef + +proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode = + if isRef(n.typ): succ(opc) else: 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 genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = + case le.kind + of nkBracketExpr: + let dest = c.genx(le.sons[0]) + let idx = c.genx(le.sons[1]) + let tmp = c.genx(ri) + if le.typ.skipTypes(abstractVarRange).kind in {tyString, tyCString}: + c.gABC(le, opcWrStrIdx, dest, idx, tmp) + else: + c.gABC(le, whichAsgnOpc(le, opcWrArr), dest, idx, tmp) + c.freeTemp(tmp) + of nkDotExpr: + let dest = c.genx(le.sons[0]) + let idx = c.genx(le.sons[1]) + let tmp = c.genx(ri) + c.gABC(le, whichAsgnOpc(le, opcWrObj), dest, idx, tmp) + c.freeTemp(tmp) + of nkSym: + let s = le.sym + if sfGlobal in s.flags: + withTemp(tmp, le.typ): + gen(c, ri, tmp) + c.gABx(le, whichAsgnOpc(le, opcWrGlobal), tmp, s.position) + else: + 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) + genAsgn(c, dest, ri, requiresCopy) + +proc genLit(c: PCtx; n: PNode; dest: var TDest) = + if dest < 0: dest = c.getTemp(n.typ) + let lit = genLiteral(c, n) + c.gABx(n, opcLdConst, dest, lit) + +proc genRdVar(c: PCtx; n: PNode; dest: var TDest) = + let s = n.sym + if sfGlobal in s.flags: + if dest < 0: dest = c.getTemp(s.typ) + if s.position == 0: + c.globals.add(s.ast) + s.position = c.globals.len + # XXX var g = codeHere() ? + c.gABx(n, opcLdGlobal, dest, s.position) + else: + if s.position > 0 or (s.position == 0 and s.kind in {skParam, skResult}): + if dest < 0: + dest = s.position + ord(s.kind == skParam) + else: + # we need to generate an assignment: + genAsgn(c, dest, n, c.prc.slots[dest].kind >= slotSomeTemp) + else: + InternalError(n.info, s.name.s & " " & $s.position) + +proc genAccess(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = + let a = c.genx(n.sons[0]) + let b = c.genx(n.sons[1]) + if dest < 0: dest = c.getTemp(n.typ) + c.gABC(n, opc, dest, a, b) + c.freeTemp(a) + c.freeTemp(b) + +proc genObjAccess(c: PCtx; n: PNode; dest: var TDest) = + genAccess(c, n, dest, opcLdObj) + +proc genArrAccess(c: PCtx; n: PNode; dest: var TDest) = + genAccess(c, n, dest, opcLdArr) + +proc getNullValue*(typ: PType, info: TLineInfo): PNode +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, tyProc, tyRef: + result = 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 setSlot(c: PCtx; v: PSym) = + # XXX generate type initialization here? + if v.position == 0: + v.position = c.prc.maxSlots + c.prc.slots[v.position] = (inUse: true, kind: slotFixed) + inc c.prc.maxSlots + echo v.name.s, " has position ", v.position + +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: + let tmp = c.genx(a.lastSon) + for i in 0 .. a.len-3: + setSlot(c, a[i].sym) + # v = t[i] + var v: TDest = -1 + genRdVar(c, a[i], v) + c.gABC(n, opcLdObj, v, tmp, i) + # XXX globals? + c.freeTemp(tmp) + elif a.sons[0].kind == nkSym: + let s = a.sons[0].sym + if sfGlobal in s.flags: + if s.position == 0: + let sa = if s.ast.isNil: getNullValue(s.typ, a.info) else: s.ast + c.globals.add(sa) + s.position = c.globals.len + if a.sons[2].kind == nkEmpty: + when false: + withTemp(tmp, s.typ): + c.gABx(a, opcLdNull, tmp, c.genType(s.typ)) + c.gABx(a, whichAsgnOpc(a.sons[0], opcWrGlobal), tmp, s.position) + else: + let tmp = genx(c, a.sons[2]) + c.gABx(a, whichAsgnOpc(a.sons[0], opcWrGlobal), tmp, s.position) + c.freeTemp(tmp) + else: + setSlot(c, s) + if a.sons[2].kind == nkEmpty: + c.gABx(a, opcLdNull, 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, opcLdNull, tmp, c.genType(a.sons[0].typ)) + c.freeTemp(tmp) + else: + genAsgn(c, a.sons[0], a.sons[2], true) + +proc genProc*(c: PCtx; s: PSym): int + +proc gen(c: PCtx; n: PNode; dest: var TDest) = + case n.kind + of nkSym: + let s = n.sym + case s.kind + of skVar, skForVar, skTemp, skLet, skParam, skResult: + genRdVar(c, n, dest) + of skProc, skConverter, skMacro, skMethod, skIterator: + genLit(c, n, dest) + of skConst: + gen(c, s.ast, dest) + of skEnumField: + if dest < 0: dest = c.getTemp(n.typ) + if s.position >= 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 skField: + InternalAssert dest < 0 + if s.position > high(dest): + InternalError(n.info, + "too large offset! cannot generate code for: " & s.name.s) + dest = s.position + 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) + 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..nkNilLit: genLit(c, 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) + of nkBracketExpr: genArrAccess(c, n, dest) + of nkDerefExpr, nkHiddenDeref: genAddrDeref(c, n, dest, opcDeref) + of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddr) + 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 .. <L: gen(c, n.sons[i]) + gen(c, n.sons[L], dest) + of nkDiscardStmt: + unused(n, dest) + gen(c, n.sons[0]) + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + genConv(c, n, n.sons[1], dest) + of nkVarSection, nkLetSection: + unused(n, dest) + genVarSection(c, n) + of declarativeDefs: + unused(n, dest) + of nkLambdaKinds: + let s = n.sons[namePos].sym + discard genProc(c, s) + genLit(c, n.sons[namePos], dest) + of nkEmpty, nkCommentStmt, nkTypeSection, nkConstSection, nkPragma, + nkTemplateDef, nkIncludeStmt, nkImportStmt: + unused(n, dest) + else: + #of nkCurly, nkBracket, nkPar: + InternalError n.info, "too implement " & $n.kind + +proc genStmt*(c: PCtx; n: PNode): int = + result = c.code.len + var d: TDest = -1 + gen(c, n, d) + let last = c.code.len-1 + if last >= 0 and c.code[last].opcode == opcEof: + # since we can re-use the EOF nothing happened: + result = last + else: + gABC(c, n, opcEof) + InternalAssert d < 0 + +proc genParams(c: PCtx; params: PNode) = + # res.sym.position is already 0 + c.prc.slots[0] = (inUse: true, kind: slotFixed) + for i in 1.. <params.len: + let param = params.sons[i].sym + c.prc.slots[i] = (inUse: true, kind: slotFixed) + c.prc.maxSlots = max(params.len, 1) + +proc genProc(c: PCtx; s: PSym): int = + let x = s.ast.sons[optimizedCodePos] + if x.kind == nkEmpty: + 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: @[]) + let oldPrc = c.prc + c.prc = p + # iterate over the parameters and allocate space for them: + genParams(c, s.typ.n) + gen(c, body) + # generate final 'return' statement: + c.gABC(body, opcRet) + c.patch(procStart) + s.position = c.prc.maxSlots + c.prc = oldPrc + else: + result = x.intVal.int |