diff options
-rw-r--r-- | compiler/ast.nim | 2 | ||||
-rw-r--r-- | compiler/commands.nim | 4 | ||||
-rw-r--r-- | compiler/extccomp.nim | 2 | ||||
-rw-r--r-- | compiler/ic/rodfiles.nim | 7 | ||||
-rw-r--r-- | compiler/main.nim | 26 | ||||
-rw-r--r-- | compiler/modulegraphs.nim | 1 | ||||
-rw-r--r-- | compiler/nim.nim | 2 | ||||
-rw-r--r-- | compiler/nir/ast2ir.nim | 592 | ||||
-rw-r--r-- | compiler/nir/nir.nim | 66 | ||||
-rw-r--r-- | compiler/nir/nirc.nim | 48 | ||||
-rw-r--r-- | compiler/nir/nirinsts.nim | 32 | ||||
-rw-r--r-- | compiler/nir/nirlineinfos.nim | 7 | ||||
-rw-r--r-- | compiler/nir/nirslots.nim | 2 | ||||
-rw-r--r-- | compiler/nir/nirtypes.nim | 143 | ||||
-rw-r--r-- | compiler/nir/nirvm.nim | 467 | ||||
-rw-r--r-- | compiler/nir/stringcases.nim | 200 | ||||
-rw-r--r-- | compiler/nir/types2ir.nim | 139 | ||||
-rw-r--r-- | compiler/options.nim | 13 | ||||
-rw-r--r-- | compiler/pipelines.nim | 6 | ||||
-rw-r--r-- | compiler/sizealignoffsetimpl.nim | 12 | ||||
-rw-r--r-- | lib/system/seqs_v2.nim | 2 | ||||
-rw-r--r-- | lib/system/strs_v2.nim | 3 |
22 files changed, 1389 insertions, 387 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 3017aedcf..8d4511436 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -924,7 +924,7 @@ type # for variables a slot index for the evaluator offset*: int32 # offset of record field disamb*: int32 # disambiguation number; the basic idea is that - # `<procname>__<module>_<disamb>` + # `<procname>__<module>_<disamb>` is unique loc*: TLoc annex*: PLib # additional fields (seldom used, so we use a # reference to another object to save space) diff --git a/compiler/commands.nim b/compiler/commands.nim index f36d82306..0e35cc3e8 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -460,6 +460,7 @@ proc handleCmdInput*(conf: ConfigRef) = proc parseCommand*(command: string): Command = case command.normalize of "c", "cc", "compile", "compiletoc": cmdCompileToC + of "nir": cmdCompileToNir of "cpp", "compiletocpp": cmdCompileToCpp of "objc", "compiletooc": cmdCompileToOC of "js", "compiletojs": cmdCompileToJS @@ -496,6 +497,7 @@ proc setCmd*(conf: ConfigRef, cmd: Command) = of cmdCompileToCpp: conf.backend = backendCpp of cmdCompileToOC: conf.backend = backendObjc of cmdCompileToJS: conf.backend = backendJs + of cmdCompileToNir: conf.backend = backendNir else: discard proc setCommandEarly*(conf: ConfigRef, command: string) = @@ -794,7 +796,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; if conf.backend == backendJs or conf.cmd == cmdNimscript: discard else: processOnOffSwitchG(conf, {optThreads}, arg, pass, info) #if optThreads in conf.globalOptions: conf.setNote(warnGcUnsafe) - of "tlsemulation": + of "tlsemulation": processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info) if optTlsEmulation in conf.globalOptions: conf.legacyFeatures.incl emitGenerics diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 75d462b06..3deab0b74 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -319,7 +319,7 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string = var fullSuffix = suffix case conf.backend of backendCpp, backendJs, backendObjc: fullSuffix = "." & $conf.backend & suffix - of backendC: discard + of backendC, backendNir: discard of backendInvalid: # during parsing of cfg files; we don't know the backend yet, no point in # guessing wrong thing diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim index 41e85084f..4968c5924 100644 --- a/compiler/ic/rodfiles.nim +++ b/compiler/ic/rodfiles.nim @@ -97,6 +97,7 @@ type typeInfoSection # required by the backend backendFlagsSection aliveSymsSection # beware, this is stored in a `.alivesyms` file. + sideChannelSection RodFileError* = enum ok, tooBig, cannotOpen, ioFailure, wrongHeader, wrongSection, configMismatch, @@ -110,7 +111,7 @@ type const RodVersion = 1 - cookie = [byte(0), byte('R'), byte('O'), byte('D'), + defaultCookie = [byte(0), byte('R'), byte('O'), byte('D'), byte(sizeof(int)*8), byte(system.cpuEndian), byte(0), byte(RodVersion)] proc setError(f: var RodFile; err: RodFileError) {.inline.} = @@ -206,13 +207,13 @@ proc loadSeq*[T](f: var RodFile; s: var seq[T]) = for i in 0..<lenPrefix: loadPrim(f, s[i]) -proc storeHeader*(f: var RodFile) = +proc storeHeader*(f: var RodFile; cookie = defaultCookie) = ## stores the header which is described by `cookie`. if f.err != ok: return if f.f.writeBytes(cookie, 0, cookie.len) != cookie.len: setError f, ioFailure -proc loadHeader*(f: var RodFile) = +proc loadHeader*(f: var RodFile; cookie = defaultCookie) = ## Loads the header which is described by `cookie`. if f.err != ok: return var thisCookie: array[cookie.len, byte] = default(array[cookie.len, byte]) diff --git a/compiler/main.nim b/compiler/main.nim index e5a5be56c..0627a61bb 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -49,6 +49,9 @@ proc writeDepsFile(g: ModuleGraph) = f.writeLine(toFullPath(g.config, k)) f.close() +proc writeNinjaFile(g: ModuleGraph) = + discard "to implement" + proc writeCMakeDepsFile(conf: ConfigRef) = ## write a list of C files for build systems like CMake. ## only updated when the C file list changes. @@ -159,6 +162,26 @@ proc commandCompileToC(graph: ModuleGraph) = if optGenCDeps in graph.config.globalOptions: writeCMakeDepsFile(conf) +proc commandCompileToNir(graph: ModuleGraph) = + let conf = graph.config + extccomp.initVars(conf) + if conf.symbolFiles == disabledSf: + if {optRun, optForceFullMake} * conf.globalOptions == {optRun}: + if not changeDetectedViaJsonBuildInstructions(conf, conf.jsonBuildInstructionsFile): + # nothing changed + graph.config.notes = graph.config.mainPackageNotes + return + + if not extccomp.ccHasSaneOverflow(conf): + conf.symbols.defineSymbol("nimEmulateOverflowChecks") + + if conf.symbolFiles == disabledSf: + setPipeLinePass(graph, NirPass) + else: + setPipeLinePass(graph, SemPass) + compilePipelineProject(graph) + writeNinjaFile(graph) + proc commandJsonScript(graph: ModuleGraph) = extccomp.runJsonBuildInstructions(graph.config, graph.config.jsonBuildInstructionsFile) @@ -270,6 +293,8 @@ proc mainCommand*(graph: ModuleGraph) = # and it has added this define implictly, so we must undo that here. # A better solution might be to fix system.nim undefSymbol(conf.symbols, "useNimRtl") + of backendNir: + if conf.exc == excNone: conf.exc = excGoto of backendInvalid: raiseAssert "unreachable" proc compileToBackend() = @@ -280,6 +305,7 @@ proc mainCommand*(graph: ModuleGraph) = of backendCpp: commandCompileToC(graph) of backendObjc: commandCompileToC(graph) of backendJs: commandCompileToJS(graph) + of backendNir: commandCompileToNir(graph) of backendInvalid: raiseAssert "unreachable" template docLikeCmd(body) = diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index f6abb0a60..c450af50f 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -65,6 +65,7 @@ type CgenPass EvalPass InterpreterPass + NirPass NirReplPass GenDependPass Docgen2TexPass diff --git a/compiler/nim.nim b/compiler/nim.nim index 023a76ff9..7ec6f3e77 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -116,7 +116,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = conf.backend = backendC if conf.selectedGC == gcUnselected: - if conf.backend in {backendC, backendCpp, backendObjc} or + if conf.backend in {backendC, backendCpp, backendObjc, backendNir} or (conf.cmd == cmdInteractive and isDefined(conf, "nir")): initOrcDefines(conf) diff --git a/compiler/nir/ast2ir.nim b/compiler/nir/ast2ir.nim index fcda145ea..fd3a4df09 100644 --- a/compiler/nir/ast2ir.nim +++ b/compiler/nir/ast2ir.nim @@ -9,7 +9,7 @@ import std / [assertions, tables, sets] import ".." / [ast, astalgo, types, options, lineinfos, msgs, magicsys, - modulegraphs, guards, renderer, transf, bitsets, trees, nimsets, + modulegraphs, renderer, transf, bitsets, trees, nimsets, expanddefaults] from ".." / lowerings import lowerSwap, lowerTupleUnpacking from ".." / pathutils import customPath @@ -22,9 +22,8 @@ when defined(nimCompilerStacktraceHints): type ModuleCon* = ref object - strings*: BiTable[string] - integers*: BiTable[int64] man*: LineInfoManager + lit*: Literals types*: TypesCon slotGenerator: ref int module*: PSym @@ -34,7 +33,8 @@ type pendingProcs: Table[ItemId, PSym] # procs we still need to generate code for ProcCon* = object - config: ConfigRef + config*: ConfigRef + lit: Literals lastFileKey: FileIndex lastFileVal: LitId labelGen: int @@ -48,8 +48,11 @@ type options: TOptions 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) + let lit = Literals() # must be shared + var g = new(int) + g[] = idgen.symId + 1 + result = ModuleCon(graph: graph, types: initTypesCon(config, lit), slotGenerator: g, + idgen: idgen, module: module, lit: lit) case config.target.intSize of 2: result.nativeIntId = Int16Id @@ -63,6 +66,7 @@ proc initModuleCon*(graph: ModuleGraph; config: ConfigRef; idgen: IdGenerator; m proc initProcCon*(m: ModuleCon; prc: PSym; config: ConfigRef): ProcCon = ProcCon(m: m, sm: initSlotManager({}, m.slotGenerator), prc: prc, config: config, + lit: m.lit, options: if prc != nil: prc.options else: config.options) @@ -71,7 +75,7 @@ proc toLineInfo(c: var ProcCon; i: TLineInfo): PackedLineInfo = if c.lastFileKey == i.fileIndex: val = c.lastFileVal else: - val = c.m.strings.getOrIncl(toFullPath(c.config, i.fileIndex)) + val = c.lit.strings.getOrIncl(toFullPath(c.config, i.fileIndex)) # remember the entry: c.lastFileKey = i.fileIndex c.lastFileVal = val @@ -117,6 +121,11 @@ proc getTemp(c: var ProcCon; n: PNode): Value = c.code.addSummon info, tmp, t result = localToValue(info, tmp) +proc getTemp(c: var ProcCon; t: TypeId; info: PackedLineInfo): Value = + 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 @@ -306,11 +315,58 @@ proc caseRange(c: var ProcCon; n: PNode) = freeTemp(c, y) freeTemp(c, x) +proc addUseCodegenProc(c: var ProcCon; dest: var Tree; name: string; info: PackedLineInfo) = + let cp = getCompilerProc(c.m.graph, name) + let theProc = c.genx newSymNode(cp) + copyTree c.code, theProc + +template buildCond(useNegation: bool; cond: typed; body: untyped) = + let lab = newLabel(c.labelGen) + #let info = toLineInfo(c, n.info) + buildTyped c.code, info, Select, Bool8Id: + c.code.copyTree cond + build c.code, info, SelectPair: + build c.code, info, SelectValue: + c.code.boolVal(info, useNegation) + c.code.gotoLabel info, Goto, lab + + body + c.code.addLabel info, Label, lab + +template buildIf(cond: typed; body: untyped) = + buildCond false, cond, body + +template buildIfNot(cond: typed; body: untyped) = + buildCond true, cond, body + +template buildIfThenElse(cond: typed; then, otherwise: untyped) = + let lelse = newLabel(c.labelGen) + let lend = newLabel(c.labelGen) + buildTyped c.code, info, Select, Bool8Id: + c.code.copyTree cond + build c.code, info, SelectPair: + build c.code, info, SelectValue: + c.code.boolVal(info, false) + c.code.gotoLabel info, Goto, lelse + + then() + c.code.gotoLabel info, Goto, lend + c.code.addLabel info, Label, lelse + otherwise() + c.code.addLabel info, Label, lend + +include stringcases + 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) + + if n[0].typ.skipTypes(abstractInst).kind == tyString: + genStringCase(c, n, d) + return + var sections = newSeqOfCap[LabelId](n.len-1) let ending = newLabel(c.labelGen) let info = toLineInfo(c, n.info) @@ -481,10 +537,11 @@ proc genField(c: var ProcCon; n: PNode; d: var Value) = proc genIndex(c: var ProcCon; n: PNode; arr: PType; d: var Value) = let info = toLineInfo(c, n.info) if arr.skipTypes(abstractInst).kind == tyArray and - (let x = firstOrd(c.config, arr); x != Zero): + (let offset = firstOrd(c.config, arr); offset != Zero): + let x = c.genx(n) buildTyped d, info, Sub, c.m.nativeIntId: - c.gen(n, d) - d.addImmediateVal toLineInfo(c, n.info), toInt(x) + copyTree d.Tree, x + d.addImmediateVal toLineInfo(c, n.info), toInt(offset) else: c.gen(n, d) if optBoundsCheck in c.options: @@ -492,7 +549,7 @@ proc genIndex(c: var ProcCon; n: PNode; arr: PType; d: var Value) = build d, info, CheckedIndex: copyTree d.Tree, idx let x = toInt64 lengthOrd(c.config, arr) - d.Tree.addIntVal c.m.integers, info, c.m.nativeIntId, x + d.addIntVal c.lit.numbers, info, c.m.nativeIntId, x d.Tree.addLabel info, CheckedGoto, c.exitLabel proc genNew(c: var ProcCon; n: PNode; needsInit: bool) = @@ -545,12 +602,10 @@ proc genNewSeqOfCap(c: var ProcCon; n: PNode; d: var Value) = 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) +proc genNewSeqPayload(c: var ProcCon; info: PackedLineInfo; d, b: Value; seqtype: PType) = let baseType = seqtype.lastSon - var d = c.genx(n[1]) - var b = c.genx(n[2]) + # $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3)) + let payloadPtr = seqPayloadPtrType(c.m.types, seqtype) # $1.len = $2 buildTyped c.code, info, Asgn, c.m.nativeIntId: @@ -558,10 +613,7 @@ proc genNewSeq(c: var ProcCon; n: PNode) = 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: @@ -571,11 +623,20 @@ proc genNewSeq(c: var ProcCon; n: PNode) = 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) + let theProc = c.genx newSymNode(codegenProc) 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)) + +proc genNewSeq(c: var ProcCon; n: PNode) = + let info = toLineInfo(c, n.info) + let seqtype = skipTypes(n[1].typ, abstractVarRange) + var d = c.genx(n[1]) + var b = c.genx(n[2]) + + genNewSeqPayload(c, info, d, b, seqtype) + freeTemp c, b freeTemp c, d @@ -589,6 +650,14 @@ template intoDest*(d: var Value; info: PackedLineInfo; typ: TypeId; body: untype copyTree c.code, d body(c.code) +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 genBinaryOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) = let info = toLineInfo(c, n.info) let tmp = c.genx(n[1]) @@ -683,7 +752,7 @@ proc genArrayLen(c: var ProcCon; n: PNode; d: var Value) = of tyArray: template body(target) = - target.addIntVal(c.m.integers, info, c.m.nativeIntId, toInt lengthOrd(c.config, typ)) + target.addIntVal(c.lit.numbers, info, c.m.nativeIntId, toInt lengthOrd(c.config, typ)) intoDest d, info, c.m.nativeIntId, body else: internalError(c.config, n.info, "genArrayLen()") @@ -694,7 +763,7 @@ proc genUnaryMinus(c: var ProcCon; n: PNode; d: var Value) = 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) + target.addIntVal(c.lit.numbers, info, t, 0) copyTree target, tmp intoDest d, info, t, body c.freeTemp(tmp) @@ -707,7 +776,7 @@ proc genHigh(c: var ProcCon; n: PNode; d: var Value) = template body(target) = buildTyped target, info, Sub, t: copyTree target, x - target.addIntVal(c.m.integers, info, t, 1) + target.addIntVal(c.lit.numbers, info, t, 1) intoDest d, info, t, body c.freeTemp x @@ -813,15 +882,15 @@ proc genInBitset(c: var ProcCon; n: PNode; d: var Value) = buildTyped target, info, BitShr, t: buildTyped target, info, Cast, expansion: copyTree target, b - addIntVal target, c.m.integers, info, expansion, 3 + addIntVal target, c.lit.numbers, info, expansion, 3 buildTyped target, info, BitShl, t: - addIntVal target, c.m.integers, info, t, 1 + addIntVal target, c.lit.numbers, 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 + addIntVal target, c.lit.numbers, info, expansion, mask + addIntVal target, c.lit.numbers, info, t, 0 intoDest d, info, t, body c.freeTemp(b) @@ -916,7 +985,7 @@ proc genEqSet(c: var ProcCon; n: PNode; d: var Value) = 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 + c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0 else: template body(target) = @@ -933,14 +1002,14 @@ proc beginCountLoop(c: var ProcCon; info: PackedLineInfo; first, last: int): (Sy 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 + c.code.addIntVal c.lit.numbers, 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 + c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, last build c.code, info, SelectPair: build c.code, info, SelectValue: c.code.boolVal(info, false) @@ -969,7 +1038,7 @@ proc endLoop(c: var ProcCon; info: PackedLineInfo; s: SymId; back, exit: LabelId 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.addIntVal c.lit.numbers, info, c.m.nativeIntId, 1 c.code.addLabel info, GotoLoop, back c.code.addLabel info, Label, exit freeTemp(c.sm, s) @@ -1000,7 +1069,7 @@ proc genLeSet(c: var ProcCon; n: PNode; d: var Value) = buildTyped c.code, info, ArrayAt, elemType: copyTree c.code, b c.code.addSymUse info, idx - c.code.addIntVal c.m.integers, info, elemType, 0 + c.code.addIntVal c.lit.numbers, info, elemType, 0 # if !$3: break buildTyped c.code, info, Select, Bool8Id: @@ -1019,7 +1088,7 @@ proc genLeSet(c: var ProcCon; n: PNode; d: var Value) = copyTree target, a buildTyped target, info, BitNot, setType: copyTree target, b - target.addIntVal c.m.integers, info, setType, 0 + target.addIntVal c.lit.numbers, info, setType, 0 intoDest d, info, Bool8Id, body @@ -1103,19 +1172,19 @@ proc genInclExcl(c: var ProcCon; n: PNode; m: TMagic) = 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 + addIntVal c.code, c.lit.numbers, 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 + addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3 buildTyped c.code, info, BitShl, t: - c.code.addIntVal c.m.integers, info, t, 1 + c.code.addIntVal c.lit.numbers, info, t, 1 buildTyped c.code, info, BitAnd, t: copyTree c.code, b - c.code.addIntVal c.m.integers, info, t, 7 + c.code.addIntVal c.lit.numbers, info, t, 7 else: # $1[(NU)($2)>>3] &= ~(1U<<($2&7U)) buildTyped c.code, info, ArrayAt, t: @@ -1123,20 +1192,20 @@ proc genInclExcl(c: var ProcCon; n: PNode; m: TMagic) = 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 + addIntVal c.code, c.lit.numbers, 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 + addIntVal c.code, c.lit.numbers, 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 + c.code.addIntVal c.lit.numbers, info, t, 1 buildTyped c.code, info, BitAnd, t: copyTree c.code, b - c.code.addIntVal c.m.integers, info, t, 7 + c.code.addIntVal c.lit.numbers, info, t, 7 else: copyTree c.code, a @@ -1145,20 +1214,20 @@ proc genInclExcl(c: var ProcCon; n: PNode; m: TMagic) = 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 + c.code.addIntVal c.lit.numbers, info, t, 1 buildTyped c.code, info, BitAnd, t: copyTree c.code, b - c.code.addIntVal c.m.integers, info, t, mask + c.code.addIntVal c.lit.numbers, 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 + c.code.addIntVal c.lit.numbers, info, t, 1 buildTyped c.code, info, BitAnd, t: copyTree c.code, b - c.code.addIntVal c.m.integers, info, t, mask + c.code.addIntVal c.lit.numbers, info, t, mask freeTemp c, b freeTemp c, a @@ -1182,7 +1251,7 @@ proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) = 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 + c.code.addIntVal c.lit.numbers, info, t, 0 for it in n: if it.kind == nkRange: @@ -1195,10 +1264,10 @@ proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) = 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 + c.code.addIntVal c.lit.numbers, info, t, 1 buildTyped c.code, info, BitAnd, t: c.code.addSymUse info, idx - c.code.addIntVal c.m.integers, info, t, mask + c.code.addIntVal c.lit.numbers, info, t, mask endLoop(c, info, idx, backLabel, endLabel) freeTemp c, b @@ -1212,10 +1281,10 @@ proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) = 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 + c.code.addIntVal c.lit.numbers, info, t, 1 buildTyped c.code, info, BitAnd, t: copyTree c.code, a - c.code.addIntVal c.m.integers, info, t, mask + c.code.addIntVal c.lit.numbers, info, t, mask freeTemp c, a else: @@ -1223,7 +1292,7 @@ proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) = 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 + c.code.addIntVal c.lit.numbers, info, t, 0 endLoop(c, info, idx, backLabel, endLabel) # incl elements: @@ -1239,19 +1308,19 @@ proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) = 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 + addIntVal c.code, c.lit.numbers, 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 + addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3 buildTyped c.code, info, BitShl, t: - c.code.addIntVal c.m.integers, info, t, 1 + c.code.addIntVal c.lit.numbers, info, t, 1 buildTyped c.code, info, BitAnd, t: c.code.addSymUse info, idx - c.code.addIntVal c.m.integers, info, t, 7 + c.code.addIntVal c.lit.numbers, info, t, 7 endLoop(c, info, idx, backLabel, endLabel) freeTemp c, b @@ -1266,19 +1335,19 @@ proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) = 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 + addIntVal c.code, c.lit.numbers, 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 + addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3 buildTyped c.code, info, BitShl, t: - c.code.addIntVal c.m.integers, info, t, 1 + c.code.addIntVal c.lit.numbers, info, t, 1 buildTyped c.code, info, BitAnd, t: copyTree c.code, a - c.code.addIntVal c.m.integers, info, t, 7 + c.code.addIntVal c.lit.numbers, info, t, 7 freeTemp c, a proc genSetConstr(c: var ProcCon; n: PNode; d: var Value) = @@ -1290,14 +1359,14 @@ proc genSetConstr(c: var ProcCon; n: PNode; d: var Value) = if c.m.types.g[setType].kind != ArrayTy: template body(target) = - target.addIntVal c.m.integers, info, setType, cast[BiggestInt](bitSetToWord(cs, size)) + target.addIntVal c.lit.numbers, 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] + target.addIntVal c.lit.numbers, info, t, int64 cs[i] intoDest d, info, setType, body else: genSetConstrDyn c, n, d @@ -1334,7 +1403,7 @@ proc genStrConcat(c: var ProcCon; n: PNode; d: var Value) = 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 + c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, precomputedLen for a in mitems(args): buildTyped c.code, info, Asgn, c.m.nativeIntId: c.code.addSymUse info, tmpLen @@ -1441,6 +1510,57 @@ proc genMove(c: var ProcCon; n: PNode; d: var Value) = buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.types.g, typeToIr(c.m.types, n1.typ)): copyTree c.code, a +template fieldAt(x: Value; i: int; t: TypeId): Tree = + var result = default(Tree) + buildTyped result, info, FieldAt, t: + copyTree result, x + result.addImmediateVal info, i + result + +template eqNil(x: Tree; t: TypeId): Tree = + var result = default(Tree) + buildTyped result, info, Eq, t: + copyTree result, x + result.addNilVal info, t + result + +template eqZero(x: Tree): Tree = + var result = default(Tree) + buildTyped result, info, Eq, c.m.nativeIntId: + copyTree result, x + result.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0 + result + +template bitOp(x: Tree; opc: Opcode; y: int): Tree = + var result = default(Tree) + buildTyped result, info, opc, c.m.nativeIntId: + copyTree result, x + result.addIntVal c.lit.numbers, info, c.m.nativeIntId, y + result + +proc genDestroySeq(c: var ProcCon; n: PNode; t: PType) = + let info = toLineInfo(c, n.info) + let strLitFlag = 1 shl (c.m.graph.config.target.intSize * 8 - 2) # see also NIM_STRLIT_FLAG + + let x = c.genx(n[1]) + let baseType = t.lastSon + + let seqType = seqPayloadPtrType(c.m.types, t) + let p = fieldAt(x, 0, seqType) + + # if $1.p != nil and ($1.p.cap and NIM_STRLIT_FLAG) == 0: + # alignedDealloc($1.p, NIM_ALIGNOF($2)) + buildIfNot p.eqNil(seqType): + buildIf fieldAt(Value(p), 0, c.m.nativeIntId).bitOp(BitAnd, 0).eqZero(): + let codegenProc = getCompilerProc(c.m.graph, "alignedDealloc") + buildTyped c.code, info, Call, VoidId: + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + copyTree c.code, p + c.code.addImmediateVal info, int(getAlign(c.config, baseType)) + + freeTemp c, x + proc genDestroy(c: var ProcCon; n: PNode) = let t = n[1].typ.skipTypes(abstractInst) case t.kind @@ -1448,16 +1568,110 @@ proc genDestroy(c: var ProcCon; n: PNode) = var unused = default(Value) genUnaryCp(c, n, unused, "nimDestroyStrV1") of tySequence: - #[ - var a = initLocExpr(c, arg) - linefmt(c, cpsStmts, "if ($1.p && ($1.p->cap & NIM_STRLIT_FLAG) == 0) {$n" & - " #alignedDealloc($1.p, NIM_ALIGNOF($2));$n" & - "}$n", - [rdLoc(a), getTypeDesc(c.module, t.lastSon)]) - ]# - globalError(c.config, n.info, "not implemented: =destroy for seqs") + genDestroySeq(c, n, t) else: discard "nothing to do" +type + IndexFor = enum + ForSeq, ForStr, ForOpenArray, ForArray + +proc genIndexCheck(c: var ProcCon; n: PNode; a: Value; kind: IndexFor; arr: PType = nil): Value = + if optBoundsCheck in c.options: + let info = toLineInfo(c, n.info) + result = default(Value) + let idx = genx(c, n) + build result, info, CheckedIndex: + copyTree result.Tree, idx + case kind + of ForSeq, ForStr: + buildTyped result, info, FieldAt, c.m.nativeIntId: + copyTree result.Tree, a + result.addImmediateVal info, 0 # (len, p)-pair + of ForOpenArray: + buildTyped result, info, FieldAt, c.m.nativeIntId: + copyTree result.Tree, a + result.addImmediateVal info, 1 # (p, len)-pair + of ForArray: + let x = toInt64 lengthOrd(c.config, arr) + result.addIntVal c.lit.numbers, info, c.m.nativeIntId, x + result.Tree.addLabel info, CheckedGoto, c.exitLabel + freeTemp c, idx + else: + result = genx(c, n) + +proc addSliceFields(c: var ProcCon; target: var Tree; info: PackedLineInfo; + x: Value; n: PNode; arrType: PType) = + let elemType = arrayPtrTypeOf(c.m.types.g, typeToIr(c.m.types, arrType.lastSon)) + case arrType.kind + of tyString, tySequence: + let t = typeToIr(c.m.types, arrType.lastSon) + let checkKind = if arrType.kind == tyString: ForStr else: ForSeq + let pay = if checkKind == ForStr: strPayloadPtrType(c.m.types) + else: seqPayloadPtrType(c.m.types, arrType) + + let y = genIndexCheck(c, n[2], x, checkKind) + let z = genIndexCheck(c, n[3], x, checkKind) + + buildTyped target, info, ObjConstr, typeToIr(c.m.types, n.typ): + target.addImmediateVal info, 0 + buildTyped target, info, AddrOf, elemType: + buildTyped target, info, ArrayAt, t: + buildTyped target, info, FieldAt, pay: + copyTree target, x + target.addImmediateVal info, 1 # (len, p)-pair + copyTree target, y + + # len: + target.addImmediateVal info, 1 + buildTyped target, info, Add, c.m.nativeIntId: + buildTyped target, info, Sub, c.m.nativeIntId: + copyTree target, z + copyTree target, y + target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 1 + + freeTemp c, z + freeTemp c, y + of tyArray, tyOpenArray: + let t = typeToIr(c.m.types, arrType.lastSon) + # XXX This evaluates the index check for `y` twice. + # This check is also still insufficient for non-zero based arrays. + let checkKind = if arrType.kind == tyArray: ForArray else: ForOpenArray + + let y = genIndexCheck(c, n[2], x, checkKind, arrType) + let z = genIndexCheck(c, n[3], x, checkKind, arrType) + + buildTyped target, info, ObjConstr, typeToIr(c.m.types, n.typ): + target.addImmediateVal info, 0 + buildTyped target, info, AddrOf, elemType: + buildTyped target, info, ArrayAt, t: + copyTree target, x + copyTree target, y + + target.addImmediateVal info, 1 + buildTyped target, info, Add, c.m.nativeIntId: + buildTyped target, info, Sub, c.m.nativeIntId: + copyTree target, z + copyTree target, y + target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 1 + + freeTemp c, z + freeTemp c, y + else: + raiseAssert "addSliceFields: " & typeToString(arrType) + +proc genSlice(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + + let x = c.genx(n[1]) + + let arrType = n[1].typ.skipTypes(abstractVar) + + template body(target) = + c.addSliceFields target, info, x, n, arrType + + valueIntoDest c, info, d, arrType, body + freeTemp c, x + proc genMagic(c: var ProcCon; n: PNode; d: var Value; m: TMagic) = case m of mAnd: c.genAndOr(n, opcFJmp, d) @@ -1470,7 +1684,7 @@ proc genMagic(c: var ProcCon; n: PNode; d: var Value; m: TMagic) = of mDec: unused(c, n, d) c.genIncDec(n, CheckedSub) - of mOrd, mChr, mArrToSeq, mUnown: + of mOrd, mChr, mUnown: c.gen(n[1], d) of generatedMagics: genCall(c, n, d) @@ -1621,154 +1835,12 @@ proc genMagic(c: var ProcCon; n: PNode; d: var Value; m: TMagic) = of mDestroy: genDestroy(c, n) #of mAccessEnv: unaryExpr(d, n, d, "$1.ClE_0") #of mAccessTypeField: genAccessTypeField(c, n, d) - #of mSlice: genSlice(c, n, d) + of mSlice: genSlice(c, n, d) of mTrace: discard "no code to generate" else: # mGCref, mGCunref: unused by ORC globalError(c.config, n.info, "cannot generate code for: " & $m) -#[ - - of mRepr: genUnaryABC(c, n, d, opcRepr) - - 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 @@ -1798,14 +1870,6 @@ proc canElimAddr(n: PNode; idgen: IdGenerator): PNode = # 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) @@ -1842,7 +1906,7 @@ proc addAddrOfFirstElem(c: var ProcCon; target: var Tree; info: PackedLineInfo; buildTyped target, info, FieldAt, strPayloadPtrType(c.m.types): copyTree target, tmp target.addImmediateVal info, 1 # (len, p)-pair - target.addIntVal c.m.integers, info, c.m.nativeIntId, 0 + target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0 # len: target.addImmediateVal info, 1 buildTyped target, info, FieldAt, c.m.nativeIntId: @@ -1857,7 +1921,7 @@ proc addAddrOfFirstElem(c: var ProcCon; target: var Tree; info: PackedLineInfo; buildTyped target, info, FieldAt, seqPayloadPtrType(c.m.types, typ): copyTree target, tmp target.addImmediateVal info, 1 # (len, p)-pair - target.addIntVal c.m.integers, info, c.m.nativeIntId, 0 + target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0 # len: target.addImmediateVal info, 1 buildTyped target, info, FieldAt, c.m.nativeIntId: @@ -1870,9 +1934,9 @@ proc addAddrOfFirstElem(c: var ProcCon; target: var Tree; info: PackedLineInfo; buildTyped target, info, AddrOf, elemType: buildTyped target, info, ArrayAt, t: copyTree target, tmp - target.addIntVal c.m.integers, info, c.m.nativeIntId, 0 + target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0 target.addImmediateVal info, 1 - target.addIntVal(c.m.integers, info, c.m.nativeIntId, toInt lengthOrd(c.config, typ)) + target.addIntVal(c.lit.numbers, info, c.m.nativeIntId, toInt lengthOrd(c.config, typ)) else: raiseAssert "addAddrOfFirstElem: " & typeToString(typ) @@ -1933,10 +1997,32 @@ proc genObjOrTupleConstr(c: var ProcCon; n: PNode, d: var Value) = valueIntoDest c, info, d, n.typ, body +proc genSeqConstr(c: var ProcCon; n: PNode; d: var Value) = + if isEmpty(d): d = getTemp(c, n) + + let info = toLineInfo(c, n.info) + let seqtype = skipTypes(n.typ, abstractVarRange) + let baseType = seqtype.lastSon + + var b = default(Value) + b.addIntVal c.lit.numbers, info, c.m.nativeIntId, n.len + + genNewSeqPayload(c, info, d, b, seqtype) + + for i in 0..<n.len: + var dd = default(Value) + buildTyped dd, info, ArrayAt, typeToIr(c.m.types, seqtype): + buildTyped dd, info, FieldAt, seqPayloadPtrType(c.m.types, seqtype): + copyTree Tree(dd), d + dd.addIntVal c.lit.numbers, info, c.m.nativeIntId, i + gen(c, n[i], dd) + + freeTemp c, d + 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" + genSeqConstr(c, n, d) return let info = toLineInfo(c, n.info) @@ -1973,7 +2059,9 @@ proc genVarSection(c: var ProcCon; n: PNode) = opc = SummonGlobal else: opc = Summon - c.code.addSummon toLineInfo(c, a.info), SymId(s.itemId.item), typeToIr(c.m.types, s.typ), opc + let t = typeToIr(c.m.types, s.typ) + #assert t.int >= 0, typeToString(s.typ) & (c.config $ n.info) + c.code.addSummon toLineInfo(c, a.info), SymId(s.itemId.item), t, opc if a[2].kind != nkEmpty: genAsgn2(c, vn, a[2]) else: @@ -2002,7 +2090,7 @@ proc genRdVar(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = 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.addStrVal c.lit.strings, info, irModule(c, ast.originatingModule(s)) target.addImmediateVal info, s.itemId.item.int valueIntoDest c, info, d, s.typ, body @@ -2026,7 +2114,7 @@ proc genSym(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) = 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 + target.addIntVal c.lit.numbers, 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) @@ -2034,13 +2122,13 @@ proc genSym(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) = 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 + target.addIntVal c.lit.numbers, 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 + target.addStrVal c.lit.strings, info, n.strVal valueIntoDest c, info, d, n.typ, body proc genNilLit(c: var ProcCon; n: PNode; d: var Value) = @@ -2068,31 +2156,6 @@ proc genRangeCheck(c: var ProcCon; n: PNode; d: var Value) = else: gen c, n[0], d -type - IndexFor = enum - ForSeq, ForStr, ForOpenArray - -proc genIndexCheck(c: var ProcCon; n: PNode; a: Value; kind: IndexFor): Value = - if optBoundsCheck in c.options: - let info = toLineInfo(c, n.info) - result = default(Value) - let idx = genx(c, n) - build result, info, CheckedIndex: - copyTree result.Tree, idx - case kind - of ForSeq, ForStr: - buildTyped result, info, FieldAt, c.m.nativeIntId: - copyTree result.Tree, a - result.addImmediateVal info, 0 # (len, p)-pair - of ForOpenArray: - buildTyped result, info, FieldAt, c.m.nativeIntId: - copyTree result.Tree, a - result.addImmediateVal info, 1 # (p, len)-pair - result.Tree.addLabel info, CheckedGoto, c.exitLabel - freeTemp c, idx - else: - result = genx(c, n) - 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) @@ -2189,7 +2252,10 @@ proc genObjAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = 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 + if not isCompileTimeOnly(s.typ): + let t = typeToIr(c.m.types, s.typ) + assert t.int != -1, typeToString(s.typ) + c.code.addSummon toLineInfo(c, params[i].info), SymId(s.itemId.item), t, SummonParam proc addCallConv(c: var ProcCon; info: PackedLineInfo; callConv: TCallingConvention) = template ann(s: untyped) = c.code.addPragmaId info, s @@ -2207,10 +2273,9 @@ proc addCallConv(c: var ProcCon; info: PackedLineInfo; callConv: TCallingConvent 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 + if isGenericRoutineStrict(prc) or isCompileTimeProc(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, keepOpenArrayConversions}) @@ -2221,26 +2286,27 @@ proc genProc(cOuter: var ProcCon; n: PNode) = 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 + c.code.addStrVal c.lit.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 + c.code.addStrVal c.lit.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 + c.code.addStrVal c.lit.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 + genParams(c, prc.typ.n) gen(c, body) patch c, body, c.exitLabel diff --git a/compiler/nir/nir.nim b/compiler/nir/nir.nim index 1994a1be7..0669bc222 100644 --- a/compiler/nir/nir.nim +++ b/compiler/nir/nir.nim @@ -10,8 +10,12 @@ ## 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 +from os import addFileExt, `/`, createDir + +import ".." / [ast, modulegraphs, renderer, transf, options, msgs, lineinfos] +import nirtypes, nirinsts, ast2ir, nirlineinfos + +import ".." / ic / [rodfiles, bitabs] type PCtx* = ref object of TPassContext @@ -45,8 +49,8 @@ proc evalStmt(c: PCtx; n: PNode) = 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 c.c.code, NodePos(pc), c.m.lit.strings, c.m.lit.numbers, res + #res.add "\n--------------------------\n" #toString res, c.m.types.g echo res @@ -61,11 +65,51 @@ proc runCode*(c: PPassContext; n: PNode): PNode = 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] +type + NirPassContext* = ref object of TPassContext + m: ModuleCon + c: ProcCon + +proc openNirBackend*(g: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = + let m = initModuleCon(g, g.config, idgen, module) + NirPassContext(m: m, c: initProcCon(m, nil, g.config), idgen: idgen) + +proc gen(c: NirPassContext; n: PNode) = + let n = transformExpr(c.m.graph, c.idgen, c.m.module, n) + let pc = genStmt(c.c, n) + +proc nirBackend*(c: PPassContext; n: PNode): PNode = + gen(NirPassContext(c), n) + result = n + +proc closeNirBackend*(c: PPassContext; finalNode: PNode) = + discard nirBackend(c, finalNode) + + let c = NirPassContext(c) + let nimcache = getNimcacheDir(c.c.config).string + createDir nimcache + let outp = nimcache / c.m.module.name.s.addFileExt("nir") + var r = rodfiles.create(outp) + try: + r.storeHeader(nirCookie) + r.storeSection stringsSection + r.store c.m.lit.strings + + r.storeSection numbersSection + r.store c.m.lit.numbers + r.storeSection bodiesSection + r.store c.c.code + + r.storeSection typesSection + r.store c.m.types.g + + r.storeSection sideChannelSection + r.store c.m.man + + finally: + r.close() + if r.err != ok: + rawMessage(c.c.config, errFatal, "serialization failed: " & outp) + else: + echo "created: ", outp diff --git a/compiler/nir/nirc.nim b/compiler/nir/nirc.nim new file mode 100644 index 000000000..da8d7d778 --- /dev/null +++ b/compiler/nir/nirc.nim @@ -0,0 +1,48 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Nir Compiler. Currently only supports a "view" command. + +import ".." / ic / [bitabs, rodfiles] +import nirinsts, nirtypes, nirlineinfos + +proc view(filename: string) = + var lit = Literals() + + var r = rodfiles.open(filename) + var code = default Tree + var man = default LineInfoManager + var types = initTypeGraph(lit) + try: + r.loadHeader(nirCookie) + r.loadSection stringsSection + r.load lit.strings + + r.loadSection numbersSection + r.load lit.numbers + + r.loadSection bodiesSection + r.load code + + r.loadSection typesSection + r.load types + + r.loadSection sideChannelSection + r.load man + + finally: + r.close() + + var res = "" + allTreesToString code, lit.strings, lit.numbers, res + echo res + +import std / os + +view paramStr(1) diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim index 2c0dc3d11..741733d48 100644 --- a/compiler/nir/nirinsts.nim +++ b/compiler/nir/nirinsts.nim @@ -10,9 +10,14 @@ ## NIR instructions. Somewhat inspired by LLVM's instructions. import std / [assertions, hashes] -import .. / ic / bitabs +import .. / ic / [bitabs, rodfiles] import nirlineinfos, nirtypes +const + NirVersion = 1 + nirCookie* = [byte(0), byte('N'), byte('I'), byte('R'), + byte(sizeof(int)*8), byte(system.cpuEndian), byte(0), byte(NirVersion)] + type SymId* = distinct int @@ -167,6 +172,8 @@ type template kind*(n: Instr): Opcode = Opcode(n.x and OpcodeMask) template operand(n: Instr): uint32 = (n.x shr OpcodeBits) +template rawOperand*(n: Instr): uint32 = (n.x shr OpcodeBits) + template toX(k: Opcode; operand: uint32): uint32 = uint32(k) or (operand shl OpcodeBits) @@ -257,6 +264,10 @@ proc newLabel*(labelGen: var int): LabelId {.inline.} = result = LabelId labelGen inc labelGen +proc newLabels*(labelGen: var int; n: int): LabelId {.inline.} = + result = LabelId labelGen + inc labelGen, n + proc addNewLabel*(t: var Tree; labelGen: var int; info: PackedLineInfo; k: Opcode): LabelId = assert k in {Label, LoopLabel} result = LabelId labelGen @@ -281,9 +292,11 @@ 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.} = + assert typ.int >= 0 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 typ.int >= 0 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) @@ -304,10 +317,16 @@ proc addIntVal*(t: var Tree; integers: var BiTable[int64]; info: PackedLineInfo; 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 addStrLit*(t: var Tree; info: PackedLineInfo; s: LitId) = + t.nodes.add Instr(x: toX(StrVal, uint32(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 store*(r: var RodFile; t: Tree) = storeSeq r, t.nodes +proc load*(r: var RodFile; t: var Tree) = loadSeq r, t.nodes + proc escapeToNimLit(s: string; result: var string) = result.add '"' for c in items s: @@ -370,6 +389,14 @@ proc toString*(t: Tree; pos: NodePos; strings: BiTable[string]; integers: BiTabl for i in 0..<nesting*2: r.add ' ' r.add "}" +proc allTreesToString*(t: Tree; strings: BiTable[string]; integers: BiTable[int64]; + r: var string) = + + var i = 0 + while i < t.len: + toString t, NodePos(i), strings, integers, r + nextChild t, i + type Value* = distinct Tree @@ -419,3 +446,6 @@ proc addStrVal*(t: var Value; strings: var BiTable[string]; info: PackedLineInfo proc addNilVal*(t: var Value; info: PackedLineInfo; typ: TypeId) = addNilVal Tree(t), info, typ + +proc addIntVal*(t: var Value; integers: var BiTable[int64]; info: PackedLineInfo; typ: TypeId; x: int64) = + addIntVal Tree(t), integers, info, typ, x diff --git a/compiler/nir/nirlineinfos.nim b/compiler/nir/nirlineinfos.nim index 4e86f619e..5f5d55086 100644 --- a/compiler/nir/nirlineinfos.nim +++ b/compiler/nir/nirlineinfos.nim @@ -34,13 +34,13 @@ const static: assert AsideBit + FileBits + LineBits + ColBits == 32 -import .. / ic / bitabs # for LitId +import .. / ic / [bitabs, rodfiles] # for LitId type PackedLineInfo* = distinct uint32 LineInfoManager* = object - aside*: seq[(LitId, int32, int32)] + 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: @@ -66,6 +66,9 @@ proc unpack*(m: LineInfoManager; i: PackedLineInfo): (LitId, int32, int32) = proc getFileId*(m: LineInfoManager; i: PackedLineInfo): LitId = result = unpack(m, i)[0] +proc store*(r: var RodFile; m: LineInfoManager) = storeSeq(r, m.aside) +proc load*(r: var RodFile; m: var LineInfoManager) = loadSeq(r, m.aside) + when isMainModule: var m = LineInfoManager(aside: @[]) for i in 0'i32..<16388'i32: diff --git a/compiler/nir/nirslots.nim b/compiler/nir/nirslots.nim index 256c25a19..d983fdea2 100644 --- a/compiler/nir/nirslots.nim +++ b/compiler/nir/nirslots.nim @@ -76,7 +76,7 @@ proc closeScope*(m: var SlotManager) = when isMainModule: var m = initSlotManager({ReuseTemps}, new(int)) - var g = initTypeGraph() + var g = initTypeGraph(Literals()) let a = g.openType ArrayTy g.addBuiltinType Int8Id diff --git a/compiler/nir/nirtypes.nim b/compiler/nir/nirtypes.nim index a42feab00..288f3063d 100644 --- a/compiler/nir/nirtypes.nim +++ b/compiler/nir/nirtypes.nim @@ -10,12 +10,15 @@ ## Type system for NIR. Close to C's type system but without its quirks. import std / [assertions, hashes] -import .. / ic / bitabs +import .. / ic / [bitabs, rodfiles] type NirTypeKind* = enum - VoidTy, IntTy, UIntTy, FloatTy, BoolTy, CharTy, NameVal, IntVal, + VoidTy, IntTy, UIntTy, FloatTy, BoolTy, CharTy, NameVal, + IntVal, SizeVal, AlignVal, OffsetVal, AnnotationVal, + ObjectTy, + UnionTy, VarargsTy, # the `...` in a C prototype; also the last "atom" APtrTy, # pointer to aliasable memory UPtrTy, # pointer to unique/unaliasable memory @@ -23,8 +26,6 @@ type 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, @@ -54,10 +55,13 @@ proc `==`*(a, b: TypeId): bool {.borrow.} proc hash*(a: TypeId): Hash {.borrow.} type + Literals* = ref object + strings*: BiTable[string] + numbers*: BiTable[int64] + TypeGraph* = object nodes: seq[TypeNode] - names: BiTable[string] - numbers: BiTable[uint64] + lit: Literals const VoidId* = TypeId 0 @@ -76,7 +80,7 @@ const VoidPtrId* = TypeId 13 LastBuiltinId* = 13 -proc initTypeGraph*(): TypeGraph = +proc initTypeGraph*(lit: Literals): TypeGraph = result = TypeGraph(nodes: @[ TypeNode(x: toX(VoidTy, 0'u32)), TypeNode(x: toX(BoolTy, 8'u32)), @@ -93,7 +97,7 @@ proc initTypeGraph*(): TypeGraph = TypeNode(x: toX(FloatTy, 64'u32)), TypeNode(x: toX(APtrTy, 2'u32)), TypeNode(x: toX(VoidTy, 0'u32)) - ]) + ], lit: lit) assert result.nodes.len == LastBuiltinId+2 type @@ -164,9 +168,9 @@ proc sons3(tree: TypeGraph; n: TypeId): (TypeId, TypeId, TypeId) = let c = b + span(tree, b) result = (TypeId a, TypeId b, TypeId c) -proc arrayLen*(tree: TypeGraph; n: TypeId): BiggestUInt = +proc arrayLen*(tree: TypeGraph; n: TypeId): BiggestInt = assert tree[n].kind == ArrayTy - result = tree.numbers[LitId tree[n].operand] + result = tree.lit.numbers[LitId tree[n].operand] proc openType*(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos = assert kind in {APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy, @@ -174,20 +178,54 @@ proc openType*(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos = 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) +template typeInvariant(p: TypePatchPos) = + when false: + if tree[TypeId(p)].kind == FieldDecl: + var k = 0 + for ch in sons(tree, TypeId(p)): + inc k + assert k > 2, "damn! " & $k + +proc sealType*(tree: var TypeGraph; p: TypePatchPos) = + patch tree, p + typeInvariant(p) + +proc finishType*(tree: var TypeGraph; p: TypePatchPos): TypeId = + # Search for an existing instance of this type in + # order to reduce memory consumption: patch tree, p + typeInvariant(p) + + let s = span(tree, p.int) + var i = 0 + while i < p.int: + if tree.nodes[i].x == tree.nodes[p.int].x: + var isMatch = true + for j in 1..<s: + if tree.nodes[j+i].x == tree.nodes[j+p.int].x: + discard "still a match" + else: + isMatch = false + break + if isMatch: + if p.int+s == tree.len: + setLen tree.nodes, p.int + return TypeId(i) + nextChild tree, i + result = TypeId(p) proc nominalType*(tree: var TypeGraph; kind: NirTypeKind; name: string): TypeId = assert kind in {ObjectTy, UnionTy} + let content = TypeNode(x: toX(kind, tree.lit.strings.getOrIncl(name))) + for i in 0..<tree.len: + if tree.nodes[i].x == content.x: + return TypeId(i) result = TypeId tree.nodes.len - tree.nodes.add TypeNode(x: toX(kind, tree.names.getOrIncl(name))) + tree.nodes.add content 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))) + tree.nodes.add TypeNode(x: toX(kind, tree.lit.strings.getOrIncl(name))) proc addVarargs*(tree: var TypeGraph) = tree.nodes.add TypeNode(x: toX(VarargsTy, 0'u32)) @@ -219,30 +257,46 @@ proc addType*(g: var TypeGraph; t: TypeId) = 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 addArrayLen*(g: var TypeGraph; len: int64) = + g.nodes.add TypeNode(x: toX(IntVal, g.lit.numbers.getOrIncl(len))) + +proc addSize*(g: var TypeGraph; s: int64) = + g.nodes.add TypeNode(x: toX(SizeVal, g.lit.numbers.getOrIncl(s))) + +proc addOffset*(g: var TypeGraph; offset: int64) = + g.nodes.add TypeNode(x: toX(OffsetVal, g.lit.numbers.getOrIncl(offset))) + +proc addAlign*(g: var TypeGraph; a: int64) = + g.nodes.add TypeNode(x: toX(AlignVal, g.lit.numbers.getOrIncl(a))) proc addName*(g: var TypeGraph; name: string) = - g.nodes.add TypeNode(x: toX(NameVal, g.names.getOrIncl(name))) + g.nodes.add TypeNode(x: toX(NameVal, g.lit.strings.getOrIncl(name))) proc addAnnotation*(g: var TypeGraph; name: string) = - g.nodes.add TypeNode(x: toX(NameVal, g.names.getOrIncl(name))) + g.nodes.add TypeNode(x: toX(NameVal, g.lit.strings.getOrIncl(name))) -proc addField*(g: var TypeGraph; name: string; typ: TypeId) = +proc addField*(g: var TypeGraph; name: string; typ: TypeId; offset: int64) = let f = g.openType FieldDecl g.addType typ + g.addOffset offset g.addName name - discard sealType(g, f) + sealType(g, f) proc ptrTypeOf*(g: var TypeGraph; t: TypeId): TypeId = let f = g.openType APtrTy g.addType t - result = sealType(g, f) + result = finishType(g, f) proc arrayPtrTypeOf*(g: var TypeGraph; t: TypeId): TypeId = let f = g.openType AArrayPtrTy g.addType t - result = sealType(g, f) + result = finishType(g, f) + +proc store*(r: var RodFile; g: TypeGraph) = + storeSeq r, g.nodes + +proc load*(r: var RodFile; g: var TypeGraph) = + loadSeq r, g.nodes proc toString*(dest: var string; g: TypeGraph; i: TypeId) = case g[i].kind @@ -263,9 +317,11 @@ proc toString*(dest: var string; g: TypeGraph; i: TypeId) = 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] + dest.add g.lit.strings[LitId g[i].operand] + of IntVal, SizeVal, AlignVal, OffsetVal: + dest.add $g[i].kind + dest.add ' ' + dest.add $g.lit.numbers[LitId g[i].operand] of VarargsTy: dest.add "..." of APtrTy: @@ -298,10 +354,10 @@ proc toString*(dest: var string; g: TypeGraph; i: TypeId) = dest.add "]" of ObjectTy: dest.add "object " - dest.add g.names[LitId g[i].operand] + dest.add g.lit.strings[LitId g[i].operand] of UnionTy: dest.add "union " - dest.add g.names[LitId g[i].operand] + dest.add g.lit.strings[LitId g[i].operand] of ProcTy: dest.add "proc[" for t in sons(g, i): toString(dest, g, t) @@ -319,10 +375,19 @@ proc toString*(dest: var string; g: TypeGraph; i: TypeId) = dest.add '\n' dest.add "]" of FieldDecl: - let (typ, name) = g.sons2(i) - toString(dest, g, typ) - dest.add ' ' - toString(dest, g, name) + dest.add "field[" + for t in sons(g, i): + toString(dest, g, t) + dest.add ' ' + dest.add "]" + + when false: + let (typ, offset, name) = g.sons3(i) + toString(dest, g, typ) + dest.add ' ' + toString(dest, g, offset) + dest.add ' ' + toString(dest, g, name) proc toString*(dest: var string; g: TypeGraph) = var i = 0 @@ -336,17 +401,17 @@ proc `$`(g: TypeGraph): string = toString(result, g) when isMainModule: - var g = initTypeGraph() + var g = initTypeGraph(Literals()) let a = g.openType ArrayTy g.addBuiltinType Int8Id - g.addArrayLen 5'u64 - let finalArrayType = sealType(g, a) + g.addArrayLen 5 + let finalArrayType = finishType(g, a) let obj = g.openType ObjectDecl - g.nodes.add TypeNode(x: toX(NameVal, g.names.getOrIncl("MyType"))) + g.nodes.add TypeNode(x: toX(NameVal, g.lit.strings.getOrIncl("MyType"))) - g.addField "p", finalArrayType - discard sealType(g, obj) + g.addField "p", finalArrayType, 0 + sealType(g, obj) echo g diff --git a/compiler/nir/nirvm.nim b/compiler/nir/nirvm.nim new file mode 100644 index 000000000..dcb5ded6f --- /dev/null +++ b/compiler/nir/nirvm.nim @@ -0,0 +1,467 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +##[ NIR is a little too high level to interpret it efficiently. Thus +we compute `addresses` for SymIds, labels and offsets for object fields +in a preprocessing step. + +We also split the instruction stream into separate (code, debug) seqs while +we're at it. +]## + +import std / [tables, intsets] +import ".." / ic / bitabs +import nirinsts, nirtypes + +type + OpcodeM = enum + ImmediateValM, + IntValM, + StrValM, + LoadLocalM, # with local ID + TypedM, # with type ID + PragmaIdM, # with Pragma ID, possible values: see PragmaKey enum + NilValM, + GotoM, + CheckedGotoM, # last atom + + LoadProcM, + LoadGlobalM, # `"module".x` + + ArrayConstrM, + ObjConstrM, + RetM, + YldM, + + SelectM, + SelectPairM, # ((values...), Label) + SelectListM, # (values...) + SelectValueM, # (value) + SelectRangeM, # (valueA..valueB) + SummonGlobalM, + SummonThreadLocalM, + SummonM, # x = Summon Typed <Type ID>; x begins to live + SummonParamM, + + AddrOfM, + ArrayAtM, # addr(a[i]) + FieldAtM, # addr(obj.field) + + LoadM, # a[] + StoreM, # a[] = b + AsgnM, # a = b + SetExcM, + TestExcM, + + CheckedRangeM, + CheckedIndexM, + + CallM, + IndirectCallM, + CheckedCallM, # call that can raise + CheckedIndirectCallM, # call that can raise + CheckedAddM, # with overflow checking etc. + CheckedSubM, + CheckedMulM, + CheckedDivM, + CheckedModM, + AddM, + SubM, + MulM, + DivM, + ModM, + BitShlM, + BitShrM, + BitAndM, + BitOrM, + BitXorM, + BitNotM, + BoolNotM, + EqM, + LeM, + LtM, + CastM, + NumberConvM, + CheckedObjConvM, + ObjConvM, + TestOfM, + ProcDeclM, + PragmaPairM + +const + LastAtomicValue = CheckedGotoM + + OpcodeBits = 8'u32 + OpcodeMask = (1'u32 shl OpcodeBits) - 1'u32 + +type + Instr = distinct uint32 + +template kind(n: Instr): OpcodeM = OpcodeM(n and OpcodeMask) +template operand(n: Instr): uint32 = (n shr OpcodeBits) + +template toIns(k: OpcodeM; operand: uint32): Instr = + Instr(uint32(k) or (operand shl OpcodeBits)) + +template toIns(k: OpcodeM; operand: LitId): Instr = + Instr(uint32(k) or (operand.uint32 shl OpcodeBits)) + +type + PatchPos = distinct int + CodePos = distinct int + + Unit = ref object ## a NIR module + procs: Table[SymId, CodePos] + globals: Table[SymId, uint32] + integers: BiTable[int64] + strings: BiTable[string] + globalsGen: uint32 + + Universe* = object ## all units: For interpretation we need that + units: Table[string, Unit] + + Bytecode = object + code: seq[Instr] + debug: seq[PackedLineInfo] + u: Unit + +const + InvalidPatchPos* = PatchPos(-1) + +proc isValid(p: PatchPos): bool {.inline.} = p.int != -1 + +proc prepare(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM): PatchPos = + result = PatchPos bc.code.len + bc.code.add toIns(kind, 1'u32) + bc.debug.add info + +proc add(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM; raw: uint32) = + bc.code.add toIns(kind, raw) + bc.debug.add info + +proc add(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM; lit: LitId) = + add bc, info, kind, uint(lit) + +proc isAtom(bc: Bytecode; pos: int): bool {.inline.} = bc.code[pos].kind <= LastAtomicValue +proc isAtom(bc: Bytecode; pos: CodePos): bool {.inline.} = bc.code[pos.int].kind <= LastAtomicValue + +proc patch(bc: var Bytecode; pos: PatchPos) = + let pos = pos.int + let k = bc.code[pos].kind + assert k > LastAtomicValue + let distance = int32(bc.code.len - pos) + assert distance > 0 + bc.code[pos] = toIns(k, cast[uint32](distance)) + +template build(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM; body: untyped) = + let pos = prepare(bc, info, kind) + body + patch(bc, pos) + +proc len*(bc: Bytecode): int {.inline.} = bc.code.len + +template rawSpan(n: Instr): int = int(operand(n)) + +proc nextChild(bc: Bytecode; pos: var int) {.inline.} = + if bc.code[pos].kind > LastAtomicValue: + assert bc.code[pos].operand > 0'u32 + inc pos, bc.code[pos].rawSpan + else: + inc pos + +iterator sons(bc: Bytecode; n: CodePos): CodePos = + var pos = n.int + assert bc.code[pos].kind > LastAtomicValue + let last = pos + bc.code[pos].rawSpan + inc pos + while pos < last: + yield CodePos pos + nextChild bc, pos + +template `[]`*(t: Bytecode; n: CodePos): Instr = t.code[n.int] + +proc span(bc: Bytecode; pos: int): int {.inline.} = + if bc.code[pos].kind <= LastAtomicValue: 1 else: int(bc.code[pos].operand) + +type + Preprocessing = object + known: Table[LabelId, CodePos] + toPatch: Table[LabelId, seq[CodePos]] + locals: Table[SymId, uint32] + c: Bytecode # to be moved out + thisModule: LitId + markedWithLabel: IntSet + +proc genGoto(c: var Preprocessing; lab: LabelId; opc: OpcodeM) = + let dest = c.known.getOrDefault(lab, CodePos(-1)) + if dest.int >= 0: + c.bc.add info, opc, uint32 dest + else: + let here = CodePos(c.bc.code.len) + c.toPatch.mgetOrPut(lab, @[]).add here + c.bc.add info, opc, 1u32 # will be patched once we traversed the label + +proc preprocess(c: var Preprocessing; u: var Universe; t: Tree; n: NodePos) = + let info = t[n].info + + template recurse(opc) = + build c.bc, info, opc: + for c in sons(t, n): preprocess(c, u, t, c) + + case t[n].kind + of Nop: + discard "don't use Nop" + of ImmediateVal: + c.bc.add info, ImmediateValM, t[n].rawOperand + of IntVal: + c.bc.add info, IntValM, t[n].rawOperand + of StrVal: + c.bc.add info, StrValM, t[n].rawOperand + of SymDef: + assert false, "SymDef outside of declaration context" + of SymUse: + let s = t[n].symId + if c.locals.hasKey(s): + c.bc.add info, LoadLocalM, c.locals[s] + elif c.bc.u.procs.hasKey(s): + build c.bc, info, LoadProcM: + c.bc.add info, StrValM, thisModule + c.bc.add info, LoadLocalM, uint32 c.bc.u.procs[s] + elif c.bc.u.globals.hasKey(s): + build c.bc, info, LoadGlobalM: + c.bc.add info, StrValM, thisModule + c.bc.add info, LoadLocalM, uint32 s + else: + assert false, "don't understand SymUse ID" + + of ModuleSymUse: + let moduleName {.cursor.} = c.bc.u.strings[t[n.firstSon].litId] + let unit = u.units.getOrDefault(moduleName) + + of Typed: + c.bc.add info, TypedM, t[n].rawOperand + of PragmaId: + c.bc.add info, TypedM, t[n].rawOperand + of NilVal: + c.bc.add info, NilValM, t[n].rawOperand + of LoopLabel, Label: + let lab = t[n].label + let here = CodePos(c.bc.code.len-1) + c.known[lab] = here + var p: seq[CodePos] + if c.toPatch.take(lab, p): + for x in p: c.bc.code[x] = toIns(c.bc.code[x].kind, here) + c.markedWithLabel.incl here.int # for toString() + of Goto, GotoLoop: + c.genGoto(t[n].label, GotoM) + of CheckedGoto: + c.genGoto(t[n].label, CheckedGotoM) + of ArrayConstr: + recurse ArrayConstrM + of ObjConstr: + recurse ObjConstrM + of Ret: + recurse RetM + of Yld: + recurse YldM + of Select: + recurse SelectM + of SelectPair: + recurse SelectPairM + of SelectList: + recurse SelectListM + of SelectValue: + recurse SelectValueM + of SelectRange: + recurse SelectRangeM + of SummonGlobal, SummonThreadLocal, SummonConst: + #let s = + discard "xxx" + of Summon, SummonParam: + # x = Summon Typed <Type ID>; x begins to live + discard "xxx" + of Kill: + discard "we don't care about Kill instructions" + of AddrOf: + recurse AddrOfM + of ArrayAt: + recurse ArrayAtM + of FieldAt: + recurse FieldAtM + of Load: + recurse LoadM + of Store: + recurse StoreM + of Asgn: + recurse AsgnM + of SetExc: + recurse SetExcM + of TestExc: + recurse TestExcM + of CheckedRange: + recurse CheckedRangeM + of CheckedIndex: + recurse CheckedIndexM + of Call: + recurse CallM + of IndirectCall: + recurse IndirectCallM + of CheckedCall: + recurse CheckedCallM + of CheckedIndirectCall: + recurse CheckedIndirectCallM + of CheckedAdd: + recurse CheckedAddM + of CheckedSub: + recurse CheckedSubM + of CheckedMul: + recurse CheckedMulM + of CheckedDiv: + recurse CheckedDivM + of CheckedMod: + recurse CheckedModM + of Add: + recurse AddM + of Sub: + recurse SubM + of Mul: + recurse MulM + of Div: + recurse DivM + of Mod: + recurse ModM + of BitShl: + recurse BitShlM + of BitShr: + recurse BitShrM + of BitAnd: + recurse BitAndM + of BitOr: + recurse BitOrM + of BitXor: + recurse BitXorM + of BitNot: + recurse BitNotM + of BoolNot: + recurse BoolNotM + of Eq: + recurse EqM + of Le: + recurse LeM + of Lt: + recurse LtM + of Cast: + recurse CastM + of NumberConv: + recurse NumberConvM + of CheckedObjConv: + recurse CheckedObjConvM + of ObjConv: + recurse ObjConvM + of TestOf: + recurse TestOfM + of Emit: + assert false, "cannot interpret: Emit" + of ProcDecl: + recurse ProcDeclM + of PragmaPair: + recurse PragmaPairM + +const PayloadSize = 128 + +type + StackFrame = ref object + locals: pointer # usually points into `payload` if size is small enough, otherwise it's `alloc`'ed. + payload: array[PayloadSize, byte] + caller: StackFrame + returnAddr: CodePos + +proc newStackFrame(size: int; caller: StackFrame; returnAddr: CodePos): StackFrame = + result = StackFrame(caller: caller, returnAddr: returnAddr) + if size <= PayloadSize: + result.locals = addr(result.payload) + else: + result.locals = alloc0(size) + +proc popStackFrame(s: StackFrame): StackFrame = + if result.locals != addr(result.payload): + dealloc result.locals + result = s.caller + +template `+!`(p: pointer; diff: uint): pointer = cast[pointer](cast[uint](p) + diff) + +proc eval(c: seq[Instr]; pc: CodePos; s: StackFrame; result: pointer) + +proc evalAddr(c: seq[Instr]; pc: CodePos; s: StackFrame): pointer = + case c[pc].kind + of LoadLocalM: + result = s.locals +! c[pc].operand + of FieldAtM: + result = eval(c, pc+1, s) + result = result +! c[pc+2].operand + of ArrayAtM: + let elemSize = c[pc+1].operand + result = eval(c, pc+2, s) + var idx: int + eval(c, pc+3, addr idx) + result = result +! (idx * elemSize) + +proc eval(c: seq[Instr]; pc: CodePos; s: StackFrame; result: pointer) = + case c[pc].kind + of AddM: + # assume `int` here for now: + var x, y: int + eval c, pc+1, s, addr x + eval c, pc+2, s, addr y + cast[ptr int](res)[] = x + y + of StrValM: + cast[ptr StringDesc](res)[] = addr(c.strings[c[pc].litId]) + of ObjConstrM: + for ch in sons(c, pc): + let offset = c[ch] + eval c, ch+2, s, result+!offset + of ArrayConstrM: + let elemSize = c[pc+1].operand + var r = result + for ch in sons(c, pc): + eval c, ch, s, r + r = r+!elemSize # can even do strength reduction here! + else: + assert false, "cannot happen" + +proc exec(c: seq[Instr]; pc: CodePos) = + var pc = pc + var currentFrame: StackFrame = nil + while true: + case c[pc].kind + of GotoM: + pc = CodePos(c[pc].operand) + of Asgn: + let (size, a, b) = sons3(c, pc) + let dest = evalAddr(c, a, s) + eval(c, b, s, dest) + of CallM: + # No support for return values, these are mapped to `var T` parameters! + let prc = evalProc(c, pc+1) + # setup storage for the proc already: + let s2 = newStackFrame(prc.frameSize, currentFrame, pc) + var i = 0 + for a in sons(c, pc): + eval(c, a, s2, paramAddr(s2, i)) + inc i + currentFrame = s2 + pc = pcOf(prc) + of RetM: + pc = currentFrame.returnAddr + currentFrame = popStackFrame(currentFrame) + of SelectM: + var x: bool + eval(c, b, addr x) + # follow the selection instructions... + pc = activeBranch(c, b, x) diff --git a/compiler/nir/stringcases.nim b/compiler/nir/stringcases.nim new file mode 100644 index 000000000..9417d613d --- /dev/null +++ b/compiler/nir/stringcases.nim @@ -0,0 +1,200 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## included from ast2ir.nim + +#[ + +case s +of "abc", "abbd": + echo 1 +of "hah": + echo 2 +of "gah": + echo 3 + +# we produce code like this: + +if s[0] <= 'a': + if s == "abc: goto L1 + elif s == "abbd": goto L1 +else: + if s[2] <= 'h': + if s == "hah": goto L2 + elif s == "gah": goto L3 +goto afterCase + +L1: + echo 1 + goto afterCase +L2: + echo 2 + goto afterCase +L3: + echo 3 + goto afterCase + +afterCase: ... + +]# + +# We split the set of strings into 2 sets of roughly the same size. +# The condition used for splitting is a (position, char) tuple. +# Every string of length > position for which s[position] <= char is in one +# set else it is in the other set. + +from sequtils import addUnique + +type + Key = (LitId, LabelId) + +proc splitValue(strings: BiTable[string]; a: openArray[Key]; position: int): (char, float) = + var cand: seq[char] = @[] + for t in items a: + let s = strings[t[0]] + if s.len > position: cand.addUnique s[position] + + result = ('\0', -1.0) + for disc in items cand: + var hits = 0 + for t in items a: + let s = strings[t[0]] + if s.len > position and s[position] <= disc: + inc hits + # the split is the better, the more `hits` is close to `a.len / 2`: + let grade = 100000.0 - abs(hits.float - a.len.float / 2.0) + if grade > result[1]: + result = (disc, grade) + +proc tryAllPositions(strings: BiTable[string]; a: openArray[Key]): (char, int) = + var m = 0 + for t in items a: + m = max(m, strings[t[0]].len) + + result = ('\0', -1) + var best = -1.0 + for i in 0 ..< m: + let current = splitValue(strings, a, i) + if current[1] > best: + best = current[1] + result = (current[0], i) + +type + SearchKind = enum + LinearSearch, SplitSearch + SearchResult* = object + case kind: SearchKind + of LinearSearch: + a: seq[Key] + of SplitSearch: + span: int + best: (char, int) + +proc emitLinearSearch(strings: BiTable[string]; a: openArray[Key]; dest: var seq[SearchResult]) = + var d = SearchResult(kind: LinearSearch, a: @[]) + for x in a: d.a.add x + dest.add d + +proc split(strings: BiTable[string]; a: openArray[Key]; dest: var seq[SearchResult]) = + if a.len <= 4: + emitLinearSearch strings, a, dest + else: + let best = tryAllPositions(strings, a) + var groupA: seq[Key] = @[] + var groupB: seq[Key] = @[] + for t in items a: + let s = strings[t[0]] + if s.len > best[1] and s[best[1]] <= best[0]: + groupA.add t + else: + groupB.add t + if groupA.len == 0 or groupB.len == 0: + emitLinearSearch strings, a, dest + else: + let toPatch = dest.len + dest.add SearchResult(kind: SplitSearch, span: 1, best: best) + split strings, groupA, dest + split strings, groupB, dest + let dist = dest.len - toPatch + assert dist > 0 + dest[toPatch].span = dist + +proc toProblemDescription(c: var ProcCon; n: PNode): (seq[Key], LabelId) = + result = (@[], newLabels(c.labelGen, n.len)) + assert n.kind == nkCaseStmt + for i in 1..<n.len: + let it = n[i] + let thisBranch = LabelId(result[1].int + i - 1) + if it.kind == nkOfBranch: + for j in 0..<it.len-1: + assert it[j].kind in {nkStrLit..nkTripleStrLit} + result[0].add (c.lit.strings.getOrIncl(it[j].strVal), thisBranch) + +proc decodeSolution(c: var ProcCon; dest: var Tree; s: seq[SearchResult]; i: int; + selector: Value; info: PackedLineInfo) = + case s[i].kind + of SplitSearch: + let thenA = i+1 + let elseA = thenA + (if s[thenA].kind == LinearSearch: 1 else: s[thenA].span) + let best = s[i].best + + let tmp = getTemp(c, Bool8Id, info) + buildTyped dest, info, Asgn, Bool8Id: + dest.copyTree tmp + buildTyped dest, info, Call, Bool8Id: + c.addUseCodegenProc dest, "nimStrAtLe", info + dest.copyTree selector + dest.addIntVal c.lit.numbers, info, c.m.nativeIntId, best[1] + dest.addIntVal c.lit.numbers, info, Char8Id, best[0].int + + template then() = + c.decodeSolution dest, s, thenA, selector, info + template otherwise() = + c.decodeSolution dest, s, elseA, selector, info + buildIfThenElse tmp, then, otherwise + freeTemp c, tmp + + of LinearSearch: + let tmp = getTemp(c, Bool8Id, info) + for x in s[i].a: + buildTyped dest, info, Asgn, Bool8Id: + dest.copyTree tmp + buildTyped dest, info, Call, Bool8Id: + c.addUseCodegenProc dest, "eqStrings", info + dest.copyTree selector + dest.addStrLit info, x[0] + buildIf tmp: + c.code.gotoLabel info, Goto, x[1] + freeTemp c, tmp + +proc genStringCase(c: var ProcCon; n: PNode; d: var Value) = + let (problem, firstBranch) = toProblemDescription(c, n) + var solution: seq[SearchResult] = @[] + split c.lit.strings, problem, solution + + # XXX Todo move complex case selector into a temporary. + let selector = c.genx(n[0]) + + let info = toLineInfo(c, n.info) + decodeSolution c, c.code, solution, 0, selector, info + + let lend = newLabel(c.labelGen) + c.code.addLabel info, Goto, lend + for i in 1..<n.len: + let it = n[i] + let thisBranch = LabelId(firstBranch.int + i - 1) + c.code.addLabel info, Label, thisBranch + if it.kind == nkOfBranch: + gen(c, it.lastSon, d) + c.code.addLabel info, Goto, lend + else: + gen(c, it.lastSon, d) + + c.code.addLabel info, Label, lend + freeTemp c, selector diff --git a/compiler/nir/types2ir.nim b/compiler/nir/types2ir.nim index 835bef03c..76907d587 100644 --- a/compiler/nir/types2ir.nim +++ b/compiler/nir/types2ir.nim @@ -18,8 +18,8 @@ type g*: TypeGraph conf: ConfigRef -proc initTypesCon*(conf: ConfigRef): TypesCon = - TypesCon(g: initTypeGraph(), conf: conf) +proc initTypesCon*(conf: ConfigRef; lit: Literals): TypesCon = + TypesCon(g: initTypeGraph(lit), conf: conf) proc mangle(c: var TypesCon; t: PType): string = result = $sighashes.hashType(t, c.conf) @@ -67,11 +67,11 @@ proc objectToIr(c: var TypesCon; n: PNode; fieldTypes: Table[ItemId, TypeId]; un let subObj = openType(c.g, ObjectDecl) c.g.addName "uo_" & $unionId & "_" & $i objectToIr c, lastSon(n[i]), fieldTypes, unionId - discard sealType(c.g, subObj) + sealType(c.g, subObj) else: discard - discard sealType(c.g, u) + sealType(c.g, u) of nkSym: - c.g.addField n.sym.name.s & "_" & $n.sym.position, fieldTypes[n.sym.itemId] + c.g.addField n.sym.name.s & "_" & $n.sym.position, fieldTypes[n.sym.itemId], n.sym.offset else: assert false, "unknown node kind: " & $n.kind @@ -85,6 +85,9 @@ proc objectToIr(c: var TypesCon; t: PType): TypeId = collectFieldTypes c, t.n, fieldTypes let obj = openType(c.g, ObjectDecl) c.g.addName mangle(c, t) + c.g.addSize c.conf.getSize(t) + c.g.addAlign c.conf.getAlign(t) + if t[0] != nil: c.g.addNominalType(ObjectTy, mangle(c, t[0])) else: @@ -93,12 +96,13 @@ proc objectToIr(c: var TypesCon; t: PType): TypeId = let f2 = c.g.openType FieldDecl let voidPtr = openType(c.g, APtrTy) c.g.addBuiltinType(VoidId) - discard sealType(c.g, voidPtr) + sealType(c.g, voidPtr) + c.g.addOffset 0 # type field is always at offset 0 c.g.addName "m_type" - discard sealType(c.g, f2) # FieldDecl + sealType(c.g, f2) # FieldDecl objectToIr c, t.n, fieldTypes, unionId - result = sealType(c.g, obj) + result = finishType(c.g, obj) proc objectHeaderToIr(c: var TypesCon; t: PType): TypeId = result = c.g.nominalType(ObjectTy, mangle(c, t)) @@ -109,9 +113,18 @@ proc tupleToIr(c: var TypesCon; t: PType): TypeId = fieldTypes[i] = typeToIr(c, t[i]) let obj = openType(c.g, ObjectDecl) c.g.addName mangle(c, t) + c.g.addSize c.conf.getSize(t) + c.g.addAlign c.conf.getAlign(t) + + var accum = OffsetAccum(maxAlign: 1) for i in 0..<t.len: - c.g.addField "f_" & $i, fieldTypes[i] - result = sealType(c.g, obj) + let child = t[i] + c.g.addField "f_" & $i, fieldTypes[i], accum.offset + + computeSizeAlign(c.conf, child) + accum.align(child.align) + accum.inc(int32(child.size)) + result = finishType(c.g, obj) proc procToIr(c: var TypesCon; t: PType; addEnv = false): TypeId = var fieldTypes = newSeq[TypeId](0) @@ -137,11 +150,11 @@ proc procToIr(c: var TypesCon; t: PType; addEnv = false): TypeId = if addEnv: let a = openType(c.g, APtrTy) c.g.addBuiltinType(VoidId) - discard sealType(c.g, a) + sealType(c.g, a) if tfVarargs in t.flags: c.g.addVarargs() - result = sealType(c.g, obj) + result = finishType(c.g, obj) proc nativeInt(c: TypesCon): TypeId = case c.conf.target.intSize @@ -154,7 +167,7 @@ proc openArrayPayloadType*(c: var TypesCon; t: PType): TypeId = let elementType = typeToIr(c, e) let arr = c.g.openType AArrayPtrTy c.g.addType elementType - result = sealType(c.g, arr) # LastArrayTy + result = finishType(c.g, arr) # LastArrayTy proc openArrayToIr(c: var TypesCon; t: PType): TypeId = # object (a: ArrayPtr[T], len: int) @@ -167,38 +180,45 @@ proc openArrayToIr(c: var TypesCon; t: PType): TypeId = let p = openType(c.g, ObjectDecl) c.g.addName typeName + c.g.addSize c.conf.target.ptrSize*2 + c.g.addAlign c.conf.target.ptrSize let f = c.g.openType FieldDecl let arr = c.g.openType AArrayPtrTy c.g.addType elementType - discard sealType(c.g, arr) # LastArrayTy + sealType(c.g, arr) # LastArrayTy + c.g.addOffset 0 c.g.addName "data" - discard sealType(c.g, f) # FieldDecl + sealType(c.g, f) # FieldDecl - c.g.addField "len", c.nativeInt + c.g.addField "len", c.nativeInt, c.conf.target.ptrSize - result = sealType(c.g, p) # ObjectDecl + result = finishType(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 + c.g.addSize c.conf.target.ptrSize*2 + c.g.addAlign c.conf.target.ptrSize + + c.g.addField "cap", c.nativeInt, 0 let f = c.g.openType FieldDecl let arr = c.g.openType LastArrayTy c.g.addBuiltinType Char8Id - discard sealType(c.g, arr) # LastArrayTy + sealType(c.g, arr) # LastArrayTy + c.g.addOffset c.conf.target.ptrSize # comes after the len field c.g.addName "data" - discard sealType(c.g, f) # FieldDecl + sealType(c.g, f) # FieldDecl - discard sealType(c.g, p) + 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 + result = finishType(c.g, ffp) # APtrTy proc stringToIr(c: var TypesCon): TypeId = #[ @@ -216,16 +236,20 @@ proc stringToIr(c: var TypesCon): TypeId = let str = openType(c.g, ObjectDecl) c.g.addName "NimStringV2" - c.g.addField "len", c.nativeInt + c.g.addSize c.conf.target.ptrSize*2 + c.g.addAlign c.conf.target.ptrSize + + c.g.addField "len", c.nativeInt, 0 let fp = c.g.openType FieldDecl let ffp = c.g.openType APtrTy c.g.addNominalType ObjectTy, "NimStrPayload" - discard sealType(c.g, ffp) # APtrTy + sealType(c.g, ffp) # APtrTy + c.g.addOffset c.conf.target.ptrSize # comes after 'len' field c.g.addName "p" - discard sealType(c.g, fp) # FieldDecl + sealType(c.g, fp) # FieldDecl - result = sealType(c.g, str) # ObjectDecl + result = finishType(c.g, str) # ObjectDecl proc seqPayloadType(c: var TypesCon; t: PType): string = #[ @@ -241,21 +265,25 @@ proc seqPayloadType(c: var TypesCon; t: PType): string = let p = openType(c.g, ObjectDecl) c.g.addName payloadName - c.g.addField "cap", c.nativeInt + c.g.addSize c.conf.target.intSize + c.g.addAlign c.conf.target.intSize + + c.g.addField "cap", c.nativeInt, 0 let f = c.g.openType FieldDecl let arr = c.g.openType LastArrayTy c.g.addType elementType - discard sealType(c.g, arr) # LastArrayTy + sealType(c.g, arr) # LastArrayTy + c.g.addOffset c.conf.target.ptrSize c.g.addName "data" - discard sealType(c.g, f) # FieldDecl - discard sealType(c.g, p) + sealType(c.g, f) # FieldDecl + 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 + result = finishType(c.g, ffp) # APtrTy proc seqToIr(c: var TypesCon; t: PType): TypeId = #[ @@ -267,16 +295,20 @@ proc seqToIr(c: var TypesCon; t: PType): TypeId = let sq = openType(c.g, ObjectDecl) c.g.addName "NimSeqV2" & mangledBase - c.g.addField "len", c.nativeInt + c.g.addSize c.conf.getSize(t) + c.g.addAlign c.conf.getAlign(t) + + c.g.addField "len", c.nativeInt, 0 let fp = c.g.openType FieldDecl let ffp = c.g.openType APtrTy c.g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase - discard sealType(c.g, ffp) # APtrTy + sealType(c.g, ffp) # APtrTy + c.g.addOffset c.conf.target.ptrSize c.g.addName "p" - discard sealType(c.g, fp) # FieldDecl + sealType(c.g, fp) # FieldDecl - result = sealType(c.g, sq) # ObjectDecl + result = finishType(c.g, sq) # ObjectDecl proc closureToIr(c: var TypesCon; t: PType): TypeId = @@ -291,21 +323,25 @@ proc closureToIr(c: var TypesCon; t: PType): TypeId = let p = openType(c.g, ObjectDecl) c.g.addName typeName + c.g.addSize c.conf.getSize(t) + c.g.addAlign c.conf.getAlign(t) let f = c.g.openType FieldDecl c.g.addType procType + c.g.addOffset 0 c.g.addName "ClP_0" - discard sealType(c.g, f) # FieldDecl + 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) + sealType(c.g, voidPtr) + c.g.addOffset c.conf.target.ptrSize c.g.addName "ClE_0" - discard sealType(c.g, f2) # FieldDecl + sealType(c.g, f2) # FieldDecl - result = sealType(c.g, p) # ObjectDecl + result = finishType(c.g, p) # ObjectDecl proc bitsetBasetype*(c: var TypesCon; t: PType): TypeId = let s = int(getSize(c.conf, t)) @@ -376,8 +412,8 @@ proc typeToIr*(c: var TypesCon; t: PType): TypeId = 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) + c.g.addArrayLen n + result = finishType(c.g, a) of tyPtr, tyRef: cached(c, t): let e = t.lastSon @@ -385,12 +421,12 @@ proc typeToIr*(c: var TypesCon; t: PType): TypeId = let elemType = typeToIr(c, e.lastSon) let a = openType(c.g, AArrayPtrTy) c.g.addType(elemType) - result = sealType(c.g, a) + result = finishType(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) + result = finishType(c.g, a) of tyVar, tyLent: cached(c, t): let e = t.lastSon @@ -401,7 +437,7 @@ proc typeToIr*(c: var TypesCon; t: PType): TypeId = let elemType = typeToIr(c, e) let a = openType(c.g, APtrTy) c.g.addType(elemType) - result = sealType(c.g, a) + result = finishType(c.g, a) of tySet: let s = int(getSize(c.conf, t)) case s @@ -414,12 +450,13 @@ proc typeToIr*(c: var TypesCon; t: PType): TypeId = 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: + c.g.addArrayLen s + result = finishType(c.g, a) + of tyPointer, tyNil: + # tyNil can happen for code like: `const CRAP = nil` which we have in posix.nim let a = openType(c.g, APtrTy) c.g.addBuiltinType(VoidId) - result = sealType(c.g, a) + result = finishType(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` @@ -451,20 +488,20 @@ proc typeToIr*(c: var TypesCon; t: PType): TypeId = cached(c, t): let a = openType(c.g, AArrayPtrTy) c.g.addBuiltinType Char8Id - result = sealType(c.g, a) + result = finishType(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) + result = finishType(c.g, a) of tyUntyped, tyTyped: # this avoids a special case for system.echo which is not a generic but # uses `varargs[typed]`: result = VoidId of tyNone, tyEmpty, tyTypeDesc, - tyNil, tyGenericInvocation, tyProxy, tyBuiltInTypeClass, + tyGenericInvocation, tyProxy, tyBuiltInTypeClass, tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward: result = TypeId(-1) diff --git a/compiler/options.nim b/compiler/options.nim index a2f50b12b..b36f72693 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -137,13 +137,15 @@ type backendCpp = "cpp" backendJs = "js" backendObjc = "objc" + backendNir = "nir" # backendNimscript = "nimscript" # this could actually work # backendLlvm = "llvm" # probably not well supported; was cmdCompileToLLVM Command* = enum ## Nim's commands cmdNone # not yet processed command cmdUnknown # command unmapped - cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS + cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS, + cmdCompileToNir, cmdCrun # compile and run in nimache cmdTcc # run the project via TCC backend cmdCheck # semantic checking for whole project @@ -170,7 +172,8 @@ type # old unused: cmdInterpret, cmdDef: def feature (find definition for IDEs) const - cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS, cmdCrun} + cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, + cmdCompileToJS, cmdCrun, cmdCompileToNir} cmdDocLike* = {cmdDoc0, cmdDoc, cmdDoc2tex, cmdJsondoc0, cmdJsondoc, cmdCtags, cmdBuildindex} @@ -235,9 +238,9 @@ type laxEffects ## Lax effects system prior to Nim 2.0. verboseTypeMismatch - emitGenerics - ## generics are emitted in the module that contains them. - ## Useful for libraries that rely on local passC + emitGenerics + ## generics are emitted in the module that contains them. + ## Useful for libraries that rely on local passC SymbolFilesOption* = enum disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest diff --git a/compiler/pipelines.nim b/compiler/pipelines.nim index e9ee1ee8b..e4c484e1f 100644 --- a/compiler/pipelines.nim +++ b/compiler/pipelines.nim @@ -45,6 +45,8 @@ proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext): result = interpreterCode(bModule, semNode) of NirReplPass: result = runCode(bModule, semNode) + of NirPass: + result = nirBackend(bModule, semNode) of NonePass: raiseAssert "use setPipeLinePass to set a proper PipelinePass" @@ -107,6 +109,8 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator case graph.pipelinePass of CgenPass: setupCgen(graph, module, idgen) + of NirPass: + openNirBackend(graph, module, idgen) of JSgenPass: when not defined(leanCompiler): setupJSgen(graph, module, idgen) @@ -203,6 +207,8 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator discard interpreterCode(bModule, finalNode) of NirReplPass: discard runCode(bModule, finalNode) + of NirPass: + closeNirBackend(bModule, finalNode) of SemPass, GenDependPass: discard of Docgen2Pass, Docgen2TexPass: diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index 424d7450f..9e5a9ab90 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -29,11 +29,11 @@ proc raiseIllegalTypeRecursion() = raise newException(IllegalTypeRecursionError, "illegal type recursion") type - OffsetAccum = object - maxAlign: int32 - offset: int32 + OffsetAccum* = object + maxAlign*: int32 + offset*: int32 -proc inc(arg: var OffsetAccum; value: int32) = +proc inc*(arg: var OffsetAccum; value: int32) = if unlikely(value == szIllegalRecursion): raiseIllegalTypeRecursion() if value == szUnknownSize or arg.offset == szUnknownSize: arg.offset = szUnknownSize @@ -47,7 +47,7 @@ proc alignmentMax(a, b: int32): int32 = else: max(a, b) -proc align(arg: var OffsetAccum; value: int32) = +proc align*(arg: var OffsetAccum; value: int32) = if unlikely(value == szIllegalRecursion): raiseIllegalTypeRecursion() if value == szUnknownSize or arg.maxAlign == szUnknownSize or arg.offset == szUnknownSize: arg.maxAlign = szUnknownSize @@ -73,7 +73,7 @@ proc finish(arg: var OffsetAccum): int32 = result = align(arg.offset, arg.maxAlign) - arg.offset arg.offset += result -proc computeSizeAlign(conf: ConfigRef; typ: PType) +proc computeSizeAlign*(conf: ConfigRef; typ: PType) proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt = ## returns object alignment diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index ee8f2d67e..d6b26493c 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -178,7 +178,7 @@ proc newSeq[T](s: var seq[T], len: Natural) = shrink(s, 0) setLen(s, len) -proc sameSeqPayload(x: pointer, y: pointer): bool {.compilerproc, inline.} = +proc sameSeqPayload(x: pointer, y: pointer): bool {.compilerRtl, inl.} = result = cast[ptr NimRawSeq](x)[].p == cast[ptr NimRawSeq](y)[].p diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim index 5e4cda186..dbee10777 100644 --- a/lib/system/strs_v2.nim +++ b/lib/system/strs_v2.nim @@ -209,6 +209,9 @@ proc nimAddStrV1(s: var NimStringV2; src: NimStringV2) {.compilerRtl, inl.} = proc nimDestroyStrV1(s: NimStringV2) {.compilerRtl, inl.} = frees(s) +proc nimStrAtLe(s: string; idx: int; ch: char): bool {.compilerRtl, inl.} = + result = idx < s.len and s[idx] <= ch + func capacity*(self: string): int {.inline.} = ## Returns the current capacity of the string. # See https://github.com/nim-lang/RFCs/issues/460 |