diff options
Diffstat (limited to 'compiler/vmgen.nim')
-rw-r--r-- | compiler/vmgen.nim | 1913 |
1 files changed, 1145 insertions, 768 deletions
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index d2243376c..0c7a49984 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -15,11 +15,10 @@ # this doesn't matter. However it matters for strings and other complex # types that use the 'node' field; the reason is that slots are # re-used in a register based VM. Example: -# -# .. code-block:: nim -# let s = a & b # no matter what, create fresh node -# s = a & b # no matter what, keep the node -# +# ```nim +# let s = a & b # no matter what, create fresh node +# s = a & b # no matter what, keep the node +# ``` # Also *stores* into non-temporary memory need to perform deep copies: # a.b = x.y # We used to generate opcAsgn for the *load* of 'x.y' but this is clearly @@ -27,23 +26,41 @@ # solves the opcLdConst vs opcAsgnConst issue. Of course whether we need # this copy depends on the involved types. +import std/[tables, intsets, strutils] + +when defined(nimPreviewSlimSystem): + import std/assertions + import - strutils, ast, astalgo, types, msgs, renderer, vmdef, - trees, intsets, magicsys, options, lowerings, lineinfos -import platform -from os import splitFile + ast, types, msgs, renderer, vmdef, trees, + magicsys, options, lowerings, lineinfos, transf, astmsgs + +from modulegraphs import getBody +when defined(nimCompilerStacktraceHints): + import std/stackframes + +const + debugEchoCode* = defined(nimVMDebug) + +when debugEchoCode: + import std/private/asciitables when hasFFI: import evalffi type - TGenFlag = enum gfAddrOf, gfFieldAccess + TGenFlag = enum + gfNode # Affects how variables are loaded - always loads as rkNode + gfNodeAddr # Affects how variables are loaded - always loads as rkNodeAddr + gfIsParam # do not deepcopy parameters, they are immutable + gfIsSinkParam # deepcopy sink parameters TGenFlags = set[TGenFlag] proc debugInfo(c: PCtx; info: TLineInfo): string = - result = toFilename(c.config, info).splitFile.name & ":" & $info.line + result = toFileLineCol(c.config, info) proc codeListing(c: PCtx, result: var string, start=0; last = -1) = + ## for debugging purposes # first iteration: compute all necessary labels: var jumpTargets = initIntSet() let last = if last < 0: c.code.len-1 else: min(last, c.code.len-1) @@ -52,7 +69,9 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) = if x.opcode in relativeJumps: jumpTargets.incl(i+x.regBx-wordExcess) - # for debugging purposes + template toStr(opc: TOpcode): string = ($opc).substr(3) + + result.add "code listing:\n" var i = start while i <= last: if i in jumpTargets: result.addf("L$1:\n", i) @@ -60,48 +79,52 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) = result.add($i) let opc = opcode(x) - if opc in {opcConv, opcCast}: + if opc in {opcIndCall, opcIndCallAsgn}: + result.addf("\t$#\tr$#, r$#, nargs:$#", opc.toStr, x.regA, + x.regB, x.regC) + elif opc in {opcConv, opcCast}: let y = c.code[i+1] let z = c.code[i+2] - result.addf("\t$#\tr$#, r$#, $#, $#", ($opc).substr(3), x.regA, x.regB, + result.addf("\t$#\tr$#, r$#, $#, $#", opc.toStr, x.regA, x.regB, c.types[y.regBx-wordExcess].typeToString, c.types[z.regBx-wordExcess].typeToString) inc i, 2 elif opc < firstABxInstr: - result.addf("\t$#\tr$#, r$#, r$#", ($opc).substr(3), x.regA, + result.addf("\t$#\tr$#, r$#, r$#", opc.toStr, x.regA, x.regB, x.regC) - elif opc in relativeJumps: - result.addf("\t$#\tr$#, L$#", ($opc).substr(3), x.regA, + elif opc in relativeJumps + {opcTry}: + result.addf("\t$#\tr$#, L$#", opc.toStr, x.regA, i+x.regBx-wordExcess) + elif opc in {opcExcept}: + let idx = x.regBx-wordExcess + result.addf("\t$#\t$#, $#", opc.toStr, x.regA, $idx) elif opc in {opcLdConst, opcAsgnConst}: let idx = x.regBx-wordExcess - result.addf("\t$#\tr$#, $# ($#)", ($opc).substr(3), x.regA, + result.addf("\t$#\tr$#, $# ($#)", opc.toStr, x.regA, c.constants[idx].renderTree, $idx) - elif opc in {opcMarshalLoad, opcMarshalStore}: - let y = c.code[i+1] - result.addf("\t$#\tr$#, r$#, $#", ($opc).substr(3), x.regA, x.regB, - c.types[y.regBx-wordExcess].typeToString) - inc i else: - result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, x.regBx-wordExcess) - result.add("\t#") + result.addf("\t$#\tr$#, $#", opc.toStr, x.regA, x.regBx-wordExcess) + result.add("\t# ") result.add(debugInfo(c, c.debug[i])) result.add("\n") inc i + when debugEchoCode: + result = result.alignTable proc echoCode*(c: PCtx; start=0; last = -1) {.deprecated.} = var buf = "" codeListing(c, buf, start, last) echo buf -proc gABC(ctx: PCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) = +proc gABC(ctx: PCtx; n: PNode; opc: TOpcode; + a: TRegister = 0, b: TRegister = 0, c: TRegister = 0) = ## Takes the registers `b` and `c`, applies the operation `opc` to them, and ## stores the result into register `a` ## The node is needed for debug information assert opc.ord < 255 - let ins = (opc.uint32 or (a.uint32 shl 8'u32) or - (b.uint32 shl 16'u32) or - (c.uint32 shl 24'u32)).TInstr + let ins = (opc.TInstrType or (a.TInstrType shl regAShift) or + (b.TInstrType shl regBShift) or + (c.TInstrType shl regCShift)).TInstr when false: if ctx.code.len == 43: writeStackTrace() @@ -110,13 +133,13 @@ proc gABC(ctx: PCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) = ctx.debug.add(n.info) proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) = - # Takes the `b` register and the immediate `imm`, appies the operation `opc`, + # Takes the `b` register and the immediate `imm`, applies the operation `opc`, # and stores the output value into `a`. # `imm` is signed and must be within [-128, 127] if imm >= -128 and imm <= 127: - let ins = (opc.uint32 or (a.uint32 shl 8'u32) or - (b.uint32 shl 16'u32) or - (imm+byteExcess).uint32 shl 24'u32).TInstr + let ins = (opc.TInstrType or (a.TInstrType shl regAShift) or + (b.TInstrType shl regBShift) or + (imm+byteExcess).TInstrType shl regCShift).TInstr c.code.add(ins) c.debug.add(n.info) else: @@ -125,20 +148,20 @@ proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) = proc gABx(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) = # Applies `opc` to `bx` and stores it into register `a` - # `bx` must be signed and in the range [-32768, 32767] + # `bx` must be signed and in the range [regBxMin, regBxMax] when false: if c.code.len == 43: writeStackTrace() echo "generating ", opc - if bx >= -32768 and bx <= 32767: - let ins = (opc.uint32 or a.uint32 shl 8'u32 or - (bx+wordExcess).uint32 shl 16'u32).TInstr + if bx >= regBxMin-1 and bx <= regBxMax: + let ins = (opc.TInstrType or a.TInstrType shl regAShift or + (bx+wordExcess).TInstrType shl regBxShift).TInstr c.code.add(ins) c.debug.add(n.info) else: localError(c.config, n.info, - "VM: immediate value does not fit into an int16") + "VM: immediate value does not fit into regBx") proc xjmp(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0): TPosition = #assert opc in {opcJmp, opcFJmp, opcTJmp} @@ -151,7 +174,7 @@ proc genLabel(c: PCtx): TPosition = proc jmpBack(c: PCtx, n: PNode, p = TPosition(0)) = let dist = p.int - c.code.len - internalAssert(c.config, -0x7fff < dist and dist < 0x7fff) + internalAssert(c.config, regBxMin < dist and dist < regBxMax) gABx(c, n, opcJmpBack, 0, dist) proc patch(c: PCtx, p: TPosition) = @@ -159,17 +182,17 @@ proc patch(c: PCtx, p: TPosition) = let p = p.int let diff = c.code.len - p #c.jumpTargets.incl(c.code.len) - internalAssert(c.config, -0x7fff < diff and diff < 0x7fff) + internalAssert(c.config, regBxMin < diff and diff < regBxMax) let oldInstr = c.code[p] # opcode and regA stay the same: - c.code[p] = ((oldInstr.uint32 and 0xffff'u32).uint32 or - uint32(diff+wordExcess) shl 16'u32).TInstr + c.code[p] = ((oldInstr.TInstrType and regBxMask).TInstrType or + TInstrType(diff+wordExcess) shl regBxShift).TInstr proc getSlotKind(t: PType): TSlotKind = case t.skipTypes(abstractRange-{tyTypeDesc}).kind of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64: slotTempInt - of tyString, tyCString: + of tyString, tyCstring: slotTempStr of tyFloat..tyFloat128: slotTempFloat @@ -180,56 +203,75 @@ const HighRegisterPressure = 40 proc bestEffort(c: PCtx): TLineInfo = - (if c.prc == nil: c.module.info else: c.prc.sym.info) + if c.prc != nil and c.prc.sym != nil: + c.prc.sym.info + else: + c.module.info -proc getTemp(cc: PCtx; tt: PType): TRegister = - let typ = tt.skipTypesOrNil({tyStatic}) +proc getFreeRegister(cc: PCtx; k: TSlotKind; start: int): TRegister = let c = cc.prc # we prefer the same slot kind here for efficiency. Unfortunately for # discardable return types we may not know the desired type. This can happen # for e.g. mNAdd[Multiple]: - let k = if typ.isNil: slotTempComplex else: typ.getSlotKind - for i in 0 .. c.maxSlots-1: - if c.slots[i].kind == k and not c.slots[i].inUse: - c.slots[i].inUse = true + for i in start..c.regInfo.len-1: + if c.regInfo[i].kind == k and not c.regInfo[i].inUse: + c.regInfo[i].inUse = true return TRegister(i) # if register pressure is high, we re-use more aggressively: - if c.maxSlots >= HighRegisterPressure and false: - for i in 0 .. c.maxSlots-1: - if not c.slots[i].inUse: - c.slots[i] = (inUse: true, kind: k) + if c.regInfo.len >= high(TRegister): + for i in start..c.regInfo.len-1: + if not c.regInfo[i].inUse: + c.regInfo[i] = (inUse: true, kind: k) return TRegister(i) - if c.maxSlots >= high(TRegister): + if c.regInfo.len >= high(TRegister): globalError(cc.config, cc.bestEffort, "VM problem: too many registers required") - result = TRegister(c.maxSlots) - c.slots[c.maxSlots] = (inUse: true, kind: k) - inc c.maxSlots + result = TRegister(max(c.regInfo.len, start)) + c.regInfo.setLen int(result)+1 + c.regInfo[result] = (inUse: true, kind: k) + +proc getTemp(cc: PCtx; tt: PType): TRegister = + let typ = tt.skipTypesOrNil({tyStatic}) + # we prefer the same slot kind here for efficiency. Unfortunately for + # discardable return types we may not know the desired type. This can happen + # for e.g. mNAdd[Multiple]: + let k = if typ.isNil: slotTempComplex else: typ.getSlotKind + result = getFreeRegister(cc, k, start = 0) + when false: + # enable this to find "register" leaks: + if result == 4: + echo "begin ---------------" + writeStackTrace() + echo "end ----------------" proc freeTemp(c: PCtx; r: TRegister) = let c = c.prc - if c.slots[r].kind in {slotSomeTemp..slotTempComplex}: c.slots[r].inUse = false + if r < c.regInfo.len and c.regInfo[r].kind in {slotSomeTemp..slotTempComplex}: + # this seems to cause https://github.com/nim-lang/Nim/issues/10647 + c.regInfo[r].inUse = false proc getTempRange(cc: PCtx; n: int; kind: TSlotKind): TRegister = # if register pressure is high, we re-use more aggressively: let c = cc.prc - if c.maxSlots >= HighRegisterPressure or c.maxSlots+n >= high(TRegister): - for i in 0 .. c.maxSlots-n: - if not c.slots[i].inUse: + # we could also customize via the following (with proper caching in ConfigRef): + # let highRegisterPressure = cc.config.getConfigVar("vm.highRegisterPressure", "40").parseInt + if c.regInfo.len >= HighRegisterPressure or c.regInfo.len+n >= high(TRegister): + for i in 0..c.regInfo.len-n: + if not c.regInfo[i].inUse: block search: - for j in i+1 .. i+n-1: - if c.slots[j].inUse: break search + for j in i+1..i+n-1: + if c.regInfo[j].inUse: break search result = TRegister(i) - for k in result .. result+n-1: c.slots[k] = (inUse: true, kind: kind) + for k in result..result+n-1: c.regInfo[k] = (inUse: true, kind: kind) return - if c.maxSlots+n >= high(TRegister): + if c.regInfo.len+n >= high(TRegister): globalError(cc.config, cc.bestEffort, "VM problem: 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) + result = TRegister(c.regInfo.len) + setLen c.regInfo, c.regInfo.len+n + for k in result..result+n-1: c.regInfo[k] = (inUse: true, kind: kind) proc freeTempRange(c: PCtx; start: TRegister, n: int) = - for i in start .. start+n-1: c.freeTemp(TRegister(i)) + for i in start..start+n-1: c.freeTemp(TRegister(i)) template withTemp(tmp, typ, body: untyped) {.dirty.} = var tmp = getTemp(c, typ) @@ -256,7 +298,9 @@ proc gen(c: PCtx; n: PNode; dest: TRegister; flags: TGenFlags = {}) = proc gen(c: PCtx; n: PNode; flags: TGenFlags = {}) = var tmp: TDest = -1 gen(c, n, tmp, flags) - #if n.typ.isEmptyType: InternalAssert tmp < 0 + if tmp >= 0: + freeTemp(c, tmp) + #if n.typ.isEmptyType: internalAssert tmp < 0 proc genx(c: PCtx; n: PNode; flags: TGenFlags = {}): TRegister = var tmp: TDest = -1 @@ -264,6 +308,8 @@ proc genx(c: PCtx; n: PNode; flags: TGenFlags = {}): TRegister = #internalAssert c.config, tmp >= 0 # 'nim check' does not like this internalAssert. if tmp >= 0: result = TRegister(tmp) + else: + result = 0 proc clearDest(c: PCtx; n: PNode; dest: var TDest) {.inline.} = # stmt is different from 'void' in meta programming contexts. @@ -273,112 +319,139 @@ proc clearDest(c: PCtx; n: PNode; dest: var TDest) {.inline.} = dest = -1 proc isNotOpr(n: PNode): bool = - n.kind in nkCallKinds and n.sons[0].kind == nkSym and - n.sons[0].sym.magic == mNot - -proc isTrue(n: PNode): bool = - n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or - n.kind == nkIntLit and n.intVal != 0 + n.kind in nkCallKinds and n[0].kind == nkSym and + n[0].sym.magic == mNot proc genWhile(c: PCtx; n: PNode) = - # L1: + # lab1: # cond, tmp - # fjmp tmp, L2 + # fjmp tmp, lab2 # body - # jmp L1 - # L2: - let L1 = c.genLabel + # jmp lab1 + # lab2: + let lab1 = c.genLabel withBlock(nil): - if isTrue(n.sons[0]): - c.gen(n.sons[1]) - c.jmpBack(n, L1) - elif isNotOpr(n.sons[0]): - var tmp = c.genx(n.sons[0].sons[1]) - let L2 = c.xjmp(n, opcTJmp, tmp) + if isTrue(n[0]): + c.gen(n[1]) + c.jmpBack(n, lab1) + elif isNotOpr(n[0]): + var tmp = c.genx(n[0][1]) + let lab2 = c.xjmp(n, opcTJmp, tmp) c.freeTemp(tmp) - c.gen(n.sons[1]) - c.jmpBack(n, L1) - c.patch(L2) + c.gen(n[1]) + c.jmpBack(n, lab1) + c.patch(lab2) else: - var tmp = c.genx(n.sons[0]) - let L2 = c.xjmp(n, opcFJmp, tmp) + var tmp = c.genx(n[0]) + let lab2 = c.xjmp(n, opcFJmp, tmp) c.freeTemp(tmp) - c.gen(n.sons[1]) - c.jmpBack(n, L1) - c.patch(L2) + c.gen(n[1]) + c.jmpBack(n, lab1) + c.patch(lab2) proc genBlock(c: PCtx; n: PNode; dest: var TDest) = - withBlock(n.sons[0].sym): - c.gen(n.sons[1], dest) + let oldRegisterCount = c.prc.regInfo.len + withBlock(n[0].sym): + c.gen(n[1], dest) + + for i in oldRegisterCount..<c.prc.regInfo.len: + #if c.prc.regInfo[i].kind in {slotFixedVar, slotFixedLet}: + if i != dest: + when not defined(release): + if c.config.cmd != cmdCheck: + if c.prc.regInfo[i].inUse and c.prc.regInfo[i].kind in {slotTempUnknown, + slotTempInt, + slotTempFloat, + slotTempStr, + slotTempComplex}: + raiseAssert "leaking temporary " & $i & " " & $c.prc.regInfo[i].kind + c.prc.regInfo[i] = (inUse: false, kind: slotEmpty) + c.clearDest(n, dest) proc genBreak(c: PCtx; n: PNode) = - let L1 = c.xjmp(n, opcJmp) - if n.sons[0].kind == nkSym: - #echo cast[int](n.sons[0].sym) + let lab1 = c.xjmp(n, opcJmp) + if n[0].kind == nkSym: + #echo cast[int](n[0].sym) for i in countdown(c.prc.blocks.len-1, 0): - if c.prc.blocks[i].label == n.sons[0].sym: - c.prc.blocks[i].fixups.add L1 + if c.prc.blocks[i].label == n[0].sym: + c.prc.blocks[i].fixups.add lab1 return globalError(c.config, n.info, "VM problem: cannot find 'break' target") else: - c.prc.blocks[c.prc.blocks.high].fixups.add L1 + c.prc.blocks[c.prc.blocks.high].fixups.add lab1 proc genIf(c: PCtx, n: PNode; dest: var TDest) = - # if (!expr1) goto L1; + # if (!expr1) goto lab1; # thenPart # goto LEnd - # L1: - # if (!expr2) goto L2; + # lab1: + # if (!expr2) goto lab2; # thenPart2 # goto LEnd - # L2: + # lab2: # 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] + for i in 0..<n.len: + var it = n[i] if it.len == 2: - withTemp(tmp, it.sons[0].typ): + withTemp(tmp, it[0].typ): var elsePos: TPosition - if isNotOpr(it.sons[0]): - c.gen(it.sons[0].sons[1], tmp) - elsePos = c.xjmp(it.sons[0].sons[1], opcTJmp, tmp) # if true + if isNotOpr(it[0]): + c.gen(it[0][1], tmp) + elsePos = c.xjmp(it[0][1], opcTJmp, tmp) # if true else: - c.gen(it.sons[0], tmp) - elsePos = c.xjmp(it.sons[0], opcFJmp, tmp) # if false + c.gen(it[0], tmp) + elsePos = c.xjmp(it[0], opcFJmp, tmp) # if false c.clearDest(n, dest) - c.gen(it.sons[1], dest) # then part - if i < sonsLen(n)-1: - endings.add(c.xjmp(it.sons[1], opcJmp, 0)) + if isEmptyType(it[1].typ): # maybe noreturn call, don't touch `dest` + c.gen(it[1]) + else: + c.gen(it[1], dest) # then part + if i < n.len-1: + endings.add(c.xjmp(it[1], opcJmp, 0)) c.patch(elsePos) else: c.clearDest(n, dest) - c.gen(it.sons[0], dest) + if isEmptyType(it[0].typ): # maybe noreturn call, don't touch `dest` + c.gen(it[0]) + else: + c.gen(it[0], dest) for endPos in endings: c.patch(endPos) c.clearDest(n, dest) +proc isTemp(c: PCtx; dest: TDest): bool = + result = dest >= 0 and c.prc.regInfo[dest].kind >= slotTempUnknown + proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) = # asgn dest, a - # tjmp|fjmp L1 + # tjmp|fjmp lab1 # asgn dest, b - # L1: - if dest < 0: dest = getTemp(c, n.typ) - c.gen(n.sons[1], dest) - let L1 = c.xjmp(n, opc, dest) - c.gen(n.sons[2], dest) - c.patch(L1) - -proc canonValue*(n: PNode): PNode = - result = n + # lab1: + let copyBack = dest < 0 or not isTemp(c, dest) + let tmp = if copyBack: + getTemp(c, n.typ) + else: + TRegister dest + c.gen(n[1], tmp) + let lab1 = c.xjmp(n, opc, tmp) + c.gen(n[2], tmp) + c.patch(lab1) + if dest < 0: + dest = tmp + elif copyBack: + c.gABC(n, opcAsgnInt, dest, tmp) + freeTemp(c, tmp) proc rawGenLiteral(c: PCtx; n: PNode): int = result = c.constants.len #assert(n.kind != nkCall) n.flags.incl nfAllConst - c.constants.add n.canonValue - internalAssert c.config, result < 0x7fff + n.flags.excl nfIsRef + c.constants.add n + internalAssert c.config, result < regBxMax proc sameConstant*(a, b: PNode): bool = result = false @@ -389,19 +462,24 @@ proc sameConstant*(a, b: PNode): bool = of nkSym: result = a.sym == b.sym of nkIdent: result = a.ident.id == b.ident.id of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal - of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal + of nkFloatLit..nkFloat64Lit: + result = cast[uint64](a.floatVal) == cast[uint64](b.floatVal) + # refs bug #16469 + # if we wanted to only distinguish 0.0 vs -0.0: + # if a.floatVal == 0.0: result = cast[uint64](a.floatVal) == cast[uint64](b.floatVal) + # else: result = a.floatVal == b.floatVal of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal of nkType, nkNilLit: result = a.typ == b.typ of nkEmpty: 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 + if a.len == b.len: + for i in 0..<a.len: + if not sameConstant(a[i], b[i]): return result = true proc genLiteral(c: PCtx; n: PNode): int = # types do not matter here: - for i in 0 ..< c.constants.len: + for i in 0..<c.constants.len: if sameConstant(c.constants[i], n): return i result = rawGenLiteral(c, n) @@ -411,14 +489,14 @@ proc unused(c: PCtx; n: PNode; x: TDest) {.inline.} = globalError(c.config, n.info, "not unused") proc genCase(c: PCtx; n: PNode; dest: var TDest) = - # if (!expr1) goto L1; + # if (!expr1) goto lab1; # thenPart # goto LEnd - # L1: - # if (!expr2) goto L2; + # lab1: + # if (!expr2) goto lab2; # thenPart2 # goto LEnd - # L2: + # lab2: # elsePart # Lend: if not isEmptyType(n.typ): @@ -426,22 +504,33 @@ proc genCase(c: PCtx; n: PNode; dest: var TDest) = else: unused(c, n, dest) var endings: seq[TPosition] = @[] - withTemp(tmp, n.sons[0].typ): - c.gen(n.sons[0], tmp) + withTemp(tmp, n[0].typ): + c.gen(n[0], tmp) # branch tmp, codeIdx # fjmp elseLabel - for i in 1 ..< n.len: - let it = n.sons[i] + for i in 1..<n.len: + let it = n[i] if it.len == 1: # else stmt: - c.gen(it.sons[0], dest) + let body = it[0] + if body.kind != nkNilLit or body.typ != nil: + # an nkNilLit with nil for typ implies there is no else branch, this + # avoids unused related errors as we've already consumed the dest + if isEmptyType(body.typ): # maybe noreturn call, don't touch `dest` + c.gen(body) + else: + c.gen(body, dest) else: let b = rawGenLiteral(c, it) c.gABx(it, opcBranch, tmp, b) - let elsePos = c.xjmp(it.lastSon, opcFJmp, tmp) - c.gen(it.lastSon, dest) - if i < sonsLen(n)-1: - endings.add(c.xjmp(it.lastSon, opcJmp, 0)) + let body = it.lastSon + let elsePos = c.xjmp(body, opcFJmp, tmp) + if isEmptyType(body.typ): # maybe noreturn call, don't touch `dest` + c.gen(body) + else: + c.gen(body, dest) + if i < n.len-1: + endings.add(c.xjmp(body, opcJmp, 0)) c.patch(elsePos) c.clearDest(n, dest) for endPos in endings: c.patch(endPos) @@ -451,51 +540,61 @@ proc genType(c: PCtx; typ: PType): int = if sameType(t, typ): return i result = c.types.len c.types.add(typ) - internalAssert(c.config, result <= 0x7fff) + internalAssert(c.config, result <= regBxMax) proc genTry(c: PCtx; n: PNode; dest: var TDest) = if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ) var endings: seq[TPosition] = @[] - let elsePos = c.xjmp(n, opcTry, 0) - c.gen(n.sons[0], dest) + let ehPos = c.xjmp(n, opcTry, 0) + if isEmptyType(n[0].typ): # maybe noreturn call, don't touch `dest` + c.gen(n[0]) + else: + c.gen(n[0], dest) c.clearDest(n, dest) - c.patch(elsePos) - for i in 1 ..< n.len: - let it = n.sons[i] + # Add a jump past the exception handling code + let jumpToFinally = c.xjmp(n, opcJmp, 0) + # This signals where the body ends and where the exception handling begins + c.patch(ehPos) + for i in 1..<n.len: + let it = n[i] if it.kind != nkFinally: - var blen = len(it) # first opcExcept contains the end label of the 'except' block: let endExcept = c.xjmp(it, opcExcept, 0) - for j in countup(0, blen - 2): - assert(it.sons[j].kind == nkType) - let typ = it.sons[j].typ.skipTypes(abstractPtrs-{tyTypeDesc}) + for j in 0..<it.len - 1: + assert(it[j].kind == nkType) + let typ = it[j].typ.skipTypes(abstractPtrs-{tyTypeDesc}) c.gABx(it, opcExcept, 0, c.genType(typ)) - if blen == 1: + if it.len == 1: # general except section: c.gABx(it, opcExcept, 0, 0) - c.gen(it.lastSon, dest) + let body = it.lastSon + if isEmptyType(body.typ): # maybe noreturn call, don't touch `dest` + c.gen(body) + else: + c.gen(body, dest) c.clearDest(n, dest) - if i < sonsLen(n)-1: + if i < n.len: endings.add(c.xjmp(it, opcJmp, 0)) c.patch(endExcept) - for endPos in endings: c.patch(endPos) let fin = lastSon(n) # we always generate an 'opcFinally' as that pops the safepoint - # from the stack + # from the stack if no exception is raised in the body. + c.patch(jumpToFinally) c.gABx(fin, opcFinally, 0, 0) + for endPos in endings: c.patch(endPos) if fin.kind == nkFinally: - c.gen(fin.sons[0]) + c.gen(fin[0]) c.clearDest(n, dest) c.gABx(fin, opcFinallyEnd, 0, 0) proc genRaise(c: PCtx; n: PNode) = - let dest = genx(c, n.sons[0]) + let dest = genx(c, n[0]) c.gABC(n, opcRaise, dest) c.freeTemp(dest) proc genReturn(c: PCtx; n: PNode) = - if n.sons[0].kind != nkEmpty: - gen(c, n.sons[0]) + if n[0].kind != nkEmpty: + gen(c, n[0]) c.gABC(n, opcRet) @@ -504,7 +603,7 @@ proc genLit(c: PCtx; n: PNode; dest: var TDest) = # assignments now: #var opc = opcLdConst if dest < 0: dest = c.getTemp(n.typ) - #elif c.prc.slots[dest].kind == slotFixedVar: opc = opcAsgnConst + #elif c.prc.regInfo[dest].kind == slotFixedVar: opc = opcAsgnConst let lit = genLiteral(c, n) c.gABx(n, opcLdConst, dest, lit) @@ -514,19 +613,25 @@ proc genCall(c: PCtx; n: PNode; dest: var TDest) = #if n.typ != nil and n.typ.sym != nil and n.typ.sym.magic == mPNimrodNode: # genLit(c, n, dest) # return + # bug #10901: do not produce code for wrong call expressions: + if n.len == 0 or n[0].typ.isNil: return if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ) let x = c.getTempRange(n.len, slotTempUnknown) # varargs need 'opcSetType' for the FFI support: - let fntyp = skipTypes(n.sons[0].typ, abstractInst) + let fntyp = skipTypes(n[0].typ, abstractInst) for i in 0..<n.len: - #if i > 0 and i < sonsLen(fntyp): - # let paramType = fntyp.n.sons[i] - # if paramType.typ.isCompileTimeOnly: continue var r: TRegister = x+i - c.gen(n.sons[i], r) - if i >= fntyp.len: + if i >= fntyp.signatureLen: + c.gen(n[i], r, {gfIsParam}) internalAssert c.config, tfVarargs in fntyp.flags - c.gABx(n, opcSetType, r, c.genType(n.sons[i].typ)) + c.gABx(n, opcSetType, r, c.genType(n[i].typ)) + else: + if fntyp[i] != nil and fntyp[i].kind == tySink and + fntyp[i].skipTypes({tySink}).kind in {tyObject, tyString, tySequence}: + c.gen(n[i], r, {gfIsSinkParam}) + else: + c.gen(n[i], r, {gfIsParam}) + if dest < 0: c.gABC(n, opcIndCall, 0, x, n.len) else: @@ -544,74 +649,95 @@ proc genField(c: PCtx; n: PNode): TRegister = if n.kind != nkSym or n.sym.kind != skField: globalError(c.config, n.info, "no field symbol") let s = n.sym - if s.position > high(result): + if s.position > high(typeof(result)): globalError(c.config, n.info, "too large offset! cannot generate code for: " & s.name.s) result = s.position proc genIndex(c: PCtx; n: PNode; arr: PType): TRegister = if arr.skipTypes(abstractInst).kind == tyArray and (let x = firstOrd(c.config, arr); - x != 0): + x != Zero): let tmp = c.genx(n) # freeing the temporary here means we can produce: regA = regA - Imm c.freeTemp(tmp) result = c.getTemp(n.typ) - c.gABI(n, opcSubImmInt, result, tmp, x.int) + c.gABI(n, opcSubImmInt, result, tmp, toInt(x)) else: result = c.genx(n) +proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) + proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) = case le.kind of nkBracketExpr: - let dest = c.genx(le.sons[0], {gfAddrOf, gfFieldAccess}) - let idx = c.genIndex(le.sons[1], le.sons[0].typ) - c.gABC(le, opcWrArr, dest, idx, value) + let + dest = c.genx(le[0], {gfNode}) + idx = c.genIndex(le[1], le[0].typ) + collTyp = le[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}) + + case collTyp.kind + of tyString, tyCstring: + c.gABC(le, opcWrStrIdx, dest, idx, value) + of tyTuple: + c.gABC(le, opcWrObj, dest, int le[1].intVal, value) + else: + c.gABC(le, opcWrArr, dest, idx, value) + c.freeTemp(dest) c.freeTemp(idx) - of nkDotExpr, nkCheckedFieldExpr: - # XXX field checks here - let left = if le.kind == nkDotExpr: le else: le.sons[0] - let dest = c.genx(left.sons[0], {gfAddrOf, gfFieldAccess}) - let idx = genField(c, left.sons[1]) - c.gABC(left, opcWrObj, dest, idx, value) + of nkCheckedFieldExpr: + var objR: TDest = -1 + genCheckedObjAccessAux(c, le, objR, {gfNode}) + let idx = genField(c, le[0][1]) + c.gABC(le[0], opcWrObj, objR, idx, value) + c.freeTemp(objR) + of nkDotExpr: + let dest = c.genx(le[0], {gfNode}) + let idx = genField(c, le[1]) + c.gABC(le, opcWrObj, dest, idx, value) c.freeTemp(dest) of nkDerefExpr, nkHiddenDeref: - let dest = c.genx(le.sons[0], {gfAddrOf}) + let dest = c.genx(le[0], {gfNode}) c.gABC(le, opcWrDeref, dest, 0, value) c.freeTemp(dest) of nkSym: if le.sym.isGlobal: - let dest = c.genx(le, {gfAddrOf}) + let dest = c.genx(le, {gfNodeAddr}) c.gABC(le, opcWrDeref, dest, 0, value) c.freeTemp(dest) + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + if sameBackendType(le.typ, le[1].typ): + genAsgnPatch(c, le[1], value) else: discard proc genNew(c: PCtx; n: PNode) = - let dest = if needsAsgnPatch(n.sons[1]): c.getTemp(n.sons[1].typ) - else: c.genx(n.sons[1]) + let dest = if needsAsgnPatch(n[1]): c.getTemp(n[1].typ) + else: c.genx(n[1]) # we use the ref's base type here as the VM conflates 'ref object' # and 'object' since internally we already have a pointer. c.gABx(n, opcNew, dest, - c.genType(n.sons[1].typ.skipTypes(abstractVar-{tyTypeDesc}).sons[0])) - c.genAsgnPatch(n.sons[1], dest) + c.genType(n[1].typ.skipTypes(abstractVar-{tyTypeDesc})[0])) + c.genAsgnPatch(n[1], dest) c.freeTemp(dest) proc genNewSeq(c: PCtx; n: PNode) = - let t = n.sons[1].typ - let dest = if needsAsgnPatch(n.sons[1]): c.getTemp(t) - else: c.genx(n.sons[1]) - let tmp = c.genx(n.sons[2]) + let t = n[1].typ + let dest = if needsAsgnPatch(n[1]): c.getTemp(t) + else: c.genx(n[1]) + let tmp = c.genx(n[2]) c.gABx(n, opcNewSeq, dest, c.genType(t.skipTypes( abstractVar-{tyTypeDesc}))) c.gABx(n, opcNewSeq, tmp, 0) c.freeTemp(tmp) - c.genAsgnPatch(n.sons[1], dest) + c.genAsgnPatch(n[1], dest) c.freeTemp(dest) proc genNewSeqOfCap(c: PCtx; n: PNode; dest: var TDest) = let t = n.typ - let tmp = c.getTemp(n.sons[1].typ) + if dest < 0: + dest = c.getTemp(n.typ) + let tmp = c.getTemp(n[1].typ) c.gABx(n, opcLdNull, dest, c.genType(t)) c.gABx(n, opcLdImmInt, tmp, 0) c.gABx(n, opcNewSeq, dest, c.genType(t.skipTypes( @@ -620,21 +746,22 @@ proc genNewSeqOfCap(c: PCtx; n: PNode; dest: var TDest) = c.freeTemp(tmp) proc genUnaryABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = - let tmp = c.genx(n.sons[1]) + let tmp = c.genx(n[1]) if dest < 0: dest = c.getTemp(n.typ) c.gABC(n, opc, dest, tmp) c.freeTemp(tmp) proc genUnaryABI(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; imm: BiggestInt=0) = - let tmp = c.genx(n.sons[1]) + let tmp = c.genx(n[1]) if dest < 0: dest = c.getTemp(n.typ) c.gABI(n, opc, dest, tmp, imm) c.freeTemp(tmp) + proc genBinaryABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = let - tmp = c.genx(n.sons[1]) - tmp2 = c.genx(n.sons[2]) + tmp = c.genx(n[1]) + tmp2 = c.genx(n[2]) if dest < 0: dest = c.getTemp(n.typ) c.gABC(n, opc, dest, tmp, tmp2) c.freeTemp(tmp) @@ -642,9 +769,9 @@ proc genBinaryABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = proc genBinaryABCD(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = let - tmp = c.genx(n.sons[1]) - tmp2 = c.genx(n.sons[2]) - tmp3 = c.genx(n.sons[3]) + tmp = c.genx(n[1]) + tmp2 = c.genx(n[2]) + tmp3 = c.genx(n[3]) if dest < 0: dest = c.getTemp(n.typ) c.gABC(n, opc, dest, tmp, tmp2) c.gABC(n, opc, tmp3) @@ -652,22 +779,27 @@ proc genBinaryABCD(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = c.freeTemp(tmp2) c.freeTemp(tmp3) +template sizeOfLikeMsg(name): string = + "'$1' requires '.importc' types to be '.completeStruct'" % [name] + proc genNarrow(c: PCtx; n: PNode; dest: TDest) = let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) # uint is uint64 in the VM, we we only need to mask the result for # other unsigned types: - if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and t.size < 8): - c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) - elif t.kind in {tyInt8..tyInt32} or (t.kind == tyInt and t.size < 8): - c.gABC(n, opcNarrowS, dest, TRegister(t.size*8)) + let size = getSize(c.config, t) + if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8): + c.gABC(n, opcNarrowU, dest, TRegister(size*8)) + elif t.kind in {tyInt8..tyInt32} or (t.kind == tyInt and size < 8): + c.gABC(n, opcNarrowS, dest, TRegister(size*8)) proc genNarrowU(c: PCtx; n: PNode; dest: TDest) = let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) # uint is uint64 in the VM, we we only need to mask the result for # other unsigned types: + let size = getSize(c.config, t) if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32} or - (t.kind in {tyUInt, tyInt} and t.size < 8): - c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) + (t.kind in {tyUInt, tyInt} and size < 8): + c.gABC(n, opcNarrowU, dest, TRegister(size*8)) proc genBinaryABCnarrow(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = genBinaryABC(c, n, dest, opc) @@ -684,69 +816,87 @@ proc genSetType(c: PCtx; n: PNode; dest: TRegister) = proc genBinarySet(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = let - tmp = c.genx(n.sons[1]) - tmp2 = c.genx(n.sons[2]) + tmp = c.genx(n[1]) + tmp2 = c.genx(n[2]) if dest < 0: dest = c.getTemp(n.typ) - c.genSetType(n.sons[1], tmp) - c.genSetType(n.sons[2], tmp2) + c.genSetType(n[1], tmp) + c.genSetType(n[2], tmp2) c.gABC(n, opc, dest, tmp, tmp2) c.freeTemp(tmp) c.freeTemp(tmp2) proc genBinaryStmt(c: PCtx; n: PNode; opc: TOpcode) = let - dest = c.genx(n.sons[1]) - tmp = c.genx(n.sons[2]) + dest = c.genx(n[1]) + tmp = c.genx(n[2]) c.gABC(n, opc, dest, tmp, 0) c.freeTemp(tmp) + c.freeTemp(dest) proc genBinaryStmtVar(c: PCtx; n: PNode; opc: TOpcode) = - var x = n.sons[1] - if x.kind in {nkAddr, nkHiddenAddr}: x = x.sons[0] + var x = n[1] + if x.kind in {nkAddr, nkHiddenAddr}: x = x[0] let dest = c.genx(x) - tmp = c.genx(n.sons[2]) + tmp = c.genx(n[2]) c.gABC(n, opc, dest, tmp, 0) - #c.genAsgnPatch(n.sons[1], dest) + #c.genAsgnPatch(n[1], dest) c.freeTemp(tmp) + c.freeTemp(dest) proc genUnaryStmt(c: PCtx; n: PNode; opc: TOpcode) = - let tmp = c.genx(n.sons[1]) + let tmp = c.genx(n[1]) c.gABC(n, opc, tmp, 0, 0) c.freeTemp(tmp) proc genVarargsABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = if dest < 0: dest = getTemp(c, n.typ) var x = c.getTempRange(n.len-1, slotTempStr) - for i in 1..n.len-1: + for i in 1..<n.len: var r: TRegister = x+i-1 - c.gen(n.sons[i], r) + c.gen(n[i], r) c.gABC(n, opc, dest, x, n.len-1) - c.freeTempRange(x, n.len) + c.freeTempRange(x, n.len-1) proc isInt8Lit(n: PNode): bool = if n.kind in {nkCharLit..nkUInt64Lit}: result = n.intVal >= low(int8) and n.intVal <= high(int8) + else: + result = false proc isInt16Lit(n: PNode): bool = if n.kind in {nkCharLit..nkUInt64Lit}: result = n.intVal >= low(int16) and n.intVal <= high(int16) + else: + result = false proc genAddSubInt(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = - if n.sons[2].isInt8Lit: - let tmp = c.genx(n.sons[1]) + if n[2].isInt8Lit: + let tmp = c.genx(n[1]) if dest < 0: dest = c.getTemp(n.typ) - c.gABI(n, succ(opc), dest, tmp, n.sons[2].intVal) + c.gABI(n, succ(opc), dest, tmp, n[2].intVal) c.freeTemp(tmp) else: genBinaryABC(c, n, dest, opc) c.genNarrow(n, dest) -proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) = - if n.typ.kind == arg.typ.kind and arg.typ.kind == tyProc: - # don't do anything for lambda lifting conversions: - gen(c, arg, dest) +proc genConv(c: PCtx; n, arg: PNode; dest: var TDest, flags: TGenFlags = {}; opc=opcConv) = + let t2 = n.typ.skipTypes({tyDistinct}) + let targ2 = arg.typ.skipTypes({tyDistinct}) + + proc implicitConv(): bool = + if sameBackendType(t2, targ2): return true + # xxx consider whether to use t2 and targ2 here + if n.typ.kind == arg.typ.kind and arg.typ.kind == tyProc: + # don't do anything for lambda lifting conversions: + result = true + else: + result = false + + if implicitConv(): + gen(c, arg, dest, flags) return + let tmp = c.genx(arg) if dest < 0: dest = c.getTemp(n.typ) c.gABC(n, opc, dest, tmp) @@ -755,54 +905,94 @@ proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) = c.freeTemp(tmp) proc genCard(c: PCtx; n: PNode; dest: var TDest) = - let tmp = c.genx(n.sons[1]) + let tmp = c.genx(n[1]) if dest < 0: dest = c.getTemp(n.typ) - c.genSetType(n.sons[1], tmp) + c.genSetType(n[1], tmp) c.gABC(n, opcCard, dest, tmp) c.freeTemp(tmp) -proc genIntCast(c: PCtx; n: PNode; dest: var TDest) = - const allowedIntegers = {tyInt..tyInt64, tyUInt..tyUInt64, tyChar} - var signedIntegers = {tyInt8..tyInt32} - var unsignedIntegers = {tyUInt8..tyUInt32, tyChar} - let src = n.sons[1].typ.skipTypes(abstractRange)#.kind - let dst = n.sons[0].typ.skipTypes(abstractRange)#.kind - let src_size = getSize(c.config, src) - - if c.config.target.intSize < 8: - signedIntegers.incl(tyInt) - unsignedIntegers.incl(tyUInt) - if src_size == getSize(c.config, dst) and src.kind in allowedIntegers and - dst.kind in allowedIntegers: - let tmp = c.genx(n.sons[1]) - var tmp2 = c.getTemp(n.sons[1].typ) - let tmp3 = c.getTemp(n.sons[1].typ) +proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) = + template isSigned(typ: PType): bool {.dirty.} = + typ.kind == tyEnum and firstOrd(c.config, typ) < 0 or + typ.kind in {tyInt..tyInt64} + template isUnsigned(typ: PType): bool {.dirty.} = + typ.kind == tyEnum and firstOrd(c.config, typ) >= 0 or + typ.kind in {tyUInt..tyUInt64, tyChar, tyBool} + + const allowedIntegers = {tyInt..tyInt64, tyUInt..tyUInt64, tyChar, tyEnum, tyBool} + + let src = n[1].typ.skipTypes(abstractRange)#.kind + let dst = n[0].typ.skipTypes(abstractRange)#.kind + let srcSize = getSize(c.config, src) + let dstSize = getSize(c.config, dst) + const unsupportedCastDifferentSize = + "VM does not support 'cast' from $1 with size $2 to $3 with size $4 due to different sizes" + if src.kind in allowedIntegers and dst.kind in allowedIntegers: + let tmp = c.genx(n[1]) if dest < 0: dest = c.getTemp(n[0].typ) - proc mkIntLit(ival: int): int = - result = genLiteral(c, newIntTypeNode(nkIntLit, ival, getSysType(c.graph, n.info, tyInt))) - if src.kind in unsignedIntegers and dst.kind in signedIntegers: - # cast unsigned to signed integer of same size - # signedVal = (unsignedVal xor offset) -% offset - let offset = 1 shl (src_size * 8 - 1) - c.gABx(n, opcLdConst, tmp2, mkIntLit(offset)) - c.gABC(n, opcBitxorInt, tmp3, tmp, tmp2) - c.gABC(n, opcSubInt, dest, tmp3, tmp2) - elif src.kind in signedIntegers and dst.kind in unsignedIntegers: - # cast signed to unsigned integer of same size - # unsignedVal = (offset +% signedVal +% 1) and offset - let offset = (1 shl (src_size * 8)) - 1 - c.gABx(n, opcLdConst, tmp2, mkIntLit(offset)) - c.gABx(n, opcLdConst, dest, mkIntLit(offset+1)) - c.gABC(n, opcAddu, tmp3, tmp, dest) - c.gABC(n, opcNarrowU, tmp3, TRegister(src_size*8)) - c.gABC(n, opcBitandInt, dest, tmp3, tmp2) + c.gABC(n, opcAsgnInt, dest, tmp) + if dstSize != sizeof(BiggestInt): # don't do anything on biggest int types + if isSigned(dst): # we need to do sign extensions + if dstSize <= srcSize: + # Sign extension can be omitted when the size increases. + c.gABC(n, opcSignExtend, dest, TRegister(dstSize*8)) + elif isUnsigned(dst): + if isSigned(src) or dstSize < srcSize: + # Cast from signed to unsigned always needs narrowing. Cast + # from unsigned to unsigned only needs narrowing when target + # is smaller than source. + c.gABC(n, opcNarrowU, dest, TRegister(dstSize*8)) + c.freeTemp(tmp) + elif src.kind in allowedIntegers and + dst.kind in {tyFloat, tyFloat32, tyFloat64}: + if srcSize != dstSize: + globalError(c.config, n.info, unsupportedCastDifferentSize % + [$src.kind, $srcSize, $dst.kind, $dstSize]) + let tmp = c.genx(n[1]) + if dest < 0: dest = c.getTemp(n[0].typ) + if dst.kind == tyFloat32: + c.gABC(n, opcCastIntToFloat32, dest, tmp) else: - c.gABC(n, opcAsgnInt, dest, tmp) + c.gABC(n, opcCastIntToFloat64, dest, tmp) c.freeTemp(tmp) - c.freeTemp(tmp2) - c.freeTemp(tmp3) + + elif src.kind in {tyFloat, tyFloat32, tyFloat64} and + dst.kind in allowedIntegers: + if srcSize != dstSize: + globalError(c.config, n.info, unsupportedCastDifferentSize % + [$src.kind, $srcSize, $dst.kind, $dstSize]) + let tmp = c.genx(n[1]) + if dest < 0: dest = c.getTemp(n[0].typ) + if src.kind == tyFloat32: + c.gABC(n, opcCastFloatToInt32, dest, tmp) + if isUnsigned(dst): + # integers are sign extended by default. + # since there is no opcCastFloatToUInt32, narrowing should do the trick. + c.gABC(n, opcNarrowU, dest, TRegister(32)) + else: + c.gABC(n, opcCastFloatToInt64, dest, tmp) + # narrowing for 64 bits not needed (no extended sign bits available). + c.freeTemp(tmp) + elif src.kind in PtrLikeKinds + {tyRef} and dst.kind == tyInt: + let tmp = c.genx(n[1]) + if dest < 0: dest = c.getTemp(n[0].typ) + var imm: BiggestInt = if src.kind in PtrLikeKinds: 1 else: 2 + c.gABI(n, opcCastPtrToInt, dest, tmp, imm) + c.freeTemp(tmp) + elif src.kind in PtrLikeKinds + {tyInt} and dst.kind in PtrLikeKinds: + let tmp = c.genx(n[1]) + if dest < 0: dest = c.getTemp(n[0].typ) + c.gABx(n, opcSetType, dest, c.genType(dst)) + c.gABC(n, opcCastIntToPtr, dest, tmp) + c.freeTemp(tmp) + elif src.kind == tyNil and dst.kind in NilableTypes: + # supports casting nil literals to NilableTypes in VM + # see #16024 + if dest < 0: dest = c.getTemp(n[0].typ) + genLit(c, n[1], dest) else: - globalError(c.config, n.info, "VM is only allowed to 'cast' between integers of same size") + # todo: support cast from tyInt to tyRef + globalError(c.config, n.info, "VM does not support 'cast' from " & $src.kind & " to " & $dst.kind) proc genVoidABC(c: PCtx, n: PNode, dest: TDest, opcode: TOpcode) = unused(c, n, dest) @@ -815,33 +1005,91 @@ proc genVoidABC(c: PCtx, n: PNode, dest: TDest, opcode: TOpcode) = c.freeTemp(tmp2) c.freeTemp(tmp3) -proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = +proc genBindSym(c: PCtx; n: PNode; dest: var TDest) = + # nah, cannot use c.config.features because sempass context + # can have local experimental switch + # if dynamicBindSym notin c.config.features: + if n.len == 2: # hmm, reliable? + # bindSym with static input + if n[1].kind in {nkClosedSymChoice, nkOpenSymChoice, nkSym}: + let idx = c.genLiteral(n[1]) + if dest < 0: dest = c.getTemp(n.typ) + c.gABx(n, opcNBindSym, dest, idx) + else: + localError(c.config, n.info, "invalid bindSym usage") + else: + # experimental bindSym + if dest < 0: dest = c.getTemp(n.typ) + let x = c.getTempRange(n.len, slotTempUnknown) + + # callee symbol + var tmp0 = TDest(x) + c.genLit(n[0], tmp0) + + # original parameters + for i in 1..<n.len-2: + var r = TRegister(x+i) + c.gen(n[i], r) + + # info node + var tmp1 = TDest(x+n.len-2) + c.genLit(n[^2], tmp1) + + # payload idx + var tmp2 = TDest(x+n.len-1) + c.genLit(n[^1], tmp2) + + c.gABC(n, opcNDynBindSym, dest, x, n.len) + c.freeTempRange(x, n.len) + +proc fitsRegister*(t: PType): bool = + assert t != nil + t.skipTypes(abstractInst + {tyStatic} - {tyTypeDesc}).kind in { + tyRange, tyEnum, tyBool, tyInt..tyUInt64, tyChar} + +proc ldNullOpcode(t: PType): TOpcode = + assert t != nil + if fitsRegister(t): opcLdNullReg else: opcLdNull + +proc whichAsgnOpc(n: PNode; requiresCopy = true): TOpcode = + case n.typ.skipTypes(abstractRange+{tyOwned}-{tyTypeDesc}).kind + of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64: + opcAsgnInt + of tyFloat..tyFloat128: + opcAsgnFloat + of tyRef, tyNil, tyVar, tyLent, tyPtr: + opcAsgnRef + else: + (if requiresCopy: opcAsgnComplex else: opcFastAsgnComplex) + +proc genMagic(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}, m: TMagic) = 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: c.genAddSubInt(n, dest, opcSubInt) of mSucc, mAddI: c.genAddSubInt(n, dest, opcAddInt) of mInc, mDec: unused(c, n, dest) - let opc = if m == mInc: opcAddInt else: opcSubInt - let d = c.genx(n.sons[1]) - if n.sons[2].isInt8Lit: - c.gABI(n, succ(opc), d, d, n.sons[2].intVal) + let isUnsigned = n[1].typ.skipTypes(abstractVarRange).kind in {tyUInt..tyUInt64} + let opc = if not isUnsigned: + if m == mInc: opcAddInt else: opcSubInt + else: + if m == mInc: opcAddu else: opcSubu + let d = c.genx(n[1]) + if n[2].isInt8Lit and not isUnsigned: + c.gABI(n, succ(opc), d, d, n[2].intVal) else: - let tmp = c.genx(n.sons[2]) + let tmp = c.genx(n[2]) c.gABC(n, opc, d, d, tmp) c.freeTemp(tmp) - c.genNarrow(n.sons[1], d) - c.genAsgnPatch(n.sons[1], d) + c.genNarrow(n[1], d) + c.genAsgnPatch(n[1], d) c.freeTemp(d) - of mOrd, mChr, mArrToSeq: c.gen(n.sons[1], dest) + of mOrd, mChr, mArrToSeq, mUnown: c.gen(n[1], dest) + of generatedMagics: + genCall(c, n, dest) of mNew, mNewFinalize: unused(c, n, dest) c.genNew(n) @@ -854,23 +1102,38 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = # XXX buggy of mNewStringOfCap: # we ignore the 'cap' argument and translate it as 'newString(0)'. - # eval n.sons[1] for possible side effects: - c.freeTemp(c.genx(n.sons[1])) - var tmp = c.getTemp(n.sons[1].typ) + # eval n[1] for possible side effects: + c.freeTemp(c.genx(n[1])) + var tmp = c.getTemp(n[1].typ) c.gABx(n, opcLdImmInt, tmp, 0) if dest < 0: dest = c.getTemp(n.typ) c.gABC(n, opcNewStr, dest, tmp) c.freeTemp(tmp) # XXX buggy - of mLengthOpenArray, mLengthArray, mLengthSeq, mXLenSeq: + of mLengthOpenArray, mLengthArray, mLengthSeq: genUnaryABI(c, n, dest, opcLenSeq) - of mLengthStr, mXLenStr: - genUnaryABI(c, n, dest, opcLenStr) + of mLengthStr: + case n[1].typ.skipTypes(abstractVarRange).kind + of tyString: genUnaryABI(c, n, dest, opcLenStr) + of tyCstring: genUnaryABI(c, n, dest, opcLenCstring) + else: raiseAssert $n[1].typ.kind + of mSlice: + var + d = c.genx(n[1]) + left = c.genIndex(n[2], n[1].typ) + right = c.genIndex(n[3], n[1].typ) + if dest < 0: dest = c.getTemp(n.typ) + c.gABC(n, opcNodeToReg, dest, d) + c.gABC(n, opcSlice, dest, left, right) + c.freeTemp(left) + c.freeTemp(right) + c.freeTemp(d) + of mIncl, mExcl: unused(c, n, dest) - var d = c.genx(n.sons[1]) - var tmp = c.genx(n.sons[2]) - c.genSetType(n.sons[1], d) + var d = c.genx(n[1]) + var tmp = c.genx(n[2]) + c.genSetType(n[1], d) c.gABC(n, if m == mIncl: opcIncl else: opcExcl, d, tmp) c.freeTemp(d) c.freeTemp(tmp) @@ -883,26 +1146,28 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mMulF64: genBinaryABC(c, n, dest, opcMulFloat) of mDivF64: genBinaryABC(c, n, dest, opcDivFloat) of mShrI: - # the idea here is to narrow type if needed before executing right shift - # inlined modified: genNarrowU(c, n, dest) - let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) - # uint is uint64 in the VM, we we only need to mask the result for - # other unsigned types: - let tmp = c.genx(n.sons[1]) - if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32}: - c.gABC(n, opcNarrowU, tmp, TRegister(t.size*8)) - - # inlined modified: genBinaryABC(c, n, dest, opcShrInt) - let tmp2 = c.genx(n.sons[2]) + # modified: genBinaryABC(c, n, dest, opcShrInt) + # narrowU is applied to the left operandthe idea here is to narrow the left operand + let tmp = c.genx(n[1]) + c.genNarrowU(n, tmp) + let tmp2 = c.genx(n[2]) if dest < 0: dest = c.getTemp(n.typ) c.gABC(n, opcShrInt, dest, tmp, tmp2) c.freeTemp(tmp) c.freeTemp(tmp2) - - of mShlI: genBinaryABCnarrowU(c, n, dest, opcShlInt) - of mBitandI: genBinaryABCnarrowU(c, n, dest, opcBitandInt) - of mBitorI: genBinaryABCnarrowU(c, n, dest, opcBitorInt) - of mBitxorI: genBinaryABCnarrowU(c, n, dest, opcBitxorInt) + of mShlI: + genBinaryABC(c, n, dest, opcShlInt) + # genNarrowU modified + let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) + let size = getSize(c.config, t) + if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8): + c.gABC(n, opcNarrowU, dest, TRegister(size*8)) + elif t.kind in {tyInt8..tyInt32} or (t.kind == tyInt and size < 8): + c.gABC(n, opcSignExtend, dest, TRegister(size*8)) + of mAshrI: genBinaryABC(c, n, dest, opcAshrInt) + of mBitandI: genBinaryABC(c, n, dest, opcBitandInt) + of mBitorI: genBinaryABC(c, n, dest, opcBitorInt) + of mBitxorI: genBinaryABC(c, n, dest, opcBitxorInt) of mAddU: genBinaryABCnarrowU(c, n, dest, opcAddu) of mSubU: genBinaryABCnarrowU(c, n, dest, opcSubu) of mMulU: genBinaryABCnarrowU(c, n, dest, opcMulu) @@ -917,40 +1182,28 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = 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: + of mLePtr, mLeU: genBinaryABC(c, n, dest, opcLeu) + of mLtPtr, mLtU: genBinaryABC(c, n, dest, opcLtu) + of mEqProc, mEqRef: genBinaryABC(c, n, dest, opcEqRef) - of mXor: genBinaryABCnarrowU(c, n, dest, opcXor) + of mXor: genBinaryABC(c, n, dest, opcXor) of mNot: genUnaryABC(c, n, dest, opcNot) of mUnaryMinusI, mUnaryMinusI64: genUnaryABC(c, n, dest, opcUnaryMinusInt) genNarrow(c, n, dest) of mUnaryMinusF64: genUnaryABC(c, n, dest, opcUnaryMinusFloat) - of mUnaryPlusI, mUnaryPlusF64: gen(c, n.sons[1], dest) + of mUnaryPlusI, mUnaryPlusF64: gen(c, n[1], dest) of mBitnotI: genUnaryABC(c, n, dest, opcBitnotInt) - genNarrowU(c, n, dest) - of mToFloat, mToBiggestFloat, mToInt, - mToBiggestInt, mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, - mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr: - genConv(c, n, n.sons[1], dest) - of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64: - #genNarrowU modified - let t = skipTypes(n.sons[1].typ, abstractVar-{tyTypeDesc}) - let tmp = c.genx(n.sons[1]) - c.gABC(n, opcNarrowU, tmp, TRegister(t.size*8)) - # assign result to dest register - if dest < 0: dest = c.getTemp(n.typ) - c.gABC(n, opcAsgnInt, dest, tmp) - c.freeTemp(tmp) - of mToU8, mToU16, mToU32: + #genNarrowU modified, do not narrow signed types let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) - var tmp = c.genx(n.sons[1]) - if dest < 0: dest = c.getTemp(n.typ) - c.gABC(n, opcToNarrowInt, dest, tmp, TRegister(t.size*8)) - c.freeTemp(tmp) - of mEqStr, mEqCString: genBinaryABC(c, n, dest, opcEqStr) + let size = getSize(c.config, t) + if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8): + c.gABC(n, opcNarrowU, dest, TRegister(size*8)) + of mCharToStr, mBoolToStr, mCStrToStr, mStrToStr, mEnumToStr: + genConv(c, n, n[1], dest, flags) + of mEqStr: genBinaryABC(c, n, dest, opcEqStr) + of mEqCString: genBinaryABC(c, n, dest, opcEqCString) of mLeStr: genBinaryABC(c, n, dest, opcLeStr) of mLtStr: genBinaryABC(c, n, dest, opcLtStr) of mEqSet: genBinarySet(c, n, dest, opcEqSet) @@ -959,102 +1212,73 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mMulSet: genBinarySet(c, n, dest, opcMulSet) of mPlusSet: genBinarySet(c, n, dest, opcPlusSet) of mMinusSet: genBinarySet(c, n, dest, opcMinusSet) - of mSymDiffSet: genBinarySet(c, n, dest, opcSymdiffSet) of mConStrStr: genVarargsABC(c, n, dest, opcConcatStr) of mInSet: genBinarySet(c, n, dest, opcContainsSet) of mRepr: genUnaryABC(c, n, dest, opcRepr) of mExit: unused(c, n, dest) - var tmp = c.genx(n.sons[1]) + var tmp = c.genx(n[1]) c.gABC(n, opcQuit, tmp) c.freeTemp(tmp) of mSetLengthStr, mSetLengthSeq: unused(c, n, dest) - var d = c.genx(n.sons[1]) - var tmp = c.genx(n.sons[2]) + var d = c.genx(n[1]) + var tmp = c.genx(n[2]) c.gABC(n, if m == mSetLengthStr: opcSetLenStr else: opcSetLenSeq, d, tmp) - c.genAsgnPatch(n.sons[1], d) + c.genAsgnPatch(n[1], d) c.freeTemp(tmp) + c.freeTemp(d) of mSwap: unused(c, n, dest) - c.gen(lowerSwap(c.graph, n, if c.prc == nil: c.module else: c.prc.sym)) + c.gen(lowerSwap(c.graph, n, c.idgen, if c.prc == nil or c.prc.sym == nil: c.module else: c.prc.sym)) 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 mParseBiggestFloat: if dest < 0: dest = c.getTemp(n.typ) var d2: TRegister # skip 'nkHiddenAddr': - let d2AsNode = n.sons[2].sons[0] + let d2AsNode = n[2][0] if needsAsgnPatch(d2AsNode): d2 = c.getTemp(getSysType(c.graph, n.info, tyFloat)) else: d2 = c.genx(d2AsNode) var - tmp1 = c.genx(n.sons[1]) - tmp3 = c.genx(n.sons[3]) + tmp1 = c.genx(n[1]) c.gABC(n, opcParseFloat, dest, tmp1, d2) - c.gABC(n, opcParseFloat, tmp3) c.freeTemp(tmp1) - c.freeTemp(tmp3) c.genAsgnPatch(d2AsNode, d2) c.freeTemp(d2) - of mReset: - unused(c, n, dest) - var d = c.genx(n.sons[1]) - c.gABC(n, opcReset, d) + of mDefault, mZeroDefault: + if dest < 0: dest = c.getTemp(n.typ) + c.gABx(n, ldNullOpcode(n.typ), dest, c.genType(n.typ)) of mOf, mIs: if dest < 0: dest = c.getTemp(n.typ) - var tmp = c.genx(n.sons[1]) + var tmp = c.genx(n[1]) var idx = c.getTemp(getSysType(c.graph, n.info, tyInt)) - var typ = n.sons[2].typ - if m == mOf: typ = typ.skipTypes(abstractPtrs-{tyTypeDesc}) + var typ = n[2].typ + if m == mOf: typ = typ.skipTypes(abstractPtrs) c.gABx(n, opcLdImmInt, idx, c.genType(typ)) c.gABC(n, if m == mOf: opcOf else: opcIs, dest, tmp, idx) c.freeTemp(tmp) c.freeTemp(idx) - of mSizeOf: - globalError(c.config, n.info, "cannot run in the VM: " & renderTree(n)) of mHigh: if dest < 0: dest = c.getTemp(n.typ) - let tmp = c.genx(n.sons[1]) - case n.sons[1].typ.skipTypes(abstractVar-{tyTypeDesc}).kind: - of tyString, tyCString: - c.gABI(n, opcLenStr, dest, tmp, 1) - else: - c.gABI(n, opcLenSeq, dest, tmp, 1) + let tmp = c.genx(n[1]) + case n[1].typ.skipTypes(abstractVar-{tyTypeDesc}).kind: + of tyString: c.gABI(n, opcLenStr, dest, tmp, 1) + of tyCstring: c.gABI(n, opcLenCstring, dest, tmp, 1) + else: c.gABI(n, opcLenSeq, dest, tmp, 1) c.freeTemp(tmp) of mEcho: unused(c, n, dest) let n = n[1].skipConv - let x = c.getTempRange(n.len, slotTempUnknown) - internalAssert c.config, n.kind == nkBracket - for i in 0..<n.len: - var r: TRegister = x+i - c.gen(n.sons[i], r) - c.gABC(n, opcEcho, x, n.len) - c.freeTempRange(x, n.len) + if n.kind == nkBracket: + # can happen for nim check, see bug #9609 + let x = c.getTempRange(n.len, slotTempUnknown) + for i in 0..<n.len: + var r: TRegister = x+i + c.gen(n[i], r) + c.gABC(n, opcEcho, x, n.len) + c.freeTempRange(x, n.len) of mAppendStrCh: unused(c, n, dest) genBinaryStmtVar(c, n, opcAddStrCh) @@ -1065,19 +1289,22 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = unused(c, n, dest) genBinaryStmtVar(c, n, opcAddSeqElem) of mParseExprToAst: - genUnaryABC(c, n, dest, opcParseExprToAst) + genBinaryABC(c, n, dest, opcParseExprToAst) of mParseStmtToAst: - genUnaryABC(c, n, dest, opcParseStmtToAst) + genBinaryABC(c, n, dest, opcParseStmtToAst) of mTypeTrait: - let tmp = c.genx(n.sons[1]) + let tmp = c.genx(n[1]) if dest < 0: dest = c.getTemp(n.typ) - c.gABx(n, opcSetType, tmp, c.genType(n.sons[1].typ)) + c.gABx(n, opcSetType, tmp, c.genType(n[1].typ)) c.gABC(n, opcTypeTrait, dest, tmp) c.freeTemp(tmp) of mSlurp: genUnaryABC(c, n, dest, opcSlurp) of mStaticExec: genBinaryABCD(c, n, dest, opcGorge) of mNLen: genUnaryABI(c, n, dest, opcLenSeq, nimNodeFlag) of mGetImpl: genUnaryABC(c, n, dest, opcGetImpl) + of mGetImplTransf: genUnaryABC(c, n, dest, opcGetImplTransf) + of mSymOwner: genUnaryABC(c, n, dest, opcSymOwner) + of mSymIsInstantiationOf: genBinaryABC(c, n, dest, opcSymIsInstantiationOf) of mNChild: genBinaryABC(c, n, dest, opcNChild) of mNSetChild: genVoidABC(c, n, dest, opcNSetChild) of mNDel: genVoidABC(c, n, dest, opcNDel) @@ -1103,7 +1330,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mNSymbol: genUnaryABC(c, n, dest, opcNSymbol) of mNIdent: genUnaryABC(c, n, dest, opcNIdent) of mNGetType: - let tmp = c.genx(n.sons[1]) + let tmp = c.genx(n[1]) if dest < 0: dest = c.getTemp(n.typ) let rc = case n[0].sym.name.s: of "getType": 0 @@ -1113,7 +1340,14 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.gABC(n, opcNGetType, dest, tmp, rc) c.freeTemp(tmp) #genUnaryABC(c, n, dest, opcNGetType) + of mNSizeOf: + let imm = case n[0].sym.name.s: + of "getSize": 0 + of "getAlign": 1 + else: 2 # "getOffset" + c.genUnaryABI(n, dest, opcNGetSize, imm) of mNStrVal: genUnaryABC(c, n, dest, opcNStrVal) + of mNSigHash: genUnaryABC(c, n , dest, opcNSigHash) of mNSetIntVal: unused(c, n, dest) genBinaryStmt(c, n, opcNSetIntVal) @@ -1126,42 +1360,45 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mNSetIdent: unused(c, n, dest) genBinaryStmt(c, n, opcNSetIdent) - of mNSetType: - unused(c, n, dest) - genBinaryStmt(c, n, opcNSetType) of mNSetStrVal: unused(c, n, dest) genBinaryStmt(c, n, opcNSetStrVal) of mNNewNimNode: genBinaryABC(c, n, dest, opcNNewNimNode) of mNCopyNimNode: genUnaryABC(c, n, dest, opcNCopyNimNode) of mNCopyNimTree: genUnaryABC(c, n, dest, opcNCopyNimTree) - of mNBindSym: - if n[1].kind in {nkClosedSymChoice, nkOpenSymChoice, nkSym}: - let idx = c.genLiteral(n[1]) - if dest < 0: dest = c.getTemp(n.typ) - c.gABx(n, opcNBindSym, dest, idx) - else: - localError(c.config, n.info, "invalid bindSym usage") + of mNBindSym: genBindSym(c, n, dest) of mStrToIdent: genUnaryABC(c, n, dest, opcStrToIdent) of mEqIdent: genBinaryABC(c, n, dest, opcEqIdent) - of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimrodNode) + of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimNode) of mSameNodeType: genBinaryABC(c, n, dest, opcSameNodeType) of mNLineInfo: case n[0].sym.name.s - of "getFile": - genUnaryABC(c, n, dest, opcNGetFile) - of "getLine": - genUnaryABC(c, n, dest, opcNGetLine) - of "getColumn": - genUnaryABC(c, n, dest, opcNGetColumn) - else: - internalAssert c.config, false + of "getFile": genUnaryABI(c, n, dest, opcNGetLineInfo, 0) + of "getLine": genUnaryABI(c, n, dest, opcNGetLineInfo, 1) + of "getColumn": genUnaryABI(c, n, dest, opcNGetLineInfo, 2) + of "copyLineInfo": + internalAssert c.config, n.len == 3 + unused(c, n, dest) + genBinaryStmt(c, n, opcNCopyLineInfo) + of "setLine": + internalAssert c.config, n.len == 3 + unused(c, n, dest) + genBinaryStmt(c, n, opcNSetLineInfoLine) + of "setColumn": + internalAssert c.config, n.len == 3 + unused(c, n, dest) + genBinaryStmt(c, n, opcNSetLineInfoColumn) + of "setFile": + internalAssert c.config, n.len == 3 + unused(c, n, dest) + genBinaryStmt(c, n, opcNSetLineInfoFile) + else: internalAssert c.config, false of mNHint: unused(c, n, dest) - genUnaryStmt(c, n, opcNHint) + genBinaryStmt(c, n, opcNHint) of mNWarning: unused(c, n, dest) - genUnaryStmt(c, n, opcNWarning) + genBinaryStmt(c, n, opcNWarning) of mNError: if n.len <= 1: # query error condition: @@ -1174,13 +1411,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = if dest < 0: dest = c.getTemp(n.typ) c.gABC(n, opcCallSite, dest) of mNGenSym: genBinaryABC(c, n, dest, opcGenSym) - of mMinI, mMaxI, mAbsF64, mMinF64, mMaxF64, mAbsI, - mDotDot: + of mMinI, mMaxI, mAbsI, mDotDot: c.genCall(n, dest) of mExpandToAst: if n.len != 2: globalError(c.config, n.info, "expandToAst requires 1 argument") - let arg = n.sons[1] + let arg = n[1] if arg.kind in nkCallKinds: #if arg[0].kind != nkSym or arg[0].sym.kind notin {skTemplate, skMacro}: # "ExpandToAst: expanded symbol is no macro or template" @@ -1190,150 +1426,131 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = # produces a value else: globalError(c.config, n.info, "expandToAst requires a call expression") + of mSizeOf: + globalError(c.config, n.info, sizeOfLikeMsg("sizeof")) + of mAlignOf: + globalError(c.config, n.info, sizeOfLikeMsg("alignof")) + of mOffsetOf: + globalError(c.config, n.info, sizeOfLikeMsg("offsetof")) of mRunnableExamples: discard "just ignore any call to runnableExamples" + of mDestroy, mTrace: discard "ignore calls to the default destructor" + of mEnsureMove: + gen(c, n[1], dest) + of mMove: + let arg = n[1] + let a = c.genx(arg) + if dest < 0: dest = c.getTemp(arg.typ) + gABC(c, arg, whichAsgnOpc(arg, requiresCopy=false), dest, a) + # XXX use ldNullOpcode() here? + # Don't zero out the arg for now #17199 + # c.gABx(n, opcLdNull, a, c.genType(arg.typ)) + # c.gABx(n, opcNodeToReg, a, a) + # c.genAsgnPatch(arg, a) + c.freeTemp(a) + of mDup: + let arg = n[1] + let a = c.genx(arg) + if dest < 0: dest = c.getTemp(arg.typ) + gABC(c, arg, whichAsgnOpc(arg, requiresCopy=false), dest, a) + c.freeTemp(a) + of mNodeId: + c.genUnaryABC(n, dest, opcNodeId) else: # mGCref, mGCunref, globalError(c.config, n.info, "cannot generate code for: " & $m) -proc genMarshalLoad(c: PCtx, n: PNode, dest: var TDest) = - ## Signature: proc to*[T](data: string): T - if dest < 0: dest = c.getTemp(n.typ) - var tmp = c.genx(n.sons[1]) - c.gABC(n, opcMarshalLoad, dest, tmp) - c.gABx(n, opcMarshalLoad, 0, c.genType(n.typ)) - c.freeTemp(tmp) - -proc genMarshalStore(c: PCtx, n: PNode, dest: var TDest) = - ## Signature: proc `$$`*[T](x: T): string - if dest < 0: dest = c.getTemp(n.typ) - var tmp = c.genx(n.sons[1]) - c.gABC(n, opcMarshalStore, dest, tmp) - c.gABx(n, opcMarshalStore, 0, c.genType(n.sons[1].typ)) - c.freeTemp(tmp) - -const - atomicTypes = {tyBool, tyChar, - tyExpr, tyStmt, tyTypeDesc, tyStatic, - tyEnum, - tyOrdinal, - tyRange, - tyProc, - tyPointer, tyOpenArray, - tyString, tyCString, - tyInt, tyInt8, tyInt16, tyInt32, tyInt64, - tyFloat, tyFloat32, tyFloat64, tyFloat128, - tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64} - -proc fitsRegister*(t: PType): bool = - assert t != nil - t.skipTypes(abstractInst-{tyTypeDesc}).kind in { - tyRange, tyEnum, tyBool, tyInt..tyUInt64, tyChar} - proc unneededIndirection(n: PNode): bool = - n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind == tyRef + n.typ.skipTypes(abstractInstOwned-{tyTypeDesc}).kind == tyRef -proc canElimAddr(n: PNode): PNode = - case n.sons[0].kind +proc canElimAddr(n: PNode; idgen: IdGenerator): PNode = + result = nil + case n[0].kind of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64: - var m = n.sons[0].sons[0] + var m = n[0][0] if m.kind in {nkDerefExpr, nkHiddenDeref}: # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x) - result = copyNode(n.sons[0]) - result.add m.sons[0] + result = copyNode(n[0]) + result.add m[0] + if n.typ.skipTypes(abstractVar).kind != tyOpenArray: + result.typ = n.typ + elif n.typ.skipTypes(abstractInst).kind in {tyVar}: + result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen) of nkHiddenStdConv, nkHiddenSubConv, nkConv: - var m = n.sons[0].sons[1] + var m = n[0][1] if m.kind in {nkDerefExpr, nkHiddenDeref}: # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x) - result = copyNode(n.sons[0]) - result.add m.sons[0] + result = copyNode(n[0]) + result.add n[0][0] + result.add m[0] + if n.typ.skipTypes(abstractVar).kind != tyOpenArray: + result.typ = n.typ + elif n.typ.skipTypes(abstractInst).kind in {tyVar}: + result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen) else: - if n.sons[0].kind in {nkDerefExpr, nkHiddenDeref}: + if n[0].kind in {nkDerefExpr, nkHiddenDeref}: # addr ( deref ( x )) --> x - result = n.sons[0].sons[0] + result = n[0][0] -proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; - flags: TGenFlags) = - # a nop for certain types - let isAddr = opc in {opcAddrNode, opcAddrReg} - if isAddr and (let m = canElimAddr(n); m != nil): +proc genAddr(c: PCtx, n: PNode, dest: var TDest, flags: TGenFlags) = + if (let m = canElimAddr(n, c.idgen); m != nil): gen(c, m, dest, flags) return - let af = if n[0].kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr}: {gfAddrOf, gfFieldAccess} - else: {gfAddrOf} - let newflags = if isAddr: flags+af else: flags - # consider: - # proc foo(f: var ref int) = - # f = new(int) - # proc blah() = - # var x: ref int - # foo x - # - # The type of 'f' is 'var ref int' and of 'x' is 'ref int'. Hence for - # nkAddr we must not use 'unneededIndirection', but for deref we use it. - if not isAddr and unneededIndirection(n.sons[0]): - gen(c, n.sons[0], dest, newflags) - if gfAddrOf notin flags and fitsRegister(n.typ): - c.gABC(n, opcNodeToReg, dest, dest) - elif isAddr and isGlobal(n.sons[0]): - gen(c, n.sons[0], dest, flags+af) + let newflags = flags-{gfNode}+{gfNodeAddr} + + if isGlobal(n[0]) or n[0].kind in {nkDotExpr, nkCheckedFieldExpr, nkBracketExpr}: + # checking for this pattern: addr(obj.field) / addr(array[i]) + gen(c, n[0], dest, newflags) else: - let tmp = c.genx(n.sons[0], newflags) + let tmp = c.genx(n[0], newflags) if dest < 0: dest = c.getTemp(n.typ) - if not isAddr: - gABC(c, n, opc, dest, tmp) - assert n.typ != nil - if gfAddrOf notin flags and fitsRegister(n.typ): - c.gABC(n, opcNodeToReg, dest, dest) - elif c.prc.slots[tmp].kind >= slotTempUnknown: + if c.prc.regInfo[tmp].kind >= slotTempUnknown: gABC(c, n, opcAddrNode, dest, tmp) # hack ahead; in order to fix bug #1781 we mark the temporary as # permanent, so that it's not used for anything else: - c.prc.slots[tmp].kind = slotTempPerm + c.prc.regInfo[tmp].kind = slotTempPerm # XXX this is still a hack - #message(n.info, warnUser, "suspicious opcode used") + #message(c.congig, n.info, warnUser, "suspicious opcode used") else: gABC(c, n, opcAddrReg, dest, tmp) c.freeTemp(tmp) -proc whichAsgnOpc(n: PNode): TOpcode = - case n.typ.skipTypes(abstractRange-{tyTypeDesc}).kind - of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64: - opcAsgnInt - of tyString, tyCString: - opcAsgnStr - of tyFloat..tyFloat128: - opcAsgnFloat - of tyRef, tyNil, tyVar, tyLent, tyPtr: - opcAsgnRef +proc genDeref(c: PCtx, n: PNode, dest: var TDest, flags: TGenFlags) = + if unneededIndirection(n[0]): + gen(c, n[0], dest, flags) + if {gfNodeAddr, gfNode} * flags == {} and fitsRegister(n.typ): + c.gABC(n, opcNodeToReg, dest, dest) else: - opcAsgnComplex - -proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode = opc + let tmp = c.genx(n[0], flags) + if dest < 0: dest = c.getTemp(n.typ) + gABC(c, n, opcLdDeref, dest, tmp) + assert n.typ != nil + if {gfNodeAddr, gfNode} * flags == {} and fitsRegister(n.typ): + c.gABC(n, opcNodeToReg, dest, dest) + c.freeTemp(tmp) proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) = let tmp = c.genx(ri) assert dest >= 0 - gABC(c, ri, whichAsgnOpc(ri), dest, tmp, 1-ord(requiresCopy)) + gABC(c, ri, whichAsgnOpc(ri, requiresCopy), dest, tmp) c.freeTemp(tmp) proc setSlot(c: PCtx; v: PSym) = # XXX generate type initialization here? if v.position == 0: - if c.prc.maxSlots == 0: c.prc.maxSlots = 1 - if c.prc.maxSlots >= high(TRegister): - globalError(c.config, v.info, "cannot generate code; too many registers required") - v.position = c.prc.maxSlots - c.prc.slots[v.position] = (inUse: true, - kind: if v.kind == skLet: slotFixedLet else: slotFixedVar) - inc c.prc.maxSlots - -proc cannotEval(c: PCtx; n: PNode) {.noinline.} = + v.position = getFreeRegister(c, if v.kind == skLet: slotFixedLet else: slotFixedVar, start = 1) + +template cannotEval(c: PCtx; n: PNode) = + if c.config.cmd == cmdCheck: + localError(c.config, n.info, "cannot evaluate at compile time: " & + n.renderTree) + return globalError(c.config, n.info, "cannot evaluate at compile time: " & n.renderTree) proc isOwnedBy(a, b: PSym): bool = + result = false var a = a.owner while a != nil and a.kind != skModule: if a == b: return true @@ -1343,28 +1560,37 @@ proc getOwner(c: PCtx): PSym = result = c.prc.sym if result.isNil: result = c.module +proc importcCondVar*(s: PSym): bool {.inline.} = + # see also importcCond + if sfImportc in s.flags: + result = s.kind in {skVar, skLet, skConst} + else: + result = false + proc checkCanEval(c: PCtx; n: PNode) = # we need to ensure that we don't evaluate 'x' here: # proc foo() = var x ... let s = n.sym if {sfCompileTime, sfGlobal} <= s.flags: return + if compiletimeFFI in c.config.features and s.importcCondVar: return if s.kind in {skVar, skTemp, skLet, skParam, skResult} and not s.isOwnedBy(c.prc.sym) and s.owner != c.module and c.mode != emRepl: - cannotEval(c, n) + # little hack ahead for bug #12612: assume gensym'ed variables + # are in the right scope: + if sfGenSym in s.flags and c.prc.sym == nil: discard + elif s.kind == skParam and s.typ.kind == tyTypeDesc: discard + else: cannotEval(c, n) elif s.kind in {skProc, skFunc, skConverter, skMethod, skIterator} and sfForward in s.flags: cannotEval(c, n) -proc isTemp(c: PCtx; dest: TDest): bool = - result = dest >= 0 and c.prc.slots[dest].kind >= slotTempUnknown - template needsAdditionalCopy(n): untyped = not c.isTemp(dest) and not fitsRegister(n.typ) proc genAdditionalCopy(c: PCtx; n: PNode; opc: TOpcode; dest, idx, value: TRegister) = var cc = c.getTemp(n.typ) - c.gABC(n, whichAsgnOpc(n), cc, value, 0) + c.gABC(n, whichAsgnOpc(n), cc, value) c.gABC(n, opc, dest, idx, cc) c.freeTemp(cc) @@ -1382,146 +1608,199 @@ proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode; proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = case le.kind of nkBracketExpr: - let dest = c.genx(le.sons[0], {gfAddrOf, gfFieldAccess}) - let idx = c.genIndex(le.sons[1], le.sons[0].typ) - let tmp = c.genx(ri) - if le.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in { - tyString, tyCString}: + let + dest = c.genx(le[0], {gfNode}) + idx = c.genIndex(le[1], le[0].typ) + tmp = c.genx(ri) + collTyp = le[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}) + case collTyp.kind + of tyString, tyCstring: c.preventFalseAlias(le, opcWrStrIdx, dest, idx, tmp) + of tyTuple: + c.preventFalseAlias(le, opcWrObj, dest, int le[1].intVal, tmp) else: c.preventFalseAlias(le, opcWrArr, dest, idx, tmp) c.freeTemp(tmp) - of nkDotExpr, nkCheckedFieldExpr: - # XXX field checks here - let left = if le.kind == nkDotExpr: le else: le.sons[0] - let dest = c.genx(left.sons[0], {gfAddrOf, gfFieldAccess}) - let idx = genField(c, left.sons[1]) + c.freeTemp(idx) + c.freeTemp(dest) + of nkCheckedFieldExpr: + var objR: TDest = -1 + genCheckedObjAccessAux(c, le, objR, {gfNode}) + let idx = genField(c, le[0][1]) let tmp = c.genx(ri) - c.preventFalseAlias(left, opcWrObj, dest, idx, tmp) + c.preventFalseAlias(le[0], opcWrObj, objR, idx, tmp) c.freeTemp(tmp) + # c.freeTemp(idx) # BUGFIX, see nkDotExpr + c.freeTemp(objR) + of nkDotExpr: + let dest = c.genx(le[0], {gfNode}) + let idx = genField(c, le[1]) + let tmp = c.genx(ri) + c.preventFalseAlias(le, opcWrObj, dest, idx, tmp) + # c.freeTemp(idx) # BUGFIX: idx is an immediate (field position), not a register + c.freeTemp(tmp) + c.freeTemp(dest) of nkDerefExpr, nkHiddenDeref: - let dest = c.genx(le.sons[0], {gfAddrOf}) + let dest = c.genx(le[0], {gfNode}) let tmp = c.genx(ri) c.preventFalseAlias(le, opcWrDeref, dest, 0, tmp) + c.freeTemp(dest) c.freeTemp(tmp) of nkSym: let s = le.sym checkCanEval(c, le) if s.isGlobal: withTemp(tmp, le.typ): - c.gen(le, tmp, {gfAddrOf}) + c.gen(le, tmp, {gfNodeAddr}) let val = c.genx(ri) c.preventFalseAlias(le, opcWrDeref, tmp, 0, val) c.freeTemp(val) else: if s.kind == skForVar: c.setSlot s internalAssert c.config, s.position > 0 or (s.position == 0 and - s.kind in {skParam,skResult}) + s.kind in {skParam, skResult}) var dest: TRegister = s.position + ord(s.kind == skParam) assert le.typ != nil if needsAdditionalCopy(le) and s.kind in {skResult, skVar, skParam}: var cc = c.getTemp(le.typ) gen(c, ri, cc) - c.gABC(le, whichAsgnOpc(le), dest, cc, 1) + c.gABC(le, whichAsgnOpc(le), dest, cc) c.freeTemp(cc) else: gen(c, ri, dest) + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + if sameBackendType(le.typ, le[1].typ): + genAsgn(c, le[1], ri, requiresCopy) else: - let dest = c.genx(le, {gfAddrOf}) + let dest = c.genx(le, {gfNodeAddr}) genAsgn(c, dest, ri, requiresCopy) + c.freeTemp(dest) proc genTypeLit(c: PCtx; t: PType; dest: var TDest) = var n = newNode(nkType) n.typ = t genLit(c, n, dest) +proc isEmptyBody(n: PNode): bool = + case n.kind + of nkStmtList: + for i in 0..<n.len: + if not isEmptyBody(n[i]): return false + result = true + else: + result = n.kind in {nkCommentStmt, nkEmpty} + +proc importcCond*(c: PCtx; s: PSym): bool {.inline.} = + ## return true to importc `s`, false to execute its body instead (refs #8405) + result = false + if sfImportc in s.flags: + if s.kind in routineKinds: + return isEmptyBody(getBody(c.graph, s)) + proc importcSym(c: PCtx; info: TLineInfo; s: PSym) = when hasFFI: - if allowFFI in c.features: - c.globals.add(importcSymbol(s)) + if compiletimeFFI in c.config.features: + c.globals.add(importcSymbol(c.config, s)) s.position = c.globals.len else: - localError(c.config, info, "VM is not allowed to 'importc'") + localError(c.config, info, + "VM is not allowed to 'importc' without --experimental:compiletimeFFI") else: localError(c.config, info, - "cannot 'importc' variable at compile time") + "cannot 'importc' variable at compile time; " & s.name.s) -proc getNullValue*(typ: PType, info: TLineInfo; conf: ConfigRef): PNode +proc getNullValue*(c: PCtx; typ: PType, info: TLineInfo; conf: ConfigRef): PNode proc genGlobalInit(c: PCtx; n: PNode; s: PSym) = - c.globals.add(getNullValue(s.typ, n.info, c.config)) + c.globals.add(getNullValue(c, s.typ, n.info, c.config)) s.position = c.globals.len # This is rather hard to support, due to the laziness of the VM code # generator. See tests/compile/tmacro2 for why this is necessary: # var decls{.compileTime.}: seq[NimNode] = @[] let dest = c.getTemp(s.typ) c.gABx(n, opcLdGlobal, dest, s.position) - if s.ast != nil: - let tmp = c.genx(s.ast) + if s.astdef != nil: + let tmp = c.genx(s.astdef) c.genAdditionalCopy(n, opcWrDeref, dest, 0, tmp) c.freeTemp(dest) c.freeTemp(tmp) proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = + # gfNodeAddr and gfNode are mutually exclusive + assert card(flags * {gfNodeAddr, gfNode}) < 2 let s = n.sym if s.isGlobal: - if sfCompileTime in s.flags or c.mode == emRepl: + let isImportcVar = importcCondVar(s) + if sfCompileTime in s.flags or c.mode == emRepl or isImportcVar: discard elif s.position == 0: cannotEval(c, n) if s.position == 0: - if sfImportc in s.flags: c.importcSym(n.info, s) + if importcCond(c, s) or isImportcVar: c.importcSym(n.info, s) else: genGlobalInit(c, n, s) if dest < 0: dest = c.getTemp(n.typ) assert s.typ != nil - if gfAddrOf notin flags and fitsRegister(s.typ): + + if gfNodeAddr in flags: + if isImportcVar: + c.gABx(n, opcLdGlobalAddrDerefFFI, dest, s.position) + else: + c.gABx(n, opcLdGlobalAddr, dest, s.position) + elif isImportcVar: + c.gABx(n, opcLdGlobalDerefFFI, dest, s.position) + elif gfIsSinkParam in flags: + genAsgn(c, dest, n, requiresCopy = true) + elif fitsRegister(s.typ) and gfNode notin flags: var cc = c.getTemp(n.typ) c.gABx(n, opcLdGlobal, cc, s.position) c.gABC(n, opcNodeToReg, dest, cc) c.freeTemp(cc) - elif {gfAddrOf, gfFieldAccess} * flags == {gfAddrOf}: - c.gABx(n, opcLdGlobalAddr, dest, s.position) else: c.gABx(n, opcLdGlobal, dest, s.position) else: if s.kind == skForVar and c.mode == emRepl: c.setSlot(s) if s.position > 0 or (s.position == 0 and - s.kind in {skParam,skResult}): + s.kind in {skParam, skResult}): if dest < 0: dest = s.position + ord(s.kind == skParam) - internalAssert(c.config, c.prc.slots[dest].kind < slotSomeTemp) + internalAssert(c.config, c.prc.regInfo.len > dest and c.prc.regInfo[dest].kind < slotSomeTemp) else: # we need to generate an assignment: - genAsgn(c, dest, n, c.prc.slots[dest].kind >= slotSomeTemp) + let requiresCopy = c.prc.regInfo[dest].kind >= slotSomeTemp and + gfIsParam notin flags + genAsgn(c, dest, n, requiresCopy) else: # see tests/t99bott for an example that triggers it: cannotEval(c, n) template needsRegLoad(): untyped = - gfAddrOf notin flags and fitsRegister(n.typ.skipTypes({tyVar, tyLent})) + {gfNode, gfNodeAddr} * flags == {} and + fitsRegister(n.typ.skipTypes({tyVar, tyLent, tyStatic})) -proc genArrAccess2(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; - flags: TGenFlags) = - let a = c.genx(n.sons[0], flags) - let b = c.genIndex(n.sons[1], n.sons[0].typ) +proc genArrAccessOpcode(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; + flags: TGenFlags) = + let a = c.genx(n[0], flags) + let b = c.genIndex(n[1], n[0].typ) if dest < 0: dest = c.getTemp(n.typ) - if needsRegLoad(): + if opc in {opcLdArrAddr, opcLdStrIdxAddr} and gfNodeAddr in flags: + c.gABC(n, opc, dest, a, b) + elif needsRegLoad(): var cc = c.getTemp(n.typ) c.gABC(n, opc, cc, a, b) c.gABC(n, opcNodeToReg, dest, cc) c.freeTemp(cc) else: - #message(n.info, warnUser, "argh") + #message(c.config, n.info, warnUser, "argh") #echo "FLAGS ", flags, " ", fitsRegister(n.typ), " ", typeToString(n.typ) c.gABC(n, opc, dest, a, b) c.freeTemp(a) c.freeTemp(b) -proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = - let a = c.genx(n.sons[0], flags) - let b = genField(c, n.sons[1]) +proc genObjAccessAux(c: PCtx; n: PNode; a, b: int, dest: var TDest; flags: TGenFlags) = if dest < 0: dest = c.getTemp(n.typ) - if needsRegLoad(): + if {gfNodeAddr} * flags != {}: + c.gABC(n, opcLdObjAddr, dest, a, b) + elif needsRegLoad(): var cc = c.getTemp(n.typ) c.gABC(n, opcLdObj, cc, a, b) c.gABC(n, opcNodeToReg, dest, cc) @@ -1530,36 +1809,114 @@ proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = c.gABC(n, opcLdObj, dest, a, b) c.freeTemp(a) +proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = + genObjAccessAux(c, n, c.genx(n[0], flags), genField(c, n[1]), dest, flags) + + + +proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = + internalAssert c.config, n.kind == nkCheckedFieldExpr + # nkDotExpr to access the requested field + let accessExpr = n[0] + # nkCall to check if the discriminant is valid + var checkExpr = n[1] + + let negCheck = checkExpr[0].sym.magic == mNot + if negCheck: + checkExpr = checkExpr[^1] + + # Discriminant symbol + let disc = checkExpr[2] + internalAssert c.config, disc.sym.kind == skField + + # Load the object in `dest` + c.gen(accessExpr[0], dest, flags) + # Load the discriminant + var discVal = c.getTemp(disc.typ) + c.gABC(n, opcLdObj, discVal, dest, genField(c, disc)) + # Check if its value is contained in the supplied set + let setLit = c.genx(checkExpr[1]) + var rs = c.getTemp(getSysType(c.graph, n.info, tyBool)) + c.gABC(n, opcContainsSet, rs, setLit, discVal) + c.freeTemp(setLit) + # If the check fails let the user know + let lab1 = c.xjmp(n, if negCheck: opcFJmp else: opcTJmp, rs) + c.freeTemp(rs) + let strType = getSysType(c.graph, n.info, tyString) + var msgReg: TDest = c.getTemp(strType) + let fieldName = $accessExpr[1] + let msg = genFieldDefect(c.config, fieldName, disc.sym) + let strLit = newStrNode(msg, accessExpr[1].info) + strLit.typ = strType + c.genLit(strLit, msgReg) + c.gABC(n, opcInvalidField, msgReg, discVal) + c.freeTemp(discVal) + c.freeTemp(msgReg) + c.patch(lab1) + proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = - # XXX implement field checks! - genObjAccess(c, n.sons[0], dest, flags) + var objR: TDest = -1 + genCheckedObjAccessAux(c, n, objR, flags) + + let accessExpr = n[0] + # Field symbol + var field = accessExpr[1] + internalAssert c.config, field.sym.kind == skField + + # Load the content now + if dest < 0: dest = c.getTemp(n.typ) + let fieldPos = genField(c, field) + + if {gfNodeAddr} * flags != {}: + c.gABC(n, opcLdObjAddr, dest, objR, fieldPos) + elif needsRegLoad(): + var cc = c.getTemp(accessExpr.typ) + c.gABC(n, opcLdObj, cc, objR, fieldPos) + c.gABC(n, opcNodeToReg, dest, cc) + c.freeTemp(cc) + else: + c.gABC(n, opcLdObj, dest, objR, fieldPos) + + c.freeTemp(objR) proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = - let arrayType = n.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind - if arrayType in {tyString, tyCString}: - genArrAccess2(c, n, dest, opcLdStrIdx, {}) - elif arrayType == tyTypeDesc: + let arrayType = n[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind + case arrayType + of tyString, tyCstring: + let opc = if gfNodeAddr in flags: opcLdStrIdxAddr else: opcLdStrIdx + genArrAccessOpcode(c, n, dest, opc, flags) + of tyTuple: + c.genObjAccessAux(n, c.genx(n[0], flags), int n[1].intVal, dest, flags) + of tyTypeDesc: c.genTypeLit(n.typ, dest) else: - genArrAccess2(c, n, dest, opcLdArr, flags) + let opc = if gfNodeAddr in flags: opcLdArrAddr else: opcLdArr + genArrAccessOpcode(c, n, dest, opc, flags) -proc getNullValueAux(obj: PNode, result: PNode; conf: ConfigRef) = +proc getNullValueAux(c: PCtx; t: PType; obj: PNode, result: PNode; conf: ConfigRef; currPosition: var int) = + if t != nil and t.baseClass != nil: + let b = skipTypes(t.baseClass, skipPtrs) + getNullValueAux(c, b, b.n, result, conf, currPosition) case obj.kind of nkRecList: - for i in countup(0, sonsLen(obj) - 1): getNullValueAux(obj.sons[i], result, conf) + for i in 0..<obj.len: getNullValueAux(c, nil, obj[i], result, conf, currPosition) of nkRecCase: - getNullValueAux(obj.sons[0], result, conf) - for i in countup(1, sonsLen(obj) - 1): - getNullValueAux(lastSon(obj.sons[i]), result, conf) + getNullValueAux(c, nil, obj[0], result, conf, currPosition) + for i in 1..<obj.len: + getNullValueAux(c, nil, lastSon(obj[i]), result, conf, currPosition) of nkSym: let field = newNodeI(nkExprColonExpr, result.info) field.add(obj) - field.add(getNullValue(obj.sym.typ, result.info, conf)) - addSon(result, field) + let value = getNullValue(c, obj.sym.typ, result.info, conf) + value.flags.incl nfSkipFieldChecking + field.add(value) + result.add field + doAssert obj.sym.position == currPosition + inc currPosition else: globalError(conf, result.info, "cannot create null element for: " & $obj) -proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode = - var t = skipTypes(typ, abstractRange-{tyTypeDesc}) +proc getNullValue(c: PCtx; typ: PType, info: TLineInfo; conf: ConfigRef): PNode = + var t = skipTypes(typ, abstractRange+{tyStatic, tyOwned}-{tyTypeDesc}) case t.kind of tyBool, tyEnum, tyChar, tyInt..tyInt64: result = newNodeIT(nkIntLit, info, t) @@ -1567,103 +1924,112 @@ proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode = result = newNodeIT(nkUIntLit, info, t) of tyFloat..tyFloat128: result = newNodeIT(nkFloatLit, info, t) - of tyCString, tyString: + of tyString: result = newNodeIT(nkStrLit, info, t) result.strVal = "" - of tyVar, tyLent, tyPointer, tyPtr, tyExpr, - tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil: + of tyCstring, tyVar, tyLent, tyPointer, tyPtr, tyUntyped, + tyTyped, tyTypeDesc, tyRef, tyNil: result = newNodeIT(nkNilLit, info, t) of tyProc: if t.callConv != ccClosure: result = newNodeIT(nkNilLit, info, t) else: result = newNodeIT(nkTupleConstr, info, t) - result.add(newNodeIT(nkNilLit, info, t)) - result.add(newNodeIT(nkNilLit, info, t)) + result.add(newNodeIT(nkNilLit, info, getSysType(c.graph, info, tyPointer))) + result.add(newNodeIT(nkNilLit, info, getSysType(c.graph, info, tyPointer))) of tyObject: result = newNodeIT(nkObjConstr, info, t) result.add(newNodeIT(nkEmpty, info, t)) - # initialize inherited fields: - var base = t.sons[0] - while base != nil: - getNullValueAux(skipTypes(base, skipPtrs).n, result, conf) - base = base.sons[0] - getNullValueAux(t.n, result, conf) + # initialize inherited fields, and all in the correct order: + var currPosition = 0 + getNullValueAux(c, t, t.n, result, conf, currPosition) of tyArray: result = newNodeIT(nkBracket, info, t) - for i in countup(0, int(lengthOrd(conf, t)) - 1): - addSon(result, getNullValue(elemType(t), info, conf)) + for i in 0..<toInt(lengthOrd(conf, t)): + result.add getNullValue(c, elemType(t), info, conf) of tyTuple: result = newNodeIT(nkTupleConstr, info, t) - for i in countup(0, sonsLen(t) - 1): - addSon(result, getNullValue(t.sons[i], info, conf)) + for a in t.kids: + result.add getNullValue(c, a, info, conf) of tySet: result = newNodeIT(nkCurly, info, t) - of tyOpt: - result = newNodeIT(nkNilLit, info, t) - of tySequence: + of tySequence, tyOpenArray: result = newNodeIT(nkBracket, info, t) else: globalError(conf, info, "cannot create null element for: " & $t.kind) result = newNodeI(nkEmpty, info) -proc ldNullOpcode(t: PType): TOpcode = - assert t != nil - if fitsRegister(t): opcLdNullReg else: opcLdNull - proc genVarSection(c: PCtx; n: PNode) = for a in n: if a.kind == nkCommentStmt: continue - #assert(a.sons[0].kind == nkSym) can happen for transformed vars + #assert(a[0].kind == nkSym) can happen for transformed vars if a.kind == nkVarTuple: - for i in 0 .. a.len-3: - if not a[i].sym.isGlobal: setSlot(c, a[i].sym) - checkCanEval(c, a[i]) - c.gen(lowerTupleUnpacking(c.graph, a, c.getOwner)) - elif a.sons[0].kind == nkSym: - let s = a.sons[0].sym - checkCanEval(c, a.sons[0]) + for i in 0..<a.len-2: + if a[i].kind == nkSym: + if not a[i].sym.isGlobal: setSlot(c, a[i].sym) + checkCanEval(c, a[i]) + c.gen(lowerTupleUnpacking(c.graph, a, c.idgen, c.getOwner)) + elif a[0].kind == nkSym: + let s = a[0].sym + checkCanEval(c, a[0]) if s.isGlobal: + let runtimeAccessToCompileTime = c.mode == emRepl and + sfCompileTime in s.flags and s.position > 0 if s.position == 0: - if sfImportc in s.flags: c.importcSym(a.info, s) + if importcCond(c, s): c.importcSym(a.info, s) else: - let sa = getNullValue(s.typ, a.info, c.config) + let sa = getNullValue(c, s.typ, a.info, c.config) #if s.ast.isNil: getNullValue(s.typ, a.info) - #else: canonValue(s.ast) + #else: s.ast assert sa.kind != nkCall c.globals.add(sa) s.position = c.globals.len - if a.sons[2].kind != nkEmpty: - let tmp = c.genx(a.sons[0], {gfAddrOf}) - let val = c.genx(a.sons[2]) - c.genAdditionalCopy(a.sons[2], opcWrDeref, tmp, 0, val) + if runtimeAccessToCompileTime: + discard + elif a[2].kind != nkEmpty: + let tmp = c.genx(a[0], {gfNodeAddr}) + let val = c.genx(a[2]) + c.genAdditionalCopy(a[2], opcWrDeref, tmp, 0, val) + c.freeTemp(val) + c.freeTemp(tmp) + elif not importcCondVar(s) and not (s.typ.kind == tyProc and s.typ.callConv == ccClosure) and + sfPure notin s.flags: # fixes #10938 + # there is a pre-existing issue with closure types in VM + # if `(var s: proc () = default(proc ()); doAssert s == nil)` works for you; + # you might remove the second condition. + # the problem is that closure types are tuples in VM, but the types of its children + # shouldn't have the same type as closure types. + let tmp = c.genx(a[0], {gfNodeAddr}) + let sa = getNullValue(c, s.typ, a.info, c.config) + let val = c.genx(sa) + c.genAdditionalCopy(sa, opcWrDeref, tmp, 0, val) c.freeTemp(val) c.freeTemp(tmp) else: setSlot(c, s) - if a.sons[2].kind == nkEmpty: + if a[2].kind == nkEmpty: c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ)) else: assert s.typ != nil if not fitsRegister(s.typ): c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ)) - let le = a.sons[0] + let le = a[0] assert le.typ != nil if not fitsRegister(le.typ) and s.kind in {skResult, skVar, skParam}: var cc = c.getTemp(le.typ) - gen(c, a.sons[2], cc) - c.gABC(le, whichAsgnOpc(le), s.position.TRegister, cc, 1) + gen(c, a[2], cc) + c.gABC(le, whichAsgnOpc(le), s.position.TRegister, cc) c.freeTemp(cc) else: - gen(c, a.sons[2], s.position.TRegister) + gen(c, a[2], s.position.TRegister) else: - # assign to a.sons[0]; happens for closures - if a.sons[2].kind == nkEmpty: - let tmp = genx(c, a.sons[0]) - c.gABx(a, ldNullOpcode(a[0].typ), tmp, c.genType(a.sons[0].typ)) + # assign to a[0]; happens for closures + if a[2].kind == nkEmpty: + let tmp = genx(c, a[0]) + c.gABx(a, ldNullOpcode(a[0].typ), tmp, c.genType(a[0].typ)) c.freeTemp(tmp) else: - genAsgn(c, a.sons[0], a.sons[2], true) + genAsgn(c, a[0], a[2], true) proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) = if dest < 0: dest = c.getTemp(n.typ) @@ -1683,7 +2049,7 @@ proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) = c.gABx(n, opcLdNullReg, tmp, c.genType(intType)) for x in n: let a = c.genx(x) - c.preventFalseAlias(n, whichAsgnOpc(x, opcWrArr), dest, tmp, a) + c.preventFalseAlias(n, opcWrArr, dest, tmp, a) c.gABI(n, opcAddImmInt, tmp, tmp, 1) c.freeTemp(a) c.freeTemp(tmp) @@ -1693,8 +2059,8 @@ proc genSetConstr(c: PCtx, n: PNode, dest: var TDest) = c.gABx(n, opcLdNull, dest, c.genType(n.typ)) for x in n: if x.kind == nkRange: - let a = c.genx(x.sons[0]) - let b = c.genx(x.sons[1]) + let a = c.genx(x[0]) + let b = c.genx(x[1]) c.gABC(n, opcInclRange, dest, a, b) c.freeTemp(b) c.freeTemp(a) @@ -1704,18 +2070,20 @@ proc genSetConstr(c: PCtx, n: PNode, dest: var TDest) = c.freeTemp(a) proc genObjConstr(c: PCtx, n: PNode, dest: var TDest) = + if tfUnion in n.typ.flags: # bug #22708 # bug #13481 + globalError(c.config, n.info, "object with '{.union.}' pragmas is not supported by VM") if dest < 0: dest = c.getTemp(n.typ) - let t = n.typ.skipTypes(abstractRange-{tyTypeDesc}) + let t = n.typ.skipTypes(abstractRange+{tyOwned}-{tyTypeDesc}) if t.kind == tyRef: - c.gABx(n, opcNew, dest, c.genType(t.sons[0])) + c.gABx(n, opcNew, dest, c.genType(t.elementType)) else: c.gABx(n, opcLdNull, dest, c.genType(n.typ)) for i in 1..<n.len: - let it = n.sons[i] - if it.kind == nkExprColonExpr and it.sons[0].kind == nkSym: - let idx = genField(c, it.sons[0]) - let tmp = c.genx(it.sons[1]) - c.preventFalseAlias(it.sons[1], whichAsgnOpc(it.sons[1], opcWrObj), + let it = n[i] + if it.kind == nkExprColonExpr and it[0].kind == nkSym: + let idx = genField(c, it[0]) + let tmp = c.genx(it[1]) + c.preventFalseAlias(it[1], opcWrObj, dest, idx, tmp) c.freeTemp(tmp) else: @@ -1723,70 +2091,77 @@ proc genObjConstr(c: PCtx, n: PNode, dest: var TDest) = proc genTupleConstr(c: PCtx, n: PNode, dest: var TDest) = if dest < 0: dest = c.getTemp(n.typ) - c.gABx(n, opcLdNull, dest, c.genType(n.typ)) - # XXX x = (x.old, 22) produces wrong code ... stupid self assignments - for i in 0..<n.len: - let it = n.sons[i] - if it.kind == nkExprColonExpr: - let idx = genField(c, it.sons[0]) - let tmp = c.genx(it.sons[1]) - c.preventFalseAlias(it.sons[1], whichAsgnOpc(it.sons[1], opcWrObj), - dest, idx, tmp) - c.freeTemp(tmp) - else: - let tmp = c.genx(it) - c.preventFalseAlias(it, whichAsgnOpc(it, opcWrObj), dest, i.TRegister, tmp) - c.freeTemp(tmp) + if n.typ.kind != tyTypeDesc: + c.gABx(n, opcLdNull, dest, c.genType(n.typ)) + # XXX x = (x.old, 22) produces wrong code ... stupid self assignments + for i in 0..<n.len: + let it = n[i] + if it.kind == nkExprColonExpr: + let idx = genField(c, it[0]) + let tmp = c.genx(it[1]) + c.preventFalseAlias(it[1], opcWrObj, + dest, idx, tmp) + c.freeTemp(tmp) + else: + let tmp = c.genx(it) + c.preventFalseAlias(it, opcWrObj, dest, i.TRegister, tmp) + c.freeTemp(tmp) proc genProc*(c: PCtx; s: PSym): int -proc matches(s: PSym; x: string): bool = - let y = x.split('.') +proc toKey(s: PSym): string = + result = "" var s = s - var L = y.len-1 - while L >= 0: - if s == nil or (y[L].cmpIgnoreStyle(s.name.s) != 0 and y[L] != "*"): - return false - s = s.owner - dec L - result = true - -proc matches(s: PSym; y: varargs[string]): bool = - var s = s - var L = y.len-1 - while L >= 0: - if s == nil or (y[L].cmpIgnoreStyle(s.name.s) != 0 and y[L] != "*"): - return false - s = if sfFromGeneric in s.flags: s.owner.owner else: s.owner - dec L - result = true + while s != nil: + result.add s.name.s + if s.owner != nil: + if sfFromGeneric in s.flags: + s = s.instantiatedFrom.owner + else: + s = s.owner + result.add "." + else: + break proc procIsCallback(c: PCtx; s: PSym): bool = if s.offset < -1: return true - var i = -2 - for key, value in items(c.callbacks): - if s.matches(key): - doAssert s.offset == -1 - s.offset = i - return true - dec i + let key = toKey(s) + if c.callbackIndex.contains(key): + let index = c.callbackIndex[key] + doAssert s.offset == -1 + s.offset = -2'i32 - index.int32 + result = true + else: + result = false proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = + when defined(nimCompilerStacktraceHints): + setFrameMsg c.config$n.info & " " & $n.kind & " " & $flags case n.kind of nkSym: let s = n.sym checkCanEval(c, n) case s.kind - of skVar, skForVar, skTemp, skLet, skParam, skResult: + of skVar, skForVar, skTemp, skLet, skResult: genRdVar(c, n, dest, flags) + of skParam: + if s.typ.kind == tyTypeDesc: + genTypeLit(c, s.typ.skipTypes({tyTypeDesc}), dest) + else: + genRdVar(c, n, dest, flags) of skProc, skFunc, skConverter, skMacro, skTemplate, skMethod, skIterator: # 'skTemplate' is only allowed for 'getAst' support: + if s.kind == skIterator and s.typ.callConv == TCallingConvention.ccClosure: + globalError(c.config, n.info, "Closure iterators are not supported by VM!") if procIsCallback(c, s): discard - elif sfImportc in s.flags: c.importcSym(n.info, s) + elif importcCond(c, s): c.importcSym(n.info, s) genLit(c, n, dest) of skConst: - let constVal = if s.ast != nil: s.ast else: s.typ.n - gen(c, constVal, dest) + let constVal = if s.astdef != nil: s.astdef else: s.typ.n + if dontInlineConstant(n, constVal): + genLit(c, constVal, dest) + else: + gen(c, constVal, dest) of skEnumField: # we never reach this case - as of the time of this comment, # skEnumField is folded to an int in semfold.nim, but this code @@ -1807,16 +2182,13 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = else: globalError(c.config, n.info, "cannot generate code for: " & s.name.s) of nkCallKinds: - if n.sons[0].kind == nkSym: - let s = n.sons[0].sym + if n[0].kind == nkSym: + let s = n[0].sym if s.magic != mNone: - genMagic(c, n, dest, s.magic) - elif matches(s, "stdlib", "marshal", "to"): - # XXX marshal load&store should not be opcodes, but use the - # general callback mechanisms. - genMarshalLoad(c, n, dest) - elif matches(s, "stdlib", "marshal", "$$"): - genMarshalStore(c, n, dest) + genMagic(c, n, dest, flags, s.magic) + elif s.kind == skMethod: + localError(c.config, n.info, "cannot call method " & s.name.s & + " at compile time") else: genCall(c, n, dest) clearDest(c, n, dest) @@ -1831,93 +2203,97 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = genLit(c, n, dest) of nkUIntLit..pred(nkNilLit): genLit(c, n, dest) of nkNilLit: - if not n.typ.isEmptyType: genLit(c, getNullValue(n.typ, n.info, c.config), dest) + if not n.typ.isEmptyType: genLit(c, getNullValue(c, n.typ, n.info, c.config), dest) else: unused(c, n, dest) - of nkAsgn, nkFastAsgn: + of nkAsgn, nkFastAsgn, nkSinkAsgn: unused(c, n, dest) - genAsgn(c, n.sons[0], n.sons[1], n.kind == nkAsgn) + genAsgn(c, n[0], n[1], n.kind == nkAsgn) of nkDotExpr: genObjAccess(c, n, dest, flags) of nkCheckedFieldExpr: genCheckedObjAccess(c, n, dest, flags) of nkBracketExpr: genArrAccess(c, n, dest, flags) - of nkDerefExpr, nkHiddenDeref: genAddrDeref(c, n, dest, opcLdDeref, flags) - of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddrNode, flags) + of nkDerefExpr, nkHiddenDeref: genDeref(c, n, dest, flags) + of nkAddr, nkHiddenAddr: genAddr(c, n, dest, flags) of nkIfStmt, nkIfExpr: genIf(c, n, dest) of nkWhenStmt: # This is "when nimvm" node. Chose the first branch. - gen(c, n.sons[0].sons[1], dest) + gen(c, n[0][1], dest) of nkCaseStmt: genCase(c, n, dest) of nkWhileStmt: unused(c, n, dest) genWhile(c, n) of nkBlockExpr, nkBlockStmt: genBlock(c, n, dest) of nkReturnStmt: - unused(c, n, dest) genReturn(c, n) of nkRaiseStmt: genRaise(c, n) of nkBreakStmt: - unused(c, n, dest) genBreak(c, n) - of nkTryStmt: genTry(c, n, dest) + of nkTryStmt, nkHiddenTryStmt: genTry(c, n, dest) of nkStmtList: #unused(c, n, dest) # XXX Fix this bug properly, lexim triggers it for x in n: gen(c, x) of nkStmtListExpr: - let L = n.len-1 - for i in 0 ..< L: gen(c, n.sons[i]) - gen(c, n.sons[L], dest, flags) + for i in 0..<n.len-1: gen(c, n[i]) + gen(c, n[^1], dest, flags) of nkPragmaBlock: gen(c, n.lastSon, dest, flags) of nkDiscardStmt: unused(c, n, dest) - gen(c, n.sons[0]) + gen(c, n[0]) of nkHiddenStdConv, nkHiddenSubConv, nkConv: - genConv(c, n, n.sons[1], dest) + genConv(c, n, n[1], dest, flags) of nkObjDownConv: - genConv(c, n, n.sons[0], dest) + genConv(c, n, n[0], dest, flags) + of nkObjUpConv: + genConv(c, n, n[0], dest, flags) of nkVarSection, nkLetSection: unused(c, n, dest) genVarSection(c, n) - of declarativeDefs, nkMacroDef: - unused(c, n, dest) of nkLambdaKinds: - #let s = n.sons[namePos].sym + #let s = n[namePos].sym #discard genProc(c, s) - genLit(c, newSymNode(n.sons[namePos].sym), dest) + genLit(c, newSymNode(n[namePos].sym), dest) of nkChckRangeF, nkChckRange64, nkChckRange: - let - tmp0 = c.genx(n.sons[0]) - tmp1 = c.genx(n.sons[1]) - tmp2 = c.genx(n.sons[2]) - c.gABC(n, opcRangeChck, tmp0, tmp1, tmp2) - c.freeTemp(tmp1) - c.freeTemp(tmp2) - if dest >= 0: - gABC(c, n, whichAsgnOpc(n), dest, tmp0, 1) - c.freeTemp(tmp0) + if skipTypes(n.typ, abstractVar).kind in {tyUInt..tyUInt64}: + genConv(c, n, n[0], dest, flags) else: - dest = tmp0 + let + tmp0 = c.genx(n[0]) + tmp1 = c.genx(n[1]) + tmp2 = c.genx(n[2]) + c.gABC(n, opcRangeChck, tmp0, tmp1, tmp2) + c.freeTemp(tmp1) + c.freeTemp(tmp2) + if dest >= 0: + gABC(c, n, whichAsgnOpc(n), dest, tmp0) + c.freeTemp(tmp0) + else: + dest = tmp0 of nkEmpty, nkCommentStmt, nkTypeSection, nkConstSection, nkPragma, - nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt: + nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt, + nkMixinStmt, nkBindStmt, declarativeDefs, nkMacroDef: unused(c, n, dest) of nkStringToCString, nkCStringToString: - gen(c, n.sons[0], dest) + gen(c, n[0], dest) of nkBracket: genArrayConstr(c, n, dest) of nkCurly: genSetConstr(c, n, dest) of nkObjConstr: genObjConstr(c, n, dest) of nkPar, nkClosure, nkTupleConstr: genTupleConstr(c, n, dest) of nkCast: if allowCast in c.features: - genConv(c, n, n.sons[1], dest, opcCast) + genConv(c, n, n[1], dest, flags, opcCast) else: - genIntCast(c, n, dest) + genCastIntFloat(c, n, dest) of nkTypeOfExpr: genTypeLit(c, n.typ, dest) of nkComesFrom: discard "XXX to implement for better stack traces" else: - globalError(c.config, n.info, "cannot generate VM code for " & $n) + if n.typ != nil and n.typ.isCompileTimeOnly: + genTypeLit(c, n.typ, dest) + else: + globalError(c.config, n.info, "cannot generate VM code for " & $n) proc removeLastEof(c: PCtx) = let last = c.code.len-1 @@ -1952,29 +2328,29 @@ proc genExpr*(c: PCtx; n: PNode, requiresValue = true): int = proc genParams(c: PCtx; params: PNode) = # res.sym.position is already 0 - c.prc.slots[0] = (inUse: true, kind: slotFixedVar) + setLen(c.prc.regInfo, max(params.len, 1)) + c.prc.regInfo[0] = (inUse: true, kind: slotFixedVar) for i in 1..<params.len: - c.prc.slots[i] = (inUse: true, kind: slotFixedLet) - c.prc.maxSlots = max(params.len, 1) + c.prc.regInfo[i] = (inUse: true, kind: slotFixedLet) proc finalJumpTarget(c: PCtx; pc, diff: int) = - internalAssert(c.config, -0x7fff < diff and diff < 0x7fff) + internalAssert(c.config, regBxMin < diff and diff < regBxMax) let oldInstr = c.code[pc] # opcode and regA stay the same: - c.code[pc] = ((oldInstr.uint32 and 0xffff'u32).uint32 or - uint32(diff+wordExcess) shl 16'u32).TInstr + c.code[pc] = ((oldInstr.TInstrType and ((regOMask shl regOShift) or (regAMask shl regAShift))).TInstrType or + TInstrType(diff+wordExcess) shl regBxShift).TInstr proc genGenericParams(c: PCtx; gp: PNode) = - var base = c.prc.maxSlots + var base = c.prc.regInfo.len + setLen c.prc.regInfo, base + gp.len for i in 0..<gp.len: - var param = gp.sons[i].sym + var param = gp[i].sym param.position = base + i # XXX: fix this earlier; make it consistent with templates - c.prc.slots[base + i] = (inUse: true, kind: slotFixedLet) - c.prc.maxSlots = base + gp.len + c.prc.regInfo[base + i] = (inUse: true, kind: slotFixedLet) proc optimizeJumps(c: PCtx; start: int) = const maxIterations = 10 - for i in start ..< c.code.len: + for i in start..<c.code.len: let opc = c.code[i].opcode case opc of opcTJmp, opcFJmp: @@ -1982,8 +2358,8 @@ proc optimizeJumps(c: PCtx; start: int) = var d = i + c.code[i].jmpDiff for iters in countdown(maxIterations, 0): case c.code[d].opcode - of opcJmp, opcJmpBack: - d = d + c.code[d].jmpDiff + of opcJmp: + d += c.code[d].jmpDiff of opcTJmp, opcFJmp: if c.code[d].regA != reg: break # tjmp x, 23 @@ -1991,12 +2367,12 @@ proc optimizeJumps(c: PCtx; start: int) = # tjmp x, 12 # -- we know 'x' is true, and so can jump to 12+13: if c.code[d].opcode == opc: - d = d + c.code[d].jmpDiff + d += c.code[d].jmpDiff else: # tjmp x, 23 # fjmp x, 22 # We know 'x' is true so skip to the next instruction: - d = d + 1 + d += 1 else: break if d != i + c.code[i].jmpDiff: c.finalJumpTarget(i, d - i) @@ -2004,7 +2380,7 @@ proc optimizeJumps(c: PCtx; start: int) = var d = i + c.code[i].jmpDiff var iters = maxIterations while c.code[d].opcode == opcJmp and iters > 0: - d = d + c.code[d].jmpDiff + d += c.code[d].jmpDiff dec iters if c.code[d].opcode == opcRet: # optimize 'jmp to ret' to 'ret' here @@ -2014,26 +2390,30 @@ proc optimizeJumps(c: PCtx; start: int) = else: discard proc genProc(c: PCtx; s: PSym): int = - var x = s.ast.sons[miscPos] - if x.kind == nkEmpty or x[0].kind == nkEmpty: + let + pos = c.procToCodePos.getOrDefault(s.id) + wasNotGenProcBefore = pos == 0 + noRegistersAllocated = s.offset == -1 + if wasNotGenProcBefore or noRegistersAllocated: + # xxx: the noRegisterAllocated check is required in order to avoid issues + # where nimsuggest can crash due as a macro with pos will be loaded + # but it doesn't have offsets for register allocations see: + # https://github.com/nim-lang/Nim/issues/18385 + # Improvements and further use of IC should remove the need for this. #if s.name.s == "outterMacro" or s.name.s == "innerProc": # echo "GENERATING CODE FOR ", s.name.s let last = c.code.len-1 - var eofInstr: TInstr + var eofInstr: TInstr = default(TInstr) if last >= 0 and c.code[last].opcode == opcEof: eofInstr = c.code[last] c.code.setLen(last) c.debug.setLen(last) #c.removeLastEof result = c.code.len+1 # skip the jump instruction - if x.kind == nkEmpty: - x = newTree(nkBracket, newIntNode(nkIntLit, result), x) - else: - x.sons[0] = newIntNode(nkIntLit, result) - s.ast.sons[miscPos] = x + c.procToCodePos[s.id] = result # thanks to the jmp we can add top level statements easily and also nest # procs easily: - let body = s.getBody + let body = transformBody(c.graph, c.idgen, s, if isCompileTimeProc(s): {} else: {useCache}) let procStart = c.xjmp(body, opcJmp, 0) var p = PProc(blocks: @[], sym: s) let oldPrc = c.prc @@ -2042,27 +2422,24 @@ proc genProc(c: PCtx; s: PSym): int = genParams(c, s.typ.n) # allocate additional space for any generically bound parameters - if s.kind == skMacro and - sfImmediate notin s.flags and - s.ast[genericParamsPos].kind != nkEmpty: + if s.kind == skMacro and s.isGenericRoutineStrict: genGenericParams(c, s.ast[genericParamsPos]) if tfCapturesEnv in s.typ.flags: - #let env = s.ast.sons[paramsPos].lastSon.sym + #let env = s.ast[paramsPos].lastSon.sym #assert env.position == 2 - c.prc.slots[c.prc.maxSlots] = (inUse: true, kind: slotFixedLet) - inc c.prc.maxSlots + c.prc.regInfo.add (inUse: true, kind: slotFixedLet) gen(c, body) # generate final 'return' statement: c.gABC(body, opcRet) c.patch(procStart) c.gABC(body, opcEof, eofInstr.regA) c.optimizeJumps(result) - s.offset = c.prc.maxSlots - #if s.name.s == "calc": + s.offset = c.prc.regInfo.len.int32 + #if s.name.s == "main" or s.name.s == "[]": # echo renderTree(body) # c.echoCode(result) c.prc = oldPrc else: - c.prc.maxSlots = s.offset - result = x[0].intVal.int + c.prc.regInfo.setLen s.offset + result = pos |