# # # 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 except getstr import strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned, parser, vmdeps, idents, trees, renderer, options from semfold import leValueConv, ordinalValToString from evaltempl import evalTemplate when hasFFI: import evalffi 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(nimComputedGoto): {.pragma: computedGoto.} proc myreset(n: PNode) = when defined(system.reset): var oldInfo = n.info reset(n[]) n.info = oldInfo proc skipMeta(n: PNode): PNode = (if n.kind != nkMetaNode: n else: n.sons[0]) proc setMeta(n, child: PNode) = assert n.kind == nkMetaNode let child = child.skipMeta if n.sons.isNil: n.sons = @[child] else: n.sons[0] = child proc uast(n: PNode): PNode {.inline.} = # "underlying ast" assert n.kind == nkMetaNode n.sons[0] 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 declBC() {.immediate, dirty.} = let rb = instr.regB let rc = instr.regC 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) template move(a, b: expr) {.immediate, dirty.} = system.shallowCopy(a, b) # XXX fix minor 'shallowCopy' overloading bug in compiler proc moveConst(x, y: PNode) = if x.kind != y.kind: 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: move(x.strVal, y.strVal) of nkIdent: x.ident = y.ident of nkSym: x.sym = y.sym of nkMetaNode: if x.sons.isNil: x.sons = @[y.sons[0]] else: x.sons[0] = y.sons[0] else: if x.kind notin {nkEmpty..nkNilLit}: move(x.sons, y.sons) # this seems to be the best way to model the reference semantics # of PNimrodNode: template asgnRef(x, y: expr) = moveConst(x, y) proc copyValue(src: PNode): PNode = if src == nil or nfIsRef in src.flags: return src result = newNode(src.kind) result.info = src.info result.typ = src.typ result.flags = src.flags * PersistentNodeFlags when defined(useNodeIds): if result.id == nodeIdToDebug: echo "COMES FROM ", src.id case src.kind of nkCharLit..nkUInt64Lit: result.intVal = src.intVal of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal of nkSym: result.sym = src.sym of nkIdent: result.ident = src.ident of nkStrLit..nkTripleStrLit: result.strVal = src.strVal else: newSeq(result.sons, sonsLen(src)) for i in countup(0, sonsLen(src) - 1): result.sons[i] = copyValue(src.sons[i]) proc asgnComplex(x, y: PNode) = if x.kind != y.kind: 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 of nkMetaNode: if x.sons.isNil: x.sons = @[y.sons[0]] else: x.sons[0] = y.sons[0] else: if x.kind notin {nkEmpty..nkNilLit}: let y = y.copyValue for i in countup(0, sonsLen(y) - 1): if i < x.len: x.sons[i] = y.sons[i] else: addSon(x, y.sons[i]) template getstr(a: expr): expr = (if a.kind in {nkStrLit..nkTripleStrLit}: 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 cleanUpOnException(c: PCtx; tos: PStackFrame; regs: TNodeSeq): int = let raisedType = c.currentExceptionA.typ.skipTypes(abstractPtrs) var f = tos while true: while f.safePoints.isNil or f.safePoints.len == 0: f = f.next if f.isNil: return -1 var pc2 = f.safePoints[f.safePoints.high] 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 # not the right one: discard f.safePoints.pop 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 opConv*(dest, src: PNode, typ: PType): bool = if typ.kind == tyString: if dest.kind != nkStrLit: myreset(dest) dest.kind = nkStrLit case src.typ.skipTypes(abstractRange).kind of tyEnum: dest.strVal = ordinalValToString(src) of tyInt..tyInt64, tyUInt..tyUInt64: dest.strVal = $src.intVal of tyBool: dest.strVal = if src.intVal == 0: "false" else: "true" of tyFloat..tyFloat128: dest.strVal = $src.floatVal of tyString, tyCString: dest.strVal = src.strVal of tyChar: dest.strVal = $chr(src.intVal) else: internalError("cannot convert to string " & typ.typeToString) else: case skipTypes(typ, abstractRange).kind of tyInt..tyInt64: if dest.kind != nkIntLit: myreset(dest); dest.kind = nkIntLit case skipTypes(src.typ, abstractRange).kind of tyFloat..tyFloat64: dest.intVal = system.toInt(src.floatVal) else: dest.intVal = src.intVal if dest.intVal < firstOrd(typ) or dest.intVal > lastOrd(typ): return true of tyUInt..tyUInt64: if dest.kind != nkIntLit: myreset(dest); dest.kind = nkIntLit case skipTypes(src.typ, abstractRange).kind of tyFloat..tyFloat64: dest.intVal = system.toInt(src.floatVal) else: dest.intVal = src.intVal and ((1 shl typ.size)-1) of tyFloat..tyFloat64: if dest.kind != nkFloatLit: myreset(dest); dest.kind = nkFloatLit case skipTypes(src.typ, abstractRange).kind of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyBool, tyChar: dest.floatVal = toFloat(src.intVal.int) else: dest.floatVal = src.floatVal else: asgnComplex(dest, src) proc compile(c: PCtx, s: PSym): int = result = vmgen.genProc(c, s) #c.echoCode proc regsContents(regs: TNodeSeq) = for i in 0.. high(int): stackTrace(c, tos, pc, errIndexOutOfBounds) let idx = regs[rc].intVal.int # XXX what if the array is not 0-based? -> codegen should insert a sub assert regs[rb].kind != nkMetaNode let src = regs[rb] if src.kind notin {nkEmpty..nkNilLit} and idx <% src.len: asgnComplex(regs[ra], src.sons[idx]) else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcLdStrIdx: decodeBC(nkIntLit) let idx = regs[rc].intVal.int if idx <=% regs[rb].strVal.len: regs[ra].intVal = regs[rb].strVal[idx].ord else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcWrArr: # a[b] = c let rb = instr.regB let rc = instr.regC let idx = regs[rb].intVal.int if idx <% regs[ra].len: asgnComplex(regs[ra].sons[idx], regs[rc]) else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcWrArrRef: let rb = instr.regB let rc = instr.regC let idx = regs[rb].intVal.int if idx <% regs[ra].len: asgnRef(regs[ra].sons[idx], regs[rc]) else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcLdObj: # a = b.c let rb = instr.regB let rc = instr.regC # XXX this creates a wrong alias #Message(c.debug[pc], warnUser, $regs[rb].safeLen & " " & $rc) asgnComplex(regs[ra], regs[rb].sons[rc]) of opcWrObj: # a.b = c let rb = instr.regB let rc = instr.regC #if regs[ra].isNil or regs[ra].sons.isNil or rb >= len(regs[ra]): # debug regs[ra] # debug regs[rc] # echo "RB ", rb # internalError(c.debug[pc], "argl") 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 if idx <% regs[ra].strVal.len: regs[ra].strVal[idx] = chr(regs[rc].intVal) else: stackTrace(c, tos, pc, errIndexOutOfBounds) 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 # XXX this is not correct 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 # also used by mNLen: regs[ra].intVal = regs[rb].skipMeta.len - imm of opcLenStr: decodeBImm(nkIntLit) if regs[rb].kind == nkNilLit: stackTrace(c, tos, pc, errNilAccess) else: assert regs[rb].kind in {nkStrLit..nkTripleStrLit} 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 opcInclRange: decodeBC(nkCurly) var r = newNode(nkRange) r.add regs[rb] r.add regs[rc] addSon(regs[ra], r.copyTree) of opcExcl: decodeB(nkCurly) 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].kind == nkNilLit and regs[rc].kind == nkNilLit) or regs[rb].sons == regs[rc].sons) of opcEqNimrodNode: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].skipMeta == regs[rc].skipMeta) 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 opcAddStrCh: decodeB(nkStrLit) regs[ra].strVal.add(regs[rb].intVal.chr) of opcAddStrStr: decodeB(nkStrLit) regs[ra].strVal.add(regs[rb].strVal) of opcAddSeqElem: decodeB(nkBracket) regs[ra].add(copyTree(regs[rb])) of opcEcho: let rb = instr.regB for i in ra..ra+rb-1: #if regs[i].kind != nkStrLit: debug regs[i] write(stdout, regs[i].strVal) writeln(stdout, "") 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 opcRangeChck: let rb = instr.regB let rc = instr.regC if not (leValueConv(regs[rb], regs[ra]) and leValueConv(regs[ra], regs[rc])): stackTrace(c, tos, pc, errGenerated, msgKindToString(errIllegalConvFromXtoY) % [ "unknown type" , "unknown type"]) of opcIndCall, opcIndCallAsgn: # dest = call regStart, n; where regStart = fn, arg1, ... let rb = instr.regB let rc = instr.regC let isClosure = regs[rb].kind == nkPar let prc = if not isClosure: regs[rb].sym else: regs[rb].sons[0].sym if sfImportc in prc.flags: if allowFFI notin c.features: globalError(c.debug[pc], errGenerated, "VM not allowed to do FFI") # we pass 'tos.slots' instead of 'regs' so that the compiler can keep # 'regs' in a register: when hasFFI: let prcValue = c.globals.sons[prc.position-1] if prcValue.kind == nkEmpty: globalError(c.debug[pc], errGenerated, "canot run " & prc.name.s) let newValue = callForeignFunction(prcValue, prc.typ, tos.slots, rb+1, rc-1, c.debug[pc]) if newValue.kind != nkEmpty: assert instr.opcode == opcIndCallAsgn asgnRef(regs[ra], newValue) else: globalError(c.debug[pc], errGenerated, "VM not built with FFI support") elif prc.kind != skTemplate: let newPc = compile(c, prc) #echo "new pc ", newPc, " calling: ", prc.name.s var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos) newSeq(newFrame.slots, prc.offset) if not isEmptyType(prc.typ.sons[0]) or prc.kind == skMacro: 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] if isClosure: newFrame.slots[rc] = regs[rb].sons[1] # allocate the temporaries: for i in rc+ord(isClosure) .. = 0) of opcIs: decodeBC(nkIntLit) let t1 = regs[rb].typ.skipTypes({tyTypeDesc}) let t2 = c.types[regs[rc].intVal.int] # XXX: This should use the standard isOpImpl let match = if t2.kind == tyTypeClass: true else: sameType(t1, t2) regs[ra].intVal = ord(match) of opcSetLenSeq: decodeB(nkBracket) let newLen = regs[rb].getOrdValue.int setLen(regs[ra].sons, newLen) of opcSwap, opcReset: internalError(c.debug[pc], "too implement") of opcIsNil: decodeB(nkIntLit) regs[ra].intVal = ord(regs[rb].skipMeta.kind == nkNilLit) of opcNBindSym: decodeBx(nkMetaNode) setMeta(regs[ra], copyTree(c.constants.sons[rbx])) of opcNChild: decodeBC(nkMetaNode) if regs[rb].kind != nkMetaNode: internalError(c.debug[pc], "no MetaNode") let idx = regs[rc].intVal.int let src = regs[rb].uast if src.kind notin {nkEmpty..nkNilLit} and idx <% src.len: setMeta(regs[ra], src.sons[idx]) else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcNSetChild: decodeBC(nkMetaNode) let idx = regs[rb].intVal.int var dest = regs[ra].uast if dest.kind notin {nkEmpty..nkNilLit} and idx <% dest.len: dest.sons[idx] = regs[rc].uast else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcNAdd: decodeBC(nkMetaNode) var u = regs[rb].uast u.add(regs[rc].uast) setMeta(regs[ra], u) of opcNAddMultiple: decodeBC(nkMetaNode) let x = regs[rc] var u = regs[rb].uast # XXX can be optimized: for i in 0.. ord(high(TNodeKind)) or k == ord(nkMetaNode): internalError(c.debug[pc], "request to create a NimNode of invalid kind") let cc = regs[rc].skipMeta setMeta(regs[ra], newNodeI(TNodeKind(int(k)), if cc.kind == nkNilLit: c.debug[pc] else: cc.info)) regs[ra].sons[0].flags.incl nfIsRef of opcNCopyNimNode: decodeB(nkMetaNode) setMeta(regs[ra], copyNode(regs[rb])) of opcNCopyNimTree: decodeB(nkMetaNode) setMeta(regs[ra], copyTree(regs[rb])) of opcNDel: decodeBC(nkMetaNode) let bb = regs[rb].intVal.int for i in countup(0, regs[rc].intVal.int-1): delSon(regs[ra].uast, bb) of opcGenSym: decodeBC(nkMetaNode) let k = regs[rb].intVal let name = if regs[rc].strVal.len == 0: ":tmp" else: regs[rc].strVal if k < 0 or k > ord(high(TSymKind)): internalError(c.debug[pc], "request to create symbol of invalid kind") var sym = newSym(k.TSymKind, name.getIdent, c.module, c.debug[pc]) incl(sym.flags, sfGenSym) setMeta(regs[ra], newSymNode(sym)) of opcTypeTrait: # XXX only supports 'name' for now; we can use regC to encode the # type trait operation decodeB(nkStrLit) let typ = regs[rb].sym.typ.skipTypes({tyTypeDesc}) regs[ra].strVal = typ.typeToString(preferExported) of opcGlobalOnce: let rb = instr.regBx if c.globals.sons[rb - wordExcess - 1].kind != nkEmpty: # skip initialization instructions: while true: inc pc if c.code[pc].opcode in {opcWrGlobal, opcWrGlobalRef} and c.code[pc].regBx == rb: break of opcGlobalAlias: let rb = instr.regBx - wordExcess - 1 regs[ra] = c.globals.sons[rb] inc pc proc fixType(result, n: PNode) {.inline.} = # XXX do it deeply for complex values; there seems to be no simple # solution except to check it deeply here. #if result.typ.isNil: result.typ = n.typ proc execute(c: PCtx, start: int): PNode = var tos = PStackFrame(prc: nil, comesFrom: 0, next: nil) newSeq(tos.slots, c.prc.maxSlots) for i in 0 .. 100: globalError(n.info, errTemplateInstantiationTooNested) setupGlobalCtx(module) var c = globalCtx c.callsite = nOrig let start = genProc(c, sym) var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil) let maxSlots = sym.offset newSeq(tos.slots, maxSlots) # setup arguments: var L = n.safeLen if L == 0: L = 1 # This is wrong for tests/reject/tind1.nim where the passed 'else' part # doesn't end up in the parameter: #InternalAssert tos.slots.len >= L # return value: tos.slots[0] = newNodeIT(nkNilLit, n.info, sym.typ.sons[0]) # setup parameters: for i in 1 .. < min(tos.slots.len, L): tos.slots[i] = setupMacroParam(n.sons[i]) # temporary storage: for i in L ..