From 2c2ae2adb2c5235e8535390efa4dbe3a8f18bcfe Mon Sep 17 00:00:00 2001 From: jbe Date: Wed, 17 Jul 2013 00:52:32 +0200 Subject: [PATCH 1/5] export pointer types for SDL event objects --- lib/wrappers/sdl/sdl.nim | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/wrappers/sdl/sdl.nim b/lib/wrappers/sdl/sdl.nim index a6f8de5d7..1d43ca556 100644 --- a/lib/wrappers/sdl/sdl.nim +++ b/lib/wrappers/sdl/sdl.nim @@ -956,7 +956,7 @@ type gain*: byte # Whether given states were gained or lost (1/0) state*: byte # A mask of the focus states - PKeyboardEvent = ptr TKeyboardEvent + PKeyboardEvent* = ptr TKeyboardEvent TKeyboardEvent*{.final.} = object # SDL_KEYDOWN or SDL_KEYUP # Mouse motion event structure kind*: TEventKind @@ -964,7 +964,7 @@ type state*: byte # SDL_PRESSED or SDL_RELEASED keysym*: TKeySym - PMouseMotionEvent = ptr TMouseMotionEvent + PMouseMotionEvent* = ptr TMouseMotionEvent TMouseMotionEvent*{.final.} = object # SDL_MOUSEMOTION # Mouse button event structure kind*: TEventKind @@ -974,7 +974,7 @@ type xrel*: int16 # The relative motion in the X direction yrel*: int16 # The relative motion in the Y direction - PMouseButtonEvent = ptr TMouseButtonEvent + PMouseButtonEvent* = ptr TMouseButtonEvent TMouseButtonEvent*{.final.} = object # SDL_MOUSEBUTTONDOWN or SDL_MOUSEBUTTONUP # Joystick axis motion event structure kind*: TEventKind @@ -984,7 +984,7 @@ type x*: UInt16 # The X coordinates of the mouse at press time y*: UInt16 # The Y coordinates of the mouse at press time - PJoyAxisEvent = ptr TJoyAxisEvent + PJoyAxisEvent* = ptr TJoyAxisEvent TJoyAxisEvent*{.final.} = object # SDL_JOYAXISMOTION # Joystick trackball motion event structure kind*: TEventKind @@ -992,7 +992,7 @@ type axis*: byte # The joystick axis index value*: int16 # The axis value (range: -32768 to 32767) - PJoyBallEvent = ptr TJoyBallEvent + PJoyBallEvent* = ptr TJoyBallEvent TJoyBallEvent*{.final.} = object # SDL_JOYAVBALLMOTION # Joystick hat position change event structure kind*: TEventKind @@ -1001,7 +1001,7 @@ type xrel*: int16 # The relative motion in the X direction yrel*: int16 # The relative motion in the Y direction - PJoyHatEvent = ptr TJoyHatEvent + PJoyHatEvent* = ptr TJoyHatEvent TJoyHatEvent*{.final.} = object # SDL_JOYHATMOTION */ # Joystick button event structure kind*: TEventKind @@ -1013,7 +1013,7 @@ type # 6 5 4 # Note that zero means the POV is centered. - PJoyButtonEvent = ptr TJoyButtonEvent + PJoyButtonEvent* = ptr TJoyButtonEvent TJoyButtonEvent*{.final.} = object # SDL_JOYBUTTONDOWN or SDL_JOYBUTTONUP # The "window resized" event # When you get this event, you are @@ -1024,7 +1024,7 @@ type button*: byte # The joystick button index state*: byte # SDL_PRESSED or SDL_RELEASED - PResizeEvent = ptr TResizeEvent + PResizeEvent* = ptr TResizeEvent TResizeEvent*{.final.} = object # SDL_VIDEORESIZE # A user-defined event type kind*: TEventKind @@ -1115,11 +1115,11 @@ type kind*: TEventKind msg*: PSysWMmsg - PExposeEvent = ptr TExposeEvent + PExposeEvent* = ptr TExposeEvent TExposeEvent*{.final.} = object kind*: TEventKind - PQuitEvent = ptr TQuitEvent + PQuitEvent* = ptr TQuitEvent TQuitEvent*{.final.} = object kind*: TEventKind From b30e237a3746b4c4c421a87277a6458642c9f13a Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 24 Jul 2013 21:39:39 +0200 Subject: [PATCH 2/5] first steps to a new evaluation engine --- compiler/nimeval.nim | 33 ++ compiler/vm.nim | 584 ++++++++++++++++++++++++ compiler/vmdef.nim | 149 +++++++ compiler/vmgen.nim | 1002 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1768 insertions(+) create mode 100644 compiler/nimeval.nim create mode 100644 compiler/vm.nim create mode 100644 compiler/vmdef.nim create mode 100644 compiler/vmgen.nim 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.. Date: Wed, 24 Jul 2013 21:58:17 +0200 Subject: [PATCH 3/5] added macros.genSym --- compiler/ast.nim | 2 +- compiler/evals.nim | 10 ++++++++++ lib/core/macros.nim | 13 ++++++++++--- todo.txt | 1 - web/news.txt | 2 ++ 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index e0c3b8e86..c86ba52c6 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -488,7 +488,7 @@ type mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr, mNBindSym, mLocals, mNCallSite, mEqIdent, mEqNimrodNode, mNHint, mNWarning, mNError, - mInstantiationInfo, mGetTypeInfo + mInstantiationInfo, mGetTypeInfo, mNGenSym # things that we can evaluate safely at compile time, even if not asked for it: const diff --git a/compiler/evals.nim b/compiler/evals.nim index f89417dc8..6266668f9 100644 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -1249,6 +1249,16 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode = of mNBindSym: # trivial implementation: result = n.sons[1] + of mNGenSym: + evalX(n.sons[1], {efLValue}) + let k = getOrdValue(result) + evalX(n.sons[2], {efLValue}) + let b = result + let name = if b.strVal.len == 0: ":tmp" else: b.strVal + if k < 0 or k > ord(high(TSymKind)): + internalError(n.info, "request to create a symbol with invalid kind") + result = newSymNode(newSym(k.TSymKind, name.getIdent, c.module, n.info)) + incl(result.sym.flags, sfGenSym) of mStrToIdent: result = evalAux(c, n.sons[1], {}) if isSpecial(result): return diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 720b15189..9130c3ffb 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -85,11 +85,13 @@ type TNimTypeKinds* = set[TNimrodTypeKind] TNimrodSymKind* = enum nskUnknown, nskConditional, nskDynLib, nskParam, - nskGenericParam, nskTemp, nskType, nskConst, - nskVar, nskProc, nskMethod, nskIterator, + nskGenericParam, nskTemp, nskModule, nskType, nskVar, nskLet, + nskConst, nskResult, + nskProc, nskMethod, nskIterator, nskConverter, nskMacro, nskTemplate, nskField, - nskEnumField, nskForVar, nskModule, nskLabel, + nskEnumField, nskForVar, nskLabel, nskStub + TNimSymKinds* = set[TNimrodSymKind] type @@ -220,6 +222,11 @@ proc bindSym*(ident: string, rule: TBindSymRule = brClosed): PNimrodNode {. ## If ``rule == brForceOpen`` always an ``nkOpenSymChoice`` tree is ## returned even if the symbol is not ambiguous. +proc genSym*(kind: TNimrodSymKind = nskLet; ident = ""): PNimrodNode {. + magic: "NGenSym".} + ## generates a fresh symbol that is guaranteed to be unique. The symbol + ## needs to occur in a declaration context. + proc callsite*(): PNimrodNode {.magic: "NCallSite".} ## returns the AST if the invokation expression that invoked this macro. diff --git a/todo.txt b/todo.txt index 35c6a5704..f213567ec 100644 --- a/todo.txt +++ b/todo.txt @@ -8,7 +8,6 @@ version 0.9.4 - mocking support with ``tyProxy`` that does: fallback for ``.`` operator - overloading of ``.``? Special case ``.=``? - built-in 'getImpl' -- macros.gensym still missing? - optimize 'genericReset'; 'newException' leads to code bloat diff --git a/web/news.txt b/web/news.txt index 74b33e2bf..56919d56c 100644 --- a/web/news.txt +++ b/web/news.txt @@ -14,6 +14,8 @@ Bugfixes Library Additions ----------------- +- Added ``macros.genSym`` builtin for AST generation. + Changes affecting backwards compatibility ----------------------------------------- From b1d4dfa6b1dc1b4f28f4634e95346d893f328465 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 24 Jul 2013 22:32:09 +0200 Subject: [PATCH 4/5] fixes #544 --- compiler/semfold.nim | 4 ++++ compiler/semtypes.nim | 4 +++- compiler/types.nim | 4 ++-- tests/compile/tvarious.nim | 12 ++++++++++++ 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 6fdb780c9..9c158b9ef 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -625,6 +625,10 @@ proc getConstExpr(m: PSym, n: PNode): PNode = result = newIntNodeT(sonsLen(a), n) else: result = magicCall(m, n) + of mLengthArray: + # It doesn't matter if the argument is const or not for mLengthArray. + # This fixes bug #544. + result = newIntNodeT(lengthOrd(n.sons[1].typ), n) of mAstToStr: result = newStrNodeT(renderTree(n[1], {renderNoComments}), n) of mConStrStr: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 7efa207a8..47d600e16 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -841,7 +841,9 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = LocalError(n.info, errTypeExpected) result = newOrPrevType(tyError, prev, c) of nkCallKinds: - if n[0].kind == nkIdent: + if isRange(n): + result = semRangeAux(c, n, prev) + elif n[0].kind == nkIdent: let op = n.sons[0].ident if op.id in {ord(wAnd), ord(wOr)} or op.s == "|": checkSonsLen(n, 3) diff --git a/compiler/types.nim b/compiler/types.nim index 3096b73c8..9dad958b8 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -959,8 +959,8 @@ proc skipGenericAlias*(t: PType): PType = proc matchTypeClass*(bindings: var TIdTable, typeClass, t: PType): bool = for i in countup(0, typeClass.sonsLen - 1): let req = typeClass.sons[i] - var match = req.kind == skipTypes(t, {tyRange, tyGenericInst}).kind - + var match = req.kind == skipTypes(t, {tyGenericInst, tyRange}).kind or + req.kind == skipTypes(t, {tyGenericInst}).kind if not match: case req.kind of tyGenericBody: diff --git a/tests/compile/tvarious.nim b/tests/compile/tvarious.nim index e301b34f6..e91de9245 100644 --- a/tests/compile/tvarious.nim +++ b/tests/compile/tvarious.nim @@ -46,3 +46,15 @@ while i < s.len: i = i + 1 write(stdout, "Du heißt " & s) + +# bug #544 + +type Bar [T; I:range] = array[I, T] +proc foo*[T; I:range](a, b: Bar[T, I]): Bar[T, I] = + when len(a) != 3: + # Error: constant expression expected + {.fatal:"Dimensions have to be 3".} + #... +block: + var a, b: Bar[int, 0..2] + discard foo(a, b) From 9acdf94cc04726cb80e3012849d8f56f06615017 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 24 Jul 2013 23:07:28 +0200 Subject: [PATCH 5/5] fixes #531 --- compiler/semgnrc.nim | 2 +- tests/manyloc/named_argument_bug/gui.nim | 44 +++++ tests/manyloc/named_argument_bug/main.nim | 23 +++ .../named_argument_bug/main.nimrod.cfg | 2 + .../named_argument_bug/tri_engine/config.nim | 6 + .../tri_engine/gfx/color.nim | 57 +++++++ .../tri_engine/gfx/gl/gl.nim | 61 +++++++ .../tri_engine/gfx/gl/primitive.nim | 157 ++++++++++++++++++ .../tri_engine/gfx/gl/shader.nim | 103 ++++++++++++ .../named_argument_bug/tri_engine/gfx/tex.nim | 31 ++++ .../tri_engine/math/circle.nim | 9 + .../tri_engine/math/rect.nim | 8 + .../tri_engine/math/vec.nim | 55 ++++++ .../tri_engine/tri_engine.nim | 106 ++++++++++++ 14 files changed, 663 insertions(+), 1 deletion(-) create mode 100644 tests/manyloc/named_argument_bug/gui.nim create mode 100644 tests/manyloc/named_argument_bug/main.nim create mode 100644 tests/manyloc/named_argument_bug/main.nimrod.cfg create mode 100644 tests/manyloc/named_argument_bug/tri_engine/config.nim create mode 100644 tests/manyloc/named_argument_bug/tri_engine/gfx/color.nim create mode 100644 tests/manyloc/named_argument_bug/tri_engine/gfx/gl/gl.nim create mode 100644 tests/manyloc/named_argument_bug/tri_engine/gfx/gl/primitive.nim create mode 100644 tests/manyloc/named_argument_bug/tri_engine/gfx/gl/shader.nim create mode 100644 tests/manyloc/named_argument_bug/tri_engine/gfx/tex.nim create mode 100644 tests/manyloc/named_argument_bug/tri_engine/math/circle.nim create mode 100644 tests/manyloc/named_argument_bug/tri_engine/math/rect.nim create mode 100644 tests/manyloc/named_argument_bug/tri_engine/math/vec.nim create mode 100644 tests/manyloc/named_argument_bug/tri_engine/tri_engine.nim diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index abee5de5a..d626d2eb2 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -312,7 +312,7 @@ proc semGenericStmt(c: PContext, n: PNode, n.sons[bodyPos] = semGenericStmtScope(c, body, flags, ctx) closeScope(c) of nkPragma, nkPragmaExpr: nil - of nkExprColonExpr: + of nkExprColonExpr, nkExprEqExpr: checkMinSonsLen(n, 2) result.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx) else: diff --git a/tests/manyloc/named_argument_bug/gui.nim b/tests/manyloc/named_argument_bug/gui.nim new file mode 100644 index 000000000..1e0bc6ffd --- /dev/null +++ b/tests/manyloc/named_argument_bug/gui.nim @@ -0,0 +1,44 @@ +import + tri_engine/gfx/gl/primitive, + tri_engine/gfx/tex, + tri_engine/gfx/color, + tri_engine/math/rect, + tri_engine/math/vec + +type + TWidgetLayer* = enum + wlBg = 100, + wlOverlap = 200, + wlMain = 300, + wlOverlay = 400, + wlCursor = 500 + TWidgetLayerType = TWidgetLayer|int + TWidgetType* = enum + wtImg + PWidget* = ref object + `type`* : TWidgetType + layer* : TWidgetLayer + rect* : TRect + prim* : PPrimitive + +const + baseZ = 5000 + +proc newWidget*(`type`: TWidgetType, layer: TWidgetLayerType, rect: TRect): PWidget = + new(result) + result.`type` = `type` + result.layer = layer + result.rect = rect + + var verts = newVert(rect) + + # This works because z is accessible at this scope. + #var z = baseZ + layer.int + #result.prim = newPrimitive(verts, z=z) + + # Doesn't work, because the compiler looks for a symbol called z in this scope, + # but it should only check that it is the name of one of the params. + #result.prim = newPrimitive(verts, z=baseZ + layer.int) + + # This doesn't work either. + result.prim = newPrimitive(verts, z=0) diff --git a/tests/manyloc/named_argument_bug/main.nim b/tests/manyloc/named_argument_bug/main.nim new file mode 100644 index 000000000..767674428 --- /dev/null +++ b/tests/manyloc/named_argument_bug/main.nim @@ -0,0 +1,23 @@ +import + tri_engine/config, + tri_engine/math/vec, + tri_engine/math/circle, + tri_engine/gfx/gl/primitive, + tri_engine/gfx/tex, + tri_engine/gfx/color, + tri_engine/tri_engine, + gui + +var isRunning = true + +block: + var renderer = newRenderer(w=10, h=10) + + var primitive = newPrimitiveCircle(0.3.TR, color=white(0.5, 0.8), z=15) + renderer.addPrimitive(primitive) + + var verts = newVert((min: newV2xy(-0.4), size: newV2xy(0.3))) + var primitive2 = newPrimitive(verts, color=red(0.5, 0.8), z=10) + renderer.addPrimitive(primitive2) + + var mainMenuWidget = newWidget(wtImg, wlBg, rect=(newV2xy(-1.0), newV2xy(2.0))) diff --git a/tests/manyloc/named_argument_bug/main.nimrod.cfg b/tests/manyloc/named_argument_bug/main.nimrod.cfg new file mode 100644 index 000000000..27cf8e688 --- /dev/null +++ b/tests/manyloc/named_argument_bug/main.nimrod.cfg @@ -0,0 +1,2 @@ +# this file only exists to mark 'main.nim' as the main file + diff --git a/tests/manyloc/named_argument_bug/tri_engine/config.nim b/tests/manyloc/named_argument_bug/tri_engine/config.nim new file mode 100644 index 000000000..b90dc0b26 --- /dev/null +++ b/tests/manyloc/named_argument_bug/tri_engine/config.nim @@ -0,0 +1,6 @@ +when defined(doublePrecision): + type + TR* = float64 +else: + type + TR* = float32 diff --git a/tests/manyloc/named_argument_bug/tri_engine/gfx/color.nim b/tests/manyloc/named_argument_bug/tri_engine/gfx/color.nim new file mode 100644 index 000000000..8e47c1f2f --- /dev/null +++ b/tests/manyloc/named_argument_bug/tri_engine/gfx/color.nim @@ -0,0 +1,57 @@ +import + tri_engine/config, + tri_engine/math/vec + +from strutils import + formatFloat, + TFloatFormat, + `%` + +from unsigned import + `shr`, + `and` + +type + TColor* = tuple[r, g, b, a: TR] + +converter toColor*(o: uint32): TColor = + ## Convert an integer to a color. This is mostly useful when the integer is specified as a hex + ## literal such as 0xFF00007F, which is 100% red, with 50% alpha. + ## TODO: turn this into a template that can take either 4 or 8 characters? + ((((o and 0xff000000'u32) shr 24).TR / 255.0).TR, + (((o and 0xff0000'u32) shr 16).TR / 255.0).TR, + (((o and 0xff00'u32) shr 8).TR / 255.0).TR, + (((o and 0xff'u32)).TR / 255.0).TR) + +converter toV4*(o: TColor): TV4[TR] = + cast[TV4[TR]](o) + +proc newColor*(r, g, b: TR=0.0, a: TR=1.0): TColor = + (r, g, b, a) + +proc white*(rgb, a: TR=1.0): TColor = + (rgb, rgb, rgb, a) + +proc red*(r, a: TR=1.0): TColor = + newColor(r=r, a=a) + +proc green*(g, a: TR=1.0): TColor = + newColor(g=g, a=a) + +proc yellow*(rg, a: TR=1.0): TColor = + newColor(r=rg, g=rg, a=a) + +proc blue*(b, a: TR=1.0): TColor = + newColor(b=b, a=a) + +proc cyan*(gb, a: TR=1.0): TColor = + newColor(g=gb, b=gb, a=a) + +proc purple*(rb, a: TR=1.0): TColor = + newColor(r=rb, b=rb, a=a) + +proc `$`*(o: TColor): string = + proc f(f: float): string = + f.formatFloat(precision=2, format=ffDecimal) + + "(r: $#, g: $#, b: $#, s: $#)" % [f(o.r), f(o.g), f(o.b), f(o.a)] diff --git a/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/gl.nim b/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/gl.nim new file mode 100644 index 000000000..e731969c1 --- /dev/null +++ b/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/gl.nim @@ -0,0 +1,61 @@ +import + opengl, + tri_engine/math/vec + +export + opengl + +type + EGL* = object of E_Base + EGL_code* = object of EGL + code*: EGL_err + EGL_err {.pure.} = enum + none = GL_NO_ERROR + invalidEnum = GL_INVALID_ENUM + invalidVal = GL_INVALID_VALUE + invalidOp = GL_INVALID_OPERATION + stackOverflow = GL_STACK_OVERFLOW + stackUnderflow = GL_STACK_UNDERFLOW + outOfMem = GL_OUT_OF_MEMORY + invalidFramebufferOp = GL_INVALID_FRAMEBUFFER_OPERATION + unknown + +proc newGL_codeException*(msg: string, code: EGL_err): ref EGL_code = + result = newException(EGL_code, $code) + result.code = code + +proc getErr*(): EGL_err = + result = glGetError().EGL_err + if result notin {EGL_err.none, + EGL_err.invalidEnum, + EGL_err.invalidVal, + EGL_err.invalidOp, + EGL_err.invalidFramebufferOp, + EGL_err.outOfMem, + EGL_err.stackUnderflow, + EGL_err.stackOverflow}: + return EGL_err.unknown + +proc errCheck*() = + let err = getErr() + if err != EGL_err.none: + raise newGL_codeException($err, err) + +macro `?`*(call: expr{nkCall}): expr = + result = call + # Can't yet reference foreign symbols in macros. + #errCheck() + +when defined(doublePrecision): + const + glRealType* = cGLdouble +else: + const + glRealType* = cGLfloat + +proc setUniformV4*[T](loc: GLint, vecs: var openarray[TV4[T]]) = + glUniform4fv(loc, vecs.len.GLsizei, cast[PGLfloat](vecs[0].addr)) + +proc setUniformV4*[T](loc: GLint, vec: TV4[T]) = + var vecs = [vec] + setUniformV4(loc, vecs) diff --git a/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/primitive.nim b/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/primitive.nim new file mode 100644 index 000000000..c67748967 --- /dev/null +++ b/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/primitive.nim @@ -0,0 +1,157 @@ +import + math, + tri_engine/config, + tri_engine/gfx/gl/gl, + tri_engine/gfx/tex, + tri_engine/gfx/color, + tri_engine/math/vec, + tri_engine/math/rect, + tri_engine/math/circle + +import strutils + +type + TVert* = tuple[pos: TV2[TR], texCoord: TV2[TR]] + TVertAttrib* = object + i* : GLuint + size* : GLint + stride* : GLsizei + offset* : PGLvoid + TVertMode* = enum + vmTriStrip = GLtriangleStrip, + vmTriFan = GLtriangleFan + TZ_range* = range[-100_000..100_000] + PPrimitive* = ref object + verts* : seq[TVert] + indices* : seq[GLushort] + arrBufId* : GLuint + elemArrBufId* : GLuint + tex* : TTex + color* : TColor + vertMode* : TVertMode + z* : int + +proc newVert*(pos, texCoord: TV2): TVert = + (pos, texCoord) + +proc newVertQuad*(min, minRight, maxLeft, max: TV2[TR]): seq[TVert] = + @[newVert(min, newV2()), + newVert(minRight, newV2(x=1.0)), + newVert(maxLeft, newV2(y=1.0)), + newVert(max, newV2xy(1.0)) + ] + +proc newVert*(rect: rect.TRect): seq[TVert] = + newVertQuad(rect.min, newV2(rect.max.x, rect.min.y), newV2(rect.min.x, rect.max.y), rect.max) + +proc newVertAttrib(i: GLuint, size: GLint, stride: GLsizei, offset: PGLvoid): TVertAttrib = + TVertAttrib(i: i, size: size, stride: stride, offset: offset) + +proc genBuf*[T](vboTarget, objUsage: GLenum, data: var openarray[T]): GLuint = + result = 0.GLuint + ?glGenBuffers(1, result.addr) + ?glBindBuffer(vboTarget, result) + + let size = (data.len * T.sizeof).GLsizeiptr + ?glBufferData(vboTarget, size, data[0].addr, objUsage) + +proc newPrimitive*(verts: var seq[TVert], + vertMode=vmTriStrip, + tex=whiteTex(), + color=white(), + z: TZ_range=0): PPrimitive = + var indices = newSeq[GLushort](verts.len) + for i in 0 .. y.z: + 1 + else: + 0) + + for x in zSortedPrimitives: + o.draw(x) + + disableVertAttribArrs() + +proc addPrimitive*(o: PRenderer, p: PPrimitive) = + o.primitives.add(p)