From a8279ad7611d20a5b64483d7ddc6ece74b92fe43 Mon Sep 17 00:00:00 2001 From: zerbina <100542850+zerbina@users.noreply.github.com> Date: Mon, 20 Feb 2023 18:02:01 +0100 Subject: [PATCH 01/20] WIP: introduce an IR for the code generators --- compiler/backend/cgir.nim | 151 +++++++++ compiler/vm/vmdef.nim | 3 +- compiler/vm/vmgen.nim | 692 ++++++++++++++++---------------------- 3 files changed, 437 insertions(+), 409 deletions(-) create mode 100644 compiler/backend/cgir.nim diff --git a/compiler/backend/cgir.nim b/compiler/backend/cgir.nim new file mode 100644 index 00000000000..9c20d3c77d6 --- /dev/null +++ b/compiler/backend/cgir.nim @@ -0,0 +1,151 @@ +## This module contains an IR of program code for use by the code-generators. +## It's semantics are derived from the MIR's, but it uses a ``ref``-based tree +## structure, which is meant to make it easier to transition over the +## code generators. +## +## The plan is to, at first, replace the usage of ``PNode`` AST in the code +## generators. Once that is done, the next step is to incrementally simplify +## and evolve the IR, propagating design improvement back into the MIR (and +## vice versa). + +## Mapping ``TNodeKind`` to ``CgNodeKind``: +## - nkNone -> cgnkInvalid +## TODO: continue + +import compiler/ast/[ast_types, lineinfos] +import compiler/mir/mirtrees + +type + CgNodeKind* = enum + cgnkInvalid ## uninitialized node kind. Used to catch issues + + cgnkEmpty ## represents the absence of a node. The meaning depend on the + ## context + cgnkType ## a literal type + + cgnkIntLit ## an embedded integer value; for internal use only -- doesn't represent a literal as used in the code + cgnkLiteralData ## embedded data + cgnkNilLit ## the nil literal + cgnkNimNodeLit ## a ``NimNode`` literal. Stores a single sub node + ## that represents the ``NimNode`` AST + ## end of atoms + + cgnkTemp + cgnkProc, cgnkLocal, cgnkGlobal, cgnkConst, cgnkField, cgnkParam + cgnkMagic, + cgnkLabel + + cgnkCall ## a procedure or magic call + cgnkArgTuple ## an argument tuple. Corresponds to ``mnkArgBlock`` + + # constructors: + cgnkTupleConstr ## tuple constructor + cgnkObjConstr ## object constructor + cgnkSetConstr ## set constructor + cgnkArrayConstr ## array constructor + cgnkClosureConstr ## closure constructor + + cgnkRange # for compatibility + + cgnkGoto ## + cgnkJoin ## the destination of one or more `goto`s + + cgnkMap + + # + cgnkEmit + cgnkAsm + + # access: + cgnkObjAccess + cgnkTupleAccess + cgnkArrayAccess + cgnkVariantAccess + + cgnkDeref + cgnkDerefView + cgnkAddr + cgnkView ## take a view of the input. Has the same meaning as ``mnkView`` + + cgnkStmtList + cgnkStmtListExpr + cgnkScope + + cgnkVoidStmt + + cgnkIf + cgnkRepeat + cgnkCase + cgnkBlock + + cgnkTryStmt + cgnkExcept + cgnkFinally + + cgnkBranch + + cgnkBreak + cgnkRaise + cgnkReturn + + cgnkConv ## a type conversion + cgnkLConv ## a type conversion that is l-value preserving + + cgnkObjDownConv ## down conversion between `object` or `ref` types. + cgnkObjUpConv ## up conversion between `object` or `ref` types. + + cgnkCast ## a type cast + + cgnkAsgn ## a = b + cgnkBlitCopy + cgnkSwitch ## change the active branch of a discriminated union + + cgnkDef + + # conversions kept for compatibility: + cgnkChckRangeF ## range check for floats + cgnkChckRange64 ## range check for 64 bit ints + cgnkChckRange ## range check for ints + cgnkStringToCString ## string to cstring + cgnkCStringToString ## cstring to string + # TODO: the above operation should never reach the code-generators. + # Instead, they need to be lowered via a MIR pass + + + CgNode* = object + ## Code-generator node. Each node + info*: TLineInfo + typ*: PType + case kind*: CgNodeKind + of cgnkInvalid, cgnkEmpty, cgnkType, cgnkNilLit: discard + of cgnkLiteralData: + data*: NimNode + of cgnkIntLit: + intVal*: BiggestInt + of cgnkNimNodeLit: + nimNodeLit*: PNode + of cgnkMagic: + magic*: TMagic + of cgnkLabel: + lbl: LabelId + else: + childs: seq[CgNode] + +func condition*(n: CgNode): CgNode = + assert n.kind in {cgnkIf, cgnkCase} + n.childs[0] + +func body*(n: CgNode): CgNode = + assert n.kind in {cgnkIf, cgnkBlock, cgnkRepeat} + n.childs[^1] + +func callee*(n: CgNode): CgNode = + assert n.kind == cgnkCall + n.childs[0] + +func label*(n: CgNode): LabelId = + assert n.kind in {cgnkBlock, cgnkBreak} + n.childs[0].lbl + +func arg*(n: CgNode, i: Natural): CgNode = + n.childs[i + 1] \ No newline at end of file diff --git a/compiler/vm/vmdef.nim b/compiler/vm/vmdef.nim index 3b31a34d848..e240a9d802c 100644 --- a/compiler/vm/vmdef.nim +++ b/compiler/vm/vmdef.nim @@ -73,7 +73,8 @@ type TBlock* = object - label*: PSym + label*: uint32 ## a ``LabelId``, but stored as a ``uint32`` in order to not + ## having to import ``mirtrees.nim`` fixups*: seq[TPosition] TEvalMode* = enum ## reason for evaluation diff --git a/compiler/vm/vmgen.nim b/compiler/vm/vmgen.nim index 6dd6003f0c2..2f21e96dba5 100644 --- a/compiler/vm/vmgen.nim +++ b/compiler/vm/vmgen.nim @@ -53,6 +53,7 @@ import compiler/utils/[ idioms ], + compiler/backend/cgir, compiler/vm/[ vmaux, vmdef, @@ -64,6 +65,8 @@ import results ] +import compiler/mir/mirtrees + import std/options as std_options from std/bitops import bitor @@ -188,7 +191,7 @@ func registerConst(c: var TCtx, sym: PSym): int {.inline.} = c.codegenInOut.nextConst) -func gABC*(ctx: var TCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) = +func gABC*(ctx: var TCtx; n: CgNode; opc: TOpcode; a, b, 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 @@ -205,7 +208,7 @@ func gABC*(ctx: var TCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) = ctx.code.add(ins) ctx.debug.add(n.info) -proc gABI(c: var TCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) = +proc gABI(c: var TCtx; n: CgNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) = # 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] @@ -218,7 +221,7 @@ proc gABI(c: var TCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) c.code.add(ins) c.debug.add(n.info) -proc gABx*(c: var TCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) = +proc gABx*(c: var TCtx; n: CgNode; 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 [regBxMin, regBxMax] @@ -237,7 +240,7 @@ proc gABx*(c: var TCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) = c.code.add(ins) c.debug.add(n.info) -proc xjmp(c: var TCtx; n: PNode; opc: TOpcode; a: TRegister = 0): TPosition = +proc xjmp(c: var TCtx; n: CgNode; opc: TOpcode; a: TRegister = 0): TPosition = #assert opc in {opcJmp, opcFJmp, opcTJmp} result = TPosition(c.code.len) gABx(c, n, opc, a, 0) @@ -246,7 +249,7 @@ func genLabel(c: TCtx): TPosition = result = TPosition(c.code.len) #c.jumpTargets.incl(c.code.len) -proc jmpBack(c: var TCtx, n: PNode, p = TPosition(0)) = +proc jmpBack(c: var TCtx, n: CgNode, p = TPosition(0)) = let dist = p.int - c.code.len internalAssert(c.config, regBxMin < dist and dist < regBxMax) gABx(c, n, opcJmpBack, 0, dist) @@ -426,26 +429,21 @@ proc popBlock(c: var TCtx; oldLen: int) = c.patch(f) c.prc.blocks.setLen(oldLen) -template withBlock(labl: PSym; body: untyped) {.dirty.} = +template withBlock(labl: LabelId; body: untyped) {.dirty.} = var oldLen {.gensym.} = c.prc.blocks.len - c.prc.blocks.add TBlock(label: labl, fixups: @[]) + c.prc.blocks.add TBlock(label: uint32(labl), fixups: @[]) body popBlock(c, oldLen) -proc gen(c: var TCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) -proc gen(c: var TCtx; n: PNode; dest: TRegister; flags: TGenFlags = {}) = +proc gen(c: var TCtx; n: CgNode; dest: var TDest; flags: TGenFlags = {}) +proc gen(c: var TCtx; n: CgNode; dest: TRegister; flags: TGenFlags = {}) = var d: TDest = dest gen(c, n, d, flags) #internalAssert c.config, d == dest # issue #7407 -proc gen(c: var TCtx; n: PNode; flags: TGenFlags = {}) = - var tmp: TDest = -1 - gen(c, n, tmp, flags) - if tmp >= 0: - freeTemp(c, tmp) - #if n.typ.isEmptyType: internalAssert tmp < 0 +proc gen(c: var TCtx; n: CgNode) -proc genx(c: var TCtx; n: PNode; flags: TGenFlags = {}): TRegister = +proc genx(c: var TCtx; n: CgNode; flags: TGenFlags = {}): TRegister = var tmp: TDest = -1 gen(c, n, tmp, flags) #internalAssert c.config, tmp >= 0 # 'nim check' does not like this internalAssert. @@ -459,131 +457,83 @@ proc clearDest(c: var TCtx; n: PNode; dest: var TDest) {.inline.} = c.freeTemp(dest) dest = -1 -proc isNotOpr(n: PNode): bool = - n.kind in nkCallKinds and n[0].kind == nkSym and - n[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 +proc isNotOpr(n: CgNode): bool = + n.kind == cgnkCall and n.callee.kind == cgnkMagic and + n.callee.magic == mNot -proc genWhile(c: var TCtx; n: PNode) = +proc genRepeat(c: var TCtx; n: CgNode) = # lab1: - # cond, tmp - # fjmp tmp, lab2 # body # jmp lab1 - # lab2: - let lab1 = c.genLabel - withBlock(nil): - 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[1]) - c.jmpBack(n, lab1) - c.patch(lab2) - else: - var tmp = c.genx(n[0]) - let lab2 = c.xjmp(n, opcFJmp, tmp) - c.freeTemp(tmp) - c.gen(n[1]) - c.jmpBack(n, lab1) - c.patch(lab2) + let lab1 = c.genLabel() + c.gen(n.body) + c.jmpBack(n, lab1) -proc genBlock(c: var TCtx; n: PNode; dest: var TDest) = +proc genScope(c: var TCtx; n: CgNode) = + assert n.kind == cgnkScope let oldRegisterCount = c.prc.regInfo.len - withBlock(n[0].sym): - c.gen(n[1], dest) + + c.gen(n.body) for i in oldRegisterCount..= 0 and c.prc.regInfo[dest].kind >= slotTempUnknown + let + tmp = c.genx(opr) + start = c.xjmp(opr, opc, tmp) # if true + c.freeTemp(tmp) -proc genAndOr(c: var TCtx; n: PNode; opc: TOpcode; dest: var TDest) = - # asgn dest, a - # tjmp|fjmp lab1 - # asgn dest, b - # lab1: - let copyBack = dest.isUnset 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.isUnset: - dest = tmp - elif copyBack: - c.gABC(n, opcAsgnInt, dest, tmp) - freeTemp(c, tmp) + c.gen(n.body) + c.patch(start) +func isTemp(c: TCtx; dest: TDest): bool = + result = dest >= 0 and c.prc.regInfo[dest].kind >= slotTempUnknown # XXX `rawGenLiteral` should be a func, but can't due to `internalAssert` proc rawGenLiteral(c: var TCtx, val: sink VmConstant): int = @@ -591,7 +541,6 @@ proc rawGenLiteral(c: var TCtx, val: sink VmConstant): int = c.constants.add val internalAssert c.config, result < regBxMax, "Too many constants used" - template cmpFloatRep(a, b: BiggestFloat): bool = ## Compares the bit-representation of floats `a` and `b` # Special handling for floats, so that floats that have the same @@ -655,7 +604,7 @@ proc genLiteral(c: var TCtx, n: PNode): int = c.config.internalError(n.info, $n.kind) 0 -template fillSliceList[T](sl: var seq[Slice[T]], nodes: openArray[PNode], +template fillSliceList[T](sl: var seq[Slice[T]], nodes: openArray[CgNode], get: untyped) = sl.newSeq(nodes.len) @@ -672,7 +621,7 @@ template fillSliceList[T](sl: var seq[Slice[T]], nodes: openArray[PNode], let e = getIt(n) e .. e -proc genBranchLit(c: var TCtx, n: PNode, t: PType): int = +proc genBranchLit(c: var TCtx, n: CgNode, t: PType): int = ## Turns the slice-list or single literal of the given `nkOfBranch` into ## a constant and returns it's index in `c.constant`. ## @@ -684,9 +633,9 @@ proc genBranchLit(c: var TCtx, n: PNode, t: PType): int = # already exist assert t.kind in IntegralTypes+{tyString} - if n.len == 2 and n[0].kind in nkLiterals: + if n.len == 2 and n[0].kind in cgnkLiteralData: # It's an 'of' branch with a single value - result = c.genLiteral(n[0]) + result = c.genLiteral(n.data) else: # It's an 'of' branch with multiple and/or range values var cnst: VmConstant @@ -716,11 +665,11 @@ proc genBranchLit(c: var TCtx, n: PNode, t: PType): int = result = c.rawGenLiteral(cnst) -proc unused(c: TCtx; n: PNode; x: TDest) {.inline.} = +proc unused(c: TCtx; n: CgNode; x: TDest) {.inline.} = if x >= 0: fail(n.info, vmGenDiagNotUnused, n) -proc genCase(c: var TCtx; n: PNode; dest: var TDest) = +proc genCase(c: var TCtx; n: CgNode) = # if (!expr1) goto lab1; # thenPart # goto LEnd @@ -731,38 +680,30 @@ proc genCase(c: var TCtx; n: PNode; dest: var TDest) = # lab2: # elsePart # Lend: - if not isEmptyType(n.typ): - if dest.isUnset: dest = getTemp(c, n.typ) - else: - unused(c, n, dest) - let selType = n[0].typ.skipTypes(abstractVarRange) + let selType = n.condition.typ.skipTypes(abstractVarRange) var endings: seq[TPosition] = @[] - withTemp(tmp, n[0].typ): - c.gen(n[0], tmp) + withTemp(tmp, n.condition.typ): + c.gen(n.condition, tmp) # branch tmp, codeIdx # fjmp elseLabel # iterate of/else branches - for i in 1..= fntyp.len: internalAssert(c.config, tfVarargs in fntyp.flags) - c.gABx(n, opcSetType, r, c.genType(n[i].typ)) + c.gABx(n, opcSetType, r, c.genType(n.typ)) + if dest.isUnset: c.gABC(n, opcIndCall, 0, x, n.len) else: @@ -946,17 +882,16 @@ proc needsAsgnPatch(n: PNode): bool = n.kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr, nkDerefExpr, nkHiddenDeref} or (n.kind == nkSym and n.sym.isGlobal) -proc genField(c: TCtx; n: PNode): TRegister = - if n.kind != nkSym or n.sym.kind != skField: - fail(n.info, vmGenDiagNotAFieldSymbol, ast = n) +proc genField(c: TCtx; n: CgNode): TRegister = + assert n.kind == cgnkField let s = n.sym - if s.position > high(typeof(result)): + if s.position > high(TRegister): fail(n.info, vmGenDiagTooLargeOffset, sym = s) result = s.position -proc genIndex(c: var TCtx; n: PNode; arr: PType): TRegister = +proc genIndex(c: var TCtx; n: CgNode; arr: PType): TRegister = if arr.skipTypes(abstractInst).kind == tyArray and (let x = firstOrd(c.config, arr); x != Zero): let tmp = c.genx(n) @@ -967,29 +902,27 @@ proc genIndex(c: var TCtx; n: PNode; arr: PType): TRegister = else: result = c.genx(n) -proc genRegLoad(c: var TCtx, n: PNode, dest, src: TRegister) = +proc genRegLoad(c: var TCtx, n: CgNode, dest, src: TRegister) = c.gABC(n, opcNodeToReg, dest, src) let t = n.typ.skipTypes(abstractInst) if t.isUnsigned() and t.size < sizeof(BiggestInt): c.gABC(n, opcNarrowU, dest, TRegister(t.size * 8)) -proc genCheckedObjAccessAux(c: var TCtx; n: PNode): TRegister -proc genSymAddr(c: var TCtx, n: PNode): TRegister +proc genCheckedObjAccessAux(c: var TCtx; n: CgNode): TRegister +proc genSymAddr(c: var TCtx, n: CgNode): TRegister -proc genAsgnPatch(c: var TCtx; le: PNode, value: TRegister) = +proc genAsgnPatch(c: var TCtx; le: CgNode, value: TRegister) = case le.kind - of nkBracketExpr: + of cgnkArrayAccess: let typ = le[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}) let dest = c.genx(le[0], {gfNode}) - let idx = if typ.kind == tyTuple: le[1].intVal else: c.genIndex(le[1], le[0].typ) + let idx = c.genIndex(le[1], le[0].typ) let opc = if typ.kind in {tyString, tyCstring}: opcWrStrIdx - elif typ.kind == tyTuple: opcWrObj else: opcWrArr c.gABC(le, opc, dest, idx, value) c.freeTemp(dest) - if typ.kind != tyTuple: - c.freeTemp(idx) + c.freeTemp(idx) of nkCheckedFieldExpr: let objR = genCheckedObjAccessAux(c, le) let idx = genField(c, le[0][1]) @@ -1012,7 +945,7 @@ proc genAsgnPatch(c: var TCtx; le: PNode, value: TRegister) = else: discard -proc genNew(c: var TCtx; n: PNode) = +proc genNew(c: var TCtx; n: CgNode) = # FIXME: ``opcNew`` stores the allocated ``ref`` in a newly created # ``ref`` location, so we currently have to assign it to the actual # destination after. This is inefficent; instead, ``opcNew`` @@ -1027,7 +960,7 @@ proc genNew(c: var TCtx; n: PNode) = c.freeTemp(tmp) c.freeTemp(dest) -proc genNewSeq(c: var TCtx; n: PNode) = +proc genNewSeq(c: var TCtx; n: CgNode) = # FIXME: ``opcNewSeq`` has the same problem as ``opcNew``. The instruction # should also reuse the location let t = n[1].typ.skipTypes(abstractVar-{tyTypeDesc}) @@ -1043,7 +976,7 @@ proc genNewSeq(c: var TCtx; n: PNode) = c.freeTemp(len) c.freeTemp(dest) -proc genNewSeqOfCap(c: var TCtx; n: PNode; dest: var TDest) = +proc genNewSeqOfCap(c: var TCtx; n: CgNode; dest: var TDest) = let t = n.typ if dest.isUnset: dest = c.getTemp(n.typ) @@ -1054,20 +987,20 @@ proc genNewSeqOfCap(c: var TCtx; n: PNode; dest: var TDest) = c.gABx(n, opcNewSeq, tmp, 0) c.freeTemp(tmp) -proc genUnaryABC(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = +proc genUnaryABC(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = let tmp = c.genx(n[1]) if dest.isUnset: dest = c.getTemp(n.typ) c.gABC(n, opc, dest, tmp) c.freeTemp(tmp) -proc genUnaryABI(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode; imm: BiggestInt=0) = +proc genUnaryABI(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode; imm: BiggestInt=0) = let tmp = c.genx(n[1]) if dest.isUnset: dest = c.getTemp(n.typ) c.gABI(n, opc, dest, tmp, imm) c.freeTemp(tmp) -proc genBinaryABC(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = +proc genBinaryABC(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = let tmp = c.genx(n[1]) tmp2 = c.genx(n[2]) @@ -1076,7 +1009,7 @@ proc genBinaryABC(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = c.freeTemp(tmp) c.freeTemp(tmp2) -proc genBinaryABCD(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = +proc genBinaryABCD(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = let tmp = c.genx(n[1]) tmp2 = c.genx(n[2]) @@ -1088,7 +1021,7 @@ proc genBinaryABCD(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = c.freeTemp(tmp2) c.freeTemp(tmp3) -proc genNarrow(c: var TCtx; n: PNode; dest: TDest) = +proc genNarrow(c: var TCtx; n: CgNode; 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: @@ -1097,7 +1030,7 @@ proc genNarrow(c: var TCtx; n: PNode; dest: TDest) = elif t.kind in {tyInt8..tyInt32} or (t.kind == tyInt and t.size < 8): c.gABC(n, opcNarrowS, dest, TRegister(t.size*8)) -proc genNarrowU(c: var TCtx; n: PNode; dest: TDest) = +proc genNarrowU(c: var TCtx; n: CgNode; 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: @@ -1105,15 +1038,15 @@ proc genNarrowU(c: var TCtx; n: PNode; dest: TDest) = (t.kind in {tyUInt, tyInt} and t.size < 8): c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) -proc genBinaryABCnarrow(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = +proc genBinaryABCnarrow(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = genBinaryABC(c, n, dest, opc) genNarrow(c, n, dest) -proc genBinaryABCnarrowU(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = +proc genBinaryABCnarrowU(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = genBinaryABC(c, n, dest, opc) genNarrowU(c, n, dest) -proc genBinarySet(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = +proc genBinarySet(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = let tmp = c.genx(n[1]) tmp2 = c.genx(n[2]) @@ -1122,7 +1055,7 @@ proc genBinarySet(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = c.freeTemp(tmp) c.freeTemp(tmp2) -proc genBinaryStmt(c: var TCtx; n: PNode; opc: TOpcode) = +proc genBinaryStmt(c: var TCtx; n: CgNode; opc: TOpcode) = let dest = c.genx(n[1]) tmp = c.genx(n[2]) @@ -1130,7 +1063,7 @@ proc genBinaryStmt(c: var TCtx; n: PNode; opc: TOpcode) = c.freeTemp(tmp) c.freeTemp(dest) -proc genBinaryStmtVar(c: var TCtx; n: PNode; opc: TOpcode) = +proc genBinaryStmtVar(c: var TCtx; n: CgNode; opc: TOpcode) = var x = n[1] if x.kind in {nkAddr, nkHiddenAddr}: x = x[0] let @@ -1770,7 +1703,7 @@ proc genMagic(c: var TCtx; n: PNode; dest: var TDest; m: TMagic) = # mGCref, mGCunref, mFinished, etc. fail(n.info, vmGenDiagCodeGenUnhandledMagic, m) -proc genDeref(c: var TCtx, n: PNode, dest: var TDest, flags: TGenFlags) = +proc genDeref(c: var TCtx, n: CgNode, dest: var TDest, flags: TGenFlags) = let tmp = c.genx(n[0]) if dest.isUnset: dest = c.getTemp(n.typ) gABC(c, n, opcLdDeref, dest, tmp) @@ -1779,7 +1712,7 @@ proc genDeref(c: var TCtx, n: PNode, dest: var TDest, flags: TGenFlags) = c.genRegLoad(n, dest, dest) c.freeTemp(tmp) -proc genAsgn(c: var TCtx; dest: TDest; ri: PNode; requiresCopy: bool) = +proc genAsgn(c: var TCtx; dest: TDest; ri: CgNode; requiresCopy: bool) = let tmp = c.genx(ri) assert dest >= 0 gABC(c, ri, whichAsgnOpc(ri, requiresCopy), dest, tmp) @@ -1790,10 +1723,9 @@ func setSlot(c: var TCtx; v: PSym): TRegister {.discardable.} = result = getFreeRegister(c, if v.kind == skLet: slotFixedLet else: slotFixedVar, start = 1) c.prc.locals[v.id] = result -func cannotEval(c: TCtx; n: PNode) {.noinline, noreturn.} = +func cannotEval(c: TCtx; n: CgNode) {.noinline, noreturn.} = raiseVmGenError(vmGenDiagCannotEvaluateAtComptime, n) - func getOwner(c: TCtx): PSym = result = c.prc.sym if result.isNil: result = c.module @@ -1803,7 +1735,7 @@ proc importcCondVar*(s: PSym): bool {.inline.} = if sfImportc in s.flags: return s.kind in {skVar, skLet, skConst} -proc checkCanEval(c: TCtx; n: PNode) = +proc checkCanEval(c: TCtx; n: CgNode) = # we need to ensure that we don't evaluate 'x' here: # proc foo() = var x ... let s = n.sym @@ -1824,7 +1756,7 @@ proc checkCanEval(c: TCtx; n: PNode) = cannotEval(c, n) -proc genDiscrVal(c: var TCtx, discr, n: PNode, oty: PType): TRegister = +proc genDiscrVal(c: var TCtx, discr: PSym, n: CgNode, oty: PType): TRegister = ## Generate the code for preparing and loading the discriminator value ## as expected by the execution engine @@ -1835,20 +1767,19 @@ proc genDiscrVal(c: var TCtx, discr, n: PNode, oty: PType): TRegister = let (o, idx) = getFieldAndOwner( c.getOrCreate(oty), - fpos(discr.sym.position)) + fpos(discr.position)) o.fieldAt(idx).typ - let recCase = findRecCase(oty, discr.sym) + let recCase = findRecCase(oty, discr) assert recCase != nil - if n.kind in nkCharLit..nkUInt64Lit: + if n.kind == cgnkLiteralData: # Discriminator value is known at compile-time - let b = findMatchingBranch(recCase, n) assert b != -1 # no matching branch; should have been caught already - assert n.intVal <= (1 shl discrTyp.numBits) - 1 - let v = bitor(b shl discrTyp.numBits, int(n.intVal)) + assert n.data.intVal <= (1 shl discrTyp.numBits) - 1 + let v = bitor(b shl discrTyp.numBits, int(n.data.intVal)) result = c.getTemp(n.typ) var tmp = TDest(result) @@ -1897,68 +1828,74 @@ proc genDiscrVal(c: var TCtx, discr, n: PNode, oty: PType): TRegister = c.freeTemp(bIReg) -proc genFieldAsgn(c: var TCtx, obj: TRegister; le, ri: PNode) = - c.config.internalAssert(le.kind == nkDotExpr) +proc genFieldAsgn(c: var TCtx, obj: TRegister; le, ri: CgNode) = + c.config.internalAssert(le.kind == cgnkObjAccess) - let idx = c.genField(le[1]) - let s = le[1].sym + let idx = c.genField(le.field) + let s = le.field.sym - var tmp: TRegister + let tmp = c.genx(ri) + c.gABC(le, opcWrObj, obj, idx, tmp) + c.freeTemp(tmp) - if sfDiscriminant notin s.flags: - tmp = c.genx(ri) - c.gABC(le, opcWrObj, obj, idx, tmp) - else: +proc genBranchSwitch(c: var TCtx, obj: TRegister; le, ri: CgNode)= # Can't use `s.owner.typ` since it may be a `tyGenericBody` - tmp = c.genDiscrVal(le[1], ri, le[0].typ) + let + idx = c.genField(le.field) + s = le.field.sym + + var tmp = c.genx(ri) + + tmp = c.genDiscrVal(le.field, ri, le.path.typ) c.gABC(le, opcSetDisc, obj, idx, tmp) - c.freeTemp(tmp) + c.freeTemp(tmp) -proc genAsgn(c: var TCtx; le, ri: PNode; requiresCopy: bool) = +proc genAsgn(c: var TCtx; le, ri: CgNode) = case le.kind - of nkBracketExpr: - let typ = le[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind - let dest = c.genx(le[0], {gfNode}) - let idx = if typ != tyTuple: c.genIndex(le[1], le[0].typ) else: le[1].intVal + of cgnkArrayAccess: + let typ = le.path.typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind + let dest = c.genx(le.path, {gfNode}) + let idx = c.genIndex(le.indexExpr, le.path.typ) let tmp = c.genx(ri) let opc = if typ in {tyString, tyCstring}: opcWrStrIdx - elif typ == tyTuple: opcWrObj else: opcWrArr c.gABC(le, opc, dest, idx, tmp) c.freeTemp(tmp) - if typ != tyTuple: - c.freeTemp(idx) + c.freeTemp(idx) c.freeTemp(dest) - of nkCheckedFieldExpr: - let objR = genCheckedObjAccessAux(c, le) - c.genFieldAsgn(objR, le[0], ri) - # c.freeTemp(idx) # BUGFIX, see nkDotExpr - c.freeTemp(objR) - of nkDotExpr: + of cgnkTupleAccess: + let dest = c.genx(le[0], {gfNode}) + let tmp = c.genx(ri) + c.gABC(le, opcWrObj, dest, tmp, ri) + c.freeTemp(dest) + c.freeTemp(tmp) + of cgnkObjAccess: let dest = c.genx(le[0], {gfNode}) c.genFieldAsgn(dest, le, ri) # c.freeTemp(idx) # BUGFIX: idx is an immediate (field position), not a register c.freeTemp(dest) - of nkDerefExpr, nkHiddenDeref: + of cgnkDeref, cgnkDerefView: let dest = c.genx(le[0], #[{gfNode}]#) let tmp = c.genx(ri) c.gABC(le, opcWrDeref, dest, 0, tmp) c.freeTemp(dest) c.freeTemp(tmp) - of nkSym: - let s = le.sym - checkCanEval(c, le) - if s.isGlobal: + of cgnkGlobal: + let s = le.sym + checkCanEval(c, le) let tmp = genSymAddr(c, le) val = c.genx(ri) c.gABC(le, opcWrDeref, tmp, 0, val) c.freeTemp(val) c.freeTemp(tmp) - else: + + of cgnkLocal, cgnkParam: + let s = le.sym + checkCanEval(c, le) var dest = c.prc.local(s) c.config.internalAssert dest >= 0 @@ -1973,10 +1910,7 @@ proc genAsgn(c: var TCtx; le, ri: PNode; requiresCopy: bool) = c.gABC(le, opcAsgnComplex, dest, cc) c.freeTemp(cc) else: - let dest = c.genx(le, {gfNode}) - # XXX: always copy, move doesn't work yet - genAsgn(c, dest, ri, true)#requiresCopy) - c.freeTemp(dest) + unreachable(n.kind) proc genTypeLit(c: var TCtx; t: PType; dest: var TDest) = var n = newNode(nkType) @@ -2486,49 +2420,39 @@ proc genClosureConstr(c: var TCtx, n: PNode, dest: var TDest) = c.freeTemp(tmp) -proc gen(c: var TCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = +proc gen(c: var TCtx; n: CgNode; dest: var TDest; flags: TGenFlags = {}) = when defined(nimCompilerStacktraceHints): setFrameMsg c.config$n.info & " " & $n.kind & " " & $flags case n.kind - of nkSym: + of cgnkLocal: let s = n.sym checkCanEval(c, n) - case s.kind - of skVar, skForVar, skTemp, skLet, skParam, skResult: - genSym(c, n, dest, flags) - - of skProc, skFunc, skConverter, skMacro, skMethod, skIterator: - if s.kind == skIterator and s.typ.callConv == TCallingConvention.ccClosure: - fail(n.info, vmGenDiagNoClosureIterators, sym = s) - if importcCond(c, s) and lookup(c.callbackKeys, s) == -1: - fail(n.info, vmGenDiagCannotImportc, sym = s) - - genProcLit(c, n, s, dest) - of skTemplate: - # template symbols can be passed to macro calls - genLit(c, n, c.toNodeCnst(n), dest) - of skConst: - if dest.isUnset: dest = c.getTemp(s.typ) - - if s.ast.kind in nkLiterals: - let lit = genLiteral(c, s.ast) - c.genLit(n, lit, dest) - else: - let idx = c.registerConst(s) - discard c.getOrCreate(s.typ) - c.gABx(n, opcLdCmplxConst, dest, idx) - 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 - # remains for robustness - if dest.isUnset: dest = c.getTemp(n.typ) - if s.position >= low(int16) and s.position <= high(int16): - c.gABx(n, opcLdImmInt, dest, s.position) - else: - var lit = genLiteral(c, newIntNode(nkIntLit, s.position)) - c.gABx(n, opcLdConst, dest, lit) - of skGenericParam: + genSym(c, n, dest, flags) + of cgnkProc: + let s = n.sym + checkCanEval(c, n) + if s.kind == skIterator and s.typ.callConv == TCallingConvention.ccClosure: + fail(n.info, vmGenDiagNoClosureIterators, sym = s) + if importcCond(c, s) and lookup(c.callbackKeys, s) == -1: + fail(n.info, vmGenDiagCannotImportc, sym = s) + + genProcLit(c, n, s, dest) + of cgnkConst: + let s = n.sym + checkCanEval(c, n) + + if dest.isUnset: dest = c.getTemp(s.typ) + + if s.ast.kind in nkLiterals: + let lit = genLiteral(c, s.ast) + c.genLit(n, lit, dest) + else: + let idx = c.registerConst(s) + discard c.getOrCreate(s.typ) + c.gABx(n, opcLdCmplxConst, dest, idx) + of cgnkParam: + if if c.getOwner().kind == skMacro: genSym(c, n, dest, flags) else: @@ -2537,42 +2461,25 @@ proc gen(c: var TCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = # at compile-time, in which case we need to report an error when # encountering an unresolved generic parameter fail(n.info, vmGenDiagCannotGenerateCode, n) - else: - fail(n.info, - vmGenDiagCodeGenUnexpectedSym, - sym = s - ) - of nkCallKinds: - if n[0].kind == nkSym: - let s = n[0].sym - if s.magic != mNone: - genMagic(c, n, dest, s.magic) - elif s.kind == skMethod: - fail(n.info, vmGenDiagCannotCallMethod, sym = s) - else: - genCall(c, n, dest) - clearDest(c, n, dest) - else: - genCall(c, n, dest) - clearDest(c, n, dest) - of nkCharLit..nkInt64Lit: + + of cgnkStmtListExpr: + for i in 0.. Date: Mon, 20 Feb 2023 18:02:02 +0100 Subject: [PATCH 02/20] vm: separate argument processing from `opcExpandToAst` The operation now expects its operands to be `NimNode`s already. A new instruction (`opcDataToAst`) is introduced for creating the AST representation of VM data. While also simplifying the VM a bit, the main reason behind the change is to not having to provide the full AST of the template call expression, as doing so is not possible when the code generator no longer operates on `PNode` AST. --- compiler/vm/vm.nim | 27 +++++++++++++-------------- compiler/vm/vm_enums.nim | 1 + 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/compiler/vm/vm.nim b/compiler/vm/vm.nim index 1797f0b4c76..db12a9fbc2b 100644 --- a/compiler/vm/vm.nim +++ b/compiler/vm/vm.nim @@ -1906,34 +1906,33 @@ proc rawExecute(c: var TCtx, pc: var int, tos: var StackFrameIndex): YieldReason else: raiseVmError(VmEvent(kind: vmEvtNodeNotASymbol)) + of opcDataToAst: + decodeBC(rkNimNode) + regs[ra].nimNode = c.regToNode(regs[rb], regs[rc].nimNode.typ, c.debug[pc]) + regs[ra].nimNode.info = c.debug[pc] of opcExpandToAst: decodeBC(rkNimNode) let - callExprAst = regs[rb].nimNode - prc = callExprAst[0].sym - prevFrame = c.sframes[tos].next + templ = regs[rb+0].nimNode.sym + prevFrame = c.sframes[tos].next - assert callExprAst.kind in nkCallKinds - assert prc.kind == skTemplate + assert templ.kind == skTemplate let genSymOwner = if prevFrame > 0 and c.sframes[prevFrame].prc != nil: c.sframes[prevFrame].prc else: c.module - var templCall = newNodeI(nkCall, c.debug[pc]) - templCall.add(newSymNode(prc)) - for i in 1..rc-1: - let node = c.regToNode(regs[rb+i], callExprAst[i].typ, c.debug[pc]) - node.info = c.debug[pc] - templCall.add(node) - - var a = evalTemplate(templCall, prc, genSymOwner, c.config, c.cache, c.templInstCounter, c.idgen) + var templCall = newNodeI(nkCall, c.debug[pc], rc) + templCall[0] = newSymNode(templ) + for i in 1.. Date: Mon, 20 Feb 2023 18:02:02 +0100 Subject: [PATCH 03/20] progress; the first bootstrap iteration works again --- compiler/backend/astgen2.nim | 1141 ++++++++++++++++++++++++++++++++++ compiler/backend/cgir.nim | 116 +++- compiler/mir/mirbridge.nim | 49 +- compiler/vm/vmbackend.nim | 16 +- compiler/vm/vmgen.nim | 1104 ++++++++++++++++---------------- compiler/vm/vmjit.nim | 8 +- 6 files changed, 1837 insertions(+), 597 deletions(-) create mode 100644 compiler/backend/astgen2.nim diff --git a/compiler/backend/astgen2.nim b/compiler/backend/astgen2.nim new file mode 100644 index 00000000000..5d8b452cf6b --- /dev/null +++ b/compiler/backend/astgen2.nim @@ -0,0 +1,1141 @@ +## This module implements the MIR -> ``CgNode`` AST translation. The translation +## preserves the semantics and uses the smallest ``CgNode`` constructs that best +## match the respective MIR construct. Note that not all constructs in the MIR +## have a direct ``CgNode`` counterpart: those require more complex translation +## logic. +## +## The translation is implemented via recursive application of the correct +## translation procedure, where each procedure processes a sub-tree either +## directly or via further recursion. Instead of one uber procedure, multiple +## smaller ones that closely match the grammer are used. This allows for +## validating that the input MIR code is grammatically correct with effectively +## no overhead and without requiring extra contextual data or a separate pass. +## +## ============ +## MIR sections +## ============ +## +## There exists no equivalent to MIR sections in the ``CgNode`` AST, so a more +## complex translation has to be used. At the start of the section, each +## section argument is assigning to a temporary, using either a ``var`` / +## ``lent`` view or shallow copy depending on the argument's mode and +## type. A section parameter reference (``mnkOpParam``) is then translated to +## accessing the temporary introduce for the parameter's argument. + +import + compiler/ast/[ + ast, + ast_types, + ast_idgen, + ast_query, + idents, + lineinfos, + types + ], + compiler/backend/[ + cgir + ], + compiler/front/[ + options + ], + compiler/mir/[ + mirtrees, + sourcemaps + ], + compiler/modules/[ + modulegraphs + ], + compiler/utils/[ + containers, + idioms + ] + +from compiler/sem/semdata import makeVarType +from compiler/sem/typeallowed import directViewType, noView + +# TODO: move the procedure somewhere common +from compiler/vm/vmaux import findRecCase + +type + OriginInfo = PNode + + ValuesKind = enum + vkNone, vkSingle, vkMulti + + ArgumentMode = enum + amValue + amName + amConsume + + ValueTag = enum + ## ``ValueTag``s are used to propgate some information forward to the + ## value's consumer (e.g. a procedure call) + vtMutable ## the value is a mutable lvalue meant to be passed to a ``var`` + ## parameter + vtVariant + + ValueTags = set[ValueTag] + + Values = object + ## Represents the inputs to an operation. A container of zero-or-more + ## values, where each value is represented by a ``CgNode`` expression + case kind: ValuesKind + of vkNone: + discard + of vkSingle: + single: CgNode + tag: ValueTags + of vkMulti: + list: seq[CgNode] + modeAndTags: seq[tuple[mode: ArgumentMode, tags: ValueTags]] + ## a separate sequence is used so that the whole ``CgNode`` list can + ## be moved into the destination node at once + + TranslateCl = object + graph: ModuleGraph + cache: IdentCache + idgen: IdGenerator + + owner: PSym + + params: Values + nextTemp: uint32 + + TreeWithSource = object + ## Combines a ``MirTree`` with its associated ``SourceMap`` for + ## convenience. It's only meant to be used as parameter type + # TODO: the fields don't need ownership and should thus be turned into + # views as soon as possible + tree: MirTree + map: SourceMap + + TreeCursor = object + ## A cursor into a ``TreeWithSource`` + pos: uint32 ## the index of the currently pointed to node + origin {.cursor.}: PNode ## the node + +template isFilled(x: ref): bool = not x.isNil + +template `^^`(s, i: untyped): untyped = + # XXX: copied from ``system.nim`` because it's not exported + (when i is BackwardsIndex: s.len - int(i) else: int(i)) + +func toValues(x: sink CgNode): Values {.inline.} = + # note: having ``toValues`` be an implicit converter lead to an overload + # resolution issue where the converter was incorrectly chosen, making + # otherwise correct code not compile + assert x != nil + Values(kind: vkSingle, single: x) + +func `[]`(v: Values, i: Natural): CgNode = + if i > 0 or v.kind == vkMulti: + v.list[i] + else: + v.single + +func len(v: Values): int = + case v.kind + of vkNone: 0 + of vkSingle: 1 + of vkMulti: v.list.len + +func add(v: var Values, n: sink CgNode, tag: ValueTags, mode: ArgumentMode) = + v.list.add n + v.modeAndTags.add (mode, tag) + +proc `[]=`(n: CgNode, i: Natural, child: sink CgNode) = + n.childs[i] = child + +proc `[]`(n: CgNode, i: Natural): CgNode = + n.childs[i] + +func add(n: CgNode, child: sink CgNode) = + n.childs.add child + +func newNode(kind: CgNodeKind, origin: OriginInfo = nil): CgNode = + CgNode(kind: kind, origin: origin) + +func newTree(kind: CgNodeKind, origin: OriginInfo, childs: varargs[CgNode]): CgNode = + {.cast(uncheckedAssign).}: + CgNode(kind: kind, origin: origin, childs: @childs) + +func newExpr(kind: CgNodeKind, origin: OriginInfo; typ: PType, childs: varargs[CgNode]): CgNode = + {.cast(uncheckedAssign).}: + CgNode(kind: kind, origin: origin, typ: typ, childs: @childs) + +func newStmt(kind: CgNodeKind, origin: OriginInfo; childs: varargs[CgNode]): CgNode = + {.cast(uncheckedAssign).}: + CgNode(kind: kind, origin: origin, childs: @childs) + +func get(t: TreeWithSource, cr: var TreeCursor): lent MirNode {.inline.} = + cr.origin = sourceFor(t.map, cr.pos.NodeInstance) + result = t.tree[cr.pos] + + inc cr.pos + +func enter(t: TreeWithSource, cr: var TreeCursor): lent MirNode {.inline.} = + assert t.tree[cr.pos].kind in SubTreeNodes, "not a sub-tree" + result = get(t, cr) + +func leave(t: TreeWithSource, cr: var TreeCursor) = + assert t.tree[cr.pos].kind == mnkEnd, "not at the end of sub-tree" + inc cr.pos + +template info(cr: TreeCursor): OriginInfo = + cr.origin + +template `[]`(t: TreeWithSource, cr: TreeCursor): untyped = + t.tree[cr.pos] + +template hasNext(cr: TreeCursor, t: TreeWithSource): bool = + cr.pos.int < t.tree.len + +func toMode(kind: range[mnkArg..mnkConsume]): ArgumentMode = + case kind + of mnkArg: amValue + of mnkName: amName + of mnkConsume: amConsume + +proc copySubTree[A, B](source: PNode, slice: HSlice[A, B], to: PNode) = + ## Copies all sub-nodes from the `slice` in `source` to the end of `to`, + ## using a full sub-tree copy (i.e. ``copyTree``) + # XXX: using an ``openArray`` instead of a ``PNode`` + ``HSlice`` pair + # would simplify this procedure a lot. As of the time of this comment + # being written, creating an openArray from a node is rather cumbersome + # however + let + a = source ^^ slice.a + b = source ^^ slice.b + + if a > b: + return + + # resize the node list first: + let start = to.len + to.sons.setLen(start + (b - a) + 1) + + # then copy all nodes: + for i in a..b: + to[start + (i - a)] = source[i] + +func copy(source: CgNode): CgNode = + result = CgNode() + result[] = source[] + +func copyTree(source: CgNode): CgNode = + result = CgNode() + result[] = source[] + # TODO: fixme + +func newLiteral(n: PNode): CgNode = + CgNode(kind: cgnkLiteralData, typ: n.typ, data: n) + +func newField(sym: PSym): CgNode = + CgNode(kind: cgnkField, sym: sym) + +func addIfNotEmpty(stmts: var seq[CgNode], n: sink CgNode) = + ## Only adds the node to the list if it's not an empty node. Used to prevent + ## the creation of statement-list expressions that only consist of empty + ## nodes + the result-expression (a statement-list expression is unnecessary + ## in that case) + if n.kind != cgnkEmpty: + stmts.add n + +func toSingleNode(stmts: sink seq[CgNode]): CgNode = + ## Creates a single ``CgNode`` from a list of *statements* + case stmts.len + of 0: + result = newNode(cgnkEmpty) + of 1: + result = move stmts[0] + else: + result = newNode(cgnkStmtList) + result.childs = move stmts + +proc wrapArg(stmts: seq[CgNode], info: OriginInfo, val: CgNode): CgNode = + ## If there are extra statements (i.e. `stmts` is not empty), wraps the + ## statements + result-expression into an ``nkStmtListExpr``. Otherwise, + ## returns `val` as is + if stmts.len == 0: + result = val + else: + assert val.kind != cgnkStmtListExpr + result = newExpr(cgnkStmtListExpr, info, val.typ, stmts) + result.add val + +func unwrap(expr: CgNode, stmts: var seq[CgNode]): CgNode = + if expr.kind == cgnkStmtListExpr: + for it in expr.stmts: + stmts.add it + + result = expr.source + else: + result = expr + +proc newTemp(cl: var TranslateCl, info: OriginInfo, typ: PType): CgNode = + ## Creates and returns a new ``skTemp`` symbol + result = CgNode(kind: cgnkTemp, origin: info, typ: typ, temp: TempId cl.nextTemp) + inc cl.nextTemp + +func findBranch(c: ConfigRef, rec: PNode, field: PIdent): int = + ## Computes the 0-based position of the branch that `field` is part of. Only + ## the direct child nodes of `rec` are searched, nested record-cases are + ## ignored + assert rec.kind == nkRecCase + template cmpSym(s: PSym): bool = + s.name.id == field.id + + for i, b in branches(rec): + assert b.kind in {nkOfBranch, nkElse} + case b.lastSon.kind + of nkSym: + if cmpSym(b.lastSon.sym): + return i + + of nkRecList: + for it in b.lastSon.items: + let sym = + case it.kind + of nkSym: it.sym + of nkRecCase: it[0].sym + else: nil + + if sym != nil and cmpSym(sym): + return i + + of nkRecCase: + if cmpSym(b[0].sym): + return i + + else: + unreachable() + + unreachable("field is not part of the record-case") + +proc genBranchLabels(n: PNode, dest: CgNode) = + template lit(x: PNode): CgNode = + CgNode(kind: cgnkLiteralData, typ: x.typ, data: x) + + for i, it in branchLabels(n): + if it.kind == nkRange: + dest.add newTree(cgnkRange, it, [lit(it[0]), lit(it[1])]) + else: + dest.add CgNode(kind: cgnkLiteralData, typ: it.typ, data: it) + +proc buildCheck(cl: var TranslateCl, recCase: PNode, pos: Natural, + info: OriginInfo): CgNode = + ## Builds the boolean expression testing if `discr` is part of the branch + ## with position `pos` + assert recCase.kind == nkRecCase + let + discr = recCase[0] ## the node holding the discriminant symbol + branch = recCase[1 + pos] + setType = newType(tySet, nextTypeId(cl.idgen), cl.owner) + + rawAddSon(setType, discr.typ) # the set's element type + + var + lit = newExpr(cgnkSetConstr, info, setType) + invert = false + + case branch.kind + of nkOfBranch: + # use the branch labels as the set to test against + genBranchLabels(branch, lit) + of nkElse: + # iterate over all branches except the ``else`` branch and collect their + # labels + for i in 1.. firstOrd(c, n.typ) or + lastOrd(c, n.typ) > lastOrd(c, orig)): # is dest not a sub-range of source? + # generate a range check: + let + rangeDest = skipTypes(orig, abstractVar) + kind = + if tyInt64 in {dest.kind, source.kind}: cgnkChckRange64 + else: cgnkChckRange + + result = newExpr(kind, info, orig): + [n, + newLiteral newIntTypeNode(firstOrd(c, rangeDest), rangeDest), + newLiteral newIntTypeNode(lastOrd(c, rangeDest), rangeDest)] + of tyFloat..tyFloat128: + let rangeDest = skipTypes(orig, abstractVar) + if rangeDest.kind == tyRange: + # a conversion to a float range (e.g. ``range[0.0 .. 1.0]``) + result = newExpr(cgnkChckRangeF, info, orig): + [n, newLiteral(rangeDest.n[0]), newLiteral(rangeDest.n[1])] + + else: + result = nil + +proc tbConv(cl: TranslateCl, n: CgNode, info: OriginInfo, dest: PType): CgNode = + ## Generates the AST for an expression that performs a type conversion for + ## `n` to type `dest` + result = handleSpecialConv(cl.graph.config, n, info, dest) + if result == nil: + # no special conversion is used + result = newExpr(cgnkConv, info, dest): n + +proc tbSingle(n: MirNode, cl: TranslateCl, info: OriginInfo): CgNode = + case n.kind + of mnkProc, mnkConst, mnkParam, mnkGlobal, mnkLocal: + CgNode(kind: cgnkProc, origin: info, typ: n.sym.typ, sym: n.sym) + of mnkTemp: + CgNode(kind: cgnkTemp, origin: info, temp: n.temp) + of mnkLiteral: + CgNode(kind: cgnkLiteralData, origin: info, typ: n.lit.typ, data: n.lit) + of mnkType: + CgNode(kind: cgnkType, origin: info, typ: n.typ) + else: + unreachable("not an atom: " & $n.kind) + +proc tbExceptItem(tree: TreeWithSource, cl: TranslateCl, cr: var TreeCursor + ): CgNode = + let n {.cursor.} = get(tree, cr) + case n.kind + of mnkPNode: unreachable("imported exception; not implemented yet") + of mnkType: newExpr(cgnkType, cr.info, n.typ) + else: unreachable() + + +proc tbDef(tree: TreeWithSource, cl: var TranslateCl, prev: sink Values, + n: MirNode, cr: var TreeCursor): CgNode = + ## Translates a 'def'-like construct + assert n.kind in DefNodes + let + entity {.cursor.} = get(tree, cr) # the name of the defined entity + info = cr.info + + var ent: CgNode + + case entity.kind + of mnkLocal, mnkGlobal: + ent = tbSingle(entity, cl, info) + of mnkParam: + # not relevant to the code-generators; ignore + discard + of mnkProc: + # skip the definition + discard get(tree, cr) + of mnkTemp: + ent = CgNode(kind: cgnkTemp, origin: info, typ: n.typ, temp: entity.temp) + else: + unreachable() + + leave(tree, cr) + + if ent != nil: + let value = + case prev.kind + of vkNone: newNode(cgnkEmpty, cr.info) + of vkSingle: prev.single + of vkMulti: unreachable() + result = newStmt(cgnkDef, info, ent, value) + else: + # return an 'empty' node; it gets eliminated later + result = newNode(cgnkEmpty, info) + +proc translate(n: PNode): CgNode = + case n.kind + of nkConstSection: + # placed in MIR code for compatibility with the IC back-end + # TODO: remove this branch once all code-generators use ``CgNode`` + result = CgNode(kind: cgnkEmpty) + of nkPragma: + unreachable("missing") + of nkAsmStmt: + # TODO: we need support from ``mirgen`` here + # for it in t.sons: + # case it.kind + # of nkStrLit..nkTripleStrLit: + # of nkSym: + # if sym.kind in {skProc, skFunc, skIterator, skMethod}: + # elif sym.kind == skType: + # else: + # of nkTypeOfExpr: + # else: + #result = CgNode(kind: cgnkAsm, ) + discard + else: + unreachable(n.kind) + +proc tbSingleStmt(tree: TreeWithSource, cl: var TranslateCl, n: MirNode, + cr: var TreeCursor): CgNode = + template body(): CgNode = + tbStmt(tree, cl, cr) + + let info = cr.info ## the source information of `n` + + case n.kind + of DefNodes: + # a definition of an entity with no initial value + result = tbDef(tree, cl, Values(kind: vkNone), n, cr) + of mnkScope: + result = tbScope(tree, cl, n, cr) + leave(tree, cr) + of mnkRepeat: + result = newStmt(cgnkRepeat, info, body()) + leave(tree, cr) + of mnkBlock: + result = newStmt(cgnkBlock, info, + CgNode(kind: cgnkLabel, lbl: n.label), # the label + body()) + leave(tree, cr) + of mnkTry: + result = newStmt(cgnkTryStmt, info, [body()]) + assert n.len <= 2 + + for _ in 0.. 1 and n.childs[1].kind == cgnkExcept: + result = n.childs[1] + +func finalizer*(n: CgNode): CgNode = + assert n.kind == cgnkTryStmt + if n.childs[^1].kind == cgnkFinally: + result = n.childs[^1] + +func value*(n: CgNode): CgNode = + assert n.kind == cgnkRaise + n.childs[0] + + +func lhs*(n: CgNode): CgNode = + assert n.kind == cgnkRaise + n.childs[0] + +func rhs*(n: CgNode): CgNode = + assert n.kind == cgnkRaise + n.childs[0] + +func index*(n: CgNode): CgNode = + assert n.kind == cgnkArrayAccess + n.childs[1] + +func entity*(n: CgNode): CgNode = + assert n.kind == cgnkDef + n.childs[0] + func callee*(n: CgNode): CgNode = assert n.kind == cgnkCall n.childs[0] @@ -148,4 +187,69 @@ func label*(n: CgNode): LabelId = n.childs[0].lbl func arg*(n: CgNode, i: Natural): CgNode = - n.childs[i + 1] \ No newline at end of file + n.childs[i + 1] + +func numArgs*(n: CgNode): int = + assert n.kind == cgnkCall + n.childs.len - 1 + +func labelAt*(n: CgNode, i: Natural): CgNode = + n.childs[i] + +iterator arguments*(n: CgNode): (int, CgNode) = + for i in 1.. 0: generateAST(graph, idgen, owner, tree, sourceMap) - else: newNode(nkEmpty) \ No newline at end of file + else: newNode(nkEmpty) + +proc canonicalizeSingle2*(graph: ModuleGraph, idgen: IdGenerator, owner: PSym, + n: PNode, options: set[GenOption]): CgNode = + ## Similar to ``canonicalize``, but accepts a freestanding expression or + ## statement. The `owner` is used as the owner when generating the necessary + ## new symbols or types + var + tree: MirTree + sourceMap: SourceMap + + # step 1: generate a ``MirTree`` from the input AST + generateCode(graph, options, n, tree, sourceMap) + # step 2: translate it back, but only if there is something to translate + result = + if tree.len > 0: astgen2.generateAST(graph, idgen, owner, tree, sourceMap) + else: CgNode(kind: cgnkEmpty) \ No newline at end of file diff --git a/compiler/vm/vmbackend.nim b/compiler/vm/vmbackend.nim index 3bfc9a9a85c..76321a640ae 100644 --- a/compiler/vm/vmbackend.nim +++ b/compiler/vm/vmbackend.nim @@ -149,7 +149,7 @@ func collectRoutineSyms(ast: PNode, syms: var seq[PSym]) = proc genStmt(c: var TCtx, n: PNode): auto = ## Wrapper around ``vmgen.genStmt`` that canonicalizes the input AST first - let n = canonicalizeSingle(c.graph, c.idgen, c.module, n, {goIsNimvm}) + let n = canonicalizeSingle2(c.graph, c.idgen, c.module, n, {goIsNimvm}) vmgen.genStmt(c, n) proc generateTopLevelStmts*(module: var Module, c: var TCtx, @@ -157,8 +157,6 @@ proc generateTopLevelStmts*(module: var Module, c: var TCtx, ## Generates code for all collected top-level statements of `module` and ## compiles the fragments into a single function. The resulting code is ## stored in `module.initProc` - let n = newNodeI(nkEmpty, module.sym.info) # for line information - c.prc = PProc(sym: module.sym) c.prc.regInfo.newSeq(1) # the first register is always the (potentially # non-existant) result @@ -173,7 +171,7 @@ proc generateTopLevelStmts*(module: var Module, c: var TCtx, if unlikely(r.isErr): config.localReport(vmGenDiagToLegacyReport(r.takeErr)) - c.gABC(n, opcRet) + c.gABC(module.sym.info, opcRet) module.initProc = (start: start, regCount: c.prc.regInfo.len) @@ -184,8 +182,8 @@ proc generateCodeForProc(c: var TCtx, s: PSym, ## to `globals`. var body = transformBody(c.graph, c.idgen, s, cache = false) extractGlobals(body, globals, isNimVm = true) - body = canonicalize(c.graph, c.idgen, s, body, {goIsNimvm}) - result = genProc(c, s, body) + let n = canonicalize2(c.graph, c.idgen, s, body, {goIsNimvm}) + result = genProc(c, s, n) proc generateGlobalInit(c: var TCtx, f: var CodeFragment, defs: openArray[PNode]) = ## Generates and emits code for the given `{.global.}` initialization @@ -284,7 +282,7 @@ proc generateEntryProc(c: var TCtx, info: TLineInfo, initProcs: Slice[int], ## to high by their function table index) and then returns the value of the ## ``programResult`` global let - n = newNodeI(nkEmpty, info) + n = info start = c.code.len # setup code-gen state. One register for the return value and one as a @@ -448,7 +446,7 @@ proc generateCode*(g: ModuleGraph) = rc = frag.prc.regInfo.len c.appendCode(frag) - c.gABC(g.emptyNode, opcRet) + c.gABC(m.sym.info, opcRet) # The code fragment isn't used anymore beyond this point, so it can be # freed already @@ -461,7 +459,7 @@ proc generateCode*(g: ModuleGraph) = let entryPoint = generateMain(c, g.getModule(conf.projectMainIdx), mlist[]) - c.gABC(g.emptyNode, opcEof) + c.gABC(unknownLineInfo, opcEof) # code generation is finished diff --git a/compiler/vm/vmgen.nim b/compiler/vm/vmgen.nim index 2f21e96dba5..ec322cf29a2 100644 --- a/compiler/vm/vmgen.nim +++ b/compiler/vm/vmgen.nim @@ -36,6 +36,8 @@ import renderer, types, ast, + ast_types, + ast_query, lineinfos, astmsgs, ], @@ -47,11 +49,9 @@ import msgs, options ], - compiler/sem/[ - lowerings - ], compiler/utils/[ - idioms + idioms, + int128 ], compiler/backend/cgir, compiler/vm/[ @@ -160,7 +160,7 @@ type TGenFlags = set[TGenFlag] # forward declarations -proc genLit(c: var TCtx; n: PNode; lit: int; dest: var TDest) +proc genLit(c: var TCtx; n: CgNode; lit: int; dest: var TDest) template isUnset(x: TDest): bool = x < 0 @@ -191,10 +191,7 @@ func registerConst(c: var TCtx, sym: PSym): int {.inline.} = c.codegenInOut.nextConst) -func gABC*(ctx: var TCtx; n: CgNode; opc: TOpcode; a, b, 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 +func gABC*(ctx: var TCtx; info: TLineInfo; opc: TOpcode; a, b, c: TRegister = 0) = assert opc.ord < 255 let ins = (opc.TInstrType or (a.TInstrType shl regAShift) or (b.TInstrType shl regBShift) or @@ -206,7 +203,13 @@ func gABC*(ctx: var TCtx; n: CgNode; opc: TOpcode; a, b, c: TRegister = 0) = echo "generating ", opc ]# ctx.code.add(ins) - ctx.debug.add(n.info) + ctx.debug.add(info) + +template gABC*(ctx: var TCtx; n: CgNode; opc: TOpcode; a, b, 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 + gABC(ctx, info(n), opc, a, b, c) proc gABI(c: var TCtx; n: CgNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) = # Takes the `b` register and the immediate `imm`, applies the operation `opc`, @@ -221,7 +224,13 @@ proc gABI(c: var TCtx; n: CgNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt c.code.add(ins) c.debug.add(n.info) -proc gABx*(c: var TCtx; n: CgNode; opc: TOpcode; a: TRegister = 0; bx: int) = +template getInfo(n: untyped): TLineInfo = + when n is CgNode: + n.info + else: + n + +proc gABx*(c: var TCtx; n: (CgNode | TLineInfo); 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 [regBxMin, regBxMax] @@ -232,15 +241,15 @@ proc gABx*(c: var TCtx; n: CgNode; opc: TOpcode; a: TRegister = 0; bx: int) = echo "generating ", opc ]# - c.config.internalAssert(bx in regBxMin-1..regBxMax, n.info, + c.config.internalAssert(bx in regBxMin-1..regBxMax, n.getInfo, "VM: immediate value does not fit into regBx") 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) + c.debug.add(n.getInfo) -proc xjmp(c: var TCtx; n: CgNode; opc: TOpcode; a: TRegister = 0): TPosition = +proc xjmp(c: var TCtx; n: TLineInfo; opc: TOpcode; a: TRegister = 0): TPosition = #assert opc in {opcJmp, opcFJmp, opcTJmp} result = TPosition(c.code.len) gABx(c, n, opc, a, 0) @@ -492,11 +501,11 @@ proc genBlock(c: var TCtx; n: CgNode) = c.gen(n.body) proc genBreak(c: var TCtx; n: CgNode) = - let lab1 = c.xjmp(n, opcJmp) + let lab1 = c.xjmp(n.info, opcJmp) if n.label != NoLabel: #echo cast[int](n[0].sym) for i in countdown(c.prc.blocks.len-1, 0): - if c.prc.blocks[i].label == n.label.LabelId: + if c.prc.blocks[i].label == n.label.uint32: c.prc.blocks[i].fixups.add lab1 return @@ -518,6 +527,7 @@ proc genIf(c: var TCtx, n: CgNode) = # if (!cond) goto lab1; # body # lab1: + # optimize the emitted bytecode a bit by fusing the `not` predicate (if one # exists) with the conditional jump instruction let (opr, opc) = @@ -526,7 +536,7 @@ proc genIf(c: var TCtx, n: CgNode) = let tmp = c.genx(opr) - start = c.xjmp(opr, opc, tmp) # if true + start = c.xjmp(opr.info, opc, tmp) # if true c.freeTemp(tmp) c.gen(n.body) @@ -604,19 +614,28 @@ proc genLiteral(c: var TCtx, n: PNode): int = c.config.internalError(n.info, $n.kind) 0 -template fillSliceList[T](sl: var seq[Slice[T]], nodes: openArray[CgNode], +template enumerate(x: untyped, iterName: untyped): untyped = + iterator inner(iterable: typeof(x)): (int, auto) {.inline.} = + var i = 0 + for it in iterable.items: + yield (i, it) + inc i + + inner(x) + +template fillSliceList[T](sl: var seq[Slice[T]], node: CgNode, get: untyped) = - sl.newSeq(nodes.len) + sl.newSeq(node.numLabels) template getIt(n): untyped = block: let it {.cursor, inject.} = n get - for (i, n) in nodes.pairs: + for i, n in node.labels: sl[i] = - if n.kind == nkRange: - getIt(n[0]) .. getIt(n[1]) + if n.kind == cgnkRange: + getIt(n.a) .. getIt(n.b) else: let e = getIt(n) e .. e @@ -633,41 +652,59 @@ proc genBranchLit(c: var TCtx, n: CgNode, t: PType): int = # already exist assert t.kind in IntegralTypes+{tyString} - if n.len == 2 and n[0].kind in cgnkLiteralData: + if n.numLabels == 1 and n.labelAt(0).kind == cgnkLiteralData: # It's an 'of' branch with a single value result = c.genLiteral(n.data) else: # It's an 'of' branch with multiple and/or range values var cnst: VmConstant - template values: untyped = - n.sons.toOpenArray(0, n.sons.high - 1) # -1 for the branch body - case t.kind of IntegralTypes-{tyFloat..tyFloat128}: cnst = VmConstant(kind: cnstSliceListInt) - cnst.intSlices.fillSliceList(values): - it.intVal + cnst.intSlices.fillSliceList(n): + it.data.intVal of tyFloat..tyFloat128: cnst = VmConstant(kind: cnstSliceListFloat) - cnst.floatSlices.fillSliceList(values): - it.floatVal + cnst.floatSlices.fillSliceList(n): + it.data.floatVal of tyString: cnst = VmConstant(kind: cnstSliceListStr) - cnst.strSlices.fillSliceList(values): - c.toStringCnst(it.strVal) + cnst.strSlices.fillSliceList(n): + c.toStringCnst(it.data.strVal) else: unreachable(t.kind) result = c.rawGenLiteral(cnst) +proc genBranchLit(c: var TCtx, n: PNode): int = + ## Turns the slice-list or single literal of the given `nkOfBranch` into + ## a constant and returns it's index in `c.constant`. + ## + ## slice-lists are always added as a new constant while single literals + ## are reused + if n.len == 1 and n[0].kind in nkIntLiterals: + # It's an 'of' branch with a single value + result = c.toIntCnst(n[0].intVal) + else: + # It's an 'of' branch with multiple and/or range values + var cnst: VmConstant + for i, it in branchLabels(n): + cnst.intSlices[i] = + if n.kind == nkRange: + it[0].intVal .. it[1].intVal + else: + let e = n.intVal + e .. e + + result = c.rawGenLiteral(cnst) proc unused(c: TCtx; n: CgNode; x: TDest) {.inline.} = if x >= 0: - fail(n.info, vmGenDiagNotUnused, n) + fail(n.info, vmGenDiagNotUnused, n.origin) proc genCase(c: var TCtx; n: CgNode) = # if (!expr1) goto lab1; @@ -687,21 +724,20 @@ proc genCase(c: var TCtx; n: CgNode) = # branch tmp, codeIdx # fjmp elseLabel - # iterate of/else branches for i, branch in branches(n): - if branch.len == 1: + if branch.numLabels == 0: # the catch-all branch c.gen(branch.body) # must be the last branch, so no jump is required else: - let b = genBranchLit(c, branch) + let b = genBranchLit(c, branch, selType) c.gABx(branch, opcBranch, tmp, b) - let elsePos = c.xjmp(branch, opcFJmp, tmp) + let elsePos = c.xjmp(branch.info, opcFJmp, tmp) c.gen(branch.body) # generate a jump to the end of the block - if i < numBranches(n): - endings.add(c.xjmp(branch.body, opcJmp, 0)) + if i < n.numBranches: + endings.add(c.xjmp(branch.body.info, opcJmp, 0)) c.patch(elsePos) for endPos in endings: c.patch(endPos) @@ -729,43 +765,44 @@ proc genTypeInfo(c: var TCtx, typ: PType): int = internalAssert(c.config, result <= regBxMax, "") -proc genTry(c: var TCtx; n: PNode) = +proc genTry(c: var TCtx; n: CgNode) = var endings: seq[TPosition] = @[] - let ehPos = c.xjmp(n, opcTry, 0) + let ehPos = c.xjmp(n.info, opcTry, 0) c.gen(n.body) # Add a jump past the exception handling code - let jumpToFinally = c.xjmp(n, opcJmp, 0) + let jumpToFinally = c.xjmp(n.info, opcJmp, 0) # This signals where the body ends and where the exception handling begins c.patch(ehPos) let eh = n.handler if eh != nil: # first opcExcept contains the end label of the 'except' block: - let endExcept = c.xjmp(eh, opcExcept, 0) - for branch in branches(eh): - for it in labels(branch): - assert(it.kind == cgnkType) + let endExcept = c.xjmp(eh.info, opcExcept, 0) + for i, branch in branches(eh): + for j, it in labels(branch): + assert it.kind == cgnkType let typ = it.typ.skipTypes(abstractPtrs-{tyTypeDesc}) c.gABx(it, opcExcept, 0, c.genType(typ)) - if branch.len == 1: + if branch.numLabels == 0: # general except section: c.gABx(branch, opcExcept, 0, 0) c.gen(branch.body) - endings.add(c.xjmp(branch, opcJmp, 0)) + endings.add(c.xjmp(branch.info, opcJmp, 0)) c.patch(endExcept) - let fin = lastSon(n) + let fin = n.finalizer # we always generate an 'opcFinally' as that pops the safepoint # 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) - let fin = n.finalizer + for endPos in endings: + c.patch(endPos) + if fin != nil: c.gen(fin.body) c.gABx(fin, opcFinallyEnd, 0, 0) proc genRaise(c: var TCtx; n: CgNode) = - if n.value.kind != nkEmpty: + if n.value.kind != cgnkEmpty: let dest = c.genx(n.value) typ = skipTypes(n.value.typ, abstractPtrs) @@ -786,7 +823,7 @@ proc genRaise(c: var TCtx; n: CgNode) = proc genReturn(c: var TCtx; n: CgNode) = c.gABC(n, opcRet) -proc genLit(c: var TCtx; n: PNode; lit: int; dest: var TDest) = +proc genLit(c: var TCtx; n: CgNode; lit: int; dest: var TDest) = ## `lit` is the index of a constant as returned by `genLiteral` # opcLdConst is now always valid. We produce the necessary copy in the @@ -796,8 +833,8 @@ proc genLit(c: var TCtx; n: PNode; lit: int; dest: var TDest) = #elif c.prc.regInfo[dest].kind == slotFixedVar: opc = opcAsgnConst c.gABx(n, opcLdConst, dest, lit) -proc genLit(c: var TCtx; n: PNode; dest: var TDest) = - let lit = genLiteral(c, n) +proc genLit(c: var TCtx; n: CgNode; dest: var TDest) = + let lit = genLiteral(c, n.data) genLit(c, n, lit, dest) @@ -825,13 +862,13 @@ proc genCall(c: var TCtx; n: CgNode; dest: var TDest) = let fntyp = skipTypes(n.callee.typ, abstractInst) - let x = c.getTempRange(n.len, slotTempUnknown) + let x = c.getTempRange(n.numArgs + 1, slotTempUnknown) # varargs need 'opcSetType' for the FFI support: for i, it in arguments(n): # skip empty arguments (i.e. arguments to compile-time parameters that # were omitted): - if it.kind == nkEmpty: + if it.kind == cgnkEmpty: continue var r = TDest(x+i) @@ -848,17 +885,17 @@ proc genCall(c: var TCtx; n: CgNode; dest: var TDest) = c.gABC(it, opcAsgnComplex, r, tmp) c.freeTemp(tmp) else: - c.genArg(it, r) + discard c.genArg(it, r) if i >= fntyp.len: internalAssert(c.config, tfVarargs in fntyp.flags) c.gABx(n, opcSetType, r, c.genType(n.typ)) if dest.isUnset: - c.gABC(n, opcIndCall, 0, x, n.len) + c.gABC(n, opcIndCall, 0, x, n.numArgs) else: - c.gABC(n, opcIndCallAsgn, dest, x, n.len) - c.freeTempRange(x, n.len) + c.gABC(n, opcIndCallAsgn, dest, x, n.numArgs) + c.freeTempRange(x, n.numArgs) template isGlobal(s: PSym): bool = sfGlobal in s.flags and s.kind != skForVar proc isGlobal(n: PNode): bool = n.kind == nkSym and isGlobal(n.sym) @@ -878,17 +915,15 @@ func local(prc: PProc, sym: PSym): TDest {.inline.} = if s.kind in {skResult, skParam}: s.position + ord(s.kind == skParam) else: -1 -proc needsAsgnPatch(n: PNode): bool = - n.kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr, - nkDerefExpr, nkHiddenDeref} or (n.kind == nkSym and n.sym.isGlobal) - -proc genField(c: TCtx; n: CgNode): TRegister = - assert n.kind == cgnkField +proc needsAsgnPatch(n: CgNode): bool = + n.kind in {cgnkArrayAccess, cgnkTupleAccess, cgnkObjAccess, + cgnkDeref, cgnkDerefView, cgnkGlobal} - let s = n.sym +proc genField(c: TCtx; s: PSym, info: TLineInfo): TRegister = if s.position > high(TRegister): - fail(n.info, vmGenDiagTooLargeOffset, sym = s) + fail(info, vmGenDiagTooLargeOffset, sym = s) + # despite what the return type indicates, this is *not* a register result = s.position proc genIndex(c: var TCtx; n: CgNode; arr: PType): TRegister = @@ -909,36 +944,29 @@ proc genRegLoad(c: var TCtx, n: CgNode, dest, src: TRegister) = if t.isUnsigned() and t.size < sizeof(BiggestInt): c.gABC(n, opcNarrowU, dest, TRegister(t.size * 8)) -proc genCheckedObjAccessAux(c: var TCtx; n: CgNode): TRegister proc genSymAddr(c: var TCtx, n: CgNode): TRegister proc genAsgnPatch(c: var TCtx; le: CgNode, value: TRegister) = case le.kind of cgnkArrayAccess: - let typ = le[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}) - let dest = c.genx(le[0], {gfNode}) - let idx = c.genIndex(le[1], le[0].typ) + let typ = le.source.typ.skipTypes(abstractVar) + let dest = c.genx(le.source, {gfNode}) + let idx = c.genIndex(le.index, le.source.typ) let opc = if typ.kind in {tyString, tyCstring}: opcWrStrIdx else: opcWrArr c.gABC(le, opc, dest, idx, value) c.freeTemp(dest) c.freeTemp(idx) - of nkCheckedFieldExpr: - let objR = genCheckedObjAccessAux(c, le) - 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]) + of cgnkObjAccess: + let dest = c.genx(le.source, {gfNode}) + let idx = genField(c, le.field, le.info) c.gABC(le, opcWrObj, dest, idx, value) c.freeTemp(dest) - of nkDerefExpr, nkHiddenDeref: - let dest = c.genx(le[0], #[{gfNode}]#) + of cgnkDerefView, cgnkDeref: + let dest = c.genx(le.source, #[{gfNode}]#) c.gABC(le, opcWrDeref, dest, 0, value) c.freeTemp(dest) - of nkSym: - if le.sym.isGlobal: + of cgnkGlobal: let dest = genSymAddr(c, le) c.gABC(le, opcWrDeref, dest, 0, value) c.freeTemp(dest) @@ -952,10 +980,11 @@ proc genNew(c: var TCtx; n: CgNode) = # should treat the destination register as a handle to a ``ref`` # location let - dest = c.genx(n[1]) - tmp = c.getTemp(n[1].typ) + arg = n.arg(0) + dest = c.genArg(n) + tmp = c.getTemp(arg.typ) c.gABx(n, opcNew, tmp, - c.genType(n[1].typ.skipTypes(abstractVar-{tyTypeDesc}))) + c.genType(arg.typ.skipTypes(abstractVar-{tyTypeDesc}))) c.gABC(n, opcWrLoc, dest, tmp) c.freeTemp(tmp) c.freeTemp(dest) @@ -963,12 +992,12 @@ proc genNew(c: var TCtx; n: CgNode) = proc genNewSeq(c: var TCtx; n: CgNode) = # FIXME: ``opcNewSeq`` has the same problem as ``opcNew``. The instruction # should also reuse the location - let t = n[1].typ.skipTypes(abstractVar-{tyTypeDesc}) + let t = n.arg(0).typ.skipTypes(abstractVar-{tyTypeDesc}) assert t.kind == tySequence let - dest = c.genx(n[1]) # ``seq`` argument - len = c.genx(n[2]) # length argument - tmp = c.getTemp(n[1].typ) + dest = c.genArg(n, 0) # ``seq`` argument + len = c.genArg(n, 1) # length argument + tmp = c.getTemp(n.arg(0).typ) c.gABx(n, opcNewSeq, tmp, c.genType(t)) c.gABx(n, opcNewSeq, len, 0) c.gABC(n, opcWrLoc, dest, tmp) @@ -980,7 +1009,7 @@ proc genNewSeqOfCap(c: var TCtx; n: CgNode; dest: var TDest) = let t = n.typ if dest.isUnset: dest = c.getTemp(n.typ) - let tmp = c.getTemp(n[1].typ) + let tmp = c.getTemp(n.arg(0).typ) c.gABx(n, opcLdNull, dest, c.genType(t)) c.gABx(n, opcLdImmInt, tmp, 0) c.gABx(n, opcNewSeq, dest, c.genType(t.skipTypes(abstractVar-{tyTypeDesc}))) @@ -988,13 +1017,13 @@ proc genNewSeqOfCap(c: var TCtx; n: CgNode; dest: var TDest) = c.freeTemp(tmp) proc genUnaryABC(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = - let tmp = c.genx(n[1]) + let tmp = c.genArg(n, 0) if dest.isUnset: dest = c.getTemp(n.typ) c.gABC(n, opc, dest, tmp) c.freeTemp(tmp) proc genUnaryABI(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode; imm: BiggestInt=0) = - let tmp = c.genx(n[1]) + let tmp = c.genArg(n, 0) if dest.isUnset: dest = c.getTemp(n.typ) c.gABI(n, opc, dest, tmp, imm) c.freeTemp(tmp) @@ -1002,8 +1031,8 @@ proc genUnaryABI(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode; imm: Big proc genBinaryABC(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = let - tmp = c.genx(n[1]) - tmp2 = c.genx(n[2]) + tmp = c.genArg(n, 0) + tmp2 = c.genArg(n, 1) if dest.isUnset: dest = c.getTemp(n.typ) c.gABC(n, opc, dest, tmp, tmp2) c.freeTemp(tmp) @@ -1011,9 +1040,9 @@ proc genBinaryABC(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = proc genBinaryABCD(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = let - tmp = c.genx(n[1]) - tmp2 = c.genx(n[2]) - tmp3 = c.genx(n[3]) + tmp = c.genArg(n, 0) + tmp2 = c.genArg(n, 1) + tmp3 = c.genArg(n, 2) if dest.isUnset: dest = c.getTemp(n.typ) c.gABC(n, opc, dest, tmp, tmp2) c.gABC(n, opc, tmp3) @@ -1048,8 +1077,8 @@ proc genBinaryABCnarrowU(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) proc genBinarySet(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = let - tmp = c.genx(n[1]) - tmp2 = c.genx(n[2]) + tmp = c.genArg(n, 0) + tmp2 = c.genArg(n, 1) if dest.isUnset: dest = c.getTemp(n.typ) c.gABC(n, opc, dest, tmp, tmp2) c.freeTemp(tmp) @@ -1057,52 +1086,43 @@ proc genBinarySet(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = proc genBinaryStmt(c: var TCtx; n: CgNode; opc: TOpcode) = let - dest = c.genx(n[1]) - tmp = c.genx(n[2]) + dest = c.genArg(n, 0) + tmp = c.genArg(n, 1) c.gABC(n, opc, dest, tmp, 0) c.freeTemp(tmp) c.freeTemp(dest) proc genBinaryStmtVar(c: var TCtx; n: CgNode; opc: TOpcode) = - var x = n[1] - if x.kind in {nkAddr, nkHiddenAddr}: x = x[0] let - dest = c.genx(x) - tmp = c.genx(n[2]) + dest = c.genArg(n, 0) + tmp = c.genArg(n, 1) c.gABC(n, opc, dest, tmp, 0) #c.genAsgnPatch(n[1], dest) c.freeTemp(tmp) c.freeTemp(dest) -proc genParseOp(c: var TCtx; n: PNode; dest: var TDest, +proc genParseOp(c: var TCtx; n: CgNode; dest: var TDest, opc: range[opcParseExprToAst..opcParseStmtToAst]) = ## Generates the code for a ``parseExpr``/``parseStmt`` magic call if dest.isUnset: dest = c.getTemp(n.typ) - # the second parameter is a ``var`` parameter. We want to access the - # register directly, so the used addr operation is skipped (if it hasn't - # been eliminated by ``transf``) - var x = n[2] - if x.kind in {nkAddr, nkHiddenAddr}: - x = x[0] - let - in1 = c.genx(n[1]) - in2 = c.genx(x) + in1 = c.genArg(n, 0) + in2 = c.genArg(n, 1) c.gABC(n, opc, dest, in1, in2) c.freeTemp(in1) c.freeTemp(in2) -proc genVarargsABC(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = +proc genVarargsABC(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = if dest.isUnset: dest = getTemp(c, n.typ) - var x = c.getTempRange(n.len-1, slotTempStr) - for i in 1..= low(int16) and n.intVal <= high(int16) -proc genAddSubInt(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = - if n[2].isInt8Lit: - let tmp = c.genx(n[1]) - if dest.isUnset: dest = c.getTemp(n.typ) - c.gABI(n, succ(opc), dest, tmp, n[2].intVal) +proc genAddSubInt(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = + if n.arg(1).kind == cgnkLiteralData and n.arg(1).data.isInt8Lit: + let tmp = c.genx(n.arg(0)) + if dest.isUnset: + dest = c.getTemp(n.typ) + c.gABI(n, succ(opc), dest, tmp, n.arg(1).data.intVal) c.freeTemp(tmp) else: genBinaryABC(c, n, dest, opc) c.genNarrow(n, dest) -proc genConv(c: var TCtx; n, arg: PNode; dest: var TDest; opc=opcConv) = +proc genConv(c: var TCtx; n, arg: CgNode; dest: var TDest; opc=opcConv) = let t2 = n.typ.skipTypes({tyDistinct}) let targ2 = arg.typ.skipTypes({tyDistinct}) @@ -1144,8 +1165,8 @@ proc genConv(c: var TCtx; n, arg: PNode; dest: var TDest; opc=opcConv) = c.gABx(n, opc, 0, c.genTypeInfo(arg.typ.skipTypes({tyStatic}))) c.freeTemp(tmp) -proc genCard(c: var TCtx; n: PNode; dest: var TDest) = - let tmp = c.genx(n[1]) +proc genCard(c: var TCtx; n: CgNode; dest: var TDest) = + let tmp = c.genArg(n, 0) if dest.isUnset: dest = c.getTemp(n.typ) c.gABC(n, opcCard, dest, tmp) c.freeTemp(tmp) @@ -1156,17 +1177,17 @@ template needsRegLoad(): untyped = gfNode notin flags and fitsRegister(n.typ.skipTypes({tyVar, tyLent, tyStatic})) -proc genCastIntFloat(c: var TCtx; n: PNode; dest: var TDest) = +proc genCastIntFloat(c: var TCtx; n: CgNode; dest: var TDest) = const allowedIntegers = {tyInt..tyInt64, tyUInt..tyUInt64, tyChar} var signedIntegers = {tyInt..tyInt64} var unsignedIntegers = {tyUInt..tyUInt64, tyChar} - let src = n[1].typ.skipTypes(abstractRange)#.kind - let dst = n[0].typ.skipTypes(abstractRange)#.kind + let src = n.source.typ.skipTypes(abstractRange)#.kind + let dst = n.typ.skipTypes(abstractRange)#.kind let srcSize = getSize(c.config, src) let dstSize = getSize(c.config, dst) if src.kind in allowedIntegers and dst.kind in allowedIntegers: - let tmp = c.genx(n[1]) - if dest.isUnset: dest = c.getTemp(n[0].typ) + let tmp = c.genx(n.source) + if dest.isUnset: dest = c.getTemp(n.typ) c.gABC(n, opcAsgnInt, dest, tmp) if dstSize != sizeof(BiggestInt): # don't do anything on biggest int types if dst.kind in signedIntegers: # we need to do sign extensions @@ -1182,8 +1203,8 @@ proc genCastIntFloat(c: var TCtx; n: PNode; dest: var TDest) = c.freeTemp(tmp) elif srcSize == dstSize and src.kind in allowedIntegers and dst.kind in {tyFloat, tyFloat32, tyFloat64}: - let tmp = c.genx(n[1]) - if dest.isUnset: dest = c.getTemp(n[0].typ) + let tmp = c.genx(n.source) + if dest.isUnset: dest = c.getTemp(n.typ) if dst.kind == tyFloat32: c.gABC(n, opcCastIntToFloat32, dest, tmp) else: @@ -1192,8 +1213,8 @@ proc genCastIntFloat(c: var TCtx; n: PNode; dest: var TDest) = elif srcSize == dstSize and src.kind in {tyFloat, tyFloat32, tyFloat64} and dst.kind in allowedIntegers: - let tmp = c.genx(n[1]) - if dest.isUnset: dest = c.getTemp(n[0].typ) + let tmp = c.genx(n.source) + if dest.isUnset: dest = c.getTemp(n.typ) if src.kind == tyFloat32: c.gABC(n, opcCastFloatToInt32, dest, tmp) if dst.kind in unsignedIntegers: @@ -1205,21 +1226,21 @@ proc genCastIntFloat(c: var TCtx; n: PNode; dest: var TDest) = # 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.isUnset: dest = c.getTemp(n[0].typ) + let tmp = c.genx(n.source) + if dest.isUnset: dest = c.getTemp(n.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.isUnset: dest = c.getTemp(n[0].typ) + let tmp = c.genx(n.source) + if dest.isUnset: dest = c.getTemp(n.typ) c.gABC(n, opcCastIntToPtr, dest, tmp) c.gABx(n, opcSetType, dest, c.genType(dst)) c.freeTemp(tmp) elif src.kind == tyNil and dst.kind in NilableTypes: # supports casting nil literals to NilableTypes in VM # see #16024 - if dest.isUnset: dest = c.getTemp(n[0].typ) + if dest.isUnset: dest = c.getTemp(n.typ) let opcode = if fitsRegister(dst): opcLdNullReg else: opcLdNull c.gABx(n, opcode, dest, c.genType(dst)) else: @@ -1231,22 +1252,25 @@ proc genCastIntFloat(c: var TCtx; n: PNode; dest: var TDest) = instLoc: instLoc(), typeMismatch: VmTypeMismatch(actualType: dst, formalType: src)) -proc genVoidABC(c: var TCtx, n: PNode, dest: TDest, opcode: TOpcode) = +proc genVoidABC(c: var TCtx, n: CgNode, dest: TDest, opcode: TOpcode) = unused(c, n, dest) var - tmp1 = c.genx(n[1]) - tmp2 = c.genx(n[2]) - tmp3 = c.genx(n[3]) + tmp1 = c.genArg(n, 0) + tmp2 = c.genArg(n, 1) + tmp3 = c.genArg(n, 2) c.gABC(n, opcode, tmp1, tmp2, tmp3) c.freeTemp(tmp1) c.freeTemp(tmp2) c.freeTemp(tmp3) -proc genSetElem(c: var TCtx, n: PNode, first: int): TRegister = +func isIntLiteral(n: CgNode): bool = + n.kind == cgnkLiteralData and n.data.kind in nkIntLiterals + +proc genSetElem(c: var TCtx, n: CgNode, first: int): TRegister = result = c.getTemp(n.typ) if first != 0: - if n.kind in nkIntKinds: + if isIntLiteral(n): # a literal value c.gABx(n, opcLdImmInt, result, int(n.intVal - first)) else: @@ -1259,7 +1283,7 @@ proc genSetElem(c: var TCtx, n: PNode, first: int): TRegister = else: gen(c, n, result) -proc genSetElem(c: var TCtx, n: PNode, typ: PType): TRegister {.inline.} = +proc genSetElem(c: var TCtx, n: CgNode, typ: PType): TRegister {.inline.} = ## `typ` is the type to derive the lower bound from let t = typ.skipTypes(abstractInst) assert t.kind == tySet @@ -1280,7 +1304,7 @@ proc ldNullOpcode(t: PType): TOpcode = assert t != nil if fitsRegister(t): opcLdNullReg else: opcLdNull -proc whichAsgnOpc(n: PNode; requiresCopy = true): TOpcode = +proc whichAsgnOpc(n: CgNode; requiresCopy = true): TOpcode = case n.typ.skipTypes(abstractRange-{tyTypeDesc}).kind of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64: opcAsgnInt @@ -1291,34 +1315,39 @@ proc whichAsgnOpc(n: PNode; requiresCopy = true): TOpcode = opcAsgnComplex #(if requiresCopy: opcAsgnComplex else: opcFastAsgnComplex) -proc genMagic(c: var TCtx; n: PNode; dest: var TDest; m: TMagic) = +proc genTypeLit(c: var TCtx; t: PType; dest: var TDest) = + let n = newNodeIT(nkType, unknownLineInfo, t) + genLit(c, nil, c.toNodeCnst(n), dest) + +proc genMagic(c: var TCtx; n: CgNode; dest: var TDest; m: TMagic) = case m - of mAnd: c.genAndOr(n, opcFJmp, dest) - of mOr: c.genAndOr(n, opcTJmp, dest) of mPred, mSubI: c.genAddSubInt(n, dest, opcSubInt) of mSucc, mAddI: c.genAddSubInt(n, dest, opcAddInt) of mInc, mDec: unused(c, n, dest) - let isUnsigned = n[1].typ.skipTypes(abstractVarRange).kind in {tyUInt..tyUInt64} + let + target = n.arg(0) + value = n.arg(1) + let isUnsigned = target.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) + let d = c.genx(target) + if value.kind == cgnkLiteralData and value.data.isInt8Lit and not isUnsigned: + c.gABI(n, succ(opc), d, d, value.intVal) else: - let tmp = c.genx(n[2]) + let tmp = c.genx(value) c.gABC(n, opc, d, d, tmp) c.freeTemp(tmp) - c.genNarrow(n[1], d) - c.genAsgnPatch(n[1], d) + c.genNarrow(target, d) + c.genAsgnPatch(target, d) c.freeTemp(d) - of mOrd, mChr: c.gen(n[1], dest) + of mOrd, mChr: c.gen(n.arg(0), dest) of mArrToSeq: - let temp = c.genx(n[1]) + let temp = c.genArg(n, 0) let L = c.getTemp(c.graph.getSysType(n.info, tyInt)) c.gABC(n, opcLenSeq, L, temp) let t = n.typ.skipTypes(abstractVar-{tyTypeDesc}) @@ -1345,8 +1374,8 @@ proc genMagic(c: var TCtx; n: PNode; dest: var TDest; m: TMagic) = of mNewStringOfCap: # we ignore the 'cap' argument and translate it as 'newString(0)'. # eval n[1] for possible side effects: - c.freeTemp(c.genx(n[1])) - var tmp = c.getTemp(n[1].typ) + c.freeTemp(c.genx(n.arg(0))) + var tmp = c.getTemp(n.arg(0).typ) c.gABx(n, opcLdImmInt, tmp, 0) if dest.isUnset: dest = c.getTemp(n.typ) c.gABC(n, opcNewStr, dest, tmp) @@ -1355,7 +1384,7 @@ proc genMagic(c: var TCtx; n: PNode; dest: var TDest; m: TMagic) = of mLengthOpenArray, mLengthArray, mLengthSeq: genUnaryABI(c, n, dest, opcLenSeq) of mLengthStr: - let t = n[1].typ.skipTypes(abstractInst) + let t = n.arg(0).typ.skipTypes(abstractInst) case t.kind of tyString: genUnaryABI(c, n, dest, opcLenStr) of tyCstring: genUnaryABI(c, n, dest, opcLenCstring) @@ -1363,8 +1392,8 @@ proc genMagic(c: var TCtx; n: PNode; dest: var TDest; m: TMagic) = of mIncl, mExcl: unused(c, n, dest) let - d = c.genx(n[1]) - tmp = c.genSetElem(n[2], n[1].typ) + d = c.genx(n.arg(0)) + tmp = c.genSetElem(n.arg(1), n.arg(0).typ) c.gABC(n, if m == mIncl: opcIncl else: opcExcl, d, tmp) c.freeTemp(d) c.freeTemp(tmp) @@ -1379,9 +1408,9 @@ proc genMagic(c: var TCtx; n: PNode; dest: var TDest; m: TMagic) = of mShrI: # 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]) + let tmp = c.genx(n.arg(0)) c.genNarrowU(n, tmp) - let tmp2 = c.genx(n[2]) + let tmp2 = c.genx(n.arg(1)) if dest.isUnset: dest = c.getTemp(n.typ) c.gABC(n, opcShrInt, dest, tmp, tmp2) c.freeTemp(tmp) @@ -1422,7 +1451,7 @@ proc genMagic(c: var TCtx; n: PNode; dest: var TDest; m: TMagic) = genUnaryABC(c, n, dest, opcUnaryMinusInt) genNarrow(c, n, dest) of mUnaryMinusF64: genUnaryABC(c, n, dest, opcUnaryMinusFloat) - of mUnaryPlusI, mUnaryPlusF64: gen(c, n[1], dest) + of mUnaryPlusI, mUnaryPlusF64: gen(c, n.arg(0), dest) of mBitnotI: genUnaryABC(c, n, dest, opcBitnotInt) #genNarrowU modified, do not narrow signed types @@ -1430,7 +1459,7 @@ proc genMagic(c: var TCtx; n: PNode; dest: var TDest; m: TMagic) = if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and t.size < 8): c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr: - genConv(c, n, n[1], dest) + genConv(c, n, n.arg(0), dest) of mEqStr, mEqCString: genBinaryABC(c, n, dest, opcEqStr) of mLeStr: genBinaryABC(c, n, dest, opcLeStr) of mLtStr: genBinaryABC(c, n, dest, opcLtStr) @@ -1443,99 +1472,103 @@ proc genMagic(c: var TCtx; n: PNode; dest: var TDest; m: TMagic) = of mConStrStr: genVarargsABC(c, n, dest, opcConcatStr) of mInSet: let - tmp = c.genx(n[1]) - tmp2 = c.genSetElem(n[2], n[1].typ) + tmp = c.genx(n.arg(0)) + tmp2 = c.genSetElem(n.arg(1), n.arg(0).typ) if dest.isUnset: dest = c.getTemp(n.typ) c.gABC(n, opcContainsSet, dest, tmp, tmp2) c.freeTemp(tmp) c.freeTemp(tmp2) of mRepr: - let tmp = c.genx(n[1]) + let tmp = c.genx(n.arg(0)) if dest.isUnset: dest = c.getTemp(n.typ) - c.gABx(n, opcRepr, dest, c.genTypeInfo(n[1].typ)) + c.gABx(n, opcRepr, dest, c.genTypeInfo(n.arg(0).typ)) c.gABC(n, opcRepr, dest, tmp) c.freeTemp(tmp) of mExit: unused(c, n, dest) - var tmp = c.genx(n[1]) + var tmp = c.genArg(n, 0) c.gABC(n, opcQuit, tmp) c.freeTemp(tmp) of mSetLengthStr, mSetLengthSeq: unused(c, n, dest) - var d = c.genx(n[1]) - var tmp = c.genx(n[2]) + var d = c.genArg(n, 0) + var tmp = c.genArg(n, 1) c.gABC(n, if m == mSetLengthStr: opcSetLenStr else: opcSetLenSeq, d, tmp) c.freeTemp(tmp) c.freeTemp(d) of mSwap: unused(c, n, dest) - c.gen(lowerSwap(c.graph, n, c.idgen, if c.prc == nil or c.prc.sym == nil: c.module else: c.prc.sym)) + let + a = n.arg(0) + b = n.arg(1) + # TODO: rework once the address rework is merged. It's super inefficient + let tmp = c.getTemp(a.typ) + let tmp1 = c.genx(a) + let tmp2 = c.genx(b) + c.gABC(n, opcFastAsgnComplex, tmp, tmp1) + c.genAsgnPatch(a, tmp2) + c.genAsgnPatch(b, tmp) + c.freeTemp(tmp2) + c.freeTemp(tmp1) + c.freeTemp(tmp) of mIsNil: genUnaryABC(c, n, dest, opcIsNil) of mParseBiggestFloat: if dest.isUnset: dest = c.getTemp(n.typ) - var d2: TRegister - # skip 'nkHiddenAddr': - 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[1]) - tmp3 = c.genx(n[3]) - c.gABC(n, opcParseFloat, dest, tmp1, d2) + tmp1 = c.genArg(n, 0) + tmp2 = c.genArg(n, 1) + tmp3 = c.genArg(n, 2) + c.gABC(n, opcParseFloat, dest, tmp1, tmp2) c.gABC(n, opcParseFloat, tmp3) c.freeTemp(tmp1) + c.freeTemp(tmp2) c.freeTemp(tmp3) - c.genAsgnPatch(d2AsNode, d2) - c.freeTemp(d2) of mReset: unused(c, n, dest) - var d = c.genx(n[1]) + var d = c.genArg(n, 1) # XXX use ldNullOpcode() here? - c.gABx(n, opcLdNull, d, c.genType(n[1].typ)) + c.gABx(n, opcLdNull, d, c.genType(n.arg(0).typ)) c.gABx(n, opcNodeToReg, d, d) - c.genAsgnPatch(n[1], d) + c.genAsgnPatch(n.arg(0), d) of mDefault: if dest.isUnset: dest = c.getTemp(n.typ) c.gABx(n, ldNullOpcode(n.typ), dest, c.genType(n.typ)) of mOf: if dest.isUnset: dest = c.getTemp(n.typ) - let t1 = n[1].typ.skipTypes(abstractRange) + let t1 = n.arg(0).typ.skipTypes(abstractRange) if t1.kind != tyRef: # XXX: the spec for `of` with non-ref types is missing, so we simply # treat it as an `is` for now. If it's decided that this is the # correct behaviour, the `of` should be eliminated in `sem` instead # of down here - c.gABx(n, opcLdImmInt, dest, ord(sameType(t1, n[2].typ))) + c.gABx(n, opcLdImmInt, dest, ord(sameType(t1, n.arg(1).typ))) else: - var tmp = c.genx(n[1]) + var tmp = c.genArg(n, 0) var idx = c.getTemp(getSysType(c.graph, n.info, tyInt)) - let typ = n[2].typ.skipTypes(abstractPtrs) + let typ = n.arg(1).typ.skipTypes(abstractPtrs) c.gABx(n, opcLdImmInt, idx, c.genType(typ)) c.gABC(n, opcOf, dest, tmp, idx) c.freeTemp(tmp) c.freeTemp(idx) of mHigh: if dest.isUnset: dest = c.getTemp(n.typ) - let tmp = c.genx(n[1]) - case n[1].typ.skipTypes(abstractVar-{tyTypeDesc}).kind: + let tmp = c.genArg(n, 0) + case n.arg(0).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 - if n.kind == nkBracket: + let n = n.arg(0) # can happen for nim check, see bug #9609 - let x = c.getTempRange(n.len, slotTempUnknown) - for i in 0.. 0: + if n.numArgs > 0: var tmp = getTemp(c, intType) c.gABx(n, opcLdNullReg, tmp, c.genType(intType)) - for x in n: + for i, x in arguments(n): let a = c.genx(x) c.gABC(n, opcWrArr, dest, tmp, a) c.gABI(n, opcAddImmInt, tmp, tmp, 1) c.freeTemp(a) c.freeTemp(tmp) -proc genSetConstr(c: var TCtx, n: PNode, dest: var TDest) = +proc genSetConstr(c: var TCtx, n: CgNode, dest: var TDest) = if dest.isUnset: dest = c.getTemp(n.typ) c.gABx(n, opcLdNull, dest, c.genType(n.typ)) # XXX: since `first` stays the same across the loop, we could invert # the loop around `genSetElem`'s logic... let first = firstOrd(c.config, n.typ.skipTypes(abstractInst)).toInt() - for x in n: - if x.kind == nkRange: - let a = c.genSetElem(x[0], first) - let b = c.genSetElem(x[1], first) + for i, x in arguments(n): + if x.kind == cgnkRange: + let a = c.genSetElem(x.arg(0), first) + let b = c.genSetElem(x.arg(1), first) c.gABC(n, opcInclRange, dest, a, b) c.freeTemp(b) c.freeTemp(a) @@ -2342,79 +2306,60 @@ proc genSetConstr(c: var TCtx, n: PNode, dest: var TDest) = c.gABC(n, opcIncl, dest, a) c.freeTemp(a) -proc genObjConstr(c: var TCtx, n: PNode, dest: var TDest) = - if dest.isUnset: dest = c.getTemp(n.typ) - let t = n.typ.skipTypes(abstractRange-{tyTypeDesc}) - var refTemp: TDest - if t.kind == tyRef: - refTemp = c.getTemp(t[0]) # The temporary register to hold the - # dereferenced location - c.gABx(n, opcNew, dest, c.genType(t)) - c.gABC(n, opcLdDeref, refTemp, dest) - swap(refTemp, dest) - else: - c.gABx(n, opcLdNull, dest, c.genType(n.typ)) - for i in 1.. Date: Thu, 3 Aug 2023 15:27:34 +0000 Subject: [PATCH 04/20] start over; revert all changes so far --- compiler/backend/astgen2.nim | 1141 ----------------------- compiler/backend/cgir.nim | 255 ------ compiler/mir/mirbridge.nim | 49 +- compiler/vm/vm.nim | 27 +- compiler/vm/vm_enums.nim | 1 - compiler/vm/vmbackend.nim | 16 +- compiler/vm/vmdef.nim | 3 +- compiler/vm/vmgen.nim | 1646 +++++++++++++++++++--------------- compiler/vm/vmjit.nim | 8 +- 9 files changed, 939 insertions(+), 2207 deletions(-) delete mode 100644 compiler/backend/astgen2.nim delete mode 100644 compiler/backend/cgir.nim diff --git a/compiler/backend/astgen2.nim b/compiler/backend/astgen2.nim deleted file mode 100644 index 5d8b452cf6b..00000000000 --- a/compiler/backend/astgen2.nim +++ /dev/null @@ -1,1141 +0,0 @@ -## This module implements the MIR -> ``CgNode`` AST translation. The translation -## preserves the semantics and uses the smallest ``CgNode`` constructs that best -## match the respective MIR construct. Note that not all constructs in the MIR -## have a direct ``CgNode`` counterpart: those require more complex translation -## logic. -## -## The translation is implemented via recursive application of the correct -## translation procedure, where each procedure processes a sub-tree either -## directly or via further recursion. Instead of one uber procedure, multiple -## smaller ones that closely match the grammer are used. This allows for -## validating that the input MIR code is grammatically correct with effectively -## no overhead and without requiring extra contextual data or a separate pass. -## -## ============ -## MIR sections -## ============ -## -## There exists no equivalent to MIR sections in the ``CgNode`` AST, so a more -## complex translation has to be used. At the start of the section, each -## section argument is assigning to a temporary, using either a ``var`` / -## ``lent`` view or shallow copy depending on the argument's mode and -## type. A section parameter reference (``mnkOpParam``) is then translated to -## accessing the temporary introduce for the parameter's argument. - -import - compiler/ast/[ - ast, - ast_types, - ast_idgen, - ast_query, - idents, - lineinfos, - types - ], - compiler/backend/[ - cgir - ], - compiler/front/[ - options - ], - compiler/mir/[ - mirtrees, - sourcemaps - ], - compiler/modules/[ - modulegraphs - ], - compiler/utils/[ - containers, - idioms - ] - -from compiler/sem/semdata import makeVarType -from compiler/sem/typeallowed import directViewType, noView - -# TODO: move the procedure somewhere common -from compiler/vm/vmaux import findRecCase - -type - OriginInfo = PNode - - ValuesKind = enum - vkNone, vkSingle, vkMulti - - ArgumentMode = enum - amValue - amName - amConsume - - ValueTag = enum - ## ``ValueTag``s are used to propgate some information forward to the - ## value's consumer (e.g. a procedure call) - vtMutable ## the value is a mutable lvalue meant to be passed to a ``var`` - ## parameter - vtVariant - - ValueTags = set[ValueTag] - - Values = object - ## Represents the inputs to an operation. A container of zero-or-more - ## values, where each value is represented by a ``CgNode`` expression - case kind: ValuesKind - of vkNone: - discard - of vkSingle: - single: CgNode - tag: ValueTags - of vkMulti: - list: seq[CgNode] - modeAndTags: seq[tuple[mode: ArgumentMode, tags: ValueTags]] - ## a separate sequence is used so that the whole ``CgNode`` list can - ## be moved into the destination node at once - - TranslateCl = object - graph: ModuleGraph - cache: IdentCache - idgen: IdGenerator - - owner: PSym - - params: Values - nextTemp: uint32 - - TreeWithSource = object - ## Combines a ``MirTree`` with its associated ``SourceMap`` for - ## convenience. It's only meant to be used as parameter type - # TODO: the fields don't need ownership and should thus be turned into - # views as soon as possible - tree: MirTree - map: SourceMap - - TreeCursor = object - ## A cursor into a ``TreeWithSource`` - pos: uint32 ## the index of the currently pointed to node - origin {.cursor.}: PNode ## the node - -template isFilled(x: ref): bool = not x.isNil - -template `^^`(s, i: untyped): untyped = - # XXX: copied from ``system.nim`` because it's not exported - (when i is BackwardsIndex: s.len - int(i) else: int(i)) - -func toValues(x: sink CgNode): Values {.inline.} = - # note: having ``toValues`` be an implicit converter lead to an overload - # resolution issue where the converter was incorrectly chosen, making - # otherwise correct code not compile - assert x != nil - Values(kind: vkSingle, single: x) - -func `[]`(v: Values, i: Natural): CgNode = - if i > 0 or v.kind == vkMulti: - v.list[i] - else: - v.single - -func len(v: Values): int = - case v.kind - of vkNone: 0 - of vkSingle: 1 - of vkMulti: v.list.len - -func add(v: var Values, n: sink CgNode, tag: ValueTags, mode: ArgumentMode) = - v.list.add n - v.modeAndTags.add (mode, tag) - -proc `[]=`(n: CgNode, i: Natural, child: sink CgNode) = - n.childs[i] = child - -proc `[]`(n: CgNode, i: Natural): CgNode = - n.childs[i] - -func add(n: CgNode, child: sink CgNode) = - n.childs.add child - -func newNode(kind: CgNodeKind, origin: OriginInfo = nil): CgNode = - CgNode(kind: kind, origin: origin) - -func newTree(kind: CgNodeKind, origin: OriginInfo, childs: varargs[CgNode]): CgNode = - {.cast(uncheckedAssign).}: - CgNode(kind: kind, origin: origin, childs: @childs) - -func newExpr(kind: CgNodeKind, origin: OriginInfo; typ: PType, childs: varargs[CgNode]): CgNode = - {.cast(uncheckedAssign).}: - CgNode(kind: kind, origin: origin, typ: typ, childs: @childs) - -func newStmt(kind: CgNodeKind, origin: OriginInfo; childs: varargs[CgNode]): CgNode = - {.cast(uncheckedAssign).}: - CgNode(kind: kind, origin: origin, childs: @childs) - -func get(t: TreeWithSource, cr: var TreeCursor): lent MirNode {.inline.} = - cr.origin = sourceFor(t.map, cr.pos.NodeInstance) - result = t.tree[cr.pos] - - inc cr.pos - -func enter(t: TreeWithSource, cr: var TreeCursor): lent MirNode {.inline.} = - assert t.tree[cr.pos].kind in SubTreeNodes, "not a sub-tree" - result = get(t, cr) - -func leave(t: TreeWithSource, cr: var TreeCursor) = - assert t.tree[cr.pos].kind == mnkEnd, "not at the end of sub-tree" - inc cr.pos - -template info(cr: TreeCursor): OriginInfo = - cr.origin - -template `[]`(t: TreeWithSource, cr: TreeCursor): untyped = - t.tree[cr.pos] - -template hasNext(cr: TreeCursor, t: TreeWithSource): bool = - cr.pos.int < t.tree.len - -func toMode(kind: range[mnkArg..mnkConsume]): ArgumentMode = - case kind - of mnkArg: amValue - of mnkName: amName - of mnkConsume: amConsume - -proc copySubTree[A, B](source: PNode, slice: HSlice[A, B], to: PNode) = - ## Copies all sub-nodes from the `slice` in `source` to the end of `to`, - ## using a full sub-tree copy (i.e. ``copyTree``) - # XXX: using an ``openArray`` instead of a ``PNode`` + ``HSlice`` pair - # would simplify this procedure a lot. As of the time of this comment - # being written, creating an openArray from a node is rather cumbersome - # however - let - a = source ^^ slice.a - b = source ^^ slice.b - - if a > b: - return - - # resize the node list first: - let start = to.len - to.sons.setLen(start + (b - a) + 1) - - # then copy all nodes: - for i in a..b: - to[start + (i - a)] = source[i] - -func copy(source: CgNode): CgNode = - result = CgNode() - result[] = source[] - -func copyTree(source: CgNode): CgNode = - result = CgNode() - result[] = source[] - # TODO: fixme - -func newLiteral(n: PNode): CgNode = - CgNode(kind: cgnkLiteralData, typ: n.typ, data: n) - -func newField(sym: PSym): CgNode = - CgNode(kind: cgnkField, sym: sym) - -func addIfNotEmpty(stmts: var seq[CgNode], n: sink CgNode) = - ## Only adds the node to the list if it's not an empty node. Used to prevent - ## the creation of statement-list expressions that only consist of empty - ## nodes + the result-expression (a statement-list expression is unnecessary - ## in that case) - if n.kind != cgnkEmpty: - stmts.add n - -func toSingleNode(stmts: sink seq[CgNode]): CgNode = - ## Creates a single ``CgNode`` from a list of *statements* - case stmts.len - of 0: - result = newNode(cgnkEmpty) - of 1: - result = move stmts[0] - else: - result = newNode(cgnkStmtList) - result.childs = move stmts - -proc wrapArg(stmts: seq[CgNode], info: OriginInfo, val: CgNode): CgNode = - ## If there are extra statements (i.e. `stmts` is not empty), wraps the - ## statements + result-expression into an ``nkStmtListExpr``. Otherwise, - ## returns `val` as is - if stmts.len == 0: - result = val - else: - assert val.kind != cgnkStmtListExpr - result = newExpr(cgnkStmtListExpr, info, val.typ, stmts) - result.add val - -func unwrap(expr: CgNode, stmts: var seq[CgNode]): CgNode = - if expr.kind == cgnkStmtListExpr: - for it in expr.stmts: - stmts.add it - - result = expr.source - else: - result = expr - -proc newTemp(cl: var TranslateCl, info: OriginInfo, typ: PType): CgNode = - ## Creates and returns a new ``skTemp`` symbol - result = CgNode(kind: cgnkTemp, origin: info, typ: typ, temp: TempId cl.nextTemp) - inc cl.nextTemp - -func findBranch(c: ConfigRef, rec: PNode, field: PIdent): int = - ## Computes the 0-based position of the branch that `field` is part of. Only - ## the direct child nodes of `rec` are searched, nested record-cases are - ## ignored - assert rec.kind == nkRecCase - template cmpSym(s: PSym): bool = - s.name.id == field.id - - for i, b in branches(rec): - assert b.kind in {nkOfBranch, nkElse} - case b.lastSon.kind - of nkSym: - if cmpSym(b.lastSon.sym): - return i - - of nkRecList: - for it in b.lastSon.items: - let sym = - case it.kind - of nkSym: it.sym - of nkRecCase: it[0].sym - else: nil - - if sym != nil and cmpSym(sym): - return i - - of nkRecCase: - if cmpSym(b[0].sym): - return i - - else: - unreachable() - - unreachable("field is not part of the record-case") - -proc genBranchLabels(n: PNode, dest: CgNode) = - template lit(x: PNode): CgNode = - CgNode(kind: cgnkLiteralData, typ: x.typ, data: x) - - for i, it in branchLabels(n): - if it.kind == nkRange: - dest.add newTree(cgnkRange, it, [lit(it[0]), lit(it[1])]) - else: - dest.add CgNode(kind: cgnkLiteralData, typ: it.typ, data: it) - -proc buildCheck(cl: var TranslateCl, recCase: PNode, pos: Natural, - info: OriginInfo): CgNode = - ## Builds the boolean expression testing if `discr` is part of the branch - ## with position `pos` - assert recCase.kind == nkRecCase - let - discr = recCase[0] ## the node holding the discriminant symbol - branch = recCase[1 + pos] - setType = newType(tySet, nextTypeId(cl.idgen), cl.owner) - - rawAddSon(setType, discr.typ) # the set's element type - - var - lit = newExpr(cgnkSetConstr, info, setType) - invert = false - - case branch.kind - of nkOfBranch: - # use the branch labels as the set to test against - genBranchLabels(branch, lit) - of nkElse: - # iterate over all branches except the ``else`` branch and collect their - # labels - for i in 1.. firstOrd(c, n.typ) or - lastOrd(c, n.typ) > lastOrd(c, orig)): # is dest not a sub-range of source? - # generate a range check: - let - rangeDest = skipTypes(orig, abstractVar) - kind = - if tyInt64 in {dest.kind, source.kind}: cgnkChckRange64 - else: cgnkChckRange - - result = newExpr(kind, info, orig): - [n, - newLiteral newIntTypeNode(firstOrd(c, rangeDest), rangeDest), - newLiteral newIntTypeNode(lastOrd(c, rangeDest), rangeDest)] - of tyFloat..tyFloat128: - let rangeDest = skipTypes(orig, abstractVar) - if rangeDest.kind == tyRange: - # a conversion to a float range (e.g. ``range[0.0 .. 1.0]``) - result = newExpr(cgnkChckRangeF, info, orig): - [n, newLiteral(rangeDest.n[0]), newLiteral(rangeDest.n[1])] - - else: - result = nil - -proc tbConv(cl: TranslateCl, n: CgNode, info: OriginInfo, dest: PType): CgNode = - ## Generates the AST for an expression that performs a type conversion for - ## `n` to type `dest` - result = handleSpecialConv(cl.graph.config, n, info, dest) - if result == nil: - # no special conversion is used - result = newExpr(cgnkConv, info, dest): n - -proc tbSingle(n: MirNode, cl: TranslateCl, info: OriginInfo): CgNode = - case n.kind - of mnkProc, mnkConst, mnkParam, mnkGlobal, mnkLocal: - CgNode(kind: cgnkProc, origin: info, typ: n.sym.typ, sym: n.sym) - of mnkTemp: - CgNode(kind: cgnkTemp, origin: info, temp: n.temp) - of mnkLiteral: - CgNode(kind: cgnkLiteralData, origin: info, typ: n.lit.typ, data: n.lit) - of mnkType: - CgNode(kind: cgnkType, origin: info, typ: n.typ) - else: - unreachable("not an atom: " & $n.kind) - -proc tbExceptItem(tree: TreeWithSource, cl: TranslateCl, cr: var TreeCursor - ): CgNode = - let n {.cursor.} = get(tree, cr) - case n.kind - of mnkPNode: unreachable("imported exception; not implemented yet") - of mnkType: newExpr(cgnkType, cr.info, n.typ) - else: unreachable() - - -proc tbDef(tree: TreeWithSource, cl: var TranslateCl, prev: sink Values, - n: MirNode, cr: var TreeCursor): CgNode = - ## Translates a 'def'-like construct - assert n.kind in DefNodes - let - entity {.cursor.} = get(tree, cr) # the name of the defined entity - info = cr.info - - var ent: CgNode - - case entity.kind - of mnkLocal, mnkGlobal: - ent = tbSingle(entity, cl, info) - of mnkParam: - # not relevant to the code-generators; ignore - discard - of mnkProc: - # skip the definition - discard get(tree, cr) - of mnkTemp: - ent = CgNode(kind: cgnkTemp, origin: info, typ: n.typ, temp: entity.temp) - else: - unreachable() - - leave(tree, cr) - - if ent != nil: - let value = - case prev.kind - of vkNone: newNode(cgnkEmpty, cr.info) - of vkSingle: prev.single - of vkMulti: unreachable() - result = newStmt(cgnkDef, info, ent, value) - else: - # return an 'empty' node; it gets eliminated later - result = newNode(cgnkEmpty, info) - -proc translate(n: PNode): CgNode = - case n.kind - of nkConstSection: - # placed in MIR code for compatibility with the IC back-end - # TODO: remove this branch once all code-generators use ``CgNode`` - result = CgNode(kind: cgnkEmpty) - of nkPragma: - unreachable("missing") - of nkAsmStmt: - # TODO: we need support from ``mirgen`` here - # for it in t.sons: - # case it.kind - # of nkStrLit..nkTripleStrLit: - # of nkSym: - # if sym.kind in {skProc, skFunc, skIterator, skMethod}: - # elif sym.kind == skType: - # else: - # of nkTypeOfExpr: - # else: - #result = CgNode(kind: cgnkAsm, ) - discard - else: - unreachable(n.kind) - -proc tbSingleStmt(tree: TreeWithSource, cl: var TranslateCl, n: MirNode, - cr: var TreeCursor): CgNode = - template body(): CgNode = - tbStmt(tree, cl, cr) - - let info = cr.info ## the source information of `n` - - case n.kind - of DefNodes: - # a definition of an entity with no initial value - result = tbDef(tree, cl, Values(kind: vkNone), n, cr) - of mnkScope: - result = tbScope(tree, cl, n, cr) - leave(tree, cr) - of mnkRepeat: - result = newStmt(cgnkRepeat, info, body()) - leave(tree, cr) - of mnkBlock: - result = newStmt(cgnkBlock, info, - CgNode(kind: cgnkLabel, lbl: n.label), # the label - body()) - leave(tree, cr) - of mnkTry: - result = newStmt(cgnkTryStmt, info, [body()]) - assert n.len <= 2 - - for _ in 0.. cgnkInvalid -## TODO: continue - -import compiler/ast/[ast_types, lineinfos] -import compiler/mir/mirtrees - -type - CgNodeKind* = enum - cgnkInvalid ## uninitialized node kind. Used to catch issues - - cgnkEmpty ## represents the absence of a node. The meaning depend on the - ## context - cgnkType ## a literal type - - cgnkIntLit ## an embedded integer value; for internal use only -- doesn't represent a literal as used in the code - cgnkLiteralData ## embedded data - cgnkNilLit ## the nil literal - cgnkNimNodeLit ## a ``NimNode`` literal. Stores a single sub node - ## that represents the ``NimNode`` AST - ## end of atoms - - cgnkTemp - cgnkProc, cgnkLocal, cgnkGlobal, cgnkConst, cgnkField, cgnkParam - cgnkMagic, - cgnkLabel - - cgnkCall ## a procedure or magic call - cgnkArgTuple ## an argument tuple. Corresponds to ``mnkArgBlock`` - - # constructors: - cgnkTupleConstr ## tuple constructor - cgnkObjConstr ## object constructor - cgnkSetConstr ## set constructor - cgnkArrayConstr ## array constructor - cgnkClosureConstr ## closure constructor - - cgnkRange # for compatibility - - cgnkGoto ## - cgnkJoin ## the destination of one or more `goto`s - - cgnkMap - - # - cgnkEmit - cgnkAsm - - # access: - cgnkObjAccess - cgnkTupleAccess - cgnkArrayAccess - cgnkVariantAccess - - cgnkDeref - cgnkDerefView - cgnkAddr - cgnkView ## take a view of the input. Has the same meaning as ``mnkView`` - - cgnkStmtList - cgnkStmtListExpr - cgnkScope - - cgnkVoidStmt - - cgnkIf - cgnkRepeat - cgnkCase - cgnkBlock - - cgnkTryStmt - cgnkExcept - cgnkFinally - - cgnkBranch - - cgnkBreak - cgnkRaise - cgnkReturn - - cgnkConv ## a type conversion - cgnkLConv ## a type conversion that is l-value preserving - - cgnkObjDownConv ## down conversion between `object` or `ref` types. - cgnkObjUpConv ## up conversion between `object` or `ref` types. - - cgnkCast ## a type cast - - cgnkAsgn ## a = b - cgnkBlitCopy - cgnkSwitch ## change the active branch of a discriminated union - - cgnkDef - - # conversions kept for compatibility: - cgnkChckRangeF ## range check for floats - cgnkChckRange64 ## range check for 64 bit ints - cgnkChckRange ## range check for ints - cgnkStringToCString ## string to cstring - cgnkCStringToString ## cstring to string - # TODO: the above operation should never reach the code-generators. - # Instead, they need to be lowered via a MIR pass - - - - CgNode* = ref object - ## Code-generator node. Each node - origin*: PNode - typ*: PType - case kind*: CgNodeKind - of cgnkInvalid, cgnkEmpty, cgnkType, cgnkNilLit: discard - of cgnkField, cgnkProc: - sym*: PSym - of cgnkTemp: - temp*: TempId - of cgnkLiteralData: - data*: PNode - of cgnkIntLit: - intVal*: BiggestInt - of cgnkNimNodeLit: - nimNodeLit*: PNode - of cgnkMagic: - magic*: TMagic - of cgnkLabel: - lbl*: LabelId - else: - childs*: seq[CgNode] - -# TODO: use ``distinct`` types to reduce the amount of dynamic typing with -# ``CgNode``. ``CgNode`` is only the data representation. - -func condition*(n: CgNode): CgNode = - assert n.kind in {cgnkIf, cgnkCase} - n.childs[0] - -func body*(n: CgNode): CgNode = - assert n.kind in {cgnkIf, cgnkBlock, cgnkRepeat} - n.childs[^1] - -func handler*(n: CgNode): CgNode = - assert n.kind == cgnkTryStmt - if n.childs.len > 1 and n.childs[1].kind == cgnkExcept: - result = n.childs[1] - -func finalizer*(n: CgNode): CgNode = - assert n.kind == cgnkTryStmt - if n.childs[^1].kind == cgnkFinally: - result = n.childs[^1] - -func value*(n: CgNode): CgNode = - assert n.kind == cgnkRaise - n.childs[0] - - -func lhs*(n: CgNode): CgNode = - assert n.kind == cgnkRaise - n.childs[0] - -func rhs*(n: CgNode): CgNode = - assert n.kind == cgnkRaise - n.childs[0] - -func index*(n: CgNode): CgNode = - assert n.kind == cgnkArrayAccess - n.childs[1] - -func entity*(n: CgNode): CgNode = - assert n.kind == cgnkDef - n.childs[0] - -func callee*(n: CgNode): CgNode = - assert n.kind == cgnkCall - n.childs[0] - -func label*(n: CgNode): LabelId = - assert n.kind in {cgnkBlock, cgnkBreak} - n.childs[0].lbl - -func arg*(n: CgNode, i: Natural): CgNode = - n.childs[i + 1] - -func numArgs*(n: CgNode): int = - assert n.kind == cgnkCall - n.childs.len - 1 - -func labelAt*(n: CgNode, i: Natural): CgNode = - n.childs[i] - -iterator arguments*(n: CgNode): (int, CgNode) = - for i in 1.. 0: generateAST(graph, idgen, owner, tree, sourceMap) - else: newNode(nkEmpty) - -proc canonicalizeSingle2*(graph: ModuleGraph, idgen: IdGenerator, owner: PSym, - n: PNode, options: set[GenOption]): CgNode = - ## Similar to ``canonicalize``, but accepts a freestanding expression or - ## statement. The `owner` is used as the owner when generating the necessary - ## new symbols or types - var - tree: MirTree - sourceMap: SourceMap - - # step 1: generate a ``MirTree`` from the input AST - generateCode(graph, options, n, tree, sourceMap) - # step 2: translate it back, but only if there is something to translate - result = - if tree.len > 0: astgen2.generateAST(graph, idgen, owner, tree, sourceMap) - else: CgNode(kind: cgnkEmpty) \ No newline at end of file + else: newNode(nkEmpty) \ No newline at end of file diff --git a/compiler/vm/vm.nim b/compiler/vm/vm.nim index db12a9fbc2b..1797f0b4c76 100644 --- a/compiler/vm/vm.nim +++ b/compiler/vm/vm.nim @@ -1906,33 +1906,34 @@ proc rawExecute(c: var TCtx, pc: var int, tos: var StackFrameIndex): YieldReason else: raiseVmError(VmEvent(kind: vmEvtNodeNotASymbol)) - of opcDataToAst: - decodeBC(rkNimNode) - regs[ra].nimNode = c.regToNode(regs[rb], regs[rc].nimNode.typ, c.debug[pc]) - regs[ra].nimNode.info = c.debug[pc] of opcExpandToAst: decodeBC(rkNimNode) let - templ = regs[rb+0].nimNode.sym - prevFrame = c.sframes[tos].next + callExprAst = regs[rb].nimNode + prc = callExprAst[0].sym + prevFrame = c.sframes[tos].next - assert templ.kind == skTemplate + assert callExprAst.kind in nkCallKinds + assert prc.kind == skTemplate let genSymOwner = if prevFrame > 0 and c.sframes[prevFrame].prc != nil: c.sframes[prevFrame].prc else: c.module - var templCall = newNodeI(nkCall, c.debug[pc], rc) - templCall[0] = newSymNode(templ) - for i in 1..= 0: + freeTemp(c, tmp) + #if n.typ.isEmptyType: internalAssert tmp < 0 -proc genx(c: var TCtx; n: CgNode; flags: TGenFlags = {}): TRegister = +proc genx(c: var TCtx; n: PNode; flags: TGenFlags = {}): TRegister = var tmp: TDest = -1 gen(c, n, tmp, flags) #internalAssert c.config, tmp >= 0 # 'nim check' does not like this internalAssert. @@ -466,91 +459,139 @@ proc clearDest(c: var TCtx; n: PNode; dest: var TDest) {.inline.} = c.freeTemp(dest) dest = -1 -proc isNotOpr(n: CgNode): bool = - n.kind == cgnkCall and n.callee.kind == cgnkMagic and - n.callee.magic == mNot +proc isNotOpr(n: PNode): bool = + n.kind in nkCallKinds and n[0].kind == nkSym and + n[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 -proc genRepeat(c: var TCtx; n: CgNode) = +proc genWhile(c: var TCtx; n: PNode) = # lab1: + # cond, tmp + # fjmp tmp, lab2 # body # jmp lab1 - let lab1 = c.genLabel() - c.gen(n.body) - c.jmpBack(n, lab1) + # lab2: + let lab1 = c.genLabel + withBlock(nil): + 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[1]) + c.jmpBack(n, lab1) + c.patch(lab2) + else: + var tmp = c.genx(n[0]) + let lab2 = c.xjmp(n, opcFJmp, tmp) + c.freeTemp(tmp) + c.gen(n[1]) + c.jmpBack(n, lab1) + c.patch(lab2) -proc genScope(c: var TCtx; n: CgNode) = - assert n.kind == cgnkScope +proc genBlock(c: var TCtx; n: PNode; dest: var TDest) = let oldRegisterCount = c.prc.regInfo.len - - c.gen(n.body) + withBlock(n[0].sym): + c.gen(n[1], dest) for i in oldRegisterCount..= 0 and c.prc.regInfo[dest].kind >= slotTempUnknown +proc genAndOr(c: var TCtx; n: PNode; opc: TOpcode; dest: var TDest) = + # asgn dest, a + # tjmp|fjmp lab1 + # asgn dest, b + # lab1: + let copyBack = dest.isUnset 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.isUnset: + dest = tmp + elif copyBack: + c.gABC(n, opcAsgnInt, dest, tmp) + freeTemp(c, tmp) + + # XXX `rawGenLiteral` should be a func, but can't due to `internalAssert` proc rawGenLiteral(c: var TCtx, val: sink VmConstant): int = result = c.constants.len c.constants.add val internalAssert c.config, result < regBxMax, "Too many constants used" + template cmpFloatRep(a, b: BiggestFloat): bool = ## Compares the bit-representation of floats `a` and `b` # Special handling for floats, so that floats that have the same @@ -614,33 +655,24 @@ proc genLiteral(c: var TCtx, n: PNode): int = c.config.internalError(n.info, $n.kind) 0 -template enumerate(x: untyped, iterName: untyped): untyped = - iterator inner(iterable: typeof(x)): (int, auto) {.inline.} = - var i = 0 - for it in iterable.items: - yield (i, it) - inc i - - inner(x) - -template fillSliceList[T](sl: var seq[Slice[T]], node: CgNode, +template fillSliceList[T](sl: var seq[Slice[T]], nodes: openArray[PNode], get: untyped) = - sl.newSeq(node.numLabels) + sl.newSeq(nodes.len) template getIt(n): untyped = block: let it {.cursor, inject.} = n get - for i, n in node.labels: + for (i, n) in nodes.pairs: sl[i] = - if n.kind == cgnkRange: - getIt(n.a) .. getIt(n.b) + if n.kind == nkRange: + getIt(n[0]) .. getIt(n[1]) else: let e = getIt(n) e .. e -proc genBranchLit(c: var TCtx, n: CgNode, t: PType): int = +proc genBranchLit(c: var TCtx, n: PNode, t: PType): int = ## Turns the slice-list or single literal of the given `nkOfBranch` into ## a constant and returns it's index in `c.constant`. ## @@ -652,61 +684,43 @@ proc genBranchLit(c: var TCtx, n: CgNode, t: PType): int = # already exist assert t.kind in IntegralTypes+{tyString} - if n.numLabels == 1 and n.labelAt(0).kind == cgnkLiteralData: + if n.len == 2 and n[0].kind in nkLiterals: # It's an 'of' branch with a single value - result = c.genLiteral(n.data) + result = c.genLiteral(n[0]) else: # It's an 'of' branch with multiple and/or range values var cnst: VmConstant + template values: untyped = + n.sons.toOpenArray(0, n.sons.high - 1) # -1 for the branch body + case t.kind of IntegralTypes-{tyFloat..tyFloat128}: cnst = VmConstant(kind: cnstSliceListInt) - cnst.intSlices.fillSliceList(n): - it.data.intVal + cnst.intSlices.fillSliceList(values): + it.intVal of tyFloat..tyFloat128: cnst = VmConstant(kind: cnstSliceListFloat) - cnst.floatSlices.fillSliceList(n): - it.data.floatVal + cnst.floatSlices.fillSliceList(values): + it.floatVal of tyString: cnst = VmConstant(kind: cnstSliceListStr) - cnst.strSlices.fillSliceList(n): - c.toStringCnst(it.data.strVal) + cnst.strSlices.fillSliceList(values): + c.toStringCnst(it.strVal) else: unreachable(t.kind) result = c.rawGenLiteral(cnst) -proc genBranchLit(c: var TCtx, n: PNode): int = - ## Turns the slice-list or single literal of the given `nkOfBranch` into - ## a constant and returns it's index in `c.constant`. - ## - ## slice-lists are always added as a new constant while single literals - ## are reused - if n.len == 1 and n[0].kind in nkIntLiterals: - # It's an 'of' branch with a single value - result = c.toIntCnst(n[0].intVal) - else: - # It's an 'of' branch with multiple and/or range values - var cnst: VmConstant - for i, it in branchLabels(n): - cnst.intSlices[i] = - if n.kind == nkRange: - it[0].intVal .. it[1].intVal - else: - let e = n.intVal - e .. e - - result = c.rawGenLiteral(cnst) -proc unused(c: TCtx; n: CgNode; x: TDest) {.inline.} = +proc unused(c: TCtx; n: PNode; x: TDest) {.inline.} = if x >= 0: - fail(n.info, vmGenDiagNotUnused, n.origin) + fail(n.info, vmGenDiagNotUnused, n) -proc genCase(c: var TCtx; n: CgNode) = +proc genCase(c: var TCtx; n: PNode; dest: var TDest) = # if (!expr1) goto lab1; # thenPart # goto LEnd @@ -717,29 +731,38 @@ proc genCase(c: var TCtx; n: CgNode) = # lab2: # elsePart # Lend: - let selType = n.condition.typ.skipTypes(abstractVarRange) + if not isEmptyType(n.typ): + if dest.isUnset: dest = getTemp(c, n.typ) + else: + unused(c, n, dest) + let selType = n[0].typ.skipTypes(abstractVarRange) var endings: seq[TPosition] = @[] - withTemp(tmp, n.condition.typ): - c.gen(n.condition, tmp) + withTemp(tmp, n[0].typ): + c.gen(n[0], tmp) # branch tmp, codeIdx # fjmp elseLabel - for i, branch in branches(n): - if branch.numLabels == 0: - # the catch-all branch - c.gen(branch.body) - # must be the last branch, so no jump is required + # iterate of/else branches + for i in 1..= fntyp.len: internalAssert(c.config, tfVarargs in fntyp.flags) - c.gABx(n, opcSetType, r, c.genType(n.typ)) - + c.gABx(n, opcSetType, r, c.genType(n[i].typ)) if dest.isUnset: - c.gABC(n, opcIndCall, 0, x, n.numArgs) + c.gABC(n, opcIndCall, 0, x, n.len) else: - c.gABC(n, opcIndCallAsgn, dest, x, n.numArgs) - c.freeTempRange(x, n.numArgs) + c.gABC(n, opcIndCallAsgn, dest, x, n.len) + c.freeTempRange(x, n.len) template isGlobal(s: PSym): bool = sfGlobal in s.flags and s.kind != skForVar proc isGlobal(n: PNode): bool = n.kind == nkSym and isGlobal(n.sym) @@ -915,18 +942,21 @@ func local(prc: PProc, sym: PSym): TDest {.inline.} = if s.kind in {skResult, skParam}: s.position + ord(s.kind == skParam) else: -1 -proc needsAsgnPatch(n: CgNode): bool = - n.kind in {cgnkArrayAccess, cgnkTupleAccess, cgnkObjAccess, - cgnkDeref, cgnkDerefView, cgnkGlobal} +proc needsAsgnPatch(n: PNode): bool = + n.kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr, + nkDerefExpr, nkHiddenDeref} or (n.kind == nkSym and n.sym.isGlobal) + +proc genField(c: TCtx; n: PNode): TRegister = + if n.kind != nkSym or n.sym.kind != skField: + fail(n.info, vmGenDiagNotAFieldSymbol, ast = n) -proc genField(c: TCtx; s: PSym, info: TLineInfo): TRegister = - if s.position > high(TRegister): - fail(info, vmGenDiagTooLargeOffset, sym = s) + let s = n.sym + if s.position > high(typeof(result)): + fail(n.info, vmGenDiagTooLargeOffset, sym = s) - # despite what the return type indicates, this is *not* a register result = s.position -proc genIndex(c: var TCtx; n: CgNode; arr: PType): TRegister = +proc genIndex(c: var TCtx; n: PNode; arr: PType): TRegister = if arr.skipTypes(abstractInst).kind == tyArray and (let x = firstOrd(c.config, arr); x != Zero): let tmp = c.genx(n) @@ -937,67 +967,75 @@ proc genIndex(c: var TCtx; n: CgNode; arr: PType): TRegister = else: result = c.genx(n) -proc genRegLoad(c: var TCtx, n: CgNode, dest, src: TRegister) = +proc genRegLoad(c: var TCtx, n: PNode, dest, src: TRegister) = c.gABC(n, opcNodeToReg, dest, src) let t = n.typ.skipTypes(abstractInst) if t.isUnsigned() and t.size < sizeof(BiggestInt): c.gABC(n, opcNarrowU, dest, TRegister(t.size * 8)) -proc genSymAddr(c: var TCtx, n: CgNode): TRegister +proc genCheckedObjAccessAux(c: var TCtx; n: PNode): TRegister +proc genSymAddr(c: var TCtx, n: PNode): TRegister -proc genAsgnPatch(c: var TCtx; le: CgNode, value: TRegister) = +proc genAsgnPatch(c: var TCtx; le: PNode, value: TRegister) = case le.kind - of cgnkArrayAccess: - let typ = le.source.typ.skipTypes(abstractVar) - let dest = c.genx(le.source, {gfNode}) - let idx = c.genIndex(le.index, le.source.typ) + of nkBracketExpr: + let typ = le[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}) + let dest = c.genx(le[0], {gfNode}) + let idx = if typ.kind == tyTuple: le[1].intVal else: c.genIndex(le[1], le[0].typ) let opc = if typ.kind in {tyString, tyCstring}: opcWrStrIdx + elif typ.kind == tyTuple: opcWrObj else: opcWrArr c.gABC(le, opc, dest, idx, value) c.freeTemp(dest) - c.freeTemp(idx) - of cgnkObjAccess: - let dest = c.genx(le.source, {gfNode}) - let idx = genField(c, le.field, le.info) + if typ.kind != tyTuple: + c.freeTemp(idx) + of nkCheckedFieldExpr: + let objR = genCheckedObjAccessAux(c, le) + 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 cgnkDerefView, cgnkDeref: - let dest = c.genx(le.source, #[{gfNode}]#) + of nkDerefExpr, nkHiddenDeref: + let dest = c.genx(le[0], #[{gfNode}]#) c.gABC(le, opcWrDeref, dest, 0, value) c.freeTemp(dest) - of cgnkGlobal: + of nkSym: + if le.sym.isGlobal: let dest = genSymAddr(c, le) c.gABC(le, opcWrDeref, dest, 0, value) c.freeTemp(dest) else: discard -proc genNew(c: var TCtx; n: CgNode) = +proc genNew(c: var TCtx; n: PNode) = # FIXME: ``opcNew`` stores the allocated ``ref`` in a newly created # ``ref`` location, so we currently have to assign it to the actual # destination after. This is inefficent; instead, ``opcNew`` # should treat the destination register as a handle to a ``ref`` # location let - arg = n.arg(0) - dest = c.genArg(n) - tmp = c.getTemp(arg.typ) + dest = c.genx(n[1]) + tmp = c.getTemp(n[1].typ) c.gABx(n, opcNew, tmp, - c.genType(arg.typ.skipTypes(abstractVar-{tyTypeDesc}))) + c.genType(n[1].typ.skipTypes(abstractVar-{tyTypeDesc}))) c.gABC(n, opcWrLoc, dest, tmp) c.freeTemp(tmp) c.freeTemp(dest) -proc genNewSeq(c: var TCtx; n: CgNode) = +proc genNewSeq(c: var TCtx; n: PNode) = # FIXME: ``opcNewSeq`` has the same problem as ``opcNew``. The instruction # should also reuse the location - let t = n.arg(0).typ.skipTypes(abstractVar-{tyTypeDesc}) + let t = n[1].typ.skipTypes(abstractVar-{tyTypeDesc}) assert t.kind == tySequence let - dest = c.genArg(n, 0) # ``seq`` argument - len = c.genArg(n, 1) # length argument - tmp = c.getTemp(n.arg(0).typ) + dest = c.genx(n[1]) # ``seq`` argument + len = c.genx(n[2]) # length argument + tmp = c.getTemp(n[1].typ) c.gABx(n, opcNewSeq, tmp, c.genType(t)) c.gABx(n, opcNewSeq, len, 0) c.gABC(n, opcWrLoc, dest, tmp) @@ -1005,44 +1043,44 @@ proc genNewSeq(c: var TCtx; n: CgNode) = c.freeTemp(len) c.freeTemp(dest) -proc genNewSeqOfCap(c: var TCtx; n: CgNode; dest: var TDest) = +proc genNewSeqOfCap(c: var TCtx; n: PNode; dest: var TDest) = let t = n.typ if dest.isUnset: dest = c.getTemp(n.typ) - let tmp = c.getTemp(n.arg(0).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(abstractVar-{tyTypeDesc}))) c.gABx(n, opcNewSeq, tmp, 0) c.freeTemp(tmp) -proc genUnaryABC(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = - let tmp = c.genArg(n, 0) +proc genUnaryABC(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = + let tmp = c.genx(n[1]) if dest.isUnset: dest = c.getTemp(n.typ) c.gABC(n, opc, dest, tmp) c.freeTemp(tmp) -proc genUnaryABI(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode; imm: BiggestInt=0) = - let tmp = c.genArg(n, 0) +proc genUnaryABI(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode; imm: BiggestInt=0) = + let tmp = c.genx(n[1]) if dest.isUnset: dest = c.getTemp(n.typ) c.gABI(n, opc, dest, tmp, imm) c.freeTemp(tmp) -proc genBinaryABC(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = +proc genBinaryABC(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = let - tmp = c.genArg(n, 0) - tmp2 = c.genArg(n, 1) + tmp = c.genx(n[1]) + tmp2 = c.genx(n[2]) if dest.isUnset: dest = c.getTemp(n.typ) c.gABC(n, opc, dest, tmp, tmp2) c.freeTemp(tmp) c.freeTemp(tmp2) -proc genBinaryABCD(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = +proc genBinaryABCD(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = let - tmp = c.genArg(n, 0) - tmp2 = c.genArg(n, 1) - tmp3 = c.genArg(n, 2) + tmp = c.genx(n[1]) + tmp2 = c.genx(n[2]) + tmp3 = c.genx(n[3]) if dest.isUnset: dest = c.getTemp(n.typ) c.gABC(n, opc, dest, tmp, tmp2) c.gABC(n, opc, tmp3) @@ -1050,7 +1088,7 @@ proc genBinaryABCD(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = c.freeTemp(tmp2) c.freeTemp(tmp3) -proc genNarrow(c: var TCtx; n: CgNode; dest: TDest) = +proc genNarrow(c: var TCtx; 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: @@ -1059,7 +1097,7 @@ proc genNarrow(c: var TCtx; n: CgNode; dest: TDest) = elif t.kind in {tyInt8..tyInt32} or (t.kind == tyInt and t.size < 8): c.gABC(n, opcNarrowS, dest, TRegister(t.size*8)) -proc genNarrowU(c: var TCtx; n: CgNode; dest: TDest) = +proc genNarrowU(c: var TCtx; 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: @@ -1067,62 +1105,71 @@ proc genNarrowU(c: var TCtx; n: CgNode; dest: TDest) = (t.kind in {tyUInt, tyInt} and t.size < 8): c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) -proc genBinaryABCnarrow(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = +proc genBinaryABCnarrow(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = genBinaryABC(c, n, dest, opc) genNarrow(c, n, dest) -proc genBinaryABCnarrowU(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = +proc genBinaryABCnarrowU(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = genBinaryABC(c, n, dest, opc) genNarrowU(c, n, dest) -proc genBinarySet(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = +proc genBinarySet(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = let - tmp = c.genArg(n, 0) - tmp2 = c.genArg(n, 1) + tmp = c.genx(n[1]) + tmp2 = c.genx(n[2]) if dest.isUnset: dest = c.getTemp(n.typ) c.gABC(n, opc, dest, tmp, tmp2) c.freeTemp(tmp) c.freeTemp(tmp2) -proc genBinaryStmt(c: var TCtx; n: CgNode; opc: TOpcode) = +proc genBinaryStmt(c: var TCtx; n: PNode; opc: TOpcode) = let - dest = c.genArg(n, 0) - tmp = c.genArg(n, 1) + 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: var TCtx; n: CgNode; opc: TOpcode) = +proc genBinaryStmtVar(c: var TCtx; n: PNode; opc: TOpcode) = + var x = n[1] + if x.kind in {nkAddr, nkHiddenAddr}: x = x[0] let - dest = c.genArg(n, 0) - tmp = c.genArg(n, 1) + dest = c.genx(x) + tmp = c.genx(n[2]) c.gABC(n, opc, dest, tmp, 0) #c.genAsgnPatch(n[1], dest) c.freeTemp(tmp) c.freeTemp(dest) -proc genParseOp(c: var TCtx; n: CgNode; dest: var TDest, +proc genParseOp(c: var TCtx; n: PNode; dest: var TDest, opc: range[opcParseExprToAst..opcParseStmtToAst]) = ## Generates the code for a ``parseExpr``/``parseStmt`` magic call if dest.isUnset: dest = c.getTemp(n.typ) + # the second parameter is a ``var`` parameter. We want to access the + # register directly, so the used addr operation is skipped (if it hasn't + # been eliminated by ``transf``) + var x = n[2] + if x.kind in {nkAddr, nkHiddenAddr}: + x = x[0] + let - in1 = c.genArg(n, 0) - in2 = c.genArg(n, 1) + in1 = c.genx(n[1]) + in2 = c.genx(x) c.gABC(n, opc, dest, in1, in2) c.freeTemp(in1) c.freeTemp(in2) -proc genVarargsABC(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = +proc genVarargsABC(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = if dest.isUnset: dest = getTemp(c, n.typ) - var x = c.getTempRange(n.numArgs, slotTempStr) - for i in 0..= low(int16) and n.intVal <= high(int16) -proc genAddSubInt(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = - if n.arg(1).kind == cgnkLiteralData and n.arg(1).data.isInt8Lit: - let tmp = c.genx(n.arg(0)) - if dest.isUnset: - dest = c.getTemp(n.typ) - c.gABI(n, succ(opc), dest, tmp, n.arg(1).data.intVal) +proc genAddSubInt(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = + if n[2].isInt8Lit: + let tmp = c.genx(n[1]) + if dest.isUnset: dest = c.getTemp(n.typ) + 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: var TCtx; n, arg: CgNode; dest: var TDest; opc=opcConv) = +proc genConv(c: var TCtx; n, arg: PNode; dest: var TDest; opc=opcConv) = let t2 = n.typ.skipTypes({tyDistinct}) let targ2 = arg.typ.skipTypes({tyDistinct}) @@ -1165,8 +1211,8 @@ proc genConv(c: var TCtx; n, arg: CgNode; dest: var TDest; opc=opcConv) = c.gABx(n, opc, 0, c.genTypeInfo(arg.typ.skipTypes({tyStatic}))) c.freeTemp(tmp) -proc genCard(c: var TCtx; n: CgNode; dest: var TDest) = - let tmp = c.genArg(n, 0) +proc genCard(c: var TCtx; n: PNode; dest: var TDest) = + let tmp = c.genx(n[1]) if dest.isUnset: dest = c.getTemp(n.typ) c.gABC(n, opcCard, dest, tmp) c.freeTemp(tmp) @@ -1177,17 +1223,17 @@ template needsRegLoad(): untyped = gfNode notin flags and fitsRegister(n.typ.skipTypes({tyVar, tyLent, tyStatic})) -proc genCastIntFloat(c: var TCtx; n: CgNode; dest: var TDest) = +proc genCastIntFloat(c: var TCtx; n: PNode; dest: var TDest) = const allowedIntegers = {tyInt..tyInt64, tyUInt..tyUInt64, tyChar} var signedIntegers = {tyInt..tyInt64} var unsignedIntegers = {tyUInt..tyUInt64, tyChar} - let src = n.source.typ.skipTypes(abstractRange)#.kind - let dst = n.typ.skipTypes(abstractRange)#.kind + 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) if src.kind in allowedIntegers and dst.kind in allowedIntegers: - let tmp = c.genx(n.source) - if dest.isUnset: dest = c.getTemp(n.typ) + let tmp = c.genx(n[1]) + if dest.isUnset: dest = c.getTemp(n[0].typ) c.gABC(n, opcAsgnInt, dest, tmp) if dstSize != sizeof(BiggestInt): # don't do anything on biggest int types if dst.kind in signedIntegers: # we need to do sign extensions @@ -1203,8 +1249,8 @@ proc genCastIntFloat(c: var TCtx; n: CgNode; dest: var TDest) = c.freeTemp(tmp) elif srcSize == dstSize and src.kind in allowedIntegers and dst.kind in {tyFloat, tyFloat32, tyFloat64}: - let tmp = c.genx(n.source) - if dest.isUnset: dest = c.getTemp(n.typ) + let tmp = c.genx(n[1]) + if dest.isUnset: dest = c.getTemp(n[0].typ) if dst.kind == tyFloat32: c.gABC(n, opcCastIntToFloat32, dest, tmp) else: @@ -1213,8 +1259,8 @@ proc genCastIntFloat(c: var TCtx; n: CgNode; dest: var TDest) = elif srcSize == dstSize and src.kind in {tyFloat, tyFloat32, tyFloat64} and dst.kind in allowedIntegers: - let tmp = c.genx(n.source) - if dest.isUnset: dest = c.getTemp(n.typ) + let tmp = c.genx(n[1]) + if dest.isUnset: dest = c.getTemp(n[0].typ) if src.kind == tyFloat32: c.gABC(n, opcCastFloatToInt32, dest, tmp) if dst.kind in unsignedIntegers: @@ -1226,21 +1272,21 @@ proc genCastIntFloat(c: var TCtx; n: CgNode; dest: var TDest) = # 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.source) - if dest.isUnset: dest = c.getTemp(n.typ) + let tmp = c.genx(n[1]) + if dest.isUnset: 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.source) - if dest.isUnset: dest = c.getTemp(n.typ) + let tmp = c.genx(n[1]) + if dest.isUnset: dest = c.getTemp(n[0].typ) c.gABC(n, opcCastIntToPtr, dest, tmp) c.gABx(n, opcSetType, dest, c.genType(dst)) c.freeTemp(tmp) elif src.kind == tyNil and dst.kind in NilableTypes: # supports casting nil literals to NilableTypes in VM # see #16024 - if dest.isUnset: dest = c.getTemp(n.typ) + if dest.isUnset: dest = c.getTemp(n[0].typ) let opcode = if fitsRegister(dst): opcLdNullReg else: opcLdNull c.gABx(n, opcode, dest, c.genType(dst)) else: @@ -1252,25 +1298,22 @@ proc genCastIntFloat(c: var TCtx; n: CgNode; dest: var TDest) = instLoc: instLoc(), typeMismatch: VmTypeMismatch(actualType: dst, formalType: src)) -proc genVoidABC(c: var TCtx, n: CgNode, dest: TDest, opcode: TOpcode) = +proc genVoidABC(c: var TCtx, n: PNode, dest: TDest, opcode: TOpcode) = unused(c, n, dest) var - tmp1 = c.genArg(n, 0) - tmp2 = c.genArg(n, 1) - tmp3 = c.genArg(n, 2) + tmp1 = c.genx(n[1]) + tmp2 = c.genx(n[2]) + tmp3 = c.genx(n[3]) c.gABC(n, opcode, tmp1, tmp2, tmp3) c.freeTemp(tmp1) c.freeTemp(tmp2) c.freeTemp(tmp3) -func isIntLiteral(n: CgNode): bool = - n.kind == cgnkLiteralData and n.data.kind in nkIntLiterals - -proc genSetElem(c: var TCtx, n: CgNode, first: int): TRegister = +proc genSetElem(c: var TCtx, n: PNode, first: int): TRegister = result = c.getTemp(n.typ) if first != 0: - if isIntLiteral(n): + if n.kind in nkIntKinds: # a literal value c.gABx(n, opcLdImmInt, result, int(n.intVal - first)) else: @@ -1283,7 +1326,7 @@ proc genSetElem(c: var TCtx, n: CgNode, first: int): TRegister = else: gen(c, n, result) -proc genSetElem(c: var TCtx, n: CgNode, typ: PType): TRegister {.inline.} = +proc genSetElem(c: var TCtx, n: PNode, typ: PType): TRegister {.inline.} = ## `typ` is the type to derive the lower bound from let t = typ.skipTypes(abstractInst) assert t.kind == tySet @@ -1304,7 +1347,7 @@ proc ldNullOpcode(t: PType): TOpcode = assert t != nil if fitsRegister(t): opcLdNullReg else: opcLdNull -proc whichAsgnOpc(n: CgNode; requiresCopy = true): TOpcode = +proc whichAsgnOpc(n: PNode; requiresCopy = true): TOpcode = case n.typ.skipTypes(abstractRange-{tyTypeDesc}).kind of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64: opcAsgnInt @@ -1315,39 +1358,34 @@ proc whichAsgnOpc(n: CgNode; requiresCopy = true): TOpcode = opcAsgnComplex #(if requiresCopy: opcAsgnComplex else: opcFastAsgnComplex) -proc genTypeLit(c: var TCtx; t: PType; dest: var TDest) = - let n = newNodeIT(nkType, unknownLineInfo, t) - genLit(c, nil, c.toNodeCnst(n), dest) - -proc genMagic(c: var TCtx; n: CgNode; dest: var TDest; m: TMagic) = +proc genMagic(c: var TCtx; n: PNode; dest: var TDest; m: TMagic) = case m + of mAnd: c.genAndOr(n, opcFJmp, dest) + of mOr: c.genAndOr(n, opcTJmp, dest) of mPred, mSubI: c.genAddSubInt(n, dest, opcSubInt) of mSucc, mAddI: c.genAddSubInt(n, dest, opcAddInt) of mInc, mDec: unused(c, n, dest) - let - target = n.arg(0) - value = n.arg(1) - let isUnsigned = target.typ.skipTypes(abstractVarRange).kind in {tyUInt..tyUInt64} + 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(target) - if value.kind == cgnkLiteralData and value.data.isInt8Lit and not isUnsigned: - c.gABI(n, succ(opc), d, d, value.intVal) + 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(value) + let tmp = c.genx(n[2]) c.gABC(n, opc, d, d, tmp) c.freeTemp(tmp) - c.genNarrow(target, d) - c.genAsgnPatch(target, d) + c.genNarrow(n[1], d) + c.genAsgnPatch(n[1], d) c.freeTemp(d) - of mOrd, mChr: c.gen(n.arg(0), dest) + of mOrd, mChr: c.gen(n[1], dest) of mArrToSeq: - let temp = c.genArg(n, 0) + let temp = c.genx(n[1]) let L = c.getTemp(c.graph.getSysType(n.info, tyInt)) c.gABC(n, opcLenSeq, L, temp) let t = n.typ.skipTypes(abstractVar-{tyTypeDesc}) @@ -1374,8 +1412,8 @@ proc genMagic(c: var TCtx; n: CgNode; dest: var TDest; m: TMagic) = of mNewStringOfCap: # we ignore the 'cap' argument and translate it as 'newString(0)'. # eval n[1] for possible side effects: - c.freeTemp(c.genx(n.arg(0))) - var tmp = c.getTemp(n.arg(0).typ) + c.freeTemp(c.genx(n[1])) + var tmp = c.getTemp(n[1].typ) c.gABx(n, opcLdImmInt, tmp, 0) if dest.isUnset: dest = c.getTemp(n.typ) c.gABC(n, opcNewStr, dest, tmp) @@ -1384,7 +1422,7 @@ proc genMagic(c: var TCtx; n: CgNode; dest: var TDest; m: TMagic) = of mLengthOpenArray, mLengthArray, mLengthSeq: genUnaryABI(c, n, dest, opcLenSeq) of mLengthStr: - let t = n.arg(0).typ.skipTypes(abstractInst) + let t = n[1].typ.skipTypes(abstractInst) case t.kind of tyString: genUnaryABI(c, n, dest, opcLenStr) of tyCstring: genUnaryABI(c, n, dest, opcLenCstring) @@ -1392,8 +1430,8 @@ proc genMagic(c: var TCtx; n: CgNode; dest: var TDest; m: TMagic) = of mIncl, mExcl: unused(c, n, dest) let - d = c.genx(n.arg(0)) - tmp = c.genSetElem(n.arg(1), n.arg(0).typ) + d = c.genx(n[1]) + tmp = c.genSetElem(n[2], n[1].typ) c.gABC(n, if m == mIncl: opcIncl else: opcExcl, d, tmp) c.freeTemp(d) c.freeTemp(tmp) @@ -1408,9 +1446,9 @@ proc genMagic(c: var TCtx; n: CgNode; dest: var TDest; m: TMagic) = of mShrI: # 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.arg(0)) + let tmp = c.genx(n[1]) c.genNarrowU(n, tmp) - let tmp2 = c.genx(n.arg(1)) + let tmp2 = c.genx(n[2]) if dest.isUnset: dest = c.getTemp(n.typ) c.gABC(n, opcShrInt, dest, tmp, tmp2) c.freeTemp(tmp) @@ -1451,7 +1489,7 @@ proc genMagic(c: var TCtx; n: CgNode; dest: var TDest; m: TMagic) = genUnaryABC(c, n, dest, opcUnaryMinusInt) genNarrow(c, n, dest) of mUnaryMinusF64: genUnaryABC(c, n, dest, opcUnaryMinusFloat) - of mUnaryPlusI, mUnaryPlusF64: gen(c, n.arg(0), dest) + of mUnaryPlusI, mUnaryPlusF64: gen(c, n[1], dest) of mBitnotI: genUnaryABC(c, n, dest, opcBitnotInt) #genNarrowU modified, do not narrow signed types @@ -1459,7 +1497,7 @@ proc genMagic(c: var TCtx; n: CgNode; dest: var TDest; m: TMagic) = if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and t.size < 8): c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr: - genConv(c, n, n.arg(0), dest) + genConv(c, n, n[1], dest) of mEqStr, mEqCString: genBinaryABC(c, n, dest, opcEqStr) of mLeStr: genBinaryABC(c, n, dest, opcLeStr) of mLtStr: genBinaryABC(c, n, dest, opcLtStr) @@ -1472,103 +1510,99 @@ proc genMagic(c: var TCtx; n: CgNode; dest: var TDest; m: TMagic) = of mConStrStr: genVarargsABC(c, n, dest, opcConcatStr) of mInSet: let - tmp = c.genx(n.arg(0)) - tmp2 = c.genSetElem(n.arg(1), n.arg(0).typ) + tmp = c.genx(n[1]) + tmp2 = c.genSetElem(n[2], n[1].typ) if dest.isUnset: dest = c.getTemp(n.typ) c.gABC(n, opcContainsSet, dest, tmp, tmp2) c.freeTemp(tmp) c.freeTemp(tmp2) of mRepr: - let tmp = c.genx(n.arg(0)) + let tmp = c.genx(n[1]) if dest.isUnset: dest = c.getTemp(n.typ) - c.gABx(n, opcRepr, dest, c.genTypeInfo(n.arg(0).typ)) + c.gABx(n, opcRepr, dest, c.genTypeInfo(n[1].typ)) c.gABC(n, opcRepr, dest, tmp) c.freeTemp(tmp) of mExit: unused(c, n, dest) - var tmp = c.genArg(n, 0) + var tmp = c.genx(n[1]) c.gABC(n, opcQuit, tmp) c.freeTemp(tmp) of mSetLengthStr, mSetLengthSeq: unused(c, n, dest) - var d = c.genArg(n, 0) - var tmp = c.genArg(n, 1) + var d = c.genx(n[1]) + var tmp = c.genx(n[2]) c.gABC(n, if m == mSetLengthStr: opcSetLenStr else: opcSetLenSeq, d, tmp) c.freeTemp(tmp) c.freeTemp(d) of mSwap: unused(c, n, dest) - let - a = n.arg(0) - b = n.arg(1) - # TODO: rework once the address rework is merged. It's super inefficient - let tmp = c.getTemp(a.typ) - let tmp1 = c.genx(a) - let tmp2 = c.genx(b) - c.gABC(n, opcFastAsgnComplex, tmp, tmp1) - c.genAsgnPatch(a, tmp2) - c.genAsgnPatch(b, tmp) - c.freeTemp(tmp2) - c.freeTemp(tmp1) - c.freeTemp(tmp) + 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 mParseBiggestFloat: if dest.isUnset: dest = c.getTemp(n.typ) + var d2: TRegister + # skip 'nkHiddenAddr': + 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.genArg(n, 0) - tmp2 = c.genArg(n, 1) - tmp3 = c.genArg(n, 2) - c.gABC(n, opcParseFloat, dest, tmp1, tmp2) + tmp1 = c.genx(n[1]) + tmp3 = c.genx(n[3]) + c.gABC(n, opcParseFloat, dest, tmp1, d2) c.gABC(n, opcParseFloat, tmp3) c.freeTemp(tmp1) - c.freeTemp(tmp2) c.freeTemp(tmp3) + c.genAsgnPatch(d2AsNode, d2) + c.freeTemp(d2) of mReset: unused(c, n, dest) - var d = c.genArg(n, 1) + var d = c.genx(n[1]) # XXX use ldNullOpcode() here? - c.gABx(n, opcLdNull, d, c.genType(n.arg(0).typ)) + c.gABx(n, opcLdNull, d, c.genType(n[1].typ)) c.gABx(n, opcNodeToReg, d, d) - c.genAsgnPatch(n.arg(0), d) + c.genAsgnPatch(n[1], d) of mDefault: if dest.isUnset: dest = c.getTemp(n.typ) c.gABx(n, ldNullOpcode(n.typ), dest, c.genType(n.typ)) of mOf: if dest.isUnset: dest = c.getTemp(n.typ) - let t1 = n.arg(0).typ.skipTypes(abstractRange) + let t1 = n[1].typ.skipTypes(abstractRange) if t1.kind != tyRef: # XXX: the spec for `of` with non-ref types is missing, so we simply # treat it as an `is` for now. If it's decided that this is the # correct behaviour, the `of` should be eliminated in `sem` instead # of down here - c.gABx(n, opcLdImmInt, dest, ord(sameType(t1, n.arg(1).typ))) + c.gABx(n, opcLdImmInt, dest, ord(sameType(t1, n[2].typ))) else: - var tmp = c.genArg(n, 0) + var tmp = c.genx(n[1]) var idx = c.getTemp(getSysType(c.graph, n.info, tyInt)) - let typ = n.arg(1).typ.skipTypes(abstractPtrs) + let typ = n[2].typ.skipTypes(abstractPtrs) c.gABx(n, opcLdImmInt, idx, c.genType(typ)) c.gABC(n, opcOf, dest, tmp, idx) c.freeTemp(tmp) c.freeTemp(idx) of mHigh: if dest.isUnset: dest = c.getTemp(n.typ) - let tmp = c.genArg(n, 0) - case n.arg(0).typ.skipTypes(abstractVar-{tyTypeDesc}).kind: + 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.arg(0) + let n = n[1].skipConv + if n.kind == nkBracket: # can happen for nim check, see bug #9609 - let x = c.getTempRange(n.numArgs, slotTempUnknown) - for i, it in arguments(n): - var r: TRegister = x+i - c.gen(it, r) - c.gABC(n, opcEcho, x, n.numArgs) - c.freeTempRange(x, n.numArgs) + let x = c.getTempRange(n.len, slotTempUnknown) + for i in 0..= 0 gABC(c, ri, whichAsgnOpc(ri, requiresCopy), dest, tmp) @@ -1748,8 +1790,9 @@ func setSlot(c: var TCtx; v: PSym): TRegister {.discardable.} = result = getFreeRegister(c, if v.kind == skLet: slotFixedLet else: slotFixedVar, start = 1) c.prc.locals[v.id] = result -func cannotEval(c: TCtx; n: CgNode) {.noinline, noreturn.} = - raiseVmGenError(vmGenDiagCannotEvaluateAtComptime, n.origin) +func cannotEval(c: TCtx; n: PNode) {.noinline, noreturn.} = + raiseVmGenError(vmGenDiagCannotEvaluateAtComptime, n) + func getOwner(c: TCtx): PSym = result = c.prc.sym @@ -1760,7 +1803,7 @@ proc importcCondVar*(s: PSym): bool {.inline.} = if sfImportc in s.flags: return s.kind in {skVar, skLet, skConst} -proc checkCanEval(c: TCtx; n: CgNode) = +proc checkCanEval(c: TCtx; n: PNode) = # we need to ensure that we don't evaluate 'x' here: # proc foo() = var x ... let s = n.sym @@ -1781,7 +1824,7 @@ proc checkCanEval(c: TCtx; n: CgNode) = cannotEval(c, n) -proc genDiscrVal(c: var TCtx, discr: PSym, n: CgNode, oty: PType): TRegister = +proc genDiscrVal(c: var TCtx, discr, n: PNode, oty: PType): TRegister = ## Generate the code for preparing and loading the discriminator value ## as expected by the execution engine @@ -1792,19 +1835,20 @@ proc genDiscrVal(c: var TCtx, discr: PSym, n: CgNode, oty: PType): TRegister = let (o, idx) = getFieldAndOwner( c.getOrCreate(oty), - fpos(discr.position)) + fpos(discr.sym.position)) o.fieldAt(idx).typ - let recCase = findRecCase(oty, discr) + let recCase = findRecCase(oty, discr.sym) assert recCase != nil - if n.kind == cgnkLiteralData: + if n.kind in nkCharLit..nkUInt64Lit: # Discriminator value is known at compile-time - let b = findMatchingBranch(recCase, n.data) + + let b = findMatchingBranch(recCase, n) assert b != -1 # no matching branch; should have been caught already - assert n.data.intVal <= (1 shl discrTyp.numBits) - 1 - let v = bitor(b shl discrTyp.numBits, int(n.data.intVal)) + assert n.intVal <= (1 shl discrTyp.numBits) - 1 + let v = bitor(b shl discrTyp.numBits, int(n.intVal)) result = c.getTemp(n.typ) var tmp = TDest(result) @@ -1831,12 +1875,12 @@ proc genDiscrVal(c: var TCtx, discr: PSym, n: CgNode, oty: PType): TRegister = c.gABx(n, opcLdImmInt, bIReg, bI) else: # of branch - let b = genBranchLit(c, branch) - c.gABx(branch.info, opcBranch, tmp, b) - let elsePos = c.xjmp(branch.lastSon.info, opcFJmp, tmp) + let b = genBranchLit(c, branch, dt) + c.gABx(branch, opcBranch, tmp, b) + let elsePos = c.xjmp(branch.lastSon, opcFJmp, tmp) c.gABx(n, opcLdImmInt, bIReg, bI) if i < recCase.len-1: - endings.add(c.xjmp(branch.lastSon.info, opcJmp, 0)) + endings.add(c.xjmp(branch.lastSon, opcJmp, 0)) c.patch(elsePos) for endPos in endings: c.patch(endPos) @@ -1853,69 +1897,68 @@ proc genDiscrVal(c: var TCtx, discr: PSym, n: CgNode, oty: PType): TRegister = c.freeTemp(bIReg) -proc genFieldAsgn(c: var TCtx, obj: TRegister; le, ri: CgNode) = - c.config.internalAssert(le.kind == cgnkObjAccess) +proc genFieldAsgn(c: var TCtx, obj: TRegister; le, ri: PNode) = + c.config.internalAssert(le.kind == nkDotExpr) - let - idx = c.genField(le.field, le.info) - tmp = c.genx(ri) + let idx = c.genField(le[1]) + let s = le[1].sym - c.gABC(le, opcWrObj, obj, idx, tmp) - c.freeTemp(tmp) + var tmp: TRegister -proc genBranchSwitch(c: var TCtx, le, ri: CgNode)= + if sfDiscriminant notin s.flags: + tmp = c.genx(ri) + c.gABC(le, opcWrObj, obj, idx, tmp) + else: # Can't use `s.owner.typ` since it may be a `tyGenericBody` - let - obj = c.genx(le.source) - idx = c.genField(le.field, le.info) - tmp = c.genDiscrVal(le.field, ri, le.typ) + tmp = c.genDiscrVal(le[1], ri, le[0].typ) c.gABC(le, opcSetDisc, obj, idx, tmp) - c.freeTemp(tmp) -proc genAsgn(c: var TCtx; le, ri: CgNode) = + c.freeTemp(tmp) + +proc genAsgn(c: var TCtx; le, ri: PNode; requiresCopy: bool) = case le.kind - of cgnkArrayAccess: - let typ = le.source.typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind - let dest = c.genx(le.source, {gfNode}) - let idx = c.genIndex(le.index, le.source.typ) + of nkBracketExpr: + let typ = le[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind + let dest = c.genx(le[0], {gfNode}) + let idx = if typ != tyTuple: c.genIndex(le[1], le[0].typ) else: le[1].intVal let tmp = c.genx(ri) let opc = if typ in {tyString, tyCstring}: opcWrStrIdx + elif typ == tyTuple: opcWrObj else: opcWrArr c.gABC(le, opc, dest, idx, tmp) c.freeTemp(tmp) - c.freeTemp(idx) - c.freeTemp(dest) - of cgnkTupleAccess: - let dest = c.genx(le.source, {gfNode}) - let tmp = c.genx(ri) - c.gABC(le, opcWrObj, dest, le.fieldIndex, tmp) + if typ != tyTuple: + c.freeTemp(idx) c.freeTemp(dest) - c.freeTemp(tmp) - of cgnkObjAccess: - let dest = c.genx(le.source, {gfNode}) + of nkCheckedFieldExpr: + let objR = genCheckedObjAccessAux(c, le) + c.genFieldAsgn(objR, le[0], ri) + # c.freeTemp(idx) # BUGFIX, see nkDotExpr + c.freeTemp(objR) + of nkDotExpr: + let dest = c.genx(le[0], {gfNode}) c.genFieldAsgn(dest, le, ri) + # c.freeTemp(idx) # BUGFIX: idx is an immediate (field position), not a register c.freeTemp(dest) - of cgnkDeref, cgnkDerefView: - let dest = c.genx(le.source, #[{gfNode}]#) + of nkDerefExpr, nkHiddenDeref: + let dest = c.genx(le[0], #[{gfNode}]#) let tmp = c.genx(ri) c.gABC(le, opcWrDeref, dest, 0, tmp) c.freeTemp(dest) c.freeTemp(tmp) - of cgnkGlobal: - let s = le.sym - checkCanEval(c, le) - let - tmp = genSymAddr(c, le) - val = c.genx(ri) - c.gABC(le, opcWrDeref, tmp, 0, val) - c.freeTemp(val) - c.freeTemp(tmp) - - of cgnkLocal, cgnkParam: - let s = le.sym - checkCanEval(c, le) + of nkSym: + let s = le.sym + checkCanEval(c, le) + if s.isGlobal: + let + tmp = genSymAddr(c, le) + val = c.genx(ri) + c.gABC(le, opcWrDeref, tmp, 0, val) + c.freeTemp(val) + c.freeTemp(tmp) + else: var dest = c.prc.local(s) c.config.internalAssert dest >= 0 @@ -1930,8 +1973,15 @@ proc genAsgn(c: var TCtx; le, ri: CgNode) = c.gABC(le, opcAsgnComplex, dest, cc) c.freeTemp(cc) else: - unreachable(le.kind) + let dest = c.genx(le, {gfNode}) + # XXX: always copy, move doesn't work yet + genAsgn(c, dest, ri, true)#requiresCopy) + c.freeTemp(dest) +proc genTypeLit(c: var TCtx; t: PType; dest: var TDest) = + var n = newNode(nkType) + n.typ = t + genLit(c, n, dest) proc importcCond*(c: TCtx; s: PSym): bool {.inline.} = ## return true to importc `s`, false to execute its body instead (refs #8405) @@ -1939,7 +1989,7 @@ proc importcCond*(c: TCtx; s: PSym): bool {.inline.} = if s.kind in routineKinds: return getBody(c.graph, s).kind == nkEmpty -func local(c: TCtx, n: CgNode): TRegister = +func local(c: TCtx, n: PNode): TRegister = ## Looks up and returns the register that stores the local named by symbol ## node `n`. let local = c.prc.local(n.sym) @@ -1958,7 +2008,7 @@ func local(c: TCtx, n: CgNode): TRegister = # it cannotEval(c, n) -proc useGlobal(c: var TCtx, n: CgNode): int = +proc useGlobal(c: var TCtx, n: PNode): int = ## Resolves the global identified by symbol node `n` to the ID that ## identifies it at run-time. If using the global is illegal (because ## it's an importc'ed variable, for example), an error is raised. @@ -1983,16 +2033,16 @@ proc useGlobal(c: var TCtx, n: CgNode): int = # a global that is not accessible in the current context cannotEval(c, n) -proc genLocation(c: var TCtx; n: CgNode; dest: var TDest; flags: TGenFlags) = +proc genSym(c: var TCtx; n: PNode; dest: var TDest; flags: TGenFlags) = ## Generates and emits the code for loading either the value or handle of - ## the location identified by name node `n` into the `dest` register. - case n.kind - of cgnkGlobal: + ## the location named by symbol node `n` into the `dest` register. + let s = n.sym + if s.isGlobal: let pos = useGlobal(c, n) if dest.isUnset: - dest = c.getTemp(n.typ) + dest = c.getTemp(s.typ) - if fitsRegister(n.typ) and gfNode notin flags: + if fitsRegister(s.typ) and gfNode notin flags: let cc = c.getTemp(n.typ) c.gABx(n, opcLdGlobal, cc, pos) c.genRegLoad(n, dest, cc) @@ -2009,34 +2059,30 @@ proc genLocation(c: var TCtx; n: CgNode; dest: var TDest; flags: TGenFlags) = # register copy, which is exactly what we need here c.gABC(n, opcFastAsgnComplex, dest, local) -proc genSymAddr(c: var TCtx, n: CgNode, dest: var TDest) = +proc genSymAddr(c: var TCtx, n: PNode, dest: var TDest) = ## Generates and emits the code for taking the address of the location ## identified by the symbol node `n`. + let s = n.sym if dest.isUnset: - dest = c.getTemp(n.typ) + dest = c.getTemp(s.typ) - case n.kind - of cgnkGlobal: + if s.isGlobal: let pos = useGlobal(c, n) c.gABx(n, opcLdGlobalAddr, dest, pos) - of cgnkLocal, cgnkParam, cgnkTemp: + else: let local = local(c, n) c.gABC(n, opcAddrReg, dest, local) - else: - unreachable(n.kind) -proc genSymAddr(c: var TCtx, n: CgNode): TRegister = +proc genSymAddr(c: var TCtx, n: PNode): TRegister = var dest = TDest(-1) genSymAddr(c, n, dest) result = dest -proc genArrAccessOpcode(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode; +proc genArrAccessOpcode(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode; flags: TGenFlags) = - let a = c.genx(n.source, {gfNode}) - let b = c.genIndex(n.index, n.source.typ) - if dest.isUnset: - dest = c.getTemp(n.typ) - + let a = c.genx(n[0], {gfNode}) + let b = c.genIndex(n[1], n[0].typ) + if dest.isUnset: dest = c.getTemp(n.typ) if opc in {opcLdArrAddr, opcLdStrIdxAddr}: c.gABC(n, opc, dest, a, b) elif opc == opcLdStrIdx: @@ -2054,7 +2100,7 @@ proc genArrAccessOpcode(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode; c.freeTemp(a) c.freeTemp(b) -proc genFieldAccessAux(c: var TCtx; n: CgNode; a, b: TRegister, dest: var TDest; flags: TGenFlags) = +proc genFieldAccessAux(c: var TCtx; n: PNode; a, b: TRegister, dest: var TDest; flags: TGenFlags) = ## Emits the code for loading either the value or handle of an ## ``object``/``tuple`` field into the `dest` register. The `a` register ## holds the handle to the object location, while `b` is an immedate value @@ -2069,62 +2115,66 @@ proc genFieldAccessAux(c: var TCtx; n: CgNode; a, b: TRegister, dest: var TDest; c.gABC(n, opcLdObj, dest, a, b) c.makeHandleReg(dest, a) -proc genFieldAccess(c: var TCtx; n: CgNode; dest: var TDest; flags: TGenFlags) = +proc genFieldAccess(c: var TCtx; n: PNode; dest: var TDest; flags: TGenFlags) = ## Generates and emits the code for a dot-expression `n` (i.e. field access). ## The resulting value/handle is stored to `dest`. - assert n.kind == cgnkObjAccess + assert n.kind == nkDotExpr let - a = c.genx(n.source, {gfNode}) - b = genField(c, n.field, n.info) + a = c.genx(n[0], {gfNode}) + b = genField(c, n[1]) genFieldAccessAux(c, n, a, b, dest, flags) c.freeTemp(a) -proc genFieldAddr(c: var TCtx, n, obj: CgNode, fieldPos: int, dest: TDest) = +proc genFieldAddr(c: var TCtx, n, obj: PNode, fieldPos: int, dest: TDest) = let obj = c.genx(obj) c.gABC(n, opcLdObjAddr, dest, obj, fieldPos) c.freeTemp(obj) -proc genBranchAccess(c: var TCtx; n: CgNode): TRegister = - assert n.kind == cgnkVariantAccess +proc genCheckedObjAccessAux(c: var TCtx; n: PNode): TRegister = + internalAssert( + c.config, + n.kind == nkCheckedFieldExpr, + "genCheckedObjAccessAux requires checked field node") + # nkDotExpr to access the requested field + let accessExpr = n[0] # nkCall to check if the discriminant is valid - var checkExpr = n.checkExpr + var checkExpr = n[1] - let negCheck = checkExpr.callee.sym.magic == mNot + let negCheck = checkExpr[0].sym.magic == mNot if negCheck: - checkExpr = checkExpr.arg(0) + checkExpr = checkExpr[^1] # Discriminant symbol - let disc = n.discriminant + let disc = checkExpr[2] internalAssert( - c.config, disc.kind == skField, "Discriminant symbol must be a field") - - # TODO: the logic here is much too complex. This kind of lowering has to - # happen *before* code generation, not during it + c.config, disc.sym.kind == skField, "Discriminant symbol must be a field") # Load the object in `dest` - result = c.genx(n.source, {gfNode}) + result = c.genx(accessExpr[0], {gfNode}) # Load the discriminant var discVal = c.getTemp(disc.typ) var discValTemp = c.getTemp(disc.typ) - c.gABC(n, opcLdObj, discValTemp, result, genField(c, disc, n.info)) + c.gABC(n, opcLdObj, discValTemp, result, genField(c, disc)) c.gABC(n, opcNodeToReg, discVal, discValTemp) c.freeTemp(discValTemp) # Check if its value is contained in the supplied set - let setLit = c.genArg(checkExpr, 0) + 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.info, if negCheck: opcFJmp else: opcTJmp, rs) + 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) var discrStrReg: TDest = c.getTemp(strType) - let fieldName = n.field.name.s - let msg = genFieldDefect(c.config, fieldName, disc) - c.genLit(n, c.toStringCnst(msg), msgReg) + 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) # repr for discriminator value c.gABx(n, opcRepr, discrStrReg, c.genTypeInfo(disc.typ)) c.gABC(n, opcRepr, discrStrReg, discVal) @@ -2134,15 +2184,23 @@ proc genBranchAccess(c: var TCtx; n: CgNode): TRegister = c.freeTemp(discrStrReg) c.patch(lab1) -proc genCheckedObjAccess(c: var TCtx; n: CgNode; dest: var TDest; flags: TGenFlags) = - let objR = genBranchAccess(c, n) +proc genCheckedObjAccess(c: var TCtx; n: PNode; dest: var TDest; flags: TGenFlags) = + let objR = genCheckedObjAccessAux(c, n) + + let accessExpr = n[0] + # Field symbol + var field = accessExpr[1] + internalAssert( + c.config, + field.sym.kind == skField, + "Access expression must be a field, but found " & $field.sym.kind) # Load the content now if dest.isUnset: dest = c.getTemp(n.typ) - let fieldPos = genField(c, n.field, n.info) + let fieldPos = genField(c, field) if needsRegLoad(): - var cc = c.getTemp(n.typ) + var cc = c.getTemp(accessExpr.typ) c.gABC(n, opcLdObj, cc, objR, fieldPos) c.genRegLoad(n, dest, cc) c.freeTemp(cc) @@ -2152,21 +2210,27 @@ proc genCheckedObjAccess(c: var TCtx; n: CgNode; dest: var TDest; flags: TGenFla c.freeTemp(objR) -proc genArrAccess(c: var TCtx; n: CgNode; dest: var TDest; flags: TGenFlags) = - let arrayType = n.source.typ.skipTypes(abstractVar).kind +proc genArrAccess(c: var TCtx; n: PNode; dest: var TDest; flags: TGenFlags) = + let arrayType = n[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind case arrayType of tyString, tyCstring: genArrAccessOpcode(c, n, dest, opcLdStrIdx, flags) + of tyTuple: + let a = genx(c, n[0], {gfNode}) + genFieldAccessAux(c, n, a, n[1].intVal, dest, flags) + c.freeTemp(a) of tyArray, tySequence, tyOpenArray, tyVarargs, tyUncheckedArray: genArrAccessOpcode(c, n, dest, opcLdArr, flags) else: unreachable() -proc genArrayAddr(c: var TCtx, n: CgNode, dest: var TDest) = - ## Generates the code for loading the address of an array-like-access - ## expression (i.e. ``addr x[0]``) +proc genBracketAddr(c: var TCtx, n: PNode, dest: var TDest) = + ## Generates the code for loading the address of a bracket expression (i.e. + ## ``addr x[0]``) assert not dest.isUnset - case n.source.typ.skipTypes(abstractVar).kind + case n[0].typ.skipTypes(abstractInst-{tyTypeDesc}).kind + of tyTuple: + genFieldAddr(c, n, n[0], n[1].intVal.int, dest) of tyString, tyCstring: genArrAccessOpcode(c, n, dest, opcLdStrIdxAddr, {}) of tyArray, tySequence, tyOpenArray, tyVarargs: @@ -2174,130 +2238,168 @@ proc genArrayAddr(c: var TCtx, n: CgNode, dest: var TDest) = else: unreachable() -proc genAddr(c: var TCtx, src, n: CgNode, dest: var TDest) = +proc genAddr(c: var TCtx, src, n: PNode, dest: var TDest) = ## Generates and emits the code for taking the address of lvalue expression ## `n`. `src` provides the line information to use for the emitted ## address-of instruction. case n.kind - of cgnkGlobal, cgnkParam, cgnkLocal: + of nkSym: prepare(c, dest, src.typ) genSymAddr(c, n, dest) - of cgnkObjAccess: + of nkDotExpr: prepare(c, dest, src.typ) - genFieldAddr(c, n, n.source, genField(c, n.field, n.info), dest) - of cgnkTupleAccess: + genFieldAddr(c, n, n[0], genField(c, n[1]), dest) + of nkBracketExpr: prepare(c, dest, src.typ) - genFieldAddr(c, n, n.source, n.fieldIndex, dest) - of cgnkArrayAccess: + genBracketAddr(c, n, dest) + of nkCheckedFieldExpr: prepare(c, dest, src.typ) - genArrayAddr(c, n, dest) - of cgnkDerefView: + + let obj = genCheckedObjAccessAux(c, n) + c.gABC(src, opcLdObjAddr, dest, obj, genField(c, n[0][1])) + c.freeTemp(obj) + of nkHiddenDeref: # a dereference of a view. Since the register stores either a handle, # an address, or a register address, we can safely treat the ``addr`` # operation as a no-op # NOTE: because of ``rkRegAddr``, this is currently not only an # optimization, but necessary - gen(c, n.source, dest) - of cgnkDeref: + gen(c, n[0], dest) + of nkDerefExpr: prepare(c, dest, src.typ) var tmp = TDest(-1) genDeref(c, n, tmp, {gfNode}) c.gABC(src, opcAddrReg, dest, tmp) c.freeTemp(tmp) - of cgnkLConv, cgnkObjUpConv, cgnkObjDownConv: + of nkConv: # an l-value conversion. Apply the operation to the source expression - genAddr(c, src, n.source, dest) + genAddr(c, src, n[1], dest) + of nkObjDownConv, nkObjUpConv: + genAddr(c, src, n[0], dest) + of nkStmtListExpr: + for i in 0.. 0: + if n.len > 0: var tmp = getTemp(c, intType) c.gABx(n, opcLdNullReg, tmp, c.genType(intType)) - for i, x in arguments(n): + for x in n: let a = c.genx(x) c.gABC(n, opcWrArr, dest, tmp, a) c.gABI(n, opcAddImmInt, tmp, tmp, 1) c.freeTemp(a) c.freeTemp(tmp) -proc genSetConstr(c: var TCtx, n: CgNode, dest: var TDest) = +proc genSetConstr(c: var TCtx, n: PNode, dest: var TDest) = if dest.isUnset: dest = c.getTemp(n.typ) c.gABx(n, opcLdNull, dest, c.genType(n.typ)) # XXX: since `first` stays the same across the loop, we could invert # the loop around `genSetElem`'s logic... let first = firstOrd(c.config, n.typ.skipTypes(abstractInst)).toInt() - for i, x in arguments(n): - if x.kind == cgnkRange: - let a = c.genSetElem(x.arg(0), first) - let b = c.genSetElem(x.arg(1), first) + for x in n: + if x.kind == nkRange: + let a = c.genSetElem(x[0], first) + let b = c.genSetElem(x[1], first) c.gABC(n, opcInclRange, dest, a, b) c.freeTemp(b) c.freeTemp(a) @@ -2306,117 +2408,171 @@ proc genSetConstr(c: var TCtx, n: CgNode, dest: var TDest) = c.gABC(n, opcIncl, dest, a) c.freeTemp(a) -proc genRecordConstrAux(c: var TCtx, n: CgNode, dest: TRegister) - -proc genRecordConstr(c: var TCtx, n: CgNode, dest: var TDest) = - if dest.isUnset: - dest = c.getTemp(n.typ) - - c.gABx(n, opcLdNull, dest, c.genType(n.typ)) - genRecordConstrAux(c, n, dest) - -proc genRefConstr(c: var TCtx, n: CgNode, dest: var TDest) = - if dest.isUnset: - dest = c.getTemp(n.typ) - - let t = n.typ.skipTypes(abstractInst) - let tmp = c.getTemp(t[0]) # The temporary register to hold the - # dereferenced location - c.gABx(n, opcNew, dest, c.genType(t)) - c.gABC(n, opcLdDeref, tmp, dest) - genRecordConstrAux(c, n, tmp) - c.freeTemp(tmp) - -proc genRecordConstrAux(c: var TCtx, n: CgNode, dest: TRegister) = - for i, it in arguments(n): - let idx = genField(c, it.field, it.info) +proc genObjConstr(c: var TCtx, n: PNode, dest: var TDest) = + if dest.isUnset: dest = c.getTemp(n.typ) + let t = n.typ.skipTypes(abstractRange-{tyTypeDesc}) + var refTemp: TDest + if t.kind == tyRef: + refTemp = c.getTemp(t[0]) # The temporary register to hold the + # dereferenced location + c.gABx(n, opcNew, dest, c.genType(t)) + c.gABC(n, opcLdDeref, refTemp, dest) + swap(refTemp, dest) + else: + c.gABx(n, opcLdNull, dest, c.genType(n.typ)) + for i in 1..= low(int16) and s.position <= high(int16): + c.gABx(n, opcLdImmInt, dest, s.position) + else: + var lit = genLiteral(c, newIntNode(nkIntLit, s.position)) + c.gABx(n, opcLdConst, dest, lit) + of skGenericParam: + if c.getOwner().kind == skMacro: + genSym(c, n, dest, flags) + else: + # note: this can't be replaced with an assert. ``tryConstExpr`` is + # sometimes used to check whether an expression can be evaluated + # at compile-time, in which case we need to report an error when + # encountering an unresolved generic parameter + fail(n.info, vmGenDiagCannotGenerateCode, n) else: - let idx = c.registerConst(s) - discard c.getOrCreate(s.typ) - c.gABx(n, opcLdCmplxConst, dest, idx) - of cgnkParam: - if c.getOwner().kind == skMacro: - genLocation(c, n, dest, flags) + fail(n.info, + vmGenDiagCodeGenUnexpectedSym, + sym = s + ) + of nkCallKinds: + if n[0].kind == nkSym: + let s = n[0].sym + if s.magic != mNone: + genMagic(c, n, dest, s.magic) + elif s.kind == skMethod: + fail(n.info, vmGenDiagCannotCallMethod, sym = s) + else: + genCall(c, n, dest) + clearDest(c, n, dest) else: - # note: this can't be replaced with an assert. ``tryConstExpr`` is - # sometimes used to check whether an expression can be evaluated - # at compile-time, in which case we need to report an error when - # encountering an unresolved generic parameter - fail(n.info, vmGenDiagCannotGenerateCode, n.origin) - of cgnkCall: - genCall(c, n, dest) - of cgnkLiteralData: - if isInt16Lit(n.data): + genCall(c, n, dest) + clearDest(c, n, dest) + of nkCharLit..nkInt64Lit: + if isInt16Lit(n): if dest.isUnset: dest = c.getTemp(n.typ) c.gABx(n, opcLdImmInt, dest, n.intVal.int) else: genLit(c, n, dest) - of cgnkNilLit: - if dest.isUnset: dest = c.getTemp(n.typ) - c.gABx(n, ldNullOpcode(n.typ), dest, c.genType(n.typ)) - of cgnkNimNodeLit: + of nkUIntLit..pred(nkNilLit): genLit(c, n, dest) + of nkNilLit: + if not n.typ.isEmptyType: + let t = n.typ.skipTypes(abstractInst) + internalAssert(c.config, + t.kind in {tyPtr, tyRef, tyPointer, tyNil, tyProc, tyCstring}, + n.info, + $t.kind) + if dest.isUnset: dest = c.getTemp(t) + c.gABx(n, ldNullOpcode(t), dest, c.genType(n.typ)) + else: unused(c, n, dest) + of nkNimNodeLit: # the VM does not copy the tree when loading a ``PNode`` constant (which # is correct). ``NimNode``s not marked with `nfSem` can be freely modified # inside macros, so in order to prevent mutations of the AST part of the @@ -2426,25 +2582,66 @@ proc gen(c: var TCtx; n: CgNode; dest: var TDest; flags: TGenFlags = {}) = dest = c.getTemp(n.typ) var tmp: TDest = c.getTemp(n.typ) - c.genLit(n, c.toNodeCnst(n.nimNodeLit), tmp) + c.genLit(n, c.toNodeCnst(n[0]), tmp) c.gABC(n, opcNCopyNimTree, dest, tmp) freeTemp(c, tmp) - of cgnkObjAccess: genFieldAccess(c, n, dest, flags) - of cgnkVariantAccess: genCheckedObjAccess(c, n, dest, flags) - of cgnkArrayAccess: genArrAccess(c, n, dest, flags) - of cgnkDeref, cgnkDerefView: genDeref(c, n, dest, flags) - of cgnkAddr, cgnkView: genAddr(c, n, n.value, dest) - of cgnkConv, cgnkObjDownConv, cgnkObjUpConv: - genConv(c, n, n.source, dest) - of cgnkLConv: - # an l-value conversion that's not related to `ref`s or `object`s. These - # don't translate to any bytecode - c.gen(n.source, dest) - of cgnkChckRangeF, cgnkChckRange64, cgnkChckRange: + of nkAsgn, nkFastAsgn: + unused(c, n, dest) + genAsgn(c, n[0], n[1], n.kind == nkAsgn) + of nkDotExpr: genFieldAccess(c, n, dest, flags) + of nkCheckedFieldExpr: genCheckedObjAccess(c, n, dest, flags) + of nkBracketExpr: genArrAccess(c, n, dest, flags) + of nkDerefExpr, nkHiddenDeref: genDeref(c, n, dest, flags) + of nkAddr, nkHiddenAddr: genAddr(c, n, n[0], dest) + of nkIfStmt, nkIfExpr: genIf(c, n, dest) + of nkWhenStmt: + # This is "when nimvm" node. Chose the first branch. + 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: + genReturn(c, n) + of nkRaiseStmt: + genRaise(c, n) + of nkBreakStmt: + genBreak(c, n) + 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: + for i in 0.. Date: Thu, 3 Aug 2023 15:31:21 +0000 Subject: [PATCH 05/20] ccgexprs: remove node flag usage The `nfAllFieldsSet` flag stopped reaching the code generator with the introduction of the MIR, meaning that the condition always evaluates to 'true'. --- compiler/backend/ccgexprs.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/backend/ccgexprs.nim b/compiler/backend/ccgexprs.nim index 1e648b1e98a..94c7fed92e5 100644 --- a/compiler/backend/ccgexprs.nim +++ b/compiler/backend/ccgexprs.nim @@ -1137,7 +1137,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = r = rdLoc(tmp) if isRef: rawGenNew(p, tmp, "", - needsInit = nfAllFieldsSet notin e.flags, + needsInit = true, doInitObj = not hasCase) t = t.lastSon.skipTypes(abstractInst) r = "(*$1)" % [r] From e35e7f62942dfd176959e626cd436fdbc53396e0 Mon Sep 17 00:00:00 2001 From: zerbina <100542850+zerbina@users.noreply.github.com> Date: Thu, 3 Aug 2023 15:31:21 +0000 Subject: [PATCH 06/20] canonicalize the main procedure's body The C and JS code-gen orchestrators were passing the AST produced for the main procedure directly to the code generators. This is no longer going to work once the code generators don't work with `PNode` anymore, so `canonicalize` is now used on the AST. --- compiler/backend/cbackend.nim | 2 +- compiler/backend/jsbackend.nim | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/backend/cbackend.nim b/compiler/backend/cbackend.nim index 58e4a1ab716..212f6812916 100644 --- a/compiler/backend/cbackend.nim +++ b/compiler/backend/cbackend.nim @@ -326,7 +326,7 @@ proc generateCodeForMain(m: BModule, modules: ModuleList) = generateTeardown(m.g.graph, modules, body) # now generate the C code for the body: - genStmts(p, body) + genStmts(p, canonicalize(m.g.graph, m.idgen, m.module, body, {})) var code: string code.add(p.s(cpsLocals)) code.add(p.s(cpsInit)) diff --git a/compiler/backend/jsbackend.nim b/compiler/backend/jsbackend.nim index 09b870ee5c2..cead500a6c6 100644 --- a/compiler/backend/jsbackend.nim +++ b/compiler/backend/jsbackend.nim @@ -19,6 +19,9 @@ import compiler/front/[ options ], + compiler/mir/[ + mirbridge, # for `canonicalize` + ], compiler/modules/[ modulegraphs ], @@ -113,7 +116,8 @@ proc generateCodeForMain(globals: PGlobals, graph: ModuleGraph, m: BModule, generateMain(graph, modules, body) generateTeardown(graph, modules, body) - genTopLevelStmt(globals, m, body) + let owner = m.module + genTopLevelStmt(globals, m, canonicalize(graph, m.idgen, owner, body, {})) proc generateCode*(graph: ModuleGraph, mlist: sink ModuleList) = ## Entry point into the JS backend. Generates the code for all modules and From 603b51ee013c680891073474cd5b29ceeca2e6c9 Mon Sep 17 00:00:00 2001 From: zerbina <100542850+zerbina@users.noreply.github.com> Date: Thu, 3 Aug 2023 15:31:21 +0000 Subject: [PATCH 07/20] vmaux: decouple `findMatchingBranch` from `PNode` Instead of an integer-literal node, the procedure now accepts the value directly. --- compiler/vm/packed_env.nim | 2 +- compiler/vm/vmaux.nim | 13 ++++++++----- compiler/vm/vmcompilerserdes.nim | 2 +- compiler/vm/vmgen.nim | 2 +- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/compiler/vm/packed_env.nim b/compiler/vm/packed_env.nim index 270384be559..0d56014d757 100644 --- a/compiler/vm/packed_env.nim +++ b/compiler/vm/packed_env.nim @@ -276,7 +276,7 @@ func storeData*(enc: var DataEncoder, e: var PackedEnv, n: PNode) func storeDiscrData(enc: var DataEncoder, e: var PackedEnv, s: PSym, v: PNode) = let recCase = findRecCase(s.owner.typ, s) - b = findMatchingBranch(recCase, v) + b = findMatchingBranch(recCase, getInt(v)) assert b != -1 # We don't have access to vm type information here, so 32 is always # used for `numBits`. This is safe, since the both `value` and `index` diff --git a/compiler/vm/vmaux.nim b/compiler/vm/vmaux.nim index 24bb6566dbc..ba9f0c2886d 100644 --- a/compiler/vm/vmaux.nim +++ b/compiler/vm/vmaux.nim @@ -20,8 +20,6 @@ import tables ] -from compiler/ast/nimsets import overlap - # XXX: this proc was previously located in ``vmgen.nim`` func matches(s: PSym; x: IdentPattern): bool = var s = s @@ -72,16 +70,21 @@ func findRecCase*(t: PType, d: PSym): PNode = if result == nil and t.sons[0] != nil: result = findRecCase(t[0].skipTypes({tyAlias, tyGenericInst, tyRef, tyPtr}), d) -func findMatchingBranch*(recCase: PNode, lit: PNode): int = +func findMatchingBranch*(recCase: PNode, val: Int128): int = # XXX: If Option[Natural] would be stored as a single integer it could be # used as the result type here instead assert recCase.kind == nkRecCase - assert lit.kind in nkLiterals + + func overlap(val: Int128, n: PNode): bool = + if n.kind == nkRange: + getInt(n[0]) <= val and val <= getInt(n[1]) + else: + getInt(n) == val for i in 1.. Date: Thu, 3 Aug 2023 15:31:21 +0000 Subject: [PATCH 08/20] add the code-generator IR `astgen` is adjusted to produce `CgNode` instead of `PNode`. For this, multiple `PNode` -> `CgNode` translation procedures had to be introduced and `canUseView` + `flattenExpr` duplicated and adjusted for `CgNode`. The general processing logic stays the same. The module's document comment is also adjusted and an outdated mention of "sections" (they are called "regions" in the MIR) fixed. `astgen` as the name doesn't make much sense anymore and is going to be changed to something more fitting. --- compiler/backend/cgir.nim | 203 +++++++++++ compiler/mir/astgen.nim | 732 +++++++++++++++++++++++--------------- 2 files changed, 648 insertions(+), 287 deletions(-) create mode 100644 compiler/backend/cgir.nim diff --git a/compiler/backend/cgir.nim b/compiler/backend/cgir.nim new file mode 100644 index 00000000000..3e4feae280f --- /dev/null +++ b/compiler/backend/cgir.nim @@ -0,0 +1,203 @@ +## Implements an IR for representing code in the code-generators. It's +## currently a slightly adjusted version of ``PNode``, but the idea is to +## simplify and evolve it, meaning that everything here is subject to +## change. + +import + compiler/ast/[ + ast_types, + lineinfos, + wordrecg + ] + +type + CgNodeKind* = enum + cnkInvalid ## the node is uninitialized + + cnkEmpty ## represents the absence of something. The meaning depends + ## on the context + cnkType ## a literal type + + cnkIntLit + cnkUIntLit + cnkFloatLit + cnkStrLit + + cnkNilLit ## the nil literal + cnkAstLit ## a ``NimNode`` literal + + cnkSym + # future direction: split up ``cnkSym`` in the way the MIR does it + + cnkCall ## a procedure call. The first operand is the procedure, + ## the following operands the arguments + + # constructors: + cnkTupleConstr ## tuple constructor + cnkObjConstr ## object constructor + cnkSetConstr ## set constructor + cnkArrayConstr ## array constructor + cnkClosureConstr ## closure constructor + + cnkRange ## a range expression in a ``case``-branch or + + cnkBinding ## special node used in ``cnkObjConstr`` to associate a + ## field with a value + + cnkFieldAccess + cnkBracketAccess + # future direction: split ``cnkBracketAccess`` into ``cnkArrayAccess`` (for + # array-like operands) and ``cnkTupleAccess`` (for tuples). + cnkCheckedFieldAccess + # future direction: lower object access checks eariler (e.g., during the + # MIR phase) and then remove ``cnkCheckedFieldAccess`` + + cnkDeref ## dereference 'x' + cnkAddr ## address of 'x' + + cnkHiddenAddr ## create an internal reference of the operand + cnkDerefView ## dereference for a view + # future direction: introduce ``cnkBind`` (or similar) for replacing the + # ``x = hiddenAddr y`` operation + # For views, consider lowering them into pointers (for backends where it + # makes sense, e.g. C). This could make ``cnkDerefView`` obsolete + + cnkConv ## a type conversion + # future direction: introduce a dedicated operation for "l-value preserving + # conversions" + cnkHiddenConv + # future direction: the notion of "hidden" doesn't make any sense in the + # context of code generation. Adjust the code generators so that they no + # longer depend on ``cnkHiddenConv`` being different from ``cnkConv``, and + # then remove the former + + cnkObjDownConv ## down conversion between `object` or `ref` types + cnkObjUpConv ## up conversion between `object` or `ref` types + + cnkCast ## reinterpret the bit-pattern of the operand as a + ## different type + + # ---- special conversions kept for compatibility + cnkChckRangeF ## range check for floats + cnkChckRange64 ## range check for 64-bit ints + cnkChckRange ## range check for ints + cnkStringToCString ## string to cstring + cnkCStringToString ## cstring to string + # future direction: lower these coversion operations during the MIR + # phase and then remove the node kinds + # ---- end + + cnkStmtList + cnkStmtListExpr + # future direction: remove ``cnkStmtListExpr``. The code generators know + # based on the context a statement list appears in whether its an + # expression or not + + cnkVoidStmt ## discard the operand value (i.e., do nothing with it) + cnkPragmaStmt ## a single compiler directive + cnkEmitStmt ## an ``emit`` statement + cnkAsmStmt ## an ``asm`` statement + + cnkIfStmt ## only execute the body when the condition expression + ## evaluates to 'true' + cnkRepeatStmt ## execute the body indefinitely + cnkCaseStmt ## a ``case`` statement + cnkBlockStmt ## an (optionally) labeled block + + cnkBreakStmt ## break out of labeled block, or, if no label is provided, + ## the closest ``repeat`` loop + cnkRaiseStmt ## raise(x) -- set the `x` as the current exception and start + ## exceptional control-flow. `x` can be ``cnkEmpty`` in which + ## case "set current exception" part is skipped + # future direction: lower the high-level raise statements (which means + # "set the current exception" + "start exceptional control-flow") into + # just "start exceptional control-flow" + cnkReturnStmt + + cnkTryStmt + cnkExcept + cnkFinally + + cnkBranch ## the branch of a ``case`` statement + + cnkDef ## starts the lifetime of a local and optionally assigns an + ## initial value + + cnkAsgn ## a = b + cnkFastAsgn ## fast assign b to a + # future direction: have ``cnkAsgn`` mean "assign without implying any + # copying" and then remove ``cnkFastAsgn`` + +const + AllKinds = {low(CgNodeKind)..high(CgNodeKind)} + + cnkWithoutItems* = {cnkInvalid..cnkSym, cnkReturnStmt, cnkPragmaStmt} + cnkWithItems* = AllKinds - cnkWithoutItems + + cnkLiterals* = {cnkIntLit, cnkUIntLit, cnkFloatLit, cnkStrLit} + +type + CgNode* = ref object + ## Code-generator node + origin*: PNode + info*: TLineInfo + typ*: PType + case kind*: CgNodeKind + of cnkInvalid, cnkEmpty, cnkType, cnkNilLit, cnkReturnStmt: discard + of cnkIntLit, cnkUIntLit: + # future direction: use a ``BiggestUint`` for uint values + intVal*: BiggestInt + of cnkFloatLit: floatVal*: BiggestFloat + of cnkStrLit: strVal*: string + of cnkAstLit: astLit*: PNode + of cnkSym: sym*: PSym + of cnkPragmaStmt: pragma*: TSpecialWord + of cnkWithItems: + childs*: seq[CgNode] + + # future direction: move to a single-sequence-based, data-oriented design + # for the code-generator IR + +func len*(n: CgNode): int {.inline.} = + n.childs.len + +template `[]`*(n: CgNode, i: Natural): CgNode = + n.childs[i] + +template `[]`*(n: CgNode, i: BackwardsIndex): CgNode = + {.cast(noSideEffect).}: + n.childs[i] + +iterator items*(n: CgNode): CgNode = + var i = 0 + let L = n.childs.len + while i < L: + yield n.childs[i] + inc i + +iterator pairs*(n: CgNode): (int, CgNode) = + var i = 0 + let L = n.childs.len + while i < L: + yield (i, n.childs[i]) + inc i + +iterator sliceIt*[T](x: seq[T], lo, hi: Natural): (int, lent T) = + var i = int(lo) + while i <= hi: + yield (i, x[i]) + inc i + +proc newStmt*(kind: CgNodeKind, info: TLineInfo, + childs: varargs[CgNode]): CgNode = + result = CgNode(kind: kind, info: info) + result.childs = @childs + +proc newExpr*(kind: CgNodeKind, info: TLineInfo, typ: PType, + childs: varargs[CgNode]): CgNode = + result = CgNode(kind: kind, info: info, typ: typ) + result.childs = @childs + +proc newNode*(kind: CgNodeKind; info = unknownLineInfo; + typ = PType(nil)): CgNode = + CgNode(kind: kind, info: info, typ: typ) \ No newline at end of file diff --git a/compiler/mir/astgen.nim b/compiler/mir/astgen.nim index 660ece1d3de..c0e15cbc89c 100644 --- a/compiler/mir/astgen.nim +++ b/compiler/mir/astgen.nim @@ -1,8 +1,5 @@ -## This module implements the MIR -> ``PNode`` AST translation. The translation -## preserves the semantics and uses the smallest ``PNode`` constructs that best -## match the respective MIR construct. Note that not all constructs in the MIR -## have a direct ``PNode`` counterpart: those require more complex translation -## logic. +## Implements generating ``CgNode`` code from MIR code. Together with the +## ``CgNode`` IR, this module is a work-in-progress. ## ## The translation is implemented via recursive application of the correct ## translation procedure, where each procedure processes a sub-tree either @@ -11,28 +8,34 @@ ## validating that the input MIR code is grammatically correct with effectively ## no overhead and without requiring extra contextual data or a separate pass. ## -## ============ -## MIR sections -## ============ +## .. note:: +## The `tb` prefix is an abbreviation of "translate back", but with the +## introduction of the code-generator IR, this doesn't make much sense +## anymore. ## -## There exists no equivalent to MIR sections in the ``PNode`` AST, so a more -## complex translation has to be used. At the start of the section, each -## section argument is assigning to a temporary, using either a ``var`` / +## =========== +## MIR regions +## =========== +## +## There exists no equivalent to MIR regions in the ``CgNode`` IR, so a more +## complex translation has to be used. At the start of the region, each +## region argument is assigned to a temporary, using either a ``var`` / ## ``lent`` view or shallow copy depending on the argument's mode and -## type. A section parameter reference (``mnkOpParam``) is then translated to +## type. A region parameter reference (``mnkOpParam``) is then translated to ## accessing the temporary introduce for the parameter's argument. import compiler/ast/[ - ast, ast_types, ast_idgen, ast_query, idents, lineinfos, - trees, types ], + compiler/backend/[ + cgir + ], compiler/front/[ options ], @@ -45,14 +48,15 @@ import modulegraphs ], compiler/sem/[ - ast_analysis, lowerings ], compiler/utils/[ containers, - idioms + idioms, + int128 ] +from compiler/ast/ast import newSym, newType, rawAddSon from compiler/sem/semdata import makeVarType # TODO: move the procedure somewhere common @@ -78,17 +82,17 @@ type Values = object ## Represents the inputs to an operation. A container of zero-or-more - ## values, where each value is represented by a ``PNode`` expression + ## values, where each value is represented by a ``CgNode`` expression case kind: ValuesKind of vkNone: discard of vkSingle: - single: PNode + single: CgNode tag: ValueTags of vkMulti: - list: seq[PNode] + list: seq[CgNode] modeAndTags: seq[tuple[mode: ArgumentMode, tags: ValueTags]] - ## a separate sequence is used so that the whole ``PNode`` list can + ## a separate sequence is used so that the whole ``CgNode`` list can ## be moved into the destination node at once TranslateCl = object @@ -105,11 +109,11 @@ type params: Values - # While in the MIR only a ``mnkScope`` opens a new scope, in ``PNode``-AST - # both ``nkStmtList`` and ``nkStmtListExpr`` do - the latter being used by + # While in the MIR only a ``mnkScope`` opens a new scope, in ``CgPNode``-IR + # both ``cnkStmtList`` and ``cnkStmtListExpr`` do - the latter being used by # the arg-block translation. A 'def'-like can appear inside an arg-block # and the defined entity be used outside of it, which would thus result - # in the definition being placed in an ``nkStmtListExpr``, producing + # in the definition being placed in an ``cnkStmtListExpr``, producing # semantically invalid code that later results in code-gen errors. # To solve the problem, if a 'def'-like appears nested inside an arg-block, # only an assignment (if necessary) is produced and the symbol node is @@ -117,7 +121,7 @@ type # is prepended to the statement list produced for the current enclosing # ``mnkScope`` inArgBlock: int ## keeps track of the current arg-block nesting - defs: seq[PSym] + defs: seq[CgNode] TreeWithSource = object ## Combines a ``MirTree`` with its associated ``SourceMap`` for @@ -130,7 +134,7 @@ type TreeCursor = object ## A cursor into a ``TreeWithSource`` pos: uint32 ## the index of the currently pointed to node - origin {.cursor.}: PNode ## the node + origin {.cursor.}: PNode ## the source node template isFilled(x: ref): bool = not x.isNil @@ -138,14 +142,14 @@ template `^^`(s, i: untyped): untyped = # XXX: copied from ``system.nim`` because it's not exported (when i is BackwardsIndex: s.len - int(i) else: int(i)) -func toValues(x: sink PNode): Values {.inline.} = +func toValues(x: sink CgNode): Values {.inline.} = # note: having ``toValues`` be an implicit converter lead to an overload # resolution issue where the converter was incorrectly chosen, making # otherwise correct code not compile assert x != nil Values(kind: vkSingle, single: x) -func `[]`(v: Values, i: Natural): PNode = +func `[]`(v: Values, i: Natural): CgNode = if i > 0 or v.kind == vkMulti: v.list[i] else: @@ -157,13 +161,13 @@ func len(v: Values): int = of vkSingle: 1 of vkMulti: v.list.len -func add(v: var Values, n: sink PNode, tag: ValueTags, mode: ArgumentMode) = +func add(v: var Values, n: sink CgNode, tag: ValueTags, mode: ArgumentMode) = v.list.add n v.modeAndTags.add (mode, tag) -func getCalleeMagic(n: PNode): TMagic = - if n.kind == nkSym: n.sym.magic - else: mNone +func getCalleeMagic(n: CgNode): TMagic = + if n.kind == cnkSym: n.sym.magic + else: mNone proc createMagic(cl: var TranslateCl, magic: TMagic): PSym = createMagic(cl.graph, cl.idgen, "op", magic) @@ -197,13 +201,110 @@ func toMode(kind: range[mnkArg..mnkConsume]): ArgumentMode = of mnkName: amName of mnkConsume: amConsume -proc copySubTree[A, B](source: PNode, slice: HSlice[A, B], to: PNode) = - ## Copies all sub-nodes from the `slice` in `source` to the end of `to`, - ## using a full sub-tree copy (i.e. ``copyTree``) - # XXX: using an ``openArray`` instead of a ``PNode`` + ``HSlice`` pair - # would simplify this procedure a lot. As of the time of this comment - # being written, creating an openArray from a node is rather cumbersome - # however +template `[]=`(x: CgNode, i: Natural, n: CgNode) = + x.childs[i] = n + +template `[]=`(x: CgNode, i: BackwardsIndex, n: CgNode) = + x.childs[i] = n + +template add(x: CgNode, y: CgNode) = + x.childs.add y + +proc copyTree(n: CgNode): CgNode = + case n.kind + of cnkWithoutItems: + new(result) + result[] = n[] + of cnkWithItems: + result = CgNode(kind: n.kind, info: n.info, typ: n.typ) + result.childs.setLen(n.childs.len) + for i, it in n.childs.pairs: + result[i] = copyTree(it) + +proc newEmpty(info = unknownLineInfo): CgNode = + CgNode(kind: cnkEmpty, info: info) + +proc newTree(kind: CgNodeKind, info: TLineInfo, childs: varargs[CgNode]): CgNode = + ## For node kinds that don't represent standalone statements. + result = CgNode(kind: kind, info: info) + result.childs = @childs + +func newTypeNode(info: TLineInfo, typ: PType): CgNode = + CgNode(kind: cnkType, info: info, typ: typ) + +func newSymNode(s: PSym; info = unknownLineInfo): CgNode = + CgNode(kind: cnkSym, info: info, typ: s.typ, sym: s) + +proc newExpr(kind: CgNodeKind, info: TLineInfo, typ: PType, + childs: sink seq[CgNode]): CgNode = + ## Variant of ``newExpr`` optimized for passing a pre-existing child + ## node sequence. + result = CgNode(kind: kind, info: info, typ: typ) + result.childs = childs + +proc newStmt(kind: CgNodeKind, info: TLineInfo, + childs: sink seq[CgNode]): CgNode = + ## Variant of ``newStmt`` optimized for passing a pre-existing child + ## node sequence. + result = CgNode(kind: kind, info: info) + result.childs = childs + +proc translateLit*(val: PNode): CgNode = + ## Translates an ``mnkLiteral`` node to a ``CgNode``. + ## Note that the MIR not only uses ``mnkLiteral`` for "real" literals, but + ## also for pushing other raw ``PNode``s through the MIR phase. + template node(k: CgNodeKind, field, value: untyped): CgNode = + CgNode(kind: k, info: val.info, typ: val.typ, field: value) + + case val.kind + of nkIntLiterals: + # use the type for deciding what whether it's a signed or unsigned value + case val.typ.skipTypes(abstractRange).kind + of tyInt..tyInt64, tyEnum, tyBool: + node(cnkIntLit, intVal, val.intVal) + of tyUInt..tyUInt64, tyChar: + node(cnkUIntLit, intVal, val.intVal) + of tyPtr, tyPointer, tyProc: + # XXX: consider adding a dedicated node for pointer-like-literals + # to both ``PNode`` and ``CgNode`` + node(cnkUIntLit, intVal, val.intVal) + else: + unreachable(val.typ.skipTypes(abstractRange).kind) + of nkFloatLiterals: + case val.typ.skipTypes(abstractRange).kind + of tyFloat, tyFloat64, tyFloat128: + node(cnkFloatLit, floatVal, val.floatVal) + of tyFloat32: + # all code-generators need to do this at one point, so we help them out + # by narrowing the value to a float32 value + node(cnkFloatLit, floatVal, val.floatVal.float32.float64) + else: + unreachable() + of nkStrKinds: + node(cnkStrLit, strVal, val.strVal) + of nkNilLit: + newNode(cnkNilLit, val.info, val.typ) + of nkNimNodeLit: + node(cnkAstLit, astLit, val[0]) + of nkRange: + node(cnkRange, childs, @[translateLit(val[0]), translateLit(val[1])]) + of nkBracket: + assert val.len == 0 + # XXX: ``mirgen`` having to generate ``mnkLiteral``s for empty seq + # construction expressions is bad design. Fully constant + # construction expresssion should probably be lifted into proper + # constants during ``transf`` + newNode(cnkArrayConstr, val.info, val.typ) + of nkSym: + # special case for raw symbols used with emit and asm statements + assert val.sym.kind == skField + node(cnkSym, sym, val.sym) + else: + unreachable("implement: " & $val.kind) + +proc copySubTree[A, B](source: PNode, slice: HSlice[A, B], to: var CgNode) = + ## Translates all sub-nodes from the `slice` in `source` to ``CgNode`` and + ## appends them to the end of `to`. let a = source ^^ slice.a b = source ^^ slice.b @@ -213,48 +314,41 @@ proc copySubTree[A, B](source: PNode, slice: HSlice[A, B], to: PNode) = # resize the node list first: let start = to.len - to.sons.setLen(start + (b - a) + 1) + to.childs.setLen(start + (b - a) + 1) # then copy all nodes: for i in a..b: - to[start + (i - a)] = source[i] + to[start + (i - a)] = translateLit(source[i]) -func addIfNotEmpty(stmts: var seq[PNode], n: sink PNode) = +func addIfNotEmpty(stmts: var seq[CgNode], n: sink CgNode) = ## Only adds the node to the list if it's not an empty node. Used to prevent ## the creation of statement-list expression that only consist of empty ## nodes + the result-expression (a statement-list expression is unnecessary ## in that case) - if n.kind != nkEmpty: + if n.kind != cnkEmpty: stmts.add n -func toSingleNode(stmts: sink seq[PNode]): PNode = - ## Creates a single ``PNode`` from a list of *statements* +func toSingleNode(stmts: sink seq[CgNode]): CgNode = + ## Creates a single ``CgNode`` from a list of *statements* case stmts.len of 0: - result = newNode(nkEmpty) + result = newEmpty() of 1: result = move stmts[0] else: - result = newNode(nkStmtList) - result.sons = move stmts + result = newNode(cnkStmtList) + result.childs = move stmts -proc wrapArg(stmts: seq[PNode], info: TLineInfo, val: PNode): PNode = +proc wrapArg(stmts: sink seq[CgNode], info: TLineInfo, val: sink CgNode): CgNode = ## If there are extra statements (i.e. `stmts` is not empty), wraps the - ## statements + result-expression into an ``nkStmtListExpr``. Otherwise, + ## statements + result-expression into an ``cnkStmtListExpr``. Otherwise, ## returns `val` as is if stmts.len == 0: result = val else: - assert val.kind != nkStmtListExpr - result = newTreeIT(nkStmtListExpr, info, val.typ, stmts) - result.add val - -proc makeVarSection(syms: openArray[PSym], info: TLineInfo): PNode = - ## Creates a var section with all symbols from `syms` - result = newNodeI(nkVarSection, info) - result.sons.newSeq(syms.len) - for i, s in syms.pairs: - result[i] = newIdentDefs(newSymNode(s)) + assert val.kind != cnkStmtListExpr + result = newExpr(cnkStmtListExpr, info, val.typ, stmts) + result.childs.add val proc newTemp(cl: var TranslateCl, info: TLineInfo, typ: PType): PSym = ## Creates and returns a new ``skTemp`` symbol @@ -297,7 +391,7 @@ func findBranch(c: ConfigRef, rec: PNode, field: PIdent): int = unreachable("field is not part of the record-case") proc buildCheck(cl: var TranslateCl, recCase: PNode, pos: Natural, - info: TLineInfo): PNode = + info: TLineInfo): CgNode = ## Builds the boolean expression testing if `discr` is part of the branch ## with position `pos` assert recCase.kind == nkRecCase @@ -309,7 +403,7 @@ proc buildCheck(cl: var TranslateCl, recCase: PNode, pos: Natural, rawAddSon(setType, discr.typ) # the set's element type var - lit = newNodeIT(nkCurly, info, setType) + lit = newExpr(cnkSetConstr, info, setType) invert = false case branch.kind @@ -329,40 +423,40 @@ proc buildCheck(cl: var TranslateCl, recCase: PNode, pos: Natural, # create a ``contains(lit, discr)`` expression: let inExpr = - newTreeIT(nkCall, info, getSysType(cl.graph, info, tyBool), [ + newExpr(cnkCall, info, getSysType(cl.graph, info, tyBool), [ newSymNode(getSysMagic(cl.graph, info, "contains", mInSet), info), lit, - copyTree(discr) + newSymNode(discr.sym) ]) if invert: result = - newTreeIT(nkCall, info, getSysType(cl.graph, info, tyBool), [ + newExpr(cnkCall, info, getSysType(cl.graph, info, tyBool), [ newSymNode(getSysMagic(cl.graph, info, "not", mNot), info), inExpr ]) else: result = inExpr -proc addToVariantAccess(cl: var TranslateCl, dest: PNode, field: PSym, - info: TLineInfo): PNode = +proc addToVariantAccess(cl: var TranslateCl, dest: CgNode, field: PSym, + info: TLineInfo): CgNode = ## Appends a field access for a field inside a record branch to `dest` - ## (transforming it into a ``nkCheckedFieldExpr`` if it isn't one already) + ## (transforming it into a ``cnkCheckedFieldAccess`` if it isn't one already) ## and returns the resulting expression let node = case dest.kind - of nkDotExpr: dest - of nkCheckedFieldExpr: dest[0] + of cnkFieldAccess: dest + of cnkCheckedFieldAccess: dest[0] else: unreachable() - # TODO: generating a field check (i.e. ``nkCheckedFieldExpr``) should not - # done by the code-generators, but instead happen at the MIR level as + # TODO: generating a field check (i.e. ``cnkCheckedFieldAccess``) should not + # be done by the code-generators, but instead happen at the MIR level as # a MIR pass. In other words, a MIR pass should insert an 'if' + # 'raise' for each access to a field inside a record branch (but only - # if ``optFieldCheck`` is enabled) and no ``nkCheckedFieldExpr`` should + # if ``optFieldCheck`` is enabled) and no ``cnkCheckedFieldAccess`` should # be generated here - assert node.kind == nkDotExpr + assert node.kind == cnkFieldAccess let # the symbol of the discriminant is on the right-side of the dot-expr @@ -375,36 +469,35 @@ proc addToVariantAccess(cl: var TranslateCl, dest: PNode, field: PSym, node.typ = field.typ case dest.kind - of nkDotExpr: - newTreeIT(nkCheckedFieldExpr, info, field.typ, [node, check]) - of nkCheckedFieldExpr: + of cnkFieldAccess: + newExpr(cnkCheckedFieldAccess, info, field.typ, [node, check]) + of cnkCheckedFieldAccess: # a field is accessed that is inside a nested record-case. Don't wrap the - # ``nkCheckedFieldExpr`` in another one -- append the check instead. + # ``cnkCheckedFieldAccess`` in another one -- append the check instead. # While the order of the checks *should* be irrelevant, we still emit them # in the order they were generated originally (i.e. innermost to outermost) - dest.sons.insert(check, 1) + dest.childs.insert(check, 1) # update the type of the expression: dest.typ = field.typ dest else: unreachable() -# FIXME: duplicated from mirgen -func isSimple(n: PNode): bool = +func isSimple(n: CgNode): bool = ## Computes if the l-value expression `n` always names the same valid ## location var n = n while true: case n.kind - of nkSym, nkLiterals: + of cnkSym, cnkLiterals: return true - of nkDotExpr: - # ``nkCheckedFieldExpr`` is deliberately not included here because it + of cnkFieldAccess: + # ``cnkCheckedFieldAccess`` is deliberately not included here because it # means the location is part of a variant-object-branch n = n[0] - of nkBracketExpr: + of cnkBracketAccess: if n[0].typ.skipTypes(abstractVarRange).kind in {tyTuple, tyArray} and - n[1].kind in nkLiterals: + n[1].kind in cnkLiterals: # tuple access and arrays indexed by a constant value are # allowed -- they always name the same location n = n[0] @@ -413,7 +506,7 @@ func isSimple(n: PNode): bool = else: return false -func underlyingLoc(n: PNode): tuple[underlying: PNode, firstConv: PNode] = +func underlyingLoc(n: CgNode): tuple[underlying: CgNode, firstConv: CgNode] = ## Returns the lvalue expression stripped from any trailing lvalue ## conversion. For convenience, the node representing the first ## applied conversion is also returned. If no conversion exists, `firstConv` @@ -422,14 +515,14 @@ func underlyingLoc(n: PNode): tuple[underlying: PNode, firstConv: PNode] = n = n orig = n - while n.kind in {nkObjDownConv, nkObjUpConv}: + while n.kind in {cnkObjDownConv, cnkObjUpConv}: orig = n - n = n.lastSon + n = n[^1] result = (n, orig) -proc useLvalueRef(n: PNode, mutable: bool, cl: var TranslateCl, - stmts: var seq[PNode]): PNode = +proc useLvalueRef(n: CgNode, mutable: bool, cl: var TranslateCl, + stmts: var seq[CgNode]): CgNode = ## Generates a temporary view into the location named by the lvalue ## expression `n` and returns the deref expression for accessing the ## location @@ -444,27 +537,95 @@ proc useLvalueRef(n: PNode, mutable: bool, cl: var TranslateCl, # conversion. Creating a view from the location *after* lvalue conversion # would break this, so instead, a view is created from the unconverted # location and the conversion is applied at each usage site - stmts.add newTreeI(nkVarSection, n.info, - [newIdentDefs(newSymNode(sym), - newTreeIT(nkHiddenAddr, n.info, typ, [locExpr])) - ]) + stmts.add newStmt(cnkDef, n.info, + [newSymNode(sym), + newExpr(cnkHiddenAddr, n.info, typ, [locExpr])] + ) if locExpr != conv: # a conversion exists. Rewrite the conversion operation to apply to the # dereferenced view - conv[0] = newTreeIT(nkHiddenDeref, n.info, locExpr.typ, [newSymNode(sym)]) + conv[0] = newExpr(cnkDerefView, n.info, locExpr.typ, [newSymNode(sym)]) result = n else: - result = newTreeIT(nkHiddenDeref, n.info, n.typ, [newSymNode(sym)]) + result = newExpr(cnkDerefView, n.info, n.typ, [newSymNode(sym)]) -proc useTemporary(n: PNode, cl: var TranslateCl, stmts: var seq[PNode]): PNode = +proc useTemporary(n: CgNode, cl: var TranslateCl, stmts: var seq[CgNode]): CgNode = let sym = newTemp(cl, n.info, n.typ) - stmts.add newTreeI(nkVarSection, n.info, [newIdentDefs(newSymNode(sym), n)]) + stmts.add newStmt(cnkDef, n.info, [newSymNode(sym), n]) result = newSymNode(sym) -proc prepareParameter(expr: PNode, tag: ValueTags, mode: ArgumentMode, - cl: var TranslateCl, stmts: var seq[PNode]): PNode = +proc flattenExpr*(expr: CgNode, stmts: var seq[CgNode]): CgNode = + ## A copy of `flattenExpr `_ adjusted for + ## ``CgNode``. + proc forward(n: var CgNode, p: int): CgNode = + ## Performs transformation #1 + if n[p].kind == cnkStmtListExpr: + result = n[p] + n[p] = result[^1] + result[^1] = n + else: + result = n + + var it = expr + while true: + # we're looking for expression nodes that represent side-effect free + # operations + case it.kind + of cnkFieldAccess, cnkCheckedFieldAccess, cnkBracketAccess, cnkHiddenAddr, cnkAddr, + cnkDeref, cnkDerefView, cnkCStringToString, cnkStringToCString, + cnkObjDownConv, cnkObjUpConv: + it = forward(it, 0) + of cnkConv, cnkHiddenConv, cnkCast: + it = forward(it, 1) + else: + # no IR to which transform #1 applies + discard + + if it.kind == cnkStmtListExpr: + # transformation #2: + for i in 0..`_ adjusted for + ## ``CgNode``. + var n {.cursor.} = n + while true: + case n.kind + of cnkAddr, cnkHiddenAddr, cnkBracketAccess, cnkObjUpConv, cnkObjDownConv, + cnkCheckedFieldAccess, cnkFieldAccess: + n = n[0] + of cnkHiddenConv, cnkConv: + if skipTypes(n.typ, abstractVarRange).kind in {tyOpenArray, tyTuple, tyObject} or + compareTypes(n.typ, n[1].typ, dcEqIgnoreDistinct): + # lvalue conversion + n = n[1] + else: + return false + + of cnkSym: + # don't use a view if the location is part of a constant + return n.sym.kind in {skVar, skLet, skForVar, skResult, skParam, skTemp} + of cnkDerefView, cnkDeref: + return true + of cnkCall: + # if the call yields a view, use an lvalue reference (view) -- otherwise, + # do not + return classifyBackendView(n.typ) != bvcNone + else: + return false + +proc prepareParameter(expr: CgNode, tag: ValueTags, mode: ArgumentMode, + cl: var TranslateCl, stmts: var seq[CgNode]): CgNode = let expr = flattenExpr(expr, stmts) if isSimple(expr): # if it's an independent expression with no side-effects, a temporary is @@ -484,7 +645,7 @@ proc prepareParameter(expr: PNode, tag: ValueTags, mode: ArgumentMode, # assign to a temporary first result = useTemporary(expr, cl, stmts) -proc prepareParameters(params: var Values, stmts: var seq[PNode], +proc prepareParameters(params: var Values, stmts: var seq[CgNode], cl: var TranslateCl) = ## Pre-processes the given arguments so that they can be used (referenced) ## as region parameters. A region can be seen as an inlined procedure @@ -502,28 +663,28 @@ proc prepareParameters(params: var Values, stmts: var seq[PNode], let (mode, tags) = params.modeAndTags[i] param = prepareParameter(param, tags, mode, cl, stmts) -proc wrapInHiddenAddr(cl: TranslateCl, n: PNode): PNode = - ## Restores the ``nkHiddenAddr`` around lvalue expressions passed to ``var`` - ## parameters. The code-generators operating on ``PNode``-AST depend on the +proc wrapInHiddenAddr(cl: TranslateCl, n: CgNode): CgNode = + ## Restores the ``cnkHiddenAddr`` around lvalue expressions passed to ``var`` + ## parameters. The code-generators operating on ``CgNode``-IR depend on the ## hidden addr to be present let inner = - if n.kind == nkStmtListExpr: n.lastSon else: n + if n.kind == cnkStmtListExpr: n[^1] else: n result = if n.typ.skipTypes(abstractInst).kind != tyVar: - newTreeIT(nkHiddenAddr, n.info, makeVarType(cl.owner, n.typ, cl.idgen), n) - elif inner.kind == nkObjDownConv and + newExpr(cnkHiddenAddr, n.info, makeVarType(cl.owner, n.typ, cl.idgen), n) + elif inner.kind == cnkObjDownConv and inner[0].typ.kind != tyVar: # TODO: ``nkHiddenSubConv`` nodes for objects (which are later # transformed into ``nkObjDownConv`` nodes) are in some cases # incorrectly typed as ``var`` somewhere in the compiler # (presumably during sem). Fix the underlying problem and remove # the special case here - newTreeIT(nkHiddenAddr, n.info, n.typ, n) + newExpr(cnkHiddenAddr, n.info, n.typ, n) else: n -proc genObjConv(n: PNode, a, b, t: PType): PNode = +proc genObjConv(n: CgNode, a, b, t: PType): CgNode = ## Depending on the relationship between `a` and `b`, wraps `n` in either an ## up- or down-conversion. `t` is the type to use for the resulting ## expression @@ -532,26 +693,37 @@ proc genObjConv(n: PNode, a, b, t: PType): PNode = #assert diff != 0 and diff != high(int), "redundant or illegal conversion" if diff == 0: return nil - result = newTreeIT( - if diff < 0: nkObjUpConv else: nkObjDownConv, + result = newExpr( + if diff < 0: cnkObjUpConv else: cnkObjDownConv, n.info, t): n # forward declarations: proc tbSeq(tree: TreeWithSource, cl: var TranslateCl, cr: var TreeCursor): Values -proc tbStmt(tree: TreeWithSource, cl: var TranslateCl, cr: var TreeCursor): PNode {.inline.} -proc tbList(tree: TreeWithSource, cl: var TranslateCl, cr: var TreeCursor): PNode +proc tbStmt(tree: TreeWithSource, cl: var TranslateCl, cr: var TreeCursor): CgNode {.inline.} +proc tbList(tree: TreeWithSource, cl: var TranslateCl, cr: var TreeCursor): CgNode -proc tbScope(tree: TreeWithSource, cl: var TranslateCl, n: MirNode, cr: var TreeCursor): PNode +proc tbScope(tree: TreeWithSource, cl: var TranslateCl, n: MirNode, cr: var TreeCursor): CgNode proc tbRegion(tree: TreeWithSource, cl: var TranslateCl, prev: sink Values, - cr: var TreeCursor): PNode + cr: var TreeCursor): CgNode + +proc newIntLit(val: Int128, t: PType): CgNode = + case t.skipTypes(abstractVarRange).kind + of tyUInt..tyUInt64, tyChar: + CgNode(kind: cnkUIntLit, info: unknownLineInfo, typ: t, + intVal: cast[BiggestInt](toUInt(val))) + of tyInt..tyInt64, tyBool, tyEnum: + CgNode(kind: cnkIntLit, info: unknownLineInfo, typ: t, + intVal: toInt(val)) + else: + unreachable() -proc handleSpecialConv(c: ConfigRef, n: PNode, info: TLineInfo, - dest: PType): PNode = +proc handleSpecialConv(c: ConfigRef, n: CgNode, info: TLineInfo, + dest: PType): CgNode = ## Checks if a special conversion operator is required for a conversion ## between the source type (i.e. that of `n`) and the destination type. - ## If it is, generates the conversion operation AST and returns it -- nil + ## If it is, generates the conversion operation IR and returns it -- nil ## otherwise let orig = dest @@ -566,10 +738,10 @@ proc handleSpecialConv(c: ConfigRef, n: PNode, info: TLineInfo, assert source.kind == dest.kind if source.base.kind == tyObject: - if n.kind in {nkObjUpConv, nkObjDownConv} and + if n.kind in {cnkObjUpConv, cnkObjDownConv} and sameType(dest, n[0].typ.skipTypes(abstractInst)): # this one and the previous conversion cancel each other out. Both - # ``nkObjUpConv`` and ``nkObjDownConv`` are not treated as lvalue + # ``cnkObjUpConv`` and ``cnkObjDownConv`` are not treated as lvalue # conversions when the source/dest operands are pointer/reference-like, # so the collapsing here is required in order to generate correct # code @@ -592,62 +764,71 @@ proc handleSpecialConv(c: ConfigRef, n: PNode, info: TLineInfo, let rangeDest = skipTypes(orig, abstractVar) kind = - if tyInt64 in {dest.kind, source.kind}: nkChckRange64 - else: nkChckRange + if tyInt64 in {dest.kind, source.kind}: cnkChckRange64 + else: cnkChckRange - result = newTreeIT(kind, info, orig): + result = newExpr(kind, info, orig): [n, - newIntTypeNode(firstOrd(c, rangeDest), rangeDest), - newIntTypeNode(lastOrd(c, rangeDest), rangeDest)] + newIntLit(firstOrd(c, rangeDest), rangeDest), + newIntLit(lastOrd(c, rangeDest), rangeDest)] of tyFloat..tyFloat128: let rangeDest = skipTypes(orig, abstractVar) if rangeDest.kind == tyRange: # a conversion to a float range (e.g. ``range[0.0 .. 1.0]``) - result = newTreeIT(nkChckRangeF, info, orig): - [n, copyTree(rangeDest.n[0]), copyTree(rangeDest.n[1])] + result = newExpr(cnkChckRangeF, info, orig): + [n, translateLit(rangeDest.n[0]), translateLit(rangeDest.n[1])] else: result = nil -proc tbConv(cl: TranslateCl, n: PNode, info: TLineInfo, dest: PType): PNode = - ## Generates the AST for an expression that performs a type conversion for +proc tbConv(cl: TranslateCl, n: CgNode, info: TLineInfo, dest: PType): CgNode = + ## Generates the IR for an expression that performs a type conversion for ## `n` to type `dest` result = handleSpecialConv(cl.graph.config, n, info, dest) if result == nil: # no special conversion is used - result = newTreeIT(nkConv, info, dest): [newNodeIT(nkType, info, dest), n] + result = newExpr(cnkConv, info, dest): [newTypeNode(info, dest), n] -proc tbSingle(n: MirNode, cl: TranslateCl, info: TLineInfo): PNode = +proc tbSingle(n: MirNode, cl: TranslateCl, info: TLineInfo): CgNode = case n.kind of mnkProc, mnkConst, mnkParam, mnkGlobal, mnkLocal: - newSymNodeIT(n.sym, info, n.sym.typ) + newSymNode(n.sym, info) of mnkTemp: newSymNode(cl.tempMap[n.temp], info) of mnkLiteral: - n.lit + translateLit(n.lit) of mnkType: - newNodeIT(nkType, info, n.typ) + newTypeNode(info, n.typ) else: unreachable("not an atom: " & $n.kind) proc tbExceptItem(tree: TreeWithSource, cl: TranslateCl, cr: var TreeCursor - ): PNode = + ): CgNode = let n {.cursor.} = get(tree, cr) case n.kind - of mnkPNode: n.node - of mnkType: newNodeIT(nkType, cr.info, n.typ) + of mnkPNode: + assert n.node.kind == nkInfix + assert n.node[1].kind == nkType + assert n.node[2].kind == nkSym + # the infix expression (``type as x``) signals that the except-branch is + # a matcher for an imported exception. We translate the infix to a + # ``cnkBinding`` node and let the code generators take care of it + newTree(cnkBinding, cr.info): + [newNode(cnkType, n.node[1].info, n.node[1].typ), + newSymNode(n.node[2].sym, n.node[2].info)] + of mnkType: newTypeNode(cr.info, n.typ) else: unreachable() proc tbDef(tree: TreeWithSource, cl: var TranslateCl, prev: sink Values, - n: MirNode, cr: var TreeCursor): PNode = + n: MirNode, cr: var TreeCursor): CgNode = ## Translates a 'def'-like construct assert n.kind in DefNodes let entity {.cursor.} = get(tree, cr) # the name of the defined entity info = cr.info - var def: PNode + var def: CgNode case entity.kind of SymbolLike: @@ -658,7 +839,7 @@ proc tbDef(tree: TreeWithSource, cl: var TranslateCl, prev: sink Values, of skParam, routineKinds: # the 'def' of params and procedures only has meaning at the MIR level; # the code generators don't care about them - def = newNode(nkEmpty) + def = newEmpty() else: unreachable() @@ -678,36 +859,45 @@ proc tbDef(tree: TreeWithSource, cl: var TranslateCl, prev: sink Values, leave(tree, cr) case def.kind - of nkSym: + of cnkSym: assert def.sym.kind in {skVar, skLet, skForVar, skTemp} - # it's a definition that needs to be put into a var section if cl.inArgBlock > 0: # if we're inside an arg-block, the var section is generated later and # placed at an earlier position. We just produce an assignment to the # entity here (if the def has an input) - cl.defs.add def.sym - + cl.defs.add newStmt(cnkDef, info, [def, newEmpty()]) result = case prev.kind - of vkNone: newNodeI(nkEmpty, info) - of vkSingle: newTreeI(nkAsgn, info, def, prev.single) + of vkNone: newEmpty(info) + of vkSingle: newStmt(cnkAsgn, info, [def, prev.single]) of vkMulti: unreachable() - else: - result = newTreeI(nkVarSection, info): + result = newStmt(cnkDef, info): case prev.kind - of vkNone: newIdentDefs(def) - of vkSingle: newIdentDefs(def, prev.single) + of vkNone: [def, newEmpty()] + of vkSingle: [def, prev.single] of vkMulti: unreachable() - - of nkEmpty: + of cnkEmpty: result = def else: unreachable() +proc translateNode(n: PNode): CgNode = + ## Translates the content of a ``mnkPNode`` node to a ``CgNode``. + case n.kind + of nkPragma: + # XXX: consider adding a dedicated ``mnkPragma`` MIR node + # only simple pragmas reach here + assert n.len == 1 + assert n[0].kind == nkIdent + CgNode(kind: cnkPragmaStmt, info: n.info, pragma: whichKeyword(n[0].ident)) + else: + # cannot reach here + unreachable(n.kind) + proc tbSingleStmt(tree: TreeWithSource, cl: var TranslateCl, n: MirNode, - cr: var TreeCursor): PNode = - template body(): PNode = + cr: var TreeCursor): CgNode = + template body(): CgNode = tbStmt(tree, cl, cr) let info = cr.info ## the source information of `n` @@ -720,24 +910,19 @@ proc tbSingleStmt(tree: TreeWithSource, cl: var TranslateCl, n: MirNode, result = tbScope(tree, cl, n, cr) leave(tree, cr) of mnkRepeat: - # translated into ``while true: body`` - result = - newTreeI(nkWhileStmt, info, - newIntTypeNode(1, cl.graph.getSysType(info, tyBool)), # condition - body()) # body - + result = newStmt(cnkRepeatStmt, info, body()) leave(tree, cr) of mnkBlock: let sym = newSym(skLabel, cl.cache.getIdent("label"), cl.idgen.nextSymId(), cl.owner, info) cl.labelMap[n.label[]] = sym - result = newTreeI(nkBlockStmt, info, - newSymNode(sym), # the label - body()) + result = newStmt(cnkBlockStmt, info, + newSymNode(sym), # the label + body()) leave(tree, cr) of mnkTry: - result = newTreeIT(nkTryStmt, info, n.typ, [body()]) + result = newStmt(cnkTryStmt, info, [body()]) assert n.len <= 2 for _ in 0.. 0: for x in 0.. prev: - # create a var section for the collected symbols: - stmts.insert( - makeVarSection(cl.defs.toOpenArray(prev, cl.defs.high), info), 0) + # create a var section for the collected symbols + for i in countdown(cl.defs.high, prev): + stmts.insert(move cl.defs[i], 0) # "pop" the elements that were added as part of this scope: cl.defs.setLen(prev) @@ -1151,8 +1315,8 @@ proc tbScope(tree: TreeWithSource, cl: var TranslateCl, n: MirNode, result = toSingleNode(stmts) proc tbRegion(tree: TreeWithSource, cl: var TranslateCl, prev: sink Values, - cr: var TreeCursor): PNode = - var stmts: seq[PNode] + cr: var TreeCursor): CgNode = + var stmts: seq[CgNode] prepareParameters(prev, stmts, cl) swap(cl.params, prev) @@ -1170,9 +1334,9 @@ proc tbRegion(tree: TreeWithSource, cl: var TranslateCl, prev: sink Values, proc tbExpr(tree: TreeWithSource, cl: var TranslateCl, cr: var TreeCursor - ): tuple[node: PNode, atEnd: bool] = + ): tuple[node: CgNode, atEnd: bool] = ## Translates the expression located at the current cursor position `cr` to - ## ``PNode`` AST + ## ``CgNode`` IR template hasNext(): bool = cr.pos.int < tree.tree.len @@ -1190,10 +1354,10 @@ proc tbExpr(tree: TreeWithSource, cl: var TranslateCl, cr: var TreeCursor else: unreachable("illformed MIR") -proc tbMulti(tree: TreeWithSource, cl: var TranslateCl, cr: var TreeCursor): PNode = +proc tbMulti(tree: TreeWithSource, cl: var TranslateCl, cr: var TreeCursor): CgNode = ## Translates expressions/statements until the cursor either reaches the end ## or a top-level argument node is encountered - var nodes: seq[PNode] + var nodes: seq[CgNode] while cr.hasNext(tree): case tree[cr].kind of InputNodes: @@ -1211,24 +1375,21 @@ proc tbMulti(tree: TreeWithSource, cl: var TranslateCl, cr: var TreeCursor): PNo # insert the var section for the collected defs at the start: if cl.defs.len > 0: - nodes.insert(makeVarSection(cl.defs, unknownLineInfo), 0) + for i in countdown(cl.defs.high, 0): + nodes.insert(cl.defs[i], 0) case nodes.len - of 0: newNode(nkEmpty) + of 0: newEmpty() of 1: nodes[0] else: - let r = - if nodes[^1].typ.isEmptyType(): - # it's a statement list - newNode(nkStmtList) - else: - newNodeIT(nkStmtListExpr, unknownLineInfo, nodes[^1].typ) - - r.sons = move nodes - r + if nodes[^1].typ.isEmptyType(): + # it's a statement list + newStmt(cnkStmtList, unknownLineInfo, nodes) + else: + newExpr(cnkStmtListExpr, unknownLineInfo, nodes[^1].typ, nodes) -proc tb(tree: TreeWithSource, cl: var TranslateCl, start: NodePosition): PNode = - ## Translate `tree` back to a ``PNode`` AST +proc tb(tree: TreeWithSource, cl: var TranslateCl, start: NodePosition): CgNode = + ## Translate `tree` back to a ``CgNode`` IR var cr = TreeCursor(pos: start.uint32) assert tree[cr].kind in InputNodes + StmtNodes, "start must point to the start of expression or statement" @@ -1236,14 +1397,11 @@ proc tb(tree: TreeWithSource, cl: var TranslateCl, start: NodePosition): PNode = proc generateAST*(graph: ModuleGraph, idgen: IdGenerator, owner: PSym, - tree: sink MirTree, sourceMap: sink SourceMap): PNode = - ## Generates a ``PNode`` AST that is semantically equivalent to `tree`, - ## using the `idgen` to provide new IDs when creating symbols. `sourceMap` + tree: sink MirTree, sourceMap: sink SourceMap): CgNode = + ## Generates the ``CgNode`` IR corresponding to the input MIR code (`tree`), + ## using `idgen` for provide new IDs when creating symbols. `sourceMap` ## must be the ``SourceMap`` corresponding to `tree` and is used as the ## provider for source position information - # TODO: `tree` and `sourceMap` are only consumed because of efficiency - # reasons (to get around a full copy for both). Remove ``sink`` for - # both once the fields of ``TreeWithSource`` are views var cl = TranslateCl(graph: graph, idgen: idgen, cache: graph.cache, owner: owner) tb(TreeWithSource(tree: tree, map: sourceMap), cl, NodePosition 0) \ No newline at end of file From eb3099a9f666305f1a362537ed2241075e84e29e Mon Sep 17 00:00:00 2001 From: zerbina <100542850+zerbina@users.noreply.github.com> Date: Thu, 3 Aug 2023 15:31:22 +0000 Subject: [PATCH 09/20] mirbridge/backend: use `CgNode` `canonicalize` and `generateAST` now return `CgNode` trees. For debug rendering, a `treeRepr` procedure for `CgNode` is added to the `cgirutils` module. --- compiler/backend/backends.nim | 7 ++--- compiler/backend/cgirutils.nim | 49 ++++++++++++++++++++++++++++++++++ compiler/mir/mirbridge.nim | 18 ++++++++----- 3 files changed, 64 insertions(+), 10 deletions(-) diff --git a/compiler/backend/backends.nim b/compiler/backend/backends.nim index 4ba5ee2c3b4..e9d43824450 100644 --- a/compiler/backend/backends.nim +++ b/compiler/backend/backends.nim @@ -12,7 +12,8 @@ import lineinfos ], compiler/backend/[ - cgmeth + cgmeth, + cgir ], compiler/front/[ options @@ -374,8 +375,8 @@ proc process(body: var MirFragment, ctx: PSym, graph: ModuleGraph, injectDestructorCalls(graph, idgen, ctx, body.tree, body.source) proc generateAST*(graph: ModuleGraph, idgen: IdGenerator, owner: PSym, - code: sink MirFragment): PNode = - ## Translates the MIR code provided by `code` into ``PNode`` AST and, + code: sink MirFragment): CgNode = + ## Translates the MIR code provided by `code` into ``CgNode`` IR and, ## if enabled, echoes the result. result = generateAST(graph, idgen, owner, code.tree, code.source) echoOutput(graph.config, owner, result) diff --git a/compiler/backend/cgirutils.nim b/compiler/backend/cgirutils.nim index 66d3760274a..041dfa4edf5 100644 --- a/compiler/backend/cgirutils.nim +++ b/compiler/backend/cgirutils.nim @@ -9,12 +9,61 @@ import ast_query, typesrenderer ], + compiler/backend/[ + cgir + ], compiler/utils/[ idioms ] from compiler/ast/renderer import renderTree, TRenderFlag +proc treeRepr*(n: CgNode): string = + ## Renders the tree representation of `n` to text. + proc treeRepr(n: CgNode, indent: int, result: var string) {.nimcall.} = + result.add $n.kind + result.add " " + if n.typ != nil: + result.add "typ: " + result.add $n.typ + result.add " " + + case n.kind + of cnkIntLit: + result.add "intVal: " + result.add $n.intVal + of cnkUIntLit: + result.add "uintVal: " + result.add $cast[BiggestUInt](n.intVal) + of cnkFloatLit: + result.add "floatVal: " + result.add $n.floatVal + of cnkStrLit: + result.add "strVal: \"" + result.add n.strVal + result.add "\"" + of cnkPragmaStmt: + result.add "pragma: " + result.add $n.pragma + of cnkSym: + result.add "sym: " + result.add n.sym.name.s + result.add " id: " + result.add $n.sym.itemId + of cnkEmpty, cnkInvalid, cnkType, cnkAstLit, cnkNilLit, cnkReturnStmt: + discard + of cnkWithItems: + result.add "\n" + for i in 0.. 0: + result.add "\n" + result.add repeat(" ", indent) + result.add $i + result.add ": " + treeRepr(n[i], indent+1, result) + + treeRepr(n, 0, result) + type RenderCtx = object syms: seq[PSym] diff --git a/compiler/mir/mirbridge.nim b/compiler/mir/mirbridge.nim index 7e507a846f3..b77f26e16c7 100644 --- a/compiler/mir/mirbridge.nim +++ b/compiler/mir/mirbridge.nim @@ -1,5 +1,5 @@ ## A temporary module that implements convenience routines for the ``PNode`` -## AST <-> ``MirTree`` translation. +## AST to ``CgNode`` translation. import compiler/ast/[ @@ -7,6 +7,10 @@ import ast_idgen, ast ], + compiler/backend/[ + cgir, + cgirutils + ], compiler/front/[ options ], @@ -66,12 +70,12 @@ proc echoMir*(config: ConfigRef, owner: PSym, tree: MirTree) = writeBody(config, "-- MIR: " & owner.name.s): config.writeln(print(tree)) -proc echoOutput*(config: ConfigRef, owner: PSym, body: PNode) = - ## If requested via the define, renders the output AST `body` and writes the +proc echoOutput*(config: ConfigRef, owner: PSym, body: CgNode) = + ## If requested via the define, renders the output IR `body` and writes the ## result out through ``config.writeLine``. if config.getStrDefine("nimShowMirOutput") == owner.name.s: writeBody(config, "-- output AST: " & owner.name.s): - config.writeln(treeRepr(config, body, reprConfig)) + config.writeln(treeRepr(body)) proc rewriteGlobalDefs*(body: var MirTree, sourceMap: var SourceMap, outermost: bool) = @@ -164,14 +168,14 @@ proc rewriteGlobalDefs*(body: var MirTree, sourceMap: var SourceMap, apply(body, prepared) proc canonicalize*(graph: ModuleGraph, idgen: IdGenerator, owner: PSym, - body: PNode, options: set[GenOption]): PNode = + body: PNode, options: set[GenOption]): CgNode = ## Legacy routine. Translates the body `body` of the procedure `owner` to - ## MIR code, and the MIR code back to ``PNode`` AST. + ## MIR code, and the MIR code to ``CgNode`` IR. echoInput(graph.config, owner, body) # step 1: generate a ``MirTree`` from the input AST let (tree, sourceMap) = generateCode(graph, owner, options, body) echoMir(graph.config, owner, tree) - # step 2: translate it back + # step 2: generate the ``CgNode`` tree result = generateAST(graph, idgen, owner, tree, sourceMap) echoOutput(graph.config, owner, result) \ No newline at end of file From 8e301b2e030515979ea351e890f9cad32493de64 Mon Sep 17 00:00:00 2001 From: zerbina <100542850+zerbina@users.noreply.github.com> Date: Thu, 3 Aug 2023 15:31:22 +0000 Subject: [PATCH 10/20] vmgen: decouple the emit procedures from `PNode` The instruction-emission procedure now accept a `TLineInfo` as input directly, instead of, unnecessarily, requiring a `PNode`. Wrappers that still use `PNode` are added for convenience. --- compiler/vm/vmgen.nim | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/compiler/vm/vmgen.nim b/compiler/vm/vmgen.nim index fbde8d7db75..5bc647934a2 100644 --- a/compiler/vm/vmgen.nim +++ b/compiler/vm/vmgen.nim @@ -254,7 +254,7 @@ func isNimNode(t: PType): bool = let t = skipTypes(t, IrrelevantTypes) t.sym != nil and t.sym.magic == mPNimrodNode -func gABC*(ctx: var TCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) = +func gABC*(ctx: var TCtx; i: TLineInfo; opc: TOpcode; a, b, 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 @@ -269,22 +269,22 @@ func gABC*(ctx: var TCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) = echo "generating ", opc ]# ctx.code.add(ins) - ctx.debug.add(n.info) + ctx.debug.add(i) -proc gABI(c: var TCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) = +proc gABI(c: var TCtx; i: TLineInfo; opc: TOpcode; a, b: TRegister; imm: BiggestInt) = # 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] - c.config.internalAssert(imm in -128..127 , n.info, + c.config.internalAssert(imm in -128..127, i, "VM: immediate value does not fit into an int8") 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) + c.debug.add(i) -proc gABx*(c: var TCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) = +proc gABx*(c: var TCtx; i: TLineInfo; 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 [regBxMin, regBxMax] @@ -295,13 +295,23 @@ proc gABx*(c: var TCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) = echo "generating ", opc ]# - c.config.internalAssert(bx in regBxMin-1..regBxMax, n.info, + c.config.internalAssert(bx in regBxMin-1..regBxMax, i, "VM: immediate value does not fit into regBx") 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) + c.debug.add(i) + +# convenience templates that take the line information from the node +template gABC*(ctx: var TCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) = + gABC(ctx, n.info, opc, a, b, c) + +template gABI(c: var TCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) = + gABI(c, n.info, opc, a, b, imm) + +template gABx(c: var TCtx, n: PNode, opc: TOpcode; a: TRegister, bx: int) = + gABx(c, n.info, opc, a, bx) proc xjmp(c: var TCtx; n: PNode; opc: TOpcode; a: TRegister = 0): TPosition = #assert opc in {opcJmp, opcFJmp, opcTJmp} From 1d36474bb6e8a06d34ed5bcd241092f9109dc1eb Mon Sep 17 00:00:00 2001 From: zerbina <100542850+zerbina@users.noreply.github.com> Date: Thu, 3 Aug 2023 15:31:22 +0000 Subject: [PATCH 11/20] cgirutils: use `CgNode` --- compiler/backend/cgirutils.nim | 148 ++++++++++++++++----------------- 1 file changed, 73 insertions(+), 75 deletions(-) diff --git a/compiler/backend/cgirutils.nim b/compiler/backend/cgirutils.nim index 041dfa4edf5..dab1ea45384 100644 --- a/compiler/backend/cgirutils.nim +++ b/compiler/backend/cgirutils.nim @@ -6,7 +6,6 @@ import ], compiler/ast/[ ast_types, - ast_query, typesrenderer ], compiler/backend/[ @@ -16,8 +15,6 @@ import idioms ] -from compiler/ast/renderer import renderTree, TRenderFlag - proc treeRepr*(n: CgNode): string = ## Renders the tree representation of `n` to text. proc treeRepr(n: CgNode, indent: int, result: var string) {.nimcall.} = @@ -82,8 +79,8 @@ proc disambiguate(c: var RenderCtx, s: PSym): int = c.syms.add s # remember the symbol -proc render(c: var RenderCtx, ind: int, n: PNode, res: var string) = - template add(s: var string, n: PNode) = +proc render(c: var RenderCtx, ind: int, n: CgNode, res: var string) = + template add(s: var string, n: CgNode) = render(c, ind, n, res) template indent(extra = 1) = @@ -96,33 +93,33 @@ proc render(c: var RenderCtx, ind: int, n: PNode, res: var string) = template newLine() = indent(0) - template renderList(n: PNode, sep: untyped; start: int = 0; fin: int = 0) = + template renderList(n: CgNode, sep: untyped; start: int = 0; fin: int = 0) = ## Renders the items in the slice ``start.. start: sep res.add n[i] - template renderList(n: PNode, sep: string; start: int = 0; fin: int = 0) = + template renderList(n: CgNode, sep: string; start: int = 0; fin: int = 0) = ## Renders the items in the slice ``start.." - of nkSym: + of cnkSym: res.add n.sym.name.s let postfix = disambiguate(c, n.sym) if postfix > 0 and n.sym.magic == mNone: @@ -132,131 +129,136 @@ proc render(c: var RenderCtx, ind: int, n: PNode, res: var string) = # highlight cursor locals if sfCursor in n.sym.flags: res.add "_cursor" - of nkType: + of cnkType: if n.typ.sym != nil: res.add $n.typ else: res.add "[type node]" - of nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, nkHiddenStdConv, nkChckRange, nkChckRange64, nkChckRangeF: + of cnkCheckedFieldAccess, cnkHiddenAddr, cnkDerefView, cnkHiddenConv, cnkChckRange, + cnkChckRange64, cnkChckRangeF: res.add n[0] - of nkAddr: + of cnkAddr: res.add "addr " res.add n[0] - of nkDerefExpr: + of cnkDeref: res.add n[0] res.add "[]" - of nkDotExpr: + of cnkFieldAccess: res.add n[0] res.add '.' res.add n[1] - of nkBracketExpr: + of cnkBracketAccess: res.add n[0] res.add '[' res.add n[1] res.add ']' - of nkRange: + of cnkRange: res.add n[0] res.add ".." res.add n[1] - of nkCast: + of cnkCast: res.add "cast[" res.add $n[0].typ res.add "](" res.add n[1] res.add ")" - of nkStringToCString: + of cnkStringToCString: res.add "cstring(" res.add n[0] res.add ')' - of nkCStringToString: + of cnkCStringToString: res.add "string(" res.add n[0] res.add ')' - of nkConv: + of cnkConv: res.add n[0] res.add '(' res.add n[1] res.add ')' - of nkObjUpConv, nkObjDownConv: + of cnkObjUpConv, cnkObjDownConv: res.add $n.typ res.add "(" res.add n[0] res.add ")" - of nkExprColonExpr: + of cnkBinding: res.add n[0] res.add ": " res.add n[1] - of nkCall: + of cnkCall: res.add n[0] res.add '(' let ind = ind + 1 renderList(n, ", ", 1) res.add ')' - of nkObjConstr: - res.add $n[0].typ + of cnkObjConstr: + res.add $n.typ res.add '(' renderList(n, ", ", 1) res.add ')' - of nkTupleConstr, nkClosure: + of cnkTupleConstr, cnkClosureConstr: res.add '(' renderList(n, ", ") res.add ')' - of nkBracket: + of cnkArrayConstr: res.add '[' renderList(n, ", ") res.add ']' - of nkCurly: + of cnkSetConstr: res.add '{' renderList(n, ", ") res.add '}' - of nkAsgn, nkFastAsgn: + of cnkAsgn, cnkFastAsgn: res.add n[0] res.add " = " let ind = ind + 1 res.add n[1] - of nkVarSection, nkLetSection: - for i, def in n.pairs: - if i > 0: - newLine() - res.add "var " - res.add def[0] - if def[2].kind != nkEmpty: - res.add " = " - let ind = ind + 1 - res.add def[2] - of nkPragma: - # re-use the ``PNode`` rendering - res.add renderTree(n, {renderIr, renderNoComments}) - of nkReturnStmt: + of cnkDef: + res.add "var " + res.add n[0] + if n[1].kind != cnkEmpty: + res.add " = " + let ind = ind + 1 + res.add n[1] + of cnkPragmaStmt: + res.add "{." + let name = substr($n.pragma, 1) # cut off the 'w' prefix + res.add name + res.add ".}" + of cnkReturnStmt: res.add "return" - of nkDiscardStmt: + of cnkVoidStmt: res.add "discard " res.add n[0] - of nkBreakStmt: - if n[0].kind == nkEmpty: + of cnkBreakStmt: + if n[0].kind == cnkEmpty: res.add "break" else: res.add "break " res.add n[0] - of nkRaiseStmt: - if n[0].kind == nkEmpty: + of cnkRaiseStmt: + if n[0].kind == cnkEmpty: res.add "raise" else: res.add "raise " res.add n[0] - of nkAsmStmt: + of cnkAsmStmt: res.add "asm " let ind = ind + 1 renderList(n, ", ") res.add "" - of nkWhileStmt: + of cnkEmitStmt: + res.add "{.emit: " + let ind = ind + 1 + renderList(n, ", ") + res.add ".}" + of cnkRepeatStmt: res.add "while true:" indent() - render(c, ind + 1, n[1], res) - of nkBlockStmt: - if n[0].kind == nkEmpty: + render(c, ind + 1, n[0], res) + of cnkBlockStmt: + if n[0].kind == cnkEmpty: res.add "block:" else: res.add "block " @@ -264,57 +266,53 @@ proc render(c: var RenderCtx, ind: int, n: PNode, res: var string) = res.add ":" indent() render(c, ind + 1, n[1], res) - of nkIfStmt: + of cnkIfStmt: res.add "if " - res.add n[0][0] + res.add n[0] res.add ':' indent() - render(c, ind + 1, n[0][1], res) - of nkCaseStmt: + render(c, ind + 1, n[1], res) + of cnkCaseStmt: res.add "case " res.add n[0] for i in 1.. 1: res.add "of " renderList(n[i], ", ", 1, 1) - of nkElse: - res.add "else" else: - unreachable() + res.add "else" res.add ":" indent() render(c, ind + 1, n[i][^1], res) - of nkTryStmt: + of cnkTryStmt: res.add "try:" indent() render(c, ind + 1, n[0], res) for i in 1.. Date: Thu, 3 Aug 2023 15:31:23 +0000 Subject: [PATCH 12/20] update the code generators All three code generators now use the `CgNode` IR. The changes to the modules are kept minimal in order to make review easier. As an additional way to keep the amount of changes smaller, the `compat` module is introduced. --- compiler/ast/ast_query.nim | 2 +- compiler/backend/cbackend.nim | 14 +- compiler/backend/ccgcalls.nim | 78 ++-- compiler/backend/ccgexprs.nim | 432 +++++++++++------------ compiler/backend/ccgliterals.nim | 6 +- compiler/backend/ccgreset.nim | 2 +- compiler/backend/ccgstmts.nim | 261 +++++++------- compiler/backend/ccgtypes.nim | 6 +- compiler/backend/ccgutils.nim | 17 +- compiler/backend/cgen.nim | 155 ++++---- compiler/backend/cgendata.nim | 56 +-- compiler/backend/compat.nim | 262 ++++++++++++++ compiler/backend/jsgen.nim | 405 +++++++++++---------- compiler/vm/vmbackend.nim | 9 +- compiler/vm/vmgen.nim | 587 +++++++++++++++---------------- compiler/vm/vmjit.nim | 19 +- 16 files changed, 1269 insertions(+), 1042 deletions(-) create mode 100644 compiler/backend/compat.nim diff --git a/compiler/ast/ast_query.nim b/compiler/ast/ast_query.nim index b6783e55d89..ea52ea998d4 100644 --- a/compiler/ast/ast_query.nim +++ b/compiler/ast/ast_query.nim @@ -608,7 +608,7 @@ proc isSinkParam*(s: PSym): bool {.inline.} = proc isSinkType*(t: PType): bool {.inline.} = t.kind == tySink -const magicsThatCanRaise = { +const magicsThatCanRaise* = { mNone, mSlurp, mStaticExec, mParseExprToAst, mParseStmtToAst, mEcho} proc canRaiseConservative*(fn: PNode): bool = diff --git a/compiler/backend/cbackend.nim b/compiler/backend/cbackend.nim index 212f6812916..c230fd6783c 100644 --- a/compiler/backend/cbackend.nim +++ b/compiler/backend/cbackend.nim @@ -36,7 +36,8 @@ import tables ], compiler/ast/[ - ast, + ast_query, + ast_types, lineinfos, ndi ], @@ -44,12 +45,15 @@ import backends, cgen, cgendata, + cgir, + compat, extccomp ], compiler/front/[ options ], compiler/mir/[ + mirbridge, mirtrees ], compiler/modules/[ @@ -67,6 +71,8 @@ import import std/options as std_options +from compiler/ast/ast import id, newNode + # XXX: reports are a legacy facility that is going to be phased out. A # note on how to move forward is left at each usage site in this # module @@ -78,7 +84,7 @@ type InlineProc = object ## Information about an inline procedure. sym: PSym - body: PNode + body: CgNode ## the fully processed body of the procedure deps: PackedSet[uint32] @@ -150,11 +156,11 @@ proc prepare(g: BModuleList, d: var DiscoveryData) = # emit definitions for the lifted globals we've discovered: for _, s in visit(d.globals): - defineGlobalVar(g.modules[moduleId(s)], newSymNode(s)) + defineGlobalVar(g.modules[moduleId(s)], s) for _, s in visit(d.threadvars): let bmod = g.modules[moduleId(s)] - fillGlobalLoc(bmod, s, newSymNode(s)) + fillGlobalLoc(bmod, s) declareThreadVar(bmod, s, sfImportc in s.flags) proc processEvent(g: BModuleList, inl: var InliningData, discovery: var DiscoveryData, partial: var Table[PSym, BProc], evt: sink BackendEvent) = diff --git a/compiler/backend/ccgcalls.nim b/compiler/backend/ccgcalls.nim index 0e386732807..a57e2f4a690 100644 --- a/compiler/backend/ccgcalls.nim +++ b/compiler/backend/ccgcalls.nim @@ -10,12 +10,12 @@ ## included from cgen.nim -proc canRaiseDisp(p: BProc; n: PNode): bool = +proc canRaiseDisp(p: BProc; n: CgNode): bool = ## 'true' if calling the callee expression `n` can exit via exceptional ## control-flow, otherwise 'false'. If panics are disabled, this also ## includes all routines that are not certain magics, compiler procs, or ## imported. - if n.kind == nkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}: + if n.kind == cnkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}: result = false elif optPanics in p.config.globalOptions: # we know we can be strict: @@ -24,16 +24,16 @@ proc canRaiseDisp(p: BProc; n: PNode): bool = # we have to be *very* conservative: result = canRaiseConservative(n) -proc reportObservableStore(p: BProc; le, ri: PNode) = +proc reportObservableStore(p: BProc; le, ri: CgNode) = ## Reports the ``rsemObservableStores`` hint when the called procedure can ## exit with an exception and `le` is something to which an assignment is ## observable in the exception-raised case. - proc locationEscapes(p: BProc; le: PNode; inTryStmt: bool): bool = + proc locationEscapes(p: BProc; le: CgNode; inTryStmt: bool): bool = var n = le while true: - # do NOT follow nkHiddenDeref here! + # do NOT follow ``cnkDerefView`` here! case n.kind - of nkSym: + of cnkSym: # we don't own the location so it escapes: if n.sym.owner != p.prc: return true @@ -42,10 +42,10 @@ proc reportObservableStore(p: BProc; le, ri: PNode) = # in 'except' or 'finally' return true return false - of nkDotExpr, nkBracketExpr, nkObjUpConv, nkObjDownConv, - nkCheckedFieldExpr: + of cnkFieldAccess, cnkBracketAccess, cnkObjUpConv, cnkObjDownConv, + cnkCheckedFieldAccess: n = n[0] - of nkHiddenStdConv, nkHiddenSubConv, nkConv: + of cnkHiddenConv, cnkConv: n = n[1] else: # cannot analyse the location; assume the worst @@ -55,10 +55,10 @@ proc reportObservableStore(p: BProc; le, ri: PNode) = # annoying warnings, see #14514 if le != nil and canRaise(ri[0]) and locationEscapes(p, le, p.nestedTryStmts.len > 0): - localReport(p.config, le, reportSem rsemObservableStores) + localReport(p.config, le.info, reportSem rsemObservableStores) -proc hasNoInit(call: PNode): bool {.inline.} = - result = call[0].kind == nkSym and sfNoInit in call[0].sym.flags +proc hasNoInit(call: CgNode): bool {.inline.} = + result = call[0].kind == cnkSym and sfNoInit in call[0].sym.flags proc isHarmlessStore(p: BProc; canRaise: bool; d: TLoc): bool = if d.k in {locTemp, locNone} or not canRaise: @@ -70,11 +70,11 @@ proc isHarmlessStore(p: BProc; canRaise: bool; d: TLoc): bool = else: result = false -proc exitCall(p: BProc, callee: PNode, canRaise: bool) = +proc exitCall(p: BProc, callee: CgNode, canRaise: bool) = ## Emits the exceptional control-flow related post-call logic. if p.config.exc == excGoto: if nimErrorFlagDisabled in p.flags: - if callee.kind == nkSym and sfNoReturn in callee.sym.flags and + if callee.kind == cnkSym and sfNoReturn in callee.sym.flags and canRaiseConservative(callee): # when using goto-exceptions, noreturn doesn't map to "doesn't return" # at the C-level. In order to still support dispatching to wrapper @@ -85,7 +85,7 @@ proc exitCall(p: BProc, callee: PNode, canRaise: bool) = elif canRaise: raiseExit(p) -proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, +proc fixupCall(p: BProc, le, ri: CgNode, d: var TLoc, callee, params: Rope) = let canRaise = canRaiseDisp(p, ri[0]) genLineDir(p, ri) @@ -135,16 +135,16 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, proc genBoundsCheck(p: BProc; arr, a, b: TLoc) -proc reifiedOpenArray(n: PNode): bool {.inline.} = +proc reifiedOpenArray(n: CgNode): bool {.inline.} = var x = n - while x.kind in {nkAddr, nkHiddenAddr, nkHiddenStdConv, nkHiddenDeref}: + while x.kind in {cnkAddr, cnkHiddenAddr, cnkHiddenConv, cnkDerefView}: x = x[0] - if x.kind == nkSym and x.sym.kind == skParam: + if x.kind == cnkSym and x.sym.kind == skParam: result = false else: result = true -proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType): (Rope, Rope) = +proc genOpenArraySlice(p: BProc; q: CgNode; formalType, destType: PType): (Rope, Rope) = var a, b, c: TLoc initLocExpr(p, q[1], a) initLocExpr(p, q[2], b) @@ -188,17 +188,17 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType): (Rope, else: internalError(p.config, "openArrayLoc: " & typeToString(a.t)) -proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope = +proc openArrayLoc(p: BProc, formalType: PType, n: CgNode): Rope = var q = skipConv(n) var skipped = false - while q.kind == nkStmtListExpr and q.len > 0: + while q.kind == cnkStmtListExpr and q.len > 0: skipped = true q = q.lastSon if getMagic(q) == mSlice: # magic: pass slice to openArray: if skipped: q = skipConv(n) - while q.kind == nkStmtListExpr and q.len > 0: + while q.kind == cnkStmtListExpr and q.len > 0: for i in 0.. 0 and isInactiveDestructorCall(p, ri): return if ri[0].typ.skipTypes({tyGenericInst, tyAlias, tySink}).callConv == ccClosure: @@ -402,4 +402,4 @@ proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) = else: genPrefixCall(p, le, ri, d) -proc genCall(p: BProc, e: PNode, d: var TLoc) = genAsgnCall(p, nil, e, d) +proc genCall(p: BProc, e: CgNode, d: var TLoc) = genAsgnCall(p, nil, e, d) diff --git a/compiler/backend/ccgexprs.nim b/compiler/backend/ccgexprs.nim index 94c7fed92e5..d926d450ceb 100644 --- a/compiler/backend/ccgexprs.nim +++ b/compiler/backend/ccgexprs.nim @@ -9,7 +9,7 @@ # included from cgen.nim -proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode, +proc getNullValueAuxT(p: BProc; orig, t: PType; obj: PNode, constOrNil: CgNode, result: var Rope; count: var int; isConst: bool, info: TLineInfo) @@ -51,11 +51,11 @@ proc intLiteral(p: BProc, i: Int128, ty: PType): Rope = else: "(($1) $2)" % [getTypeDesc(p.module, ty), intLiteral(i)] -proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = +proc genLiteral(p: BProc, n: CgNode, ty: PType): Rope = case n.kind - of nkCharLit..nkUInt64Lit: + of cnkIntLit, cnkUIntLit: result = intLiteral(p, getInt(n), ty) - of nkNilLit: + of cnkNilLit: let k = if ty == nil: tyPointer else: skipTypes(ty, abstractVarRange).kind if k == tyProc and skipTypes(ty, abstractVarRange).callConv == ccClosure: let id = getOrPut(p.module.dataCache, n, p.module.labels) @@ -70,7 +70,7 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = result = rope("NIM_NIL") else: result = "(($1) NIM_NIL)" % [getTypeDesc(p.module, ty)] - of nkStrLit..nkTripleStrLit: + of cnkStrLit: let k = if ty == nil: tyString else: skipTypes(ty, abstractVarRange + {tyStatic, tyUserTypeClass, tyUserTypeClassInst}).kind case k @@ -82,18 +82,16 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = result = genStringLiteral(p.module, n) else: result = makeCString(n.strVal) - of nkFloatLit, nkFloat64Lit: + of cnkFloatLit: if ty.kind == tyFloat32: result = rope(n.floatVal.float32.toStrMaxPrecision) else: result = rope(n.floatVal.toStrMaxPrecision) - of nkFloat32Lit: - result = rope(n.floatVal.float32.toStrMaxPrecision) else: internalError(p.config, n.info, "genLiteral(" & $n.kind & ')') result = "" -proc genLiteral(p: BProc, n: PNode): Rope = +proc genLiteral(p: BProc, n: CgNode): Rope = result = genLiteral(p, n, n.typ) proc bitSetToWord(s: TBitSet, size: int): BiggestUInt = @@ -121,7 +119,7 @@ proc genRawSetData(cs: TBitSet, size: int): Rope = else: result = intLiteral(cast[BiggestInt](bitSetToWord(cs, size))) -proc genSetNode(p: BProc, n: PNode): Rope = +proc genSetNode(p: BProc, n: CgNode): Rope = var size = int(getSize(p.config, n.typ)) let cs = toBitSet(p.config, n) if size > 8: @@ -238,7 +236,7 @@ proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) = else: d = s # ``d`` is free, so fill it with ``s`` -proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) = +proc putDataIntoDest(p: BProc, d: var TLoc, n: CgNode, r: Rope) = var a: TLoc if d.k != locNone: # need to generate an assignment here @@ -252,7 +250,7 @@ proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) = d.lode = n d.r = r -proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) = +proc putIntoDest(p: BProc, d: var TLoc, n: CgNode, r: Rope; s=OnUnknown) = var a: TLoc if d.k != locNone: # need to generate an assignment here @@ -266,27 +264,27 @@ proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) = d.lode = n d.r = r -proc binaryStmt(p: BProc, e: PNode, d: var TLoc, op: string) = +proc binaryStmt(p: BProc, e: CgNode, d: var TLoc, op: string) = var a, b: TLoc if d.k != locNone: internalError(p.config, e.info, "binaryStmt") initLocExpr(p, e[1], a) initLocExpr(p, e[2], b) lineCg(p, cpsStmts, "$1 $2 $3;$n", [rdLoc(a), op, rdLoc(b)]) -proc binaryStmtAddr(p: BProc, e: PNode, d: var TLoc, cpname: string) = +proc binaryStmtAddr(p: BProc, e: CgNode, d: var TLoc, cpname: string) = var a, b: TLoc if d.k != locNone: internalError(p.config, e.info, "binaryStmtAddr") initLocExpr(p, e[1], a) initLocExpr(p, e[2], b) lineCg(p, cpsStmts, "#$1($2, $3);$n", [cpname, byRefLoc(p, a), rdLoc(b)]) -template unaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) = +template unaryStmt(p: BProc, e: CgNode, d: var TLoc, frmt: string) = var a: TLoc if d.k != locNone: internalError(p.config, e.info, "unaryStmt") initLocExpr(p, e[1], a) lineCg(p, cpsStmts, frmt, [rdLoc(a)]) -template binaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) = +template binaryExpr(p: BProc, e: CgNode, d: var TLoc, frmt: string) = var a, b: TLoc assert(e[1].typ != nil) assert(e[2].typ != nil) @@ -294,7 +292,7 @@ template binaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) = initLocExpr(p, e[2], b) putIntoDest(p, d, e, ropecg(p.module, frmt, [rdLoc(a), rdLoc(b)])) -template binaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) = +template binaryExprChar(p: BProc, e: CgNode, d: var TLoc, frmt: string) = var a, b: TLoc assert(e[1].typ != nil) assert(e[2].typ != nil) @@ -302,12 +300,12 @@ template binaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) = initLocExpr(p, e[2], b) putIntoDest(p, d, e, ropecg(p.module, frmt, [a.rdCharLoc, b.rdCharLoc])) -template unaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) = +template unaryExpr(p: BProc, e: CgNode, d: var TLoc, frmt: string) = var a: TLoc initLocExpr(p, e[1], a) putIntoDest(p, d, e, ropecg(p.module, frmt, [rdLoc(a)])) -template unaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) = +template unaryExprChar(p: BProc, e: CgNode, d: var TLoc, frmt: string) = var a: TLoc initLocExpr(p, e[1], a) putIntoDest(p, d, e, ropecg(p.module, frmt, [rdCharLoc(a)])) @@ -327,7 +325,7 @@ template binaryArithOverflowRaw(p: BProc, t: PType, a, b: TLoc; raiseInstr(p)]) result -proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = +proc binaryArithOverflow(p: BProc, e: CgNode, d: var TLoc, m: TMagic) = const prc: array[mAddI..mPred, string] = [ "nimAddInt", "nimSubInt", @@ -362,7 +360,7 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = if t.kind == tyInt64: prc64[m] else: prc[m]) putIntoDest(p, d, e, "($#)($#)" % [getTypeDesc(p.module, e.typ), res]) -proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = +proc unaryArithOverflow(p: BProc, e: CgNode, d: var TLoc, m: TMagic) = var a: TLoc t: PType @@ -382,7 +380,7 @@ proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = else: assert(false, $m) -proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = +proc binaryArith(p: BProc, e: CgNode, d: var TLoc, op: TMagic) = var a, b: TLoc s, k: BiggestInt @@ -442,7 +440,7 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = else: assert(false, $op) -proc genEqProc(p: BProc, e: PNode, d: var TLoc) = +proc genEqProc(p: BProc, e: CgNode, d: var TLoc) = var a, b: TLoc assert(e[1].typ != nil) assert(e[2].typ != nil) @@ -454,14 +452,14 @@ proc genEqProc(p: BProc, e: PNode, d: var TLoc) = else: putIntoDest(p, d, e, "($1 == $2)" % [rdLoc(a), rdLoc(b)]) -proc genIsNil(p: BProc, e: PNode, d: var TLoc) = +proc genIsNil(p: BProc, e: CgNode, d: var TLoc) = let t = skipTypes(e[1].typ, abstractRange) if t.kind == tyProc and t.callConv == ccClosure: unaryExpr(p, e, d, "($1.ClP_0 == 0)") else: unaryExpr(p, e, d, "($1 == 0)") -proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = +proc unaryArith(p: BProc, e: CgNode, d: var TLoc, op: TMagic) = var a: TLoc t: PType @@ -486,7 +484,7 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = else: assert false, $op -proc genDeref(p: BProc, e: PNode, d: var TLoc) = +proc genDeref(p: BProc, e: CgNode, d: var TLoc) = let mt = mapType(p.config, e[0].typ, mapTypeChooser(e[0])) if mt in {ctArray, ctPtrToArray} and lfEnforceDeref notin d.flags: # XXX the amount of hacks for C's arrays is incredible, maybe we should @@ -528,7 +526,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) = discard getTypeDesc(p.module, e.typ, skVar) putIntoDest(p, d, e, "(*$1)" % [rdLoc(a)], a.storage) -proc genAddr(p: BProc, e: PNode, mutate: bool, d: var TLoc) = +proc genAddr(p: BProc, e: CgNode, mutate: bool, d: var TLoc) = if mapType(p.config, e[0].typ, mapTypeChooser(e[0])) == ctArray: expr(p, e[0], d) else: @@ -544,26 +542,23 @@ proc genAddr(p: BProc, e: PNode, mutate: bool, d: var TLoc) = template inheritLocation(d: var TLoc, a: TLoc) = if d.k == locNone: d.storage = a.storage -proc genRecordFieldAux(p: BProc, e: PNode, d, a: var TLoc) = +proc genRecordFieldAux(p: BProc, e: CgNode, d, a: var TLoc) = initLocExpr(p, e[0], a) - if e[1].kind != nkSym: internalError(p.config, e.info, "genRecordFieldAux") + internalAssert(p.config, e[1].kind == cnkSym, e.info, "genRecordFieldAux") d.inheritLocation(a) discard getTypeDesc(p.module, a.t) # fill the record's fields.loc -proc genTupleElem(p: BProc, e: PNode, d: var TLoc) = +proc genTupleElem(p: BProc, e: CgNode, d: var TLoc) = var a: TLoc - i: int initLocExpr(p, e[0], a) let tupType = a.t.skipTypes(abstractInst+{tyVar}) assert tupType.kind == tyTuple d.inheritLocation(a) discard getTypeDesc(p.module, a.t) # fill the record's fields.loc var r = rdLoc(a) - case e[1].kind - of nkIntLit..nkUInt64Lit: i = int(e[1].intVal) - else: internalError(p.config, e.info, "genTupleElem") - r.addf(".Field$1", [rope(i)]) + internalAssert(p.config, e[1].kind == cnkIntLit, e.info) + r.addf(".Field$1", [rope(e[1].intVal)]) putIntoDest(p, d, e, r, a.storage) proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope; @@ -581,7 +576,7 @@ proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope; ty = ty[0] if result == nil: internalError(p.config, field.info, "genCheckedRecordField") -proc genRecordField(p: BProc, e: PNode, d: var TLoc) = +proc genRecordField(p: BProc, e: CgNode, d: var TLoc) = var a: TLoc genRecordFieldAux(p, e, d, a) var r = rdLoc(a) @@ -599,18 +594,18 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) = r.addf(".$1", [p.fieldName(field)]) putIntoDest(p, d, e, r, a.storage) -proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) +proc genInExprAux(p: BProc, e: CgNode, a, b, d: var TLoc) -proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = +proc genFieldCheck(p: BProc, e: CgNode, obj: Rope, field: PSym) = var test, u, v: TLoc for i in 1..= 0: if (firstOrd(p.config, b.t) < firstOrd(p.config, ty)) or @@ -722,14 +717,13 @@ proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = indexSpec: ( usedIdx: idx, minIdx: firstOrd(p.config, ty), - maxIdx: lastOrd(p.config, ty)), - ast: y)) + maxIdx: lastOrd(p.config, ty)))) d.inheritLocation(a) putIntoDest(p, d, n, ropecg(p.module, "$1[($2)- $3]", [rdLoc(a), rdCharLoc(b), first]), a.storage) -proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) = +proc genCStringElem(p: BProc, n, x, y: CgNode, d: var TLoc) = var a, b: TLoc initLocExpr(p, x, a) initLocExpr(p, y, b) @@ -764,7 +758,7 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) = [rdLoc(a), rdLoc(b), lenExpr(p, arr), raiseInstr(p)]) else: discard -proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = +proc genOpenArrayElem(p: BProc, n, x, y: CgNode, d: var TLoc) = var a, b: TLoc initLocExpr(p, x, a) initLocExpr(p, y, b) @@ -784,7 +778,7 @@ proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = putIntoDest(p, d, n, ropecg(p.module, "$1.Field0[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage) -proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = +proc genSeqElem(p: BProc, n, x, y: CgNode, d: var TLoc) = var a, b: TLoc initLocExpr(p, x, a) initLocExpr(p, y, b) @@ -804,7 +798,7 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = putIntoDest(p, d, n, ropecg(p.module, "$1$3[$2]", [rdLoc(a), rdCharLoc(b), dataField(p)]), a.storage) -proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) = +proc genBracketExpr(p: BProc; n: CgNode; d: var TLoc) = var ty = skipTypes(n[0].typ, abstractVarRange + tyUserTypeClasses) if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange) case ty.kind @@ -817,10 +811,10 @@ proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) = else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')') discard getTypeDesc(p.module, n.typ) -proc genEcho(p: BProc, n: PNode) = +proc genEcho(p: BProc, n: CgNode) = # this unusual way of implementing it ensures that e.g. ``echo("hallo", 45)`` # is threadsafe. - internalAssert p.config, n.kind == nkBracket + internalAssert p.config, n.kind == cnkArrayConstr block: if n.len == 0: linefmt(p, cpsStmts, "#echoBinSafe(NIM_NIL, $1);$n", [n.len]) @@ -832,7 +826,7 @@ proc genEcho(p: BProc, n: PNode) = proc strLoc(p: BProc; d: TLoc): Rope = result = byRefLoc(p, d) -proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = +proc genStrConcat(p: BProc, e: CgNode, d: var TLoc) = # # s = 'Hello ' & name & ', how do you feel?' & 'z' # @@ -861,7 +855,7 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = inc(L) appends.add(ropecg(p.module, "#appendChar($1, $2);$n", [strLoc(p, tmp), rdLoc(a)])) else: - if e[i + 1].kind in {nkStrLit..nkTripleStrLit}: + if e[i + 1].kind == cnkStrLit: inc(L, e[i + 1].strVal.len) else: lens.add(lenExpr(p, a)) @@ -874,7 +868,7 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = else: genAssignment(p, d, tmp) -proc genStrAppend(p: BProc, e: PNode, d: var TLoc) = +proc genStrAppend(p: BProc, e: CgNode, d: var TLoc) = # # s &= 'Hello ' & name & ', how do you feel?' & 'z' # // BUG: what if s is on the left side too? @@ -900,7 +894,7 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) = appends.add(ropecg(p.module, "#appendChar($1, $2);$n", [strLoc(p, dest), rdLoc(a)])) else: - if e[i + 2].kind in {nkStrLit..nkTripleStrLit}: + if e[i + 2].kind == cnkStrLit: inc(L, e[i + 2].strVal.len) else: lens.add(lenExpr(p, a)) @@ -912,7 +906,7 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) = [byRefLoc(p, dest), lens, L]) p.s(cpsStmts).add appends -proc genReset(p: BProc, n: PNode) = +proc genReset(p: BProc, n: CgNode) = var a: TLoc initLocExpr(p, n[1], a) specializeReset(p, a) @@ -921,7 +915,7 @@ proc genReset(p: BProc, n: PNode) = [addrLoc(p.config, a), genTypeInfoV1(p.module, skipTypes(a.t, {tyVar}), n.info)]) -proc genDefault(p: BProc; n: PNode; d: var TLoc) = +proc genDefault(p: BProc; n: CgNode; d: var TLoc) = if d.k == locNone: getTemp(p, n.typ, d, needsInit=true) else: resetLoc(p, d) @@ -949,7 +943,7 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool; doInitObj # set the object type: genObjectInit(p, cpsStmts, bt, a, constructRefObj) -proc genNew(p: BProc, e: PNode) = +proc genNew(p: BProc, e: CgNode) = var a: TLoc initLocExpr(p, e[1], a) # 'genNew' also handles 'unsafeNew': @@ -960,7 +954,7 @@ proc genNew(p: BProc, e: PNode) = else: rawGenNew(p, a, "", needsInit = true) -proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) = +proc genNewSeqOfCap(p: BProc; e: CgNode; d: var TLoc) = let seqtype = skipTypes(e.typ, abstractVarRange) var a: TLoc initLocExpr(p, e[1], a) @@ -971,7 +965,7 @@ proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) = getSeqPayloadType(p.module, seqtype), ]) -proc rawConstExpr(p: BProc, n: PNode; d: var TLoc) = +proc rawConstExpr(p: BProc, n: CgNode; d: var TLoc) = let t = n.typ discard getTypeDesc(p.module, t) # so that any fields are initialized let id = getOrPut(p.module.dataCache, n, p.module.labels) @@ -982,8 +976,8 @@ proc rawConstExpr(p: BProc, n: PNode; d: var TLoc) = p.module.s[cfsData].addf("static NIM_CONST $1 $2 = $3;$n", [getTypeDesc(p.module, t), d.r, genBracedInit(p, n, isConst = true, t)]) -proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = - if d.k == locNone and n.len > ord(n.kind == nkObjConstr) and n.isDeepConstExpr: +proc handleConstExpr(p: BProc, n: CgNode, d: var TLoc): bool = + if d.k == locNone and n.len > 0 and n.isDeepConstExpr: rawConstExpr(p, n, d) result = true else: @@ -1096,7 +1090,7 @@ proc specializeInitObject(p: BProc, accessor: Rope, typ: PType, else: discard -proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = +proc genObjConstr(p: BProc, e: CgNode, d: var TLoc) = #echo renderTree e, " ", e.isDeepConstExpr when false: # disabled optimization: see bug https://github.com/nim-lang/nim/issues/13240 @@ -1148,8 +1142,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = r = rdLoc(d) discard getTypeDesc(p.module, t) let ty = getUniqueType(t) - for i in 1..typeInfoV1" -proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) = +proc genGetTypeInfo(p: BProc, e: CgNode, d: var TLoc) = discard cgsym(p.module, "TNimType") let t = e[1].typ # ordinary static type information putIntoDest(p, d, e, genTypeInfoV1(p.module, t, e.info)) -proc genGetTypeInfoV2(p: BProc, e: PNode, d: var TLoc) = +proc genGetTypeInfoV2(p: BProc, e: CgNode, d: var TLoc) = let t = e[1].typ if isFinal(t) or e[0].sym.name.s != "getDynamicTypeInfo": # ordinary static type information @@ -1311,14 +1304,14 @@ proc genGetTypeInfoV2(p: BProc, e: PNode, d: var TLoc) = # use the dynamic type stored at offset 0: putIntoDest(p, d, e, rdMType(p, a, nilCheck)) -proc genAccessTypeField(p: BProc; e: PNode; d: var TLoc) = +proc genAccessTypeField(p: BProc; e: CgNode; d: var TLoc) = var a: TLoc initLocExpr(p, e[1], a) var nilCheck = "" # use the dynamic type stored at offset 0: putIntoDest(p, d, e, rdMType(p, a, nilCheck)) -template genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) = +template genDollar(p: BProc, n: CgNode, d: var TLoc, frmt: string) = var a: TLoc initLocExpr(p, n[1], a) a.r = ropecg(p.module, frmt, [rdLoc(a)]) @@ -1326,14 +1319,14 @@ template genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) = if d.k == locNone: getTemp(p, n.typ, d) genAssignment(p, d, a) -proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = +proc genArrayLen(p: BProc, e: CgNode, d: var TLoc, op: TMagic) = var a = e[1] - if a.kind == nkHiddenAddr: a = a[0] + if a.kind == cnkHiddenAddr: a = a[0] var typ = skipTypes(a.typ, abstractVar + tyUserTypeClasses) case typ.kind of tyOpenArray, tyVarargs: # Bug #9279, len(toOpenArray()) has to work: - if a.kind in nkCallKinds and a[0].kind == nkSym and a[0].sym.magic == mSlice: + if a.kind == cnkCall and a[0].kind == cnkSym and a[0].sym.magic == mSlice: # magic: pass slice to openArray: var b, c: TLoc initLocExpr(p, a[2], b) @@ -1377,17 +1370,16 @@ proc makePtrType(baseType: PType; idgen: IdGenerator): PType = result = newType(tyPtr, nextTypeId idgen, baseType.owner) addSonSkipIntLit(result, baseType, idgen) -proc makeAddr(n: PNode; idgen: IdGenerator): PNode = - if n.kind == nkHiddenAddr: +proc makeAddr(n: CgNode; idgen: IdGenerator): CgNode = + if n.kind == cnkHiddenAddr: result = n else: - result = newTree(nkHiddenAddr, n) - result.typ = makePtrType(n.typ, idgen) + result = newExpr(cnkHiddenAddr, n.info, makePtrType(n.typ, idgen), [n]) -proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) = +proc genSetLengthStr(p: BProc, e: CgNode, d: var TLoc) = binaryStmtAddr(p, e, d, "setLengthStrV2") -proc genSwap(p: BProc, e: PNode, d: var TLoc) = +proc genSwap(p: BProc, e: CgNode, d: var TLoc) = # swap(a, b) --> # temp = a # a = b @@ -1413,10 +1405,10 @@ proc rdSetElemLoc(conf: ConfigRef; a: TLoc, typ: PType): Rope = if firstOrd(conf, setType) != 0: result = "($1- $2)" % [result, rope(firstOrd(conf, setType))] -proc fewCmps(conf: ConfigRef; s: PNode): bool = +proc fewCmps(conf: ConfigRef; s: CgNode): bool = # this function estimates whether it is better to emit code # for constructing the set or generating a bunch of comparisons directly - if s.kind != nkCurly: return false + if s.kind != cnkSetConstr: return false if (getSize(conf, s.typ) <= conf.target.intSize) and isDeepConstExpr(s): result = false # it is better to emit the set generation code elif elemType(s.typ).kind in {tyInt, tyInt16..tyInt64}: @@ -1424,10 +1416,10 @@ proc fewCmps(conf: ConfigRef; s: PNode): bool = else: result = s.len <= 8 # 8 seems to be a good value -template binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) = +template binaryExprIn(p: BProc, e: CgNode, a, b, d: var TLoc, frmt: string) = putIntoDest(p, d, e, frmt % [rdLoc(a), rdSetElemLoc(p.config, b, a.t)]) -proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) = +proc genInExprAux(p: BProc, e: CgNode, a, b, d: var TLoc) = case int(getSize(p.config, skipTypes(e[1].typ, abstractVar))) of 1: binaryExprIn(p, e, a, b, d, "(($1 &((NU8)1<<((NU)($2)&7U)))!=0)") of 2: binaryExprIn(p, e, a, b, d, "(($1 &((NU16)1<<((NU)($2)&15U)))!=0)") @@ -1435,21 +1427,21 @@ proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) = of 8: binaryExprIn(p, e, a, b, d, "(($1 &((NU64)1<<((NU)($2)&63U)))!=0)") else: binaryExprIn(p, e, a, b, d, "(($1[(NU)($2)>>3] &(1U<<((NU)($2)&7U)))!=0)") -template binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) = +template binaryStmtInExcl(p: BProc, e: CgNode, d: var TLoc, frmt: string) = var a, b: TLoc assert(d.k == locNone) initLocExpr(p, e[1], a) initLocExpr(p, e[2], b) lineF(p, cpsStmts, frmt, [rdLoc(a), rdSetElemLoc(p.config, b, a.t)]) -proc genInOp(p: BProc, e: PNode, d: var TLoc) = +proc genInOp(p: BProc, e: CgNode, d: var TLoc) = var a, b, x, y: TLoc - if (e[1].kind == nkCurly) and fewCmps(p.config, e[1]): + if (e[1].kind == cnkSetConstr) and fewCmps(p.config, e[1]): # a set constructor but not a constant set: # do not emit the set, but generate a bunch of comparisons; and if we do # so, we skip the unnecessary range check: This is a semantical extension # that code now relies on. :-/ XXX - let ea = if e[2].kind in {nkChckRange, nkChckRange64}: + let ea = if e[2].kind in {cnkChckRange, cnkChckRange64}: e[2][0] else: e[2] @@ -1459,7 +1451,7 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) = b.r = rope("(") for i in 0..= $2 && $1 <= $3", @@ -1480,7 +1472,7 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) = initLocExpr(p, e[2], b) genInExprAux(p, e, a, b, d) -proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = +proc genSetOp(p: BProc, e: CgNode, d: var TLoc, op: TMagic) = const lookupOpr: array[mLeSet..mMinusSet, string] = [ "for ($1 = 0; $1 < $2; $1++) { $n" & @@ -1565,10 +1557,10 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mInSet: genInOp(p, e, d) else: internalError(p.config, e.info, "genSetOp") -proc genOrd(p: BProc, e: PNode, d: var TLoc) = +proc genOrd(p: BProc, e: CgNode, d: var TLoc) = unaryExprChar(p, e, d, "$1") -proc genSomeCast(p: BProc, e: PNode, d: var TLoc) = +proc genSomeCast(p: BProc, e: CgNode, d: var TLoc) = const ValueTypes = {tyTuple, tyObject, tyArray, tyOpenArray, tyVarargs, tyUncheckedArray} # we use whatever C gives us. Except if we have a value-type, we need to go @@ -1598,7 +1590,7 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, d, e, "(($1) ($2))" % [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage) -proc genCast(p: BProc, e: PNode, d: var TLoc) = +proc genCast(p: BProc, e: CgNode, d: var TLoc) = const ValueTypes = {tyFloat..tyFloat128, tyTuple, tyObject, tyArray} let destt = skipTypes(e.typ, abstractRange) @@ -1622,7 +1614,7 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) = # C code; plus it's the right thing to do for closures: genSomeCast(p, e, d) -proc genRangeChck(p: BProc, n: PNode, d: var TLoc) = +proc genRangeChck(p: BProc, n: CgNode, d: var TLoc) = var a: TLoc var dest = skipTypes(n.typ, abstractVar) initLocExpr(p, n[0], a) @@ -1658,14 +1650,14 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc) = putIntoDest(p, d, n, "(($1) ($2))" % [getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage) -proc genConv(p: BProc, e: PNode, d: var TLoc) = +proc genConv(p: BProc, e: CgNode, d: var TLoc) = let destType = e.typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink}) if sameBackendType(destType, e[1].typ): expr(p, e[1], d) else: genSomeCast(p, e, d) -proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) = +proc convStrToCStr(p: BProc, n: CgNode, d: var TLoc) = var a: TLoc initLocExpr(p, n[0], a) putIntoDest(p, d, n, @@ -1673,29 +1665,29 @@ proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) = # "($1 ? $1->data : (NCSTRING)\"\")" % [a.rdLoc], a.storage) -proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) = +proc convCStrToStr(p: BProc, n: CgNode, d: var TLoc) = var a: TLoc initLocExpr(p, n[0], a) putIntoDest(p, d, n, ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]), a.storage) -proc genStrEquals(p: BProc, e: PNode, d: var TLoc) = +proc genStrEquals(p: BProc, e: CgNode, d: var TLoc) = var x: TLoc var a = e[1] var b = e[2] - if a.kind in {nkStrLit..nkTripleStrLit} and a.strVal == "": + if a.kind == cnkStrLit and a.strVal == "": initLocExpr(p, e[2], x) putIntoDest(p, d, e, ropecg(p.module, "($1 == 0)", [lenExpr(p, x)])) - elif b.kind in {nkStrLit..nkTripleStrLit} and b.strVal == "": + elif b.kind == cnkStrLit and b.strVal == "": initLocExpr(p, e[1], x) putIntoDest(p, d, e, ropecg(p.module, "($1 == 0)", [lenExpr(p, x)])) else: binaryExpr(p, e, d, "#eqStrings($1, $2)") -proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) = +proc binaryFloatArith(p: BProc, e: CgNode, d: var TLoc, m: TMagic) = if {optNaNCheck, optInfCheck} * p.options != {}: const opr: array[mAddF64..mDivF64, string] = ["+", "-", "*", "/"] var a, b: TLoc @@ -1713,10 +1705,10 @@ proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) = else: binaryArith(p, e, d, m) -proc skipAddr(n: PNode): PNode = - result = if n.kind in {nkAddr, nkHiddenAddr}: n[0] else: n +proc skipAddr(n: CgNode): CgNode = + result = if n.kind in {cnkAddr, cnkHiddenAddr}: n[0] else: n -proc genWasMoved(p: BProc; n: PNode) = +proc genWasMoved(p: BProc; n: CgNode) = var a: TLoc let n1 = n[1].skipAddr if p.withinBlockLeaveActions > 0 and notYetAlive(n1): @@ -1727,7 +1719,7 @@ proc genWasMoved(p: BProc; n: PNode) = #linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n", # [addrLoc(p.config, a), getTypeDesc(p.module, a.t)]) -proc genMove(p: BProc; n: PNode; d: var TLoc) = +proc genMove(p: BProc; n: CgNode; d: var TLoc) = var a: TLoc initLocExpr(p, n[1].skipAddr, a) if n.len == 4: @@ -1742,7 +1734,7 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) = genAssignment(p, d, a) resetLoc(p, a) -proc genDestroy(p: BProc; n: PNode) = +proc genDestroy(p: BProc; n: CgNode) = let arg = n[1].skipAddr let t = arg.typ.skipTypes(abstractInst) case t.kind @@ -1766,7 +1758,7 @@ proc genDestroy(p: BProc; n: PNode) = [rdLoc(a), getTypeDesc(p.module, t.lastSon)]) else: discard "nothing to do" -proc genSlice(p: BProc; e: PNode; d: var TLoc) = +proc genSlice(p: BProc; e: CgNode; d: var TLoc) = let (x, y) = genOpenArraySlice(p, e, e.typ, e.typ.lastSon) if d.k == locNone: getTemp(p, e.typ, d) linefmt(p, cpsStmts, "$1.Field0 = $2; $1.Field1 = $3;$n", [rdLoc(d), x, y]) @@ -1774,7 +1766,7 @@ proc genSlice(p: BProc; e: PNode; d: var TLoc) = localReport(p.config, e.info, "invalid context for 'toOpenArray'; " & "'toOpenArray' is only valid within a call expression") -proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = +proc genMagicExpr(p: BProc, e: CgNode, d: var TLoc, op: TMagic) = case op of mNot..mUnaryMinusF64: unaryArith(p, e, d, op) of mUnaryMinusI..mAbsI: unaryArithOverflow(p, e, d, op) @@ -1834,10 +1826,11 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = let t = e[1].typ.skipTypes({tyTypeDesc}) putIntoDest(p, d, e, "((NI)NIM_ALIGNOF($1))" % [getTypeDesc(p.module, t, skVar)]) of mOffsetOf: - var dotExpr: PNode - if e[1].kind == nkDotExpr: + var dotExpr: CgNode + case e[1].kind + of cnkFieldAccess: dotExpr = e[1] - elif e[1].kind == nkCheckedFieldExpr: + of cnkCheckedFieldAccess: dotExpr = e[1][0] else: internalError(p.config, e.info, "unknown ast") @@ -1880,10 +1873,10 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mDeepCopy: if p.config.selectedGC in {gcArc, gcOrc} and optEnableDeepCopy notin p.config.globalOptions: - localReport(p.config, e, reportSem rsemRequiresDeepCopyEnabled) + localReport(p.config, e.info, reportSem rsemRequiresDeepCopyEnabled) var a, b: TLoc - let x = if e[1].kind in {nkAddr, nkHiddenAddr}: e[1][0] else: e[1] + let x = if e[1].kind in {cnkAddr, cnkHiddenAddr}: e[1][0] else: e[1] initLocExpr(p, x, a) initLocExpr(p, e[2], b) genDeepCopy(p, a, b) @@ -1911,7 +1904,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = echo p.prc.name.s, " ", p.prc.id, " ", p.prc.flags, " ", p.prc.ast[genericParamsPos].kind internalError(p.config, e.info, "genMagicExpr: " & $op) -proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = +proc genSetConstr(p: BProc, e: CgNode, d: var TLoc) = # example: { a..b, c, d, e, f..g } # we have to emit an expression of the form: # nimZeroMem(tmp, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c); @@ -1924,8 +1917,8 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = # big set: linefmt(p, cpsStmts, "#nimZeroMem($1, sizeof($2));$n", [rdLoc(d), getTypeDesc(p.module, e.typ)]) - for it in e.sons: - if it.kind == nkRange: + for it in e.items: + if it.kind == cnkRange: getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), idx) # our counter initLocExpr(p, it[0], a) initLocExpr(p, it[1], b) @@ -1940,8 +1933,8 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = # small set var ts = "NU" & $(getSize(p.config, e.typ) * 8) lineF(p, cpsStmts, "$1 = 0;$n", [rdLoc(d)]) - for it in e.sons: - if it.kind == nkRange: + for it in e.items: + if it.kind == cnkRange: getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), idx) # our counter initLocExpr(p, it[0], a) initLocExpr(p, it[1], b) @@ -1955,7 +1948,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = "$1 |=(($3)(1)<<(($2)%(sizeof($3)*8)));$n", [rdLoc(d), rdSetElemLoc(p.config, a, e.typ), rope(ts)]) -proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = +proc genTupleConstr(p: BProc, n: CgNode, d: var TLoc) = var rec: TLoc if not handleConstExpr(p, n, d): let t = n.typ @@ -1967,12 +1960,12 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = rec.flags.incl(lfEnforceDeref) expr(p, it, rec) -proc isConstClosure(n: PNode): bool {.inline.} = - result = n[0].kind == nkSym and isRoutine(n[0].sym) and - n[1].kind == nkNilLit +proc isConstClosure(n: CgNode): bool {.inline.} = + result = n[0].kind == cnkSym and isRoutine(n[0].sym) and + n[1].kind == cnkNilLit -proc genClosure(p: BProc, n: PNode, d: var TLoc) = - assert n.kind in {nkPar, nkTupleConstr, nkClosure} +proc genClosure(p: BProc, n: CgNode, d: var TLoc) = + assert n.kind == cnkClosureConstr if isConstClosure(n): inc(p.module.labels) @@ -1984,8 +1977,8 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) = var tmp, a, b: TLoc initLocExpr(p, n[0], a) initLocExpr(p, n[1], b) - if n[0].skipConv.kind == nkClosure: - internalError(p.config, n.info, "closure to closure created") + internalAssert(p.config, n[0].skipConv.kind != cnkClosureConstr, n.info): + "closure to closure created" # tasyncawait.nim breaks with this optimization: when false: if d.k != locNone: @@ -1997,7 +1990,7 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) = [tmp.rdLoc, a.rdLoc, b.rdLoc]) putLocIntoDest(p, d, tmp) -proc genArrayConstr(p: BProc, n: PNode, d: var TLoc) = +proc genArrayConstr(p: BProc, n: CgNode, d: var TLoc) = var arr: TLoc if not handleConstExpr(p, n, d): if d.k == locNone: getTemp(p, n.typ, d) @@ -2012,17 +2005,15 @@ template genStmtListExprImpl(exprOrStmt) {.dirty.} = genStmts(p, n[i]) if n.len > 0: exprOrStmt -proc genStmtListExpr(p: BProc, n: PNode, d: var TLoc) = +proc genStmtListExpr(p: BProc, n: CgNode, d: var TLoc) = genStmtListExprImpl: expr(p, n[^1], d) -proc genStmtList(p: BProc, n: PNode) = +proc genStmtList(p: BProc, n: CgNode) = genStmtListExprImpl: genStmts(p, n[^1]) -from compiler/sem/parampatterns import isLValue - -proc upConv(p: BProc, n: PNode, d: var TLoc) = +proc upConv(p: BProc, n: CgNode, d: var TLoc) = var a: TLoc initLocExpr(p, n[0], a) let dest = skipTypes(n.typ, abstractPtrs) @@ -2047,9 +2038,9 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) = putIntoDest(p, d, n, "(*($1*) ($2))" % [getTypeDesc(p.module, dest), addrLoc(p.config, a)], a.storage) -proc downConv(p: BProc, n: PNode, d: var TLoc) = +proc downConv(p: BProc, n: CgNode, d: var TLoc) = var arg = n[0] - while arg.kind == nkObjDownConv: arg = arg[0] + while arg.kind == cnkObjDownConv: arg = arg[0] let dest = skipTypes(n.typ, abstractPtrs) let src = skipTypes(arg.typ, abstractPtrs) @@ -2072,7 +2063,7 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) = for i in 2..abs(inheritanceDiff(dest, src)): r.add(".Sup") putIntoDest(p, d, n, if isRef: "&" & r else: r, a.storage) -proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = +proc exprComplexConst(p: BProc, n: CgNode, d: var TLoc) = let t = n.typ discard getTypeDesc(p.module, t) # so that any fields are initialized let id = getOrPut(p.module.dataCache, n, p.module.labels) @@ -2112,18 +2103,18 @@ proc genConstDefinition*(q: BModule; sym: PSym) = let p = newProc(nil, q) q.s[cfsData].addf("N_LIB_PRIVATE NIM_CONST $1 $2 = $3;$n", [getTypeDesc(q, sym.typ), name, - genBracedInit(p, sym.ast, isConst = true, sym.typ)]) + genBracedInit(p, translate(sym.ast), isConst = true, sym.typ)]) # all constants need a loc: q.consts.put(sym, initLoc(locData, newSymNode(sym), name, OnStatic)) -proc expr(p: BProc, n: PNode, d: var TLoc) = +proc expr(p: BProc, n: CgNode, d: var TLoc) = when defined(nimCompilerStacktraceHints): frameMsg(p.config, n) p.currLineInfo = n.info case n.kind - of nkSym: + of cnkSym: var sym = n.sym case sym.kind of skProc, skConverter, skIterator, skFunc, skMethod: @@ -2135,7 +2126,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = putIntoDest(p, d, n, p.module.procs[sym].name, OnStack) of skConst: if isSimpleConst(sym.typ): - putIntoDest(p, d, n, genLiteral(p, sym.ast, sym.typ), OnStatic) + putIntoDest(p, d, n, genLiteral(p, translate(sym.ast), sym.typ), OnStatic) else: useConst(p.module, sym) putLocIntoDest(p, d, p.module.consts[sym]) @@ -2170,94 +2161,92 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = # local) putLocIntoDest(p, d, p.locals[sym]) else: internalError(p.config, n.info, "expr(" & $sym.kind & "); unknown symbol") - of nkStrLiterals: + of cnkStrLit: putDataIntoDest(p, d, n, genLiteral(p, n)) - of nkIntLiterals, nkFloatLiterals, nkNilLit: + of cnkIntLit, cnkUIntLit, cnkFloatLit, cnkNilLit: putIntoDest(p, d, n, genLiteral(p, n)) - of nkCall: + of cnkCall: genLineDir(p, n) # may be redundant, it is generated in fixupCall as well let op = n[0] if n.typ.isNil: # discard the value: var a: TLoc - if op.kind == nkSym and op.sym.magic != mNone: + if op.kind == cnkSym and op.sym.magic != mNone: genMagicExpr(p, n, a, op.sym.magic) else: genCall(p, n, a) else: # load it into 'd': - if op.kind == nkSym and op.sym.magic != mNone: + if op.kind == cnkSym and op.sym.magic != mNone: genMagicExpr(p, n, d, op.sym.magic) else: genCall(p, n, d) - of nkCurly: + of cnkSetConstr: if isDeepConstExpr(n) and n.len != 0: putIntoDest(p, d, n, genSetNode(p, n)) else: genSetConstr(p, n, d) - of nkBracket: + of cnkArrayConstr: if isDeepConstExpr(n) and n.len != 0: exprComplexConst(p, n, d) elif skipTypes(n.typ, abstractVarRange).kind == tySequence: genSeqConstr(p, n, d) else: genArrayConstr(p, n, d) - of nkTupleConstr: + of cnkTupleConstr: if n.typ != nil and n.typ.kind == tyProc and n.len == 2: genClosure(p, n, d) elif isDeepConstExpr(n) and n.len != 0: exprComplexConst(p, n, d) else: genTupleConstr(p, n, d) - of nkObjConstr: genObjConstr(p, n, d) - of nkCast: genCast(p, n, d) - of nkHiddenStdConv, nkConv: genConv(p, n, d) - of nkHiddenAddr, nkAddr: - if n[0].kind in {nkHiddenDeref, nkDerefExpr}: + of cnkObjConstr: genObjConstr(p, n, d) + of cnkCast: genCast(p, n, d) + of cnkHiddenConv, cnkConv: genConv(p, n, d) + of cnkHiddenAddr, cnkAddr: + if n[0].kind in {cnkDerefView, cnkDeref}: # views and ``ref``s also map to pointers at the C level. We collapse # ``&(*x)`` to just ``x`` expr(p, n[0][0], d) else: - let mutate = n.kind == nkHiddenAddr and n.typ.kind == tyVar + let mutate = n.kind == cnkHiddenAddr and n.typ.kind == tyVar genAddr(p, n, mutate, d) - of nkBracketExpr: genBracketExpr(p, n, d) - of nkDerefExpr, nkHiddenDeref: genDeref(p, n, d) - of nkDotExpr: genRecordField(p, n, d) - of nkCheckedFieldExpr: genCheckedRecordField(p, n, d) - of nkBlockStmt: genBlock(p, n) - of nkStmtListExpr: genStmtListExpr(p, n, d) - of nkStmtList: genStmtList(p, n) - of nkIfStmt: genIf(p, n) - of nkObjDownConv: downConv(p, n, d) - of nkObjUpConv: upConv(p, n, d) - of nkChckRangeF, nkChckRange64, nkChckRange: genRangeChck(p, n, d) - of nkStringToCString: convStrToCStr(p, n, d) - of nkCStringToString: convCStrToStr(p, n, d) - of nkClosure: genClosure(p, n, d) - - of nkEmpty: discard - of nkWhileStmt: genWhileStmt(p, n) - of nkVarSection, nkLetSection: genVarStmt(p, n) - of nkCaseStmt: genCase(p, n) - of nkReturnStmt: genReturnStmt(p, n) - of nkBreakStmt: genBreakStmt(p, n) - of nkAsgn, nkFastAsgn: + of cnkBracketAccess: genBracketExpr(p, n, d) + of cnkDeref, cnkDerefView: genDeref(p, n, d) + of cnkFieldAccess: genRecordField(p, n, d) + of cnkCheckedFieldAccess: genCheckedRecordField(p, n, d) + of cnkBlockStmt: genBlock(p, n) + of cnkStmtListExpr: genStmtListExpr(p, n, d) + of cnkStmtList: genStmtList(p, n) + of cnkIfStmt: genIf(p, n) + of cnkObjDownConv: downConv(p, n, d) + of cnkObjUpConv: upConv(p, n, d) + of cnkChckRangeF, cnkChckRange64, cnkChckRange: genRangeChck(p, n, d) + of cnkStringToCString: convStrToCStr(p, n, d) + of cnkCStringToString: convCStrToStr(p, n, d) + of cnkClosureConstr: genClosure(p, n, d) + of cnkEmpty: discard + of cnkRepeatStmt: genRepeatStmt(p, n) + of cnkDef: genDef(p, n) + of cnkCaseStmt: genCase(p, n) + of cnkReturnStmt: genReturnStmt(p, n) + of cnkBreakStmt: genBreakStmt(p, n) + of cnkAsgn, cnkFastAsgn: genAsgn(p, n) - of nkDiscardStmt: - let ex = n[0] + of cnkVoidStmt: genLineDir(p, n) var a: TLoc - initLocExprSingleUse(p, ex, a) + initLocExprSingleUse(p, n[0], a) line(p, cpsStmts, "(void)(" & a.r & ");\L") - of nkAsmStmt: genAsmStmt(p, n) - of nkTryStmt: + of cnkAsmStmt: genAsmStmt(p, n) + of cnkEmitStmt: genEmit(p, n) + of cnkTryStmt: assert p.config.exc == excGoto genTryGoto(p, n) - of nkRaiseStmt: genRaiseStmt(p, n) - of nkPragma: genPragma(p, n) - of nkType, nkNimNodeLit: - unreachable() - of nkWithSons + nkWithoutSons - codegenExprNodeKinds: + of cnkRaiseStmt: genRaiseStmt(p, n) + of cnkPragmaStmt: discard + of cnkInvalid, cnkType, cnkAstLit, cnkRange, cnkBinding, cnkExcept, + cnkFinally, cnkBranch: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind") proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope = @@ -2318,7 +2307,7 @@ proc caseObjDefaultBranch(obj: PNode; branch: Int128): int = return i assert(false, "unreachable") -proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode, +proc getNullValueAux(p: BProc; t: PType; obj: PNode, constOrNil: CgNode, result: var Rope; count: var int; isConst: bool, info: TLineInfo) = case obj.kind @@ -2331,10 +2320,10 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode, var branch = Zero if constOrNil != nil: ## find kind value, default is zero if not specified - for i in 1.. 0: result.add ",\n" - if it.kind == nkExprColonExpr: result.add genBracedInit(p, it[1], isConst, tup[i]) - else: result.add genBracedInit(p, it, isConst, tup[i]) + result.add genBracedInit(p, it, isConst, tup[i]) result.add("}\n") -proc genConstSeqV2(p: BProc, n: PNode, t: PType; isConst: bool): Rope = +proc genConstSeqV2(p: BProc, n: CgNode, t: PType; isConst: bool): Rope = let base = t.skipTypes(abstractInst)[0] var data = rope"{" for i in 0.. 10_000: - localReport(p.config, it, reportSem rsemTooManyEntriesForComputedGoto) + localReport(p.config, it.info, reportSem rsemTooManyEntriesForComputedGoto) return arraySize = toInt(aSize) if firstOrd(p.config, it[0].typ) != 0: - localReport(p.config, it, reportSem rsemExpectedLow0ForComputedGoto) + localReport(p.config, it.info, reportSem rsemExpectedLow0ForComputedGoto) return if casePos < 0: - localReport(p.config, n, reportSem rsemExpectedCaseForComputedGoto) + localReport(p.config, n.info, reportSem rsemExpectedCaseForComputedGoto) return var id = p.labels+1 @@ -289,8 +277,8 @@ proc genComputedGoto(p: BProc; n: PNode) = startBlock(p) let it = caseStmt[i] for j in 0.. stringCaseThreshold: var bitMask = math.nextPowerOfTwo(strings) - 1 var branches: seq[Rope] @@ -515,7 +492,7 @@ proc genStringCase(p: BProc, t: PNode) = var labId = p.labels for i in 1.. RangeExpandLimit: return true -proc ifSwitchSplitPoint(p: BProc, n: PNode): int = +proc ifSwitchSplitPoint(p: BProc, n: CgNode): int = for i in 1..= $2 && $1 <= $3) goto $4;$n", "if ($1 == $2) goto $3;$n") else: - if t[0].kind == nkSym and sfGoto in t[0].sym.flags: + if t[0].kind == cnkSym and sfGoto in t[0].sym.flags: genGotoForCase(p, t) else: genOrdinalCase(p, t) -proc bodyCanRaise(p: BProc; n: PNode): bool = +proc bodyCanRaise(p: BProc; n: CgNode): bool = case n.kind - of nkCallKinds: + of cnkCall: result = canRaiseDisp(p, n[0]) if not result: # also check the arguments: for i in 1 ..< n.len: if bodyCanRaise(p, n[i]): return true - of nkRaiseStmt: + of cnkRaiseStmt: result = true - of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, - nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef: + of cnkWithoutItems: result = false - else: - for i in 0 ..< safeLen(n): - if bodyCanRaise(p, n[i]): return true + of cnkWithItems - {cnkCall, cnkRaiseStmt}: + for it in n.items: + if bodyCanRaise(p, it): return true result = false -proc genTryGoto(p: BProc; t: PNode) = - let fin = if t[^1].kind == nkFinally: t[^1] else: nil +proc genTryGoto(p: BProc; t: CgNode) = + let fin = if t[^1].kind == cnkFinally: t[^1] else: nil inc p.labels let lab = p.labels - let hasExcept = t[1].kind == nkExceptBranch + let hasExcept = t[1].kind == cnkExcept if hasExcept: inc p.withinTryWithExcept p.nestedTryStmts.add((fin, false, Natural lab)) @@ -648,14 +631,14 @@ proc genTryGoto(p: BProc; t: PNode) = var errorFlagSet = false ## tracks whether the error flag is set to 'true' ## on a control-flow path connected to the finally section - template checkSetsErrorFlag(n: PNode) = + template checkSetsErrorFlag(n: CgNode) = if fin != nil and not errorFlagSet: errorFlagSet = bodyCanRaise(p, n) genStmts(p, t[0]) checkSetsErrorFlag(t[0]) - if 1 < t.len and t[1].kind == nkExceptBranch: + if 1 < t.len and t[1].kind == cnkExcept: startBlock(p, "if (NIM_UNLIKELY(*nimErr_)) {$n") else: startBlock(p) @@ -663,7 +646,7 @@ proc genTryGoto(p: BProc; t: PNode) = p.nestedTryStmts[^1].inExcept = true var i = 1 - while (i < t.len) and (t[i].kind == nkExceptBranch): + while (i < t.len) and (t[i].kind == cnkExcept): inc p.labels let nextExcept = p.labels @@ -680,7 +663,7 @@ proc genTryGoto(p: BProc; t: PNode) = else: var orExpr = "" for j in 0..= 1 and n[0].kind in {nkStrLit..nkTripleStrLit}: + if n.len >= 1 and n[0].kind == cnkStrLit: let sec = n[0].strVal if sec.startsWith("/*TYPESECTION*/"): result = cfsTypes elif sec.startsWith("/*VARSECTION*/"): result = cfsVars elif sec.startsWith("/*INCLUDESECTION*/"): result = cfsHeaders -proc genEmit(p: BProc, t: PNode) = - var s = genAsmOrEmitStmt(p, t[1]) +proc genEmit(p: BProc, t: CgNode) = + var s = genAsmOrEmitStmt(p, t) if sfTopLevel in p.prc.flags: # top level emit pragma? - let section = determineSection(t[1]) + let section = determineSection(t) genCLineDir(p.module.s[section], t.info, p.config) p.module.s[section].add(s) else: genLineDir(p, t) line(p, cpsStmts, s) -proc genPragma(p: BProc, n: PNode) = - for it in n.sons: - case whichPragma(it) - of wEmit: genEmit(p, it) - else: discard - when false: proc genCaseObjDiscMapping(p: BProc, e: PNode, t: PType, field: PSym; d: var TLoc) = const ObjDiscMappingProcSlot = -5 @@ -837,17 +814,17 @@ when false: call.add e expr(p, call, d) -proc asgnFieldDiscriminant(p: BProc, e: PNode) = +proc asgnFieldDiscriminant(p: BProc, e: CgNode) = var a, tmp: TLoc var dotExpr = e[0] - if dotExpr.kind == nkCheckedFieldExpr: dotExpr = dotExpr[0] + if dotExpr.kind == cnkCheckedFieldAccess: dotExpr = dotExpr[0] initLocExpr(p, e[0], a) getTemp(p, a.t, tmp) expr(p, e[1], tmp) genAssignment(p, a, tmp) -proc genAsgn(p: BProc, e: PNode) = - if e[0].kind == nkSym and sfGoto in e[0].sym.flags: +proc genAsgn(p: BProc, e: CgNode) = + if e[0].kind == cnkSym and sfGoto in e[0].sym.flags: genLineDir(p, e) genGotoVar(p, e[1]) elif optFieldCheck in p.options and isDiscriminantField(e[0]): @@ -866,7 +843,7 @@ proc genAsgn(p: BProc, e: PNode) = genLineDir(p, ri) loadInto(p, le, ri, a) -proc genStmts(p: BProc, t: PNode) = +proc genStmts(p: BProc, t: CgNode) = var a: TLoc let isPush = p.config.hasHint(rsemExtendedContext) diff --git a/compiler/backend/ccgtypes.nim b/compiler/backend/ccgtypes.nim index 6d08c75c709..49d123e4c82 100644 --- a/compiler/backend/ccgtypes.nim +++ b/compiler/backend/ccgtypes.nim @@ -215,7 +215,7 @@ proc addAbiCheck(m: BModule, t: PType, name: Rope) = # see `testCodegenABICheck` for example error message it generates -proc initResultParamLoc(conf: ConfigRef; param: PNode): TLoc = +proc initResultParamLoc(conf: ConfigRef; param: CgNode): TLoc = result = initLoc(locParam, param, "Result", OnStack) let t = param.sym.typ if mapReturnType(conf, t) != ctArray and isInvalidReturnType(conf, t): @@ -381,8 +381,8 @@ proc prepareParameters(m: BModule, t: PType): seq[TLoc] = else: OnStack - result[i] = initLoc(locParam, params[i], mangleParamName(m.config, param), - storage) + result[i] = initLoc(locParam, toSymNode(params[i]), + mangleParamName(m.config, param), storage) if ccgIntroducedPtr(m.config, param, t[0]): # the parameter is passed by address; mark it as indirect diff --git a/compiler/backend/ccgutils.nim b/compiler/backend/ccgutils.nim index 2191d644163..99e3adc7e41 100644 --- a/compiler/backend/ccgutils.nim +++ b/compiler/backend/ccgutils.nim @@ -16,8 +16,7 @@ import compiler/ast/[ wordrecg, ast, - types, - trees + types ], compiler/front/[ options @@ -28,21 +27,21 @@ import compiler/sem/[ ], compiler/backend/[ - cgendata + cgendata, + cgir ] -proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode = +proc getPragmaStmt*(n: CgNode, w: TSpecialWord): CgNode = case n.kind - of nkStmtList: + of cnkStmtList: for i in 0.. 0: @@ -500,7 +508,7 @@ proc localVarDecl(p: BProc; n: PNode): Rope = # generation p.locals.assign(s, loc) -proc assignLocalVar(p: BProc, n: PNode) = +proc assignLocalVar(p: BProc, n: CgNode) = let nl = if optLineDir in p.config.options: "" else: "\L" let decl = localVarDecl(p, n) & ";" & nl line(p, cpsLocals, decl) @@ -509,12 +517,12 @@ include ccgthreadvars proc varInDynamicLib(m: BModule, sym: PSym) -proc fillGlobalLoc*(m: BModule, s: PSym, n: PNode) = +proc fillGlobalLoc*(m: BModule, s: PSym) = + let n = CgNode(kind: cnkSym, info: s.info, typ: s.typ, sym: s) m.globals.put(s, initLoc(locGlobalVar, n, mangleName(m.g.graph, s), OnHeap)) -proc defineGlobalVar*(m: BModule, n: PNode) = - let s = n.sym - fillGlobalLoc(m, s, n) +proc defineGlobalVar*(m: BModule, s: PSym) = + fillGlobalLoc(m, s) assert s.id notin m.declaredThings assert findPendingModule(m, s) == m, "not the attached-to module" @@ -553,23 +561,23 @@ proc getLabel(p: BProc): TLabel = proc fixLabel(p: BProc, labl: TLabel) = lineF(p, cpsStmts, "$1: ;$n", [labl]) -proc genVarPrototype*(m: BModule, n: PNode) +proc genVarPrototype*(m: BModule, n: CgNode) proc genProcPrototype*(m: BModule, sym: PSym) -proc genStmts*(p: BProc, t: PNode) -proc expr(p: BProc, n: PNode, d: var TLoc) +proc genStmts*(p: BProc, t: CgNode) +proc expr(p: BProc, n: CgNode, d: var TLoc) proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) proc intLiteral(i: BiggestInt): Rope proc intLiteral(p: BProc, i: Int128, ty: PType): Rope -proc genLiteral(p: BProc, n: PNode): Rope +proc genLiteral(p: BProc, n: CgNode): Rope proc raiseExit(p: BProc) -proc initLocExpr(p: BProc, e: PNode, result: var TLoc) = +proc initLocExpr(p: BProc, e: CgNode, result: var TLoc) = initLoc(result, locNone, e, OnUnknown) expr(p, e, result) -proc initLocExprSingleUse(p: BProc, e: PNode, result: var TLoc) = +proc initLocExprSingleUse(p: BProc, e: CgNode, result: var TLoc) = initLoc(result, locNone, e, OnUnknown) - if e.kind in nkCallKinds and (e[0].kind != nkSym or e[0].sym.magic == mNone): + if e.kind == cnkCall and (e[0].kind != cnkSym or e[0].sym.magic == mNone): # We cannot check for tfNoSideEffect here because of mutable parameters. discard "bug #8202; enforce evaluation order for nested calls" # We may need to consider that 'f(g())' cannot be rewritten to 'tmp = g(); f(tmp)' @@ -680,31 +688,27 @@ proc closureSetup(p: BProc, prc: PSym) = p.config.internalAssert(ls.kind == nkSym, prc.info, "closure generation failed") var env = ls.sym #echo "created environment: ", env.id, " for ", prc.name.s - assignLocalVar(p, ls) + assignLocalVar(p, toSymNode(ls)) # generate cast assignment: linefmt(p, cpsStmts, "$1 = ($2) ClE_0;$n", [rdLoc(p.locals[env]), getTypeDesc(p.module, env.typ)]) -proc containsResult(n: PNode): bool = +func containsResult(n: CgNode): bool = result = false case n.kind - of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkFormalParams: - discard - of nkSym: + of cnkWithoutItems - {cnkSym}: + discard "ignore" + of cnkSym: if n.sym.kind == skResult: result = true - else: + of cnkWithItems: for i in 0.. 1 + + +proc isDeepConstExpr*(n: CgNode): bool = + case n.kind + of cnkLiterals, cnkNilLit: + result = true + of cnkSetConstr, cnkArrayConstr, cnkClosureConstr, cnkTupleConstr, cnkRange: + result = true + for it in n.items: + if not isDeepConstExpr(it): + result = false + break + of cnkObjConstr: + let t = n.typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink}) + if t.kind == tyRef: + # ref-constructions are never constant + return false + + result = true + for it in n.items: + if not isDeepConstExpr(it[1]): + result = false + break + else: + result = false + +proc getRoot*(n: CgNode): PSym = + ## ``getRoot`` takes a *path* ``n``. A path is an lvalue expression + ## like ``obj.x[i].y``. The *root* of a path is the symbol that can be + ## determined as the owner; ``obj`` in the example. + case n.kind + of cnkSym: + if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar, skParam}: + result = n.sym + of cnkFieldAccess, cnkBracketAccess, cnkDerefView, cnkDeref, + cnkObjUpConv, cnkObjDownConv, cnkCheckedFieldAccess, cnkHiddenAddr, + cnkAddr: + result = getRoot(n[0]) + of cnkHiddenConv, cnkConv: + result = getRoot(n[1]) + of cnkCall: + if getMagic(n) == mSlice: + result = getRoot(n[1]) + else: discard + +proc isLValue*(n: CgNode): bool = + ## Duplicate of `isLValue `_, + ## but simplified to the needs of the C code generator. + # XXX: remove this as soon as possible + case n.kind + of cnkEmpty: + n.typ.kind == tyVar + of cnkSym: + (n.sym.kind == skParam and n.sym.typ.kind in {tyVar, tySink}) or + n.sym.kind in {skVar, skResult, skTemp} + of cnkFieldAccess: + let t = skipTypes(n[0].typ, abstractInst-{tyTypeDesc}) + t.kind in {tyVar, tySink, tyPtr, tyRef} or + ((n[1].kind != cnkSym or sfDiscriminant notin n[1].sym.flags) and + isLValue(n[0])) + of cnkBracketAccess: + let t = skipTypes(n[0].typ, abstractInst-{tyTypeDesc}) + t.kind in {tyVar, tySink, tyPtr, tyRef} or isLValue(n[0]) + of cnkHiddenConv, cnkConv: + if skipTypes(n.typ, abstractPtrs-{tyTypeDesc}).kind in + {tyOpenArray, tyTuple, tyObject}: + isLValue(n[1]) + elif compareTypes(n.typ, n[1].typ, dcEqIgnoreDistinct): + isLValue(n[1]) + else: + false + of cnkDerefView: + let n0 = n[0] + n0.typ.kind != tyLent or (n0.kind == cnkSym and n0.sym.kind == skResult) + of cnkDeref, cnkHiddenAddr: + true + of cnkObjUpConv, cnkObjDownConv, cnkCheckedFieldAccess: + isLValue(n[0]) + of cnkCall: + (getMagic(n) == mSlice and isLValue(n[1])) or n.typ.kind in {tyVar} + of cnkStmtListExpr: + isLValue(n[^1]) + else: + false + +proc canRaiseConservative*(fn: CgNode): bool = + ## Duplicate of `canRaiseConservative `_. + if fn.kind == cnkSym and fn.sym.magic notin magicsThatCanRaise: + result = false + else: + result = true + +proc canRaise*(fn: CgNode): bool = + ## Duplicate of `canRaise `_. + if fn.kind == cnkSym and (fn.sym.magic notin magicsThatCanRaise or + {sfImportc, sfInfixCall} * fn.sym.flags == {sfImportc} or + sfGeneratedOp in fn.sym.flags): + result = false + elif fn.kind == cnkSym and fn.sym.magic == mEcho: + result = true + else: + if fn.typ != nil and fn.typ.n != nil and fn.typ.n[0].kind == nkSym: + result = false + else: + result = fn.typ != nil and fn.typ.n != nil and + ((fn.typ.n[0].len < effectListLen) or + (fn.typ.n[0][exceptionEffects] != nil and + fn.typ.n[0][exceptionEffects].safeLen > 0)) + +proc toBitSet*(conf: ConfigRef; s: CgNode): TBitSet = + ## Duplicate of `toBitSet `_ + bitSetInit(result, int(getSize(conf, s.typ))) + + var first, j: Int128 + first = firstOrd(conf, s.typ[0]) + for it in s.items: + if it.kind == cnkRange: + j = getOrdValue(it[0]) + while j <= getOrdValue(it[1]): + bitSetIncl(result, toInt64(j - first)) + inc(j) + else: + bitSetIncl(result, toInt64(getOrdValue(it) - first)) + +proc flattenStmts*(n: CgNode): CgNode = + ## Duplicate of `flattenStmts `_ + # XXX: this doesn't work as intended. The intention is to bring all 'def's + # to the top so that they can be special cased, but + # ``cnkStmtListExpr``s cannot be unnested, meaning that not all + # 'def's are brought to the top-level + proc unnestStmts(n: CgNode, result: var CgNode) = + case n.kind + of cnkStmtList: + for it in n.items: + unnestStmts(it, result) + else: + result.childs.add n + + result = CgNode(kind: cnkStmtList) + unnestStmts(n, result) + if result.len == 1: + result = result[0] + +proc toSymNode*(n: PNode): CgNode {.inline.} = + CgNode(kind: cnkSym, info: n.info, typ: n.typ, sym: n.sym) + +proc newSymNode*(s: PSym): CgNode {.inline.} = + CgNode(kind: cnkSym, info: s.info, typ: s.typ, sym: s) + +proc newStrNode*(str: sink string): CgNode {.inline.} = + CgNode(kind: cnkStrLit, info: unknownLineInfo, strVal: str) + +proc translate*(n: PNode): CgNode = + ## Compatibility routine for translating a ``PNode`` value-construction tree + ## to a ``CgNode`` tree. + case n.kind + of nkObjConstr: + result = newExpr(cnkObjConstr, n.info, n.typ) + for i, it in sliceIt(n.sons, 1, n.len-1): + result.childs.add translate(it) + of nkBracket: + result = newExpr(cnkArrayConstr, n.info, n.typ) + for it in n.items: + result.childs.add translate(it) + of nkCurly: + result = newExpr(cnkSetConstr, n.info, n.typ) + for it in n.items: + result.childs.add translate(it) + of nkTupleConstr: + result = newExpr(cnkTupleConstr, n.info, n.typ) + for it in n.items: + let it = if it.kind == nkExprColonExpr: it[1] else: it + result.childs.add translate(it) + of nkClosure: + result = newExpr(cnkClosureConstr, n.info, n.typ) + result.childs = @[translate(n[0]), translate(n[1])] + of nkRange: + result = newNode(cnkRange, n.info) + result.childs = @[translate(n[0]), translate(n[1])] + of nkSym: + result = newSymNode(n.sym) + result.info = n.info + of nkExprColonExpr: + result = newNode(cnkBinding, n.info) + result.childs = @[translate(n[0]), translate(n[1])] + of nkLiterals: + result = translateLit(n) + of nkNilLit: + result = newNode(cnkNilLit, n.info, n.typ) + else: + unreachable(n.kind) \ No newline at end of file diff --git a/compiler/backend/jsgen.nim b/compiler/backend/jsgen.nim index e658be40d0e..ef4c743195c 100644 --- a/compiler/backend/jsgen.nim +++ b/compiler/backend/jsgen.nim @@ -31,11 +31,11 @@ import intsets ], compiler/ast/[ - ast, - trees, + ast_idgen, + ast_query, + ast_types, idents, types, - wordrecg, renderer, lineinfos, astmsgs, @@ -50,11 +50,14 @@ import ], compiler/utils/[ idioms, + int128, nversion, ropes ], compiler/backend/[ ccgutils, + cgir, + compat ] # xxx: reports are a code smell meaning data types are misplaced @@ -69,7 +72,7 @@ import std/strutils except addf # clashes with ropes.addf type TJSGen = object idgen*: IdGenerator - module: PSym + module*: PSym graph: ModuleGraph config: ConfigRef @@ -335,8 +338,8 @@ proc makeJSString(s: string, escapeNonAscii = true): Rope = include jstypes -proc gen(p: PProc, n: PNode, r: var TCompRes) -proc genStmt*(p: PProc, n: PNode) +proc gen(p: PProc, n: CgNode, r: var TCompRes) +proc genStmt*(p: PProc, n: CgNode) proc useMagic(p: PProc, name: string) = if name.len == 0: return @@ -425,22 +428,26 @@ const # magic checked op; magic unchecked op; mCStrToStr: ["cstrToNimstr", "cstrToNimstr"], mStrToStr: ["", ""]] -proc needsTemp(n: PNode): bool = +proc needsTemp(n: CgNode): bool = # check if n contains a call to determine # if a temp should be made to prevent multiple evals - if n.kind in nkCallKinds + {nkTupleConstr, nkObjConstr, nkBracket, nkCurly}: - return true - for c in n: - if needsTemp(c): - return true + case n.kind + of cnkCall, cnkTupleConstr, cnkObjConstr, cnkArrayConstr, cnkSetConstr: + result = true + of cnkWithoutItems: + result = false + else: + for c in n.items: + if needsTemp(c): + return true -proc maybeMakeTemp(p: PProc, n: PNode; x: TCompRes): tuple[a, tmp: Rope] = +proc maybeMakeTemp(p: PProc, n: CgNode; x: TCompRes): tuple[a, tmp: Rope] = var a = x.rdLoc b = a if needsTemp(n): # if we have tmp just use it - if x.tmpLoc != "" and (mapType(n.typ) == etyBaseIndex or n.kind in {nkHiddenDeref, nkDerefExpr}): + if x.tmpLoc != "" and (mapType(n.typ) == etyBaseIndex or n.kind in {cnkDerefView, cnkDeref}): b = "$1[0][$1[1]]" % [x.tmpLoc] (a: a, tmp: b) else: @@ -451,16 +458,16 @@ proc maybeMakeTemp(p: PProc, n: PNode; x: TCompRes): tuple[a, tmp: Rope] = else: (a: a, tmp: b) -proc maybeMakeTempAssignable(p: PProc, n: PNode; x: TCompRes): tuple[a, tmp: Rope] = +proc maybeMakeTempAssignable(p: PProc, n: CgNode; x: TCompRes): tuple[a, tmp: Rope] = var a = x.rdLoc b = a if needsTemp(n): # if we have tmp just use it - if x.tmpLoc != "" and (mapType(n.typ) == etyBaseIndex or n.kind in {nkHiddenDeref, nkDerefExpr}): + if x.tmpLoc != "" and (mapType(n.typ) == etyBaseIndex or n.kind in {cnkDerefView, cnkDeref}): b = "$1[0][$1[1]]" % [x.tmpLoc] result = (a: a, tmp: b) - elif x.tmpLoc != "" and n.kind == nkBracketExpr: + elif x.tmpLoc != "" and n.kind == cnkBracketAccess: # genArrayAddr var address, index: TCompRes @@ -491,7 +498,7 @@ proc maybeMakeTempAssignable(p: PProc, n: PNode; x: TCompRes): tuple[a, tmp: Rop else: result = (a: a, tmp: b) -template binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string, +template binaryExpr(p: PProc, n: CgNode, r: var TCompRes, magic, frmt: string, reassign = false) = # $1 and $2 in the `frmt` string bind to lhs and rhs of the expr, # if $3 or $4 are present they will be substituted with temps for @@ -524,7 +531,7 @@ proc unsignedTrimmerJS(size: BiggestInt): Rope = template unsignedTrimmer(size: BiggestInt): Rope = size.unsignedTrimmerJS -proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string, +proc binaryUintExpr(p: PProc, n: CgNode, r: var TCompRes, op: string, reassign: static[bool] = false) = var x, y: TCompRes gen(p, n[1], x) @@ -537,7 +544,7 @@ proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string, r.res = "(($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer] r.kind = resExpr -template unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = +template unaryExpr(p: PProc, n: CgNode, r: var TCompRes, magic, frmt: string) = # $1 binds to n[1], if $2 is present it will be substituted to a tmp of $1 useMagic(p, magic) gen(p, n[1], r) @@ -546,7 +553,7 @@ template unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = r.res = frmt % [a, tmp] r.kind = resExpr -proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = +proc arithAux(p: PProc, n: CgNode, r: var TCompRes, op: TMagic) = var x, y: TCompRes xLoc,yLoc: Rope @@ -639,7 +646,7 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = else: assert false, $op -proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = +proc arith(p: PProc, n: CgNode, r: var TCompRes, op: TMagic) = case op of mAddU: binaryUintExpr(p, n, r, "+") of mSubU: binaryUintExpr(p, n, r, "-") @@ -686,7 +693,7 @@ proc lineDir(config: ConfigRef, info: TLineInfo, line: int): Rope = ropes.`%`("/* line $2 \"$1\" */$n", [rope(toFullPath(config, info)), rope(line)]) -proc genLineDir(p: PProc, n: PNode) = +proc genLineDir(p: PProc, n: CgNode) = let line = toLinenumber(n.info) if line < 0: return @@ -695,7 +702,7 @@ proc genLineDir(p: PProc, n: PNode) = if hasFrameInfo(p): lineF(p, "F.line = $1;$n", [rope(line)]) -proc genWhileStmt(p: PProc, n: PNode) = +proc genRepeatStmt(p: PProc, n: CgNode) = internalAssert p.config, isEmptyType(n.typ) genLineDir(p, n) inc(p.unique) @@ -704,12 +711,12 @@ proc genWhileStmt(p: PProc, n: PNode) = p.blocks[^1].isLoop = true let labl = p.unique.rope lineF(p, "Label$1: while (true) {$n", [labl]) - p.nested: genStmt(p, n[1]) + p.nested: genStmt(p, n[0]) lineF(p, "}$n", [labl]) setLen(p.blocks, p.blocks.len - 1) -proc genTry(p: PProc, n: PNode) = +proc genTry(p: PProc, n: CgNode) = # code to generate: # # ++excHandler; @@ -736,7 +743,7 @@ proc genTry(p: PProc, n: PNode) = genLineDir(p, n) inc(p.unique) var i = 1 - var catchBranchesExist = n.len > 1 and n[i].kind == nkExceptBranch + var catchBranchesExist = n.len > 1 and n[i].kind == cnkExcept if catchBranchesExist: p.body.add("++excHandler;\L") var tmpFramePtr = rope"F" @@ -750,7 +757,7 @@ proc genTry(p: PProc, n: PNode) = p.body.addf("--excHandler;$n} catch (EXCEPTION) {$n var prevJSError = lastJSError;$n" & " lastJSError = EXCEPTION;$n --excHandler;$n", []) line(p, "framePtr = $1;$n" % [tmpFramePtr]) - while i < n.len and n[i].kind == nkExceptBranch: + while i < n.len and n[i].kind == cnkExcept: if n[i].len == 1: # general except section: generalCatchBranchExists = true @@ -759,21 +766,21 @@ proc genTry(p: PProc, n: PNode) = if i > 1: lineF(p, "}$n", []) else: var orExpr = "" - var excAlias: PNode = nil + var excAlias: CgNode = nil useMagic(p, "isObj") for j in 0..= 2 and x[0].typ.skipTypes(abstractInst).kind == tyCstring: - localReport(p.config, x, reportSem rsemUnexpectedArrayAssignForCstring) + if x.kind == cnkBracketAccess and x[0].typ.skipTypes(abstractInst).kind == tyCstring: + localReport(p.config, x.info, reportSem rsemUnexpectedArrayAssignForCstring) gen(p, x, a) genLineDir(p, y) @@ -1018,7 +1021,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = else: useMagic(p, "nimCopy") # supports proc getF(): var T - if x.kind in {nkHiddenDeref, nkDerefExpr} and x[0].kind in nkCallKinds: + if x.kind in {cnkDerefView, cnkDeref} and x[0].kind == cnkCall: lineF(p, "nimCopy($1, $2, $3);$n", [a.res, b.res, genTypeInfo(p, x.typ)]) else: @@ -1026,7 +1029,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = [a.res, b.res, genTypeInfo(p, x.typ)]) of etyBaseIndex: if a.typ != etyBaseIndex or b.typ != etyBaseIndex: - if y.kind == nkCall: + if y.kind == cnkCall: let tmp = p.getTemp(false) lineF(p, "var $1 = $4; $2 = $1[0]; $3 = $1[1];$n", [tmp, a.address, a.res, b.rdLoc]) elif b.typ == etyBaseIndex: @@ -1050,10 +1053,10 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = else: lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) -proc genAsgn(p: PProc, n: PNode) = +proc genAsgn(p: PProc, n: CgNode) = genAsgnAux(p, n[0], n[1], noCopyNeeded=false) -proc genFastAsgn(p: PProc, n: PNode) = +proc genFastAsgn(p: PProc, n: CgNode) = # 'shallowCopy' always produced 'noCopyNeeded = true' here but this is wrong # for code like # while j >= pos: @@ -1064,7 +1067,7 @@ proc genFastAsgn(p: PProc, n: PNode) = let noCopy = n[0].typ.skipTypes(abstractInst).kind in {tySequence, tyString} genAsgnAux(p, n[0], n[1], noCopyNeeded=noCopy) -proc genSwap(p: PProc, n: PNode) = +proc genSwap(p: PProc, n: CgNode) = var a, b: TCompRes gen(p, n[1], a) gen(p, n[2], b) @@ -1078,27 +1081,27 @@ proc genSwap(p: PProc, n: PNode) = lineF(p, "var $1 = $2; $2 = $3; $3 = $1;", [tmp, a.res, b.res]) -proc getFieldPosition(p: PProc; f: PNode): int = +proc getFieldPosition(p: PProc; f: CgNode): int = case f.kind - of nkIntLit..nkUInt64Lit: result = int(f.intVal) - of nkSym: result = f.sym.position + of cnkIntLit: result = int(f.intVal) + of cnkSym: result = f.sym.position else: internalError(p.config, f.info, "genFieldPosition") -proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) = +proc genFieldAddr(p: PProc, n: CgNode, r: var TCompRes) = var a: TCompRes r.typ = etyBaseIndex - let b = if n.kind == nkHiddenAddr: n[0] else: n + let b = if n.kind == cnkHiddenAddr: n[0] else: n gen(p, b[0], a) if skipTypes(b[0].typ, abstractVarRange).kind == tyTuple: r.res = makeJSString("Field" & $getFieldPosition(p, b[1])) else: - p.config.internalAssert(b[1].kind == nkSym, b[1].info, "genFieldAddr") + p.config.internalAssert(b[1].kind == cnkSym, b[1].info, "genFieldAddr") r.res = makeJSString(ensureMangledName(p, b[1].sym)) internalAssert p.config, a.typ != etyBaseIndex r.address = a.res r.kind = resExpr -proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) = +proc genFieldAccess(p: PProc, n: CgNode, r: var TCompRes) = gen(p, n[0], r) r.typ = mapType(n.typ) let otyp = skipTypes(n[0].typ, abstractVarRange) @@ -1118,15 +1121,15 @@ proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) = [r.res, getFieldPosition(p, n[1]).rope] mkTemp(0) else: - p.config.internalAssert(n[1].kind == nkSym, n[1].info, "genFieldAccess") + p.config.internalAssert(n[1].kind == cnkSym, n[1].info, "genFieldAccess") r.res = "$1.$2" % [r.res, ensureMangledName(p, n[1].sym)] mkTemp(1) r.kind = resExpr -proc genAddr(p: PProc, n: PNode, r: var TCompRes) +proc genAddr(p: PProc, n: CgNode, r: var TCompRes) -proc genCheckedFieldOp(p: PProc, n: PNode, takeAddr: bool, r: var TCompRes) = - internalAssert p.config, n.kind == nkCheckedFieldExpr +proc genCheckedFieldOp(p: PProc, n: CgNode, takeAddr: bool, r: var TCompRes) = + internalAssert p.config, n.kind == cnkCheckedFieldAccess # nkDotExpr to access the requested field let accessExpr = n[0] # nkCall to check if the discriminant is valid @@ -1171,12 +1174,12 @@ proc genCheckedFieldOp(p: PProc, n: PNode, takeAddr: bool, r: var TCompRes) = r.res = "$1.$2" % [tmp, name] r.kind = resExpr -proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = +proc genArrayAddr(p: PProc, n: CgNode, r: var TCompRes) = var a, b: TCompRes first: Int128 r.typ = etyBaseIndex - let m = if n.kind == nkHiddenAddr: n[0] else: n + let m = if n.kind == cnkHiddenAddr: n[0] else: n gen(p, m[0], a) gen(p, m[1], b) #internalAssert p.config, a.typ != etyBaseIndex and b.typ != etyBaseIndex @@ -1198,7 +1201,7 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = r.res = b.res r.kind = resExpr -proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) = +proc genArrayAccess(p: PProc, n: CgNode, r: var TCompRes) = var ty = skipTypes(n[0].typ, abstractVarRange) if ty.kind in {tyRef, tyPtr, tyLent}: ty = skipTypes(ty.lastSon, abstractVarRange) case ty.kind @@ -1238,7 +1241,7 @@ template isIndirect(x: PSym): bool = v.kind notin {skProc, skFunc, skConverter, skMethod, skIterator, skConst, skTemp, skLet, skParam}) -proc genSymAddr(p: PProc, n: PNode, r: var TCompRes) = +proc genSymAddr(p: PProc, n: CgNode, r: var TCompRes) = ## Generates the code for taking the address of the location identified by ## symbol node `n` let s = n.sym @@ -1259,17 +1262,17 @@ proc genSymAddr(p: PProc, n: PNode, r: var TCompRes) = r.address = "[$1]" % name else: internalError(p.config, n.info, $("genAddr: 2", s.kind)) -proc genAddr(p: PProc, n: PNode, r: var TCompRes) = +proc genAddr(p: PProc, n: CgNode, r: var TCompRes) = ## Dispatches to the appropriate procedure for generating the address-of ## operation based on the kind of node `n` case n.kind - of nkSym: + of cnkSym: genSymAddr(p, n, r) - of nkCheckedFieldExpr: + of cnkCheckedFieldAccess: genCheckedFieldOp(p, n, takeAddr=true, r) - of nkDotExpr: + of cnkFieldAccess: genFieldAddr(p, n, r) - of nkBracketExpr: + of cnkBracketAccess: let kind = skipTypes(n[0].typ, abstractVarRange).kind case kind of tyArray, tyOpenArray, tySequence, tyString, tyCstring, tyVarargs: @@ -1278,21 +1281,21 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) = genFieldAddr(p, n, r) else: internalError(p.config, n[0].info, "expr(nkBracketExpr, " & $kind & ')') - of nkHiddenDeref, nkDerefExpr: + of cnkDerefView, cnkDeref: # attemping to take the address of a deref expression -> skip both the # addr and deref gen(p, n[0], r) - of nkConv: + of cnkConv: # an explicit lvalue conversion. Conversion between lvalues of different # underlying type is not possible, so we simply skip the conversion and # apply the operation to the source expression genAddr(p, n[1], r) - of nkStmtListExpr: + of cnkStmtListExpr: for i in 0..= n.len: globalReport(p.config, n.info, semReportCountMismatch( rsemExpectedParameterForJsPattern, expected = i, - got = n.len - 1, - node = n)) + got = n.len - 1)) let it = n[i] var paramType: PNode = nil @@ -1436,7 +1438,7 @@ proc genOtherArg(p: PProc; n: PNode; i: int; typ: PType; genArg(p, it, paramType.sym, r) inc generated -proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType; +proc genPatternCall(p: PProc; n: CgNode; pat: string; typ: PType; r: var TCompRes) = var i = 0 var j = 1 @@ -1470,7 +1472,7 @@ proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType; if i - 1 >= start: r.res.add(substr(pat, start, i - 1)) -proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) = +proc genInfixCall(p: PProc, n: CgNode, r: var TCompRes) = # don't call '$' here for efficiency: let f = n[0].sym assert sfInfixCall in f.flags @@ -1495,7 +1497,7 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) = r.res.add(op.res) genArgs(p, n, r, 2) -proc genCall(p: PProc, n: PNode, r: var TCompRes) = +proc genCall(p: PProc, n: CgNode, r: var TCompRes) = gen(p, n[0], r) genArgs(p, n, r) if n.typ != nil: @@ -1507,9 +1509,9 @@ proc genCall(p: PProc, n: PNode, r: var TCompRes) = r.tmpLoc = tmp r.typ = t -proc genEcho(p: PProc, n: PNode, r: var TCompRes) = +proc genEcho(p: PProc, n: CgNode, r: var TCompRes) = let n = n[1].skipConv - internalAssert p.config, n.kind == nkBracket + internalAssert p.config, n.kind == cnkArrayConstr useMagic(p, "toJSStr") # Used in rawEcho useMagic(p, "rawEcho") r.res.add("rawEcho(") @@ -1669,12 +1671,12 @@ proc defineGlobals*(globals: PGlobals, m: BModule, vars: openArray[PSym]) = # add to the top-level section: globals.code.add(p.body) -proc genVarInit(p: PProc, v: PSym, varName: string, n: PNode) = +proc genVarInit(p: PProc, v: PSym, varName: string, n: CgNode) = var a: TCompRes s: Rope - if n.kind == nkEmpty: + if n.kind == cnkEmpty: if not isIndirect(v) and v.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and mapType(v.typ) == etyBaseIndex: lineF(p, "var $1 = null;$n", [varName]) @@ -1706,15 +1708,14 @@ proc genVarInit(p: PProc, v: PSym, varName: string, n: PNode) = else: lineF(p, "var $1 = $2;$n", [varName, s]) -proc genVarStmt(p: PProc, n: PNode) = - for it in n.items: - assert it.kind == nkIdentDefs - assert it[0].kind == nkSym +proc genDef(p: PProc, it: CgNode) = + if true: + assert it[0].kind == cnkSym let v = it[0].sym let name = mangleName(p.module, v) if exfNoDecl notin v.extFlags and sfImportc notin v.flags: genLineDir(p, it) - genVarInit(p, v, name, it[2]) + genVarInit(p, v, name, it[1]) # all locals need a name: p.localNames[v.id] = name @@ -1724,13 +1725,13 @@ proc genConstant*(g: PGlobals, m: BModule, c: PSym) = if exfNoDecl notin c.extFlags: var p = newInitProc(g, m) #genLineDir(p, c.ast) - genVarInit(p, c, name, c.ast) + genVarInit(p, c, name, translate(c.ast)) g.constants.add(p.body) # all constants need a name: g.names[c.id] = name -proc genNew(p: PProc, n: PNode) = +proc genNew(p: PProc, n: CgNode) = var a: TCompRes gen(p, n[1], a) var t = skipTypes(n[1].typ, abstractVar)[0] @@ -1741,7 +1742,7 @@ proc genNew(p: PProc, n: PNode) = else: lineF(p, "$1 = [[$2], 0];$n", [a.rdLoc, createVar(p, t, false)]) -proc genNewSeq(p: PProc, n: PNode) = +proc genNewSeq(p: PProc, n: CgNode) = var x, y: TCompRes gen(p, n[1], x) gen(p, n[2], y) @@ -1749,13 +1750,13 @@ proc genNewSeq(p: PProc, n: PNode) = lineF(p, "$1 = new Array($2); for (var i = 0 ; i < $2 ; ++i) { $1[i] = $3; }", [ x.rdLoc, y.rdLoc, createVar(p, t, false)]) -proc genOrd(p: PProc, n: PNode, r: var TCompRes) = +proc genOrd(p: PProc, n: CgNode, r: var TCompRes) = case skipTypes(n[1].typ, abstractVar + abstractRange).kind of tyEnum, tyInt..tyUInt64, tyChar: gen(p, n[1], r) of tyBool: unaryExpr(p, n, r, "", "($1 ? 1 : 0)") else: internalError(p.config, n.info, "genOrd") -proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) = +proc genConStrStr(p: PProc, n: CgNode, r: var TCompRes) = var a: TCompRes gen(p, n[1], a) @@ -1778,7 +1779,7 @@ proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) = else: r.res.add("$1 || [])" % [a.res]) -proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string; typ = "") = +proc genReprAux(p: PProc, n: CgNode, r: var TCompRes, magic: string; typ = "") = useMagic(p, magic) r.res.add(magic & "(") var a: TCompRes @@ -1800,7 +1801,7 @@ proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string; typ = "") = r.res.add(typ) r.res.add(")") -proc genRepr(p: PProc, n: PNode, r: var TCompRes) = +proc genRepr(p: PProc, n: CgNode, r: var TCompRes) = let t = skipTypes(n[1].typ, abstractVarRange) case t.kind: of tyInt..tyInt64, tyUInt..tyUInt64: @@ -1818,7 +1819,7 @@ proc genRepr(p: PProc, n: PNode, r: var TCompRes) = of tySet: genReprAux(p, n, r, "reprSet", genTypeInfo(p, t)) of tyEmpty, tyVoid: - localReport(p.config, n, reportSem rsemUnexpectedVoidType) + localReport(p.config, n.info, reportSem rsemUnexpectedVoidType) of tyPointer: genReprAux(p, n, r, "reprPointer") of tyOpenArray, tyVarargs: @@ -1827,7 +1828,7 @@ proc genRepr(p: PProc, n: PNode, r: var TCompRes) = genReprAux(p, n, r, "reprAny", genTypeInfo(p, t)) r.kind = resExpr -proc genOf(p: PProc, n: PNode, r: var TCompRes) = +proc genOf(p: PProc, n: CgNode, r: var TCompRes) = var x: TCompRes let t = skipTypes(n[2].typ, abstractVarRange+{tyRef, tyPtr, tyLent, tyTypeDesc}) @@ -1839,11 +1840,11 @@ proc genOf(p: PProc, n: PNode, r: var TCompRes) = r.res = "isObj($1.m_type, $2)" % [x.res, genTypeInfo(p, t)] r.kind = resExpr -proc genDefault(p: PProc, n: PNode; r: var TCompRes) = +proc genDefault(p: PProc, n: CgNode; r: var TCompRes) = r.res = createVar(p, n.typ, indirect = false) r.kind = resExpr -proc genReset(p: PProc, n: PNode) = +proc genReset(p: PProc, n: CgNode) = var x: TCompRes useMagic(p, "genericReset") gen(p, n[1], x) @@ -1854,7 +1855,7 @@ proc genReset(p: PProc, n: PNode) = lineF(p, "$1 = genericReset($3, $2);$n", [a, genTypeInfo(p, n[1].typ), tmp]) -proc genMove(p: PProc; n: PNode; r: var TCompRes) = +proc genMove(p: PProc; n: CgNode; r: var TCompRes) = var a: TCompRes r.kind = resVal r.res = p.getTemp() @@ -1863,7 +1864,7 @@ proc genMove(p: PProc; n: PNode; r: var TCompRes) = genReset(p, n) #lineF(p, "$1 = $2;$n", [dest.rdLoc, src.rdLoc]) -proc genJSArrayConstr(p: PProc, n: PNode, r: var TCompRes) = +proc genJSArrayConstr(p: PProc, n: CgNode, r: var TCompRes) = var a: TCompRes r.res = rope("[") r.kind = resExpr @@ -1880,7 +1881,7 @@ proc genJSArrayConstr(p: PProc, n: PNode, r: var TCompRes) = r.res.add(a.res) r.res.add("]") -proc genMagic(p: PProc, n: PNode, r: var TCompRes) = +proc genMagic(p: PProc, n: CgNode, r: var TCompRes) = let op = n[0].sym.magic case op of mAddI..mStrToStr: arith(p, n, r, op) @@ -1939,7 +1940,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = of mChr: gen(p, n[1], r) of mArrToSeq: # only array literals doesn't need copy - if n[1].kind == nkBracket: + if n[1].kind == cnkArrayConstr: genJSArrayConstr(p, n[1], r) else: var x: TCompRes @@ -1972,7 +1973,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = else: if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 += $2") else: binaryExpr(p, n, r, "addInt", "$1 = addInt($3, $2)", true) - of ast.mDec: + of mDec: if n[1].typ.skipTypes(abstractRange).kind in {tyUInt..tyUInt64}: binaryUintExpr(p, n, r, "-", true) else: @@ -2046,7 +2047,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = genCall(p, n, r) #else internalError(p.config, e.info, 'genMagic: ' + magicToStr[op]); -proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) = +proc genSetConstr(p: PProc, n: CgNode, r: var TCompRes) = var a, b: TCompRes useMagic(p, "setConstr") @@ -2055,7 +2056,7 @@ proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) = for i in 0.. 0: r.res.add(", ") var it = n[i] - if it.kind == nkRange: + if it.kind == cnkRange: gen(p, it[0], a) gen(p, it[1], b) @@ -2074,7 +2075,7 @@ proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) = p.g.constants.addf("var $1 = $2;$n", [tmp, r.res]) r.res = tmp -proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) = +proc genArrayConstr(p: PProc, n: CgNode, r: var TCompRes) = ## Constructs array or sequence. ## Nim array of uint8..uint32, int8..int32 maps to JS typed arrays. ## Nim sequence maps to JS array. @@ -2096,14 +2097,13 @@ proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) = else: genJSArrayConstr(p, n, r) -proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) = +proc genTupleConstr(p: PProc, n: CgNode, r: var TCompRes) = var a: TCompRes r.res = rope("{") r.kind = resExpr for i in 0.. 0: r.res.add(", ") var it = n[i] - if it.kind == nkExprColonExpr: it = it[1] gen(p, it, a) let typ = it.typ.skipTypes(abstractInst) if a.typ == etyBaseIndex: @@ -2115,15 +2115,15 @@ proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) = r.res.addf("Field$#: $#", [i.rope, a.res]) r.res.add("}") -proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = +proc genObjConstr(p: PProc, n: CgNode, r: var TCompRes) = var a: TCompRes r.kind = resExpr var initList : Rope var fieldIDs = initIntSet() - for i in 1.. 1: initList.add(", ") + for i in 0.. 0: initList.add(", ") var it = n[i] - internalAssert p.config, it.kind == nkExprColonExpr + internalAssert p.config, it.kind == cnkBinding let val = it[1] gen(p, val, a) var f = it[0].sym @@ -2142,7 +2142,7 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = createObjInitList(p, t, fieldIDs, initList) r.res = ("{$1}") % [initList] -proc genConv(p: PProc, n: PNode, r: var TCompRes) = +proc genConv(p: PProc, n: CgNode, r: var TCompRes) = var dest = skipTypes(n.typ, abstractVarRange) var src = skipTypes(n[1].typ, abstractVarRange) gen(p, n[1], r) @@ -2165,10 +2165,10 @@ proc genConv(p: PProc, n: PNode, r: var TCompRes) = # TODO: What types must we handle here? discard -proc upConv(p: PProc, n: PNode, r: var TCompRes) = +proc upConv(p: PProc, n: CgNode, r: var TCompRes) = gen(p, n[0], r) # XXX -proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) = +proc genRangeChck(p: PProc, n: CgNode, r: var TCompRes, magic: string) = var a, b: TCompRes gen(p, n[0], r) if optRangeCheck notin p.options: @@ -2183,10 +2183,10 @@ proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) = r.res = "chckRange($1, $2, $3)" % [r.res, a.res, b.res] r.kind = resExpr -proc convStrToCStr(p: PProc, n: PNode, r: var TCompRes) = +proc convStrToCStr(p: PProc, n: CgNode, r: var TCompRes) = # we do an optimization here as this is likely to slow down # much of the code otherwise: - if n[0].kind == nkCStringToString: + if n[0].kind == cnkCStringToString: gen(p, n[0][0], r) else: gen(p, n[0], r) @@ -2195,10 +2195,10 @@ proc convStrToCStr(p: PProc, n: PNode, r: var TCompRes) = r.res = "toJSStr($1)" % [r.res] r.kind = resExpr -proc convCStrToStr(p: PProc, n: PNode, r: var TCompRes) = +proc convCStrToStr(p: PProc, n: CgNode, r: var TCompRes) = # we do an optimization here as this is likely to slow down # much of the code otherwise: - if n[0].kind == nkStringToCString: + if n[0].kind == cnkStringToCString: gen(p, n[0][0], r) else: gen(p, n[0], r) @@ -2207,7 +2207,7 @@ proc convCStrToStr(p: PProc, n: PNode, r: var TCompRes) = r.res = "cstrToNimstr($1)" % [r.res] r.kind = resExpr -proc genReturnStmt(p: PProc, n: PNode) = +proc genReturnStmt(p: PProc, n: CgNode) = p.config.internalAssert(p.prc != nil, n.info, "genReturnStmt") p.beforeRetNeeded = true genLineDir(p, n) @@ -2305,7 +2305,7 @@ proc finishProc*(p: PProc): string = let resVar = createVar(p, resultSym.typ, isIndirect(resultSym)) resultAsgn = p.indentLine(("var $# = $#;$n") % [mname, resVar]) var a: TCompRes - gen(p, prc.ast[resultPos], a) + gen(p, newSymNode(prc.ast[resultPos].sym), a) if returnAddress: returnStmt = "return [$#, $#];$n" % [a.address, a.res] else: @@ -2348,23 +2348,17 @@ proc finishProc*(p: PProc): string = # echo "END generated code for: " & prc.name.s proc genProc*(g: PGlobals, module: BModule, prc: PSym, - transformedBody: PNode): Rope = + transformedBody: CgNode): Rope = var p = startProc(g, module, prc) p.nested: genStmt(p, transformedBody) result = finishProc(p) -proc genStmt(p: PProc, n: PNode) = +proc genStmt(p: PProc, n: CgNode) = var r: TCompRes gen(p, n, r) if r.res != "": lineF(p, "$#;$n", [r.res]) -proc genPragma(p: PProc, n: PNode) = - for it in n.sons: - case whichPragma(it) - of wEmit: genAsmOrEmitStmt(p, it[1]) - else: discard - -proc genCast(p: PProc, n: PNode, r: var TCompRes) = +proc genCast(p: PProc, n: CgNode, r: var TCompRes) = var dest = skipTypes(n.typ, abstractVarRange) var src = skipTypes(n[1].typ, abstractVarRange) gen(p, n[1], r) @@ -2402,19 +2396,19 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) = r.res = r.address r.typ = etyObject -proc gen(p: PProc, n: PNode, r: var TCompRes) = +proc gen(p: PProc, n: CgNode, r: var TCompRes) = r.typ = etyNone if r.kind != resCallee: r.kind = resNone #r.address = nil r.res = "" case n.kind - of nkSym: + of cnkSym: genSym(p, n, r) - of nkCharLit..nkUInt64Lit: + of cnkIntLit, cnkUIntLit: r.res = intLiteral(getInt(n), n.typ) r.kind = resExpr - of nkNilLit: + of cnkNilLit: if mapType(n.typ) == etyBaseIndex: r.typ = etyBaseIndex r.address = rope"null" @@ -2423,7 +2417,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = else: r.res = rope"null" r.kind = resExpr - of nkStrLit..nkTripleStrLit: + of cnkStrLit: if skipTypes(n.typ, abstractVarRange).kind == tyString: if n.strVal.len != 0: useMagic(p, "makeNimstrLit") @@ -2433,7 +2427,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = else: r.res = makeJSString(n.strVal, false) r.kind = resExpr - of nkFloatLit..nkFloat64Lit: + of cnkFloatLit: let f = n.floatVal case classify(f) of fcNan: @@ -2455,29 +2449,29 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = else: r.res.addFloatRoundtrip(f) r.kind = resExpr - of nkCall: + of cnkCall: if isEmptyType(n.typ): genLineDir(p, n) - if (n[0].kind == nkSym) and (n[0].sym.magic != mNone): + if (n[0].kind == cnkSym) and (n[0].sym.magic != mNone): genMagic(p, n, r) - elif n[0].kind == nkSym and sfInfixCall in n[0].sym.flags and + elif n[0].kind == cnkSym and sfInfixCall in n[0].sym.flags and n.len >= 1: genInfixCall(p, n, r) else: genCall(p, n, r) - of nkClosure: + of cnkClosureConstr: useMagic(p, "makeClosure") var tmp1, tmp2: TCompRes gen(p, n[0], tmp1) gen(p, n[1], tmp2) r.res = "makeClosure($1, $2)" % [tmp1.rdLoc, tmp2.rdLoc] r.kind = resExpr - of nkCurly: genSetConstr(p, n, r) - of nkBracket: genArrayConstr(p, n, r) - of nkTupleConstr: genTupleConstr(p, n, r) - of nkObjConstr: genObjConstr(p, n, r) - of nkHiddenStdConv, nkConv: genConv(p, n, r) - of nkAddr, nkHiddenAddr: + of cnkSetConstr: genSetConstr(p, n, r) + of cnkArrayConstr: genArrayConstr(p, n, r) + of cnkTupleConstr: genTupleConstr(p, n, r) + of cnkObjConstr: genObjConstr(p, n, r) + of cnkHiddenConv, cnkConv: genConv(p, n, r) + of cnkAddr, cnkHiddenAddr: if n.typ.kind == tyLent or mapType(n.typ) != etyBaseIndex: # the operation doesn't produce an address-like value (e.g. because the # operand is a JS object and those already have reference semantics). @@ -2486,25 +2480,25 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = gen(p, n[0], r) else: genAddr(p, n[0], r) - of nkDerefExpr, nkHiddenDeref: + of cnkDeref, cnkDerefView: if n.typ.kind in {tyLent}: gen(p, n[0], r) else: genDeref(p, n, r) - of nkBracketExpr: genArrayAccess(p, n, r) - of nkDotExpr: genFieldAccess(p, n, r) - of nkCheckedFieldExpr: genCheckedFieldOp(p, n, takeAddr=false, r) - of nkObjDownConv: gen(p, n[0], r) - of nkObjUpConv: upConv(p, n, r) - of nkCast: genCast(p, n, r) - of nkChckRangeF: genRangeChck(p, n, r, "chckRangeF") - of nkChckRange64: genRangeChck(p, n, r, "chckRange64") - of nkChckRange: genRangeChck(p, n, r, "chckRange") - of nkStringToCString: convStrToCStr(p, n, r) - of nkCStringToString: convCStrToStr(p, n, r) - of nkEmpty: discard - of nkType: r.res = genTypeInfo(p, n.typ) - of nkStmtList, nkStmtListExpr: + of cnkBracketAccess: genArrayAccess(p, n, r) + of cnkFieldAccess: genFieldAccess(p, n, r) + of cnkCheckedFieldAccess: genCheckedFieldOp(p, n, takeAddr=false, r) + of cnkObjDownConv: gen(p, n[0], r) + of cnkObjUpConv: upConv(p, n, r) + of cnkCast: genCast(p, n, r) + of cnkChckRangeF: genRangeChck(p, n, r, "chckRangeF") + of cnkChckRange64: genRangeChck(p, n, r, "chckRange64") + of cnkChckRange: genRangeChck(p, n, r, "chckRange") + of cnkStringToCString: convStrToCStr(p, n, r) + of cnkCStringToString: convCStrToStr(p, n, r) + of cnkEmpty: discard + of cnkType: r.res = genTypeInfo(p, n.typ) + of cnkStmtList, cnkStmtListExpr: # this shows the distinction is nice for backends and should be kept # in the frontend let isExpr = not isEmptyType(n.typ) @@ -2512,26 +2506,25 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = genStmt(p, n[i]) if isExpr: gen(p, lastSon(n), r) - of nkBlockStmt: genBlock(p, n) - of nkIfStmt: genIf(p, n) - of nkWhileStmt: genWhileStmt(p, n) - of nkVarSection, nkLetSection: genVarStmt(p, n) - of nkCaseStmt: genCaseJS(p, n) - of nkReturnStmt: genReturnStmt(p, n) - of nkBreakStmt: genBreakStmt(p, n) - of nkAsgn: genAsgn(p, n) - of nkFastAsgn: genFastAsgn(p, n) - of nkDiscardStmt: + of cnkBlockStmt: genBlock(p, n) + of cnkIfStmt: genIf(p, n) + of cnkRepeatStmt: genRepeatStmt(p, n) + of cnkDef: genDef(p, n) + of cnkCaseStmt: genCaseJS(p, n) + of cnkReturnStmt: genReturnStmt(p, n) + of cnkBreakStmt: genBreakStmt(p, n) + of cnkAsgn: genAsgn(p, n) + of cnkFastAsgn: genFastAsgn(p, n) + of cnkVoidStmt: genLineDir(p, n) gen(p, n[0], r) r.res = "var _ = " & r.res - of nkAsmStmt: genAsmOrEmitStmt(p, n) - of nkTryStmt: genTry(p, n) - of nkRaiseStmt: genRaiseStmt(p, n) - of nkPragma: genPragma(p, n) - of nkFloat128Lit, nkNimNodeLit: - unreachable() - of nkWithSons + nkWithoutSons - codegenExprNodeKinds: + of cnkAsmStmt, cnkEmitStmt: genAsmOrEmitStmt(p, n) + of cnkTryStmt: genTry(p, n) + of cnkRaiseStmt: genRaiseStmt(p, n) + of cnkPragmaStmt: discard + of cnkInvalid, cnkRange, cnkBinding, cnkExcept, cnkFinally, cnkBranch, + cnkAstLit: internalError(p.config, n.info, "gen: unknown node type: " & $n.kind) proc newModule*(g: ModuleGraph; module: PSym): BModule = @@ -2547,7 +2540,7 @@ proc genHeader*(): Rope = var lastJSError = null; """.unindent.format(VersionAsString)) -proc genTopLevelStmt*(globals: PGlobals, m: BModule, n: PNode) = +proc genTopLevelStmt*(globals: PGlobals, m: BModule, n: CgNode) = m.config.internalAssert(m.module != nil, n.info, "genTopLevelStmt") var p = newInitProc(globals, m) p.unique = globals.unique diff --git a/compiler/vm/vmbackend.nim b/compiler/vm/vmbackend.nim index 4d869669b3b..1773850cd13 100644 --- a/compiler/vm/vmbackend.nim +++ b/compiler/vm/vmbackend.nim @@ -16,7 +16,8 @@ import lineinfos ], compiler/backend/[ - backends + backends, + cgir ], compiler/front/[ msgs, @@ -113,7 +114,7 @@ proc generateCodeForProc(c: var TCtx, s: PSym, body: sink MirFragment): CodeInfo else: c.config.localReport(vmGenDiagToLegacyReport(r.takeErr)) -proc genStmt(c: var TCtx, f: var CodeFragment, stmt: PNode) = +proc genStmt(c: var TCtx, f: var CodeFragment, stmt: CgNode) = ## Generates and emits the code for a statement into the fragment `f`. template swapState() = swap(c.code, f.code) @@ -209,7 +210,7 @@ proc generateAliveProcs(c: var TCtx, config: BackendConfig, rc = frag.prc.regInfo.len c.appendCode(frag) - c.gABC(c.graph.emptyNode, opcRet) + c.gABC(unknownLineInfo, opcRet) let id = registerProc(c, frag.prc.sym) fillProcEntry(c.functions[id.int]): (start: start, regCount: rc) @@ -278,7 +279,7 @@ proc generateCode*(g: ModuleGraph, mlist: sink ModuleList) = let entryPoint = generateCodeForMain(c, bconf, mlist) - c.gABC(g.emptyNode, opcEof) + c.gABC(unknownLineInfo, opcEof) # ----- code generation is finished diff --git a/compiler/vm/vmgen.nim b/compiler/vm/vmgen.nim index 5bc647934a2..65effc558ff 100644 --- a/compiler/vm/vmgen.nim +++ b/compiler/vm/vmgen.nim @@ -43,6 +43,9 @@ import lineinfos, astmsgs, ], + compiler/backend/[ + cgir + ], compiler/modules/[ magicsys, modulegraphs @@ -67,6 +70,8 @@ import import std/options as std_options +from compiler/backend/compat import getInt, isOfBranch, skipConv, lastSon + from std/bitops import bitor when defined(nimCompilerStacktraceHints): @@ -176,12 +181,13 @@ template tryOrReturn(code): untyped = return VmGenResult.err(move e.diag) # forward declarations -proc genLit(c: var TCtx; n: PNode; lit: int; dest: var TDest) -proc genTypeLit(c: var TCtx; t: PType; dest: var TDest) +proc genLit(c: var TCtx; n: CgNode; lit: int; dest: var TDest) +proc genTypeLit(c: var TCtx; info: CgNode, t: PType; dest: var TDest) proc genType(c: var TCtx, typ: PType): int func fitsRegister(t: PType): bool func local(prc: PProc, sym: PSym): TDest {.inline.} -proc genRegLoad(c: var TCtx, n: PNode, dest, src: TRegister) +proc genRegLoad(c: var TCtx, n: CgNode, dest, src: TRegister) {.inline.} +proc genRegLoad(c: var TCtx, n: CgNode, typ: PType, dest, src: TRegister) template isUnset(x: TDest): bool = x < 0 @@ -191,28 +197,28 @@ proc routineSignature(s: PSym): PType {.inline.} = if s.kind == skMacro: s.internal else: s.typ -func underlyingLoc(n: PNode): PSym = +func underlyingLoc(n: CgNode): PSym = ## Computes and returns the symbol of the complete location (i.e., a location ## not part of a compound location) that l-value expression `n` names. If no ## complete location is named, ``nil`` is returned. var root {.cursor.} = n # skip nodes that don't change the location until we arrive at either one # that does, or a symbol - while root.kind in {nkConv, nkStmtListExpr}: + while root.kind in {cnkConv, cnkStmtListExpr}: root = root.lastSon result = - if root.kind == nkSym: root.sym + if root.kind == cnkSym: root.sym else: nil -func analyseIfAddressTaken(n: PNode, locs: var IntSet) = +func analyseIfAddressTaken(n: CgNode, locs: var IntSet) = ## Recursively traverses the tree `n` and collects the symbols IDs of all ## complete locations of which the address is taken. Note that this analysis ## doesn't rely on the ``sfAddrTaken`` flag (because it's not reliable). # TODO: turn this into a MIR analysis. Doing so will simplify the code, make # it less error-prone, and likely also faster case n.kind - of nkHiddenAddr, nkAddr: + of cnkHiddenAddr, cnkAddr: # the nodes we're interested let loc = underlyingLoc(n[0]) if loc != nil: @@ -222,26 +228,9 @@ func analyseIfAddressTaken(n: PNode, locs: var IntSet) = else: # the operand expression must still be anaylsed analyseIfAddressTaken(n[0], locs) - of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkCast: - analyseIfAddressTaken(n[1], locs) - of nkVarSection, nkLetSection: - for it in n.items: - case it.kind - of nkCommentStmt: discard - of nkIdentDefs, nkVarTuple: - # only analyse the initializer expression, the other parts are - # declarative - analyseIfAddressTaken(n.lastSon, locs) - else: - unreachable(it.kind) - - of nkEmpty, nkCommentStmt, nkTypeSection, nkConstSection, nkPragma, - nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt, nkMixinStmt, - nkBindStmt, nkLambdaKinds, routineDefs: - discard "ignore declarative nodes" - of nkLiterals, nkNimNodeLit, nkSym, nkNone, nkIdent, nkError: + of cnkWithoutItems: discard "ignore" - elif n.kind in nkWithSons: + of cnkWithItems - {cnkHiddenAddr, cnkAddr}: for it in n.items: analyseIfAddressTaken(it, locs) @@ -304,17 +293,16 @@ proc gABx*(c: var TCtx; i: TLineInfo; opc: TOpcode; a: TRegister = 0; bx: int) = c.debug.add(i) # convenience templates that take the line information from the node -template gABC*(ctx: var TCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) = +template gABC*(ctx: var TCtx; n: CgNode; opc: TOpcode; a, b, c: TRegister = 0) = gABC(ctx, n.info, opc, a, b, c) -template gABI(c: var TCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) = +template gABI(c: var TCtx; n: CgNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) = gABI(c, n.info, opc, a, b, imm) -template gABx(c: var TCtx, n: PNode, opc: TOpcode; a: TRegister, bx: int) = +template gABx(c: var TCtx, n: CgNode, opc: TOpcode; a: TRegister, bx: int) = gABx(c, n.info, opc, a, bx) -proc xjmp(c: var TCtx; n: PNode; opc: TOpcode; a: TRegister = 0): TPosition = - #assert opc in {opcJmp, opcFJmp, opcTJmp} +proc xjmp(c: var TCtx; n: CgNode; opc: TOpcode; a: TRegister = 0): TPosition = result = TPosition(c.code.len) gABx(c, n, opc, a, 0) @@ -322,7 +310,7 @@ func genLabel(c: TCtx): TPosition = result = TPosition(c.code.len) #c.jumpTargets.incl(c.code.len) -proc jmpBack(c: var TCtx, n: PNode, p = TPosition(0)) = +proc jmpBack(c: var TCtx, n: CgNode, p = TPosition(0)) = let dist = p.int - c.code.len internalAssert(c.config, regBxMin < dist and dist < regBxMax) gABx(c, n, opcJmpBack, 0, dist) @@ -487,7 +475,7 @@ proc freeTempRange(c: var TCtx; start: TRegister, n: int) = for i in start .. start + n - 1: c.freeTemp(TRegister(i)) -proc getFullTemp(c: var TCtx, n: PNode, t: PType): TRegister = +proc getFullTemp(c: var TCtx, n: CgNode, t: PType): TRegister = ## Allocates a register for a value of type `t`, and, if the value doesn't ## fit into a register, emits the bytecode for allocating and setting up a ## temporary location. @@ -518,7 +506,7 @@ func isDirectView(t: PType): bool {.inline.} = ## Returns whether `t` is a direct view-type. classifyBackendView(t) != bvcNone -proc prepare(c: var TCtx, dest: var TDest, n: PNode, typ: PType) = +proc prepare(c: var TCtx, dest: var TDest, n: CgNode, typ: PType) = ## If `dest` is not already set or refers to an argument slot, allocates a ## location of `typ` and assigns the register holding the resulting handle or ## value to it. `n` only provides the line information to use for emitted @@ -546,59 +534,54 @@ template withBlock(labl: PSym; body: untyped) {.dirty.} = body popBlock(c, oldLen) -proc gen(c: var TCtx; n: PNode; dest: var TDest) -proc gen(c: var TCtx; n: PNode; dest: TRegister) = +proc gen(c: var TCtx; n: CgNode; dest: var TDest) +proc gen(c: var TCtx; n: CgNode; dest: TRegister) = var d: TDest = dest gen(c, n, d) #internalAssert c.config, d == dest # issue #7407 -proc gen(c: var TCtx; n: PNode) = +proc gen(c: var TCtx; n: CgNode) = var tmp: TDest = -1 gen(c, n, tmp) if tmp >= 0: freeTemp(c, tmp) #if n.typ.isEmptyType: internalAssert tmp < 0 -proc genx(c: var TCtx; n: PNode): TRegister = +proc genx(c: var TCtx; n: CgNode): TRegister = var tmp: TDest = -1 gen(c, n, tmp) #internalAssert c.config, tmp >= 0 # 'nim check' does not like this internalAssert. if tmp >= 0: result = TRegister(tmp) -proc genLvalue(c: var TCtx, n: PNode, dest: var TDest) -proc genLvalue(c: var TCtx, n: PNode): TRegister {.inline.} = +proc genLvalue(c: var TCtx, n: CgNode, dest: var TDest) +proc genLvalue(c: var TCtx, n: CgNode): TRegister {.inline.} = var dest = noDest genLvalue(c, n, dest) result = TRegister(dest) -proc clearDest(c: var TCtx; n: PNode; dest: var TDest) {.inline.} = +proc clearDest(c: var TCtx; n: CgNode; dest: var TDest) {.inline.} = # stmt is different from 'void' in meta programming contexts. # So we only set dest to -1 if 'void': if dest >= 0 and (n.typ.isNil or n.typ.kind == tyVoid): c.freeTemp(dest) dest = -1 -proc isNotOpr(n: PNode): bool = - n.kind in nkCallKinds and n[0].kind == nkSym and +proc isNotOpr(n: CgNode): bool = + n.kind == cnkCall and n[0].kind == cnkSym and n[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 - -proc genWhile(c: var TCtx; n: PNode) = +proc genRepeat(c: var TCtx; n: CgNode) = # lab1: # body # jmp lab1 # lab2: let lab1 = c.genLabel withBlock(nil): - assert isTrue(n[0]) - c.gen(n[1]) + c.gen(n[0]) c.jmpBack(n, lab1) -proc genBlock(c: var TCtx; n: PNode) = +proc genBlock(c: var TCtx; n: CgNode) = let oldRegisterCount = c.prc.regInfo.len withBlock(n[0].sym): c.gen(n[1]) @@ -614,9 +597,9 @@ proc genBlock(c: var TCtx; n: PNode) = doAssert false, "leaking temporary " & $i & " " & $c.prc.regInfo[i].kind c.prc.regInfo[i] = RegInfo(kind: slotEmpty) -proc genBreak(c: var TCtx; n: PNode) = +proc genBreak(c: var TCtx; n: CgNode) = let lab1 = c.xjmp(n, opcJmp) - if n[0].kind == nkSym: + if n[0].kind == cnkSym: #echo cast[int](n[0].sym) for i in countdown(c.prc.blocks.len-1, 0): if c.prc.blocks[i].label == n[0].sym: @@ -626,13 +609,12 @@ proc genBreak(c: var TCtx; n: PNode) = else: c.prc.blocks[c.prc.blocks.high].fixups.add lab1 -proc genIf(c: var TCtx, n: PNode) = +proc genIf(c: var TCtx, n: CgNode) = # if (!expr1) goto lab1; # thenPart # lab1: - assert n.len == 1 block: - let it = n[0] + let it = n withTemp(tmp, it[0], it[0].typ): var elsePos: TPosition if isNotOpr(it[0]): @@ -707,6 +689,14 @@ proc toIntCnst(c: var TCtx, val: Int128): int = # integer constants are stored as their raw bit representation toIntCnst(c, BiggestInt(toInt64(val))) +proc genLiteral(c: var TCtx, n: CgNode): int = + case n.kind + of cnkIntLit: toIntCnst(c, n.intVal) + of cnkUIntLit: toIntCnst(c, n.intVal) + of cnkFloatLit: toFloatCnst(c, n.floatVal) + of cnkStrLit: toStringCnst(c, n.strVal) + else: unreachable(n.kind) + proc genLiteral(c: var TCtx, n: PNode): int = ## Create a constant, add it to the `c.constants` list and return ## the index of where it's located there @@ -722,7 +712,7 @@ proc genLiteral(c: var TCtx, n: PNode): int = c.config.internalError(n.info, $n.kind) 0 -template fillSliceList[T](sl: var seq[Slice[T]], nodes: openArray[PNode], +template fillSliceList[T](sl: var seq[Slice[T]], nodes: openArray[CgNode], get: untyped) = sl.newSeq(nodes.len) @@ -733,15 +723,15 @@ template fillSliceList[T](sl: var seq[Slice[T]], nodes: openArray[PNode], for (i, n) in nodes.pairs: sl[i] = - if n.kind == nkRange: + if n.kind == cnkRange: getIt(n[0]) .. getIt(n[1]) else: let e = getIt(n) e .. e -proc genBranchLit(c: var TCtx, n: PNode, t: PType): int = - ## Turns the slice-list or single literal of the given `nkOfBranch` into - ## a constant and returns it's index in `c.constant`. +proc genBranchLit(c: var TCtx, n: CgNode, t: PType): int = + ## Turns the slice-list or single literal of the given `cnkBranch` into + ## a constant and returns its index in `c.constant`. ## ## slice-lists are always added as a new constant while single literals ## are reused @@ -751,7 +741,7 @@ proc genBranchLit(c: var TCtx, n: PNode, t: PType): int = # already exist assert t.kind in IntegralTypes+{tyString} - if n.len == 2 and n[0].kind in nkLiterals: + if n.len == 2 and n[0].kind in cnkLiterals: # It's an 'of' branch with a single value result = c.genLiteral(n[0]) else: @@ -759,7 +749,7 @@ proc genBranchLit(c: var TCtx, n: PNode, t: PType): int = var cnst: VmConstant template values: untyped = - n.sons.toOpenArray(0, n.sons.high - 1) # -1 for the branch body + n.childs.toOpenArray(0, n.childs.high - 1) # -1 for the branch body case t.kind of IntegralTypes-{tyFloat..tyFloat128}: @@ -782,12 +772,26 @@ proc genBranchLit(c: var TCtx, n: PNode, t: PType): int = result = c.rawGenLiteral(cnst) +proc genBranchLit(c: var TCtx, n: PNode): int = + ## Compatibility overload that accepts a ``PNode``. Only branches with + ## integer values are supported. + if n.len == 2 and n[0].kind != nkRange: + toIntCnst(c, n[0].intVal) + else: + var sl = VmConstant(kind: cnstSliceListInt) + sl.intSlices.newSeq(n.len - 1) + for i, it in sliceIt(n.sons, 0, n.len-2): + sl.intSlices[i] = + if it.kind == nkRange: it[0].intVal .. it[1].intVal + else: it.intVal .. it.intVal + + c.rawGenLiteral(sl) -proc unused(c: TCtx; n: PNode; x: TDest) {.inline.} = +proc unused(c: TCtx; n: CgNode; x: TDest) {.inline.} = if x >= 0: - fail(n.info, vmGenDiagNotUnused, n) + fail(n.info, vmGenDiagNotUnused, PNode(nil)) -proc genCase(c: var TCtx; n: PNode) = +proc genCase(c: var TCtx; n: CgNode) = # if (!expr1) goto lab1; # thenPart # goto LEnd @@ -808,8 +812,7 @@ proc genCase(c: var TCtx; n: PNode) = # iterate of/else branches for i in 1.. high(typeof(result)): @@ -1015,7 +1011,7 @@ proc genField(c: TCtx; n: PNode): TRegister = result = s.position -proc genIndex(c: var TCtx; n: PNode; arr: PType): TRegister = +proc genIndex(c: var TCtx; n: CgNode; arr: PType): TRegister = if arr.skipTypes(abstractInst).kind == tyArray and (let x = firstOrd(c.config, arr); x != Zero): let tmp = c.genx(n) @@ -1026,28 +1022,31 @@ proc genIndex(c: var TCtx; n: PNode; arr: PType): TRegister = else: result = c.genx(n) -proc genRegLoad(c: var TCtx, n: PNode, dest, src: TRegister) = +proc genRegLoad(c: var TCtx, n: CgNode, typ: PType, dest, src: TRegister) = c.gABC(n, opcNodeToReg, dest, src) - let t = n.typ.skipTypes(abstractInst) + let t = typ.skipTypes(abstractInst) if t.isUnsigned() and t.size < sizeof(BiggestInt): c.gABC(n, opcNarrowU, dest, TRegister(t.size * 8)) -proc genCheckedObjAccessAux(c: var TCtx; n: PNode): TRegister -proc genSym(c: var TCtx, n: PNode, dest: var TDest, load = true) +proc genRegLoad(c: var TCtx, n: CgNode, dest, src: TRegister) {.inline.} = + genRegLoad(c, n, n.typ, dest, src) + +proc genCheckedObjAccessAux(c: var TCtx; n: CgNode): TRegister +proc genSym(c: var TCtx, n: CgNode, dest: var TDest, load = true) func usesRegister(p: PProc, s: PSym): bool = ## Returns whether the location identified by `s` is backed by a register ## (that is, whether the value is stored in a register directly) fitsRegister(s.typ) and s.id notin p.addressTaken -proc genNew(c: var TCtx; n: PNode) = +proc genNew(c: var TCtx; n: CgNode) = let dest = c.genLvalue(n[1]) c.gABx(n, opcNew, dest, c.genType(n[1].typ.skipTypes(abstractVar-{tyTypeDesc}))) c.freeTemp(dest) -proc genNewSeq(c: var TCtx; n: PNode) = +proc genNewSeq(c: var TCtx; n: CgNode) = let t = n[1].typ.skipTypes(abstractVar) assert t.kind == tySequence let @@ -1058,7 +1057,7 @@ proc genNewSeq(c: var TCtx; n: PNode) = c.freeTemp(len) c.freeTemp(dest) -proc genNewSeqOfCap(c: var TCtx; n: PNode; dest: var TDest) = +proc genNewSeqOfCap(c: var TCtx; n: CgNode; dest: var TDest) = prepare(c, dest, n, n.typ) let tmp = c.getTemp(n[1].typ) @@ -1067,20 +1066,20 @@ proc genNewSeqOfCap(c: var TCtx; n: PNode; dest: var TDest) = c.gABx(n, opcNewSeq, tmp, 0) c.freeTemp(tmp) -proc genUnaryABC(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = +proc genUnaryABC(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = prepare(c, dest, n, n.typ) let tmp = c.genx(n[1]) c.gABC(n, opc, dest, tmp) c.freeTemp(tmp) -proc genUnaryABI(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode; imm: BiggestInt=0) = +proc genUnaryABI(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode; imm: BiggestInt=0) = prepare(c, dest, n, n.typ) let tmp = c.genx(n[1]) c.gABI(n, opc, dest, tmp, imm) c.freeTemp(tmp) -proc genBinaryABC(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = +proc genBinaryABC(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = prepare(c, dest, n, n.typ) let tmp = c.genx(n[1]) @@ -1089,7 +1088,7 @@ proc genBinaryABC(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = c.freeTemp(tmp) c.freeTemp(tmp2) -proc genBinaryABCD(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = +proc genBinaryABCD(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = prepare(c, dest, n, n.typ) let tmp = c.genx(n[1]) @@ -1101,7 +1100,7 @@ proc genBinaryABCD(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = c.freeTemp(tmp2) c.freeTemp(tmp3) -proc genNarrow(c: var TCtx; n: PNode; dest: TDest) = +proc genNarrow(c: var TCtx; n: CgNode; 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: @@ -1110,7 +1109,7 @@ proc genNarrow(c: var TCtx; n: PNode; dest: TDest) = elif t.kind in {tyInt8..tyInt32} or (t.kind == tyInt and t.size < 8): c.gABC(n, opcNarrowS, dest, TRegister(t.size*8)) -proc genNarrowU(c: var TCtx; n: PNode; dest: TDest) = +proc genNarrowU(c: var TCtx; n: CgNode; 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: @@ -1118,15 +1117,15 @@ proc genNarrowU(c: var TCtx; n: PNode; dest: TDest) = (t.kind in {tyUInt, tyInt} and t.size < 8): c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) -proc genBinaryABCnarrow(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = +proc genBinaryABCnarrow(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = genBinaryABC(c, n, dest, opc) genNarrow(c, n, dest) -proc genBinaryABCnarrowU(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = +proc genBinaryABCnarrowU(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = genBinaryABC(c, n, dest, opc) genNarrowU(c, n, dest) -proc genBinarySet(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = +proc genBinarySet(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = prepare(c, dest, n, n.typ) let tmp = c.genx(n[1]) @@ -1135,7 +1134,7 @@ proc genBinarySet(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = c.freeTemp(tmp) c.freeTemp(tmp2) -proc genBinaryStmt(c: var TCtx; n: PNode; opc: TOpcode) = +proc genBinaryStmt(c: var TCtx; n: CgNode; opc: TOpcode) = let dest = c.genx(n[1]) tmp = c.genx(n[2]) @@ -1143,7 +1142,7 @@ proc genBinaryStmt(c: var TCtx; n: PNode; opc: TOpcode) = c.freeTemp(tmp) c.freeTemp(dest) -proc genBinaryStmtVar(c: var TCtx; n: PNode; opc: TOpcode) = +proc genBinaryStmtVar(c: var TCtx; n: CgNode; opc: TOpcode) = let dest = c.genLvalue(n[1]) tmp = c.genx(n[2]) @@ -1151,7 +1150,7 @@ proc genBinaryStmtVar(c: var TCtx; n: PNode; opc: TOpcode) = c.freeTemp(tmp) c.freeTemp(dest) -proc genParseOp(c: var TCtx; n: PNode; dest: var TDest, +proc genParseOp(c: var TCtx; n: CgNode; dest: var TDest, opc: range[opcParseExprToAst..opcParseStmtToAst]) = ## Generates the code for a ``parseExpr``/``parseStmt`` magic call if dest.isUnset: @@ -1161,7 +1160,7 @@ proc genParseOp(c: var TCtx; n: PNode; dest: var TDest, # register directly, so the used addr operation is skipped (if it hasn't # been eliminated by ``transf``) var x = n[2] - if x.kind in {nkAddr, nkHiddenAddr}: + if x.kind in {cnkAddr, cnkHiddenAddr}: x = x[0] let @@ -1172,7 +1171,7 @@ proc genParseOp(c: var TCtx; n: PNode; dest: var TDest, c.freeTemp(in1) c.freeTemp(in2) -proc genVarargsABC(c: var TCtx; n: PNode; dest: TRegister; opc: TOpcode) = +proc genVarargsABC(c: var TCtx; n: CgNode; dest: TRegister; opc: TOpcode) = var x = c.getTempRange(n.len-1, slotTempUnknown) for i in 1..= low(int8) and val <= high(int8) -proc genAddSubInt(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = +proc genAddSubInt(c: var TCtx; n: CgNode; dest: var TDest; opc: TOpcode) = if n[2].isInt8Lit: let tmp = c.genx(n[1]) if dest.isUnset: dest = c.getTemp(n.typ) @@ -1197,7 +1196,7 @@ proc genAddSubInt(c: var TCtx; n: PNode; dest: var TDest; opc: TOpcode) = genBinaryABC(c, n, dest, opc) c.genNarrow(n, dest) -proc genNumberConv(c: var TCtx, info: PNode, dest, src: TRegister, +proc genNumberConv(c: var TCtx, info: CgNode, dest, src: TRegister, desttype, srctype: PType) = ## Generates and emits the code for an *unchecked* conversion between two ## numeric types. @@ -1257,7 +1256,7 @@ proc genNumberConv(c: var TCtx, info: PNode, dest, src: TRegister, else: unreachable() -proc genConv(c: var TCtx; n, arg: PNode; dest: var TDest) = +proc genConv(c: var TCtx; n, arg: CgNode; dest: var TDest) = let a = skipTypes(n.typ, IrrelevantTypes + {tyRange}) b = skipTypes(arg.typ, IrrelevantTypes + {tyRange}) @@ -1285,7 +1284,7 @@ proc genConv(c: var TCtx; n, arg: PNode; dest: var TDest) = c.freeTemp(tmp) -proc genToStr(c: var TCtx, n, arg: PNode, dest: var TDest) = +proc genToStr(c: var TCtx, n, arg: CgNode, dest: var TDest) = # TODO: don't use ``opcConv`` for to-string conversions prepare(c, dest, n, n.typ) let tmp = c.genx(arg) @@ -1293,7 +1292,7 @@ proc genToStr(c: var TCtx, n, arg: PNode, dest: var TDest) = c.gABx(n, opcConv, tmp, c.genTypeInfo(arg.typ.skipTypes(IrrelevantTypes))) c.freeTemp(tmp) -proc genObjConv(c: var TCtx, n: PNode, dest: var TDest) = +proc genObjConv(c: var TCtx, n: CgNode, dest: var TDest) = prepare(c, dest, n.typ) let tmp = genx(c, n[0]) @@ -1314,7 +1313,7 @@ proc genObjConv(c: var TCtx, n: PNode, dest: var TDest) = c.freeTemp(tmp) -proc genCard(c: var TCtx; n: PNode; dest: var TDest) = +proc genCard(c: var TCtx; n: CgNode; dest: var TDest) = let tmp = c.genx(n[1]) if dest.isUnset: dest = c.getTemp(n.typ) c.gABC(n, opcCard, dest, tmp) @@ -1334,7 +1333,7 @@ template needsRegLoad(): untyped = mixin load load and fitsRegisterConsiderView(n.typ) -proc genCast(c: var TCtx, n, arg: PNode, dest: var TDest) = +proc genCast(c: var TCtx, n, arg: CgNode, dest: var TDest) = let a = skipTypes(n.typ, IrrelevantTypes + {tyRange}) b = skipTypes(arg.typ, IrrelevantTypes + {tyRange}) @@ -1351,7 +1350,7 @@ proc genCast(c: var TCtx, n, arg: PNode, dest: var TDest) = instLoc: instLoc(-1), typeMismatch: VmTypeMismatch(actualType: arg.typ, formalType: n.typ)) -proc genCastIntFloat(c: var TCtx; n: PNode; dest: var TDest) = +proc genCastIntFloat(c: var TCtx; n: CgNode; dest: var TDest) = const allowedIntegers = {tyInt..tyInt64, tyUInt..tyUInt64, tyChar} var signedIntegers = {tyInt..tyInt64} var unsignedIntegers = {tyUInt..tyUInt64, tyChar} @@ -1426,17 +1425,17 @@ proc genCastIntFloat(c: var TCtx; n: PNode; dest: var TDest) = instLoc: instLoc(), typeMismatch: VmTypeMismatch(actualType: dst, formalType: src)) -proc genDataToAst(c: var TCtx, n: PNode, dest: TRegister) = +proc genDataToAst(c: var TCtx, n: CgNode, dest: TRegister) = ## Generates and emits the bytecode for evaluating the expression `n` and ## deserializing the result to ``NimNode`` AST let tmp = c.genx(n) var typLit = TDest(-1) - c.genTypeLit(n.typ, typLit) + c.genTypeLit(n, n.typ, typLit) c.gABC(n, opcDataToAst, dest, tmp, typLit) c.freeTemp(typLit) c.freeTemp(tmp) -proc genVoidABC(c: var TCtx, n: PNode, dest: TDest, opcode: TOpcode) = +proc genVoidABC(c: var TCtx, n: CgNode, dest: TDest, opcode: TOpcode) = unused(c, n, dest) var tmp1 = c.genx(n[1]) @@ -1447,7 +1446,7 @@ proc genVoidABC(c: var TCtx, n: PNode, dest: TDest, opcode: TOpcode) = c.freeTemp(tmp2) c.freeTemp(tmp3) -proc genVoidBC(c: var TCtx, n: PNode, dest: TDest, opcode: TOpcode) = +proc genVoidBC(c: var TCtx, n: CgNode, dest: TDest, opcode: TOpcode) = ## Special convention used by some macrocache-related ops unused(c, n, dest) var @@ -1457,7 +1456,7 @@ proc genVoidBC(c: var TCtx, n: PNode, dest: TDest, opcode: TOpcode) = c.freeTemp(tmp1) c.freeTemp(tmp2) -proc loadInt(c: var TCtx, n: PNode, dest: TRegister, val: Int128) = +proc loadInt(c: var TCtx, n: CgNode, dest: TRegister, val: Int128) = ## Loads the integer `val` into `dest`, choosing the most efficient way to ## do so. if val in regBxMin-1..regBxMax: @@ -1467,11 +1466,11 @@ proc loadInt(c: var TCtx, n: PNode, dest: TRegister, val: Int128) = # requires a constant c.gABx(n, opcLdConst, dest, c.toIntCnst(val)) -proc genSetElem(c: var TCtx, n: PNode, first: Int128): TRegister = +proc genSetElem(c: var TCtx, n: CgNode, first: Int128): TRegister = result = c.getTemp(n.typ) if first != 0: - if n.kind in nkIntKinds: + if n.kind in {cnkIntLit, cnkUIntLit}: # a literal value. Since sem makes sure sets cannot store elements # with an adjusted value of >= 2^16, we know that the result of the # subtraction fits into the encodable range for ABX @@ -1497,7 +1496,7 @@ proc genSetElem(c: var TCtx, n: PNode, first: Int128): TRegister = else: gen(c, n, result) -proc genSetElem(c: var TCtx, n: PNode, typ: PType): TRegister {.inline.} = +proc genSetElem(c: var TCtx, n: CgNode, typ: PType): TRegister {.inline.} = ## `typ` is the type to derive the lower bound from let t = typ.skipTypes(abstractInst) assert t.kind == tySet @@ -1517,7 +1516,7 @@ proc ldNullOpcode(t: PType): TOpcode = assert t != nil if fitsRegister(t): opcLdNullReg else: opcLdNull -proc whichAsgnOpc(n: PNode; requiresCopy = true): TOpcode = +proc whichAsgnOpc(n: CgNode; requiresCopy = true): TOpcode = case n.typ.skipTypes(abstractRange-{tyTypeDesc}).kind of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64: opcAsgnInt @@ -1528,31 +1527,31 @@ proc whichAsgnOpc(n: PNode; requiresCopy = true): TOpcode = opcAsgnComplex #(if requiresCopy: opcAsgnComplex else: opcFastAsgnComplex) -func usesRegister(p: PProc, n: PNode): bool = +func usesRegister(p: PProc, n: CgNode): bool = ## Analyses and returns whether the value of the location named by l-value ## expression `n` is stored in a register instead of a memory location # XXX: instead of using a separate analysis, compute and return this as part # of ``genLValue`` and case n.kind - of nkSym: + of cnkSym: let s = n.sym not s.isGlobal and usesRegister(p, s) - of nkDerefExpr, nkHiddenDeref, nkDotExpr, nkBracketExpr, nkCheckedFieldExpr, - nkConv, nkObjDownConv, nkObjUpConv: + of cnkDeref, cnkDerefView, cnkFieldAccess, cnkBracketAccess, cnkCheckedFieldAccess, + cnkConv, cnkObjDownConv, cnkObjUpConv: false - of nkStmtListExpr: + of cnkStmtListExpr: usesRegister(p, n.lastSon) else: unreachable(n.kind) -proc genNoLoad(c: var TCtx, n: PNode): tuple[reg: TRegister, isDirect: bool] = +proc genNoLoad(c: var TCtx, n: CgNode): tuple[reg: TRegister, isDirect: bool] = ## Similar to ``genLValue``, but also returns whether the register storing ## the result stores a handle or a value. var dest = noDest genLvalue(c, n, dest) result = (TRegister(dest), usesRegister(c.prc, n)) -proc genLoc(c: var TCtx, n: PNode): Loc = +proc genLoc(c: var TCtx, n: CgNode): Loc = ## Generates and emits the code for evaluating the l-value expression `n`. ## The returned ``Loc`` holds the register information. assert fitsRegister(n.typ), "`genLoc` is not needed" @@ -1570,7 +1569,7 @@ proc genLoc(c: var TCtx, n: PNode): Loc = result.val = c.getTemp(n.typ) genRegLoad(c, n, result.val, result.handleReg) -proc finish(c: var TCtx, info: PNode, loc: sink Loc) = +proc finish(c: var TCtx, info: CgNode, loc: sink Loc) = ## Wraps up the modification to `loc` by writing the register-stored ## value back to the source memory location. if loc.handleReg != -1: @@ -1580,7 +1579,7 @@ proc finish(c: var TCtx, info: PNode, loc: sink Loc) = c.freeTemp(loc.val) -proc genMagic(c: var TCtx; n: PNode; dest: var TDest; m: TMagic) = +proc genMagic(c: var TCtx; n: CgNode; dest: var TDest; m: TMagic) = case m of mPred, mSubI: c.genAddSubInt(n, dest, opcSubInt) @@ -1870,7 +1869,7 @@ proc genMagic(c: var TCtx; n: PNode; dest: var TDest; m: TMagic) = of mEcho: unused(c, n, dest) let n = n[1].skipConv - if n.kind == nkBracket: + if n.kind == cnkArrayConstr: # can happen for nim check, see bug #9609 let x = c.getTempRange(n.len, slotTempUnknown) for i in 0.. 0: generateAST(c.graph, c.idgen, c.module, tree, source) - else: newNode(nkEmpty) + else: newNode(cnkEmpty) proc genStmt*(jit: var JitState, c: var TCtx; n: PNode): VmGenResult = ## Generates and emits code for the standalone top-level statement `n`. @@ -246,7 +245,7 @@ proc genExpr*(jit: var JitState, c: var TCtx, n: PNode): VmGenResult = proc genProc(jit: var JitState, c: var TCtx, s: PSym): VmGenResult = c.removeLastEof() - var body = + let body = if s.kind == skMacro: transformBody(c.graph, c.idgen, s, s.ast[bodyPos]) else: @@ -268,15 +267,15 @@ proc genProc(jit: var JitState, c: var TCtx, s: PSym): VmGenResult = discoverFrom(jit.discovery, MagicsToKeep, tree) register(c, jit.discovery) - body = generateAST(c.graph, c.idgen, s, tree, sourceMap) - echoOutput(c.config, s, body) + let outBody = generateAST(c.graph, c.idgen, s, tree, sourceMap) + echoOutput(c.config, s, outBody) - result = genProc(c, s, body) + result = genProc(c, s, outBody) if unlikely(result.isErr): rewind(jit.discovery) return - c.gABC(body, opcEof) + c.gABC(outBody, opcEof) updateEnvironment(c, jit.discovery) proc registerProcedure*(jit: var JitState, c: var TCtx, prc: PSym): FunctionIndex = From 756d2ab043593c49d49cd07a0745e49b429e3ed3 Mon Sep 17 00:00:00 2001 From: zerbina <100542850+zerbina@users.noreply.github.com> Date: Thu, 3 Aug 2023 15:31:23 +0000 Subject: [PATCH 13/20] ast_types: remove the `codegenExprNodeKinds` set It's obsolete now. --- compiler/ast/ast_types.nim | 42 -------------------------------------- 1 file changed, 42 deletions(-) diff --git a/compiler/ast/ast_types.nim b/compiler/ast/ast_types.nim index f65267c1db6..6867c3a886a 100644 --- a/compiler/ast/ast_types.nim +++ b/compiler/ast/ast_types.nim @@ -349,48 +349,6 @@ const nkUsingStmt, } - codegenExprNodeKinds* = { - nkEmpty, - nkSym, - nkType, - - nkCharLit, - nkIntLit, nkInt8Lit, nkInt16Lit, nkInt32Lit, nkInt64Lit, - nkUIntLit, nkUInt8Lit, nkUInt16Lit, nkUInt32Lit, nkUInt64Lit, - nkFloatLit, nkFloat32Lit, nkFloat64Lit, nkFloat128Lit, - nkStrLit, nkRStrLit, nkTripleStrLit, - nkNilLit, - - nkCall, - - nkObjConstr, nkCurly, nkBracket, - - nkBracketExpr, nkDotExpr, nkCheckedFieldExpr, nkDerefExpr, - - nkHiddenStdConv, nkConv, nkCast, nkAddr, nkHiddenAddr, - nkHiddenDeref, nkObjDownConv, nkObjUpConv, - - nkChckRangeF, nkChckRange64, nkChckRange, nkStringToCString, - nkCStringToString, - - nkAsgn, nkFastAsgn, - - nkAsmStmt, nkPragma, - - nkIfStmt, nkWhileStmt, nkCaseStmt, - - nkVarSection, nkLetSection, - nkTryStmt, - - nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkBlockStmt, nkDiscardStmt, - - nkStmtList, nkStmtListExpr, - - nkClosure, - nkTupleConstr, - nkNimNodeLit, - } - type TSymFlag* = enum # 48 flags! sfUsed ## read access of sym (for warnings) or simply used From 8499b486a1f2b485f989ddedabf6523a8a7d2ae0 Mon Sep 17 00:00:00 2001 From: zerbina <100542850+zerbina@users.noreply.github.com> Date: Thu, 3 Aug 2023 15:31:23 +0000 Subject: [PATCH 14/20] debugutils: add a `frameMsg` overload for `CgNode` --- compiler/utils/debugutils.nim | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/compiler/utils/debugutils.nim b/compiler/utils/debugutils.nim index d8102c59041..208ba32d2e5 100644 --- a/compiler/utils/debugutils.nim +++ b/compiler/utils/debugutils.nim @@ -15,6 +15,9 @@ import compiler/ast/[ ast_types ], + compiler/backend/[ + cgir + ], compiler/front/[ options, msgs @@ -632,6 +635,14 @@ template frameMsg*(c: ConfigRef, n: PNode) = $n.info.line, $n.info.col] +template frameMsg*(c: ConfigRef, n: CgNode) = + {.line.}: + setFrameMsg "$1 $2($3, $4)" % [ + $n.kind, + c.toFullPath(n.info.fileIndex), + $n.info.line, + $n.info.col] + const locOffset = -2 template addInNimDebugUtils*(c: ConfigRef; action: string; n, r: PNode; From 941b3f7d57898c27dfd7ba5568c2ea5c29e7ca8e Mon Sep 17 00:00:00 2001 From: zerbina <100542850+zerbina@users.noreply.github.com> Date: Thu, 3 Aug 2023 15:31:24 +0000 Subject: [PATCH 15/20] astgen: rename `generateAST` to `generateIR` --- compiler/backend/backends.nim | 4 ++-- compiler/backend/cbackend.nim | 4 ++-- compiler/backend/jsbackend.nim | 4 ++-- compiler/mir/astgen.nim | 2 +- compiler/mir/mirbridge.nim | 2 +- compiler/sem/injectdestructors.nim | 2 +- compiler/vm/vmbackend.nim | 4 ++-- compiler/vm/vmjit.nim | 12 ++++++------ 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/compiler/backend/backends.nim b/compiler/backend/backends.nim index e9d43824450..dd898f588b2 100644 --- a/compiler/backend/backends.nim +++ b/compiler/backend/backends.nim @@ -374,11 +374,11 @@ proc process(body: var MirFragment, ctx: PSym, graph: ModuleGraph, ## is used for the purpose of error reporting and debug tracing. injectDestructorCalls(graph, idgen, ctx, body.tree, body.source) -proc generateAST*(graph: ModuleGraph, idgen: IdGenerator, owner: PSym, +proc generateIR*(graph: ModuleGraph, idgen: IdGenerator, owner: PSym, code: sink MirFragment): CgNode = ## Translates the MIR code provided by `code` into ``CgNode`` IR and, ## if enabled, echoes the result. - result = generateAST(graph, idgen, owner, code.tree, code.source) + result = generateIR(graph, idgen, owner, code.tree, code.source) echoOutput(graph.config, owner, result) # ------- handling of lifted globals --------- diff --git a/compiler/backend/cbackend.nim b/compiler/backend/cbackend.nim index c230fd6783c..3ce176bd7ef 100644 --- a/compiler/backend/cbackend.nim +++ b/compiler/backend/cbackend.nim @@ -226,7 +226,7 @@ proc processEvent(g: BModuleList, inl: var InliningData, discovery: var Discover p = startProc(g.modules[evt.module.int], evt.sym) partial[evt.sym] = p - let body = generateAST(g.graph, bmod.idgen, evt.sym, evt.body) + let body = generateIR(g.graph, bmod.idgen, evt.sym, evt.body) # emit into the procedure: genStmts(p, body) @@ -238,7 +238,7 @@ proc processEvent(g: BModuleList, inl: var InliningData, discovery: var Discover # emit of the prototype in the case of self-recursion bmod.declaredThings.incl(evt.sym.id) let - body = generateAST(g.graph, bmod.idgen, evt.sym, evt.body) + body = generateIR(g.graph, bmod.idgen, evt.sym, evt.body) p = startProc(bmod, evt.sym, body) # we can't generate with ``genProc`` because we still need to output diff --git a/compiler/backend/jsbackend.nim b/compiler/backend/jsbackend.nim index cead500a6c6..aac00ee4225 100644 --- a/compiler/backend/jsbackend.nim +++ b/compiler/backend/jsbackend.nim @@ -74,13 +74,13 @@ proc processEvent(g: PGlobals, graph: ModuleGraph, modules: BModuleList, p = startProc(g, bmod, evt.sym) partial[evt.sym.id] = p - let body = generateAST(graph, bmod.idgen, evt.sym, evt.body) + let body = generateIR(graph, bmod.idgen, evt.sym, evt.body) genStmt(p, body) processLate(g, discovery) of bekProcedure: let - body = generateAST(graph, bmod.idgen, evt.sym, evt.body) + body = generateIR(graph, bmod.idgen, evt.sym, evt.body) r = genProc(g, bmod, evt.sym, body) if sfCompilerProc in evt.sym.flags: diff --git a/compiler/mir/astgen.nim b/compiler/mir/astgen.nim index c0e15cbc89c..95e80496d98 100644 --- a/compiler/mir/astgen.nim +++ b/compiler/mir/astgen.nim @@ -1396,7 +1396,7 @@ proc tb(tree: TreeWithSource, cl: var TranslateCl, start: NodePosition): CgNode tbMulti(tree, cl, cr) -proc generateAST*(graph: ModuleGraph, idgen: IdGenerator, owner: PSym, +proc generateIR*(graph: ModuleGraph, idgen: IdGenerator, owner: PSym, tree: sink MirTree, sourceMap: sink SourceMap): CgNode = ## Generates the ``CgNode`` IR corresponding to the input MIR code (`tree`), ## using `idgen` for provide new IDs when creating symbols. `sourceMap` diff --git a/compiler/mir/mirbridge.nim b/compiler/mir/mirbridge.nim index b77f26e16c7..36fd09315e3 100644 --- a/compiler/mir/mirbridge.nim +++ b/compiler/mir/mirbridge.nim @@ -177,5 +177,5 @@ proc canonicalize*(graph: ModuleGraph, idgen: IdGenerator, owner: PSym, echoMir(graph.config, owner, tree) # step 2: generate the ``CgNode`` tree - result = generateAST(graph, idgen, owner, tree, sourceMap) + result = generateIR(graph, idgen, owner, tree, sourceMap) echoOutput(graph.config, owner, result) \ No newline at end of file diff --git a/compiler/sem/injectdestructors.nim b/compiler/sem/injectdestructors.nim index 130eaf268ed..c0ac8f42783 100644 --- a/compiler/sem/injectdestructors.nim +++ b/compiler/sem/injectdestructors.nim @@ -1328,7 +1328,7 @@ proc injectDestructorCalls*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; # the MIR code wouldn't be very useful, so we turn it into backend IR # first, which we then render to text # XXX: this needs a deeper rethink - let n = generateAST(g, idgen, owner, tree, sourceMap) + let n = generateIR(g, idgen, owner, tree, sourceMap) g.config.msgWrite("--expandArc: " & owner.name.s & "\n") g.config.msgWrite(render(n)) g.config.msgWrite("\n-- end of expandArc ------------------------\n") \ No newline at end of file diff --git a/compiler/vm/vmbackend.nim b/compiler/vm/vmbackend.nim index 1773850cd13..8c083ed6d2a 100644 --- a/compiler/vm/vmbackend.nim +++ b/compiler/vm/vmbackend.nim @@ -106,7 +106,7 @@ proc generateCodeForProc(c: var TCtx, s: PSym, body: sink MirFragment): CodeInfo ## Generates and the bytecode for the procedure `s` with body `body`. The ## resulting bytecode is emitted into the global bytecode section. let - body = generateAST(c.graph, c.idgen, s, body) + body = generateIR(c.graph, c.idgen, s, body) r = genProc(c, s, body) if r.isOk: @@ -182,7 +182,7 @@ proc processEvent(c: var TCtx, mlist: ModuleList, discovery: var DiscoveryData, # it's a fragment that was just started p.prc = PProc(sym: evt.sym) - let stmt = generateAST(c.graph, c.idgen, evt.sym, evt.body) + let stmt = generateIR(c.graph, c.idgen, evt.sym, evt.body) genStmt(c, p[], stmt) of bekProcedure: # a complete procedure became available diff --git a/compiler/vm/vmjit.nim b/compiler/vm/vmjit.nim index c527c1a17f2..ac0b24b80e3 100644 --- a/compiler/vm/vmjit.nim +++ b/compiler/vm/vmjit.nim @@ -178,9 +178,9 @@ proc generateMirCode(c: var TCtx, n: PNode; else: generateCode(c.graph, selectOptions(c), n, result[0], result[1]) -proc generateAST(c: var TCtx, tree: sink MirTree, - source: sink SourceMap): CgNode {.inline.} = - if tree.len > 0: generateAST(c.graph, c.idgen, c.module, tree, source) +proc generateIR(c: var TCtx, tree: sink MirTree, + source: sink SourceMap): CgNode {.inline.} = + if tree.len > 0: generateIR(c.graph, c.idgen, c.module, tree, source) else: newNode(cnkEmpty) proc genStmt*(jit: var JitState, c: var TCtx; n: PNode): VmGenResult = @@ -194,7 +194,7 @@ proc genStmt*(jit: var JitState, c: var TCtx; n: PNode): VmGenResult = register(c, jit.discovery) let - n = generateAST(c, tree, sourceMap) + n = generateIR(c, tree, sourceMap) start = c.code.len r = vmgen.genStmt(c, n) @@ -229,7 +229,7 @@ proc genExpr*(jit: var JitState, c: var TCtx, n: PNode): VmGenResult = register(c, jit.discovery) let - n = generateAST(c, tree, sourceMap) + n = generateIR(c, tree, sourceMap) start = c.code.len r = vmgen.genExpr(c, n) @@ -267,7 +267,7 @@ proc genProc(jit: var JitState, c: var TCtx, s: PSym): VmGenResult = discoverFrom(jit.discovery, MagicsToKeep, tree) register(c, jit.discovery) - let outBody = generateAST(c.graph, c.idgen, s, tree, sourceMap) + let outBody = generateIR(c.graph, c.idgen, s, tree, sourceMap) echoOutput(c.config, s, outBody) result = genProc(c, s, outBody) From 4565463069d2987919520fe9ef65828f8f9715f6 Mon Sep 17 00:00:00 2001 From: zerbina <100542850+zerbina@users.noreply.github.com> Date: Thu, 3 Aug 2023 15:31:24 +0000 Subject: [PATCH 16/20] rename `astgen` to `cgirgen` In addition, the module is moved to the `backend` directory. --- compiler/backend/backends.nim | 4 ++-- compiler/{mir/astgen.nim => backend/cgirgen.nim} | 0 compiler/backend/compat.nim | 2 +- compiler/mir/mirbridge.nim | 2 +- compiler/sem/injectdestructors.nim | 2 +- compiler/vm/vmjit.nim | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) rename compiler/{mir/astgen.nim => backend/cgirgen.nim} (100%) diff --git a/compiler/backend/backends.nim b/compiler/backend/backends.nim index dd898f588b2..272dada5b61 100644 --- a/compiler/backend/backends.nim +++ b/compiler/backend/backends.nim @@ -13,13 +13,13 @@ import ], compiler/backend/[ cgmeth, - cgir + cgir, + cgirgen ], compiler/front/[ options ], compiler/mir/[ - astgen, mirbridge, mirconstr, mirgen, diff --git a/compiler/mir/astgen.nim b/compiler/backend/cgirgen.nim similarity index 100% rename from compiler/mir/astgen.nim rename to compiler/backend/cgirgen.nim diff --git a/compiler/backend/compat.nim b/compiler/backend/compat.nim index f78dff59211..23a267552c1 100644 --- a/compiler/backend/compat.nim +++ b/compiler/backend/compat.nim @@ -28,7 +28,7 @@ import int128 ] -from compiler/mir/astgen import translateLit +from compiler/backend/cgirgen import translateLit func lastSon*(n: CgNode): CgNode {.inline.} = # XXX: replace usages with `n[^1]` diff --git a/compiler/mir/mirbridge.nim b/compiler/mir/mirbridge.nim index 36fd09315e3..4c536d9d59e 100644 --- a/compiler/mir/mirbridge.nim +++ b/compiler/mir/mirbridge.nim @@ -9,13 +9,13 @@ import ], compiler/backend/[ cgir, + cgirgen, cgirutils ], compiler/front/[ options ], compiler/mir/[ - astgen, mirchangesets, mirconstr, mirtrees, diff --git a/compiler/sem/injectdestructors.nim b/compiler/sem/injectdestructors.nim index c0ac8f42783..a3e4a99d099 100644 --- a/compiler/sem/injectdestructors.nim +++ b/compiler/sem/injectdestructors.nim @@ -171,7 +171,6 @@ import ], compiler/mir/[ analysis, - astgen, mirchangesets, mirconstr, mirtrees, @@ -203,6 +202,7 @@ from compiler/ast/report_enums import ReportKind # XXX: we shouldn't need to be concerned with rendering backend- # IR to text here from compiler/backend/cgirutils import render +from compiler/backend/cgirgen import generateIR type AnalyseCtx = object diff --git a/compiler/vm/vmjit.nim b/compiler/vm/vmjit.nim index ac0b24b80e3..7aa76b2b06c 100644 --- a/compiler/vm/vmjit.nim +++ b/compiler/vm/vmjit.nim @@ -23,10 +23,10 @@ import ], compiler/backend/[ backends, - cgir + cgir, + cgirgen ], compiler/mir/[ - astgen, mirbridge, mirgen, mirtrees, From 1e950cdadf0629d9a4b5da9237014d5c5df67d26 Mon Sep 17 00:00:00 2001 From: zerbina <100542850+zerbina@users.noreply.github.com> Date: Fri, 4 Aug 2023 18:44:10 +0000 Subject: [PATCH 17/20] jsbackend: use a selective import for `mirbridge` --- compiler/backend/jsbackend.nim | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/backend/jsbackend.nim b/compiler/backend/jsbackend.nim index aac00ee4225..1536c050598 100644 --- a/compiler/backend/jsbackend.nim +++ b/compiler/backend/jsbackend.nim @@ -19,9 +19,6 @@ import compiler/front/[ options ], - compiler/mir/[ - mirbridge, # for `canonicalize` - ], compiler/modules/[ modulegraphs ], @@ -34,6 +31,8 @@ import ropes ] +from compiler/mir/mirbridge import canonicalize + type BModuleList = SeqMap[FileIndex, BModule] PartialTable = Table[int, PProc] From 3b7139ef3ab0544f4a8fef3c8cbfa0200c5f7329 Mon Sep 17 00:00:00 2001 From: zerbina <100542850+zerbina@users.noreply.github.com> Date: Fri, 4 Aug 2023 18:44:10 +0000 Subject: [PATCH 18/20] cgir: rename `childs` to `kids` --- compiler/backend/cgen.nim | 2 +- compiler/backend/cgir.nim | 24 +++++++++---------- compiler/backend/cgirgen.nim | 45 ++++++++++++++++++------------------ compiler/backend/compat.nim | 18 +++++++-------- compiler/vm/vmgen.nim | 2 +- 5 files changed, 45 insertions(+), 46 deletions(-) diff --git a/compiler/backend/cgen.nim b/compiler/backend/cgen.nim index 7e5a58841ca..1ca80ef04ea 100644 --- a/compiler/backend/cgen.nim +++ b/compiler/backend/cgen.nim @@ -96,7 +96,7 @@ const ## emit, asm, and error handling works template `[]=`(x: CgNode, i: Natural, n: CgNode) = - x.childs[i] = n + x.kids[i] = n proc findPendingModule(m: BModule, s: PSym): BModule = let ms = s.itemId.module #getModule(s) diff --git a/compiler/backend/cgir.nim b/compiler/backend/cgir.nim index 3e4feae280f..13d4261c231 100644 --- a/compiler/backend/cgir.nim +++ b/compiler/backend/cgir.nim @@ -153,33 +153,33 @@ type of cnkSym: sym*: PSym of cnkPragmaStmt: pragma*: TSpecialWord of cnkWithItems: - childs*: seq[CgNode] + kids*: seq[CgNode] # future direction: move to a single-sequence-based, data-oriented design # for the code-generator IR func len*(n: CgNode): int {.inline.} = - n.childs.len + n.kids.len template `[]`*(n: CgNode, i: Natural): CgNode = - n.childs[i] + n.kids[i] template `[]`*(n: CgNode, i: BackwardsIndex): CgNode = {.cast(noSideEffect).}: - n.childs[i] + n.kids[i] iterator items*(n: CgNode): CgNode = var i = 0 - let L = n.childs.len + let L = n.kids.len while i < L: - yield n.childs[i] + yield n.kids[i] inc i iterator pairs*(n: CgNode): (int, CgNode) = var i = 0 - let L = n.childs.len + let L = n.kids.len while i < L: - yield (i, n.childs[i]) + yield (i, n.kids[i]) inc i iterator sliceIt*[T](x: seq[T], lo, hi: Natural): (int, lent T) = @@ -189,14 +189,14 @@ iterator sliceIt*[T](x: seq[T], lo, hi: Natural): (int, lent T) = inc i proc newStmt*(kind: CgNodeKind, info: TLineInfo, - childs: varargs[CgNode]): CgNode = + kids: varargs[CgNode]): CgNode = result = CgNode(kind: kind, info: info) - result.childs = @childs + result.kids = @kids proc newExpr*(kind: CgNodeKind, info: TLineInfo, typ: PType, - childs: varargs[CgNode]): CgNode = + kids: varargs[CgNode]): CgNode = result = CgNode(kind: kind, info: info, typ: typ) - result.childs = @childs + result.kids = @kids proc newNode*(kind: CgNodeKind; info = unknownLineInfo; typ = PType(nil)): CgNode = diff --git a/compiler/backend/cgirgen.nim b/compiler/backend/cgirgen.nim index 95e80496d98..fe7d21bc71b 100644 --- a/compiler/backend/cgirgen.nim +++ b/compiler/backend/cgirgen.nim @@ -202,13 +202,13 @@ func toMode(kind: range[mnkArg..mnkConsume]): ArgumentMode = of mnkConsume: amConsume template `[]=`(x: CgNode, i: Natural, n: CgNode) = - x.childs[i] = n + x.kids[i] = n template `[]=`(x: CgNode, i: BackwardsIndex, n: CgNode) = - x.childs[i] = n + x.kids[i] = n template add(x: CgNode, y: CgNode) = - x.childs.add y + x.kids.add y proc copyTree(n: CgNode): CgNode = case n.kind @@ -217,17 +217,17 @@ proc copyTree(n: CgNode): CgNode = result[] = n[] of cnkWithItems: result = CgNode(kind: n.kind, info: n.info, typ: n.typ) - result.childs.setLen(n.childs.len) - for i, it in n.childs.pairs: + result.kids.setLen(n.kids.len) + for i, it in n.pairs: result[i] = copyTree(it) proc newEmpty(info = unknownLineInfo): CgNode = CgNode(kind: cnkEmpty, info: info) -proc newTree(kind: CgNodeKind, info: TLineInfo, childs: varargs[CgNode]): CgNode = +proc newTree(kind: CgNodeKind, info: TLineInfo, kids: varargs[CgNode]): CgNode = ## For node kinds that don't represent standalone statements. result = CgNode(kind: kind, info: info) - result.childs = @childs + result.kids = @kids func newTypeNode(info: TLineInfo, typ: PType): CgNode = CgNode(kind: cnkType, info: info, typ: typ) @@ -236,18 +236,18 @@ func newSymNode(s: PSym; info = unknownLineInfo): CgNode = CgNode(kind: cnkSym, info: info, typ: s.typ, sym: s) proc newExpr(kind: CgNodeKind, info: TLineInfo, typ: PType, - childs: sink seq[CgNode]): CgNode = + kids: sink seq[CgNode]): CgNode = ## Variant of ``newExpr`` optimized for passing a pre-existing child ## node sequence. result = CgNode(kind: kind, info: info, typ: typ) - result.childs = childs + result.kids = kids proc newStmt(kind: CgNodeKind, info: TLineInfo, - childs: sink seq[CgNode]): CgNode = + kids: sink seq[CgNode]): CgNode = ## Variant of ``newStmt`` optimized for passing a pre-existing child ## node sequence. result = CgNode(kind: kind, info: info) - result.childs = childs + result.kids = kids proc translateLit*(val: PNode): CgNode = ## Translates an ``mnkLiteral`` node to a ``CgNode``. @@ -287,7 +287,7 @@ proc translateLit*(val: PNode): CgNode = of nkNimNodeLit: node(cnkAstLit, astLit, val[0]) of nkRange: - node(cnkRange, childs, @[translateLit(val[0]), translateLit(val[1])]) + node(cnkRange, kids, @[translateLit(val[0]), translateLit(val[1])]) of nkBracket: assert val.len == 0 # XXX: ``mirgen`` having to generate ``mnkLiteral``s for empty seq @@ -314,7 +314,7 @@ proc copySubTree[A, B](source: PNode, slice: HSlice[A, B], to: var CgNode) = # resize the node list first: let start = to.len - to.childs.setLen(start + (b - a) + 1) + to.kids.setLen(start + (b - a) + 1) # then copy all nodes: for i in a..b: @@ -336,8 +336,7 @@ func toSingleNode(stmts: sink seq[CgNode]): CgNode = of 1: result = move stmts[0] else: - result = newNode(cnkStmtList) - result.childs = move stmts + result = newStmt(cnkStmtList, unknownLineInfo, stmts) proc wrapArg(stmts: sink seq[CgNode], info: TLineInfo, val: sink CgNode): CgNode = ## If there are extra statements (i.e. `stmts` is not empty), wraps the @@ -348,7 +347,7 @@ proc wrapArg(stmts: sink seq[CgNode], info: TLineInfo, val: sink CgNode): CgNode else: assert val.kind != cnkStmtListExpr result = newExpr(cnkStmtListExpr, info, val.typ, stmts) - result.childs.add val + result.add val proc newTemp(cl: var TranslateCl, info: TLineInfo, typ: PType): PSym = ## Creates and returns a new ``skTemp`` symbol @@ -476,7 +475,7 @@ proc addToVariantAccess(cl: var TranslateCl, dest: CgNode, field: PSym, # ``cnkCheckedFieldAccess`` in another one -- append the check instead. # While the order of the checks *should* be irrelevant, we still emit them # in the order they were generated originally (i.e. innermost to outermost) - dest.childs.insert(check, 1) + dest.kids.insert(check, 1) # update the type of the expression: dest.typ = field.typ dest @@ -1130,16 +1129,16 @@ proc tbInOut(tree: TreeWithSource, cl: var TranslateCl, prev: sink Values, tbArgs(prev, n.magic, cl) var node = newExpr(cnkCall, info, n.typ) - node.childs.newSeq(1 + prev.len) + node.kids.newSeq(1 + prev.len) # ``CgNode`` requires a symbol for magics, so we have to create one - node.childs[0] = newSymNode(createMagic(cl, n.magic)) + node.kids[0] = newSymNode(createMagic(cl, n.magic)) case prev.kind of vkNone: discard - of vkSingle: node.childs[1] = move prev.single + of vkSingle: node.kids[1] = move prev.single of vkMulti: for i, v in prev.list.mpairs: - node.childs[1 + i] = move v + node.kids[1 + i] = move v toValues node of mnkCall: @@ -1150,10 +1149,10 @@ proc tbInOut(tree: TreeWithSource, cl: var TranslateCl, prev: sink Values, # pre-process the argument expressions: tbArgs(prev, getCalleeMagic(prev.list[0]), cl) - node.childs = move prev.list + node.kids = move prev.list of vkSingle: # the procedure is called with no arguments - node.childs = @[prev.single] + node.kids = @[prev.single] of vkNone: unreachable() diff --git a/compiler/backend/compat.nim b/compiler/backend/compat.nim index 23a267552c1..317290aeb24 100644 --- a/compiler/backend/compat.nim +++ b/compiler/backend/compat.nim @@ -33,7 +33,7 @@ from compiler/backend/cgirgen import translateLit func lastSon*(n: CgNode): CgNode {.inline.} = # XXX: replace usages with `n[^1]` {.cast(noSideEffect).}: - n.childs[^1] + n.kids[^1] proc skipConv*(n: CgNode): CgNode {.inline.} = result = n @@ -205,7 +205,7 @@ proc flattenStmts*(n: CgNode): CgNode = for it in n.items: unnestStmts(it, result) else: - result.childs.add n + result.kids.add n result = CgNode(kind: cnkStmtList) unnestStmts(n, result) @@ -228,32 +228,32 @@ proc translate*(n: PNode): CgNode = of nkObjConstr: result = newExpr(cnkObjConstr, n.info, n.typ) for i, it in sliceIt(n.sons, 1, n.len-1): - result.childs.add translate(it) + result.kids.add translate(it) of nkBracket: result = newExpr(cnkArrayConstr, n.info, n.typ) for it in n.items: - result.childs.add translate(it) + result.kids.add translate(it) of nkCurly: result = newExpr(cnkSetConstr, n.info, n.typ) for it in n.items: - result.childs.add translate(it) + result.kids.add translate(it) of nkTupleConstr: result = newExpr(cnkTupleConstr, n.info, n.typ) for it in n.items: let it = if it.kind == nkExprColonExpr: it[1] else: it - result.childs.add translate(it) + result.kids.add translate(it) of nkClosure: result = newExpr(cnkClosureConstr, n.info, n.typ) - result.childs = @[translate(n[0]), translate(n[1])] + result.kids = @[translate(n[0]), translate(n[1])] of nkRange: result = newNode(cnkRange, n.info) - result.childs = @[translate(n[0]), translate(n[1])] + result.kids = @[translate(n[0]), translate(n[1])] of nkSym: result = newSymNode(n.sym) result.info = n.info of nkExprColonExpr: result = newNode(cnkBinding, n.info) - result.childs = @[translate(n[0]), translate(n[1])] + result.kids = @[translate(n[0]), translate(n[1])] of nkLiterals: result = translateLit(n) of nkNilLit: diff --git a/compiler/vm/vmgen.nim b/compiler/vm/vmgen.nim index 65effc558ff..caf7eae3a12 100644 --- a/compiler/vm/vmgen.nim +++ b/compiler/vm/vmgen.nim @@ -749,7 +749,7 @@ proc genBranchLit(c: var TCtx, n: CgNode, t: PType): int = var cnst: VmConstant template values: untyped = - n.childs.toOpenArray(0, n.childs.high - 1) # -1 for the branch body + n.kids.toOpenArray(0, n.kids.high - 1) # -1 for the branch body case t.kind of IntegralTypes-{tyFloat..tyFloat128}: From 1092b3f71abbb1f7006b073c5b89de33748011d1 Mon Sep 17 00:00:00 2001 From: zerbina <100542850+zerbina@users.noreply.github.com> Date: Fri, 4 Aug 2023 20:15:51 +0000 Subject: [PATCH 19/20] cgirgen: work around strict-side-effect analysis bug --- compiler/backend/cgirgen.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/backend/cgirgen.nim b/compiler/backend/cgirgen.nim index fe7d21bc71b..fcfccc5f0fc 100644 --- a/compiler/backend/cgirgen.nim +++ b/compiler/backend/cgirgen.nim @@ -336,7 +336,8 @@ func toSingleNode(stmts: sink seq[CgNode]): CgNode = of 1: result = move stmts[0] else: - result = newStmt(cnkStmtList, unknownLineInfo, stmts) + result = newNode(cnkStmtList) + result.kids = stmts proc wrapArg(stmts: sink seq[CgNode], info: TLineInfo, val: sink CgNode): CgNode = ## If there are extra statements (i.e. `stmts` is not empty), wraps the From 67754034ac037ea34cfc6ecaebd245ab4d02b295 Mon Sep 17 00:00:00 2001 From: zerbina <100542850+zerbina@users.noreply.github.com> Date: Fri, 4 Aug 2023 20:15:51 +0000 Subject: [PATCH 20/20] docs: update mentions of `astgen` The module is named `cgirgen` now. `debug.rst` also contained outdated mentions of `PNode` being the IR the code generators -- this is fixed too. --- compiler/backend/ccgexprs.nim | 4 ++-- compiler/mir/mirtrees.nim | 2 +- compiler/sem/injectdestructors.nim | 2 +- compiler/vm/vmgen.nim | 2 +- doc/debug.rst | 4 ++-- tests/lang_callable/generics/tobjecttyperel.nim | 2 +- .../concepts/tconstructor_with_concept_type.nim | 2 +- tests/lang_experimental/views/tviews_in_object.nim | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/compiler/backend/ccgexprs.nim b/compiler/backend/ccgexprs.nim index d926d450ceb..256431a7165 100644 --- a/compiler/backend/ccgexprs.nim +++ b/compiler/backend/ccgexprs.nim @@ -175,9 +175,9 @@ proc genAssignment(p: BProc, dest, src: TLoc) = linefmt(p, cpsStmts, "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, $3);$n", [rdLoc(dest), rdLoc(src), getSize(p.config, dest.t)]) of ctNimOpenArray: - # HACK: ``astgen`` elides to-openArray-conversion operations, so we + # HACK: ``cgirgen`` elides to-openArray-conversion operations, so we # need to reconstruct that information here. Remove this case - # once ``astgen`` no longer elides the operations + # once ``cgirgen`` no longer elides the operations if reifiedOpenArray(dest.lode): genOpenArrayConv(p, dest, src) else: diff --git a/compiler/mir/mirtrees.nim b/compiler/mir/mirtrees.nim index 5b15201b18c..181edc40cdf 100644 --- a/compiler/mir/mirtrees.nim +++ b/compiler/mir/mirtrees.nim @@ -132,7 +132,7 @@ type mnkConv ## ``conv(x)``; a conversion. Depending on the source and ## target type, lvalue-ness is preserved # XXX: distinguishing between ``stdConv`` and ``conv`` is only done to - # make ``astgen`` a bit more efficient. Further progress should focus + # make ``cgirgen`` a bit more efficient. Further progress should focus # on removing the need for it mnkCast ## ``cast(x)``; produces a new *instance* of the input value ## with a different type diff --git a/compiler/sem/injectdestructors.nim b/compiler/sem/injectdestructors.nim index a3e4a99d099..27217005929 100644 --- a/compiler/sem/injectdestructors.nim +++ b/compiler/sem/injectdestructors.nim @@ -610,7 +610,7 @@ func undoConversions(buf: var MirNodeSeq, tree: MirTree, src: OpValue) = ## When performing a destructive move for ``ref`` values, it's possible for ## the source to be an lvalue conversion -- in that case, we want pass the ## uncoverted root location to the ``wasMoved`` operation. To do so, we apply - ## the conversions in *reverse*. ``astgen`` detects this pattern and removes + ## the conversions in *reverse*. ``cgirgen`` detects this pattern and removes ## the conversions that cancel each other out. var p = NodePosition(src) while tree[p].kind in {mnkStdConv, mnkConv}: diff --git a/compiler/vm/vmgen.nim b/compiler/vm/vmgen.nim index caf7eae3a12..b1c98434dc2 100644 --- a/compiler/vm/vmgen.nim +++ b/compiler/vm/vmgen.nim @@ -2759,7 +2759,7 @@ proc genLvalue(c: var TCtx, n: CgNode, dest: var TDest) = of cnkCall: # we only reach this case for ``HiddenAddr (DerefView (Call ...))``. # Generate the call returning a view as is - # XXX: ``astgen`` should not emit these instead + # XXX: ``cgirgen`` should not emit these instead assert isLocView(n.typ) gen(c, n, dest) of cnkStmtListExpr: diff --git a/doc/debug.rst b/doc/debug.rst index 8fd624cbbc4..9156eca6d7a 100644 --- a/doc/debug.rst +++ b/doc/debug.rst @@ -437,8 +437,8 @@ printed twice. To print the generated MIR code for a procedure, `--define:nimShowMir=name` can be used. The same limitation as for `nimShowMirInput` apply. -`--define:nimShowMirOutput=name` prints the `PNode`-AST that is output by -`astgen`. This is AST that the code-generators will operate on. +`--define:nimShowMirOutput=name` prints the `CgNode`-IR that is output by +`cgirgen`. This is IR that the code generators operate on. While all of the defines listed above can be used simultaneously, only a single occurrence of each is considered. Each further occurrence will override the diff --git a/tests/lang_callable/generics/tobjecttyperel.nim b/tests/lang_callable/generics/tobjecttyperel.nim index 5842a508476..2fc28c22d57 100644 --- a/tests/lang_callable/generics/tobjecttyperel.nim +++ b/tests/lang_callable/generics/tobjecttyperel.nim @@ -9,7 +9,7 @@ cool test''' """ -# knownIssue: fails for the VM target because ``astgen`` emits the +# knownIssue: fails for the VM target because ``cgirgen`` emits the # incorrect conversion operator for ``ref`` types involving # generics diff --git a/tests/lang_experimental/concepts/tconstructor_with_concept_type.nim b/tests/lang_experimental/concepts/tconstructor_with_concept_type.nim index 47830276c54..824c8c92611 100644 --- a/tests/lang_experimental/concepts/tconstructor_with_concept_type.nim +++ b/tests/lang_experimental/concepts/tconstructor_with_concept_type.nim @@ -13,7 +13,7 @@ type x is MyType # semantic analysis changes the type of the ``nkObjConstr`` node to a resolved -# ``tyUserTypeClass``, which is something not considered by ``astgen`` and the +# ``tyUserTypeClass``, which is something not considered by ``cgirgen`` and the # code generators var x: Concept = MyType(val: 1) doAssert x.val == 1 \ No newline at end of file diff --git a/tests/lang_experimental/views/tviews_in_object.nim b/tests/lang_experimental/views/tviews_in_object.nim index 8ed9d5e997a..c5d56cb769f 100644 --- a/tests/lang_experimental/views/tviews_in_object.nim +++ b/tests/lang_experimental/views/tviews_in_object.nim @@ -55,7 +55,7 @@ testCase(seq[int], @[1]): doAssert o.field[0] == 1 # subscript access works doAssert o.field == [1] -# XXX: internal compiler error in ``astgen`` +# XXX: internal compiler error in ``cgirgen`` when false: # test: single tuple location testCase(tuple[x: int], (1,)):