From 8241e55023ee32c9ec0b5443723bbe91f4fe875f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 23 Jan 2021 08:06:15 +0100 Subject: [PATCH] IC: next steps (#16729) * IC: dead code elimination pass * preparations for a different codegen strategy * added documentation to the newly written code * IC: backend code * IC: backend adjustments * optimized the compiler a bit * IC: yet another massive refactoring * fixes regressions * cleanups --- compiler/ast.nim | 56 +++++++++++------ compiler/ccgexprs.nim | 97 +++++++++++++++++++++++------ compiler/ccgstmts.nim | 29 ++++----- compiler/ccgtypes.nim | 11 ++-- compiler/cgen.nim | 86 +++++++++----------------- compiler/cgendata.nim | 4 +- compiler/cgmeth.nim | 7 ++- compiler/guards.nim | 62 +++++++------------ compiler/ic/cbackend.nim | 102 +++++++++++++++++++++++++++++++ compiler/ic/dce.nim | 108 +++++++++++++++++++++++++++++++++ compiler/ic/packed_ast.nim | 10 +++ compiler/ic/rodfiles.nim | 3 +- compiler/ic/to_packed_ast.nim | 60 +++++++++++------- compiler/injectdestructors.nim | 16 ++--- compiler/liftdestructors.nim | 92 +++++++++++++++++----------- compiler/main.nim | 26 +++++--- compiler/modulegraphs.nim | 90 +++++++++++++++++++++++++++ compiler/msgs.nim | 2 +- compiler/options.nim | 4 +- compiler/sem.nim | 12 ++-- compiler/semcall.nim | 4 +- compiler/semdata.nim | 1 + compiler/seminst.nim | 8 +-- compiler/semmagic.nim | 10 ++- compiler/semparallel.nim | 20 +++--- compiler/sempass2.nim | 12 ++-- compiler/semstmts.nim | 23 ++++--- compiler/semtypes.nim | 22 +++++-- compiler/semtypinst.nim | 33 +++++----- compiler/transf.nim | 4 +- compiler/types.nim | 12 ++-- compiler/varpartitions.nim | 26 ++++---- 32 files changed, 729 insertions(+), 323 deletions(-) create mode 100644 compiler/ic/cbackend.nim create mode 100644 compiler/ic/dce.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 0c075cb120..c9eccadac2 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -720,6 +720,16 @@ type module*: int32 item*: int32 +proc `==`*(a, b: ItemId): bool {.inline.} = + a.item == b.item and a.module == b.module + +proc hash*(x: ItemId): Hash = + var h: Hash = hash(x.module) + h = h !& hash(x.item) + result = !$h + + +type TIdObj* = object of RootObj itemId*: ItemId PIdObj* = ref TIdObj @@ -830,10 +840,8 @@ type TSym* {.acyclic.} = object of TIdObj # Keep in sync with PackedSym # proc and type instantiations are cached in the generic symbol case kind*: TSymKind - of skType, skGenericParam: - typeInstCache*: seq[PType] of routineKinds: - procInstCache*: seq[PInstantiation] + #procInstCache*: seq[PInstantiation] gcUnsafetyReason*: PSym # for better error messages wrt gcsafe transformedBody*: PNode # cached body after transf pass of skLet, skVar, skField, skForVar: @@ -913,8 +921,6 @@ type owner*: PSym # the 'owner' of the type sym*: PSym # types have the sym associated with them # it is used for converting types to strings - attachedOps*: array[TTypeAttachedOp, PSym] # destructors, etc. - methods*: seq[(int,PSym)] # attached methods size*: BiggestInt # the size of the type in bytes # -1 means that the size is unkwown align*: int16 # the type's alignment requirements @@ -1069,11 +1075,7 @@ type module*: int32 symId*: int32 typeId*: int32 - -proc hash*(x: ItemId): Hash = - var h: Hash = hash(x.module) - h = h !& hash(x.item) - result = !$h + sealed*: bool const PackageModuleId* = -3'i32 @@ -1083,10 +1085,12 @@ proc idGeneratorFromModule*(m: PSym): IdGenerator = result = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0) proc nextSymId*(x: IdGenerator): ItemId {.inline.} = + assert(not x.sealed) inc x.symId result = ItemId(module: x.module, item: x.symId) proc nextTypeId*(x: IdGenerator): ItemId {.inline.} = + assert(not x.sealed) inc x.typeId result = ItemId(module: x.module, item: x.typeId) @@ -1210,11 +1214,32 @@ proc newTreeIT*(kind: TNodeKind; info: TLineInfo; typ: PType; children: varargs[ template previouslyInferred*(t: PType): PType = if t.sons.len > 1: t.lastSon else: nil +when false: + import tables, strutils + var x: CountTable[string] + + addQuitProc proc () {.noconv.} = + for k, v in pairs(x): + echo k + echo v + proc newSym*(symKind: TSymKind, name: PIdent, id: ItemId, owner: PSym, info: TLineInfo; options: TOptions = {}): PSym = # generates a symbol and initializes the hash field too result = PSym(name: name, kind: symKind, flags: {}, info: info, itemId: id, options: options, owner: owner, offset: defaultOffset) + when false: + if id.item > 2141: + let s = getStackTrace() + const words = ["createTypeBoundOps", + "initOperators", + "generateInstance", + "semIdentDef", "addLocalDecl"] + for w in words: + if w in s: + x.inc w + return + x.inc "" proc astdef*(s: PSym): PNode = # get only the definition (initializer) portion of the ast @@ -1422,7 +1447,6 @@ proc assignType*(dest, src: PType) = dest.n = src.n dest.size = src.size dest.align = src.align - dest.attachedOps = src.attachedOps dest.lockLevel = src.lockLevel # this fixes 'type TLock = TSysLock': if src.sym != nil: @@ -1622,11 +1646,9 @@ template transitionSymKindCommon*(k: TSymKind) = proc transitionGenericParamToType*(s: PSym) = transitionSymKindCommon(skType) - s.typeInstCache = obj.typeInstCache proc transitionRoutineSymKind*(s: PSym, kind: range[skProc..skTemplate]) = transitionSymKindCommon(kind) - s.procInstCache = obj.procInstCache s.gcUnsafetyReason = obj.gcUnsafetyReason s.transformedBody = obj.transformedBody @@ -1893,10 +1915,8 @@ when false: for i in 0..finalizer = (void*)$2;$n", [ti, rdLoc(f)]) if a.storage == OnHeap and usesWriteBarrier(p.config): @@ -2204,7 +2205,8 @@ proc genDestroy(p: BProc; n: PNode) = else: discard "nothing to do" else: let t = n[1].typ.skipTypes(abstractVar) - if t.destructor != nil and getBody(p.module.g.graph, t.destructor).len != 0: + let op = getAttachedOp(p.module.g.graph, t, attachedDestructor) + if op != nil and getBody(p.module.g.graph, op).len != 0: internalError(p.config, n.info, "destructor turned out to be not trivial") discard "ignore calls to the default destructor" @@ -2236,14 +2238,8 @@ proc genSlice(p: BProc; e: PNode; d: var TLoc) = proc genEnumToStr(p: BProc, e: PNode, d: var TLoc) = const ToStringProcSlot = -4 let t = e[1].typ.skipTypes(abstractInst+{tyRange}) - var toStrProc: PSym = nil - for idx, p in items(t.methods): - if idx == ToStringProcSlot: - toStrProc = p - break - if toStrProc == nil: - toStrProc = genEnumToStrProc(t, e.info, p.module.g.graph, p.module.idgen) - t.methods.add((ToStringProcSlot, toStrProc)) + let toStrProc = getToStringProc(p.module.g.graph, t) + # XXX need to modify this logic for IC. var n = copyTree(e) n[0] = newSymNode(toStrProc) expr(p, n, d) @@ -2645,6 +2641,57 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = if t.kind notin {tySequence, tyString}: d.storage = OnStatic +proc genConstSetup(p: BProc; sym: PSym): bool = + let m = p.module + useHeader(m, sym) + if sym.loc.k == locNone: + fillLoc(sym.loc, locData, sym.ast, mangleName(p.module, sym), OnStatic) + if m.hcrOn: incl(sym.loc.flags, lfIndirect) + result = lfNoDecl notin sym.loc.flags + +proc genConstHeader(m, q: BModule; p: BProc, sym: PSym) = + assert(sym.loc.r != nil) + if m.hcrOn: + m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r]); + m.initProc.procSec(cpsLocals).addf( + "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r, + getTypeDesc(m, sym.loc.t, skVar), getModuleDllPath(q, sym)]) + else: + let headerDecl = "extern NIM_CONST $1 $2;$n" % + [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r] + m.s[cfsData].add(headerDecl) + if sfExportc in sym.flags and p.module.g.generatedHeader != nil: + p.module.g.generatedHeader.s[cfsData].add(headerDecl) + +proc genConstDefinition(q: BModule; p: BProc; sym: PSym) = + # add a suffix for hcr - will later init the global pointer with this data + let actualConstName = if q.hcrOn: sym.loc.r & "_const" else: sym.loc.r + q.s[cfsData].addf("N_LIB_PRIVATE NIM_CONST $1 $2 = $3;$n", + [getTypeDesc(q, sym.typ), actualConstName, + genBracedInit(q.initProc, sym.ast, isConst = true, sym.typ)]) + if q.hcrOn: + # generate the global pointer with the real name + q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(q, sym.loc.t, skVar), sym.loc.r]) + # register it (but ignore the boolean result of hcrRegisterGlobal) + q.initProc.procSec(cpsLocals).addf( + "\thcrRegisterGlobal($1, \"$2\", sizeof($3), NULL, (void**)&$2);$n", + [getModuleDllPath(q, sym), sym.loc.r, rdLoc(sym.loc)]) + # always copy over the contents of the actual constant with the _const + # suffix ==> this means that the constant is reloadable & updatable! + q.initProc.procSec(cpsLocals).add(ropecg(q, + "\t#nimCopyMem((void*)$1, (NIM_CONST void*)&$2, sizeof($3));$n", + [sym.loc.r, actualConstName, rdLoc(sym.loc)])) + +proc genConstStmt(p: BProc, n: PNode) = + # This code is only used in the new DCE implementation. + assert useAliveDataFromDce in p.module.flags + let m = p.module + for it in n: + if it[0].kind == nkSym: + let sym = it[0].sym + if not isSimpleConst(sym.typ) and sym.itemId.item in m.alive and genConstSetup(p, sym): + genConstDefinition(m, p, sym) + proc expr(p: BProc, n: PNode, d: var TLoc) = when defined(nimCompilerStackraceHints): setFrameMsg p.config$n.info & " " & $n.kind @@ -2655,7 +2702,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = var sym = n.sym case sym.kind of skMethod: - if {sfDispatcher, sfForward} * sym.flags != {}: + if useAliveDataFromDce in p.module.flags or {sfDispatcher, sfForward} * sym.flags != {}: # we cannot produce code for the dispatcher yet: fillProcLoc(p.module, n) genProcPrototype(p.module, sym) @@ -2668,13 +2715,19 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = if sfCompileTime in sym.flags: localError(p.config, n.info, "request to generate code for .compileTime proc: " & sym.name.s) - genProc(p.module, sym) + if useAliveDataFromDce in p.module.flags: + fillProcLoc(p.module, n) + genProcPrototype(p.module, sym) + else: + genProc(p.module, sym) if sym.loc.r == nil or sym.loc.lode == nil: internalError(p.config, n.info, "expr: proc not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) of skConst: if isSimpleConst(sym.typ): putIntoDest(p, d, n, genLiteral(p, sym.ast, sym.typ), OnStatic) + elif useAliveDataFromDce in p.module.flags: + genConstHeader(p.module, p.module, p, sym) else: genComplexConst(p, sym, d) of skEnumField: @@ -2795,7 +2848,10 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of nkEmpty: discard of nkWhileStmt: genWhileStmt(p, n) of nkVarSection, nkLetSection: genVarStmt(p, n) - of nkConstSection: discard # consts generated lazily on use + of nkConstSection: + if useAliveDataFromDce in p.module.flags: + genConstStmt(p, n) + # else: consts generated lazily on use of nkForStmt: internalError(p.config, n.info, "for statement not eliminated") of nkCaseStmt: genCase(p, n, d) of nkReturnStmt: genReturnStmt(p, n) @@ -2840,13 +2896,16 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef: if n[genericParamsPos].kind == nkEmpty: var prc = n[namePos].sym - # due to a bug/limitation in the lambda lifting, unused inner procs - # are not transformed correctly. We work around this issue (#411) here - # by ensuring it's no inner proc (owner is a module): - if prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags: + if useAliveDataFromDce in p.module.flags: + if p.module.alive.contains(prc.itemId.item): + genProc(p.module, prc) + elif prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags: if ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or (prc.kind == skMethod): + # due to a bug/limitation in the lambda lifting, unused inner procs + # are not transformed correctly. We work around this issue (#411) here + # by ensuring it's no inner proc (owner is a module). # Generate proc even if empty body, bugfix #11651. genProc(p.module, prc) of nkParForStmt: genParForStmt(p, n) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 8c419caacb..7269b337c0 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -1530,20 +1530,21 @@ proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType, [rdLoc(a), rdLoc(tmp), discriminatorTableName(p.module, t, field), intLiteral(toInt64(lengthOrd(p.config, field.typ))+1)]) -proc genCaseObjDiscMapping(p: BProc, e: PNode, t: PType, field: PSym; d: var TLoc) = - const ObjDiscMappingProcSlot = -5 - var theProc: PSym = nil - for idx, p in items(t.methods): - if idx == ObjDiscMappingProcSlot: - theProc = p - break - if theProc == nil: - theProc = genCaseObjDiscMapping(t, field, e.info, p.module.g.graph, p.module.idgen) - t.methods.add((ObjDiscMappingProcSlot, theProc)) - var call = newNodeIT(nkCall, e.info, getSysType(p.module.g.graph, e.info, tyUInt8)) - call.add newSymNode(theProc) - call.add e - expr(p, call, d) +when false: + proc genCaseObjDiscMapping(p: BProc, e: PNode, t: PType, field: PSym; d: var TLoc) = + const ObjDiscMappingProcSlot = -5 + var theProc: PSym = nil + for idx, p in items(t.methods): + if idx == ObjDiscMappingProcSlot: + theProc = p + break + if theProc == nil: + theProc = genCaseObjDiscMapping(t, field, e.info, p.module.g.graph, p.module.idgen) + t.methods.add((ObjDiscMappingProcSlot, theProc)) + var call = newNodeIT(nkCall, e.info, getSysType(p.module.g.graph, e.info, tyUInt8)) + call.add newSymNode(theProc) + call.add e + expr(p, call, d) proc asgnFieldDiscriminant(p: BProc, e: PNode) = var a, tmp: TLoc diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index e7e9d97d85..9c751b1ca1 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -1308,7 +1308,7 @@ proc genTypeInfo2Name(m: BModule; t: PType): Rope = proc isTrivialProc(g: ModuleGraph; s: PSym): bool {.inline.} = getBody(g, s).len == 0 proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp): Rope = - let theProc = t.attachedOps[op] + let theProc = getAttachedOp(m.g.graph, t, op) if theProc != nil and not isTrivialProc(m.g.graph, theProc): # the prototype of a destructor is ``=destroy(x: var T)`` and that of a # finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling @@ -1476,10 +1476,11 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope = genTupleInfo(m, x, origType, result, info) else: internalError(m.config, "genTypeInfoV1(" & $t.kind & ')') - if t.attachedOps[attachedDeepCopy] != nil: - genDeepCopyProc(m, t.attachedOps[attachedDeepCopy], result) - elif origType.attachedOps[attachedDeepCopy] != nil: - genDeepCopyProc(m, origType.attachedOps[attachedDeepCopy], result) + var op = getAttachedOp(m.g.graph, t, attachedDeepCopy) + if op == nil: + op = getAttachedOp(m.g.graph, origType, attachedDeepCopy) + if op != nil: + genDeepCopyProc(m, op, result) if optTinyRtti in m.config.globalOptions and t.kind == tyObject and sfImportc notin t.sym.flags: let v2info = genTypeInfoV2(m, origType, info) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 220b8e3c12..967afb0642 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -14,7 +14,7 @@ import nversion, nimsets, msgs, bitsets, idents, types, ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth, rodutils, renderer, cgendata, ccgmerge, aliases, - lowerings, tables, sets, ndi, lineinfos, pathutils, transf, enumtostr, + lowerings, tables, sets, ndi, lineinfos, pathutils, transf, injectdestructors when not defined(leanCompiler): @@ -1189,48 +1189,16 @@ proc genProcNoForward(m: BModule, prc: PSym) = if sfInfixCall notin prc.flags: genProcPrototype(m, prc) proc requestConstImpl(p: BProc, sym: PSym) = - var m = p.module - useHeader(m, sym) - if sym.loc.k == locNone: - fillLoc(sym.loc, locData, sym.ast, mangleName(p.module, sym), OnStatic) - if m.hcrOn: incl(sym.loc.flags, lfIndirect) - - if lfNoDecl in sym.loc.flags: return - # declare implementation: - var q = findPendingModule(m, sym) - if q != nil and not containsOrIncl(q.declaredThings, sym.id): - assert q.initProc.module == q - # add a suffix for hcr - will later init the global pointer with this data - let actualConstName = if m.hcrOn: sym.loc.r & "_const" else: sym.loc.r - q.s[cfsData].addf("N_LIB_PRIVATE NIM_CONST $1 $2 = $3;$n", - [getTypeDesc(q, sym.typ), actualConstName, - genBracedInit(q.initProc, sym.ast, isConst = true, sym.typ)]) - if m.hcrOn: - # generate the global pointer with the real name - q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r]) - # register it (but ignore the boolean result of hcrRegisterGlobal) - q.initProc.procSec(cpsLocals).addf( - "\thcrRegisterGlobal($1, \"$2\", sizeof($3), NULL, (void**)&$2);$n", - [getModuleDllPath(q, sym), sym.loc.r, rdLoc(sym.loc)]) - # always copy over the contents of the actual constant with the _const - # suffix ==> this means that the constant is reloadable & updatable! - q.initProc.procSec(cpsLocals).add(ropecg(q, - "\t#nimCopyMem((void*)$1, (NIM_CONST void*)&$2, sizeof($3));$n", - [sym.loc.r, actualConstName, rdLoc(sym.loc)])) - # declare header: - if q != m and not containsOrIncl(m.declaredThings, sym.id): - assert(sym.loc.r != nil) - if m.hcrOn: - m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r]); - m.initProc.procSec(cpsLocals).addf( - "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r, - getTypeDesc(m, sym.loc.t, skVar), getModuleDllPath(q, sym)]) - else: - let headerDecl = "extern NIM_CONST $1 $2;$n" % - [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r] - m.s[cfsData].add(headerDecl) - if sfExportc in sym.flags and p.module.g.generatedHeader != nil: - p.module.g.generatedHeader.s[cfsData].add(headerDecl) + if genConstSetup(p, sym): + let m = p.module + # declare implementation: + var q = findPendingModule(m, sym) + if q != nil and not containsOrIncl(q.declaredThings, sym.id): + assert q.initProc.module == q + genConstDefinition(q, p, sym) + # declare header: + if q != m and not containsOrIncl(m.declaredThings, sym.id): + genConstHeader(m, q, p, sym) proc isActivated(prc: PSym): bool = prc.typ != nil @@ -1839,7 +1807,7 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: AbsoluteFile): BModule proc rawNewModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule = result = rawNewModule(g, module, AbsoluteFile toFullPath(conf, module.position.FileIndex)) -proc newModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule = +proc newModule*(g: BModuleList; module: PSym; conf: ConfigRef): BModule = # we should create only one cgen module for each module sym result = rawNewModule(g, module, conf) if module.position >= g.modules.len: @@ -1922,13 +1890,9 @@ proc addHcrInitGuards(p: BProc, n: PNode, inInitGuard: var bool) = genStmts(p, n) -proc myProcess(b: PPassContext, n: PNode): PNode = - result = n - if b == nil: return - var m = BModule(b) - if passes.skipCodegen(m.config, n) or - not moduleHasChanged(m.g.graph, m.module): - return +proc genTopLevelStmt*(m: BModule; n: PNode) = + ## Also called from `ic/cbackend.nim`. + if passes.skipCodegen(m.config, n): return m.initProc.options = initProcOptions(m) #softRnl = if optLineDir in m.config.options: noRnl else: rnl # XXX replicate this logic! @@ -1941,6 +1905,12 @@ proc myProcess(b: PPassContext, n: PNode): PNode = else: genProcBody(m.initProc, transformedN) +proc myProcess(b: PPassContext, n: PNode): PNode = + result = n + if b != nil: + var m = BModule(b) + genTopLevelStmt(m, n) + proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool = if optForceFullMake notin m.config.globalOptions: if not moduleHasChanged(m.g.graph, m.module): @@ -2013,7 +1983,7 @@ proc writeModule(m: BModule, pending: bool) = # that ``system.o`` is missing, so we need to call the C compiler for it: var cf = Cfile(nimname: m.module.name.s, cname: cfile, obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {}) - if not fileExists(cf.obj): cf.flags = {CfileFlag.Cached} + if fileExists(cf.obj): cf.flags = {CfileFlag.Cached} addFileToCompile(m.config, cf) onExit() @@ -2037,10 +2007,8 @@ proc updateCachedModule(m: BModule) = cf.flags = {CfileFlag.Cached} addFileToCompile(m.config, cf) -proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = - result = n - if b == nil: return - var m = BModule(b) +proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) = + ## Also called from IC. if sfMainModule in m.module.flags: # phase ordering problem here: We need to announce this # dependency to 'nimTestErrorFlag' before system.c has been written to disk. @@ -2091,6 +2059,12 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = let mm = m m.g.modulesClosed.add mm + +proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = + result = n + if b == nil: return + finalCodegenActions(graph, BModule(b), n) + proc genForwardedProcs(g: BModuleList) = # Forward declared proc:s lack bodies when first encountered, so they're given # a second pass here diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 8e50943366..6a128466a5 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -112,7 +112,8 @@ type isHeaderFile, # C source file is the header file includesStringh, # C source file already includes ```` objHasKidsValid # whether we can rely on tfObjHasKids - + useAliveDataFromDce # use the `alive: IntSet` field instead of + # computing alive data on our own. BModuleList* = ref object of RootObj mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope @@ -154,6 +155,7 @@ type forwTypeCache*: TypeCache # cache for forward declarations of types declaredThings*: IntSet # things we have declared in this .c file declaredProtos*: IntSet # prototypes we have declared in this .c file + alive*: IntSet # symbol IDs of alive data as computed by `dce.nim` headerFiles*: seq[string] # needed headers to include typeInfoMarker*: TypeCache # needed for generating type information typeInfoMarkerV2*: TypeCache diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index a995804c73..b144658289 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -107,11 +107,14 @@ proc attachDispatcher(s: PSym, dispatcher: PNode) = s.ast[resultPos] = newNodeI(nkEmpty, s.info) s.ast[dispatcherPos] = dispatcher -proc createDispatcher(s: PSym; idgen: IdGenerator): PSym = +proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym = var disp = copySym(s, nextSymId(idgen)) incl(disp.flags, sfDispatcher) excl(disp.flags, sfExported) + let old = disp.typ disp.typ = copyType(disp.typ, nextTypeId(idgen), disp.typ.owner) + copyTypeProps(g, idgen.module, disp.typ, old) + # we can't inline the dispatcher itself (for now): if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall disp.ast = copyTree(s.ast) @@ -177,7 +180,7 @@ proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym, fromCache: bool) = of Invalid: if witness.isNil: witness = g.methods[i].methods[0] # create a new dispatcher: - g.methods.add((methods: @[s], dispatcher: createDispatcher(s, idgen))) + g.methods.add((methods: @[s], dispatcher: createDispatcher(s, g, idgen))) #echo "adding ", s.info #if fromCache: # internalError(s.info, "no method dispatcher found") diff --git a/compiler/guards.nim b/compiler/guards.nim index 4f07df201a..4a7a5f2841 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -82,26 +82,6 @@ proc isLetLocation(m: PNode, isApprox: bool): bool = proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true) -type - Operators* = object - opNot*, opContains*, opLe*, opLt*, opAnd*, opOr*, opIsNil*, opEq*: PSym - opAdd*, opSub*, opMul*, opDiv*, opLen*: PSym - -proc initOperators*(g: ModuleGraph): Operators = - result.opLe = createMagic(g, "<=", mLeI) - result.opLt = createMagic(g, "<", mLtI) - result.opAnd = createMagic(g, "and", mAnd) - result.opOr = createMagic(g, "or", mOr) - result.opIsNil = createMagic(g, "isnil", mIsNil) - result.opEq = createMagic(g, "==", mEqI) - result.opAdd = createMagic(g, "+", mAddI) - result.opSub = createMagic(g, "-", mSubI) - result.opMul = createMagic(g, "*", mMulI) - result.opDiv = createMagic(g, "div", mDivI) - result.opLen = createMagic(g, "len", mLengthSeq) - result.opNot = createMagic(g, "not", mNot) - result.opContains = createMagic(g, "contains", mInSet) - proc swapArgs(fact: PNode, newOp: PSym): PNode = result = newNodeI(nkCall, fact.info, 3) result[0] = newSymNode(newOp) @@ -404,16 +384,16 @@ proc usefulFact(n: PNode; o: Operators): PNode = type TModel* = object s*: seq[PNode] # the "knowledge base" - o*: Operators + g*: ModuleGraph beSmart*: bool proc addFact*(m: var TModel, nn: PNode) = - let n = usefulFact(nn, m.o) + let n = usefulFact(nn, m.g.operators) if n != nil: if not m.beSmart: m.s.add n else: - let c = canon(n, m.o) + let c = canon(n, m.g.operators) if c.getMagic == mAnd: addFact(m, c[1]) addFact(m, c[2]) @@ -421,7 +401,7 @@ proc addFact*(m: var TModel, nn: PNode) = m.s.add c proc addFactNeg*(m: var TModel, n: PNode) = - let n = n.neg(m.o) + let n = n.neg(m.g.operators) if n != nil: addFact(m, n) proc sameOpr(a, b: PSym): bool = @@ -740,7 +720,7 @@ proc doesImply*(facts: TModel, prop: PNode): TImplication = if result != impUnknown: return proc impliesNotNil*(m: TModel, arg: PNode): TImplication = - result = doesImply(m, m.o.opIsNil.buildCall(arg).neg(m.o)) + result = doesImply(m, m.g.operators.opIsNil.buildCall(arg).neg(m.g.operators)) proc simpleSlice*(a, b: PNode): BiggestInt = # returns 'c' if a..b matches (i+c)..(i+c), -1 otherwise. (i)..(i) is matched @@ -833,7 +813,7 @@ proc ple(m: TModel; a, b: PNode): TImplication = if b.getMagic in someAdd: if zero() <=? b[2] and a <=? b[1]: return impYes # x <= y-c if x+c <= y - if b[2] <=? zero() and (canon(m.o.opSub.buildCall(a, b[2]), m.o) <=? b[1]): + if b[2] <=? zero() and (canon(m.g.operators.opSub.buildCall(a, b[2]), m.g.operators) <=? b[1]): return impYes # x+c <= y if c <= 0 and x <= y @@ -847,20 +827,20 @@ proc ple(m: TModel; a, b: PNode): TImplication = if a.getMagic in someMul and a[2].isValue and a[1].getMagic in someDiv and a[1][2].isValue: # simplify (x div 4) * 2 <= y to x div (c div d) <= y - if ple(m, buildCall(m.o.opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes: + if ple(m, buildCall(m.g.operators.opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes: return impYes # x*3 + x == x*4. It follows that: # x*3 + y <= x*4 if y <= x and 3 <= 4 if a =~ x*dc + y and b =~ x2*ec: if sameTree(x, x2): - let ec1 = m.o.opAdd.buildCall(ec, minusOne()) + let ec1 = m.g.operators.opAdd.buildCall(ec, minusOne()) if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? x: return impYes elif a =~ x*dc and b =~ x2*ec + y: #echo "BUG cam ehrer e ", a, " <=? ", b if sameTree(x, x2): - let ec1 = m.o.opAdd.buildCall(ec, minusOne()) + let ec1 = m.g.operators.opAdd.buildCall(ec, minusOne()) if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? zero(): return impYes @@ -963,12 +943,12 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication = var b = bb if replacements.len > 0: m.s = @[] - m.o = model.o + m.g = model.g # make the other facts consistent: for fact in model.s: if fact != nil and fact.getMagic notin someEq: # XXX 'canon' should not be necessary here, but it is - m.s.add applyReplacements(fact, replacements).canon(m.o) + m.s.add applyReplacements(fact, replacements).canon(m.g.operators) a = applyReplacements(aa, replacements) b = applyReplacements(bb, replacements) else: @@ -977,19 +957,19 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication = result = pleViaModelRec(m, a, b) proc proveLe*(m: TModel; a, b: PNode): TImplication = - let x = canon(m.o.opLe.buildCall(a, b), m.o) + let x = canon(m.g.operators.opLe.buildCall(a, b), m.g.operators) #echo "ROOT ", renderTree(x[1]), " <=? ", renderTree(x[2]) result = ple(m, x[1], x[2]) if result == impUnknown: # try an alternative: a <= b iff not (b < a) iff not (b+1 <= a): - let y = canon(m.o.opLe.buildCall(m.o.opAdd.buildCall(b, one()), a), m.o) + let y = canon(m.g.operators.opLe.buildCall(m.g.operators.opAdd.buildCall(b, one()), a), m.g.operators) result = ~ple(m, y[1], y[2]) proc addFactLe*(m: var TModel; a, b: PNode) = - m.s.add canon(m.o.opLe.buildCall(a, b), m.o) + m.s.add canon(m.g.operators.opLe.buildCall(a, b), m.g.operators) proc addFactLt*(m: var TModel; a, b: PNode) = - let bb = m.o.opAdd.buildCall(b, minusOne()) + let bb = m.g.operators.opAdd.buildCall(b, minusOne()) addFactLe(m, a, bb) proc settype(n: PNode): PType = @@ -1021,14 +1001,14 @@ proc buildElse(n: PNode; o: Operators): PNode = proc addDiscriminantFact*(m: var TModel, n: PNode) = var fact = newNodeI(nkCall, n.info, 3) - fact[0] = newSymNode(m.o.opEq) + fact[0] = newSymNode(m.g.operators.opEq) fact[1] = n[0] fact[2] = n[1] m.s.add fact proc addAsgnFact*(m: var TModel, key, value: PNode) = var fact = newNodeI(nkCall, key.info, 3) - fact[0] = newSymNode(m.o.opEq) + fact[0] = newSymNode(m.g.operators.opEq) fact[1] = key fact[2] = value m.s.add fact @@ -1044,7 +1024,7 @@ proc sameSubexprs*(m: TModel; a, b: PNode): bool = # However, nil checking requires exactly the same mechanism! But for now # we simply use sameTree and live with the unsoundness of the analysis. var check = newNodeI(nkCall, a.info, 3) - check[0] = newSymNode(m.o.opEq) + check[0] = newSymNode(m.g.operators.opEq) check[1] = a check[2] = b result = m.doesImply(check) == impYes @@ -1052,9 +1032,9 @@ proc sameSubexprs*(m: TModel; a, b: PNode): bool = proc addCaseBranchFacts*(m: var TModel, n: PNode, i: int) = let branch = n[i] if branch.kind == nkOfBranch: - m.s.add buildOf(branch, n[0], m.o) + m.s.add buildOf(branch, n[0], m.g.operators) else: - m.s.add n.buildElse(m.o).neg(m.o) + m.s.add n.buildElse(m.g.operators).neg(m.g.operators) proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode = if check[1].kind == nkCurly: @@ -1072,6 +1052,6 @@ proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode = proc checkFieldAccess*(m: TModel, n: PNode; conf: ConfigRef) = for i in 1.. 0: + let (modId, ast) = c.stack.pop() + c.thisModule = modId + aliveCode(c, g, g[modId].fromDisk.bodies, ast) + +proc computeAliveSyms*(g: PackedModuleGraph; conf: ConfigRef): AliveSyms = + ## Entry point for our DCE algorithm. + var c = AliveContext(stack: @[], decoder: PackedDecoder(config: conf), + thisModule: -1, alive: newSeq[IntSet](g.len)) + for i in countdown(high(g), 0): + if g[i].status != undefined: + c.thisModule = i + for p in allNodes(g[i].fromDisk.topLevel): + aliveCode(c, g, g[i].fromDisk.topLevel, p) + followNow(c, g) + result = move(c.alive) + +proc isAlive*(a: AliveSyms; module: int, item: int32): bool = + ## Backends use this to query if a symbol is `alive` which means + ## we need to produce (C/C++/etc) code for it. + result = a[module].contains(item) + diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim index 708761764b..213b21b233 100644 --- a/compiler/ic/packed_ast.nim +++ b/compiler/ic/packed_ast.nim @@ -290,6 +290,9 @@ template typ*(n: NodePos): PackedItemId = template flags*(n: NodePos): TNodeFlags = tree.nodes[n.int].flags +template operand*(n: NodePos): int32 = + tree.nodes[n.int].operand + proc span*(tree: PackedTree; pos: int): int {.inline.} = if isAtom(tree, pos): 1 else: tree.nodes[pos].operand @@ -451,3 +454,10 @@ when false: dest.add nkStrLit, msg, n.info copyTree(dest, tree, n) patch dest, patchPos + +iterator allNodes*(tree: PackedTree): NodePos = + var p = 0 + while p < tree.len: + yield NodePos(p) + let s = span(tree, p) + inc p, s diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim index fe71f2441f..98399ceded 100644 --- a/compiler/ic/rodfiles.nim +++ b/compiler/ic/rodfiles.nim @@ -31,6 +31,7 @@ type bodiesSection symsSection typesSection + aliveSymsSection # beware, this is stored in a `.alivesyms` file. RodFileError* = enum ok, tooBig, cannotOpen, ioFailure, wrongHeader, wrongSection, configMismatch, @@ -134,7 +135,7 @@ proc loadHeader*(f: var RodFile) = proc storeSection*(f: var RodFile; s: RodSection) = if f.err != ok: return - assert f.currentSection == pred s + assert f.currentSection < s f.currentSection = s storePrim(f, s) diff --git a/compiler/ic/to_packed_ast.nim b/compiler/ic/to_packed_ast.nim index cf1e940d31..d3e68d6cab 100644 --- a/compiler/ic/to_packed_ast.nim +++ b/compiler/ic/to_packed_ast.nim @@ -107,7 +107,7 @@ proc toLitId(x: FileIndex; c: var PackedEncoder; m: var PackedModule): LitId = c.lastLit = result assert result != LitId(0) -proc toFileIndex(x: LitId; m: PackedModule; config: ConfigRef): FileIndex = +proc toFileIndex*(x: LitId; m: PackedModule; config: ConfigRef): FileIndex = result = msgs.fileInfoIdx(config, AbsoluteFile m.sh.strings[x]) proc includesIdentical(m: var PackedModule; config: ConfigRef): bool = @@ -280,16 +280,19 @@ proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemI paddingAtEnd: t.paddingAtEnd, lockLevel: t.lockLevel) storeNode(p, t, n) - for op, s in pairs t.attachedOps: - c.addMissing s - p.attachedOps[op] = s.safeItemId(c, m) + when false: + for op, s in pairs t.attachedOps: + c.addMissing s + p.attachedOps[op] = s.safeItemId(c, m) p.typeInst = t.typeInst.storeType(c, m) for kid in items t.sons: p.types.add kid.storeType(c, m) - for i, s in items t.methods: - c.addMissing s - p.methods.add (i, s.safeItemId(c, m)) + + when false: + for i, s in items t.methods: + c.addMissing s + p.methods.add (i, s.safeItemId(c, m)) c.addMissing t.sym p.sym = t.sym.safeItemId(c, m) c.addMissing t.owner @@ -568,11 +571,11 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac type PackedDecoder* = object - lastModule*: int - lastLit*: LitId - lastFile*: FileIndex # remember the last lookup entry. + lastModule: int + lastLit: LitId + lastFile: FileIndex # remember the last lookup entry. config*: ConfigRef - cache: IdentCache + cache*: IdentCache type ModuleStatus* = enum @@ -596,7 +599,7 @@ type proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; t: PackedItemId): PType proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; s: PackedItemId): PSym -proc toFileIndexCached(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; f: LitId): FileIndex = +proc toFileIndexCached*(c: var PackedDecoder; g: PackedModuleGraph; thisModule: int; f: LitId): FileIndex = if c.lastLit == f and c.lastModule == thisModule: result = c.lastFile else: @@ -611,8 +614,8 @@ proc translateLineInfo(c: var PackedDecoder; g: var PackedModuleGraph; thisModul result = TLineInfo(line: x.line, col: x.col, fileIndex: toFileIndexCached(c, g, thisModule, x.file)) -proc loadNodes(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; - tree: PackedTree; n: NodePos): PNode = +proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; + tree: PackedTree; n: NodePos): PNode = let k = n.kind if k == nkNilRodNode: return nil @@ -647,6 +650,14 @@ proc loadNodes(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; for n0 in sonsReadonly(tree, n): result.addAllowNil loadNodes(c, g, thisModule, tree, n0) +proc initPackedDecoder*(config: ConfigRef; cache: IdentCache): PackedDecoder = + result = PackedDecoder( + lastModule: int32(-1), + lastLit: LitId(0), + lastFile: FileIndex(-1), + config: config, + cache: cache) + proc loadProcHeader(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; tree: PackedTree; n: NodePos): PNode = # do not load the body of the proc. This will be done later in @@ -761,14 +772,16 @@ proc typeBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; t: PackedType; si, item: int32; result: PType) = result.sym = loadSym(c, g, si, t.sym) result.owner = loadSym(c, g, si, t.owner) - for op, item in pairs t.attachedOps: - result.attachedOps[op] = loadSym(c, g, si, item) + when false: + for op, item in pairs t.attachedOps: + result.attachedOps[op] = loadSym(c, g, si, item) result.typeInst = loadType(c, g, si, t.typeInst) for son in items t.types: result.sons.add loadType(c, g, si, son) loadAstBody(t, n) - for gen, id in items t.methods: - result.methods.add((gen, loadSym(c, g, si, id))) + when false: + for gen, id in items t.methods: + result.methods.add((gen, loadSym(c, g, si, id))) proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; t: PackedItemId): PType = if t == nilItemId: @@ -819,11 +832,8 @@ proc loadToReplayNodes(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCa lastFile: FileIndex(-1), config: conf, cache: cache) - var p = 0 - while p < m.fromDisk.toReplay.len: - m.module.ast.add loadNodes(decoder, g, int(fileIdx), m.fromDisk.toReplay, NodePos p) - let s = span(m.fromDisk.toReplay, p) - inc p, s + for p in allNodes(m.fromDisk.toReplay): + m.module.ast.add loadNodes(decoder, g, int(fileIdx), m.fromDisk.toReplay, p) proc needsRecompile(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache; fileIdx: FileIndex): bool = @@ -979,6 +989,10 @@ proc interfaceSymbol*(config: ConfigRef, cache: IdentCache; let values = g[int module].iface.getOrDefault(name) result = loadSym(decoder, g, int(module), values[0]) +proc idgenFromLoadedModule*(m: LoadedModule): IdGenerator = + IdGenerator(module: m.module.itemId.module, symId: int32 m.fromDisk.sh.syms.len, + typeId: int32 m.fromDisk.sh.types.len) + # ------------------------- .rod file viewer --------------------------------- proc rodViewer*(rodfile: AbsoluteFile; config: ConfigRef, cache: IdentCache) = diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index fc404f90f6..cb9547bf23 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -260,13 +260,13 @@ proc genOp(c: var Con; op: PSym; dest: PNode): PNode = result = newTree(nkCall, newSymNode(op), addrExp) proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode = - var op = t.attachedOps[kind] + var op = getAttachedOp(c.graph, t, kind) if op == nil or op.ast[genericParamsPos].kind != nkEmpty: # give up and find the canonical type instead: let h = sighashes.hashType(t, {CoType, CoConsiderOwned, CoDistinct}) let canon = c.graph.canonTypes.getOrDefault(h) if canon != nil: - op = canon.attachedOps[kind] + op = getAttachedOp(c.graph, canon, kind) if op == nil: #echo dest.typ.id globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] & @@ -287,9 +287,9 @@ proc genDestroy(c: var Con; dest: PNode): PNode = proc canBeMoved(c: Con; t: PType): bool {.inline.} = let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) if optOwnedRefs in c.graph.config.globalOptions: - result = t.kind != tyRef and t.attachedOps[attachedSink] != nil + result = t.kind != tyRef and getAttachedOp(c.graph, t, attachedSink) != nil else: - result = t.attachedOps[attachedSink] != nil + result = getAttachedOp(c.graph, t, attachedSink) != nil proc isNoInit(dest: PNode): bool {.inline.} = result = dest.kind == nkSym and sfNoInit in dest.sym.flags @@ -302,7 +302,7 @@ proc genSink(c: var Con; dest, ri: PNode, isDecl = false): PNode = result = newTree(nkFastAsgn, dest, ri) else: let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink}) - if t.attachedOps[attachedSink] != nil: + if getAttachedOp(c.graph, t, attachedSink) != nil: result = c.genOp(t, attachedSink, dest, ri) result.add ri else: @@ -375,8 +375,8 @@ proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode = let objType = leDotExpr[0].typ if hasDestructor(c, objType): - if objType.attachedOps[attachedDestructor] != nil and - sfOverriden in objType.attachedOps[attachedDestructor].flags: + if getAttachedOp(c.graph, objType, attachedDestructor) != nil and + sfOverriden in getAttachedOp(c.graph, objType, attachedDestructor).flags: localError(c.graph.config, n.info, errGenerated, """Assignment to discriminant for objects with user defined destructor is not supported, object must have default destructor. It is best to factor out piece of object that needs custom destructor into separate object or not use discriminator assignment""") result.add newTree(nkFastAsgn, le, tmp) @@ -1095,7 +1095,7 @@ proc injectDestructorCalls*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n: echo n if optCursorInference in g.config.options: - computeCursors(owner, n, g.config) + computeCursors(owner, n, g) var scope: Scope let body = p(n, c, scope, normal) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index f797423897..8429a353d4 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -29,6 +29,10 @@ type c: PContext # c can be nil, then we are called from lambdalifting! idgen: IdGenerator +template destructor*(t: PType): PSym = getAttachedOp(c.g, t, attachedDestructor) +template assignment*(t: PType): PSym = getAttachedOp(c.g, t, attachedAsgn) +template asink*(t: PType): PSym = getAttachedOp(c.g, t, attachedSink) + proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo; idgen: IdGenerator): PSym @@ -42,8 +46,9 @@ proc at(a, i: PNode, elemType: PType): PNode = result[1] = i result.typ = elemType -proc destructorOverriden(t: PType): bool = - t.attachedOps[attachedDestructor] != nil and sfOverriden in t.attachedOps[attachedDestructor].flags +proc destructorOverriden(g: ModuleGraph; t: PType): bool = + let op = getAttachedOp(g, t, attachedDestructor) + op != nil and sfOverriden in op.flags proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) = for i in 0.. 0: return # issue #9933 - cgenWriteModules(graph.backend, conf) - if conf.cmd != cmdTcc: + if conf.symbolFiles == disabledSf: + cgenWriteModules(graph.backend, conf) + else: + generateCode(graph) + # graph.backend can be nil under IC when nothing changed at all: + if graph.backend != nil: + cgenWriteModules(graph.backend, conf) + if conf.cmd != cmdTcc and graph.backend != nil: extccomp.callCCompiler(conf) # for now we do not support writing out a .json file with the build instructions when HCR is on if not conf.hcrOn: diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 8498cb2b96..32c68e7369 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -27,9 +27,20 @@ type pureEnums*: seq[PSym] interf: TStrTable + Operators* = object + opNot*, opContains*, opLe*, opLt*, opAnd*, opOr*, opIsNil*, opEq*: PSym + opAdd*, opSub*, opMul*, opDiv*, opLen*: PSym + ModuleGraph* = ref object ifaces*: seq[Iface] ## indexed by int32 fileIdx packed*: PackedModuleGraph + + typeInstCache*: Table[ItemId, seq[PType]] # A symbol's ItemId. + procInstCache*: Table[ItemId, seq[PInstantiation]] # A symbol's ItemId. + attachedOps*: array[TTypeAttachedOp, Table[ItemId, PSym]] # Type ID, destructors, etc. + methodsPerType*: Table[ItemId, seq[(int, PSym)]] # Type ID, attached methods + enumToStringProcs*: Table[ItemId, PSym] + startupPackedConfig*: PackedConfig packageSyms*: TStrTable deps*: IntSet # the dependency graph or potentially its transitive closure. @@ -71,6 +82,7 @@ type strongSemCheck*: proc (graph: ModuleGraph; owner: PSym; body: PNode) {.nimcall.} compatibleProps*: proc (graph: ModuleGraph; formal, actual: PType): bool {.nimcall.} idgen*: IdGenerator + operators*: Operators TPassContext* = object of RootObj # the pass's context idgen*: IdGenerator @@ -85,6 +97,67 @@ type close: TPassClose, isFrontend: bool] +iterator typeInstCacheItems*(g: ModuleGraph; s: PSym): PType = + if g.typeInstCache.contains(s.itemId): + let x = addr(g.typeInstCache[s.itemId]) + for t in x[]: + yield t + +proc addToGenericCache*(g: ModuleGraph; module: int; s: PSym; inst: PType) = + g.typeInstCache.mgetOrPut(s.itemId, @[]).add inst + # XXX Also add to the packed module! + +iterator procInstCacheItems*(g: ModuleGraph; s: PSym): PInstantiation = + if g.procInstCache.contains(s.itemId): + let x = addr(g.procInstCache[s.itemId]) + for t in x[]: + yield t + +proc addToGenericProcCache*(g: ModuleGraph; module: int; s: PSym; inst: PInstantiation) = + g.procInstCache.mgetOrPut(s.itemId, @[]).add inst + # XXX Also add to the packed module! + +proc getAttachedOp*(g: ModuleGraph; t: PType; op: TTypeAttachedOp): PSym = + ## returns the requested attached operation for type `t`. Can return nil + ## if no such operation exists. + result = g.attachedOps[op].getOrDefault(t.itemId) + +proc setAttachedOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) = + ## we also need to record this to the packed module. + g.attachedOps[op][t.itemId] = value + # XXX Also add to the packed module! + +proc setAttachedOpPartial*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) = + ## we also need to record this to the packed module. + g.attachedOps[op][t.itemId] = value + # XXX Also add to the packed module! + +proc completePartialOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) = + discard "To implement" + +proc getToStringProc*(g: ModuleGraph; t: PType): PSym = + result = g.enumToStringProcs.getOrDefault(t.itemId) + assert result != nil + +proc setToStringProc*(g: ModuleGraph; t: PType; value: PSym) = + g.enumToStringProcs[t.itemId] = value + +iterator methodsForGeneric*(g: ModuleGraph; t: PType): (int, PSym) = + for a, b in items g.methodsPerType.getOrDefault(t.itemId): + yield (a, b) + +proc addMethodToGeneric*(g: ModuleGraph; module: int; t: PType; col: int; m: PSym) = + g.methodsPerType.mgetOrPut(t.itemId, @[]).add (col, m) + +proc hasDisabledAsgn*(g: ModuleGraph; t: PType): bool = + let op = getAttachedOp(g, t, attachedAsgn) + result = op != nil and sfError in op.flags + +proc copyTypeProps*(g: ModuleGraph; module: int; dest, src: PType) = + for k in low(TTypeAttachedOp)..high(TTypeAttachedOp): + let op = getAttachedOp(g, src, k) + if op != nil: + setAttachedOp(g, module, dest, k, op) const cb64 = [ @@ -241,6 +314,22 @@ proc registerModule*(g: ModuleGraph; m: PSym) = g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[]) initStrTable(g.ifaces[m.position].interf) +proc initOperators(g: ModuleGraph): Operators = + # These are safe for IC. + result.opLe = createMagic(g, "<=", mLeI) + result.opLt = createMagic(g, "<", mLtI) + result.opAnd = createMagic(g, "and", mAnd) + result.opOr = createMagic(g, "or", mOr) + result.opIsNil = createMagic(g, "isnil", mIsNil) + result.opEq = createMagic(g, "==", mEqI) + result.opAdd = createMagic(g, "+", mAddI) + result.opSub = createMagic(g, "-", mSubI) + result.opMul = createMagic(g, "*", mMulI) + result.opDiv = createMagic(g, "div", mDivI) + result.opLen = createMagic(g, "len", mLengthSeq) + result.opNot = createMagic(g, "not", mNot) + result.opContains = createMagic(g, "contains", mInSet) + proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = result = ModuleGraph() # A module ID of -1 means that the symbol is not attached to a module at all, @@ -265,6 +354,7 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = result.cacheTables = initTable[string, BTree[string, PNode]]() result.canonTypes = initTable[SigHash, PType]() result.symBodyHashes = initTable[int, SigHash]() + result.operators = initOperators(result) proc resetAllModules*(g: ModuleGraph) = initStrTable(g.packageSyms) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 442695ea1c..e796b18ee1 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -460,7 +460,7 @@ proc numLines*(conf: ConfigRef, fileIdx: FileIndex): int = if result == 0: try: for line in lines(toFullPathConsiderDirty(conf, fileIdx).string): - addSourceLine conf, fileIdx, line.string + addSourceLine conf, fileIdx, line except IOError: discard result = conf.m.fileInfos[fileIdx.int32].lines.len diff --git a/compiler/options.nim b/compiler/options.nim index 015e4162ce..425cf9c7f9 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -721,9 +721,9 @@ proc completeGeneratedFilePath*(conf: ConfigRef; f: AbsoluteFile, result = subdir / RelativeFile f.string.splitPath.tail #echo "completeGeneratedFilePath(", f, ") = ", result -proc toRodFile*(conf: ConfigRef; f: AbsoluteFile): AbsoluteFile = +proc toRodFile*(conf: ConfigRef; f: AbsoluteFile; ext = RodExt): AbsoluteFile = result = changeFileExt(completeGeneratedFilePath(conf, - withPackageName(conf, f)), RodExt) + withPackageName(conf, f)), ext) proc rawFindFile(conf: ConfigRef; f: RelativeFile; suppressStdlib: bool): AbsoluteFile = for it in conf.searchPaths: diff --git a/compiler/sem.nim b/compiler/sem.nim index 15b7eee4a5..54a97901e0 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -17,10 +17,7 @@ import intsets, transf, vmdef, vm, aliases, cgmeth, lambdalifting, evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity, lowerings, plugins/active, lineinfos, strtabs, int128, - isolation_check, typeallowed - -from modulegraphs import ModuleGraph, PPassContext, onUse, onDef, onDefResolveForward, - systemModuleSym, semtab, getBody, someSym, allSyms + isolation_check, typeallowed, modulegraphs, enumtostr when defined(nimfix): import nimfix/prettybase @@ -84,7 +81,7 @@ proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode = if x.kind in {nkPar, nkTupleConstr, nkCurly} and formal.kind != tyUntyped: changeType(c, x, formal, check=true) result = arg - result = skipHiddenSubConv(result, c.idgen) + result = skipHiddenSubConv(result, c.graph, c.idgen) proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = @@ -138,7 +135,10 @@ proc commonType*(c: PContext; x, y: PType): PType = let aEmpty = isEmptyContainer(a[i]) let bEmpty = isEmptyContainer(b[i]) if aEmpty != bEmpty: - if nt.isNil: nt = copyType(a, nextTypeId(c.idgen), a.owner) + if nt.isNil: + nt = copyType(a, nextTypeId(c.idgen), a.owner) + copyTypeProps(c.graph, c.idgen.module, nt, a) + nt[i] = if aEmpty: b[i] else: a[i] if not nt.isNil: result = nt #elif b[idx].kind == tyEmpty: return x diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 1e8da02988..a33ad90130 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -685,9 +685,9 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym = var x: PType if param.typ.kind == tyVar: x = newTypeS(param.typ.kind, c) - x.addSonSkipIntLit(t.baseOfDistinct(c.idgen), c.idgen) + x.addSonSkipIntLit(t.baseOfDistinct(c.graph, c.idgen), c.idgen) else: - x = t.baseOfDistinct(c.idgen) + x = t.baseOfDistinct(c.graph, c.idgen) call.add(newNodeIT(nkEmpty, fn.info, x)) if hasDistinct: let filter = if fn.kind in {skProc, skFunc}: {skProc, skFunc} else: {fn.kind} diff --git a/compiler/semdata.nim b/compiler/semdata.nim index d974f60487..4fd3c85fda 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -558,6 +558,7 @@ proc saveRodFile*(c: PContext) = addPragmaComputation(c, n) if sfSystemModule in c.module.flags: c.graph.systemModuleComplete = true + c.idgen.sealed = true # no further additions are allowed if c.config.symbolFiles != stressTest: # For stress testing we seek to reload the symbols from memory. This # way much of the logic is tested but the test is reproducible as it does diff --git a/compiler/seminst.nim b/compiler/seminst.nim index d273cac142..3fd1d04f81 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -95,9 +95,9 @@ proc sameInstantiation(a, b: TInstantiation): bool = ExactGcSafety}): return result = true -proc genericCacheGet(genericSym: PSym, entry: TInstantiation; +proc genericCacheGet(g: ModuleGraph; genericSym: PSym, entry: TInstantiation; id: CompilesId): PSym = - for inst in genericSym.procInstCache: + for inst in procInstCacheItems(g, genericSym): if (inst.compilesId == 0 or inst.compilesId == id) and sameInstantiation(entry, inst[]): return inst.sym @@ -369,7 +369,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, if tfTriggersCompileTime in result.typ.flags: incl(result.flags, sfCompileTime) n[genericParamsPos] = c.graph.emptyNode - var oldPrc = genericCacheGet(fn, entry[], c.compilesContextId) + var oldPrc = genericCacheGet(c.graph, fn, entry[], c.compilesContextId) if oldPrc == nil: # we MUST not add potentially wrong instantiations to the caching mechanism. # This means recursive instantiations behave differently when in @@ -378,7 +378,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, #if c.compilesContextId == 0: rawHandleSelf(c, result) entry.compilesId = c.compilesContextId - fn.procInstCache.add(entry) + addToGenericProcCache(c.graph, c.module.position, fn, entry) c.generics.add(makeInstPair(fn, entry)) if n[pragmasPos].kind != nkEmpty: pragma(c, result, n[pragmasPos], allRoutinePragmas) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index d5ce043896..d0ec1a2e95 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -382,6 +382,8 @@ proc semUnown(c: PContext; n: PNode): PNode = let b = unownedType(c, t[^1]) if b != t[^1]: result = copyType(t, nextTypeId c.idgen, t.owner) + copyTypeProps(c.graph, c.idgen.module, result, t) + result[^1] = b result.flags.excl tfHasOwned else: @@ -541,7 +543,8 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, # check if we converted this finalizer into a destructor already: let t = whereToBindTypeHook(c, fin.typ[1].skipTypes(abstractInst+{tyRef})) - if t != nil and t.attachedOps[attachedDestructor] != nil and t.attachedOps[attachedDestructor].owner == fin: + if t != nil and getAttachedOp(c.graph, t, attachedDestructor) != nil and + getAttachedOp(c.graph, t, attachedDestructor).owner == fin: discard "already turned this one into a finalizer" else: bindTypeHook(c, turnFinalizerIntoDestructor(c, fin, n.info), n, attachedDestructor) @@ -549,8 +552,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, of mDestroy: result = n let t = n[1].typ.skipTypes(abstractVar) - if t.destructor != nil: - result[0] = newSymNode(t.destructor) + let op = getAttachedOp(c.graph, t, attachedDestructor) + if op != nil: + result[0] = newSymNode(op) of mUnown: result = semUnown(c, n) of mExists, mForall: diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim index 3948ac748a..d0fc329a0c 100644 --- a/compiler/semparallel.nim +++ b/compiler/semparallel.nim @@ -81,7 +81,7 @@ proc initAnalysisCtx(g: ModuleGraph): AnalysisCtx = result.slices = @[] result.args = @[] result.guards.s = @[] - result.guards.o = initOperators(g) + result.guards.g = g result.graph = g proc lookupSlot(c: AnalysisCtx; s: PSym): int = @@ -138,7 +138,7 @@ proc checkLe(c: AnalysisCtx; a, b: PNode) = proc checkBounds(c: AnalysisCtx; arr, idx: PNode) = checkLe(c, lowBound(c.graph.config, arr), idx) - checkLe(c, idx, highBound(c.graph.config, arr, c.guards.o)) + checkLe(c, idx, highBound(c.graph.config, arr, c.graph.operators)) proc addLowerBoundAsFacts(c: var AnalysisCtx) = for v in c.locals: @@ -147,8 +147,8 @@ proc addLowerBoundAsFacts(c: var AnalysisCtx) = proc addSlice(c: var AnalysisCtx; n: PNode; x, le, ri: PNode) = checkLocal(c, n) - let le = le.canon(c.guards.o) - let ri = ri.canon(c.guards.o) + let le = le.canon(c.graph.operators) + let ri = ri.canon(c.graph.operators) # perform static bounds checking here; and not later! let oldState = c.guards.s.len addLowerBoundAsFacts(c) @@ -192,7 +192,7 @@ proc subStride(c: AnalysisCtx; n: PNode): PNode = if isLocal(n): let s = c.lookupSlot(n.sym) if s >= 0 and c.locals[s].stride != nil: - result = buildAdd(n, c.locals[s].stride.intVal, c.guards.o) + result = buildAdd(n, c.locals[s].stride.intVal, c.graph.operators) else: result = n elif n.safeLen > 0: @@ -307,16 +307,16 @@ proc analyseCase(c: var AnalysisCtx; n: PNode) = proc analyseIf(c: var AnalysisCtx; n: PNode) = analyse(c, n[0][0]) let oldFacts = c.guards.s.len - addFact(c.guards, canon(n[0][0], c.guards.o)) + addFact(c.guards, canon(n[0][0], c.graph.operators)) analyse(c, n[0][1]) for i in 1.. 1: - addFact(c.guards, canon(branch[0], c.guards.o)) + addFact(c.guards, canon(branch[0], c.graph.operators)) for i in 0..= 0: @@ -539,13 +539,13 @@ proc borrowingCall(c: var Partitions; destType: PType; n: PNode; i: int) = when false: let isView = directViewType(destType) == immutableView if n[0].kind == nkSym and n[0].sym.name.s == "[]=": - localError(c.config, n[i].info, "attempt to mutate an immutable view") + localError(c.g.config, n[i].info, "attempt to mutate an immutable view") for j in i+1..