diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2023-10-11 17:44:14 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-10-11 17:44:14 +0200 |
commit | 816589b6674e3af281766f1279420758dcacedc4 (patch) | |
tree | 3e4f825e29e2068466a35edc69cc27967b743687 | |
parent | ecaccafa6c18e0b92cd5f75d3363d61c0866dec9 (diff) | |
download | Nim-816589b6674e3af281766f1279420758dcacedc4.tar.gz |
NIR: Nim intermediate representation (#22777)
Theoretical Benefits / Plans: - Typed assembler-like language. - Allows for a CPS transformation. - Can replace the existing C backend by a new C backend. - Can replace the VM. - Can do more effective "not nil" checking and static array bounds checking. - Can be used instead of the DFA. - Easily translatable to LLVM. - Reasonably easy to produce native code from. - Tiny memory consumption. No pointers, no cry. **In very early stages of development.** Todo: - [x] Map Nim types to IR types. - [ ] Map Nim AST to IR instructions: - [x] Map bitsets to bitops. - [ ] Implement string cases. - [ ] Implement range and index checks. - [x] Implement `default(T)` builtin. - [x] Implement multi string concat. - [ ] Write some analysis passes. - [ ] Write a backend. - [x] Integrate into the compilation pipeline.
35 files changed, 4009 insertions, 122 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index dd7561264..4fe72929f 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -936,7 +936,7 @@ type # it won't cause problems # for skModule the string literal to output for # deprecated modules. - instantiatedFrom*: PSym # for instances, the generic symbol where it came from. + instantiatedFrom*: PSym # for instances, the generic symbol where it came from. when defined(nimsuggest): allUsages*: seq[TLineInfo] @@ -2173,3 +2173,7 @@ const nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr, nkMixinStmt, nkBindStmt} + +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 diff --git a/compiler/bitsets.nim b/compiler/bitsets.nim index 756d93217..7d142b01d 100644 --- a/compiler/bitsets.nim +++ b/compiler/bitsets.nim @@ -90,3 +90,8 @@ proc bitSetCard*(x: TBitSet): BiggestInt = result = 0 for it in x: result.inc int(populationCount[it]) + +proc bitSetToWord*(s: TBitSet; size: int): BiggestUInt = + result = 0 + for j in 0..<size: + if j < s.len: result = result or (BiggestUInt(s[j]) shl (j * 8)) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index b2c5a5ca8..44d04d763 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -112,11 +112,6 @@ proc genLiteral(p: BProc, n: PNode, ty: PType; result: var Rope) = proc genLiteral(p: BProc, n: PNode; result: var Rope) = genLiteral(p, n, n.typ, result) -proc bitSetToWord(s: TBitSet, size: int): BiggestUInt = - result = 0 - for j in 0..<size: - if j < s.len: result = result or (BiggestUInt(s[j]) shl (j * 8)) - proc genRawSetData(cs: TBitSet, size: int; result: var Rope) = if size > 8: var res = "{\n" @@ -1561,7 +1556,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = if e[i].len == 3 and optFieldCheck in p.options: check = e[i][2] genFieldObjConstr(p, ty, useTemp, isRef, e[i][0], e[i][1], check, d, r, e.info) - + if useTemp: if d.k == locNone: d = tmp @@ -1868,8 +1863,8 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = else: unaryExpr(p, e, d, "$1.Field1") of tyCstring: - if op == mHigh: unaryExpr(p, e, d, "($1 ? (#nimCStrLen($1)-1) : -1)") - else: unaryExpr(p, e, d, "($1 ? #nimCStrLen($1) : 0)") + if op == mHigh: unaryExpr(p, e, d, "(#nimCStrLen($1)-1)") + else: unaryExpr(p, e, d, "#nimCStrLen($1)") of tyString: var a: TLoc = initLocExpr(p, e[1]) var x = lenExpr(p, a) @@ -1889,13 +1884,6 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = else: putIntoDest(p, d, e, rope(lengthOrd(p.config, typ))) else: internalError(p.config, e.info, "genArrayLen()") -proc makeAddr(n: PNode; idgen: IdGenerator): PNode = - if n.kind == nkHiddenAddr: - result = n - else: - result = newTree(nkHiddenAddr, n) - result.typ = makePtrType(n.typ, idgen) - proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = if optSeqDestructors in p.config.globalOptions: e[1] = makeAddr(e[1], p.module.idgen) @@ -2521,8 +2509,12 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mOrd: genOrd(p, e, d) of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray: genArrayLen(p, e, d, op) - of mGCref: unaryStmt(p, e, d, "if ($1) { #nimGCref($1); }$n") - of mGCunref: unaryStmt(p, e, d, "if ($1) { #nimGCunref($1); }$n") + of mGCref: + # only a magic for the old GCs + unaryStmt(p, e, d, "if ($1) { #nimGCref($1); }$n") + of mGCunref: + # only a magic for the old GCs + unaryStmt(p, e, d, "if ($1) { #nimGCunref($1); }$n") of mSetLengthStr: genSetLengthStr(p, e, d) of mSetLengthSeq: genSetLengthSeq(p, e, d) of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet, @@ -3217,22 +3209,6 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Rope) = else: globalError(p.config, info, "cannot create null element for: " & $t.kind) -proc caseObjDefaultBranch(obj: PNode; branch: Int128): int = - result = 0 - for i in 1 ..< obj.len: - for j in 0 .. obj[i].len - 2: - if obj[i][j].kind == nkRange: - let x = getOrdValue(obj[i][j][0]) - let y = getOrdValue(obj[i][j][1]) - if branch >= x and branch <= y: - return i - elif getOrdValue(obj[i][j]) == branch: - return i - if obj[i].len == 1: - # else branch - return i - assert(false, "unreachable") - proc isEmptyCaseObjectBranch(n: PNode): bool = for it in n: if it.kind == nkSym and not isEmptyType(it.sym.typ): return false diff --git a/compiler/ccgreset.nim b/compiler/ccgreset.nim index bd0e2a58a..47b6a1e15 100644 --- a/compiler/ccgreset.nim +++ b/compiler/ccgreset.nim @@ -95,11 +95,11 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) = lineCg(p, cpsStmts, "$1 = 0;$n", [accessor]) else: raiseAssert "unexpected set type kind" - of {tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation, + of tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation, tyGenericParam, tyOrdinal, tyRange, tyOpenArray, tyForward, tyVarargs, tyUncheckedArray, tyProxy, tyBuiltInTypeClass, tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, - tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable}: + tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable: discard proc specializeReset(p: BProc, a: TLoc) = diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index c8f68c124..45a234332 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -1457,7 +1457,8 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) = let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type" if optTinyRtti in p.config.globalOptions: let checkFor = $getObjDepth(t[i][j].typ) - appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))]) + appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", + [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))]) else: let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info) appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor]) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 2f92cb12b..a591ce93b 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -24,7 +24,7 @@ type dkResult #skResult dkConst #skConst dkOther #skType, skTemp, skLet and skForVar so far - + proc descKindFromSymKind(kind: TSymKind): TypeDescKind = case kind of skParam: dkParam @@ -33,7 +33,7 @@ proc descKindFromSymKind(kind: TSymKind): TypeDescKind = of skResult: dkResult of skConst: dkConst else: dkOther - + proc isKeyword(w: PIdent): bool = # Nim and C++ share some keywords # it's more efficient to test the whole Nim keywords range @@ -464,7 +464,7 @@ proc multiFormat*(frmt: var string, chars : static openArray[char], args: openAr var num = 0 while i < frmt.len: if frmt[i] == c: - inc(i) + inc(i) case frmt[i] of c: res.add(c) @@ -521,7 +521,7 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params types.add getTypeDescWeak(m, this.typ, check, dkParam) let firstParam = if isCtor: 1 else: 2 - for i in firstParam..<t.n.len: + for i in firstParam..<t.n.len: if t.n[i].kind != nkSym: internalError(m.config, t.n.info, "genMemberProcParams") var param = t.n[i].sym var descKind = dkParam @@ -649,7 +649,7 @@ proc mangleRecFieldName(m: BModule; field: PSym): Rope = result = rope(mangleField(m, field.name)) if result == "": internalError(m.config, field.info, "mangleRecFieldName") -proc hasCppCtor(m: BModule; typ: PType): bool = +proc hasCppCtor(m: BModule; typ: PType): bool = result = false if m.compileToCpp and typ != nil and typ.itemId in m.g.graph.memberProcsPerType: for prc in m.g.graph.memberProcsPerType[typ.itemId]: @@ -763,7 +763,7 @@ proc getRecordFields(m: BModule; typ: PType, check: var IntSet): Rope = if isCtorGen and not isDefaultCtorGen: var ch: IntSet result.addf "$1() = default;$n", [getTypeDescAux(m, typ, ch, dkOther)] - + proc fillObjectFields*(m: BModule; typ: PType) = # sometimes generic objects are not consistently merged. We patch over # this fact here. @@ -771,7 +771,7 @@ proc fillObjectFields*(m: BModule; typ: PType) = discard getRecordFields(m, typ, check) proc mangleDynLibProc(sym: PSym): Rope - + proc getRecordDescAux(m: BModule; typ: PType, name, baseType: Rope, check: var IntSet, hasField:var bool): Rope = result = "" @@ -816,7 +816,7 @@ proc getRecordDesc(m: BModule; typ: PType, name: Rope, else: structOrUnion = structOrUnion(typ) var baseType: string = "" - if typ[0] != nil: + if typ[0] != nil: baseType = getTypeDescAux(m, typ[0].skipTypes(skipPtrs), check, dkField) if typ.sym == nil or sfCodegenDecl notin typ.sym.flags: result = structOrUnion & " " & name @@ -1128,8 +1128,8 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes result = "" # fixes bug #145: excl(check, t.id) - - + + proc getTypeDesc(m: BModule; typ: PType; kind = dkParam): Rope = var check = initIntSet() result = getTypeDescAux(m, typ, check, kind) @@ -1214,7 +1214,7 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = var name, params, rettype, superCall: string = "" var isFnConst, isOverride, isMemberVirtual: bool = false parseVFunctionDecl(prc.constraint.strVal, name, params, rettype, superCall, isFnConst, isOverride, isMemberVirtual, isCtor) - genMemberProcParams(m, prc, superCall, rettype, name, params, check, true, false) + genMemberProcParams(m, prc, superCall, rettype, name, params, check, true, false) let isVirtual = sfVirtual in prc.flags or isMemberVirtual var fnConst, override: string = "" if isCtor: @@ -1224,7 +1224,7 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = if isFwdDecl: if isVirtual: rettype = "virtual " & rettype - if isOverride: + if isOverride: override = " override" superCall = "" else: @@ -1232,14 +1232,14 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = prc.loc.r = "$1$2(@)" % [memberOp, name] elif superCall != "": superCall = " : " & superCall - + name = "$1::$2" % [typDesc, name] result.add "N_LIB_PRIVATE " result.addf("$1$2($3, $4)$5$6$7$8", [rope(CallingConvToStr[prc.typ.callConv]), asPtrStr, rettype, name, params, fnConst, override, superCall]) - + proc genProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false) = # using static is needed for inline procs var check = initIntSet() @@ -1575,10 +1575,6 @@ proc genTypeInfo2Name(m: BModule; t: PType): Rope = proc isTrivialProc(g: ModuleGraph; s: PSym): bool {.inline.} = getBody(g, s).len == 0 -proc makePtrType(baseType: PType; idgen: IdGenerator): PType = - result = newType(tyPtr, nextTypeId idgen, baseType.owner) - addSonSkipIntLit(result, baseType, idgen) - proc generateRttiDestructor(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp; info: TLineInfo; idgen: IdGenerator; theProc: PSym): PSym = # the wrapper is roughly like: @@ -1946,14 +1942,14 @@ proc genTypeInfo*(config: ConfigRef, m: BModule; t: PType; info: TLineInfo): Rop else: result = genTypeInfoV1(m, t, info) -proc genTypeSection(m: BModule, n: PNode) = +proc genTypeSection(m: BModule, n: PNode) = var intSet = initIntSet() for i in 0..<n.len: if len(n[i]) == 0: continue if n[i][0].kind != nkPragmaExpr: continue for p in 0..<n[i][0].len: if (n[i][0][p].kind != nkSym): continue - if sfExportc in n[i][0][p].sym.flags: + if sfExportc in n[i][0][p].sym.flags: discard getTypeDescAux(m, n[i][0][p].typ, intSet, descKindFromSymKind(n[i][0][p].sym.kind)) if m.g.generatedHeader != nil: discard getTypeDescAux(m.g.generatedHeader, n[i][0][p].typ, intSet, descKindFromSymKind(n[i][0][p].sym.kind)) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index e9045b066..adee8f845 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -17,6 +17,8 @@ import lowerings, tables, sets, ndi, lineinfos, pathutils, transf, injectdestructors, astmsgs, modulepaths, backendpragmas +from expanddefaults import caseObjDefaultBranch + import pipelineutils when defined(nimPreviewSlimSystem): @@ -499,8 +501,8 @@ proc resetLoc(p: BProc, loc: var TLoc) = # array passed as argument decayed into pointer, bug #7332 # so we use getTypeDesc here rather than rdLoc(loc) let tyDesc = getTypeDesc(p.module, loc.t, descKindFromSymKind mapTypeChooser(loc)) - if p.module.compileToCpp and isOrHasImportedCppType(typ): - if lfIndirect in loc.flags: + if p.module.compileToCpp and isOrHasImportedCppType(typ): + if lfIndirect in loc.flags: #C++ cant be just zeroed. We need to call the ctors var tmp = getTemp(p, loc.t) linefmt(p, cpsStmts,"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n", @@ -508,7 +510,7 @@ proc resetLoc(p: BProc, loc: var TLoc) = else: linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n", [addrLoc(p.config, loc), tyDesc]) - + # XXX: We can be extra clever here and call memset only # on the bytes following the m_type field? genObjectInit(p, cpsStmts, loc.t, loc, constructObj) @@ -551,7 +553,7 @@ proc getTemp(p: BProc, t: PType, needsInit=false): TLoc = result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t, storage: OnStack, flags: {}) if p.module.compileToCpp and isOrHasImportedCppType(t): - linefmt(p, cpsLocals, "$1 $2$3;$n", [getTypeDesc(p.module, t, dkVar), result.r, + linefmt(p, cpsLocals, "$1 $2$3;$n", [getTypeDesc(p.module, t, dkVar), result.r, genCppInitializer(p.module, p, t)]) else: linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, dkVar), result.r]) @@ -607,8 +609,8 @@ proc assignLocalVar(p: BProc, n: PNode) = # this need not be fulfilled for inline procs; they are regenerated # for each module that uses them! let nl = if optLineDir in p.config.options: "" else: "\n" - var decl = localVarDecl(p, n) - if p.module.compileToCpp and isOrHasImportedCppType(n.typ): + var decl = localVarDecl(p, n) + if p.module.compileToCpp and isOrHasImportedCppType(n.typ): decl.add genCppInitializer(p.module, p, n.typ) decl.add ";" & nl line(p, cpsLocals, decl) diff --git a/compiler/dfa.nim b/compiler/dfa.nim index 1459bde45..4cae9ec42 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -129,10 +129,6 @@ template withBlock(labl: PSym; body: untyped) = body popBlock(c, oldLen) -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 - template forkT(body) = let lab1 = c.forkI() body diff --git a/compiler/expanddefaults.nim b/compiler/expanddefaults.nim new file mode 100644 index 000000000..988433278 --- /dev/null +++ b/compiler/expanddefaults.nim @@ -0,0 +1,131 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +import lineinfos, ast, types + +proc caseObjDefaultBranch*(obj: PNode; branch: Int128): int = + result = 0 + for i in 1 ..< obj.len: + for j in 0 .. obj[i].len - 2: + if obj[i][j].kind == nkRange: + let x = getOrdValue(obj[i][j][0]) + let y = getOrdValue(obj[i][j][1]) + if branch >= x and branch <= y: + return i + elif getOrdValue(obj[i][j]) == branch: + return i + if obj[i].len == 1: + # else branch + return i + return 1 + +template newZero(t: PType; info: TLineInfo; k = nkIntLit): PNode = newNodeIT(k, info, t) + +proc expandDefault*(t: PType; info: TLineInfo): PNode + +proc expandField(s: PSym; info: TLineInfo): PNode = + result = newNodeIT(nkExprColonExpr, info, s.typ) + result.add newSymNode(s) + result.add expandDefault(s.typ, info) + +proc expandDefaultN(n: PNode; info: TLineInfo; res: PNode) = + case n.kind + of nkRecList: + for i in 0..<n.len: + expandDefaultN(n[i], info, res) + of nkRecCase: + res.add expandField(n[0].sym, info) + var branch = Zero + let constOrNil = n[0].sym.astdef + if constOrNil != nil: + branch = getOrdValue(constOrNil) + + let selectedBranch = caseObjDefaultBranch(n, branch) + let b = lastSon(n[selectedBranch]) + expandDefaultN b, info, res + of nkSym: + res.add expandField(n.sym, info) + else: + discard + +proc expandDefaultObj(t: PType; info: TLineInfo; res: PNode) = + if t[0] != nil: + expandDefaultObj(t[0], info, res) + expandDefaultN(t.n, info, res) + +proc expandDefault(t: PType; info: TLineInfo): PNode = + case t.kind + of tyInt: result = newZero(t, info, nkIntLit) + of tyInt8: result = newZero(t, info, nkInt8Lit) + of tyInt16: result = newZero(t, info, nkInt16Lit) + of tyInt32: result = newZero(t, info, nkInt32Lit) + of tyInt64: result = newZero(t, info, nkInt64Lit) + of tyUInt: result = newZero(t, info, nkUIntLit) + of tyUInt8: result = newZero(t, info, nkUInt8Lit) + of tyUInt16: result = newZero(t, info, nkUInt16Lit) + of tyUInt32: result = newZero(t, info, nkUInt32Lit) + of tyUInt64: result = newZero(t, info, nkUInt64Lit) + of tyFloat: result = newZero(t, info, nkFloatLit) + of tyFloat32: result = newZero(t, info, nkFloat32Lit) + of tyFloat64: result = newZero(t, info, nkFloat64Lit) + of tyFloat128: result = newZero(t, info, nkFloat64Lit) + of tyChar: result = newZero(t, info, nkCharLit) + of tyBool: result = newZero(t, info, nkIntLit) + of tyEnum: + # Could use low(T) here to finally fix old language quirks + result = newZero(t, info, nkIntLit) + of tyRange: + # Could use low(T) here to finally fix old language quirks + result = expandDefault(t[0], info) + of tyVoid: result = newZero(t, info, nkEmpty) + of tySink, tyGenericInst, tyDistinct, tyAlias, tyOwned: + result = expandDefault(t.lastSon, info) + of tyOrdinal, tyGenericBody, tyGenericParam, tyInferred, tyStatic: + if t.len > 0: + result = expandDefault(t.lastSon, info) + else: + result = newZero(t, info, nkEmpty) + of tyFromExpr: + if t.n != nil and t.n.typ != nil: + result = expandDefault(t.n.typ, info) + else: + result = newZero(t, info, nkEmpty) + of tyArray: + result = newZero(t, info, nkBracket) + let n = toInt64(lengthOrd(nil, t)) + for i in 0..<n: + result.add expandDefault(t[1], info) + of tyPtr, tyRef, tyProc, tyPointer, tyCstring: + result = newZero(t, info, nkNilLit) + of tyVar, tyLent: + let e = t.lastSon + if e.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}: + # skip the modifier, `var openArray` is a (ptr, len) pair too: + result = expandDefault(e, info) + else: + result = newZero(t.lastSon, info, nkNilLit) + of tySet: + result = newZero(t, info, nkCurly) + of tyObject: + result = newNodeIT(nkObjConstr, info, t) + result.add newNodeIT(nkType, info, t) + expandDefaultObj(t, info, result) + of tyTuple: + result = newZero(t, info, nkTupleConstr) + for it in t: + result.add expandDefault(it, info) + of tyVarargs, tyOpenArray, tySequence, tyUncheckedArray: + result = newZero(t, info, nkBracket) + of tyString: + result = newZero(t, info, nkStrLit) + of tyNone, tyEmpty, tyUntyped, tyTyped, tyTypeDesc, + tyNil, tyGenericInvocation, tyProxy, tyBuiltInTypeClass, + tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass, + tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward: + result = newZero(t, info, nkEmpty) # bug indicator diff --git a/compiler/ic/bitabs.nim b/compiler/ic/bitabs.nim index a55131051..edbaeb240 100644 --- a/compiler/ic/bitabs.nim +++ b/compiler/ic/bitabs.nim @@ -13,6 +13,8 @@ type vals: seq[T] # indexed by LitId keys: seq[LitId] # indexed by hash(val) +proc initBiTable*[T](): BiTable[T] = BiTable[T](vals: @[], keys: @[]) + proc nextTry(h, maxHash: Hash): Hash {.inline.} = result = (h + 1) and maxHash diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index bb8c6a5b9..63fe0369c 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -150,4 +150,13 @@ proc getMagicEqSymForType*(g: ModuleGraph; t: PType; info: TLineInfo): PSym = globalError(g.config, info, "can't find magic equals operator for type kind " & $t.kind) +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: + result = n + else: + result = newTree(nkHiddenAddr, n) + result.typ = makePtrType(n.typ, idgen) diff --git a/compiler/main.nim b/compiler/main.nim index 7118a253c..e5a5be56c 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -22,6 +22,8 @@ import modules, modulegraphs, lineinfos, pathutils, vmprofiler +# ensure NIR compiles: +import nir / nir when defined(nimPreviewSlimSystem): import std/[syncio, assertions] @@ -173,13 +175,14 @@ proc commandCompileToJS(graph: ModuleGraph) = if optGenScript in conf.globalOptions: writeDepsFile(graph) -proc commandInteractive(graph: ModuleGraph) = +proc commandInteractive(graph: ModuleGraph; useNir: bool) = graph.config.setErrorMaxHighMaybe initDefines(graph.config.symbols) - defineSymbol(graph.config.symbols, "nimscript") + if not useNir: + defineSymbol(graph.config.symbols, "nimscript") # note: seems redundant with -d:nimHasLibFFI when hasFFI: defineSymbol(graph.config.symbols, "nimffi") - setPipeLinePass(graph, InterpreterPass) + setPipeLinePass(graph, if useNir: NirReplPass else: InterpreterPass) compilePipelineSystemModule(graph) if graph.config.commandArgs.len > 0: discard graph.compilePipelineModule(fileInfoIdx(graph.config, graph.config.projectFull), {}) @@ -407,7 +410,7 @@ proc mainCommand*(graph: ModuleGraph) = wantMainModule(conf) commandView(graph) #msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!") - of cmdInteractive: commandInteractive(graph) + of cmdInteractive: commandInteractive(graph, isDefined(conf, "nir")) of cmdNimscript: if conf.projectIsCmd or conf.projectIsStdin: discard elif not fileExists(conf.projectFull): diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 4dadb45b6..f6abb0a60 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -65,6 +65,7 @@ type CgenPass EvalPass InterpreterPass + NirReplPass GenDependPass Docgen2TexPass Docgen2JsonPass @@ -99,6 +100,7 @@ type cache*: IdentCache vm*: RootRef # unfortunately the 'vm' state is shared project-wise, this will # be clarified in later compiler implementations. + repl*: RootRef # REPL state is shared project-wise. doStopCompile*: proc(): bool {.closure.} usageSym*: PSym # for nimsuggest owners*: seq[PSym] diff --git a/compiler/nim.nim b/compiler/nim.nim index d0aa888c4..023a76ff9 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -116,7 +116,8 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = conf.backend = backendC if conf.selectedGC == gcUnselected: - if conf.backend in {backendC, backendCpp, backendObjc}: + if conf.backend in {backendC, backendCpp, backendObjc} or + (conf.cmd == cmdInteractive and isDefined(conf, "nir")): initOrcDefines(conf) mainCommand(graph) diff --git a/compiler/nir/ast2ir.nim b/compiler/nir/ast2ir.nim new file mode 100644 index 000000000..f06bb3ae8 --- /dev/null +++ b/compiler/nir/ast2ir.nim @@ -0,0 +1,2189 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +import std / [assertions, tables, sets] +import ".." / [ast, astalgo, types, options, lineinfos, msgs, magicsys, + modulegraphs, guards, renderer, transf, bitsets, trees, nimsets, + expanddefaults] +from ".." / lowerings import lowerSwap, lowerTupleUnpacking +from ".." / pathutils import customPath +import .. / ic / bitabs + +import nirtypes, nirinsts, nirlineinfos, nirslots, types2ir + +type + ModuleCon* = ref object + strings*: BiTable[string] + integers*: BiTable[int64] + man*: LineInfoManager + types*: TypesCon + slotGenerator: ref int + module*: PSym + graph*: ModuleGraph + nativeIntId, nativeUIntId: TypeId + idgen: IdGenerator + pendingProcs: Table[ItemId, PSym] # procs we still need to generate code for + + ProcCon* = object + config: ConfigRef + lastFileKey: FileIndex + lastFileVal: LitId + labelGen: int + exitLabel: LabelId + code*: Tree + blocks: seq[(PSym, LabelId)] + sm: SlotManager + locGen: int + m: ModuleCon + prc: PSym + +proc initModuleCon*(graph: ModuleGraph; config: ConfigRef; idgen: IdGenerator; module: PSym): ModuleCon = + result = ModuleCon(graph: graph, types: initTypesCon(config), slotGenerator: new(int), + idgen: idgen, module: module) + case config.target.intSize + of 2: + result.nativeIntId = Int16Id + result.nativeUIntId = UInt16Id + of 4: + result.nativeIntId = Int32Id + result.nativeUIntId = UInt16Id + else: + result.nativeIntId = Int64Id + result.nativeUIntId = UInt16Id + +proc initProcCon*(m: ModuleCon; prc: PSym; config: ConfigRef): ProcCon = + ProcCon(m: m, sm: initSlotManager({}, m.slotGenerator), prc: prc, config: config) + +proc toLineInfo(c: var ProcCon; i: TLineInfo): PackedLineInfo = + var val: LitId + if c.lastFileKey == i.fileIndex: + val = c.lastFileVal + else: + val = c.m.strings.getOrIncl(toFullPath(c.config, i.fileIndex)) + # remember the entry: + c.lastFileKey = i.fileIndex + c.lastFileVal = val + result = pack(c.m.man, val, int32 i.line, int32 i.col) + +proc bestEffort(c: ProcCon): TLineInfo = + if c.prc != nil: + c.prc.info + else: + c.m.module.info + +proc popBlock(c: var ProcCon; oldLen: int) = + c.blocks.setLen(oldLen) + +template withBlock(labl: PSym; info: PackedLineInfo; asmLabl: LabelId; body: untyped) {.dirty.} = + var oldLen {.gensym.} = c.blocks.len + c.blocks.add (labl, asmLabl) + body + popBlock(c, oldLen) + +type + GenFlag = enum + gfAddrOf # load the address of the expression + gfToOutParam # the expression is passed to an `out` parameter + GenFlags = set[GenFlag] + +proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) + +proc genScope(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) = + openScope c.sm + gen c, n, d, flags + closeScope c.sm + +proc freeTemp(c: var ProcCon; tmp: Value) = + let s = extractTemp(tmp) + if s != SymId(-1): + freeTemp(c.sm, s) + +proc getTemp(c: var ProcCon; n: PNode): Value = + let info = toLineInfo(c, n.info) + let t = typeToIr(c.m.types, n.typ) + let tmp = allocTemp(c.sm, t) + c.code.addSummon info, tmp, t + result = localToValue(info, tmp) + +template withTemp(tmp, n, body: untyped) {.dirty.} = + var tmp = getTemp(c, n) + body + c.freeTemp(tmp) + +proc gen(c: var ProcCon; n: PNode; flags: GenFlags = {}) = + var tmp = default(Value) + gen(c, n, tmp, flags) + freeTemp c, tmp + +proc genScope(c: var ProcCon; n: PNode; flags: GenFlags = {}) = + openScope c.sm + gen c, n, flags + closeScope c.sm + +proc genx(c: var ProcCon; n: PNode; flags: GenFlags = {}): Value = + result = default(Value) + gen(c, n, result, flags) + assert Tree(result).len > 0, $n + +proc clearDest(c: var ProcCon; n: PNode; d: var Value) {.inline.} = + when false: + if n.typ.isNil or n.typ.kind == tyVoid: + let s = extractTemp(d) + if s != SymId(-1): + freeLoc(c.sm, s) + +proc isNotOpr(n: PNode): bool = + n.kind in nkCallKinds and n[0].kind == nkSym and n[0].sym.magic == mNot + +proc jmpBack(c: var ProcCon; n: PNode; lab: LabelId) = + c.code.gotoLabel toLineInfo(c, n.info), GotoLoop, lab + +type + JmpKind = enum opcFJmp, opcTJmp + +proc xjmp(c: var ProcCon; n: PNode; jk: JmpKind; v: Value): LabelId = + result = newLabel(c.labelGen) + let info = toLineInfo(c, n.info) + buildTyped c.code, info, Select, Bool8Id: + c.code.copyTree Tree(v) + build c.code, info, SelectPair: + build c.code, info, SelectValue: + c.code.boolVal(info, jk == opcTJmp) + c.code.gotoLabel info, Goto, result + +proc patch(c: var ProcCon; n: PNode; L: LabelId) = + addLabel c.code, toLineInfo(c, n.info), Label, L + +proc genWhile(c: var ProcCon; n: PNode) = + # lab1: + # cond, tmp + # fjmp tmp, lab2 + # body + # jmp lab1 + # lab2: + let info = toLineInfo(c, n.info) + let lab1 = c.code.addNewLabel(c.labelGen, info, LoopLabel) + withBlock(nil, info, lab1): + 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(n, 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(n, lab2) + +proc genBlock(c: var ProcCon; n: PNode; d: var Value) = + openScope c.sm + let info = toLineInfo(c, n.info) + let lab1 = newLabel(c.labelGen) + + withBlock(n[0].sym, info, lab1): + c.gen(n[1], d) + + c.code.addLabel(info, Label, lab1) + closeScope c.sm + c.clearDest(n, d) + +proc jumpTo(c: var ProcCon; n: PNode; L: LabelId) = + c.code.addLabel(toLineInfo(c, n.info), Goto, L) + +proc genBreak(c: var ProcCon; n: PNode) = + if n[0].kind == nkSym: + for i in countdown(c.blocks.len-1, 0): + if c.blocks[i][0] == n[0].sym: + c.jumpTo n, c.blocks[i][1] + return + localError(c.config, n.info, "NIR problem: cannot find 'break' target") + else: + c.jumpTo n, c.blocks[c.blocks.high][1] + +proc genIf(c: var ProcCon; n: PNode; d: var Value) = + # if (!expr1) goto lab1; + # thenPart + # goto LEnd + # lab1: + # if (!expr2) goto lab2; + # thenPart2 + # goto LEnd + # lab2: + # elsePart + # Lend: + if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n) + var ending = newLabel(c.labelGen) + for i in 0..<n.len: + var it = n[i] + if it.len == 2: + let info = toLineInfo(c, it[0].info) + withTemp(tmp, it[0]): + var elsePos: LabelId + if isNotOpr(it[0]): + c.gen(it[0][1], tmp) + elsePos = c.xjmp(it[0][1], opcTJmp, tmp) # if true + else: + c.gen(it[0], tmp) + elsePos = c.xjmp(it[0], opcFJmp, tmp) # if false + c.clearDest(n, d) + if isEmptyType(it[1].typ): # maybe noreturn call, don't touch `d` + c.genScope(it[1]) + else: + c.genScope(it[1], d) # then part + if i < n.len-1: + c.jumpTo it[1], ending + c.patch(it, elsePos) + else: + c.clearDest(n, d) + if isEmptyType(it[0].typ): # maybe noreturn call, don't touch `d` + c.genScope(it[0]) + else: + c.genScope(it[0], d) + c.patch(n, ending) + c.clearDest(n, d) + +proc tempToDest(c: var ProcCon; n: PNode; d: var Value; tmp: Value) = + if isEmpty(d): + d = tmp + else: + let info = toLineInfo(c, n.info) + build c.code, info, Asgn: + c.code.addTyped info, typeToIr(c.m.types, n.typ) + c.code.copyTree d + c.code.copyTree tmp + freeTemp(c, tmp) + +proc genAndOr(c: var ProcCon; n: PNode; opc: JmpKind; d: var Value) = + # asgn d, a + # tjmp|fjmp lab1 + # asgn d, b + # lab1: + var tmp = getTemp(c, n) + c.gen(n[1], tmp) + let lab1 = c.xjmp(n, opc, tmp) + c.gen(n[2], tmp) + c.patch(n, lab1) + tempToDest c, n, d, tmp + +proc unused(c: var ProcCon; n: PNode; x: Value) {.inline.} = + if hasValue(x): + #debug(n) + localError(c.config, n.info, "not unused") + +proc caseValue(c: var ProcCon; n: PNode) = + let info = toLineInfo(c, n.info) + build c.code, info, SelectValue: + let x = genx(c, n) + c.code.copyTree x + freeTemp(c, x) + +proc caseRange(c: var ProcCon; n: PNode) = + let info = toLineInfo(c, n.info) + build c.code, info, SelectRange: + let x = genx(c, n[0]) + let y = genx(c, n[1]) + c.code.copyTree x + c.code.copyTree y + freeTemp(c, y) + freeTemp(c, x) + +proc genCase(c: var ProcCon; n: PNode; d: var Value) = + if not isEmptyType(n.typ): + if isEmpty(d): d = getTemp(c, n) + else: + unused(c, n, d) + var sections = newSeqOfCap[LabelId](n.len-1) + let ending = newLabel(c.labelGen) + let info = toLineInfo(c, n.info) + withTemp(tmp, n[0]): + build c.code, info, Select: + c.code.addTyped info, typeToIr(c.m.types, n[0].typ) + c.gen(n[0], tmp) + for i in 1..<n.len: + let section = newLabel(c.labelGen) + sections.add section + let it = n[i] + let itinfo = toLineInfo(c, it.info) + build c.code, itinfo, SelectPair: + build c.code, itinfo, SelectList: + for j in 0..<it.len-1: + if it[j].kind == nkRange: + caseRange c, it[j] + else: + caseValue c, it[j] + c.code.addLabel itinfo, Goto, section + for i in 1..<n.len: + let it = n[i] + let itinfo = toLineInfo(c, it.info) + c.code.addLabel itinfo, Label, sections[i-1] + c.gen it.lastSon + if i != n.len-1: + c.code.addLabel itinfo, Goto, ending + c.code.addLabel info, Label, ending + +proc rawCall(c: var ProcCon; info: PackedLineInfo; opc: Opcode; t: TypeId; args: var openArray[Value]) = + build c.code, info, opc: + c.code.addTyped info, t + if opc in {CheckedCall, CheckedIndirectCall}: + c.code.addLabel info, CheckedGoto, c.exitLabel + for a in mitems(args): + c.code.copyTree a + freeTemp c, a + +proc canRaiseDisp(c: ProcCon; n: PNode): bool = + # we assume things like sysFatal cannot raise themselves + if n.kind == nkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}: + result = false + elif optPanics in c.config.globalOptions or + (n.kind == nkSym and sfSystemModule in getModule(n.sym).flags and + sfSystemRaisesDefect notin n.sym.flags): + # we know we can be strict: + result = canRaise(n) + else: + # we have to be *very* conservative: + result = canRaiseConservative(n) + +proc genCall(c: var ProcCon; n: PNode; d: var Value) = + let canRaise = canRaiseDisp(c, n[0]) + + let opc = if n[0].kind == nkSym and n[0].sym.kind in routineKinds: + (if canRaise: CheckedCall else: Call) + else: + (if canRaise: CheckedIndirectCall else: IndirectCall) + let info = toLineInfo(c, n.info) + + # In the IR we cannot nest calls. Thus we use two passes: + var args: seq[Value] = @[] + var t = n[0].typ + if t != nil: t = t.skipTypes(abstractInst) + args.add genx(c, n[0]) + for i in 1..<n.len: + if t != nil and i < t.len: + if isCompileTimeOnly(t[i]): discard + elif isOutParam(t[i]): args.add genx(c, n[i], {gfToOutParam}) + else: args.add genx(c, n[i]) + else: + args.add genx(c, n[i]) + + let tb = typeToIr(c.m.types, n.typ) + if not isEmptyType(n.typ): + if isEmpty(d): d = getTemp(c, n) + # XXX Handle problematic aliasing here: `a = f_canRaise(a)`. + build c.code, info, Asgn: + c.code.addTyped info, tb + c.code.copyTree d + rawCall c, info, opc, tb, args + else: + rawCall c, info, opc, tb, args + +proc genRaise(c: var ProcCon; n: PNode) = + let info = toLineInfo(c, n.info) + let tb = typeToIr(c.m.types, n[0].typ) + + let d = genx(c, n[0]) + build c.code, info, SetExc: + c.code.addTyped info, tb + c.code.copyTree d + c.freeTemp(d) + c.code.addLabel info, Goto, c.exitLabel + +proc genReturn(c: var ProcCon; n: PNode) = + if n[0].kind != nkEmpty: + gen(c, n[0]) + # XXX Block leave actions? + let info = toLineInfo(c, n.info) + c.code.addLabel info, Goto, c.exitLabel + +proc genTry(c: var ProcCon; n: PNode; d: var Value) = + if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n) + var endings: seq[LabelId] = @[] + let ehPos = newLabel(c.labelGen) + let oldExitLab = c.exitLabel + c.exitLabel = ehPos + if isEmptyType(n[0].typ): # maybe noreturn call, don't touch `d` + c.gen(n[0]) + else: + c.gen(n[0], d) + c.clearDest(n, d) + + # Add a jump past the exception handling code + let jumpToFinally = newLabel(c.labelGen) + c.jumpTo n, jumpToFinally + # This signals where the body ends and where the exception handling begins + c.patch(n, ehPos) + c.exitLabel = oldExitLab + for i in 1..<n.len: + let it = n[i] + if it.kind != nkFinally: + # first opcExcept contains the end label of the 'except' block: + let endExcept = newLabel(c.labelGen) + for j in 0..<it.len - 1: + assert(it[j].kind == nkType) + let typ = it[j].typ.skipTypes(abstractPtrs-{tyTypeDesc}) + let itinfo = toLineInfo(c, it[j].info) + build c.code, itinfo, TestExc: + c.code.addTyped itinfo, typeToIr(c.m.types, typ) + if it.len == 1: + let itinfo = toLineInfo(c, it.info) + build c.code, itinfo, TestExc: + c.code.addTyped itinfo, VoidId + let body = it.lastSon + if isEmptyType(body.typ): # maybe noreturn call, don't touch `d` + c.gen(body) + else: + c.gen(body, d) + c.clearDest(n, d) + if i < n.len: + endings.add newLabel(c.labelGen) + c.patch(it, endExcept) + let fin = lastSon(n) + # we always generate an 'opcFinally' as that pops the safepoint + # from the stack if no exception is raised in the body. + c.patch(fin, jumpToFinally) + #c.gABx(fin, opcFinally, 0, 0) + for endPos in endings: c.patch(n, endPos) + if fin.kind == nkFinally: + c.gen(fin[0]) + c.clearDest(n, d) + #c.gABx(fin, opcFinallyEnd, 0, 0) + +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) + +proc genField(c: var ProcCon; n: PNode; d: var Value) = + var pos: int + if n.kind != nkSym or n.sym.kind != skField: + localError(c.config, n.info, "no field symbol") + pos = 0 + else: + pos = n.sym.position + d.addImmediateVal toLineInfo(c, n.info), pos + +proc genIndex(c: var ProcCon; n: PNode; arr: PType; d: var Value) = + if arr.skipTypes(abstractInst).kind == tyArray and + (let x = firstOrd(c.config, arr); x != Zero): + let info = toLineInfo(c, n.info) + buildTyped d, info, Sub, c.m.nativeIntId: + c.gen(n, d) + d.addImmediateVal toLineInfo(c, n.info), toInt(x) + else: + c.gen(n, d) + +proc genNew(c: var ProcCon; n: PNode; needsInit: bool) = + # If in doubt, always follow the blueprint of the C code generator for `mm:orc`. + let refType = n[1].typ.skipTypes(abstractInstOwned) + assert refType.kind == tyRef + let baseType = refType.lastSon + + let info = toLineInfo(c, n.info) + let codegenProc = magicsys.getCompilerProc(c.m.graph, + if needsInit: "nimNewObj" else: "nimNewObjUninit") + let x = genx(c, n[1]) + let refTypeIr = typeToIr(c.m.types, refType) + buildTyped c.code, info, Asgn, refTypeIr: + copyTree c.code, x + buildTyped c.code, info, Cast, refTypeIr: + buildTyped c.code, info, Call, VoidPtrId: + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + c.code.addImmediateVal info, int(getSize(c.config, baseType)) + c.code.addImmediateVal info, int(getAlign(c.config, baseType)) + +proc genNewSeqOfCap(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + let seqtype = skipTypes(n.typ, abstractVarRange) + let baseType = seqtype.lastSon + var a = c.genx(n[1]) + if isEmpty(d): d = getTemp(c, n) + # $1.len = 0 + buildTyped c.code, info, Asgn, c.m.nativeIntId: + buildTyped c.code, info, FieldAt, c.m.nativeIntId: + copyTree c.code, d + c.code.addImmediateVal info, 0 + c.code.addImmediateVal info, 0 + # $1.p = ($4*) #newSeqPayloadUninit($2, sizeof($3), NIM_ALIGNOF($3)) + let payloadPtr = seqPayloadPtrType(c.m.types, seqtype) + buildTyped c.code, info, Asgn, payloadPtr: + # $1.p + buildTyped c.code, info, FieldAt, payloadPtr: + copyTree c.code, d + c.code.addImmediateVal info, 1 + # ($4*) #newSeqPayloadUninit($2, sizeof($3), NIM_ALIGNOF($3)) + buildTyped c.code, info, Cast, payloadPtr: + buildTyped c.code, info, Call, VoidPtrId: + let codegenProc = magicsys.getCompilerProc(c.m.graph, "newSeqPayloadUninit") + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + copyTree c.code, a + c.code.addImmediateVal info, int(getSize(c.config, baseType)) + c.code.addImmediateVal info, int(getAlign(c.config, baseType)) + freeTemp c, a + +proc genNewSeq(c: var ProcCon; n: PNode) = + let info = toLineInfo(c, n.info) + let seqtype = skipTypes(n[1].typ, abstractVarRange) + let baseType = seqtype.lastSon + var d = c.genx(n[1]) + var b = c.genx(n[2]) + + # $1.len = $2 + buildTyped c.code, info, Asgn, c.m.nativeIntId: + buildTyped c.code, info, FieldAt, c.m.nativeIntId: + copyTree c.code, d + c.code.addImmediateVal info, 0 + copyTree c.code, b + c.code.addImmediateVal info, 0 + + # $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3)) + let payloadPtr = seqPayloadPtrType(c.m.types, seqtype) + buildTyped c.code, info, Asgn, payloadPtr: + # $1.p + buildTyped c.code, info, FieldAt, payloadPtr: + copyTree c.code, d + c.code.addImmediateVal info, 1 + # ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3)) + buildTyped c.code, info, Cast, payloadPtr: + buildTyped c.code, info, Call, VoidPtrId: + let codegenProc = magicsys.getCompilerProc(c.m.graph, "newSeqPayload") + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + copyTree c.code, b + c.code.addImmediateVal info, int(getSize(c.config, baseType)) + c.code.addImmediateVal info, int(getAlign(c.config, baseType)) + freeTemp c, b + freeTemp c, d + +template intoDest*(d: var Value; info: PackedLineInfo; typ: TypeId; body: untyped) = + if typ == VoidId: + body(c.code) + elif isEmpty(d): + body(Tree(d)) + else: + buildTyped c.code, info, Asgn, typ: + copyTree c.code, d + body(c.code) + +proc genBinaryOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) = + let info = toLineInfo(c, n.info) + let tmp = c.genx(n[1]) + let tmp2 = c.genx(n[2]) + let t = typeToIr(c.m.types, n.typ) + template body(target) = + buildTyped target, info, opc, t: + copyTree target, tmp + copyTree target, tmp2 + intoDest d, info, t, body + c.freeTemp(tmp) + c.freeTemp(tmp2) + +proc genCmpOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) = + let info = toLineInfo(c, n.info) + let tmp = c.genx(n[1]) + let tmp2 = c.genx(n[2]) + let t = typeToIr(c.m.types, n[1].typ) + template body(target) = + buildTyped target, info, opc, t: + copyTree target, tmp + copyTree target, tmp2 + intoDest d, info, Bool8Id, body + c.freeTemp(tmp) + c.freeTemp(tmp2) + +proc genUnaryOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) = + let info = toLineInfo(c, n.info) + let tmp = c.genx(n[1]) + let t = typeToIr(c.m.types, n.typ) + template body(target) = + buildTyped target, info, opc, t: + copyTree target, tmp + intoDest d, info, t, body + c.freeTemp(tmp) + +proc genIncDec(c: var ProcCon; n: PNode; opc: Opcode) = + let info = toLineInfo(c, n.info) + let t = typeToIr(c.m.types, skipTypes(n[1].typ, abstractVar)) + + let d = c.genx(n[1]) + let tmp = c.genx(n[2]) + # we produce code like: i = i + 1 + buildTyped c.code, info, Asgn, t: + copyTree c.code, d + buildTyped c.code, info, opc, t: + copyTree c.code, d + copyTree c.code, tmp + c.freeTemp(tmp) + #c.genNarrow(n[1], d) + c.freeTemp(d) + +proc genArrayLen(c: var ProcCon; n: PNode; d: var Value) = + #echo c.m.graph.config $ n.info, " ", n + let info = toLineInfo(c, n.info) + var a = n[1] + #if a.kind == nkHiddenAddr: a = a[0] + var typ = skipTypes(a.typ, abstractVar + tyUserTypeClasses) + case typ.kind + of tyOpenArray, tyVarargs: + let xa = c.genx(a) + template body(target) = + buildTyped target, info, FieldAt, c.m.nativeIntId: + copyTree target, xa + target.addImmediateVal info, 1 # (p, len)-pair so len is at index 1 + intoDest d, info, c.m.nativeIntId, body + + of tyCstring: + let xa = c.genx(a) + if isEmpty(d): d = getTemp(c, n) + buildTyped c.code, info, Call, c.m.nativeIntId: + let codegenProc = magicsys.getCompilerProc(c.m.graph, "nimCStrLen") + assert codegenProc != nil + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + copyTree c.code, xa + + of tyString, tySequence: + let xa = c.genx(a) + + if typ.kind == tySequence: + # we go through a temporary here because people write bullshit code. + if isEmpty(d): d = getTemp(c, n) + + template body(target) = + buildTyped target, info, FieldAt, c.m.nativeIntId: + copyTree target, xa + target.addImmediateVal info, 0 # (len, p)-pair so len is at index 0 + intoDest d, info, c.m.nativeIntId, body + + of tyArray: + template body(target) = + target.addIntVal(c.m.integers, info, c.m.nativeIntId, toInt lengthOrd(c.config, typ)) + intoDest d, info, c.m.nativeIntId, body + else: internalError(c.config, n.info, "genArrayLen()") + +proc genUnaryMinus(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + let tmp = c.genx(n[1]) + let t = typeToIr(c.m.types, n.typ) + template body(target) = + buildTyped target, info, Sub, t: + # Little hack: This works because we know that `0.0` is all 0 bits: + target.addIntVal(c.m.integers, info, t, 0) + copyTree target, tmp + intoDest d, info, t, body + c.freeTemp(tmp) + +proc genHigh(c: var ProcCon; n: PNode; d: var Value) = + let subOpr = createMagic(c.m.graph, c.m.idgen, "-", mSubI) + let lenOpr = createMagic(c.m.graph, c.m.idgen, "len", mLengthOpenArray) + let asLenExpr = subOpr.buildCall(lenOpr.buildCall(n[1]), nkIntLit.newIntNode(1)) + c.gen asLenExpr, d + +proc genBinaryCp(c: var ProcCon; n: PNode; d: var Value; compilerProc: string) = + let info = toLineInfo(c, n.info) + let xa = c.genx(n[1]) + let xb = c.genx(n[2]) + if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n) + + let t = typeToIr(c.m.types, n.typ) + template body(target) = + buildTyped target, info, Call, t: + let codegenProc = magicsys.getCompilerProc(c.m.graph, compilerProc) + #assert codegenProc != nil, $n & " " & (c.m.graph.config $ n.info) + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree target, theProc + copyTree target, xa + copyTree target, xb + + intoDest d, info, t, body + c.freeTemp xb + c.freeTemp xa + +proc genUnaryCp(c: var ProcCon; n: PNode; d: var Value; compilerProc: string; argAt = 1) = + let info = toLineInfo(c, n.info) + let xa = c.genx(n[argAt]) + if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n) + + let t = typeToIr(c.m.types, n.typ) + template body(target) = + buildTyped target, info, Call, t: + let codegenProc = magicsys.getCompilerProc(c.m.graph, compilerProc) + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree target, theProc + copyTree target, xa + + intoDest d, info, t, body + c.freeTemp xa + +proc genEnumToStr(c: var ProcCon; n: PNode; d: var Value) = + let t = n[1].typ.skipTypes(abstractInst+{tyRange}) + let toStrProc = getToStringProc(c.m.graph, t) + # XXX need to modify this logic for IC. + var nb = copyTree(n) + nb[0] = newSymNode(toStrProc) + gen(c, nb, d) + +proc genOf(c: var ProcCon; n: PNode; d: var Value) = + genUnaryOp c, n, d, TestOf + +template sizeOfLikeMsg(name): string = + "'" & name & "' requires '.importc' types to be '.completeStruct'" + +proc genIsNil(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + let tmp = c.genx(n[1]) + let t = typeToIr(c.m.types, n[1].typ) + template body(target) = + buildTyped target, info, Eq, t: + copyTree target, tmp + addNilVal target, info, t + intoDest d, info, Bool8Id, body + c.freeTemp(tmp) + +proc fewCmps(conf: ConfigRef; s: PNode): 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: + result = false + elif (getSize(conf, s.typ) <= conf.target.intSize) and (nfAllConst in s.flags): + result = false # it is better to emit the set generation code + elif elemType(s.typ).kind in {tyInt, tyInt16..tyInt64}: + result = true # better not emit the set if int is basetype! + else: + result = s.len <= 8 # 8 seems to be a good value + +proc genInBitset(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + let a = c.genx(n[1]) + let b = c.genx(n[2]) + + let t = bitsetBasetype(c.m.types, n[1].typ) + let setType = typeToIr(c.m.types, n[1].typ) + let mask = + case t + of UInt8Id: 7 + of UInt16Id: 15 + of UInt32Id: 31 + else: 63 + let expansion = if t == UInt64Id: UInt64Id else: c.m.nativeUIntId + # "(($1 &(1U<<((NU)($2)&7U)))!=0)" - or - + # "(($1[(NU)($2)>>3] &(1U<<((NU)($2)&7U)))!=0)" + + template body(target) = + buildTyped target, info, BoolNot, Bool8Id: + buildTyped target, info, Eq, t: + buildTyped target, info, BitAnd, t: + if c.m.types.g[setType].kind != ArrayTy: + copyTree target, a + else: + buildTyped target, info, ArrayAt, t: + copyTree target, a + buildTyped target, info, BitShr, t: + buildTyped target, info, Cast, expansion: + copyTree target, b + addIntVal target, c.m.integers, info, expansion, 3 + + buildTyped target, info, BitShl, t: + addIntVal target, c.m.integers, info, t, 1 + buildTyped target, info, BitAnd, t: + buildTyped target, info, Cast, expansion: + copyTree target, b + addIntVal target, c.m.integers, info, expansion, mask + addIntVal target, c.m.integers, info, t, 0 + intoDest d, info, t, body + + c.freeTemp(b) + c.freeTemp(a) + +proc genInSet(c: var ProcCon; n: PNode; d: var Value) = + let g {.cursor.} = c.m.graph + if n[1].kind == nkCurly and fewCmps(g.config, n[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 elem = if n[2].kind in {nkChckRange, nkChckRange64}: n[2][0] + else: n[2] + let curly = n[1] + var ex: PNode = nil + for it in curly: + var test: PNode + if it.kind == nkRange: + test = newTree(nkCall, g.operators.opAnd.newSymNode, + newTree(nkCall, g.operators.opLe.newSymNode, it[0], elem), # a <= elem + newTree(nkCall, g.operators.opLe.newSymNode, elem, it[1]) + ) + else: + test = newTree(nkCall, g.operators.opEq.newSymNode, elem, it) + test.typ = getSysType(g, it.info, tyBool) + + if ex == nil: ex = test + else: ex = newTree(nkCall, g.operators.opOr.newSymNode, ex, test) + + if ex == nil: + let info = toLineInfo(c, n.info) + template body(target) = + boolVal target, info, false + intoDest d, info, Bool8Id, body + else: + gen c, ex, d + else: + genInBitset c, n, d + +proc genCard(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + let a = c.genx(n[1]) + let t = typeToIr(c.m.types, n.typ) + + let setType = typeToIr(c.m.types, n[1].typ) + if isEmpty(d): d = getTemp(c, n) + + buildTyped c.code, info, Asgn, t: + copyTree c.code, d + buildTyped c.code, info, Call, t: + if c.m.types.g[setType].kind == ArrayTy: + let codegenProc = magicsys.getCompilerProc(c.m.graph, "cardSet") + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.types.g, setType): + copyTree c.code, a + c.code.addImmediateVal info, int(getSize(c.config, n[1].typ)) + elif t == UInt64Id: + let codegenProc = magicsys.getCompilerProc(c.m.graph, "countBits64") + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + copyTree c.code, a + else: + let codegenProc = magicsys.getCompilerProc(c.m.graph, "countBits32") + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + buildTyped c.code, info, Cast, UInt32Id: + copyTree c.code, a + freeTemp c, a + +proc genEqSet(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + let a = c.genx(n[1]) + let b = c.genx(n[2]) + let t = typeToIr(c.m.types, n.typ) + + let setType = typeToIr(c.m.types, n[1].typ) + + if c.m.types.g[setType].kind == ArrayTy: + if isEmpty(d): d = getTemp(c, n) + + buildTyped c.code, info, Asgn, t: + copyTree c.code, d + buildTyped c.code, info, Eq, t: + buildTyped c.code, info, Call, t: + let codegenProc = magicsys.getCompilerProc(c.m.graph, "nimCmpMem") + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.types.g, setType): + copyTree c.code, a + buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.types.g, setType): + copyTree c.code, b + c.code.addImmediateVal info, int(getSize(c.config, n[1].typ)) + c.code.addIntVal c.m.integers, info, c.m.nativeIntId, 0 + + else: + template body(target) = + buildTyped target, info, Eq, setType: + copyTree target, a + copyTree target, b + intoDest d, info, Bool8Id, body + + freeTemp c, b + freeTemp c, a + +proc beginCountLoop(c: var ProcCon; info: PackedLineInfo; first, last: int): (SymId, LabelId, LabelId) = + let tmp = allocTemp(c.sm, c.m.nativeIntId) + c.code.addSummon info, tmp, c.m.nativeIntId + buildTyped c.code, info, Asgn, c.m.nativeIntId: + c.code.addSymUse info, tmp + c.code.addIntVal c.m.integers, info, c.m.nativeIntId, first + let lab1 = c.code.addNewLabel(c.labelGen, info, LoopLabel) + result = (tmp, lab1, newLabel(c.labelGen)) + + buildTyped c.code, info, Select, Bool8Id: + buildTyped c.code, info, Lt, c.m.nativeIntId: + c.code.addSymUse info, tmp + c.code.addIntVal c.m.integers, info, c.m.nativeIntId, last + build c.code, info, SelectPair: + build c.code, info, SelectValue: + c.code.boolVal(info, false) + c.code.gotoLabel info, Goto, result[2] + +proc beginCountLoop(c: var ProcCon; info: PackedLineInfo; first, last: Value): (SymId, LabelId, LabelId) = + let tmp = allocTemp(c.sm, c.m.nativeIntId) + c.code.addSummon info, tmp, c.m.nativeIntId + buildTyped c.code, info, Asgn, c.m.nativeIntId: + c.code.addSymUse info, tmp + copyTree c.code, first + let lab1 = c.code.addNewLabel(c.labelGen, info, LoopLabel) + result = (tmp, lab1, newLabel(c.labelGen)) + + buildTyped c.code, info, Select, Bool8Id: + buildTyped c.code, info, Le, c.m.nativeIntId: + c.code.addSymUse info, tmp + copyTree c.code, last + build c.code, info, SelectPair: + build c.code, info, SelectValue: + c.code.boolVal(info, false) + c.code.gotoLabel info, Goto, result[2] + +proc endLoop(c: var ProcCon; info: PackedLineInfo; s: SymId; back, exit: LabelId) = + buildTyped c.code, info, Asgn, c.m.nativeIntId: + c.code.addSymUse info, s + buildTyped c.code, info, Add, c.m.nativeIntId: + c.code.addSymUse info, s + c.code.addIntVal c.m.integers, info, c.m.nativeIntId, 1 + c.code.addLabel info, GotoLoop, back + c.code.addLabel info, Label, exit + freeTemp(c.sm, s) + +proc genLeSet(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + let a = c.genx(n[1]) + let b = c.genx(n[2]) + let t = typeToIr(c.m.types, n.typ) + + let setType = typeToIr(c.m.types, n[1].typ) + + if c.m.types.g[setType].kind == ArrayTy: + let elemType = bitsetBasetype(c.m.types, n[1].typ) + if isEmpty(d): d = getTemp(c, n) + # "for ($1 = 0; $1 < $2; $1++):" + # " $3 = (($4[$1] & ~ $5[$1]) == 0)" + # " if (!$3) break;" + let (idx, backLabel, endLabel) = beginCountLoop(c, info, 0, int(getSize(c.config, n[1].typ))) + buildTyped c.code, info, Asgn, Bool8Id: + copyTree c.code, d + buildTyped c.code, info, Eq, elemType: + buildTyped c.code, info, BitAnd, elemType: + buildTyped c.code, info, ArrayAt, elemType: + copyTree c.code, a + c.code.addSymUse info, idx + buildTyped c.code, info, BitNot, elemType: + buildTyped c.code, info, ArrayAt, elemType: + copyTree c.code, b + c.code.addSymUse info, idx + c.code.addIntVal c.m.integers, info, elemType, 0 + + # if !$3: break + buildTyped c.code, info, Select, Bool8Id: + c.code.copyTree d + build c.code, info, SelectPair: + build c.code, info, SelectValue: + c.code.boolVal(info, false) + c.code.gotoLabel info, Goto, endLabel + + endLoop(c, info, idx, backLabel, endLabel) + else: + # "(($1 & ~ $2)==0)" + template body(target) = + buildTyped target, info, Eq, setType: + buildTyped target, info, BitAnd, setType: + copyTree target, a + buildTyped target, info, BitNot, setType: + copyTree target, b + target.addIntVal c.m.integers, info, setType, 0 + + intoDest d, info, Bool8Id, body + + freeTemp c, b + freeTemp c, a + +proc genLtSet(c: var ProcCon; n: PNode; d: var Value) = + localError(c.m.graph.config, n.info, "`<` for sets not implemented") + +proc genBinarySet(c: var ProcCon; n: PNode; d: var Value; m: TMagic) = + let info = toLineInfo(c, n.info) + let a = c.genx(n[1]) + let b = c.genx(n[2]) + let t = typeToIr(c.m.types, n.typ) + + let setType = typeToIr(c.m.types, n[1].typ) + + if c.m.types.g[setType].kind == ArrayTy: + let elemType = bitsetBasetype(c.m.types, n[1].typ) + if isEmpty(d): d = getTemp(c, n) + # "for ($1 = 0; $1 < $2; $1++):" + # " $3 = (($4[$1] & ~ $5[$1]) == 0)" + # " if (!$3) break;" + let (idx, backLabel, endLabel) = beginCountLoop(c, info, 0, int(getSize(c.config, n[1].typ))) + buildTyped c.code, info, Asgn, elemType: + buildTyped c.code, info, ArrayAt, elemType: + copyTree c.code, d + c.code.addSymUse info, idx + buildTyped c.code, info, (if m == mPlusSet: BitOr else: BitAnd), elemType: + buildTyped c.code, info, ArrayAt, elemType: + copyTree c.code, a + c.code.addSymUse info, idx + if m == mMinusSet: + buildTyped c.code, info, BitNot, elemType: + buildTyped c.code, info, ArrayAt, elemType: + copyTree c.code, b + c.code.addSymUse info, idx + else: + buildTyped c.code, info, ArrayAt, elemType: + copyTree c.code, b + c.code.addSymUse info, idx + + endLoop(c, info, idx, backLabel, endLabel) + else: + # "(($1 & ~ $2)==0)" + template body(target) = + buildTyped target, info, (if m == mPlusSet: BitOr else: BitAnd), setType: + copyTree target, a + if m == mMinusSet: + buildTyped target, info, BitNot, setType: + copyTree target, b + else: + copyTree target, b + + intoDest d, info, setType, body + + freeTemp c, b + freeTemp c, a + +proc genInclExcl(c: var ProcCon; n: PNode; m: TMagic) = + let info = toLineInfo(c, n.info) + let a = c.genx(n[1]) + let b = c.genx(n[2]) + + let setType = typeToIr(c.m.types, n[1].typ) + + let t = bitsetBasetype(c.m.types, n[1].typ) + let mask = + case t + of UInt8Id: 7 + of UInt16Id: 15 + of UInt32Id: 31 + else: 63 + + buildTyped c.code, info, Asgn, setType: + if c.m.types.g[setType].kind == ArrayTy: + if m == mIncl: + # $1[(NU)($2)>>3] |=(1U<<($2&7U)) + buildTyped c.code, info, ArrayAt, t: + copyTree c.code, a + buildTyped c.code, info, BitShr, t: + buildTyped c.code, info, Cast, c.m.nativeUIntId: + copyTree c.code, b + addIntVal c.code, c.m.integers, info, c.m.nativeUIntId, 3 + buildTyped c.code, info, BitOr, t: + buildTyped c.code, info, ArrayAt, t: + copyTree c.code, a + buildTyped c.code, info, BitShr, t: + buildTyped c.code, info, Cast, c.m.nativeUIntId: + copyTree c.code, b + addIntVal c.code, c.m.integers, info, c.m.nativeUIntId, 3 + buildTyped c.code, info, BitShl, t: + c.code.addIntVal c.m.integers, info, t, 1 + buildTyped c.code, info, BitAnd, t: + copyTree c.code, b + c.code.addIntVal c.m.integers, info, t, 7 + else: + # $1[(NU)($2)>>3] &= ~(1U<<($2&7U)) + buildTyped c.code, info, ArrayAt, t: + copyTree c.code, a + buildTyped c.code, info, BitShr, t: + buildTyped c.code, info, Cast, c.m.nativeUIntId: + copyTree c.code, b + addIntVal c.code, c.m.integers, info, c.m.nativeUIntId, 3 + buildTyped c.code, info, BitAnd, t: + buildTyped c.code, info, ArrayAt, t: + copyTree c.code, a + buildTyped c.code, info, BitShr, t: + buildTyped c.code, info, Cast, c.m.nativeUIntId: + copyTree c.code, b + addIntVal c.code, c.m.integers, info, c.m.nativeUIntId, 3 + buildTyped c.code, info, BitNot, t: + buildTyped c.code, info, BitShl, t: + c.code.addIntVal c.m.integers, info, t, 1 + buildTyped c.code, info, BitAnd, t: + copyTree c.code, b + c.code.addIntVal c.m.integers, info, t, 7 + + else: + copyTree c.code, a + if m == mIncl: + # $1 |= ((NU8)1)<<(($2) & 7) + buildTyped c.code, info, BitOr, setType: + copyTree c.code, a + buildTyped c.code, info, BitShl, t: + c.code.addIntVal c.m.integers, info, t, 1 + buildTyped c.code, info, BitAnd, t: + copyTree c.code, b + c.code.addIntVal c.m.integers, info, t, mask + else: + # $1 &= ~(((NU8)1) << (($2) & 7)) + buildTyped c.code, info, BitAnd, setType: + copyTree c.code, a + buildTyped c.code, info, BitNot, t: + buildTyped c.code, info, BitShl, t: + c.code.addIntVal c.m.integers, info, t, 1 + buildTyped c.code, info, BitAnd, t: + copyTree c.code, b + c.code.addIntVal c.m.integers, info, t, mask + freeTemp c, b + freeTemp c, a + +proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) = + # 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); + # incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g); + let info = toLineInfo(c, n.info) + let setType = typeToIr(c.m.types, n.typ) + let size = int(getSize(c.config, n.typ)) + let t = bitsetBasetype(c.m.types, n.typ) + let mask = + case t + of UInt8Id: 7 + of UInt16Id: 15 + of UInt32Id: 31 + else: 63 + + if isEmpty(d): d = getTemp(c, n) + if c.m.types.g[setType].kind != ArrayTy: + buildTyped c.code, info, Asgn, setType: + copyTree c.code, d + c.code.addIntVal c.m.integers, info, t, 0 + + for it in n: + if it.kind == nkRange: + let a = genx(c, it[0]) + let b = genx(c, it[1]) + let (idx, backLabel, endLabel) = beginCountLoop(c, info, a, b) + buildTyped c.code, info, Asgn, setType: + copyTree c.code, d + buildTyped c.code, info, BitAnd, setType: + copyTree c.code, d + buildTyped c.code, info, BitNot, t: + buildTyped c.code, info, BitShl, t: + c.code.addIntVal c.m.integers, info, t, 1 + buildTyped c.code, info, BitAnd, t: + c.code.addSymUse info, idx + c.code.addIntVal c.m.integers, info, t, mask + + endLoop(c, info, idx, backLabel, endLabel) + freeTemp c, b + freeTemp c, a + + else: + let a = genx(c, it) + buildTyped c.code, info, Asgn, setType: + copyTree c.code, d + buildTyped c.code, info, BitAnd, setType: + copyTree c.code, d + buildTyped c.code, info, BitNot, t: + buildTyped c.code, info, BitShl, t: + c.code.addIntVal c.m.integers, info, t, 1 + buildTyped c.code, info, BitAnd, t: + copyTree c.code, a + c.code.addIntVal c.m.integers, info, t, mask + freeTemp c, a + + else: + # init loop: + let (idx, backLabel, endLabel) = beginCountLoop(c, info, 0, size) + buildTyped c.code, info, Asgn, t: + copyTree c.code, d + c.code.addIntVal c.m.integers, info, t, 0 + endLoop(c, info, idx, backLabel, endLabel) + + # incl elements: + for it in n: + if it.kind == nkRange: + let a = genx(c, it[0]) + let b = genx(c, it[1]) + let (idx, backLabel, endLabel) = beginCountLoop(c, info, a, b) + + buildTyped c.code, info, Asgn, t: + buildTyped c.code, info, ArrayAt, t: + copyTree c.code, d + buildTyped c.code, info, BitShr, t: + buildTyped c.code, info, Cast, c.m.nativeUIntId: + c.code.addSymUse info, idx + addIntVal c.code, c.m.integers, info, c.m.nativeUIntId, 3 + buildTyped c.code, info, BitOr, t: + buildTyped c.code, info, ArrayAt, t: + copyTree c.code, d + buildTyped c.code, info, BitShr, t: + buildTyped c.code, info, Cast, c.m.nativeUIntId: + c.code.addSymUse info, idx + addIntVal c.code, c.m.integers, info, c.m.nativeUIntId, 3 + buildTyped c.code, info, BitShl, t: + c.code.addIntVal c.m.integers, info, t, 1 + buildTyped c.code, info, BitAnd, t: + c.code.addSymUse info, idx + c.code.addIntVal c.m.integers, info, t, 7 + + endLoop(c, info, idx, backLabel, endLabel) + freeTemp c, b + freeTemp c, a + + else: + let a = genx(c, it) + # $1[(NU)($2)>>3] |=(1U<<($2&7U)) + buildTyped c.code, info, Asgn, t: + buildTyped c.code, info, ArrayAt, t: + copyTree c.code, d + buildTyped c.code, info, BitShr, t: + buildTyped c.code, info, Cast, c.m.nativeUIntId: + copyTree c.code, a + addIntVal c.code, c.m.integers, info, c.m.nativeUIntId, 3 + buildTyped c.code, info, BitOr, t: + buildTyped c.code, info, ArrayAt, t: + copyTree c.code, d + buildTyped c.code, info, BitShr, t: + buildTyped c.code, info, Cast, c.m.nativeUIntId: + copyTree c.code, a + addIntVal c.code, c.m.integers, info, c.m.nativeUIntId, 3 + buildTyped c.code, info, BitShl, t: + c.code.addIntVal c.m.integers, info, t, 1 + buildTyped c.code, info, BitAnd, t: + copyTree c.code, a + c.code.addIntVal c.m.integers, info, t, 7 + freeTemp c, a + +proc genSetConstr(c: var ProcCon; n: PNode; d: var Value) = + if isDeepConstExpr(n): + let info = toLineInfo(c, n.info) + let setType = typeToIr(c.m.types, n.typ) + let size = int(getSize(c.config, n.typ)) + let cs = toBitSet(c.config, n) + + if c.m.types.g[setType].kind != ArrayTy: + template body(target) = + target.addIntVal c.m.integers, info, setType, cast[BiggestInt](bitSetToWord(cs, size)) + intoDest d, info, setType, body + else: + let t = bitsetBasetype(c.m.types, n.typ) + template body(target) = + buildTyped target, info, ArrayConstr, setType: + for i in 0..high(cs): + target.addIntVal c.m.integers, info, t, int64 cs[i] + intoDest d, info, setType, body + else: + genSetConstrDyn c, n, d + +proc genStrConcat(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + # <Nim code> + # s = "Hello " & name & ", how do you feel?" & 'z' + # + # <generated code> + # { + # string tmp0; + # ... + # tmp0 = rawNewString(6 + 17 + 1 + s2->len); + # // we cannot generate s = rawNewString(...) here, because + # // ``s`` may be used on the right side of the expression + # appendString(tmp0, strlit_1); + # appendString(tmp0, name); + # appendString(tmp0, strlit_2); + # appendChar(tmp0, 'z'); + # asgn(s, tmp0); + # } + var args: seq[Value] = @[] + var precomputedLen = 0 + for i in 1 ..< n.len: + let it = n[i] + if skipTypes(it.typ, abstractVarRange).kind == tyChar: + inc precomputedLen + elif it.kind in {nkStrLit..nkTripleStrLit}: + inc precomputedLen, it.strVal.len + args.add genx(c, it) + + # generate length computation: + var tmpLen = allocTemp(c.sm, c.m.nativeIntId) + buildTyped c.code, info, Asgn, c.m.nativeIntId: + c.code.addSymUse info, tmpLen + c.code.addIntVal c.m.integers, info, c.m.nativeIntId, precomputedLen + for a in mitems(args): + buildTyped c.code, info, Asgn, c.m.nativeIntId: + c.code.addSymUse info, tmpLen + buildTyped c.code, info, CheckedAdd, c.m.nativeIntId: + c.code.addSymUse info, tmpLen + buildTyped c.code, info, FieldAt, c.m.nativeIntId: + copyTree c.code, a + c.code.addImmediateVal info, 0 # (len, p)-pair so len is at index 0 + + var tmpStr = getTemp(c, n) + # ^ because of aliasing, we always go through a temporary + let t = typeToIr(c.m.types, n.typ) + buildTyped c.code, info, Asgn, t: + copyTree c.code, tmpStr + buildTyped c.code, info, Call, t: + let codegenProc = magicsys.getCompilerProc(c.m.graph, "rawNewString") + #assert codegenProc != nil, $n & " " & (c.m.graph.config $ n.info) + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + c.code.addSymUse info, tmpLen + freeTemp c.sm, tmpLen + + for i in 1 ..< n.len: + let it = n[i] + let isChar = skipTypes(it.typ, abstractVarRange).kind == tyChar + buildTyped c.code, info, Call, VoidId: + let codegenProc = magicsys.getCompilerProc(c.m.graph, + (if isChar: "appendChar" else: "appendString")) + #assert codegenProc != nil, $n & " " & (c.m.graph.config $ n.info) + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.types.g, t): + copyTree c.code, tmpStr + copyTree c.code, args[i-1] + freeTemp c, args[i-1] + + if isEmpty(d): + d = tmpStr + else: + # XXX Test that this does not cause memory leaks! + buildTyped c.code, info, Asgn, t: + copyTree c.code, d + copyTree c.code, tmpStr + +proc genDefault(c: var ProcCon; n: PNode; d: var Value) = + let m = expandDefault(n.typ, n.info) + gen c, m, d + +proc genMagic(c: var ProcCon; n: PNode; d: var Value; m: TMagic) = + case m + of mAnd: c.genAndOr(n, opcFJmp, d) + of mOr: c.genAndOr(n, opcTJmp, d) + of mPred, mSubI: c.genBinaryOp(n, d, CheckedSub) + of mSucc, mAddI: c.genBinaryOp(n, d, CheckedAdd) + of mInc: + unused(c, n, d) + c.genIncDec(n, CheckedAdd) + of mDec: + unused(c, n, d) + c.genIncDec(n, CheckedSub) + of mOrd, mChr, mArrToSeq, mUnown: + c.gen(n[1], d) + of generatedMagics: + genCall(c, n, d) + of mNew, mNewFinalize: + unused(c, n, d) + c.genNew(n, needsInit = true) + of mNewSeq: + unused(c, n, d) + c.genNewSeq(n) + of mNewSeqOfCap: c.genNewSeqOfCap(n, d) + of mNewString, mNewStringOfCap, mExit: c.genCall(n, d) + of mLengthOpenArray, mLengthArray, mLengthSeq, mLengthStr: + genArrayLen(c, n, d) + of mMulI: genBinaryOp(c, n, d, Mul) + of mDivI: genBinaryOp(c, n, d, Div) + of mModI: genBinaryOp(c, n, d, Mod) + of mAddF64: genBinaryOp(c, n, d, Add) + of mSubF64: genBinaryOp(c, n, d, Sub) + of mMulF64: genBinaryOp(c, n, d, Mul) + of mDivF64: genBinaryOp(c, n, d, Div) + of mShrI: genBinaryOp(c, n, d, BitShr) + of mShlI: genBinaryOp(c, n, d, BitShl) + of mAshrI: genBinaryOp(c, n, d, BitShr) + of mBitandI: genBinaryOp(c, n, d, BitAnd) + of mBitorI: genBinaryOp(c, n, d, BitOr) + of mBitxorI: genBinaryOp(c, n, d, BitXor) + of mAddU: genBinaryOp(c, n, d, Add) + of mSubU: genBinaryOp(c, n, d, Sub) + of mMulU: genBinaryOp(c, n, d, Mul) + of mDivU: genBinaryOp(c, n, d, Div) + of mModU: genBinaryOp(c, n, d, Mod) + of mEqI, mEqB, mEqEnum, mEqCh: + genCmpOp(c, n, d, Eq) + of mLeI, mLeEnum, mLeCh, mLeB: + genCmpOp(c, n, d, Le) + of mLtI, mLtEnum, mLtCh, mLtB: + genCmpOp(c, n, d, Lt) + of mEqF64: genCmpOp(c, n, d, Eq) + of mLeF64: genCmpOp(c, n, d, Le) + of mLtF64: genCmpOp(c, n, d, Lt) + of mLePtr, mLeU: genCmpOp(c, n, d, Le) + of mLtPtr, mLtU: genCmpOp(c, n, d, Lt) + of mEqProc, mEqRef: + genCmpOp(c, n, d, Eq) + of mXor: genBinaryOp(c, n, d, BitXor) + of mNot: genUnaryOp(c, n, d, BoolNot) + of mUnaryMinusI, mUnaryMinusI64: + genUnaryMinus(c, n, d) + #genNarrow(c, n, d) + of mUnaryMinusF64: genUnaryMinus(c, n, d) + of mUnaryPlusI, mUnaryPlusF64: gen(c, n[1], d) + of mBitnotI: + genUnaryOp(c, n, d, BitNot) + when false: + # XXX genNarrowU modified, do not narrow signed types + let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) + let size = getSize(c.config, t) + if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8): + c.gABC(n, opcNarrowU, d, TRegister(size*8)) + of mStrToStr, mEnsureMove: c.gen n[1], d + of mIntToStr: genUnaryCp(c, n, d, "nimIntToStr") + of mInt64ToStr: genUnaryCp(c, n, d, "nimInt64ToStr") + of mBoolToStr: genUnaryCp(c, n, d, "nimBoolToStr") + of mCharToStr: genUnaryCp(c, n, d, "nimCharToStr") + of mFloatToStr: + if n[1].typ.skipTypes(abstractInst).kind == tyFloat32: + genUnaryCp(c, n, d, "nimFloat32ToStr") + else: + genUnaryCp(c, n, d, "nimFloatToStr") + of mCStrToStr: genUnaryCp(c, n, d, "cstrToNimstr") + of mEnumToStr: genEnumToStr(c, n, d) + + of mEqStr: genBinaryCp(c, n, d, "eqStrings") + of mEqCString: genCall(c, n, d) + of mLeStr: genBinaryCp(c, n, d, "leStrings") + of mLtStr: genBinaryCp(c, n, d, "ltStrings") + + of mSetLengthStr: + unused(c, n, d) + let nb = copyTree(n) + nb[1] = makeAddr(nb[1], c.m.idgen) + genBinaryCp(c, nb, d, "setLengthStrV2") + + of mSetLengthSeq: + unused(c, n, d) + let nb = copyTree(n) + nb[1] = makeAddr(nb[1], c.m.idgen) + genCall(c, nb, d) + + of mSwap: + unused(c, n, d) + c.gen(lowerSwap(c.m.graph, n, c.m.idgen, + if c.prc == nil: c.m.module else: c.prc), d) + of mParseBiggestFloat: + genCall c, n, d + of mHigh: + c.genHigh n, d + + of mEcho: + unused(c, n, d) + genUnaryCp c, n, d, "echoBinSafe" + + of mAppendStrCh: + unused(c, n, d) + let nb = copyTree(n) + nb[1] = makeAddr(nb[1], c.m.idgen) + genBinaryCp(c, nb, d, "nimAddCharV1") + of mMinI, mMaxI, mAbsI, mDotDot: + c.genCall(n, d) + of mSizeOf: + localError(c.config, n.info, sizeOfLikeMsg("sizeof")) + of mAlignOf: + localError(c.config, n.info, sizeOfLikeMsg("alignof")) + of mOffsetOf: + localError(c.config, n.info, sizeOfLikeMsg("offsetof")) + of mRunnableExamples: + discard "just ignore any call to runnableExamples" + of mDestroy, mTrace: discard "ignore calls to the default destructor" + of mOf: genOf(c, n, d) + of mAppendStrStr: + unused(c, n, d) + let nb = copyTree(n) + nb[1] = makeAddr(nb[1], c.m.idgen) + genBinaryCp(c, nb, d, "nimAddStrV1") + of mAppendSeqElem: + unused(c, n, d) + let nb = copyTree(n) + nb[1] = makeAddr(nb[1], c.m.idgen) + genCall(c, nb, d) + of mIsNil: genIsNil(c, n, d) + of mInSet: genInSet(c, n, d) + of mCard: genCard(c, n, d) + of mEqSet: genEqSet(c, n, d) + of mLeSet: genLeSet(c, n, d) + of mLtSet: genLtSet(c, n, d) + of mMulSet: genBinarySet(c, n, d, m) + of mPlusSet: genBinarySet(c, n, d, m) + of mMinusSet: genBinarySet(c, n, d, m) + of mIncl, mExcl: + unused(c, n, d) + genInclExcl(c, n, m) + of mConStrStr: genStrConcat(c, n, d) + of mDefault, mZeroDefault: + genDefault c, n, d + else: + # mGCref, mGCunref, + globalError(c.config, n.info, "cannot generate code for: " & $m) + +#[ + + of mReset: + unused(c, n, d) + var d = c.genx(n[1]) + # XXX use ldNullOpcode() here? + c.gABx(n, opcLdNull, d, c.genType(n[1].typ)) + c.gABC(n, opcNodeToReg, d, d) + c.gABx(n, ldNullOpcode(n.typ), d, c.genType(n.typ)) + + of mConStrStr: genVarargsABC(c, n, d, opcConcatStr) + + of mRepr: genUnaryABC(c, n, d, opcRepr) + + of mSlice: + var + d = c.genx(n[1]) + left = c.genIndex(n[2], n[1].typ) + right = c.genIndex(n[3], n[1].typ) + if isEmpty(d): d = c.getTemp(n) + c.gABC(n, opcNodeToReg, d, d) + c.gABC(n, opcSlice, d, left, right) + c.freeTemp(left) + c.freeTemp(right) + c.freeTemp(d) + + of mMove: + let arg = n[1] + let a = c.genx(arg) + if isEmpty(d): d = c.getTemp(arg) + gABC(c, arg, whichAsgnOpc(arg, requiresCopy=false), d, a) + c.freeTemp(a) + of mDup: + let arg = n[1] + let a = c.genx(arg) + if isEmpty(d): d = c.getTemp(arg) + gABC(c, arg, whichAsgnOpc(arg, requiresCopy=false), d, a) + c.freeTemp(a) + + of mNodeId: + c.genUnaryABC(n, d, opcNodeId) + + of mExpandToAst: + if n.len != 2: + globalError(c.config, n.info, "expandToAst requires 1 argument") + let arg = n[1] + if arg.kind in nkCallKinds: + #if arg[0].kind != nkSym or arg[0].sym.kind notin {skTemplate, skMacro}: + # "ExpandToAst: expanded symbol is no macro or template" + if isEmpty(d): d = c.getTemp(n) + c.genCall(arg, d) + # do not call clearDest(n, d) here as getAst has a meta-type as such + # produces a value + else: + globalError(c.config, n.info, "expandToAst requires a call expression") + of mParseExprToAst: + genBinaryABC(c, n, d, opcParseExprToAst) + of mParseStmtToAst: + genBinaryABC(c, n, d, opcParseStmtToAst) + of mTypeTrait: + let tmp = c.genx(n[1]) + if isEmpty(d): d = c.getTemp(n) + c.gABx(n, opcSetType, tmp, c.genType(n[1].typ)) + c.gABC(n, opcTypeTrait, d, tmp) + c.freeTemp(tmp) + of mSlurp: genUnaryABC(c, n, d, opcSlurp) + of mNLen: genUnaryABI(c, n, d, opcLenSeq, nimNodeFlag) + of mGetImpl: genUnaryABC(c, n, d, opcGetImpl) + of mGetImplTransf: genUnaryABC(c, n, d, opcGetImplTransf) + of mSymOwner: genUnaryABC(c, n, d, opcSymOwner) + of mSymIsInstantiationOf: genBinaryABC(c, n, d, opcSymIsInstantiationOf) + of mNChild: genBinaryABC(c, n, d, opcNChild) + of mNAdd: genBinaryABC(c, n, d, opcNAdd) + of mNAddMultiple: genBinaryABC(c, n, d, opcNAddMultiple) + of mNKind: genUnaryABC(c, n, d, opcNKind) + of mNSymKind: genUnaryABC(c, n, d, opcNSymKind) + + of mNccValue: genUnaryABC(c, n, d, opcNccValue) + of mNccInc: genBinaryABC(c, n, d, opcNccInc) + of mNcsAdd: genBinaryABC(c, n, d, opcNcsAdd) + of mNcsIncl: genBinaryABC(c, n, d, opcNcsIncl) + of mNcsLen: genUnaryABC(c, n, d, opcNcsLen) + of mNcsAt: genBinaryABC(c, n, d, opcNcsAt) + of mNctLen: genUnaryABC(c, n, d, opcNctLen) + of mNctGet: genBinaryABC(c, n, d, opcNctGet) + of mNctHasNext: genBinaryABC(c, n, d, opcNctHasNext) + of mNctNext: genBinaryABC(c, n, d, opcNctNext) + + of mNIntVal: genUnaryABC(c, n, d, opcNIntVal) + of mNFloatVal: genUnaryABC(c, n, d, opcNFloatVal) + of mNSymbol: genUnaryABC(c, n, d, opcNSymbol) + of mNIdent: genUnaryABC(c, n, d, opcNIdent) + of mNGetType: + let tmp = c.genx(n[1]) + if isEmpty(d): d = c.getTemp(n) + let rc = case n[0].sym.name.s: + of "getType": 0 + of "typeKind": 1 + of "getTypeInst": 2 + else: 3 # "getTypeImpl" + c.gABC(n, opcNGetType, d, tmp, rc) + c.freeTemp(tmp) + #genUnaryABC(c, n, d, opcNGetType) + of mNSizeOf: + let imm = case n[0].sym.name.s: + of "getSize": 0 + of "getAlign": 1 + else: 2 # "getOffset" + c.genUnaryABI(n, d, opcNGetSize, imm) + of mNStrVal: genUnaryABC(c, n, d, opcNStrVal) + of mNSigHash: genUnaryABC(c, n , d, opcNSigHash) + of mNSetIntVal: + unused(c, n, d) + genBinaryStmt(c, n, opcNSetIntVal) + of mNSetFloatVal: + unused(c, n, d) + genBinaryStmt(c, n, opcNSetFloatVal) + of mNSetSymbol: + unused(c, n, d) + genBinaryStmt(c, n, opcNSetSymbol) + of mNSetIdent: + unused(c, n, d) + genBinaryStmt(c, n, opcNSetIdent) + of mNSetStrVal: + unused(c, n, d) + genBinaryStmt(c, n, opcNSetStrVal) + of mNNewNimNode: genBinaryABC(c, n, d, opcNNewNimNode) + of mNCopyNimNode: genUnaryABC(c, n, d, opcNCopyNimNode) + of mNCopyNimTree: genUnaryABC(c, n, d, opcNCopyNimTree) + of mNBindSym: genBindSym(c, n, d) + of mStrToIdent: genUnaryABC(c, n, d, opcStrToIdent) + of mEqIdent: genBinaryABC(c, n, d, opcEqIdent) + of mEqNimrodNode: genBinaryABC(c, n, d, opcEqNimNode) + of mSameNodeType: genBinaryABC(c, n, d, opcSameNodeType) + of mNLineInfo: + case n[0].sym.name.s + of "getFile": genUnaryABI(c, n, d, opcNGetLineInfo, 0) + of "getLine": genUnaryABI(c, n, d, opcNGetLineInfo, 1) + of "getColumn": genUnaryABI(c, n, d, opcNGetLineInfo, 2) + of "copyLineInfo": + internalAssert c.config, n.len == 3 + unused(c, n, d) + genBinaryStmt(c, n, opcNCopyLineInfo) + of "setLine": + internalAssert c.config, n.len == 3 + unused(c, n, d) + genBinaryStmt(c, n, opcNSetLineInfoLine) + of "setColumn": + internalAssert c.config, n.len == 3 + unused(c, n, d) + genBinaryStmt(c, n, opcNSetLineInfoColumn) + of "setFile": + internalAssert c.config, n.len == 3 + unused(c, n, d) + genBinaryStmt(c, n, opcNSetLineInfoFile) + else: internalAssert c.config, false + of mNHint: + unused(c, n, d) + genBinaryStmt(c, n, opcNHint) + of mNWarning: + unused(c, n, d) + genBinaryStmt(c, n, opcNWarning) + of mNError: + if n.len <= 1: + # query error condition: + c.gABC(n, opcQueryErrorFlag, d) + else: + # setter + unused(c, n, d) + genBinaryStmt(c, n, opcNError) + of mNCallSite: + if isEmpty(d): d = c.getTemp(n) + c.gABC(n, opcCallSite, d) + of mNGenSym: genBinaryABC(c, n, d, opcGenSym) + +]# + +proc canElimAddr(n: PNode; idgen: IdGenerator): PNode = + result = nil + case n[0].kind + of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64: + var m = n[0][0] + if m.kind in {nkDerefExpr, nkHiddenDeref}: + # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x) + result = copyNode(n[0]) + result.add m[0] + if n.typ.skipTypes(abstractVar).kind != tyOpenArray: + result.typ = n.typ + elif n.typ.skipTypes(abstractInst).kind in {tyVar}: + result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen) + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + var m = n[0][1] + if m.kind in {nkDerefExpr, nkHiddenDeref}: + # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x) + result = copyNode(n[0]) + result.add n[0][0] + result.add m[0] + if n.typ.skipTypes(abstractVar).kind != tyOpenArray: + result.typ = n.typ + elif n.typ.skipTypes(abstractInst).kind in {tyVar}: + result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen) + else: + if n[0].kind in {nkDerefExpr, nkHiddenDeref}: + # addr ( deref ( x )) --> x + result = n[0][0] + +template valueIntoDest(c: var ProcCon; info: PackedLineInfo; d: var Value; typ: PType; body: untyped) = + if isEmpty(d): + body(Tree d) + else: + buildTyped c.code, info, Asgn, typeToIr(c.m.types, typ): + copyTree c.code, d + body(c.code) + +proc genAddr(c: var ProcCon; n: PNode; d: var Value, flags: GenFlags) = + if (let m = canElimAddr(n, c.m.idgen); m != nil): + gen(c, m, d, flags) + return + + let info = toLineInfo(c, n.info) + let tmp = c.genx(n[0], flags) + template body(target) = + buildTyped target, info, AddrOf, typeToIr(c.m.types, n.typ): + copyTree target, tmp + + valueIntoDest c, info, d, n.typ, body + freeTemp c, tmp + +proc genDeref(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = + let info = toLineInfo(c, n.info) + let tmp = c.genx(n[0], flags) + template body(target) = + buildTyped target, info, Load, typeToIr(c.m.types, n.typ): + copyTree target, tmp + + valueIntoDest c, info, d, n.typ, body + freeTemp c, tmp + +proc genConv(c: var ProcCon; n, arg: PNode; d: var Value; flags: GenFlags; opc: Opcode) = + let targetType = n.typ.skipTypes({tyDistinct}) + let argType = arg.typ.skipTypes({tyDistinct}) + + if sameBackendType(targetType, argType) or ( + argType.kind == tyProc and targetType.kind == argType.kind): + # don't do anything for lambda lifting conversions: + gen c, arg, d + return + + let info = toLineInfo(c, n.info) + let tmp = c.genx(arg, flags) + template body(target) = + buildTyped target, info, opc, typeToIr(c.m.types, n.typ): + if opc == CheckedObjConv: + target.addLabel info, CheckedGoto, c.exitLabel + copyTree target, tmp + + valueIntoDest c, info, d, n.typ, body + freeTemp c, tmp + +proc genObjOrTupleConstr(c: var ProcCon; n: PNode, d: var Value) = + # XXX x = (x.old, 22) produces wrong code ... stupid self assignments + let info = toLineInfo(c, n.info) + template body(target) = + buildTyped target, info, ObjConstr, typeToIr(c.m.types, n.typ): + for i in ord(n.kind == nkObjConstr)..<n.len: + let it = n[i] + if it.kind == nkExprColonExpr: + genField(c, it[0], Value target) + let tmp = c.genx(it[1]) + copyTree target, tmp + c.freeTemp(tmp) + else: + let tmp = c.genx(it) + target.addImmediateVal info, i + copyTree target, tmp + c.freeTemp(tmp) + + valueIntoDest c, info, d, n.typ, body + +proc genArrayConstr(c: var ProcCon; n: PNode, d: var Value) = + let seqType = n.typ.skipTypes(abstractVar-{tyTypeDesc}) + if seqType.kind == tySequence: + localError c.config, n.info, "sequence constructor not implemented" + return + + let info = toLineInfo(c, n.info) + template body(target) = + buildTyped target, info, ArrayConstr, typeToIr(c.m.types, n.typ): + for i in 0..<n.len: + let tmp = c.genx(n[i]) + copyTree target, tmp + c.freeTemp(tmp) + + valueIntoDest c, info, d, n.typ, body + +proc genAsgn2(c: var ProcCon; a, b: PNode) = + assert a != nil + assert b != nil + var d = c.genx(a) + c.gen b, d + +proc genVarSection(c: var ProcCon; n: PNode) = + for a in n: + if a.kind == nkCommentStmt: continue + #assert(a[0].kind == nkSym) can happen for transformed vars + if a.kind == nkVarTuple: + c.gen(lowerTupleUnpacking(c.m.graph, a, c.m.idgen, c.prc)) + else: + var vn = a[0] + if vn.kind == nkPragmaExpr: vn = vn[0] + if vn.kind == nkSym: + let s = vn.sym + var opc: Opcode + if sfThread in s.flags: + opc = SummonThreadLocal + elif sfGlobal in s.flags: + opc = SummonGlobal + else: + opc = Summon + c.code.addSummon toLineInfo(c, a.info), SymId(s.itemId.item), typeToIr(c.m.types, s.typ), opc + if a[2].kind != nkEmpty: + genAsgn2(c, vn, a[2]) + else: + if a[2].kind == nkEmpty: + discard "XXX assign default value to location here" + else: + genAsgn2(c, vn, a[2]) + +proc genAsgn(c: var ProcCon; n: PNode) = + var d = c.genx(n[0]) + c.gen n[1], d + +proc convStrToCStr(c: var ProcCon; n: PNode; d: var Value) = + genUnaryCp(c, n, d, "nimToCStringConv", argAt = 0) + +proc convCStrToStr(c: var ProcCon; n: PNode; d: var Value) = + genUnaryCp(c, n, d, "cstrToNimstr", argAt = 0) + +proc irModule(c: var ProcCon; owner: PSym): string = + #if owner == c.m.module: "" else: + customPath(toFullPath(c.config, owner.info)) + +proc genRdVar(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = + let info = toLineInfo(c, n.info) + let s = n.sym + if ast.originatingModule(s) != c.m.module: + template body(target) = + build target, info, ModuleSymUse: + target.addStrVal c.m.strings, info, irModule(c, ast.originatingModule(s)) + target.addImmediateVal info, s.itemId.item.int + + valueIntoDest c, info, d, s.typ, body + else: + template body(target) = + target.addSymUse info, SymId(s.itemId.item) + valueIntoDest c, info, d, s.typ, body + +proc genSym(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) = + let s = n.sym + case s.kind + of skVar, skForVar, skTemp, skLet, skResult, skParam, skConst: + genRdVar(c, n, d, flags) + of skProc, skFunc, skConverter, skMethod, skIterator: + if ast.originatingModule(s) == c.m.module: + # anon and generic procs have no AST so we need to remember not to forget + # to emit these: + if not c.m.pendingProcs.hasKey(s.itemId): + c.m.pendingProcs[s.itemId] = s + genRdVar(c, n, d, flags) + of skEnumField: + let info = toLineInfo(c, n.info) + template body(target) = + target.addIntVal c.m.integers, info, typeToIr(c.m.types, n.typ), s.position + valueIntoDest c, info, d, n.typ, body + else: + localError(c.config, n.info, "cannot generate code for: " & s.name.s) + +proc genNumericLit(c: var ProcCon; n: PNode; d: var Value; bits: int64) = + let info = toLineInfo(c, n.info) + template body(target) = + target.addIntVal c.m.integers, info, typeToIr(c.m.types, n.typ), bits + valueIntoDest c, info, d, n.typ, body + +proc genStringLit(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + template body(target) = + target.addStrVal c.m.strings, info, n.strVal + valueIntoDest c, info, d, n.typ, body + +proc genNilLit(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + template body(target) = + target.addNilVal info, typeToIr(c.m.types, n.typ) + valueIntoDest c, info, d, n.typ, body + +proc genRangeCheck(c: var ProcCon; n: PNode; d: var Value) = + # XXX to implement properly + gen c, n[0], d + +proc genArrAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = + let arrayKind = n[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind + let info = toLineInfo(c, n.info) + case arrayKind + of tyString: + # XXX implement range check + let a = genx(c, n[0], flags) + let b = genx(c, n[1]) + let t = typeToIr(c.m.types, n.typ) + template body(target) = + buildTyped target, info, ArrayAt, t: + buildTyped target, info, FieldAt, strPayloadPtrType(c.m.types): + copyTree target, a + target.addImmediateVal info, 1 # (len, p)-pair + copyTree target, b + intoDest d, info, t, body + freeTemp c, b + freeTemp c, a + + of tyCstring, tyPtr, tyUncheckedArray: + let a = genx(c, n[0], flags) + let b = genx(c, n[1]) + template body(target) = + buildTyped target, info, ArrayAt, typeToIr(c.m.types, n.typ): + copyTree target, a + copyTree target, b + valueIntoDest c, info, d, n.typ, body + + freeTemp c, b + freeTemp c, a + of tyTuple: + let a = genx(c, n[0], flags) + let b = int n[1].intVal + template body(target) = + buildTyped target, info, FieldAt, typeToIr(c.m.types, n.typ): + copyTree target, a + target.addImmediateVal info, b + valueIntoDest c, info, d, n.typ, body + + freeTemp c, a + of tyOpenArray, tyVarargs: + # XXX implement range check + let a = genx(c, n[0], flags) + let b = genx(c, n[1]) + let t = typeToIr(c.m.types, n.typ) + template body(target) = + buildTyped target, info, ArrayAt, t: + buildTyped target, info, FieldAt, openArrayPayloadType(c.m.types, n[0].typ): + copyTree target, a + target.addImmediateVal info, 0 # (p, len)-pair + copyTree target, b + intoDest d, info, t, body + + freeTemp c, b + freeTemp c, a + of tyArray: + # XXX implement range check + let a = genx(c, n[0], flags) + var b = default(Value) + genIndex(c, n[1], n[0].typ, b) + + template body(target) = + buildTyped target, info, ArrayAt, typeToIr(c.m.types, n.typ): + copyTree target, a + copyTree target, b + valueIntoDest c, info, d, n.typ, body + freeTemp c, b + freeTemp c, a + of tySequence: + let a = genx(c, n[0], flags) + let b = genx(c, n[1]) + let t = typeToIr(c.m.types, n.typ) + template body(target) = + buildTyped target, info, ArrayAt, t: + buildTyped target, info, FieldAt, seqPayloadPtrType(c.m.types, n[0].typ): + copyTree target, a + target.addImmediateVal info, 1 # (len, p)-pair + copyTree target, b + intoDest d, info, t, body + freeTemp c, b + freeTemp c, a + else: + localError c.config, n.info, "invalid type for nkBracketExpr: " & $arrayKind + +proc genObjAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = + let info = toLineInfo(c, n.info) + let a = genx(c, n[0], flags) + + template body(target) = + buildTyped target, info, FieldAt, typeToIr(c.m.types, n.typ): + copyTree target, a + genField c, n[1], Value(target) + + valueIntoDest c, info, d, n.typ, body + freeTemp c, a + +proc genParams(c: var ProcCon; params: PNode) = + for i in 1..<params.len: + let s = params[i].sym + c.code.addSummon toLineInfo(c, params[i].info), SymId(s.itemId.item), typeToIr(c.m.types, s.typ), SummonParam + +proc addCallConv(c: var ProcCon; info: PackedLineInfo; callConv: TCallingConvention) = + template ann(s: untyped) = c.code.addPragmaId info, s + case callConv + of ccNimCall, ccFastCall, ccClosure: ann FastCall + of ccStdCall: ann StdCall + of ccCDecl: ann CDeclCall + of ccSafeCall: ann SafeCall + of ccSysCall: ann SysCall + of ccInline: ann InlineCall + of ccNoInline: ann NoinlineCall + of ccThisCall: ann ThisCall + of ccNoConvention: ann NoCall + +proc genProc(cOuter: var ProcCon; n: PNode) = + if n.len == 0 or n[namePos].kind != nkSym: return + let prc = n[namePos].sym + if isGenericRoutineStrict(prc): return + + var c = initProcCon(cOuter.m, prc, cOuter.m.graph.config) + genParams(c, prc.typ.n) + + let body = transformBody(c.m.graph, c.m.idgen, prc, useCache) + + let info = toLineInfo(c, body.info) + build c.code, info, ProcDecl: + addSymDef c.code, info, SymId(prc.itemId.item) + addCallConv c, info, prc.typ.callConv + if {sfImportc, sfExportc} * prc.flags != {}: + build c.code, info, PragmaPair: + c.code.addPragmaId info, ExternName + c.code.addStrVal c.m.strings, info, prc.loc.r + if sfImportc in prc.flags: + if lfHeader in prc. loc.flags: + assert(prc. annex != nil) + let str = getStr(prc. annex.path) + build c.code, info, PragmaPair: + c.code.addPragmaId info, HeaderImport + c.code.addStrVal c.m.strings, info, str + elif lfDynamicLib in prc. loc.flags: + assert(prc. annex != nil) + let str = getStr(prc. annex.path) + build c.code, info, PragmaPair: + c.code.addPragmaId info, DllImport + c.code.addStrVal c.m.strings, info, str + elif sfExportc in prc.flags: + if lfDynamicLib in prc. loc.flags: + c.code.addPragmaId info, DllExport + else: + c.code.addPragmaId info, ObjExport + + gen(c, body) + patch c, body, c.exitLabel + + copyTree cOuter.code, c.code + +proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) = + when defined(nimCompilerStacktraceHints): + setFrameMsg c.config$n.info & " " & $n.kind & " " & $flags + case n.kind + of nkSym: genSym(c, n, d, flags) + of nkCallKinds: + if n[0].kind == nkSym: + let s = n[0].sym + if s.magic != mNone: + genMagic(c, n, d, s.magic) + elif s.kind == skMethod: + localError(c.config, n.info, "cannot call method " & s.name.s & + " at compile time") + else: + genCall(c, n, d) + clearDest(c, n, d) + else: + genCall(c, n, d) + clearDest(c, n, d) + of nkCharLit..nkInt64Lit, nkUIntLit..nkUInt64Lit: + genNumericLit(c, n, d, n.intVal) + of nkFloatLit..nkFloat128Lit: + genNumericLit(c, n, d, cast[int64](n.floatVal)) + of nkStrLit..nkTripleStrLit: + genStringLit(c, n, d) + of nkNilLit: + if not n.typ.isEmptyType: genNilLit(c, n, d) + else: unused(c, n, d) + of nkAsgn, nkFastAsgn, nkSinkAsgn: + unused(c, n, d) + genAsgn(c, n) + of nkDotExpr: genObjAccess(c, n, d, flags) + of nkCheckedFieldExpr: genObjAccess(c, n[0], d, flags) + of nkBracketExpr: genArrAccess(c, n, d, flags) + of nkDerefExpr, nkHiddenDeref: genDeref(c, n, d, flags) + of nkAddr, nkHiddenAddr: genAddr(c, n, d, flags) + of nkIfStmt, nkIfExpr: genIf(c, n, d) + of nkWhenStmt: + # This is "when nimvm" node. Chose the first branch. + gen(c, n[0][1], d) + of nkCaseStmt: genCase(c, n, d) + of nkWhileStmt: + unused(c, n, d) + genWhile(c, n) + of nkBlockExpr, nkBlockStmt: genBlock(c, n, d) + of nkReturnStmt: genReturn(c, n) + of nkRaiseStmt: genRaise(c, n) + of nkBreakStmt: genBreak(c, n) + of nkTryStmt, nkHiddenTryStmt: genTry(c, n, d) + of nkStmtList: + #unused(c, n, d) + # XXX Fix this bug properly, lexim triggers it + for x in n: gen(c, x) + of nkStmtListExpr: + for i in 0..<n.len-1: gen(c, n[i]) + gen(c, n[^1], d, flags) + of nkPragmaBlock: + gen(c, n.lastSon, d, flags) + of nkDiscardStmt: + unused(c, n, d) + gen(c, n[0], d) + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + genConv(c, n, n[1], d, flags, NumberConv) # misnomer? + of nkObjDownConv: + genConv(c, n, n[0], d, flags, ObjConv) + of nkObjUpConv: + genConv(c, n, n[0], d, flags, CheckedObjConv) + of nkVarSection, nkLetSection, nkConstSection: + unused(c, n, d) + genVarSection(c, n) + of nkLambdaKinds: + #let s = n[namePos].sym + #discard genProc(c, s) + gen(c, newSymNode(n[namePos].sym), d) + of nkChckRangeF, nkChckRange64, nkChckRange: + genRangeCheck(c, n, d) + of declarativeDefs - {nkIteratorDef}: + unused(c, n, d) + genProc(c, n) + of nkEmpty, nkCommentStmt, nkTypeSection, nkPragma, + nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt, + nkMixinStmt, nkBindStmt, nkMacroDef, nkIteratorDef: + unused(c, n, d) + of nkStringToCString: convStrToCStr(c, n, d) + of nkCStringToString: convCStrToStr(c, n, d) + of nkBracket: genArrayConstr(c, n, d) + of nkCurly: genSetConstr(c, n, d) + of nkObjConstr, nkPar, nkClosure, nkTupleConstr: + genObjOrTupleConstr(c, n, d) + of nkCast: + genConv(c, n, n[1], d, flags, Cast) + of nkComesFrom: + discard "XXX to implement for better stack traces" + else: + localError(c.config, n.info, "cannot generate IR code for " & $n) + +proc genStmt*(c: var ProcCon; n: PNode): int = + result = c.code.len + var d = default(Value) + c.gen(n, d) + unused c, n, d + +proc genExpr*(c: var ProcCon; n: PNode, requiresValue = true): int = + result = c.code.len + var d = default(Value) + c.gen(n, d) + if isEmpty d: + if requiresValue: + globalError(c.config, n.info, "VM problem: d register is not set") diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim new file mode 100644 index 000000000..90a3035dd --- /dev/null +++ b/compiler/nir/cir.nim @@ -0,0 +1,78 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# We produce C code as a list of tokens. + +import std / assertions +import .. / ic / bitabs + +type + Token = LitId # indexing into the tokens BiTable[string] + + PredefinedToken = enum + IgnoreMe = "<unused>" + EmptyToken = "" + DeclPrefix = "" # the next token is the name of a definition + CurlyLe = "{" + CurlyRi = "}" + ParLe = "(" + ParRi = ")" + BracketLe = "[" + BracketRi = "]" + NewLine = "\n" + Semicolon = ";" + Comma = ", " + Space = " " + Colon = ":" + Dot = "." + Arrow = "->" + Star = "*" + Amp = "&" + AsgnOpr = " = " + ScopeOpr = "::" + ConstKeyword = "const " + StaticKeyword = "static " + NimString = "NimString" + StrLitPrefix = "(NimChar*)" + StrLitNamePrefix = "Qstr" + LoopKeyword = "while (true) " + WhileKeyword = "while (" + IfKeyword = "if (" + ElseKeyword = "else " + SwitchKeyword = "switch (" + CaseKeyword = "case " + DefaultKeyword = "default:" + BreakKeyword = "break" + NullPtr = "nullptr" + IfNot = "if (!(" + ReturnKeyword = "return " + +const + ModulePrefix = Token(int(ReturnKeyword)+1) + +proc fillTokenTable(tab: var BiTable[string]) = + for e in EmptyToken..high(PredefinedToken): + let id = tab.getOrIncl $e + assert id == LitId(e) + +type + GeneratedCode* = object + code: seq[LitId] + tokens: BiTable[string] + +proc initGeneratedCode*(): GeneratedCode = + result = GeneratedCode(code: @[], tokens: initBiTable[string]()) + fillTokenTable(result.tokens) + +proc add*(g: var GeneratedCode; t: PredefinedToken) {.inline.} = + g.code.add Token(t) + +proc add*(g: var GeneratedCode; s: string) {.inline.} = + g.code.add g.tokens.getOrIncl(s) + diff --git a/compiler/nir/nir.nim b/compiler/nir/nir.nim new file mode 100644 index 000000000..1994a1be7 --- /dev/null +++ b/compiler/nir/nir.nim @@ -0,0 +1,71 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Nim Intermediate Representation, designed to capture all of Nim's semantics without losing too much +## precious information. Can easily be translated into C. And to JavaScript, hopefully. + +import ".." / [ast, modulegraphs, renderer, transf] +import nirtypes, nirinsts, ast2ir + +type + PCtx* = ref object of TPassContext + m: ModuleCon + c: ProcCon + oldErrorCount: int + +proc newCtx*(module: PSym; g: ModuleGraph; idgen: IdGenerator): PCtx = + let m = initModuleCon(g, g.config, idgen, module) + PCtx(m: m, c: initProcCon(m, nil, g.config), idgen: idgen) + +proc refresh*(c: PCtx; module: PSym; idgen: IdGenerator) = + c.m = initModuleCon(c.m.graph, c.m.graph.config, idgen, module) + c.c = initProcCon(c.m, nil, c.m.graph.config) + c.idgen = idgen + +proc setupGlobalCtx*(module: PSym; graph: ModuleGraph; idgen: IdGenerator) = + if graph.repl.isNil: + graph.repl = newCtx(module, graph, idgen) + #registerAdditionalOps(PCtx graph.repl) + else: + refresh(PCtx graph.repl, module, idgen) + +proc setupNirReplGen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = + setupGlobalCtx(module, graph, idgen) + result = PCtx graph.repl + +proc evalStmt(c: PCtx; n: PNode) = + let n = transformExpr(c.m.graph, c.idgen, c.m.module, n) + let pc = genStmt(c.c, n) + + var res = "" + if pc < c.c.code.len: + toString c.c.code, NodePos(pc), c.m.strings, c.m.integers, res + #res.add "\n" + #toString res, c.m.types.g + echo res + + +proc runCode*(c: PPassContext; n: PNode): PNode = + let c = PCtx(c) + # don't eval errornous code: + if c.oldErrorCount == c.m.graph.config.errorCounter: + evalStmt(c, n) + result = newNodeI(nkEmpty, n.info) + else: + result = n + c.oldErrorCount = c.m.graph.config.errorCounter + +when false: + type + Module* = object + types: TypeGraph + data: seq[Tree] + init: seq[Tree] + procs: seq[Tree] + diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim new file mode 100644 index 000000000..f037b4f0e --- /dev/null +++ b/compiler/nir/nirinsts.nim @@ -0,0 +1,418 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## NIR instructions. Somewhat inspired by LLVM's instructions. + +import std / [assertions, hashes] +import .. / ic / bitabs +import nirlineinfos, nirtypes + +type + SymId* = distinct int + +proc `$`*(s: SymId): string {.borrow.} +proc hash*(s: SymId): Hash {.borrow.} +proc `==`*(a, b: SymId): bool {.borrow.} + +type + Opcode* = enum + Nop, + ImmediateVal, + IntVal, + StrVal, + SymDef, + SymUse, + Typed, # with type ID + PragmaId, # with Pragma ID, possible values: see PragmaKey enum + NilVal, + Label, + Goto, + CheckedGoto, + LoopLabel, + GotoLoop, # last atom + + ModuleSymUse, # `"module".x` + + ArrayConstr, + ObjConstr, + Ret, + Yld, + + Select, + SelectPair, # ((values...), Label) + SelectList, # (values...) + SelectValue, # (value) + SelectRange, # (valueA..valueB) + SummonGlobal, + SummonThreadLocal, + Summon, # x = Summon Typed <Type ID>; x begins to live + SummonParam, + SummonConst, + Kill, # `Kill x`: scope end for `x` + + AddrOf, + ArrayAt, # addr(a[i]) + FieldAt, # addr(obj.field) + + Load, # a[] + Store, # a[] = b + Asgn, # a = b + SetExc, + TestExc, + + Call, + IndirectCall, + CheckedCall, # call that can raise + CheckedIndirectCall, # call that can raise + CheckedAdd, # with overflow checking etc. + CheckedSub, + CheckedMul, + CheckedDiv, + CheckedMod, + Add, + Sub, + Mul, + Div, + Mod, + BitShl, + BitShr, + BitAnd, + BitOr, + BitXor, + BitNot, + BoolNot, + Eq, + Le, + Lt, + Cast, + NumberConv, + CheckedObjConv, + ObjConv, + TestOf, + Emit, + ProcDecl, + PragmaPair + +type + PragmaKey* = enum + FastCall, StdCall, CDeclCall, SafeCall, SysCall, InlineCall, NoinlineCall, ThisCall, NoCall, + ExternName, + HeaderImport, + DllImport, + DllExport, + ObjExport + +const + LastAtomicValue = GotoLoop + + OpcodeBits = 8'u32 + OpcodeMask = (1'u32 shl OpcodeBits) - 1'u32 + + ValueProducingAtoms = {ImmediateVal, IntVal, StrVal, SymUse, NilVal} + + ValueProducing* = { + ImmediateVal, + IntVal, + StrVal, + SymUse, + NilVal, + ModuleSymUse, + ArrayConstr, + ObjConstr, + CheckedAdd, + CheckedSub, + CheckedMul, + CheckedDiv, + CheckedMod, + Add, + Sub, + Mul, + Div, + Mod, + BitShl, + BitShr, + BitAnd, + BitOr, + BitXor, + BitNot, + BoolNot, + Eq, + Le, + Lt, + Cast, + NumberConv, + CheckedObjConv, + ObjConv, + AddrOf, + Load, + ArrayAt, + FieldAt, + TestOf + } + +type + Instr* = object # 8 bytes + x: uint32 + info: PackedLineInfo + +template kind*(n: Instr): Opcode = Opcode(n.x and OpcodeMask) +template operand(n: Instr): uint32 = (n.x shr OpcodeBits) + +template toX(k: Opcode; operand: uint32): uint32 = + uint32(k) or (operand shl OpcodeBits) + +template toX(k: Opcode; operand: LitId): uint32 = + uint32(k) or (operand.uint32 shl OpcodeBits) + +type + Tree* = object + nodes: seq[Instr] + + Values* = object + numbers: BiTable[int64] + strings: BiTable[string] + +type + PatchPos* = distinct int + NodePos* = distinct int + +const + InvalidPatchPos* = PatchPos(-1) + +proc isValid(p: PatchPos): bool {.inline.} = p.int != -1 + +proc prepare*(tree: var Tree; info: PackedLineInfo; kind: Opcode): PatchPos = + result = PatchPos tree.nodes.len + tree.nodes.add Instr(x: toX(kind, 1'u32), info: info) + +proc isAtom(tree: Tree; pos: int): bool {.inline.} = tree.nodes[pos].kind <= LastAtomicValue +proc isAtom(tree: Tree; pos: NodePos): bool {.inline.} = tree.nodes[pos.int].kind <= LastAtomicValue + +proc patch*(tree: var Tree; pos: PatchPos) = + let pos = pos.int + let k = tree.nodes[pos].kind + assert k > LastAtomicValue + let distance = int32(tree.nodes.len - pos) + assert distance > 0 + tree.nodes[pos].x = toX(k, cast[uint32](distance)) + +template build*(tree: var Tree; info: PackedLineInfo; kind: Opcode; body: untyped) = + let pos = prepare(tree, info, kind) + body + patch(tree, pos) + +template buildTyped*(tree: var Tree; info: PackedLineInfo; kind: Opcode; typ: TypeId; body: untyped) = + let pos = prepare(tree, info, kind) + tree.addTyped info, typ + body + patch(tree, pos) + +proc len*(tree: Tree): int {.inline.} = tree.nodes.len + +template rawSpan(n: Instr): int = int(operand(n)) + +proc nextChild(tree: Tree; pos: var int) {.inline.} = + if tree.nodes[pos].kind > LastAtomicValue: + assert tree.nodes[pos].operand > 0'u32 + inc pos, tree.nodes[pos].rawSpan + else: + inc pos + +iterator sons*(tree: Tree; n: NodePos): NodePos = + var pos = n.int + assert tree.nodes[pos].kind > LastAtomicValue + let last = pos + tree.nodes[pos].rawSpan + inc pos + while pos < last: + yield NodePos pos + nextChild tree, pos + +template `[]`*(t: Tree; n: NodePos): Instr = t.nodes[n.int] + +proc span(tree: Tree; pos: int): int {.inline.} = + if tree.nodes[pos].kind <= LastAtomicValue: 1 else: int(tree.nodes[pos].operand) + +proc copyTree*(dest: var Tree; src: Tree) = + let pos = 0 + let L = span(src, pos) + let d = dest.nodes.len + dest.nodes.setLen(d + L) + assert L > 0 + for i in 0..<L: + dest.nodes[d+i] = src.nodes[pos+i] + +type + LabelId* = distinct int + +proc newLabel*(labelGen: var int): LabelId {.inline.} = + result = LabelId labelGen + inc labelGen + +proc addNewLabel*(t: var Tree; labelGen: var int; info: PackedLineInfo; k: Opcode): LabelId = + assert k in {Label, LoopLabel} + result = LabelId labelGen + t.nodes.add Instr(x: toX(k, uint32(result)), info: info) + inc labelGen + +proc boolVal*(t: var Tree; info: PackedLineInfo; b: bool) = + t.nodes.add Instr(x: toX(ImmediateVal, uint32(b)), info: info) + +proc gotoLabel*(t: var Tree; info: PackedLineInfo; k: Opcode; L: LabelId) = + assert k in {Goto, GotoLoop, CheckedGoto} + t.nodes.add Instr(x: toX(k, uint32(L)), info: info) + +proc addLabel*(t: var Tree; info: PackedLineInfo; k: Opcode; L: LabelId) {.inline.} = + assert k in {Label, LoopLabel, Goto, GotoLoop, CheckedGoto} + t.nodes.add Instr(x: toX(k, uint32(L)), info: info) + +proc addSymUse*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} = + t.nodes.add Instr(x: toX(SymUse, uint32(s)), info: info) + +proc addSymDef*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} = + t.nodes.add Instr(x: toX(SymDef, uint32(s)), info: info) + +proc addTyped*(t: var Tree; info: PackedLineInfo; typ: TypeId) {.inline.} = + t.nodes.add Instr(x: toX(Typed, uint32(typ)), info: info) + +proc addSummon*(t: var Tree; info: PackedLineInfo; s: SymId; typ: TypeId; opc = Summon) {.inline.} = + assert opc in {Summon, SummonConst, SummonGlobal, SummonThreadLocal, SummonParam} + let x = prepare(t, info, opc) + t.nodes.add Instr(x: toX(Typed, uint32(typ)), info: info) + t.nodes.add Instr(x: toX(SymDef, uint32(s)), info: info) + patch t, x + +proc addImmediateVal*(t: var Tree; info: PackedLineInfo; x: int) = + assert x >= 0 and x < ((1 shl 32) - OpcodeBits.int) + t.nodes.add Instr(x: toX(ImmediateVal, uint32(x)), info: info) + +proc addPragmaId*(t: var Tree; info: PackedLineInfo; x: PragmaKey) = + t.nodes.add Instr(x: toX(PragmaId, uint32(x)), info: info) + +proc addIntVal*(t: var Tree; integers: var BiTable[int64]; info: PackedLineInfo; typ: TypeId; x: int64) = + buildTyped t, info, NumberConv, typ: + t.nodes.add Instr(x: toX(IntVal, uint32(integers.getOrIncl(x))), info: info) + +proc addStrVal*(t: var Tree; strings: var BiTable[string]; info: PackedLineInfo; s: string) = + t.nodes.add Instr(x: toX(StrVal, uint32(strings.getOrIncl(s))), info: info) + +proc addNilVal*(t: var Tree; info: PackedLineInfo; typ: TypeId) = + buildTyped t, info, NumberConv, typ: + t.nodes.add Instr(x: toX(NilVal, uint32(0)), info: info) + +proc escapeToNimLit(s: string; result: var string) = + result.add '"' + for c in items s: + if c < ' ' or int(c) >= 128: + result.add '\\' + result.addInt int(c) + elif c == '\\': + result.add r"\\" + elif c == '\n': + result.add r"\n" + elif c == '\r': + result.add r"\r" + elif c == '\t': + result.add r"\t" + else: + result.add c + result.add '"' + +proc toString*(t: Tree; pos: NodePos; strings: BiTable[string]; integers: BiTable[int64]; + r: var string; nesting = 0) = + if r.len > 0 and r[r.len-1] notin {' ', '\n', '(', '[', '{'}: + r.add ' ' + + case t[pos].kind + of Nop: r.add "Nop" + of ImmediateVal: + r.add $t[pos].operand + of IntVal: + r.add "IntVal " + r.add $integers[LitId t[pos].operand] + of StrVal: + escapeToNimLit(strings[LitId t[pos].operand], r) + of SymDef: + r.add "SymDef " + r.add $t[pos].operand + of SymUse: + r.add "SymUse " + r.add $t[pos].operand + of PragmaId: + r.add $cast[PragmaKey](t[pos].operand) + of Typed: + r.add "Typed " + r.add $t[pos].operand + of NilVal: + r.add "NilVal" + of Label: + r.add "L" + r.add $t[pos].operand + of Goto, CheckedGoto, LoopLabel, GotoLoop: + r.add $t[pos].kind + r.add ' ' + r.add $t[pos].operand + else: + r.add $t[pos].kind + r.add "{\n" + for i in 0..<(nesting+1)*2: r.add ' ' + for p in sons(t, pos): + toString t, p, strings, integers, r, nesting+1 + r.add "\n" + for i in 0..<nesting*2: r.add ' ' + r.add "}" + +type + Value* = distinct Tree + +proc prepare*(dest: var Value; info: PackedLineInfo; k: Opcode): PatchPos {.inline.} = + assert k in ValueProducing - ValueProducingAtoms + result = prepare(Tree(dest), info, k) + +proc patch*(dest: var Value; pos: PatchPos) {.inline.} = + patch(Tree(dest), pos) + +proc localToValue*(info: PackedLineInfo; s: SymId): Value = + result = Value(Tree()) + Tree(result).addSymUse info, s + +proc hasValue*(v: Value): bool {.inline.} = Tree(v).len > 0 + +proc isEmpty*(v: Value): bool {.inline.} = Tree(v).len == 0 + +proc extractTemp*(v: Value): SymId = + if hasValue(v) and Tree(v)[NodePos 0].kind == SymUse: + result = SymId(Tree(v)[NodePos 0].operand) + else: + result = SymId(-1) + +proc copyTree*(dest: var Tree; src: Value) = copyTree dest, Tree(src) + +proc addImmediateVal*(t: var Value; info: PackedLineInfo; x: int) = + assert x >= 0 and x < ((1 shl 32) - OpcodeBits.int) + Tree(t).nodes.add Instr(x: toX(ImmediateVal, uint32(x)), info: info) + +template build*(tree: var Value; info: PackedLineInfo; kind: Opcode; body: untyped) = + let pos = prepare(Tree(tree), info, kind) + body + patch(tree, pos) + +proc addTyped*(t: var Value; info: PackedLineInfo; typ: TypeId) {.inline.} = + addTyped(Tree(t), info, typ) + +template buildTyped*(tree: var Value; info: PackedLineInfo; kind: Opcode; typ: TypeId; body: untyped) = + let pos = prepare(tree, info, kind) + tree.addTyped info, typ + body + patch(tree, pos) + +proc addStrVal*(t: var Value; strings: var BiTable[string]; info: PackedLineInfo; s: string) = + addStrVal(Tree(t), strings, info, s) + +proc addNilVal*(t: var Value; info: PackedLineInfo; typ: TypeId) = + addNilVal Tree(t), info, typ diff --git a/compiler/nir/nirlineinfos.nim b/compiler/nir/nirlineinfos.nim new file mode 100644 index 000000000..4e86f619e --- /dev/null +++ b/compiler/nir/nirlineinfos.nim @@ -0,0 +1,78 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# For the line information we use 32 bits. They are used as follows: +# Bit 0 (AsideBit): If we have inline line information or not. If not, the +# remaining 31 bits are used as an index into a seq[(LitId, int, int)]. +# +# We use 10 bits for the "file ID", this means a program can consist of as much +# as 1024 different files. (If it uses more files than that, the overflow bit +# would be set.) +# This means we have 21 bits left to encode the (line, col) pair. We use 7 bits for the column +# so 128 is the limit and 14 bits for the line number. +# The packed representation supports files with up to 16384 lines. +# Keep in mind that whenever any limit is reached the AsideBit is set and the real line +# information is kept in a side channel. + +import std / assertions + +const + AsideBit = 1 + FileBits = 10 + LineBits = 14 + ColBits = 7 + FileMax = (1 shl FileBits) - 1 + LineMax = (1 shl LineBits) - 1 + ColMax = (1 shl ColBits) - 1 + +static: + assert AsideBit + FileBits + LineBits + ColBits == 32 + +import .. / ic / bitabs # for LitId + +type + PackedLineInfo* = distinct uint32 + + LineInfoManager* = object + aside*: seq[(LitId, int32, int32)] + +proc pack*(m: var LineInfoManager; file: LitId; line, col: int32): PackedLineInfo = + if file.uint32 <= FileMax.uint32 and line <= LineMax and col <= ColMax: + let col = if col < 0'i32: 0'u32 else: col.uint32 + let line = if line < 0'i32: 0'u32 else: line.uint32 + # use inline representation: + result = PackedLineInfo((file.uint32 shl 1'u32) or (line shl uint32(AsideBit + FileBits)) or + (col shl uint32(AsideBit + FileBits + LineBits))) + else: + result = PackedLineInfo((m.aside.len shl 1) or AsideBit) + m.aside.add (file, line, col) + +proc unpack*(m: LineInfoManager; i: PackedLineInfo): (LitId, int32, int32) = + let i = i.uint32 + if (i and 1'u32) == 0'u32: + # inline representation: + result = (LitId((i shr 1'u32) and FileMax.uint32), + int32((i shr uint32(AsideBit + FileBits)) and LineMax.uint32), + int32((i shr uint32(AsideBit + FileBits + LineBits)) and ColMax.uint32)) + else: + result = m.aside[int(i shr 1'u32)] + +proc getFileId*(m: LineInfoManager; i: PackedLineInfo): LitId = + result = unpack(m, i)[0] + +when isMainModule: + var m = LineInfoManager(aside: @[]) + for i in 0'i32..<16388'i32: + for col in 0'i32..<100'i32: + let packed = pack(m, LitId(1023), i, col) + let u = unpack(m, packed) + assert u[0] == LitId(1023) + assert u[1] == i + assert u[2] == col + echo m.aside.len diff --git a/compiler/nir/nirslots.nim b/compiler/nir/nirslots.nim new file mode 100644 index 000000000..256c25a19 --- /dev/null +++ b/compiler/nir/nirslots.nim @@ -0,0 +1,105 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Management of slots. Similar to "register allocation" +## in lower level languages. + +import std / [assertions, tables] +import nirtypes, nirinsts + +type + SlotManagerFlag* = enum + ReuseTemps, + ReuseVars + SlotKind* = enum + Temp, Perm + SlotManager* = object # "register allocator" + live: Table[SymId, (SlotKind, TypeId)] + dead: Table[TypeId, seq[SymId]] + flags: set[SlotManagerFlag] + inScope: seq[SymId] + locGen: ref int + +proc initSlotManager*(flags: set[SlotManagerFlag]; generator: ref int): SlotManager {.inline.} = + SlotManager(flags: flags, locGen: generator) + +proc allocRaw(m: var SlotManager; t: TypeId; f: SlotManagerFlag; k: SlotKind): SymId {.inline.} = + if f in m.flags and m.dead.hasKey(t) and m.dead[t].len > 0: + result = m.dead[t].pop() + else: + result = SymId(m.locGen[]) + inc m.locGen[] + m.inScope.add result + m.live[result] = (k, t) + +proc allocTemp*(m: var SlotManager; t: TypeId): SymId {.inline.} = + result = allocRaw(m, t, ReuseTemps, Temp) + +proc allocVar*(m: var SlotManager; t: TypeId): SymId {.inline.} = + result = allocRaw(m, t, ReuseVars, Perm) + +proc freeLoc*(m: var SlotManager; s: SymId) = + let t = m.live.getOrDefault(s) + assert t[1].int != 0 + m.live.del s + m.dead.mgetOrPut(t[1], @[]).add s + +proc freeTemp*(m: var SlotManager; s: SymId) = + let t = m.live.getOrDefault(s) + if t[1].int != 0 and t[0] == Temp: + m.live.del s + m.dead.mgetOrPut(t[1], @[]).add s + +iterator stillAlive*(m: SlotManager): (SymId, TypeId) = + for k, v in pairs(m.live): + yield (k, v[1]) + +proc getType*(m: SlotManager; s: SymId): TypeId {.inline.} = m.live[s][1] + +proc openScope*(m: var SlotManager) = + m.inScope.add SymId(-1) # add marker + +proc closeScope*(m: var SlotManager) = + var i = m.inScope.len - 1 + while i >= 0: + if m.inScope[i] == SymId(-1): + m.inScope.setLen i + break + dec i + +when isMainModule: + var m = initSlotManager({ReuseTemps}, new(int)) + + var g = initTypeGraph() + + let a = g.openType ArrayTy + g.addBuiltinType Int8Id + g.addArrayLen 5'u64 + let finalArrayType = sealType(g, a) + + let obj = g.openType ObjectDecl + g.addName "MyType" + + g.addField "p", finalArrayType + let objB = sealType(g, obj) + + let x = m.allocTemp(objB) + assert x.int == 0 + + let y = m.allocTemp(objB) + assert y.int == 1 + + let z = m.allocTemp(Int8Id) + assert z.int == 2 + + m.freeLoc y + let y2 = m.allocTemp(objB) + assert y2.int == 1 + + diff --git a/compiler/nir/nirtypes.nim b/compiler/nir/nirtypes.nim new file mode 100644 index 000000000..d989397a6 --- /dev/null +++ b/compiler/nir/nirtypes.nim @@ -0,0 +1,347 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Type system for NIR. Close to C's type system but without its quirks. + +import std / [assertions, hashes] +import .. / ic / bitabs + +type + NirTypeKind* = enum + VoidTy, IntTy, UIntTy, FloatTy, BoolTy, CharTy, NameVal, IntVal, + AnnotationVal, + VarargsTy, # the `...` in a C prototype; also the last "atom" + APtrTy, # pointer to aliasable memory + UPtrTy, # pointer to unique/unaliasable memory + AArrayPtrTy, # pointer to array of aliasable memory + UArrayPtrTy, # pointer to array of unique/unaliasable memory + ArrayTy, + LastArrayTy, # array of unspecified size as a last field inside an object + ObjectTy, + UnionTy, + ProcTy, + ObjectDecl, + UnionDecl, + FieldDecl + +const + TypeKindBits = 8'u32 + TypeKindMask = (1'u32 shl TypeKindBits) - 1'u32 + +type + TypeNode* = object # 4 bytes + x: uint32 + +template kind*(n: TypeNode): NirTypeKind = NirTypeKind(n.x and TypeKindMask) +template operand(n: TypeNode): uint32 = (n.x shr TypeKindBits) + +template toX(k: NirTypeKind; operand: uint32): uint32 = + uint32(k) or (operand shl TypeKindBits) + +template toX(k: NirTypeKind; operand: LitId): uint32 = + uint32(k) or (operand.uint32 shl TypeKindBits) + +type + TypeId* = distinct int + +proc `==`*(a, b: TypeId): bool {.borrow.} +proc hash*(a: TypeId): Hash {.borrow.} + +type + TypeGraph* = object + nodes: seq[TypeNode] + names: BiTable[string] + numbers: BiTable[uint64] + +const + VoidId* = TypeId 0 + Bool8Id* = TypeId 1 + Char8Id* = TypeId 2 + Int8Id* = TypeId 3 + Int16Id* = TypeId 4 + Int32Id* = TypeId 5 + Int64Id* = TypeId 6 + UInt8Id* = TypeId 7 + UInt16Id* = TypeId 8 + UInt32Id* = TypeId 9 + UInt64Id* = TypeId 10 + Float32Id* = TypeId 11 + Float64Id* = TypeId 12 + VoidPtrId* = TypeId 13 + LastBuiltinId* = 13 + +proc initTypeGraph*(): TypeGraph = + result = TypeGraph(nodes: @[ + TypeNode(x: toX(VoidTy, 0'u32)), + TypeNode(x: toX(BoolTy, 8'u32)), + TypeNode(x: toX(CharTy, 8'u32)), + TypeNode(x: toX(IntTy, 8'u32)), + TypeNode(x: toX(IntTy, 16'u32)), + TypeNode(x: toX(IntTy, 32'u32)), + TypeNode(x: toX(IntTy, 64'u32)), + TypeNode(x: toX(UIntTy, 8'u32)), + TypeNode(x: toX(UIntTy, 16'u32)), + TypeNode(x: toX(UIntTy, 32'u32)), + TypeNode(x: toX(UIntTy, 64'u32)), + TypeNode(x: toX(FloatTy, 32'u32)), + TypeNode(x: toX(FloatTy, 64'u32)), + TypeNode(x: toX(APtrTy, 2'u32)), + TypeNode(x: toX(VoidTy, 0'u32)) + ]) + assert result.nodes.len == LastBuiltinId+2 + +type + TypePatchPos* = distinct int + +const + InvalidTypePatchPos* = TypePatchPos(-1) + LastAtomicValue = VarargsTy + +proc isValid(p: TypePatchPos): bool {.inline.} = p.int != -1 + +proc prepare(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos = + result = TypePatchPos tree.nodes.len + tree.nodes.add TypeNode(x: toX(kind, 1'u32)) + +proc isAtom(tree: TypeGraph; pos: int): bool {.inline.} = tree.nodes[pos].kind <= LastAtomicValue +proc isAtom(tree: TypeGraph; pos: TypeId): bool {.inline.} = tree.nodes[pos.int].kind <= LastAtomicValue + +proc patch(tree: var TypeGraph; pos: TypePatchPos) = + let pos = pos.int + let k = tree.nodes[pos].kind + assert k > LastAtomicValue + let distance = int32(tree.nodes.len - pos) + assert distance > 0 + tree.nodes[pos].x = toX(k, cast[uint32](distance)) + +proc len*(tree: TypeGraph): int {.inline.} = tree.nodes.len + +template rawSpan(n: TypeNode): int = int(operand(n)) + +proc nextChild(tree: TypeGraph; pos: var int) {.inline.} = + if tree.nodes[pos].kind > LastAtomicValue: + assert tree.nodes[pos].operand > 0'u32 + inc pos, tree.nodes[pos].rawSpan + else: + inc pos + +iterator sons*(tree: TypeGraph; n: TypeId): TypeId = + var pos = n.int + assert tree.nodes[pos].kind > LastAtomicValue + let last = pos + tree.nodes[pos].rawSpan + inc pos + while pos < last: + yield TypeId pos + nextChild tree, pos + +template `[]`*(t: TypeGraph; n: TypeId): TypeNode = t.nodes[n.int] + +proc elementType*(tree: TypeGraph; n: TypeId): TypeId {.inline.} = + assert tree[n].kind in {APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy, ArrayTy, LastArrayTy} + result = TypeId(n.int+1) + +proc kind*(tree: TypeGraph; n: TypeId): NirTypeKind {.inline.} = tree[n].kind + +proc span(tree: TypeGraph; pos: int): int {.inline.} = + if tree.nodes[pos].kind <= LastAtomicValue: 1 else: int(tree.nodes[pos].operand) + +proc sons2(tree: TypeGraph; n: TypeId): (TypeId, TypeId) = + assert(not isAtom(tree, n.int)) + let a = n.int+1 + let b = a + span(tree, a) + result = (TypeId a, TypeId b) + +proc sons3(tree: TypeGraph; n: TypeId): (TypeId, TypeId, TypeId) = + assert(not isAtom(tree, n.int)) + let a = n.int+1 + let b = a + span(tree, a) + let c = b + span(tree, b) + result = (TypeId a, TypeId b, TypeId c) + +proc arrayLen*(tree: TypeGraph; n: TypeId): BiggestUInt = + assert tree[n].kind == ArrayTy + result = tree.numbers[LitId tree[n].operand] + +proc openType*(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos = + assert kind in {APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy, + ArrayTy, LastArrayTy, ProcTy, ObjectDecl, UnionDecl, + FieldDecl} + result = prepare(tree, kind) + +proc sealType*(tree: var TypeGraph; p: TypePatchPos): TypeId = + # TODO: Search for an existing instance of this type in + # order to reduce memory consumption. + result = TypeId(p) + patch tree, p + +proc nominalType*(tree: var TypeGraph; kind: NirTypeKind; name: string): TypeId = + assert kind in {ObjectTy, UnionTy} + result = TypeId tree.nodes.len + tree.nodes.add TypeNode(x: toX(kind, tree.names.getOrIncl(name))) + +proc addNominalType*(tree: var TypeGraph; kind: NirTypeKind; name: string) = + assert kind in {ObjectTy, UnionTy} + tree.nodes.add TypeNode(x: toX(kind, tree.names.getOrIncl(name))) + +proc addVarargs*(tree: var TypeGraph) = + tree.nodes.add TypeNode(x: toX(VarargsTy, 0'u32)) + +proc getFloat128Type*(tree: var TypeGraph): TypeId = + result = TypeId tree.nodes.len + tree.nodes.add TypeNode(x: toX(FloatTy, 128'u32)) + +proc addBuiltinType*(g: var TypeGraph; id: TypeId) = + g.nodes.add g[id] + +template firstSon(n: TypeId): TypeId = TypeId(n.int+1) + +proc addType*(g: var TypeGraph; t: TypeId) = + # We cannot simply copy `*Decl` nodes. We have to introduce `*Ty` nodes instead: + if g[t].kind in {ObjectDecl, UnionDecl}: + assert g[t.firstSon].kind == NameVal + let name = LitId g[t.firstSon].operand + if g[t].kind == ObjectDecl: + g.nodes.add TypeNode(x: toX(ObjectTy, name)) + else: + g.nodes.add TypeNode(x: toX(UnionTy, name)) + else: + let pos = t.int + let L = span(g, pos) + let d = g.nodes.len + g.nodes.setLen(d + L) + assert L > 0 + for i in 0..<L: + g.nodes[d+i] = g.nodes[pos+i] + +proc addArrayLen*(g: var TypeGraph; len: uint64) = + g.nodes.add TypeNode(x: toX(IntVal, g.numbers.getOrIncl(len))) + +proc addName*(g: var TypeGraph; name: string) = + g.nodes.add TypeNode(x: toX(NameVal, g.names.getOrIncl(name))) + +proc addAnnotation*(g: var TypeGraph; name: string) = + g.nodes.add TypeNode(x: toX(NameVal, g.names.getOrIncl(name))) + +proc addField*(g: var TypeGraph; name: string; typ: TypeId) = + let f = g.openType FieldDecl + g.addType typ + g.addName name + discard sealType(g, f) + +proc ptrTypeOf*(g: var TypeGraph; t: TypeId): TypeId = + let f = g.openType APtrTy + g.addType t + result = sealType(g, f) + +proc toString*(dest: var string; g: TypeGraph; i: TypeId) = + case g[i].kind + of VoidTy: dest.add "void" + of IntTy: + dest.add "i" + dest.addInt g[i].operand + of UIntTy: + dest.add "u" + dest.addInt g[i].operand + of FloatTy: + dest.add "f" + dest.addInt g[i].operand + of BoolTy: + dest.add "b" + dest.addInt g[i].operand + of CharTy: + dest.add "c" + dest.addInt g[i].operand + of NameVal, AnnotationVal: + dest.add g.names[LitId g[i].operand] + of IntVal: + dest.add $g.numbers[LitId g[i].operand] + of VarargsTy: + dest.add "..." + of APtrTy: + dest.add "aptr[" + toString(dest, g, g.elementType(i)) + dest.add "]" + of UPtrTy: + dest.add "uptr[" + toString(dest, g, g.elementType(i)) + dest.add "]" + of AArrayPtrTy: + dest.add "aArrayPtr[" + toString(dest, g, g.elementType(i)) + dest.add "]" + of UArrayPtrTy: + dest.add "uArrayPtr[" + toString(dest, g, g.elementType(i)) + dest.add "]" + of ArrayTy: + dest.add "Array[" + let (elems, len) = g.sons2(i) + toString(dest, g, elems) + dest.add ", " + toString(dest, g, len) + dest.add "]" + of LastArrayTy: + # array of unspecified size as a last field inside an object + dest.add "LastArrayTy[" + toString(dest, g, g.elementType(i)) + dest.add "]" + of ObjectTy: + dest.add "object " + dest.add g.names[LitId g[i].operand] + of UnionTy: + dest.add "union " + dest.add g.names[LitId g[i].operand] + of ProcTy: + dest.add "proc[" + for t in sons(g, i): toString(dest, g, t) + dest.add "]" + of ObjectDecl: + dest.add "object[" + for t in sons(g, i): + toString(dest, g, t) + dest.add '\n' + dest.add "]" + of UnionDecl: + dest.add "union[" + for t in sons(g, i): + toString(dest, g, t) + dest.add '\n' + dest.add "]" + of FieldDecl: + let (typ, name) = g.sons2(i) + toString(dest, g, typ) + dest.add ' ' + toString(dest, g, name) + +proc toString*(dest: var string; g: TypeGraph) = + var i = 0 + while i < g.len: + toString(dest, g, TypeId i) + dest.add '\n' + nextChild g, i + +proc `$`(g: TypeGraph): string = + result = "" + toString(result, g) + +when isMainModule: + var g = initTypeGraph() + + let a = g.openType ArrayTy + g.addBuiltinType Int8Id + g.addArrayLen 5'u64 + let finalArrayType = sealType(g, a) + + let obj = g.openType ObjectDecl + g.nodes.add TypeNode(x: toX(NameVal, g.names.getOrIncl("MyType"))) + + g.addField "p", finalArrayType + discard sealType(g, obj) + + echo g diff --git a/compiler/nir/types2ir.nim b/compiler/nir/types2ir.nim new file mode 100644 index 000000000..6d163c6c7 --- /dev/null +++ b/compiler/nir/types2ir.nim @@ -0,0 +1,466 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +import std / [assertions, tables, sets] +import ".." / [ast, types, options, sighashes, modulegraphs] +import nirtypes + +type + TypesCon* = object + processed: Table[ItemId, TypeId] + recursionCheck: HashSet[ItemId] + g*: TypeGraph + conf: ConfigRef + +proc initTypesCon*(conf: ConfigRef): TypesCon = + TypesCon(g: initTypeGraph(), conf: conf) + +proc mangle(c: var TypesCon; t: PType): string = + result = $sighashes.hashType(t, c.conf) + +template cached(c: var TypesCon; t: PType; body: untyped) = + result = c.processed.getOrDefault(t.itemId) + if result.int == 0: + body + c.processed[t.itemId] = result + +proc typeToIr*(c: var TypesCon; t: PType): TypeId + +proc collectFieldTypes(c: var TypesCon; n: PNode; dest: var Table[ItemId, TypeId]) = + case n.kind + of nkRecList: + for i in 0..<n.len: + collectFieldTypes(c, n[i], dest) + of nkRecCase: + assert(n[0].kind == nkSym) + collectFieldTypes(c, n[0], dest) + for i in 1..<n.len: + case n[i].kind + of nkOfBranch, nkElse: + collectFieldTypes c, lastSon(n[i]), dest + else: discard + of nkSym: + dest[n.sym.itemId] = typeToIr(c, n.sym.typ) + else: + assert false, "unknown node kind: " & $n.kind + +proc objectToIr(c: var TypesCon; n: PNode; fieldTypes: Table[ItemId, TypeId]; unionId: var int) = + case n.kind + of nkRecList: + for i in 0..<n.len: + objectToIr(c, n[i], fieldTypes, unionId) + of nkRecCase: + assert(n[0].kind == nkSym) + objectToIr(c, n[0], fieldTypes, unionId) + let u = openType(c.g, UnionDecl) + c.g.addName "u_" & $unionId + inc unionId + for i in 1..<n.len: + case n[i].kind + of nkOfBranch, nkElse: + let subObj = openType(c.g, ObjectDecl) + c.g.addName "uo_" & $unionId & "_" & $i + objectToIr c, lastSon(n[i]), fieldTypes, unionId + discard sealType(c.g, subObj) + else: discard + discard sealType(c.g, u) + of nkSym: + c.g.addField n.sym.name.s & "_" & $n.sym.position, fieldTypes[n.sym.itemId] + else: + assert false, "unknown node kind: " & $n.kind + +proc objectToIr(c: var TypesCon; t: PType): TypeId = + if t[0] != nil: + # ensure we emitted the base type: + discard typeToIr(c, t[0]) + + var unionId = 0 + var fieldTypes = initTable[ItemId, TypeId]() + collectFieldTypes c, t.n, fieldTypes + let obj = openType(c.g, ObjectDecl) + c.g.addName mangle(c, t) + if t[0] != nil: + c.g.addNominalType(ObjectTy, mangle(c, t[0])) + else: + c.g.addBuiltinType VoidId # object does not inherit + if not lacksMTypeField(t): + let f2 = c.g.openType FieldDecl + let voidPtr = openType(c.g, APtrTy) + c.g.addBuiltinType(VoidId) + discard sealType(c.g, voidPtr) + c.g.addName "m_type" + discard sealType(c.g, f2) # FieldDecl + + objectToIr c, t.n, fieldTypes, unionId + result = sealType(c.g, obj) + +proc objectHeaderToIr(c: var TypesCon; t: PType): TypeId = + result = c.g.nominalType(ObjectTy, mangle(c, t)) + +proc tupleToIr(c: var TypesCon; t: PType): TypeId = + var fieldTypes = newSeq[TypeId](t.len) + for i in 0..<t.len: + fieldTypes[i] = typeToIr(c, t[i]) + let obj = openType(c.g, ObjectDecl) + c.g.addName mangle(c, t) + for i in 0..<t.len: + c.g.addField "f_" & $i, fieldTypes[i] + result = sealType(c.g, obj) + +proc procToIr(c: var TypesCon; t: PType; addEnv = false): TypeId = + var fieldTypes = newSeq[TypeId](0) + for i in 0..<t.len: + if t[i] == nil or not isCompileTimeOnly(t[i]): + fieldTypes.add typeToIr(c, t[i]) + let obj = openType(c.g, ProcTy) + + case t.callConv + of ccNimCall, ccFastCall, ccClosure: c.g.addAnnotation "__fastcall" + of ccStdCall: c.g.addAnnotation "__stdcall" + of ccCDecl: c.g.addAnnotation "__cdecl" + of ccSafeCall: c.g.addAnnotation "__safecall" + of ccSysCall: c.g.addAnnotation "__syscall" + of ccInline: c.g.addAnnotation "__inline" + of ccNoInline: c.g.addAnnotation "__noinline" + of ccThisCall: c.g.addAnnotation "__thiscall" + of ccNoConvention: c.g.addAnnotation "" + + for i in 0..<fieldTypes.len: + c.g.addType fieldTypes[i] + + if addEnv: + let a = openType(c.g, APtrTy) + c.g.addBuiltinType(VoidId) + discard sealType(c.g, a) + + if tfVarargs in t.flags: + c.g.addVarargs() + result = sealType(c.g, obj) + +proc nativeInt(c: TypesCon): TypeId = + case c.conf.target.intSize + of 2: result = Int16Id + of 4: result = Int32Id + else: result = Int64Id + +proc openArrayPayloadType*(c: var TypesCon; t: PType): TypeId = + let e = lastSon(t) + let elementType = typeToIr(c, e) + let arr = c.g.openType AArrayPtrTy + c.g.addType elementType + result = sealType(c.g, arr) # LastArrayTy + +proc openArrayToIr(c: var TypesCon; t: PType): TypeId = + # object (a: ArrayPtr[T], len: int) + let e = lastSon(t) + let mangledBase = mangle(c, e) + let typeName = "NimOpenArray" & mangledBase + + let elementType = typeToIr(c, e) + #assert elementType.int >= 0, typeToString(t) + + let p = openType(c.g, ObjectDecl) + c.g.addName typeName + + let f = c.g.openType FieldDecl + let arr = c.g.openType AArrayPtrTy + c.g.addType elementType + discard sealType(c.g, arr) # LastArrayTy + c.g.addName "data" + discard sealType(c.g, f) # FieldDecl + + c.g.addField "len", c.nativeInt + + result = sealType(c.g, p) # ObjectDecl + +proc strPayloadType(c: var TypesCon): string = + result = "NimStrPayload" + let p = openType(c.g, ObjectDecl) + c.g.addName result + c.g.addField "cap", c.nativeInt + + let f = c.g.openType FieldDecl + let arr = c.g.openType LastArrayTy + c.g.addBuiltinType Char8Id + discard sealType(c.g, arr) # LastArrayTy + c.g.addName "data" + discard sealType(c.g, f) # FieldDecl + + discard sealType(c.g, p) + +proc strPayloadPtrType*(c: var TypesCon): TypeId = + let mangled = strPayloadType(c) + let ffp = c.g.openType APtrTy + c.g.addNominalType ObjectTy, mangled + result = sealType(c.g, ffp) # APtrTy + +proc stringToIr(c: var TypesCon): TypeId = + #[ + + NimStrPayload = object + cap: int + data: UncheckedArray[char] + + NimStringV2 = object + len: int + p: ptr NimStrPayload + + ]# + let payload = strPayloadType(c) + + let str = openType(c.g, ObjectDecl) + c.g.addName "NimStringV2" + c.g.addField "len", c.nativeInt + + let fp = c.g.openType FieldDecl + let ffp = c.g.openType APtrTy + c.g.addNominalType ObjectTy, "NimStrPayload" + discard sealType(c.g, ffp) # APtrTy + c.g.addName "p" + discard sealType(c.g, fp) # FieldDecl + + result = sealType(c.g, str) # ObjectDecl + +proc seqPayloadType(c: var TypesCon; t: PType): string = + #[ + NimSeqPayload[T] = object + cap: int + data: UncheckedArray[T] + ]# + let e = lastSon(t) + result = mangle(c, e) + let payloadName = "NimSeqPayload" & result + + let elementType = typeToIr(c, e) + + let p = openType(c.g, ObjectDecl) + c.g.addName payloadName + c.g.addField "cap", c.nativeInt + + let f = c.g.openType FieldDecl + let arr = c.g.openType LastArrayTy + c.g.addType elementType + discard sealType(c.g, arr) # LastArrayTy + c.g.addName "data" + discard sealType(c.g, f) # FieldDecl + discard sealType(c.g, p) + +proc seqPayloadPtrType*(c: var TypesCon; t: PType): TypeId = + let mangledBase = seqPayloadType(c, t) + let ffp = c.g.openType APtrTy + c.g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase + result = sealType(c.g, ffp) # APtrTy + +proc seqToIr(c: var TypesCon; t: PType): TypeId = + #[ + NimSeqV2*[T] = object + len: int + p: ptr NimSeqPayload[T] + ]# + let mangledBase = seqPayloadType(c, t) + + let sq = openType(c.g, ObjectDecl) + c.g.addName "NimSeqV2" & mangledBase + c.g.addField "len", c.nativeInt + + let fp = c.g.openType FieldDecl + let ffp = c.g.openType APtrTy + c.g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase + discard sealType(c.g, ffp) # APtrTy + c.g.addName "p" + discard sealType(c.g, fp) # FieldDecl + + result = sealType(c.g, sq) # ObjectDecl + + +proc closureToIr(c: var TypesCon; t: PType): TypeId = + # struct {fn(args, void* env), env} + # typedef struct {$n" & + # "N_NIMCALL_PTR($2, ClP_0) $3;$n" & + # "void* ClE_0;$n} $1;$n" + let mangledBase = mangle(c, t) + let typeName = "NimClosure" & mangledBase + + let procType = procToIr(c, t, addEnv=true) + + let p = openType(c.g, ObjectDecl) + c.g.addName typeName + + let f = c.g.openType FieldDecl + c.g.addType procType + c.g.addName "ClP_0" + discard sealType(c.g, f) # FieldDecl + + let f2 = c.g.openType FieldDecl + let voidPtr = openType(c.g, APtrTy) + c.g.addBuiltinType(VoidId) + discard sealType(c.g, voidPtr) + + c.g.addName "ClE_0" + discard sealType(c.g, f2) # FieldDecl + + result = sealType(c.g, p) # ObjectDecl + +proc bitsetBasetype*(c: var TypesCon; t: PType): TypeId = + let s = int(getSize(c.conf, t)) + case s + of 1: result = UInt8Id + of 2: result = UInt16Id + of 4: result = UInt32Id + of 8: result = UInt64Id + else: result = UInt8Id + +proc typeToIr*(c: var TypesCon; t: PType): TypeId = + if t == nil: return VoidId + case t.kind + of tyInt: + case int(getSize(c.conf, t)) + of 2: result = Int16Id + of 4: result = Int32Id + else: result = Int64Id + of tyInt8: result = Int8Id + of tyInt16: result = Int16Id + of tyInt32: result = Int32Id + of tyInt64: result = Int64Id + of tyFloat: + case int(getSize(c.conf, t)) + of 4: result = Float32Id + else: result = Float64Id + of tyFloat32: result = Float32Id + of tyFloat64: result = Float64Id + of tyFloat128: result = getFloat128Type(c.g) + of tyUInt: + case int(getSize(c.conf, t)) + of 2: result = UInt16Id + of 4: result = UInt32Id + else: result = UInt64Id + of tyUInt8: result = UInt8Id + of tyUInt16: result = UInt16Id + of tyUInt32: result = UInt32Id + of tyUInt64: result = UInt64Id + of tyBool: result = Bool8Id + of tyChar: result = Char8Id + of tyVoid: result = VoidId + of tySink, tyGenericInst, tyDistinct, tyAlias, tyOwned, tyRange: + result = typeToIr(c, t.lastSon) + of tyEnum: + if firstOrd(c.conf, t) < 0: + result = Int32Id + else: + case int(getSize(c.conf, t)) + of 1: result = UInt8Id + of 2: result = UInt16Id + of 4: result = Int32Id + of 8: result = Int64Id + else: result = Int32Id + of tyOrdinal, tyGenericBody, tyGenericParam, tyInferred, tyStatic: + if t.len > 0: + result = typeToIr(c, t.lastSon) + else: + result = TypeId(-1) + of tyFromExpr: + if t.n != nil and t.n.typ != nil: + result = typeToIr(c, t.n.typ) + else: + result = TypeId(-1) + of tyArray: + cached(c, t): + var n = toInt64(lengthOrd(c.conf, t)) + if n <= 0: n = 1 # make an array of at least one element + let elemType = typeToIr(c, t[1]) + let a = openType(c.g, ArrayTy) + c.g.addType(elemType) + c.g.addArrayLen uint64(n) + result = sealType(c.g, a) + of tyPtr, tyRef: + cached(c, t): + let e = t.lastSon + if e.kind == tyUncheckedArray: + let elemType = typeToIr(c, e.lastSon) + let a = openType(c.g, AArrayPtrTy) + c.g.addType(elemType) + result = sealType(c.g, a) + else: + let elemType = typeToIr(c, t.lastSon) + let a = openType(c.g, APtrTy) + c.g.addType(elemType) + result = sealType(c.g, a) + of tyVar, tyLent: + cached(c, t): + let e = t.lastSon + if e.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}: + # skip the modifier, `var openArray` is a (ptr, len) pair too: + result = typeToIr(c, e) + else: + let elemType = typeToIr(c, e) + let a = openType(c.g, APtrTy) + c.g.addType(elemType) + result = sealType(c.g, a) + of tySet: + let s = int(getSize(c.conf, t)) + case s + of 1: result = UInt8Id + of 2: result = UInt16Id + of 4: result = UInt32Id + of 8: result = UInt64Id + else: + # array[U8, s] + cached(c, t): + let a = openType(c.g, ArrayTy) + c.g.addType(UInt8Id) + c.g.addArrayLen uint64(s) + result = sealType(c.g, a) + of tyPointer: + let a = openType(c.g, APtrTy) + c.g.addBuiltinType(VoidId) + result = sealType(c.g, a) + of tyObject: + # Objects are special as they can be recursive in Nim. This is easily solvable. + # We check if we are already "processing" t. If so, we produce `ObjectTy` + # instead of `ObjectDecl`. + cached(c, t): + if not c.recursionCheck.containsOrIncl(t.itemId): + result = objectToIr(c, t) + else: + result = objectHeaderToIr(c, t) + of tyTuple: + cached(c, t): + result = tupleToIr(c, t) + of tyProc: + cached(c, t): + if t.callConv == ccClosure: + result = closureToIr(c, t) + else: + result = procToIr(c, t) + of tyVarargs, tyOpenArray: + cached(c, t): + result = openArrayToIr(c, t) + of tyString: + cached(c, t): + result = stringToIr(c) + of tySequence: + cached(c, t): + result = seqToIr(c, t) + of tyCstring: + cached(c, t): + let a = openType(c.g, AArrayPtrTy) + c.g.addBuiltinType Char8Id + result = sealType(c.g, a) + of tyUncheckedArray: + # We already handled the `ptr UncheckedArray` in a special way. + cached(c, t): + let elemType = typeToIr(c, t.lastSon) + let a = openType(c.g, LastArrayTy) + c.g.addType(elemType) + result = sealType(c.g, a) + of tyNone, tyEmpty, tyUntyped, tyTyped, tyTypeDesc, + tyNil, tyGenericInvocation, tyProxy, tyBuiltInTypeClass, + tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass, + tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward: + result = TypeId(-1) diff --git a/compiler/pipelines.nim b/compiler/pipelines.nim index 8517cd942..e9ee1ee8b 100644 --- a/compiler/pipelines.nim +++ b/compiler/pipelines.nim @@ -11,7 +11,7 @@ when not defined(leanCompiler): import std/[syncio, objectdollar, assertions, tables, strutils] import renderer import ic/replayer - +import nir/nir proc setPipeLinePass*(graph: ModuleGraph; pass: PipelinePass) = graph.pipelinePass = pass @@ -43,6 +43,8 @@ proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext): result = nil of EvalPass, InterpreterPass: result = interpreterCode(bModule, semNode) + of NirReplPass: + result = runCode(bModule, semNode) of NonePass: raiseAssert "use setPipeLinePass to set a proper PipelinePass" @@ -112,6 +114,8 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator nil of EvalPass, InterpreterPass: setupEvalGen(graph, module, idgen) + of NirReplPass: + setupNirReplGen(graph, module, idgen) of GenDependPass: setupDependPass(graph, module, idgen) of Docgen2Pass: @@ -197,6 +201,8 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator discard finalJSCodeGen(graph, bModule, finalNode) of EvalPass, InterpreterPass: discard interpreterCode(bModule, finalNode) + of NirReplPass: + discard runCode(bModule, finalNode) of SemPass, GenDependPass: discard of Docgen2Pass, Docgen2TexPass: diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 2d366d8fc..ebc8be0c3 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -68,9 +68,7 @@ proc locateFieldInInitExpr(c: PContext, field: PSym, initExpr: PNode): PNode = let assignment = initExpr[i] if assignment.kind != nkExprColonExpr: invalidObjConstr(c, assignment) - continue - - if fieldId == considerQuotedIdent(c, assignment[0]).id: + elif fieldId == considerQuotedIdent(c, assignment[0]).id: return assignment proc semConstrField(c: PContext, flags: TExprFlags, @@ -125,7 +123,7 @@ proc pickCaseBranch(caseExpr, matched: PNode): PNode = return caseExpr[i] if endsWithElse: - return caseExpr[^1] + result = caseExpr[^1] else: result = nil @@ -138,8 +136,8 @@ iterator directFieldsInRecList(recList: PNode): PNode = else: doAssert recList.kind == nkRecList for field in recList: - if field.kind != nkSym: continue - yield field + if field.kind == nkSym: + yield field template quoteStr(s: string): string = "'" & s & "'" @@ -416,8 +414,8 @@ proc semConstructTypeAux(c: PContext, if status in {initPartial, initNone, initUnknown}: discard collectMissingFields(c, t.n, constrCtx, result.defaults) let base = t[0] - if base == nil or base.id == t.id or - base.kind in { tyRef, tyPtr } and base[0].id == t.id: + if base == nil or base.id == t.id or + base.kind in {tyRef, tyPtr} and base[0].id == t.id: break t = skipTypes(base, skipPtrs) if t.kind != tyObject: @@ -463,7 +461,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType if t == nil: return localErrorNode(c, result, "object constructor needs an object type") - + if t.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned, tyRef}).kind != tyObject and expectedType != nil and expectedType.skipTypes({tyGenericInst, diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim index d14f19a8a..41ec3e480 100644 --- a/compiler/semparallel.nim +++ b/compiler/semparallel.nim @@ -25,7 +25,7 @@ import ast, astalgo, idents, lowerings, magicsys, guards, msgs, renderer, types, modulegraphs, options, spawn, lineinfos -from trees import getMagic, isTrue, getRoot +from trees import getMagic, getRoot from strutils import `%` discard """ diff --git a/compiler/trees.nim b/compiler/trees.nim index cc2b0eafd..f038fbc1e 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -198,10 +198,6 @@ proc extractRange*(k: TNodeKind, n: PNode, a, b: int): PNode = result = newNodeI(k, n.info, b-a+1) for i in 0..b-a: result[i] = n[i+a] -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 getRoot*(n: PNode): 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 diff --git a/compiler/vm.nim b/compiler/vm.nim index 579bab600..85c1305e9 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1521,7 +1521,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = frame = frame.next jumpTo = findExceptionHandler(c, frame, raised) - case jumpTo.why: + case jumpTo.why of ExceptionGotoHandler: # Jump to the handler, do nothing when the `finally` block ends. savedPC = -1 diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 03265f5b5..42ce09596 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -321,10 +321,6 @@ 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 genWhile(c: PCtx; n: PNode) = # lab1: # cond, tmp diff --git a/lib/std/formatfloat.nim b/lib/std/formatfloat.nim index 48973aa55..872549c3b 100644 --- a/lib/std/formatfloat.nim +++ b/lib/std/formatfloat.nim @@ -100,22 +100,23 @@ proc addFloatSprintf*(result: var string; x: float) = let n = writeFloatToBufferSprintf(buffer, x) result.addCstringN(cast[cstring](buffer[0].addr), n) -proc nimFloatToString(a: float): cstring = - ## ensures the result doesn't print like an integer, i.e. return 2.0, not 2 - # print `-0.0` properly - asm """ - function nimOnlyDigitsOrMinus(n) { - return n.toString().match(/^-?\d+$/); - } - if (Number.isSafeInteger(`a`)) - `result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0" - else { - `result` = `a`+"" - if(nimOnlyDigitsOrMinus(`result`)){ - `result` = `a`+".0" +when defined(js): + proc nimFloatToString(a: float): cstring = + ## ensures the result doesn't print like an integer, i.e. return 2.0, not 2 + # print `-0.0` properly + asm """ + function nimOnlyDigitsOrMinus(n) { + return n.toString().match(/^-?\d+$/); } - } - """ + if (Number.isSafeInteger(`a`)) + `result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0" + else { + `result` = `a`+"" + if(nimOnlyDigitsOrMinus(`result`)){ + `result` = `a`+".0" + } + } + """ proc addFloat*(result: var string; x: float | float32) {.inline.} = ## Converts float to its string representation and appends it to `result`. diff --git a/lib/system.nim b/lib/system.nim index 3e7d84b1d..8d1cda868 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1117,8 +1117,6 @@ template sysAssert(cond: bool, msg: string) = const hasAlloc = (hostOS != "standalone" or not defined(nogc)) and not defined(nimscript) -when notJSnotNims and hostOS != "standalone" and hostOS != "any": - include "system/cgprocs" when notJSnotNims and hasAlloc and not defined(nimSeqsV2): proc addChar(s: NimString, c: char): NimString {.compilerproc, benign.} @@ -1431,7 +1429,6 @@ proc isNil*[T: proc | iterator {.closure.}](x: T): bool {.noSideEffect, magic: " ## Fast check whether `x` is nil. This is sometimes more efficient than ## `== nil`. - when defined(nimHasTopDownInference): # magic used for seq type inference proc `@`*[T](a: openArray[T]): seq[T] {.magic: "OpenArrayToSeq".} = @@ -2200,6 +2197,16 @@ when not defined(js): when notJSnotNims: when hostOS != "standalone" and hostOS != "any": + type + LibHandle = pointer # private type + ProcAddr = pointer # library loading and loading of procs: + + proc nimLoadLibrary(path: string): LibHandle {.compilerproc, hcrInline, nonReloadable.} + proc nimUnloadLibrary(lib: LibHandle) {.compilerproc, hcrInline, nonReloadable.} + proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr {.compilerproc, hcrInline, nonReloadable.} + + proc nimLoadLibraryError(path: string) {.compilerproc, hcrInline, nonReloadable.} + include "system/dyncalls" import system/countbits_impl @@ -2673,7 +2680,7 @@ proc `==`*(x, y: cstring): bool {.magic: "EqCString", noSideEffect, proc strcmp(a, b: cstring): cint {.noSideEffect, importc, header: "<string.h>".} if pointer(x) == pointer(y): result = true - elif x.isNil or y.isNil: result = false + elif pointer(x) == nil or pointer(y) == nil: result = false else: result = strcmp(x, y) == 0 template closureScope*(body: untyped): untyped = diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index a8a4bd767..be931ed14 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -224,7 +224,7 @@ proc rawWriteString*(f: CFilePtr, s: cstring, length: int) {.compilerproc, nonRe proc rawWrite*(f: CFilePtr, s: cstring) {.compilerproc, nonReloadable, inline.} = # we cannot throw an exception here! - discard c_fwrite(s, 1, cast[csize_t](s.len), f) + discard c_fwrite(s, 1, c_strlen(s), f) discard c_fflush(f) {.pop.} diff --git a/lib/system/cgprocs.nim b/lib/system/cgprocs.nim index 9d0d248c3..9a7645f9b 100644 --- a/lib/system/cgprocs.nim +++ b/lib/system/cgprocs.nim @@ -8,13 +8,3 @@ # # Headers for procs that the code generator depends on ("compilerprocs") - -type - LibHandle = pointer # private type - ProcAddr = pointer # library loading and loading of procs: - -proc nimLoadLibrary(path: string): LibHandle {.compilerproc, hcrInline, nonReloadable.} -proc nimUnloadLibrary(lib: LibHandle) {.compilerproc, hcrInline, nonReloadable.} -proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr {.compilerproc, hcrInline, nonReloadable.} - -proc nimLoadLibraryError(path: string) {.compilerproc, hcrInline, nonReloadable.} diff --git a/lib/system/memory.nim b/lib/system/memory.nim index ebda60d8d..156773c48 100644 --- a/lib/system/memory.nim +++ b/lib/system/memory.nim @@ -43,6 +43,7 @@ proc nimCmpMem*(a, b: pointer, size: Natural): cint {.compilerproc, nonReloadabl inc i proc nimCStrLen*(a: cstring): int {.compilerproc, nonReloadable, inline.} = + if a.isNil: return 0 when useLibC: cast[int](c_strlen(a)) else: diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim index 60c63501c..89046253b 100644 --- a/lib/system/strmantle.nim +++ b/lib/system/strmantle.nim @@ -23,6 +23,14 @@ proc cmpStrings(a, b: string): int {.inline, compilerproc.} = else: result = alen - blen +proc leStrings(a, b: string): bool {.inline, compilerproc.} = + # required by upcoming backends (NIR). + cmpStrings(a, b) <= 0 + +proc ltStrings(a, b: string): bool {.inline, compilerproc.} = + # required by upcoming backends (NIR). + cmpStrings(a, b) < 0 + proc eqStrings(a, b: string): bool {.inline, compilerproc.} = let alen = a.len let blen = b.len diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim index abdbcd7c3..e79a2b324 100644 --- a/lib/system/strs_v2.nim +++ b/lib/system/strs_v2.nim @@ -201,6 +201,10 @@ proc prepareMutation*(s: var string) {.inline.} = let s = unsafeAddr s nimPrepareStrMutationV2(cast[ptr NimStringV2](s)[]) +proc nimAddStrV1(s: var NimStringV2; src: NimStringV2) {.compilerRtl, inl.} = + #if (s.p == nil) or (s.len+1 > s.p.cap and not strlitFlag): + prepareAdd(s, src.len) + appendString s, src func capacity*(self: string): int {.inline.} = ## Returns the current capacity of the string. |