diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim new file mode 100644 index 000000000..a239d4ef2 --- /dev/null +++ b/compiler/nimeval.nim @@ -0,0 +1,33 @@ +# +# +# The Nimrod Compiler +# (c) Copyright 2013 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## exposes the Nimrod VM to clients. + +import + ast, modules, passes, passaux, condsyms, + options, nimconf, lists, sem, semdata, llstream, vm + +proc execute*(program: string) = + passes.gIncludeFile = includeModule + passes.gImportModule = importModule + initDefines() + LoadConfigs(DefaultConfig) + + initDefines() + DefineSymbol("nimrodvm") + when hasFFI: DefineSymbol("nimffi") + registerPass(verbosePass) + registerPass(semPass) + registerPass(vmPass) + + appendStr(searchPaths, options.libpath) + compileSystemModule() + var m = makeStdinModule() + incl(m.flags, sfMainModule) + processModule(m, LLStreamOpen(program), nil) diff --git a/compiler/vm.nim b/compiler/vm.nim new file mode 100644 index 000000000..72e0cb5b1 --- /dev/null +++ b/compiler/vm.nim @@ -0,0 +1,584 @@ +# +# +# The Nimrod Compiler +# (c) Copyright 2013 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This file implements the new evaluation engine for Nimrod code. +## An instruction is 1-2 int32s in memory, it is a register based VM. + +import ast, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned + +type + PStackFrame* = ref TStackFrame + TStackFrame* = object + prc: PSym # current prc; proc that is evaluated + slots: TNodeSeq # parameters passed to the proc + locals; + # parameters come first + next: PStackFrame # for stacking + comesFrom: int + safePoints: seq[int] # used for exception handling + # XXX 'break' should perform cleanup actions + # What does the C backend do for it? + +proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int) = + if x != nil: + stackTraceAux(c, x.next, x.comesFrom) + var info = c.debug[pc] + # we now use the same format as in system/except.nim + var s = toFilename(info) + var line = toLineNumber(info) + if line > 0: + add(s, '(') + add(s, $line) + add(s, ')') + if x.prc != nil: + for k in 1..max(1, 25-s.len): add(s, ' ') + add(s, x.prc.name.s) + MsgWriteln(s) + +proc stackTrace(c: PCtx, tos: PStackFrame, pc: int, + msg: TMsgKind, arg = "") = + MsgWriteln("stack trace: (most recent call last)") + stackTraceAux(c, tos, pc) + LocalError(c.debug[pc], msg, arg) + +proc bailOut(c: PCtx; tos: PStackFrame) = + stackTrace(c, tos, c.exceptionInstr, errUnhandledExceptionX, + c.currentExceptionA.sons[2].strVal) + +when not defined(nimHasInterpreterLoop): + {.pragma: interpreterLoop.} + +template inc(pc: ptr TInstr, diff = 1) = + inc cast[TAddress](pc), TInstr.sizeof * diff + +template ensureKind(k: expr) {.immediate, dirty.} = + if regs[ra].kind != k: + myreset(regs[ra]) + regs[ra].kind = k + +template decodeB(k: expr) {.immediate, dirty.} = + let rb = instr.regB + ensureKind(k) + +template decodeBC(k: expr) {.immediate, dirty.} = + let rb = instr.regB + let rc = instr.regC + ensureKind(k) + +template decodeBImm(k: expr) {.immediate, dirty.} = + let rb = instr.regB + let imm = instr.regC - byteExcess + ensureKind(k) + +template decodeBx(k: expr) {.immediate, dirty.} = + let rbx = instr.regBx - wordExcess + ensureKind(k) + +proc compile(c: PCtx, s: PSym): int = vmgen.genProc(c, s) + +proc myreset(n: PNode) = + when defined(system.reset): + var oldInfo = n.info + reset(n[]) + n.info = oldInfo + +template move(a, b: expr) = system.shallowCopy(a, b) +# XXX fix minor 'shallowCopy' overloading bug in compiler + +proc asgnRef(x, y: PNode) = + myreset(x) + x.kind = y.kind + x.typ = y.typ + case x.kind + of nkCharLit..nkInt64Lit: x.intVal = y.intVal + of nkFloatLit..nkFloat64Lit: x.floatVal = y.floatVal + of nkStrLit..nkTripleStrLit: x.strVal = y.strVal + of nkIdent: x.ident = y.ident + of nkSym: x.sym = y.sym + else: + if x.kind notin {nkEmpty..nkNilLit}: + move(x.sons, y.sons) + +proc asgnComplex(x, y: PNode) = + myreset(x) + x.kind = y.kind + x.typ = y.typ + case x.kind + of nkCharLit..nkInt64Lit: x.intVal = y.intVal + of nkFloatLit..nkFloat64Lit: x.floatVal = y.floatVal + of nkStrLit..nkTripleStrLit: x.strVal = y.strVal + of nkIdent: x.ident = y.ident + of nkSym: x.sym = y.sym + else: + if x.kind notin {nkEmpty..nkNilLit}: + let y = y.copyTree + for i in countup(0, sonsLen(y) - 1): addSon(x, y.sons[i]) + +template getstr(a: expr): expr = + (if a.kind == nkStrLit: a.strVal else: $chr(int(a.intVal))) + +proc pushSafePoint(f: PStackFrame; pc: int) = + if f.safePoints.isNil: f.safePoints = @[] + f.safePoints.add(pc) + +proc popSafePoint(f: PStackFrame) = discard f.safePoints.pop() + +proc nextSafePoint(f: PStackFrame): int = + var f = f + while f.safePoints.isNil or f.safePoints.len == 0: + f = f.next + if f.isNil: return -1 + result = f.safePoints.pop + +proc cleanUpOnException(c: PCtx; tos: PStackFrame; regs: TNodeSeq): int = + let raisedType = c.currentExceptionA.typ.skipTypes(abstractPtrs) + while true: + var pc2 = tos.nextSafePoint + if pc2 == -1: return -1 + + var nextExceptOrFinally = -1 + if c.code[pc2].opcode == opcExcept: + nextExceptOrFinally = pc2 + c.code[pc2].regBx - wordExcess + inc pc2 + while c.code[pc2].opcode == opcExcept: + let exceptType = c.types[c.code[pc2].regBx-wordExcess].skipTypes( + abstractPtrs) + if inheritanceDiff(exceptType, raisedType) <= 0: + # mark exception as handled but keep it in B for + # the getCurrentException() builtin: + c.currentExceptionB = c.currentExceptionA + c.currentExceptionA = nil + # execute the corresponding handler: + return pc2 + inc pc2 + if nextExceptOrFinally >= 0: + pc2 = nextExceptOrFinally + if c.code[pc2].opcode == opcFinally: + # execute the corresponding handler, but don't quit walking the stack: + return pc2 + +proc cleanUpOnReturn(c: PCtx; f: PStackFrame): int = + if f.safePoints.isNil: return -1 + for s in f.safePoints: + var pc = s + while c.code[pc].opcode == opcExcept: + pc = pc + c.code[pc].regBx - wordExcess + if c.code[pc].opcode == opcFinally: + return pc + return -1 + +proc execute(c: PCtx, start: int) = + var pc = start + var regs: TNodeSeq # alias to tos.slots for performance + var tos: PStackFrame + newSeq(regs, c.prc.maxSlots) + while true: + {.interpreterLoop.} + let instr = c.code[pc] + let ra = instr.regA + echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra + case instr.opcode + of opcEof: break + of opcRet: + # XXX perform any cleanup actions + tos = tos.next + if tos.isNil: return + let retVal = regs[0] + move(regs, tos.slots) + pc = tos.comesFrom + assert c.code[pc].opcode in {opcIndCall, opcIndCallAsgn} + if c.code[pc].opcode == opcIndCallAsgn: + regs[c.code[pc].regA] = retVal + of opcYldYoid: assert false + of opcYldVal: assert false + of opcAsgnInt: + echo ra, " ", instr.regB, " ", regs.len, tos.prc.name.s + decodeB(nkIntLit) + regs[ra].intVal = regs[rb].intVal + of opcAsgnStr: + decodeB(nkStrLit) + debug regs[rb] + echo rb + Message(c.debug[pc], warnUser, " here") + regs[ra].strVal = regs[rb].strVal + of opcAsgnFloat: + decodeB(nkFloatLit) + regs[ra].floatVal = regs[rb].floatVal + of opcAsgnComplex: + asgnComplex(regs[ra], regs[instr.regB]) + of opcAsgnRef: + asgnRef(regs[ra], regs[instr.regB]) + of opcWrGlobalRef: + asgnRef(c.globals[instr.regBx-wordExcess-1], regs[ra]) + of opcWrGlobal: + asgnComplex(c.globals.sons[instr.regBx-wordExcess-1], regs[ra]) + of opcLdArr: + # a = b[c] + let rb = instr.regB + let rc = instr.regC + let idx = regs[rc].intVal + # XXX what if the array is not 0-based? -> codegen should insert a sub + regs[ra] = regs[rb].sons[idx.int] + of opcWrArr: + # a[b] = c + let rb = instr.regB + let rc = instr.regC + let idx = regs[rb].intVal + asgnComplex(regs[ra].sons[idx.int], regs[rc]) + of opcWrArrRef: + let rb = instr.regB + let rc = instr.regC + let idx = regs[rb].intVal + asgnRef(regs[ra].sons[idx.int], regs[rc]) + of opcLdObj: + # a = b.c + let rb = instr.regB + let rc = instr.regC + # XXX this creates a wrong alias + asgnComplex(regs[ra], regs[rb].sons[rc]) + of opcWrObj: + # a.b = c + let rb = instr.regB + let rc = instr.regC + asgnComplex(regs[ra].sons[rb], regs[rc]) + of opcWrObjRef: + let rb = instr.regB + let rc = instr.regC + asgnRef(regs[ra].sons[rb], regs[rc]) + of opcWrStrIdx: + decodeBC(nkStrLit) + let idx = regs[rb].intVal.int + regs[ra].strVal[idx] = chr(regs[rc].intVal) + of opcAddr: + decodeB(nkRefTy) + if regs[ra].len == 0: regs[ra].add regs[rb] + else: regs[ra].sons[0] = regs[rb] + of opcDeref: + # a = b[] + let rb = instr.regB + if regs[rb].kind == nkNilLit: + stackTrace(c, tos, pc, errNilAccess) + assert regs[rb].kind == nkRefTy + regs[ra] = regs[rb].sons[0] + of opcAddInt: + decodeBC(nkIntLit) + regs[ra].intVal = regs[rb].intVal + regs[rc].intVal + of opcAddImmInt: + decodeBImm(nkIntLit) + regs[ra].intVal = regs[rb].intVal + imm + of opcSubInt: + decodeBC(nkIntLit) + regs[ra].intVal = regs[rb].intVal - regs[rc].intVal + of opcSubImmInt: + decodeBImm(nkIntLit) + regs[ra].intVal = regs[rb].intVal - imm + of opcLenSeq: + decodeBImm(nkIntLit) + assert regs[rb].kind == nkBracket + regs[ra].intVal = regs[rb].len - imm + of opcLenStr: + decodeBImm(nkIntLit) + assert regs[rb].kind == nkStrLit + regs[ra].intVal = regs[rb].strVal.len - imm + of opcIncl: + decodeB(nkCurly) + if not inSet(regs[ra], regs[rb]): addSon(regs[ra], copyTree(regs[rb])) + of opcExcl: + decodeB(nkCurly) + # XXX arg we need types here :-( + var b = newNodeIT(nkCurly, regs[rb].info, regs[rb].typ) + addSon(b, regs[rb]) + var r = diffSets(regs[ra], b) + discardSons(regs[ra]) + for i in countup(0, sonsLen(r) - 1): addSon(regs[ra], r.sons[i]) + of opcCard: + decodeB(nkIntLit) + regs[ra].intVal = nimsets.cardSet(regs[rb]) + of opcMulInt: + decodeBC(nkIntLit) + regs[ra].intVal = regs[rb].intVal * regs[rc].intVal + of opcDivInt: + decodeBC(nkIntLit) + regs[ra].intVal = regs[rb].intVal div regs[rc].intVal + of opcModInt: + decodeBC(nkIntLit) + regs[ra].intVal = regs[rb].intVal mod regs[rc].intVal + of opcAddFloat: + decodeBC(nkFloatLit) + regs[ra].floatVal = regs[rb].floatVal + regs[rc].floatVal + of opcSubFloat: + decodeBC(nkFloatLit) + regs[ra].floatVal = regs[rb].floatVal - regs[rc].floatVal + of opcMulFloat: + decodeBC(nkFloatLit) + regs[ra].floatVal = regs[rb].floatVal * regs[rc].floatVal + of opcDivFloat: + decodeBC(nkFloatLit) + regs[ra].floatVal = regs[rb].floatVal / regs[rc].floatVal + of opcShrInt: + decodeBC(nkIntLit) + regs[ra].intVal = regs[rb].intVal shr regs[rc].intVal + of opcShlInt: + decodeBC(nkIntLit) + regs[ra].intVal = regs[rb].intVal shl regs[rc].intVal + of opcBitandInt: + decodeBC(nkIntLit) + regs[ra].intVal = regs[rb].intVal and regs[rc].intVal + of opcBitorInt: + decodeBC(nkIntLit) + regs[ra].intVal = regs[rb].intVal or regs[rc].intVal + of opcBitxorInt: + decodeBC(nkIntLit) + regs[ra].intVal = regs[rb].intVal xor regs[rc].intVal + of opcAddu: + decodeBC(nkIntLit) + regs[ra].intVal = regs[rb].intVal +% regs[rc].intVal + of opcSubu: + decodeBC(nkIntLit) + regs[ra].intVal = regs[rb].intVal -% regs[rc].intVal + of opcMulu: + decodeBC(nkIntLit) + regs[ra].intVal = regs[rb].intVal *% regs[rc].intVal + of opcDivu: + decodeBC(nkIntLit) + regs[ra].intVal = regs[rb].intVal /% regs[rc].intVal + of opcModu: + decodeBC(nkIntLit) + regs[ra].intVal = regs[rb].intVal %% regs[rc].intVal + of opcEqInt: + decodeBC(nkIntLit) + regs[ra].intVal = ord(regs[rb].intVal == regs[rc].intVal) + of opcLeInt: + decodeBC(nkIntLit) + regs[ra].intVal = ord(regs[rb].intVal <= regs[rc].intVal) + of opcLtInt: + decodeBC(nkIntLit) + regs[ra].intVal = ord(regs[rb].intVal < regs[rc].intVal) + of opcEqFloat: + decodeBC(nkIntLit) + regs[ra].intVal = ord(regs[rb].floatVal == regs[rc].floatVal) + of opcLeFloat: + decodeBC(nkIntLit) + regs[ra].intVal = ord(regs[rb].floatVal <= regs[rc].floatVal) + of opcLtFloat: + decodeBC(nkIntLit) + regs[ra].intVal = ord(regs[rb].floatVal < regs[rc].floatVal) + of opcLeu: + decodeBC(nkIntLit) + regs[ra].intVal = ord(regs[rb].intVal <=% regs[rc].intVal) + of opcLtu: + decodeBC(nkIntLit) + regs[ra].intVal = ord(regs[rb].intVal <% regs[rc].intVal) + of opcEqRef: + decodeBC(nkIntLit) + regs[ra].intVal = ord(regs[rb] == regs[rc]) + # XXX is this correct? nope ... + of opcXor: + decodeBC(nkIntLit) + regs[ra].intVal = ord(regs[rb].intVal != regs[rc].intVal) + of opcNot: + decodeB(nkIntLit) + assert regs[rb].kind == nkIntLit + regs[ra].intVal = 1 - regs[rb].intVal + of opcUnaryMinusInt: + decodeB(nkIntLit) + assert regs[rb].kind == nkIntLit + regs[ra].intVal = -regs[rb].intVal + of opcUnaryMinusFloat: + decodeB(nkFloatLit) + assert regs[rb].kind == nkFloatLit + regs[ra].floatVal = -regs[rb].floatVal + of opcBitnotInt: + decodeB(nkIntLit) + assert regs[rb].kind == nkIntLit + regs[ra].intVal = not regs[rb].intVal + of opcEqStr: + decodeBC(nkIntLit) + regs[ra].intVal = Ord(regs[rb].strVal == regs[rc].strVal) + of opcLeStr: + decodeBC(nkIntLit) + regs[ra].intVal = Ord(regs[rb].strVal <= regs[rc].strVal) + of opcLtStr: + decodeBC(nkIntLit) + regs[ra].intVal = Ord(regs[rb].strVal < regs[rc].strVal) + of opcLeSet: + decodeBC(nkIntLit) + regs[ra].intVal = Ord(containsSets(regs[rb], regs[rc])) + of opcEqSet: + decodeBC(nkIntLit) + regs[ra].intVal = Ord(equalSets(regs[rb], regs[rc])) + of opcLtSet: + decodeBC(nkIntLit) + let a = regs[rb] + let b = regs[rc] + regs[ra].intVal = Ord(containsSets(a, b) and not equalSets(a, b)) + of opcMulSet: + decodeBC(nkCurly) + move(regs[ra].sons, nimsets.intersectSets(regs[rb], regs[rc]).sons) + of opcPlusSet: + decodeBC(nkCurly) + move(regs[ra].sons, nimsets.unionSets(regs[rb], regs[rc]).sons) + of opcMinusSet: + decodeBC(nkCurly) + move(regs[ra].sons, nimsets.diffSets(regs[rb], regs[rc]).sons) + of opcSymDiffSet: + decodeBC(nkCurly) + move(regs[ra].sons, nimsets.symdiffSets(regs[rb], regs[rc]).sons) + of opcConcatStr: + decodeBC(nkStrLit) + regs[ra].strVal = getstr(regs[rb]) + for i in rb+1..rb+rc-1: + regs[ra].strVal.add getstr(regs[i]) + of opcEcho: + echo regs[ra].strVal + of opcContainsSet: + decodeBC(nkIntLit) + regs[ra].intVal = Ord(inSet(regs[rb], regs[rc])) + of opcSubStr: + decodeBC(nkStrLit) + inc pc + assert c.code[pc].opcode == opcSubStr + let rd = c.code[pc].regA + regs[ra].strVal = substr(regs[rb].strVal, regs[rc].intVal.int, + regs[rd].intVal.int) + of opcIndCall, opcIndCallAsgn: + # dest = call regStart, n; where regStart = fn, arg1, ... + let rb = instr.regB + let rc = instr.regC + let prc = regs[rb].sym + let newPc = compile(c, prc) + var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos) + newSeq(newFrame.slots, prc.position) + if not isEmptyType(prc.typ.sons[0]): + newFrame.slots[0] = getNullValue(prc.typ.sons[0], prc.info) + # pass every parameter by var (the language definition allows this): + for i in 1 .. rc-1: + newFrame.slots[i] = regs[rb+i] + # allocate the temporaries: + for i in rc .. = 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 .. = 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.. = 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 .. = 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..