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
This commit is contained in:
Andreas Rumpf 2021-01-23 08:06:15 +01:00 • committed by GitHub
commit 8241e55023
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 729 additions and 323 deletions

View file

@ -720,6 +720,16 @@ type
module*: int32 module*: int32
item*: 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 TIdObj* = object of RootObj
itemId*: ItemId itemId*: ItemId
PIdObj* = ref TIdObj PIdObj* = ref TIdObj
@ -830,10 +840,8 @@ type
TSym* {.acyclic.} = object of TIdObj # Keep in sync with PackedSym TSym* {.acyclic.} = object of TIdObj # Keep in sync with PackedSym
# proc and type instantiations are cached in the generic symbol # proc and type instantiations are cached in the generic symbol
case kind*: TSymKind case kind*: TSymKind
of skType, skGenericParam:
typeInstCache*: seq[PType]
of routineKinds: of routineKinds:
procInstCache*: seq[PInstantiation] #procInstCache*: seq[PInstantiation]
gcUnsafetyReason*: PSym # for better error messages wrt gcsafe gcUnsafetyReason*: PSym # for better error messages wrt gcsafe
transformedBody*: PNode # cached body after transf pass transformedBody*: PNode # cached body after transf pass
of skLet, skVar, skField, skForVar: of skLet, skVar, skField, skForVar:
@ -913,8 +921,6 @@ type
owner*: PSym # the 'owner' of the type owner*: PSym # the 'owner' of the type
sym*: PSym # types have the sym associated with them sym*: PSym # types have the sym associated with them
# it is used for converting types to strings # 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 size*: BiggestInt # the size of the type in bytes
# -1 means that the size is unkwown # -1 means that the size is unkwown
align*: int16 # the type's alignment requirements align*: int16 # the type's alignment requirements
@ -1069,11 +1075,7 @@ type
module*: int32 module*: int32
symId*: int32 symId*: int32
typeId*: int32 typeId*: int32
sealed*: bool
proc hash*(x: ItemId): Hash =
var h: Hash = hash(x.module)
h = h !& hash(x.item)
result = !$h
const const
PackageModuleId* = -3'i32 PackageModuleId* = -3'i32
@ -1083,10 +1085,12 @@ proc idGeneratorFromModule*(m: PSym): IdGenerator =
result = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0) result = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0)
proc nextSymId*(x: IdGenerator): ItemId {.inline.} = proc nextSymId*(x: IdGenerator): ItemId {.inline.} =
assert(not x.sealed)
inc x.symId inc x.symId
result = ItemId(module: x.module, item: x.symId) result = ItemId(module: x.module, item: x.symId)
proc nextTypeId*(x: IdGenerator): ItemId {.inline.} = proc nextTypeId*(x: IdGenerator): ItemId {.inline.} =
assert(not x.sealed)
inc x.typeId inc x.typeId
result = ItemId(module: x.module, item: 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 = template previouslyInferred*(t: PType): PType =
if t.sons.len > 1: t.lastSon else: nil 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, proc newSym*(symKind: TSymKind, name: PIdent, id: ItemId, owner: PSym,
info: TLineInfo; options: TOptions = {}): PSym = info: TLineInfo; options: TOptions = {}): PSym =
# generates a symbol and initializes the hash field too # generates a symbol and initializes the hash field too
result = PSym(name: name, kind: symKind, flags: {}, info: info, itemId: id, result = PSym(name: name, kind: symKind, flags: {}, info: info, itemId: id,
options: options, owner: owner, offset: defaultOffset) 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 "<no category>"
proc astdef*(s: PSym): PNode = proc astdef*(s: PSym): PNode =
# get only the definition (initializer) portion of the ast # get only the definition (initializer) portion of the ast
@ -1422,7 +1447,6 @@ proc assignType*(dest, src: PType) =
dest.n = src.n dest.n = src.n
dest.size = src.size dest.size = src.size
dest.align = src.align dest.align = src.align
dest.attachedOps = src.attachedOps
dest.lockLevel = src.lockLevel dest.lockLevel = src.lockLevel
# this fixes 'type TLock = TSysLock': # this fixes 'type TLock = TSysLock':
if src.sym != nil: if src.sym != nil:
@ -1622,11 +1646,9 @@ template transitionSymKindCommon*(k: TSymKind) =
proc transitionGenericParamToType*(s: PSym) = proc transitionGenericParamToType*(s: PSym) =
transitionSymKindCommon(skType) transitionSymKindCommon(skType)
s.typeInstCache = obj.typeInstCache
proc transitionRoutineSymKind*(s: PSym, kind: range[skProc..skTemplate]) = proc transitionRoutineSymKind*(s: PSym, kind: range[skProc..skTemplate]) =
transitionSymKindCommon(kind) transitionSymKindCommon(kind)
s.procInstCache = obj.procInstCache
s.gcUnsafetyReason = obj.gcUnsafetyReason s.gcUnsafetyReason = obj.gcUnsafetyReason
s.transformedBody = obj.transformedBody s.transformedBody = obj.transformedBody
@ -1893,10 +1915,8 @@ when false:
for i in 0..<n.safeLen: for i in 0..<n.safeLen:
if n[i].containsNil: return true if n[i].containsNil: return true
template hasDestructor*(t: PType): bool = {tfHasAsgn, tfHasOwned} * t.flags != {}
proc hasDisabledAsgn*(t: PType): bool = template hasDestructor*(t: PType): bool = {tfHasAsgn, tfHasOwned} * t.flags != {}
t.attachedOps[attachedAsgn] != nil and sfError in t.attachedOps[attachedAsgn].flags
template incompleteType*(t: PType): bool = template incompleteType*(t: PType): bool =
t.sym != nil and {sfForward, sfNoForward} * t.sym.flags == {sfForward} t.sym != nil and {sfForward, sfNoForward} * t.sym.flags == {sfForward}
@ -1936,10 +1956,6 @@ proc addParam*(procType: PType; param: PSym) =
procType.n.add newSymNode(param) procType.n.add newSymNode(param)
rawAddSon(procType, param.typ) rawAddSon(procType, param.typ)
template destructor*(t: PType): PSym = t.attachedOps[attachedDestructor]
template assignment*(t: PType): PSym = t.attachedOps[attachedAsgn]
template asink*(t: PType): PSym = t.attachedOps[attachedSink]
const magicsThatCanRaise = { const magicsThatCanRaise = {
mNone, mSlurp, mStaticExec, mParseExprToAst, mParseStmtToAst, mEcho} mNone, mSlurp, mStaticExec, mParseExprToAst, mParseStmtToAst, mEcho}

View file

@ -1292,16 +1292,17 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) =
genAssignment(p, a, b, {}) genAssignment(p, a, b, {})
else: else:
let ti = genTypeInfoV1(p.module, typ, a.lode.info) let ti = genTypeInfoV1(p.module, typ, a.lode.info)
if bt.destructor != nil and not isTrivialProc(p.module.g.graph, bt.destructor): let op = getAttachedOp(p.module.g.graph, bt, attachedDestructor)
if op != nil and not isTrivialProc(p.module.g.graph, op):
# the prototype of a destructor is ``=destroy(x: var T)`` and that of a # 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 # finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling
# convention at least: # convention at least:
if bt.destructor.typ == nil or bt.destructor.typ.callConv != ccNimCall: if op.typ == nil or op.typ.callConv != ccNimCall:
localError(p.module.config, a.lode.info, localError(p.module.config, a.lode.info,
"the destructor that is turned into a finalizer needs " & "the destructor that is turned into a finalizer needs " &
"to have the 'nimcall' calling convention") "to have the 'nimcall' calling convention")
var f: TLoc var f: TLoc
initLocExpr(p, newSymNode(bt.destructor), f) initLocExpr(p, newSymNode(op), f)
p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)]) p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
if a.storage == OnHeap and usesWriteBarrier(p.config): if a.storage == OnHeap and usesWriteBarrier(p.config):
@ -2204,7 +2205,8 @@ proc genDestroy(p: BProc; n: PNode) =
else: discard "nothing to do" else: discard "nothing to do"
else: else:
let t = n[1].typ.skipTypes(abstractVar) 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") internalError(p.config, n.info, "destructor turned out to be not trivial")
discard "ignore calls to the default destructor" 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) = proc genEnumToStr(p: BProc, e: PNode, d: var TLoc) =
const ToStringProcSlot = -4 const ToStringProcSlot = -4
let t = e[1].typ.skipTypes(abstractInst+{tyRange}) let t = e[1].typ.skipTypes(abstractInst+{tyRange})
var toStrProc: PSym = nil let toStrProc = getToStringProc(p.module.g.graph, t)
for idx, p in items(t.methods): # XXX need to modify this logic for IC.
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))
var n = copyTree(e) var n = copyTree(e)
n[0] = newSymNode(toStrProc) n[0] = newSymNode(toStrProc)
expr(p, n, d) expr(p, n, d)
@ -2645,6 +2641,57 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
if t.kind notin {tySequence, tyString}: if t.kind notin {tySequence, tyString}:
d.storage = OnStatic 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) = proc expr(p: BProc, n: PNode, d: var TLoc) =
when defined(nimCompilerStackraceHints): when defined(nimCompilerStackraceHints):
setFrameMsg p.config$n.info & " " & $n.kind setFrameMsg p.config$n.info & " " & $n.kind
@ -2655,7 +2702,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
var sym = n.sym var sym = n.sym
case sym.kind case sym.kind
of skMethod: 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: # we cannot produce code for the dispatcher yet:
fillProcLoc(p.module, n) fillProcLoc(p.module, n)
genProcPrototype(p.module, sym) genProcPrototype(p.module, sym)
@ -2668,13 +2715,19 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
if sfCompileTime in sym.flags: if sfCompileTime in sym.flags:
localError(p.config, n.info, "request to generate code for .compileTime proc: " & localError(p.config, n.info, "request to generate code for .compileTime proc: " &
sym.name.s) 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: if sym.loc.r == nil or sym.loc.lode == nil:
internalError(p.config, n.info, "expr: proc not init " & sym.name.s) internalError(p.config, n.info, "expr: proc not init " & sym.name.s)
putLocIntoDest(p, d, sym.loc) putLocIntoDest(p, d, sym.loc)
of skConst: of skConst:
if isSimpleConst(sym.typ): if isSimpleConst(sym.typ):
putIntoDest(p, d, n, genLiteral(p, sym.ast, sym.typ), OnStatic) 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: else:
genComplexConst(p, sym, d) genComplexConst(p, sym, d)
of skEnumField: of skEnumField:
@ -2795,7 +2848,10 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
of nkEmpty: discard of nkEmpty: discard
of nkWhileStmt: genWhileStmt(p, n) of nkWhileStmt: genWhileStmt(p, n)
of nkVarSection, nkLetSection: genVarStmt(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 nkForStmt: internalError(p.config, n.info, "for statement not eliminated")
of nkCaseStmt: genCase(p, n, d) of nkCaseStmt: genCase(p, n, d)
of nkReturnStmt: genReturnStmt(p, n) of nkReturnStmt: genReturnStmt(p, n)
@ -2840,13 +2896,16 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef: of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef:
if n[genericParamsPos].kind == nkEmpty: if n[genericParamsPos].kind == nkEmpty:
var prc = n[namePos].sym var prc = n[namePos].sym
# due to a bug/limitation in the lambda lifting, unused inner procs if useAliveDataFromDce in p.module.flags:
# are not transformed correctly. We work around this issue (#411) here if p.module.alive.contains(prc.itemId.item):
# by ensuring it's no inner proc (owner is a module): genProc(p.module, prc)
if prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags: elif prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags:
if ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or if ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or
(sfExportc in prc.flags and lfExportLib in prc.loc.flags) or (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or
(prc.kind == skMethod): (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. # Generate proc even if empty body, bugfix #11651.
genProc(p.module, prc) genProc(p.module, prc)
of nkParForStmt: genParForStmt(p, n) of nkParForStmt: genParForStmt(p, n)

View file

@ -1530,20 +1530,21 @@ proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType,
[rdLoc(a), rdLoc(tmp), discriminatorTableName(p.module, t, field), [rdLoc(a), rdLoc(tmp), discriminatorTableName(p.module, t, field),
intLiteral(toInt64(lengthOrd(p.config, field.typ))+1)]) intLiteral(toInt64(lengthOrd(p.config, field.typ))+1)])
proc genCaseObjDiscMapping(p: BProc, e: PNode, t: PType, field: PSym; d: var TLoc) = when false:
const ObjDiscMappingProcSlot = -5 proc genCaseObjDiscMapping(p: BProc, e: PNode, t: PType, field: PSym; d: var TLoc) =
var theProc: PSym = nil const ObjDiscMappingProcSlot = -5
for idx, p in items(t.methods): var theProc: PSym = nil
if idx == ObjDiscMappingProcSlot: for idx, p in items(t.methods):
theProc = p if idx == ObjDiscMappingProcSlot:
break theProc = p
if theProc == nil: break
theProc = genCaseObjDiscMapping(t, field, e.info, p.module.g.graph, p.module.idgen) if theProc == nil:
t.methods.add((ObjDiscMappingProcSlot, theProc)) theProc = genCaseObjDiscMapping(t, field, e.info, p.module.g.graph, p.module.idgen)
var call = newNodeIT(nkCall, e.info, getSysType(p.module.g.graph, e.info, tyUInt8)) t.methods.add((ObjDiscMappingProcSlot, theProc))
call.add newSymNode(theProc) var call = newNodeIT(nkCall, e.info, getSysType(p.module.g.graph, e.info, tyUInt8))
call.add e call.add newSymNode(theProc)
expr(p, call, d) call.add e
expr(p, call, d)
proc asgnFieldDiscriminant(p: BProc, e: PNode) = proc asgnFieldDiscriminant(p: BProc, e: PNode) =
var a, tmp: TLoc var a, tmp: TLoc

View file

@ -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 isTrivialProc(g: ModuleGraph; s: PSym): bool {.inline.} = getBody(g, s).len == 0
proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp): Rope = 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): if theProc != nil and not isTrivialProc(m.g.graph, theProc):
# the prototype of a destructor is ``=destroy(x: var T)`` and that of a # 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 # 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) genTupleInfo(m, x, origType, result, info)
else: internalError(m.config, "genTypeInfoV1(" & $t.kind & ')') else: internalError(m.config, "genTypeInfoV1(" & $t.kind & ')')
if t.attachedOps[attachedDeepCopy] != nil: var op = getAttachedOp(m.g.graph, t, attachedDeepCopy)
genDeepCopyProc(m, t.attachedOps[attachedDeepCopy], result) if op == nil:
elif origType.attachedOps[attachedDeepCopy] != nil: op = getAttachedOp(m.g.graph, origType, attachedDeepCopy)
genDeepCopyProc(m, origType.attachedOps[attachedDeepCopy], result) if op != nil:
genDeepCopyProc(m, op, result)
if optTinyRtti in m.config.globalOptions and t.kind == tyObject and sfImportc notin t.sym.flags: if optTinyRtti in m.config.globalOptions and t.kind == tyObject and sfImportc notin t.sym.flags:
let v2info = genTypeInfoV2(m, origType, info) let v2info = genTypeInfoV2(m, origType, info)

View file

@ -14,7 +14,7 @@ import
nversion, nimsets, msgs, bitsets, idents, types, nversion, nimsets, msgs, bitsets, idents, types,
ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth, ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth,
rodutils, renderer, cgendata, ccgmerge, aliases, rodutils, renderer, cgendata, ccgmerge, aliases,
lowerings, tables, sets, ndi, lineinfos, pathutils, transf, enumtostr, lowerings, tables, sets, ndi, lineinfos, pathutils, transf,
injectdestructors injectdestructors
when not defined(leanCompiler): when not defined(leanCompiler):
@ -1189,48 +1189,16 @@ proc genProcNoForward(m: BModule, prc: PSym) =
if sfInfixCall notin prc.flags: genProcPrototype(m, prc) if sfInfixCall notin prc.flags: genProcPrototype(m, prc)
proc requestConstImpl(p: BProc, sym: PSym) = proc requestConstImpl(p: BProc, sym: PSym) =
var m = p.module if genConstSetup(p, sym):
useHeader(m, sym) let m = p.module
if sym.loc.k == locNone: # declare implementation:
fillLoc(sym.loc, locData, sym.ast, mangleName(p.module, sym), OnStatic) var q = findPendingModule(m, sym)
if m.hcrOn: incl(sym.loc.flags, lfIndirect) if q != nil and not containsOrIncl(q.declaredThings, sym.id):
assert q.initProc.module == q
if lfNoDecl in sym.loc.flags: return genConstDefinition(q, p, sym)
# declare implementation: # declare header:
var q = findPendingModule(m, sym) if q != m and not containsOrIncl(m.declaredThings, sym.id):
if q != nil and not containsOrIncl(q.declaredThings, sym.id): genConstHeader(m, q, p, sym)
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)
proc isActivated(prc: PSym): bool = prc.typ != nil 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 = proc rawNewModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
result = rawNewModule(g, module, AbsoluteFile toFullPath(conf, module.position.FileIndex)) 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 # we should create only one cgen module for each module sym
result = rawNewModule(g, module, conf) result = rawNewModule(g, module, conf)
if module.position >= g.modules.len: if module.position >= g.modules.len:
@ -1922,13 +1890,9 @@ proc addHcrInitGuards(p: BProc, n: PNode, inInitGuard: var bool) =
genStmts(p, n) genStmts(p, n)
proc myProcess(b: PPassContext, n: PNode): PNode = proc genTopLevelStmt*(m: BModule; n: PNode) =
result = n ## Also called from `ic/cbackend.nim`.
if b == nil: return if passes.skipCodegen(m.config, n): return
var m = BModule(b)
if passes.skipCodegen(m.config, n) or
not moduleHasChanged(m.g.graph, m.module):
return
m.initProc.options = initProcOptions(m) m.initProc.options = initProcOptions(m)
#softRnl = if optLineDir in m.config.options: noRnl else: rnl #softRnl = if optLineDir in m.config.options: noRnl else: rnl
# XXX replicate this logic! # XXX replicate this logic!
@ -1941,6 +1905,12 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
else: else:
genProcBody(m.initProc, transformedN) 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 = proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
if optForceFullMake notin m.config.globalOptions: if optForceFullMake notin m.config.globalOptions:
if not moduleHasChanged(m.g.graph, m.module): 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: # 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, var cf = Cfile(nimname: m.module.name.s, cname: cfile,
obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {}) 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) addFileToCompile(m.config, cf)
onExit() onExit()
@ -2037,10 +2007,8 @@ proc updateCachedModule(m: BModule) =
cf.flags = {CfileFlag.Cached} cf.flags = {CfileFlag.Cached}
addFileToCompile(m.config, cf) addFileToCompile(m.config, cf)
proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
result = n ## Also called from IC.
if b == nil: return
var m = BModule(b)
if sfMainModule in m.module.flags: if sfMainModule in m.module.flags:
# phase ordering problem here: We need to announce this # phase ordering problem here: We need to announce this
# dependency to 'nimTestErrorFlag' before system.c has been written to disk. # 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 let mm = m
m.g.modulesClosed.add mm 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) = proc genForwardedProcs(g: BModuleList) =
# Forward declared proc:s lack bodies when first encountered, so they're given # Forward declared proc:s lack bodies when first encountered, so they're given
# a second pass here # a second pass here

View file

@ -112,7 +112,8 @@ type
isHeaderFile, # C source file is the header file isHeaderFile, # C source file is the header file
includesStringh, # C source file already includes ``<string.h>`` includesStringh, # C source file already includes ``<string.h>``
objHasKidsValid # whether we can rely on tfObjHasKids 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 BModuleList* = ref object of RootObj
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope
@ -154,6 +155,7 @@ type
forwTypeCache*: TypeCache # cache for forward declarations of types forwTypeCache*: TypeCache # cache for forward declarations of types
declaredThings*: IntSet # things we have declared in this .c file declaredThings*: IntSet # things we have declared in this .c file
declaredProtos*: IntSet # prototypes 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 headerFiles*: seq[string] # needed headers to include
typeInfoMarker*: TypeCache # needed for generating type information typeInfoMarker*: TypeCache # needed for generating type information
typeInfoMarkerV2*: TypeCache typeInfoMarkerV2*: TypeCache

View file

@ -107,11 +107,14 @@ proc attachDispatcher(s: PSym, dispatcher: PNode) =
s.ast[resultPos] = newNodeI(nkEmpty, s.info) s.ast[resultPos] = newNodeI(nkEmpty, s.info)
s.ast[dispatcherPos] = dispatcher 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)) var disp = copySym(s, nextSymId(idgen))
incl(disp.flags, sfDispatcher) incl(disp.flags, sfDispatcher)
excl(disp.flags, sfExported) excl(disp.flags, sfExported)
let old = disp.typ
disp.typ = copyType(disp.typ, nextTypeId(idgen), disp.typ.owner) 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): # we can't inline the dispatcher itself (for now):
if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall
disp.ast = copyTree(s.ast) disp.ast = copyTree(s.ast)
@ -177,7 +180,7 @@ proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym, fromCache: bool) =
of Invalid: of Invalid:
if witness.isNil: witness = g.methods[i].methods[0] if witness.isNil: witness = g.methods[i].methods[0]
# create a new dispatcher: # 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 #echo "adding ", s.info
#if fromCache: #if fromCache:
# internalError(s.info, "no method dispatcher found") # internalError(s.info, "no method dispatcher found")

View file

@ -82,26 +82,6 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true) 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 = proc swapArgs(fact: PNode, newOp: PSym): PNode =
result = newNodeI(nkCall, fact.info, 3) result = newNodeI(nkCall, fact.info, 3)
result[0] = newSymNode(newOp) result[0] = newSymNode(newOp)
@ -404,16 +384,16 @@ proc usefulFact(n: PNode; o: Operators): PNode =
type type
TModel* = object TModel* = object
s*: seq[PNode] # the "knowledge base" s*: seq[PNode] # the "knowledge base"
o*: Operators g*: ModuleGraph
beSmart*: bool beSmart*: bool
proc addFact*(m: var TModel, nn: PNode) = proc addFact*(m: var TModel, nn: PNode) =
let n = usefulFact(nn, m.o) let n = usefulFact(nn, m.g.operators)
if n != nil: if n != nil:
if not m.beSmart: if not m.beSmart:
m.s.add n m.s.add n
else: else:
let c = canon(n, m.o) let c = canon(n, m.g.operators)
if c.getMagic == mAnd: if c.getMagic == mAnd:
addFact(m, c[1]) addFact(m, c[1])
addFact(m, c[2]) addFact(m, c[2])
@ -421,7 +401,7 @@ proc addFact*(m: var TModel, nn: PNode) =
m.s.add c m.s.add c
proc addFactNeg*(m: var TModel, n: PNode) = 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) if n != nil: addFact(m, n)
proc sameOpr(a, b: PSym): bool = proc sameOpr(a, b: PSym): bool =
@ -740,7 +720,7 @@ proc doesImply*(facts: TModel, prop: PNode): TImplication =
if result != impUnknown: return if result != impUnknown: return
proc impliesNotNil*(m: TModel, arg: PNode): TImplication = 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 = proc simpleSlice*(a, b: PNode): BiggestInt =
# returns 'c' if a..b matches (i+c)..(i+c), -1 otherwise. (i)..(i) is matched # 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 b.getMagic in someAdd:
if zero() <=? b[2] and a <=? b[1]: return impYes if zero() <=? b[2] and a <=? b[1]: return impYes
# x <= y-c if x+c <= y # 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 return impYes
# x+c <= y if c <= 0 and x <= y # 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 if a.getMagic in someMul and a[2].isValue and a[1].getMagic in someDiv and
a[1][2].isValue: a[1][2].isValue:
# simplify (x div 4) * 2 <= y to x div (c div d) <= y # 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 return impYes
# x*3 + x == x*4. It follows that: # x*3 + x == x*4. It follows that:
# x*3 + y <= x*4 if y <= x and 3 <= 4 # x*3 + y <= x*4 if y <= x and 3 <= 4
if a =~ x*dc + y and b =~ x2*ec: if a =~ x*dc + y and b =~ x2*ec:
if sameTree(x, x2): 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: if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? x:
return impYes return impYes
elif a =~ x*dc and b =~ x2*ec + y: elif a =~ x*dc and b =~ x2*ec + y:
#echo "BUG cam ehrer e ", a, " <=? ", b #echo "BUG cam ehrer e ", a, " <=? ", b
if sameTree(x, x2): 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(): if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? zero():
return impYes return impYes
@ -963,12 +943,12 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
var b = bb var b = bb
if replacements.len > 0: if replacements.len > 0:
m.s = @[] m.s = @[]
m.o = model.o m.g = model.g
# make the other facts consistent: # make the other facts consistent:
for fact in model.s: for fact in model.s:
if fact != nil and fact.getMagic notin someEq: if fact != nil and fact.getMagic notin someEq:
# XXX 'canon' should not be necessary here, but it is # 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) a = applyReplacements(aa, replacements)
b = applyReplacements(bb, replacements) b = applyReplacements(bb, replacements)
else: else:
@ -977,19 +957,19 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
result = pleViaModelRec(m, a, b) result = pleViaModelRec(m, a, b)
proc proveLe*(m: TModel; a, b: PNode): TImplication = 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]) #echo "ROOT ", renderTree(x[1]), " <=? ", renderTree(x[2])
result = ple(m, x[1], x[2]) result = ple(m, x[1], x[2])
if result == impUnknown: if result == impUnknown:
# try an alternative: a <= b iff not (b < a) iff not (b+1 <= a): # 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]) result = ~ple(m, y[1], y[2])
proc addFactLe*(m: var TModel; a, b: PNode) = 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) = 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) addFactLe(m, a, bb)
proc settype(n: PNode): PType = proc settype(n: PNode): PType =
@ -1021,14 +1001,14 @@ proc buildElse(n: PNode; o: Operators): PNode =
proc addDiscriminantFact*(m: var TModel, n: PNode) = proc addDiscriminantFact*(m: var TModel, n: PNode) =
var fact = newNodeI(nkCall, n.info, 3) 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[1] = n[0]
fact[2] = n[1] fact[2] = n[1]
m.s.add fact m.s.add fact
proc addAsgnFact*(m: var TModel, key, value: PNode) = proc addAsgnFact*(m: var TModel, key, value: PNode) =
var fact = newNodeI(nkCall, key.info, 3) var fact = newNodeI(nkCall, key.info, 3)
fact[0] = newSymNode(m.o.opEq) fact[0] = newSymNode(m.g.operators.opEq)
fact[1] = key fact[1] = key
fact[2] = value fact[2] = value
m.s.add fact 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 # However, nil checking requires exactly the same mechanism! But for now
# we simply use sameTree and live with the unsoundness of the analysis. # we simply use sameTree and live with the unsoundness of the analysis.
var check = newNodeI(nkCall, a.info, 3) var check = newNodeI(nkCall, a.info, 3)
check[0] = newSymNode(m.o.opEq) check[0] = newSymNode(m.g.operators.opEq)
check[1] = a check[1] = a
check[2] = b check[2] = b
result = m.doesImply(check) == impYes 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) = proc addCaseBranchFacts*(m: var TModel, n: PNode, i: int) =
let branch = n[i] let branch = n[i]
if branch.kind == nkOfBranch: if branch.kind == nkOfBranch:
m.s.add buildOf(branch, n[0], m.o) m.s.add buildOf(branch, n[0], m.g.operators)
else: 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 = proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode =
if check[1].kind == nkCurly: 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) = proc checkFieldAccess*(m: TModel, n: PNode; conf: ConfigRef) =
for i in 1..<n.len: for i in 1..<n.len:
let check = buildProperFieldCheck(n[0], n[i], m.o) let check = buildProperFieldCheck(n[0], n[i], m.g.operators)
if check != nil and m.doesImply(check) != impYes: if check != nil and m.doesImply(check) != impYes:
message(conf, n.info, warnProveField, renderTree(n[0])); break message(conf, n.info, warnProveField, renderTree(n[0])); break

102
compiler/ic/cbackend.nim Normal file
View file

@ -0,0 +1,102 @@
#
#
# The Nim Compiler
# (c) Copyright 2021 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## New entry point into our C/C++ code generator. Ideally
## somebody would rewrite the old backend (which is 8000 lines of crufty Nim code)
## to work on packed trees directly and produce the C code as an AST which can
## then be rendered to text in a very simple manner. Unfortunately nobody wrote
## this code. So instead we wrap the existing cgen.nim and its friends so that
## we call directly into the existing code generation logic but avoiding the
## naive, outdated `passes` design. Thus you will see some
## `useAliveDataFromDce in flags` checks in the old code -- the old code is
## also doing cross-module dependency tracking and DCE that we don't need
## anymore. DCE is now done as prepass over the entire packed module graph.
import std / [intsets, algorithm]
import ".." / [ast, options, lineinfos, modulegraphs, cgendata, cgen,
pathutils, extccomp, msgs]
import packed_ast, to_packed_ast, bitabs, dce, rodfiles
proc unpackTree(g: ModuleGraph; thisModule: int;
tree: PackedTree; n: NodePos): PNode =
var decoder = initPackedDecoder(g.config, g.cache)
result = loadNodes(decoder, g.packed, thisModule, tree, n)
proc generateCodeForModule(g: ModuleGraph; m: var LoadedModule; alive: var AliveSyms) =
if g.backend == nil:
g.backend = cgendata.newModuleList(g)
var bmod = cgen.newModule(BModuleList(g.backend), m.module, g.config)
bmod.idgen = idgenFromLoadedModule(m)
bmod.flags.incl useAliveDataFromDce
bmod.alive = move alive[m.module.position]
for p in allNodes(m.fromDisk.topLevel):
let n = unpackTree(g, m.module.position, m.fromDisk.topLevel, p)
cgen.genTopLevelStmt(bmod, n)
finalCodegenActions(g, bmod, newNodeI(nkStmtList, m.module.info))
proc addFileToLink(config: ConfigRef; m: PSym) =
let filename = AbsoluteFile toFullPath(config, m.position.FileIndex)
let ext =
if config.backend == backendCpp: ".nim.cpp"
elif config.backend == backendObjc: ".nim.m"
else: ".nim.c"
let cfile = changeFileExt(completeCfilePath(config, withPackageName(config, filename)), ext)
var cf = Cfile(nimname: m.name.s, cname: cfile,
obj: completeCfilePath(config, toObjFile(config, cfile)),
flags: {CfileFlag.Cached})
addFileToCompile(config, cf)
proc aliveSymsChanged(config: ConfigRef; position: int; alive: AliveSyms): bool =
let asymFile = toRodFile(config, AbsoluteFile toFullPath(config, position.FileIndex), ".alivesyms")
var s = newSeqOfCap[int32](alive[position].len)
for a in items(alive[position]): s.add int32(a)
sort(s)
var f2 = rodfiles.open(asymFile.string)
f2.loadHeader()
f2.loadSection aliveSymsSection
var oldData: seq[int32]
f2.loadSeq(oldData)
f2.close
if f2.err == ok and oldData == s:
result = false
else:
result = true
var f = rodfiles.create(asymFile.string)
f.storeHeader()
f.storeSection aliveSymsSection
f.storeSeq(s)
close f
proc generateCode*(g: ModuleGraph) =
## The single entry point, generate C(++) code for the entire
## Nim program aka `ModuleGraph`.
var alive = computeAliveSyms(g.packed, g.config)
for i in 0..high(g.packed):
# case statement here to enforce exhaustive checks.
case g.packed[i].status
of undefined:
discard "nothing to do"
of loading:
assert false
of storing, outdated:
generateCodeForModule(g, g.packed[i], alive)
of loaded:
# Even though this module didn't change, DCE might trigger a change.
# Consider this case: Module A uses symbol S from B and B does not use
# S itself. A is then edited not to use S either. Thus we have to
# recompile B in order to remove S from the final result.
if aliveSymsChanged(g.config, g.packed[i].module.position, alive):
generateCodeForModule(g, g.packed[i], alive)
else:
addFileToLink(g.config, g.packed[i].module)

108
compiler/ic/dce.nim Normal file
View file

@ -0,0 +1,108 @@
#
#
# The Nim Compiler
# (c) Copyright 2021 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Dead code elimination (=DCE) for IC.
import std / intsets
import ".." / [ast, options, lineinfos]
import packed_ast, to_packed_ast, bitabs
type
AliveSyms* = seq[IntSet]
AliveContext* = object ## Purpose is to fill the 'alive' field.
stack: seq[(int, NodePos)] ## A stack for marking symbols as alive.
decoder: PackedDecoder ## We need a PackedDecoder for module ID address translations.
thisModule: int ## The module we're currently analysing for DCE.
alive: AliveSyms ## The final result of our computation.
proc isExportedToC(c: var AliveContext; g: PackedModuleGraph; symId: int32): bool =
## "Exported to C" procs are special (these are marked with '.exportc') because these
## must not be optimized away!
let symPtr = addr g[c.thisModule].fromDisk.sh.syms[symId]
let flags = symPtr.flags
# due to a bug/limitation in the lambda lifting, unused inner procs
# are not transformed correctly; issue (#411). However, the whole purpose here
# is to eliminate unused procs. So there is no special logic required for this case.
if sfCompileTime notin flags:
if ({sfExportc, sfCompilerProc} * flags == {sfExportc}) or
(symPtr.kind == skMethod):
result = true
# XXX: This used to be a condition to:
# (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or
template isNotGeneric(n: NodePos): bool = ithSon(tree, n, genericParamsPos).kind == nkEmpty
proc followLater(c: var AliveContext; g: PackedModuleGraph; module: int; item: int32) =
## Marks a symbol 'item' as used and later in 'followNow' the symbol's body will
## be analysed.
if not c.alive[module].containsOrIncl(item):
let body = g[module].fromDisk.sh.syms[item].ast
if body != emptyNodeId:
c.stack.add((module, NodePos(body)))
proc aliveCode(c: var AliveContext; g: PackedModuleGraph; tree: PackedTree; n: NodePos) =
## Marks the symbols we encounter when we traverse the AST at `tree[n]` as alive, unless
## it is purely in a declarative context (type section etc.).
case n.kind
of nkNone..pred(nkSym), succ(nkSym)..nkNilLit:
discard "ignore non-sym atoms"
of nkSym:
# This symbol is alive and everything its body references.
followLater(c, g, c.thisModule, n.operand)
of nkModuleRef:
let (n1, n2) = sons2(tree, n)
assert n1.kind == nkInt32Lit
assert n2.kind == nkInt32Lit
let m = n1.litId
let item = n2.operand
let otherModule = toFileIndexCached(c.decoder, g, c.thisModule, m).int
followLater(c, g, otherModule, item)
of nkMacroDef, nkTemplateDef, nkTypeSection, nkTypeOfExpr,
nkCommentStmt, nkIteratorDef, nkIncludeStmt,
nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
nkFromStmt, nkStaticStmt:
discard
of nkVarSection, nkLetSection, nkConstSection:
discard
of nkProcDef, nkConverterDef, nkMethodDef, nkLambda, nkDo, nkFuncDef:
if n.firstSon.kind == nkSym and isNotGeneric(n):
if isExportedToC(c, g, n.firstSon.operand):
let item = n.operand
# This symbol is alive and everything its body references.
followLater(c, g, c.thisModule, item)
else:
for son in sonsReadonly(tree, n):
aliveCode(c, g, tree, son)
proc followNow(c: var AliveContext; g: PackedModuleGraph) =
## Mark all entries in the stack. Marking can add more entries
## to the stack but eventually we have looked at every alive symbol.
while c.stack.len > 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)

View file

@ -290,6 +290,9 @@ template typ*(n: NodePos): PackedItemId =
template flags*(n: NodePos): TNodeFlags = template flags*(n: NodePos): TNodeFlags =
tree.nodes[n.int].flags tree.nodes[n.int].flags
template operand*(n: NodePos): int32 =
tree.nodes[n.int].operand
proc span*(tree: PackedTree; pos: int): int {.inline.} = proc span*(tree: PackedTree; pos: int): int {.inline.} =
if isAtom(tree, pos): 1 else: tree.nodes[pos].operand if isAtom(tree, pos): 1 else: tree.nodes[pos].operand
@ -451,3 +454,10 @@ when false:
dest.add nkStrLit, msg, n.info dest.add nkStrLit, msg, n.info
copyTree(dest, tree, n) copyTree(dest, tree, n)
patch dest, patchPos 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

View file

@ -31,6 +31,7 @@ type
bodiesSection bodiesSection
symsSection symsSection
typesSection typesSection
aliveSymsSection # beware, this is stored in a `.alivesyms` file.
RodFileError* = enum RodFileError* = enum
ok, tooBig, cannotOpen, ioFailure, wrongHeader, wrongSection, configMismatch, ok, tooBig, cannotOpen, ioFailure, wrongHeader, wrongSection, configMismatch,
@ -134,7 +135,7 @@ proc loadHeader*(f: var RodFile) =
proc storeSection*(f: var RodFile; s: RodSection) = proc storeSection*(f: var RodFile; s: RodSection) =
if f.err != ok: return if f.err != ok: return
assert f.currentSection == pred s assert f.currentSection < s
f.currentSection = s f.currentSection = s
storePrim(f, s) storePrim(f, s)

View file

@ -107,7 +107,7 @@ proc toLitId(x: FileIndex; c: var PackedEncoder; m: var PackedModule): LitId =
c.lastLit = result c.lastLit = result
assert result != LitId(0) 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]) result = msgs.fileInfoIdx(config, AbsoluteFile m.sh.strings[x])
proc includesIdentical(m: var PackedModule; config: ConfigRef): bool = 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) paddingAtEnd: t.paddingAtEnd, lockLevel: t.lockLevel)
storeNode(p, t, n) storeNode(p, t, n)
for op, s in pairs t.attachedOps: when false:
c.addMissing s for op, s in pairs t.attachedOps:
p.attachedOps[op] = s.safeItemId(c, m) c.addMissing s
p.attachedOps[op] = s.safeItemId(c, m)
p.typeInst = t.typeInst.storeType(c, m) p.typeInst = t.typeInst.storeType(c, m)
for kid in items t.sons: for kid in items t.sons:
p.types.add kid.storeType(c, m) p.types.add kid.storeType(c, m)
for i, s in items t.methods:
c.addMissing s when false:
p.methods.add (i, s.safeItemId(c, m)) for i, s in items t.methods:
c.addMissing s
p.methods.add (i, s.safeItemId(c, m))
c.addMissing t.sym c.addMissing t.sym
p.sym = t.sym.safeItemId(c, m) p.sym = t.sym.safeItemId(c, m)
c.addMissing t.owner c.addMissing t.owner
@ -568,11 +571,11 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac
type type
PackedDecoder* = object PackedDecoder* = object
lastModule*: int lastModule: int
lastLit*: LitId lastLit: LitId
lastFile*: FileIndex # remember the last lookup entry. lastFile: FileIndex # remember the last lookup entry.
config*: ConfigRef config*: ConfigRef
cache: IdentCache cache*: IdentCache
type type
ModuleStatus* = enum ModuleStatus* = enum
@ -596,7 +599,7 @@ type
proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; t: PackedItemId): PType 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 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: if c.lastLit == f and c.lastModule == thisModule:
result = c.lastFile result = c.lastFile
else: else:
@ -611,8 +614,8 @@ proc translateLineInfo(c: var PackedDecoder; g: var PackedModuleGraph; thisModul
result = TLineInfo(line: x.line, col: x.col, result = TLineInfo(line: x.line, col: x.col,
fileIndex: toFileIndexCached(c, g, thisModule, x.file)) fileIndex: toFileIndexCached(c, g, thisModule, x.file))
proc loadNodes(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
tree: PackedTree; n: NodePos): PNode = tree: PackedTree; n: NodePos): PNode =
let k = n.kind let k = n.kind
if k == nkNilRodNode: if k == nkNilRodNode:
return nil return nil
@ -647,6 +650,14 @@ proc loadNodes(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
for n0 in sonsReadonly(tree, n): for n0 in sonsReadonly(tree, n):
result.addAllowNil loadNodes(c, g, thisModule, tree, n0) 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; proc loadProcHeader(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
tree: PackedTree; n: NodePos): PNode = tree: PackedTree; n: NodePos): PNode =
# do not load the body of the proc. This will be done later in # 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) = t: PackedType; si, item: int32; result: PType) =
result.sym = loadSym(c, g, si, t.sym) result.sym = loadSym(c, g, si, t.sym)
result.owner = loadSym(c, g, si, t.owner) result.owner = loadSym(c, g, si, t.owner)
for op, item in pairs t.attachedOps: when false:
result.attachedOps[op] = loadSym(c, g, si, item) for op, item in pairs t.attachedOps:
result.attachedOps[op] = loadSym(c, g, si, item)
result.typeInst = loadType(c, g, si, t.typeInst) result.typeInst = loadType(c, g, si, t.typeInst)
for son in items t.types: for son in items t.types:
result.sons.add loadType(c, g, si, son) result.sons.add loadType(c, g, si, son)
loadAstBody(t, n) loadAstBody(t, n)
for gen, id in items t.methods: when false:
result.methods.add((gen, loadSym(c, g, si, id))) 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 = proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; t: PackedItemId): PType =
if t == nilItemId: if t == nilItemId:
@ -819,11 +832,8 @@ proc loadToReplayNodes(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCa
lastFile: FileIndex(-1), lastFile: FileIndex(-1),
config: conf, config: conf,
cache: cache) cache: cache)
var p = 0 for p in allNodes(m.fromDisk.toReplay):
while p < m.fromDisk.toReplay.len: m.module.ast.add loadNodes(decoder, g, int(fileIdx), m.fromDisk.toReplay, p)
m.module.ast.add loadNodes(decoder, g, int(fileIdx), m.fromDisk.toReplay, NodePos p)
let s = span(m.fromDisk.toReplay, p)
inc p, s
proc needsRecompile(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache; proc needsRecompile(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
fileIdx: FileIndex): bool = fileIdx: FileIndex): bool =
@ -979,6 +989,10 @@ proc interfaceSymbol*(config: ConfigRef, cache: IdentCache;
let values = g[int module].iface.getOrDefault(name) let values = g[int module].iface.getOrDefault(name)
result = loadSym(decoder, g, int(module), values[0]) 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 --------------------------------- # ------------------------- .rod file viewer ---------------------------------
proc rodViewer*(rodfile: AbsoluteFile; config: ConfigRef, cache: IdentCache) = proc rodViewer*(rodfile: AbsoluteFile; config: ConfigRef, cache: IdentCache) =

View file

@ -260,13 +260,13 @@ proc genOp(c: var Con; op: PSym; dest: PNode): PNode =
result = newTree(nkCall, newSymNode(op), addrExp) result = newTree(nkCall, newSymNode(op), addrExp)
proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode = 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: if op == nil or op.ast[genericParamsPos].kind != nkEmpty:
# give up and find the canonical type instead: # give up and find the canonical type instead:
let h = sighashes.hashType(t, {CoType, CoConsiderOwned, CoDistinct}) let h = sighashes.hashType(t, {CoType, CoConsiderOwned, CoDistinct})
let canon = c.graph.canonTypes.getOrDefault(h) let canon = c.graph.canonTypes.getOrDefault(h)
if canon != nil: if canon != nil:
op = canon.attachedOps[kind] op = getAttachedOp(c.graph, canon, kind)
if op == nil: if op == nil:
#echo dest.typ.id #echo dest.typ.id
globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] & 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.} = proc canBeMoved(c: Con; t: PType): bool {.inline.} =
let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
if optOwnedRefs in c.graph.config.globalOptions: 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: else:
result = t.attachedOps[attachedSink] != nil result = getAttachedOp(c.graph, t, attachedSink) != nil
proc isNoInit(dest: PNode): bool {.inline.} = proc isNoInit(dest: PNode): bool {.inline.} =
result = dest.kind == nkSym and sfNoInit in dest.sym.flags 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) result = newTree(nkFastAsgn, dest, ri)
else: else:
let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink}) 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 = c.genOp(t, attachedSink, dest, ri)
result.add ri result.add ri
else: else:
@ -375,8 +375,8 @@ proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode =
let objType = leDotExpr[0].typ let objType = leDotExpr[0].typ
if hasDestructor(c, objType): if hasDestructor(c, objType):
if objType.attachedOps[attachedDestructor] != nil and if getAttachedOp(c.graph, objType, attachedDestructor) != nil and
sfOverriden in objType.attachedOps[attachedDestructor].flags: 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. 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""") 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) result.add newTree(nkFastAsgn, le, tmp)
@ -1095,7 +1095,7 @@ proc injectDestructorCalls*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n:
echo n echo n
if optCursorInference in g.config.options: if optCursorInference in g.config.options:
computeCursors(owner, n, g.config) computeCursors(owner, n, g)
var scope: Scope var scope: Scope
let body = p(n, c, scope, normal) let body = p(n, c, scope, normal)

View file

@ -29,6 +29,10 @@ type
c: PContext # c can be nil, then we are called from lambdalifting! c: PContext # c can be nil, then we are called from lambdalifting!
idgen: IdGenerator 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 fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode)
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
info: TLineInfo; idgen: IdGenerator): PSym info: TLineInfo; idgen: IdGenerator): PSym
@ -42,8 +46,9 @@ proc at(a, i: PNode, elemType: PType): PNode =
result[1] = i result[1] = i
result.typ = elemType result.typ = elemType
proc destructorOverriden(t: PType): bool = proc destructorOverriden(g: ModuleGraph; t: PType): bool =
t.attachedOps[attachedDestructor] != nil and sfOverriden in t.attachedOps[attachedDestructor].flags let op = getAttachedOp(g, t, attachedDestructor)
op != nil and sfOverriden in op.flags
proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) = proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
for i in 0..<t.len: for i in 0..<t.len:
@ -316,7 +321,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
field: var PSym): bool = field: var PSym): bool =
if optSeqDestructors in c.g.config.globalOptions: if optSeqDestructors in c.g.config.globalOptions:
var op = field var op = field
let destructorOverriden = destructorOverriden(t) let destructorOverriden = destructorOverriden(c.g, t)
if op != nil and op != c.fn and if op != nil and op != c.fn and
(sfOverriden in op.flags or destructorOverriden): (sfOverriden in op.flags or destructorOverriden):
if sfError in op.flags: if sfError in op.flags:
@ -368,7 +373,7 @@ proc addDestructorCall(c: var TLiftCtx; orig: PType; body, x: PNode) =
if op.ast[genericParamsPos].kind != nkEmpty: if op.ast[genericParamsPos].kind != nkEmpty:
# patch generic destructor: # patch generic destructor:
op = instantiateGeneric(c, op, t, t.typeInst) op = instantiateGeneric(c, op, t, t.typeInst)
t.attachedOps[attachedDestructor] = op setAttachedOp(c.g, c.idgen.module, t, attachedDestructor, op)
if op == nil and (useNoGc(c, t) or requiresDestructor(c, t)): if op == nil and (useNoGc(c, t) or requiresDestructor(c, t)):
op = produceSym(c.g, c.c, t, attachedDestructor, c.info, c.idgen) op = produceSym(c.g, c.c, t, attachedDestructor, c.info, c.idgen)
@ -392,7 +397,7 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
if op.ast[genericParamsPos].kind != nkEmpty: if op.ast[genericParamsPos].kind != nkEmpty:
# patch generic destructor: # patch generic destructor:
op = instantiateGeneric(c, op, t, t.typeInst) op = instantiateGeneric(c, op, t, t.typeInst)
t.attachedOps[attachedDestructor] = op setAttachedOp(c.g, c.idgen.module, t, attachedDestructor, op)
#markUsed(c.g.config, c.info, op, c.g.usageSym) #markUsed(c.g.config, c.info, op, c.g.usageSym)
onUse(c.info, op) onUse(c.info, op)
@ -400,11 +405,17 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
result = true result = true
#result = addDestructorCall(c, t, body, x) #result = addDestructorCall(c, t, body, x)
of attachedAsgn, attachedSink, attachedTrace: of attachedAsgn, attachedSink, attachedTrace:
result = considerAsgnOrSink(c, t, body, x, y, t.attachedOps[c.kind]) var op = getAttachedOp(c.g, t, c.kind)
result = considerAsgnOrSink(c, t, body, x, y, op)
if op != nil:
setAttachedOp(c.g, c.idgen.module, t, c.kind, op)
of attachedDispose: of attachedDispose:
result = considerAsgnOrSink(c, t, body, x, nil, t.attachedOps[c.kind]) var op = getAttachedOp(c.g, t, c.kind)
result = considerAsgnOrSink(c, t, body, x, nil, op)
if op != nil:
setAttachedOp(c.g, c.idgen.module, t, c.kind, op)
of attachedDeepCopy: of attachedDeepCopy:
let op = t.attachedOps[attachedDeepCopy] let op = getAttachedOp(c.g, t, attachedDeepCopy)
if op != nil: if op != nil:
#markUsed(c.g.config, c.info, op, c.g.usageSym) #markUsed(c.g.config, c.info, op, c.g.usageSym)
onUse(c.info, op) onUse(c.info, op)
@ -523,13 +534,15 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
doAssert t.destructor != nil doAssert t.destructor != nil
body.add destructorCall(c, t.destructor, x) body.add destructorCall(c, t.destructor, x)
of attachedTrace: of attachedTrace:
if t.attachedOps[c.kind] == nil: let op = getAttachedOp(c.g, t, c.kind)
if op == nil:
return # protect from recursion return # protect from recursion
body.add newHookCall(c, t.attachedOps[c.kind], x, y) body.add newHookCall(c, op, x, y)
of attachedDispose: of attachedDispose:
if t.attachedOps[c.kind] == nil: let op = getAttachedOp(c.g, t, c.kind)
if op == nil:
return # protect from recursion return # protect from recursion
body.add newHookCall(c, t.attachedOps[c.kind], x, nil) body.add newHookCall(c, op, x, nil)
proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
case c.kind case c.kind
@ -890,10 +903,10 @@ proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType;
idgen: IdGenerator): PSym = idgen: IdGenerator): PSym =
assert typ.kind == tyDistinct assert typ.kind == tyDistinct
let baseType = typ[0] let baseType = typ[0]
if baseType.attachedOps[kind] == nil: if getAttachedOp(g, baseType, kind) == nil:
discard produceSym(g, c, baseType, kind, info, idgen) discard produceSym(g, c, baseType, kind, info, idgen)
typ.attachedOps[kind] = baseType.attachedOps[kind] result = getAttachedOp(g, baseType, kind)
result = typ.attachedOps[kind] setAttachedOp(g, idgen.module, typ, kind, result)
proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp; proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
info: TLineInfo; idgen: IdGenerator): PSym = info: TLineInfo; idgen: IdGenerator): PSym =
@ -936,7 +949,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
if typ.kind == tyDistinct: if typ.kind == tyDistinct:
return produceSymDistinctType(g, c, typ, kind, info, idgen) return produceSymDistinctType(g, c, typ, kind, info, idgen)
result = typ.attachedOps[kind] result = getAttachedOp(g, typ, kind)
if result == nil: if result == nil:
result = symPrototype(g, typ, typ.owner, kind, info, idgen) result = symPrototype(g, typ, typ.owner, kind, info, idgen)
@ -949,12 +962,12 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
else: newSymNode(result.typ.n[2].sym) else: newSymNode(result.typ.n[2].sym)
# register this operation already: # register this operation already:
typ.attachedOps[kind] = result setAttachedOpPartial(g, idgen.module, typ, kind, result)
if kind == attachedSink and destructorOverriden(typ): if kind == attachedSink and destructorOverriden(g, typ):
## compiler can use a combination of `=destroy` and memCopy for sink op ## compiler can use a combination of `=destroy` and memCopy for sink op
dest.flags.incl sfCursor dest.flags.incl sfCursor
result.ast[bodyPos].add newOpCall(a, typ.attachedOps[attachedDestructor], d[0]) result.ast[bodyPos].add newOpCall(a, getAttachedOp(g, typ, attachedDestructor), d[0])
result.ast[bodyPos].add newAsgnStmt(d, src) result.ast[bodyPos].add newAsgnStmt(d, src)
else: else:
var tk: TTypeKind var tk: TTypeKind
@ -970,6 +983,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
else: else:
fillBody(a, typ, result.ast[bodyPos], d, src) fillBody(a, typ, result.ast[bodyPos], d, src)
if not a.canRaise: incl result.flags, sfNeverRaises if not a.canRaise: incl result.flags, sfNeverRaises
completePartialOp(g, idgen.module, typ, kind, result)
proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym, proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym,
@ -1002,30 +1016,34 @@ proc patchBody(g: ModuleGraph; c: PContext; n: PNode; info: TLineInfo; idgen: Id
if n.kind in nkCallKinds: if n.kind in nkCallKinds:
if n[0].kind == nkSym and n[0].sym.magic == mDestroy: if n[0].kind == nkSym and n[0].sym.magic == mDestroy:
let t = n[1].typ.skipTypes(abstractVar) let t = n[1].typ.skipTypes(abstractVar)
if t.destructor == nil: if getAttachedOp(g, t, attachedDestructor) == nil:
discard produceSym(g, c, t, attachedDestructor, info, idgen) discard produceSym(g, c, t, attachedDestructor, info, idgen)
if t.destructor != nil: let op = getAttachedOp(g, t, attachedDestructor)
if t.destructor.ast[genericParamsPos].kind != nkEmpty: if op != nil:
if op.ast[genericParamsPos].kind != nkEmpty:
internalError(g.config, info, "resolved destructor is generic") internalError(g.config, info, "resolved destructor is generic")
if t.destructor.magic == mDestroy: if op.magic == mDestroy:
internalError(g.config, info, "patching mDestroy with mDestroy?") internalError(g.config, info, "patching mDestroy with mDestroy?")
n[0] = newSymNode(t.destructor) n[0] = newSymNode(op)
for x in n: patchBody(g, c, x, info, idgen) for x in n: patchBody(g, c, x, info, idgen)
template inst(field, t, idgen) = proc inst(g: ModuleGraph; c: PContext; t: PType; kind: TTypeAttachedOp; idgen: IdGenerator;
if field.ast != nil and field.ast[genericParamsPos].kind != nkEmpty: info: TLineInfo) =
let op = getAttachedOp(g, t, kind)
if op != nil and op.ast != nil and op.ast[genericParamsPos].kind != nkEmpty:
if t.typeInst != nil: if t.typeInst != nil:
var a: TLiftCtx var a: TLiftCtx
a.info = info a.info = info
a.g = g a.g = g
a.kind = k a.kind = kind
a.c = c a.c = c
a.idgen = idgen a.idgen = idgen
field = instantiateGeneric(a, field, t, t.typeInst) let opInst = instantiateGeneric(a, op, t, t.typeInst)
if field.ast != nil: if opInst.ast != nil:
patchBody(g, c, field.ast, info, a.idgen) patchBody(g, c, opInst.ast, info, a.idgen)
setAttachedOp(g, idgen.module, t, kind, opInst)
else: else:
localError(g.config, info, "unresolved generic parameter") localError(g.config, info, "unresolved generic parameter")
@ -1062,24 +1080,26 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
else: attachedSink else: attachedSink
# bug #15122: We need to produce all prototypes before entering the # bug #15122: We need to produce all prototypes before entering the
# mind boggling recursion. Hacks like these imply we shoule rewrite # mind boggling recursion. Hacks like these imply we should rewrite
# this module. # this module.
var generics: array[attachedDestructor..attachedDispose, bool] var generics: array[attachedDestructor..attachedDispose, bool]
for k in attachedDestructor..lastAttached: for k in attachedDestructor..lastAttached:
generics[k] = canon.attachedOps[k] != nil generics[k] = getAttachedOp(g, canon, k) != nil
if not generics[k]: if not generics[k]:
canon.attachedOps[k] = symPrototype(g, canon, canon.owner, k, info, idgen) setAttachedOp(g, idgen.module, canon, k,
symPrototype(g, canon, canon.owner, k, info, idgen))
# we generate the destructor first so that other operators can depend on it: # we generate the destructor first so that other operators can depend on it:
for k in attachedDestructor..lastAttached: for k in attachedDestructor..lastAttached:
if not generics[k]: if not generics[k]:
discard produceSym(g, c, canon, k, info, idgen) discard produceSym(g, c, canon, k, info, idgen)
else: else:
inst(canon.attachedOps[k], canon, idgen) inst(g, c, canon, k, idgen, info)
if canon != orig: if canon != orig:
orig.attachedOps[k] = canon.attachedOps[k] setAttachedOp(g, idgen.module, orig, k, getAttachedOp(g, canon, k))
if not isTrival(orig.destructor): if not isTrival(getAttachedOp(g, orig, attachedDestructor)):
#or not isTrival(orig.assignment) or #or not isTrival(orig.assignment) or
# not isTrival(orig.sink): # not isTrival(orig.sink):
orig.flags.incl tfHasAsgn orig.flags.incl tfHasAsgn
# ^ XXX Breaks IC!

View file

@ -21,6 +21,7 @@ import
modules, modules,
modulegraphs, tables, lineinfos, pathutils, vmprofiler modulegraphs, tables, lineinfos, pathutils, vmprofiler
import ic / cbackend
from ic / to_packed_ast import rodViewer from ic / to_packed_ast import rodViewer
when not defined(leanCompiler): when not defined(leanCompiler):
@ -73,14 +74,15 @@ proc commandCompileToC(graph: ModuleGraph) =
setOutFile(conf) setOutFile(conf)
extccomp.initVars(conf) extccomp.initVars(conf)
semanticPasses(graph) semanticPasses(graph)
registerPass(graph, cgenPass) if conf.symbolFiles == disabledSf:
registerPass(graph, cgenPass)
if {optRun, optForceFullMake} * conf.globalOptions == {optRun} or isDefined(conf, "nimBetterRun"): if {optRun, optForceFullMake} * conf.globalOptions == {optRun} or isDefined(conf, "nimBetterRun"):
let proj = changeFileExt(conf.projectFull, "") let proj = changeFileExt(conf.projectFull, "")
if not changeDetectedViaJsonBuildInstructions(conf, proj): if not changeDetectedViaJsonBuildInstructions(conf, proj):
# nothing changed # nothing changed
graph.config.notes = graph.config.mainPackageNotes graph.config.notes = graph.config.mainPackageNotes
return return
if not extccomp.ccHasSaneOverflow(conf): if not extccomp.ccHasSaneOverflow(conf):
conf.symbols.defineSymbol("nimEmulateOverflowChecks") conf.symbols.defineSymbol("nimEmulateOverflowChecks")
@ -88,8 +90,14 @@ proc commandCompileToC(graph: ModuleGraph) =
compileProject(graph) compileProject(graph)
if graph.config.errorCounter > 0: if graph.config.errorCounter > 0:
return # issue #9933 return # issue #9933
cgenWriteModules(graph.backend, conf) if conf.symbolFiles == disabledSf:
if conf.cmd != cmdTcc: 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) extccomp.callCCompiler(conf)
# for now we do not support writing out a .json file with the build instructions when HCR is on # for now we do not support writing out a .json file with the build instructions when HCR is on
if not conf.hcrOn: if not conf.hcrOn:

View file

@ -27,9 +27,20 @@ type
pureEnums*: seq[PSym] pureEnums*: seq[PSym]
interf: TStrTable interf: TStrTable
Operators* = object
opNot*, opContains*, opLe*, opLt*, opAnd*, opOr*, opIsNil*, opEq*: PSym
opAdd*, opSub*, opMul*, opDiv*, opLen*: PSym
ModuleGraph* = ref object ModuleGraph* = ref object
ifaces*: seq[Iface] ## indexed by int32 fileIdx ifaces*: seq[Iface] ## indexed by int32 fileIdx
packed*: PackedModuleGraph 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 startupPackedConfig*: PackedConfig
packageSyms*: TStrTable packageSyms*: TStrTable
deps*: IntSet # the dependency graph or potentially its transitive closure. deps*: IntSet # the dependency graph or potentially its transitive closure.
@ -71,6 +82,7 @@ type
strongSemCheck*: proc (graph: ModuleGraph; owner: PSym; body: PNode) {.nimcall.} strongSemCheck*: proc (graph: ModuleGraph; owner: PSym; body: PNode) {.nimcall.}
compatibleProps*: proc (graph: ModuleGraph; formal, actual: PType): bool {.nimcall.} compatibleProps*: proc (graph: ModuleGraph; formal, actual: PType): bool {.nimcall.}
idgen*: IdGenerator idgen*: IdGenerator
operators*: Operators
TPassContext* = object of RootObj # the pass's context TPassContext* = object of RootObj # the pass's context
idgen*: IdGenerator idgen*: IdGenerator
@ -85,6 +97,67 @@ type
close: TPassClose, close: TPassClose,
isFrontend: bool] 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 const
cb64 = [ cb64 = [
@ -241,6 +314,22 @@ proc registerModule*(g: ModuleGraph; m: PSym) =
g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[]) g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[])
initStrTable(g.ifaces[m.position].interf) 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 = proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
result = ModuleGraph() result = ModuleGraph()
# A module ID of -1 means that the symbol is not attached to a module at all, # 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.cacheTables = initTable[string, BTree[string, PNode]]()
result.canonTypes = initTable[SigHash, PType]() result.canonTypes = initTable[SigHash, PType]()
result.symBodyHashes = initTable[int, SigHash]() result.symBodyHashes = initTable[int, SigHash]()
result.operators = initOperators(result)
proc resetAllModules*(g: ModuleGraph) = proc resetAllModules*(g: ModuleGraph) =
initStrTable(g.packageSyms) initStrTable(g.packageSyms)

View file

@ -460,7 +460,7 @@ proc numLines*(conf: ConfigRef, fileIdx: FileIndex): int =
if result == 0: if result == 0:
try: try:
for line in lines(toFullPathConsiderDirty(conf, fileIdx).string): for line in lines(toFullPathConsiderDirty(conf, fileIdx).string):
addSourceLine conf, fileIdx, line.string addSourceLine conf, fileIdx, line
except IOError: except IOError:
discard discard
result = conf.m.fileInfos[fileIdx.int32].lines.len result = conf.m.fileInfos[fileIdx.int32].lines.len

View file

@ -721,9 +721,9 @@ proc completeGeneratedFilePath*(conf: ConfigRef; f: AbsoluteFile,
result = subdir / RelativeFile f.string.splitPath.tail result = subdir / RelativeFile f.string.splitPath.tail
#echo "completeGeneratedFilePath(", f, ") = ", result #echo "completeGeneratedFilePath(", f, ") = ", result
proc toRodFile*(conf: ConfigRef; f: AbsoluteFile): AbsoluteFile = proc toRodFile*(conf: ConfigRef; f: AbsoluteFile; ext = RodExt): AbsoluteFile =
result = changeFileExt(completeGeneratedFilePath(conf, result = changeFileExt(completeGeneratedFilePath(conf,
withPackageName(conf, f)), RodExt) withPackageName(conf, f)), ext)
proc rawFindFile(conf: ConfigRef; f: RelativeFile; suppressStdlib: bool): AbsoluteFile = proc rawFindFile(conf: ConfigRef; f: RelativeFile; suppressStdlib: bool): AbsoluteFile =
for it in conf.searchPaths: for it in conf.searchPaths:

View file

@ -17,10 +17,7 @@ import
intsets, transf, vmdef, vm, aliases, cgmeth, lambdalifting, intsets, transf, vmdef, vm, aliases, cgmeth, lambdalifting,
evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity, evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity,
lowerings, plugins/active, lineinfos, strtabs, int128, lowerings, plugins/active, lineinfos, strtabs, int128,
isolation_check, typeallowed isolation_check, typeallowed, modulegraphs, enumtostr
from modulegraphs import ModuleGraph, PPassContext, onUse, onDef, onDefResolveForward,
systemModuleSym, semtab, getBody, someSym, allSyms
when defined(nimfix): when defined(nimfix):
import nimfix/prettybase 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: if x.kind in {nkPar, nkTupleConstr, nkCurly} and formal.kind != tyUntyped:
changeType(c, x, formal, check=true) changeType(c, x, formal, check=true)
result = arg 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 = 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 aEmpty = isEmptyContainer(a[i])
let bEmpty = isEmptyContainer(b[i]) let bEmpty = isEmptyContainer(b[i])
if aEmpty != bEmpty: 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] nt[i] = if aEmpty: b[i] else: a[i]
if not nt.isNil: result = nt if not nt.isNil: result = nt
#elif b[idx].kind == tyEmpty: return x #elif b[idx].kind == tyEmpty: return x

View file

@ -685,9 +685,9 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
var x: PType var x: PType
if param.typ.kind == tyVar: if param.typ.kind == tyVar:
x = newTypeS(param.typ.kind, c) 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: else:
x = t.baseOfDistinct(c.idgen) x = t.baseOfDistinct(c.graph, c.idgen)
call.add(newNodeIT(nkEmpty, fn.info, x)) call.add(newNodeIT(nkEmpty, fn.info, x))
if hasDistinct: if hasDistinct:
let filter = if fn.kind in {skProc, skFunc}: {skProc, skFunc} else: {fn.kind} let filter = if fn.kind in {skProc, skFunc}: {skProc, skFunc} else: {fn.kind}

View file

@ -558,6 +558,7 @@ proc saveRodFile*(c: PContext) =
addPragmaComputation(c, n) addPragmaComputation(c, n)
if sfSystemModule in c.module.flags: if sfSystemModule in c.module.flags:
c.graph.systemModuleComplete = true c.graph.systemModuleComplete = true
c.idgen.sealed = true # no further additions are allowed
if c.config.symbolFiles != stressTest: if c.config.symbolFiles != stressTest:
# For stress testing we seek to reload the symbols from memory. This # 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 # way much of the logic is tested but the test is reproducible as it does

View file

@ -95,9 +95,9 @@ proc sameInstantiation(a, b: TInstantiation): bool =
ExactGcSafety}): return ExactGcSafety}): return
result = true result = true
proc genericCacheGet(genericSym: PSym, entry: TInstantiation; proc genericCacheGet(g: ModuleGraph; genericSym: PSym, entry: TInstantiation;
id: CompilesId): PSym = 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[]): if (inst.compilesId == 0 or inst.compilesId == id) and sameInstantiation(entry, inst[]):
return inst.sym return inst.sym
@ -369,7 +369,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
if tfTriggersCompileTime in result.typ.flags: if tfTriggersCompileTime in result.typ.flags:
incl(result.flags, sfCompileTime) incl(result.flags, sfCompileTime)
n[genericParamsPos] = c.graph.emptyNode n[genericParamsPos] = c.graph.emptyNode
var oldPrc = genericCacheGet(fn, entry[], c.compilesContextId) var oldPrc = genericCacheGet(c.graph, fn, entry[], c.compilesContextId)
if oldPrc == nil: if oldPrc == nil:
# we MUST not add potentially wrong instantiations to the caching mechanism. # we MUST not add potentially wrong instantiations to the caching mechanism.
# This means recursive instantiations behave differently when in # This means recursive instantiations behave differently when in
@ -378,7 +378,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
#if c.compilesContextId == 0: #if c.compilesContextId == 0:
rawHandleSelf(c, result) rawHandleSelf(c, result)
entry.compilesId = c.compilesContextId entry.compilesId = c.compilesContextId
fn.procInstCache.add(entry) addToGenericProcCache(c.graph, c.module.position, fn, entry)
c.generics.add(makeInstPair(fn, entry)) c.generics.add(makeInstPair(fn, entry))
if n[pragmasPos].kind != nkEmpty: if n[pragmasPos].kind != nkEmpty:
pragma(c, result, n[pragmasPos], allRoutinePragmas) pragma(c, result, n[pragmasPos], allRoutinePragmas)

View file

@ -382,6 +382,8 @@ proc semUnown(c: PContext; n: PNode): PNode =
let b = unownedType(c, t[^1]) let b = unownedType(c, t[^1])
if b != t[^1]: if b != t[^1]:
result = copyType(t, nextTypeId c.idgen, t.owner) result = copyType(t, nextTypeId c.idgen, t.owner)
copyTypeProps(c.graph, c.idgen.module, result, t)
result[^1] = b result[^1] = b
result.flags.excl tfHasOwned result.flags.excl tfHasOwned
else: else:
@ -541,7 +543,8 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
# check if we converted this finalizer into a destructor already: # check if we converted this finalizer into a destructor already:
let t = whereToBindTypeHook(c, fin.typ[1].skipTypes(abstractInst+{tyRef})) 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" discard "already turned this one into a finalizer"
else: else:
bindTypeHook(c, turnFinalizerIntoDestructor(c, fin, n.info), n, attachedDestructor) bindTypeHook(c, turnFinalizerIntoDestructor(c, fin, n.info), n, attachedDestructor)
@ -549,8 +552,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
of mDestroy: of mDestroy:
result = n result = n
let t = n[1].typ.skipTypes(abstractVar) let t = n[1].typ.skipTypes(abstractVar)
if t.destructor != nil: let op = getAttachedOp(c.graph, t, attachedDestructor)
result[0] = newSymNode(t.destructor) if op != nil:
result[0] = newSymNode(op)
of mUnown: of mUnown:
result = semUnown(c, n) result = semUnown(c, n)
of mExists, mForall: of mExists, mForall:

View file

@ -81,7 +81,7 @@ proc initAnalysisCtx(g: ModuleGraph): AnalysisCtx =
result.slices = @[] result.slices = @[]
result.args = @[] result.args = @[]
result.guards.s = @[] result.guards.s = @[]
result.guards.o = initOperators(g) result.guards.g = g
result.graph = g result.graph = g
proc lookupSlot(c: AnalysisCtx; s: PSym): int = 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) = proc checkBounds(c: AnalysisCtx; arr, idx: PNode) =
checkLe(c, lowBound(c.graph.config, arr), idx) 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) = proc addLowerBoundAsFacts(c: var AnalysisCtx) =
for v in c.locals: 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) = proc addSlice(c: var AnalysisCtx; n: PNode; x, le, ri: PNode) =
checkLocal(c, n) checkLocal(c, n)
let le = le.canon(c.guards.o) let le = le.canon(c.graph.operators)
let ri = ri.canon(c.guards.o) let ri = ri.canon(c.graph.operators)
# perform static bounds checking here; and not later! # perform static bounds checking here; and not later!
let oldState = c.guards.s.len let oldState = c.guards.s.len
addLowerBoundAsFacts(c) addLowerBoundAsFacts(c)
@ -192,7 +192,7 @@ proc subStride(c: AnalysisCtx; n: PNode): PNode =
if isLocal(n): if isLocal(n):
let s = c.lookupSlot(n.sym) let s = c.lookupSlot(n.sym)
if s >= 0 and c.locals[s].stride != nil: 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: else:
result = n result = n
elif n.safeLen > 0: elif n.safeLen > 0:
@ -307,16 +307,16 @@ proc analyseCase(c: var AnalysisCtx; n: PNode) =
proc analyseIf(c: var AnalysisCtx; n: PNode) = proc analyseIf(c: var AnalysisCtx; n: PNode) =
analyse(c, n[0][0]) analyse(c, n[0][0])
let oldFacts = c.guards.s.len 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]) analyse(c, n[0][1])
for i in 1..<n.len: for i in 1..<n.len:
let branch = n[i] let branch = n[i]
setLen(c.guards.s, oldFacts) setLen(c.guards.s, oldFacts)
for j in 0..i-1: for j in 0..i-1:
addFactNeg(c.guards, canon(n[j][0], c.guards.o)) addFactNeg(c.guards, canon(n[j][0], c.graph.operators))
if branch.len > 1: if branch.len > 1:
addFact(c.guards, canon(branch[0], c.guards.o)) addFact(c.guards, canon(branch[0], c.graph.operators))
for i in 0..<branch.len: for i in 0..<branch.len:
analyse(c, branch[i]) analyse(c, branch[i])
setLen(c.guards.s, oldFacts) setLen(c.guards.s, oldFacts)
@ -382,13 +382,13 @@ proc analyse(c: var AnalysisCtx; n: PNode) =
# loop may never execute: # loop may never execute:
let oldState = c.locals.len let oldState = c.locals.len
let oldFacts = c.guards.s.len let oldFacts = c.guards.s.len
addFact(c.guards, canon(n[0], c.guards.o)) addFact(c.guards, canon(n[0], c.graph.operators))
analyse(c, n[1]) analyse(c, n[1])
setLen(c.locals, oldState) setLen(c.locals, oldState)
setLen(c.guards.s, oldFacts) setLen(c.guards.s, oldFacts)
# we know after the loop the negation holds: # we know after the loop the negation holds:
if not hasSubnodeWith(n[1], nkBreakStmt): if not hasSubnodeWith(n[1], nkBreakStmt):
addFactNeg(c.guards, canon(n[0], c.guards.o)) addFactNeg(c.guards, canon(n[0], c.graph.operators))
dec c.inLoop dec c.inLoop
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
nkMacroDef, nkTemplateDef, nkConstSection, nkPragma, nkFuncDef: nkMacroDef, nkTemplateDef, nkConstSection, nkPragma, nkFuncDef:

View file

@ -723,7 +723,7 @@ proc checkLe(c: PEffects; a, b: PNode) =
proc checkBounds(c: PEffects; arr, idx: PNode) = proc checkBounds(c: PEffects; arr, idx: PNode) =
checkLe(c, lowBound(c.config, arr), idx) checkLe(c, lowBound(c.config, arr), idx)
checkLe(c, idx, highBound(c.config, arr, c.guards.o)) checkLe(c, idx, highBound(c.config, arr, c.guards.g.operators))
proc checkRange(c: PEffects; value: PNode; typ: PType) = proc checkRange(c: PEffects; value: PNode; typ: PType) =
let t = typ.skipTypes(abstractInst - {tyRange}) let t = typ.skipTypes(abstractInst - {tyRange})
@ -818,9 +818,9 @@ proc trackCall(tracked: PEffects; n: PNode) =
if opKind != -1: if opKind != -1:
# rebind type bounds operations after createTypeBoundOps call # rebind type bounds operations after createTypeBoundOps call
let t = n[1].typ.skipTypes({tyAlias, tyVar}) let t = n[1].typ.skipTypes({tyAlias, tyVar})
if a.sym != t.attachedOps[TTypeAttachedOp(opKind)]: if a.sym != getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind)):
createTypeBoundOps(tracked, t, n.info) createTypeBoundOps(tracked, t, n.info)
let op = t.attachedOps[TTypeAttachedOp(opKind)] let op = getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind))
if op != nil: if op != nil:
n[0].sym = op n[0].sym = op
@ -1236,7 +1236,7 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects; c: PC
t.ownerModule = s.getModule t.ownerModule = s.getModule
t.init = @[] t.init = @[]
t.guards.s = @[] t.guards.s = @[]
t.guards.o = initOperators(g) t.guards.g = g
when defined(drnim): when defined(drnim):
t.currOptions = g.config.options + s.options - {optStaticBoundsCheck} t.currOptions = g.config.options + s.options - {optStaticBoundsCheck}
else: else:
@ -1310,7 +1310,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
var goals: set[Goal] = {} var goals: set[Goal] = {}
if strictFuncs in c.features: goals.incl constParameters if strictFuncs in c.features: goals.incl constParameters
if views in c.features: goals.incl borrowChecking if views in c.features: goals.incl borrowChecking
var partitions = computeGraphPartitions(s, body, g.config, goals) var partitions = computeGraphPartitions(s, body, g, goals)
if not t.hasSideEffect and t.hasDangerousAssign: if not t.hasSideEffect and t.hasDangerousAssign:
t.hasSideEffect = varpartitions.hasSideEffect(partitions, mutationInfo) t.hasSideEffect = varpartitions.hasSideEffect(partitions, mutationInfo)
if views in c.features: if views in c.features:

View file

@ -1693,12 +1693,13 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
else: break else: break
if obj.kind in {tyObject, tyDistinct, tySequence, tyString}: if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
obj = canonType(c, obj) obj = canonType(c, obj)
if obj.attachedOps[op] == s: let ao = getAttachedOp(c.graph, obj, op)
if ao == s:
discard "forward declared destructor" discard "forward declared destructor"
elif obj.attachedOps[op].isNil and tfCheckedForDestructor notin obj.flags: elif ao.isNil and tfCheckedForDestructor notin obj.flags:
obj.attachedOps[op] = s setAttachedOp(c.graph, c.module.position, obj, op, s)
else: else:
prevDestructor(c, obj.attachedOps[op], obj, n.info) prevDestructor(c, ao, obj, n.info)
noError = true noError = true
if obj.owner.getModule != s.getModule: if obj.owner.getModule != s.getModule:
localError(c.config, n.info, errGenerated, localError(c.config, n.info, errGenerated,
@ -1726,7 +1727,8 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
elif t.kind == tyGenericInvocation: t = t[0] elif t.kind == tyGenericInvocation: t = t[0]
else: break else: break
if t.kind in {tyObject, tyDistinct, tyEnum, tySequence, tyString}: if t.kind in {tyObject, tyDistinct, tyEnum, tySequence, tyString}:
if t.attachedOps[attachedDeepCopy].isNil: t.attachedOps[attachedDeepCopy] = s if getAttachedOp(c.graph, t, attachedDeepCopy).isNil:
setAttachedOp(c.graph, c.module.position, t, attachedDeepCopy, s)
else: else:
localError(c.config, n.info, errGenerated, localError(c.config, n.info, errGenerated,
"cannot bind another 'deepCopy' to: " & typeToString(t)) "cannot bind another 'deepCopy' to: " & typeToString(t))
@ -1766,12 +1768,13 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
obj = canonType(c, obj) obj = canonType(c, obj)
#echo "ATTACHING TO ", obj.id, " ", s.name.s, " ", cast[int](obj) #echo "ATTACHING TO ", obj.id, " ", s.name.s, " ", cast[int](obj)
let k = if name == "=" or name == "=copy": attachedAsgn else: attachedSink let k = if name == "=" or name == "=copy": attachedAsgn else: attachedSink
if obj.attachedOps[k] == s: let ao = getAttachedOp(c.graph, obj, k)
if ao == s:
discard "forward declared op" discard "forward declared op"
elif obj.attachedOps[k].isNil and tfCheckedForDestructor notin obj.flags: elif ao.isNil and tfCheckedForDestructor notin obj.flags:
obj.attachedOps[k] = s setAttachedOp(c.graph, c.module.position, obj, k, s)
else: else:
prevDestructor(c, obj.attachedOps[k], obj, n.info) prevDestructor(c, ao, obj, n.info)
if obj.owner.getModule != s.getModule: if obj.owner.getModule != s.getModule:
localError(c.config, n.info, errGenerated, localError(c.config, n.info, errGenerated,
"type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")") "type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
@ -1827,7 +1830,7 @@ proc semMethodPrototype(c: PContext; s: PSym; n: PNode) =
tyAlias, tySink, tyOwned}) tyAlias, tySink, tyOwned})
if x.kind == tyObject and t.len-1 == n[genericParamsPos].len: if x.kind == tyObject and t.len-1 == n[genericParamsPos].len:
foundObj = true foundObj = true
x.methods.add((col,s)) addMethodToGeneric(c.graph, c.module.position, x, col, s)
message(c.config, n.info, warnDeprecated, "generic methods are deprecated") message(c.config, n.info, warnDeprecated, "generic methods are deprecated")
#if not foundObj: #if not foundObj:
# message(c.config, n.info, warnDeprecated, "generic method not attachable to object type is deprecated") # message(c.config, n.info, warnDeprecated, "generic method not attachable to object type is deprecated")

View file

@ -154,6 +154,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
addPureEnum(c, result.sym) addPureEnum(c, result.sym)
if tfNotNil in e.typ.flags and not hasNull: if tfNotNil in e.typ.flags and not hasNull:
result.flags.incl tfRequiresInit result.flags.incl tfRequiresInit
setToStringProc(c.graph, result, genEnumToStrProc(result, n.info, c.graph, c.idgen))
proc semSet(c: PContext, n: PNode, prev: PType): PType = proc semSet(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tySet, prev, c) result = newOrPrevType(tySet, prev, c)
@ -1117,6 +1118,8 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
of tyGenericInst: of tyGenericInst:
if paramType.lastSon.kind == tyUserTypeClass: if paramType.lastSon.kind == tyUserTypeClass:
var cp = copyType(paramType, nextTypeId c.idgen, getCurrOwner(c)) var cp = copyType(paramType, nextTypeId c.idgen, getCurrOwner(c))
copyTypeProps(c.graph, c.idgen.module, cp, paramType)
cp.kind = tyUserTypeClassInst cp.kind = tyUserTypeClassInst
return addImplicitGeneric(c, cp, paramTypId, info, genericParams, paramName) return addImplicitGeneric(c, cp, paramTypId, info, genericParams, paramName)
@ -1530,6 +1533,7 @@ proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType =
proc freshType(c: PContext; res, prev: PType): PType {.inline.} = proc freshType(c: PContext; res, prev: PType): PType {.inline.} =
if prev.isNil: if prev.isNil:
result = copyType(res, nextTypeId c.idgen, res.owner) result = copyType(res, nextTypeId c.idgen, res.owner)
copyTypeProps(c.graph, c.idgen.module, result, res)
else: else:
result = res result = res
@ -1843,7 +1847,9 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of mExpr: of mExpr:
result = semTypeNode(c, n[0], nil) result = semTypeNode(c, n[0], nil)
if result != nil: if result != nil:
let old = result
result = copyType(result, nextTypeId c.idgen, getCurrOwner(c)) result = copyType(result, nextTypeId c.idgen, getCurrOwner(c))
copyTypeProps(c.graph, c.idgen.module, result, old)
for i in 1..<n.len: for i in 1..<n.len:
result.rawAddSon(semTypeNode(c, n[i], nil)) result.rawAddSon(semTypeNode(c, n[i], nil))
of mDistinct: of mDistinct:
@ -2117,12 +2123,16 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
typ.flags.incl tfGenericTypeParam typ.flags.incl tfGenericTypeParam
for j in 0..<a.len-2: for j in 0..<a.len-2:
let finalType = if j == 0: typ var finalType: PType
else: copyType(typ, nextTypeId c.idgen, typ.owner) if j == 0:
# it's important the we create an unique finalType = typ
# type for each generic param. the index else:
# of the parameter will be stored in the finalType = copyType(typ, nextTypeId c.idgen, typ.owner)
# attached symbol. copyTypeProps(c.graph, c.idgen.module, finalType, typ)
# it's important the we create an unique
# type for each generic param. the index
# of the parameter will be stored in the
# attached symbol.
var paramName = a[j] var paramName = a[j]
var covarianceFlag = tfUnresolved var covarianceFlag = tfUnresolved

View file

@ -10,7 +10,7 @@
# This module does the instantiation of generic types. # This module does the instantiation of generic types.
import ast, astalgo, msgs, types, magicsys, semdata, renderer, options, import ast, astalgo, msgs, types, magicsys, semdata, renderer, options,
lineinfos lineinfos, modulegraphs
const tfInstClearedFlags = {tfHasMeta, tfUnresolved} const tfInstClearedFlags = {tfHasMeta, tfUnresolved}
@ -30,15 +30,12 @@ proc checkConstructedType*(conf: ConfigRef; info: TLineInfo, typ: PType) =
if t[0].kind != tyObject or tfFinal in t[0].flags: if t[0].kind != tyObject or tfFinal in t[0].flags:
localError(info, errInheritanceOnlyWithNonFinalObjects) localError(info, errInheritanceOnlyWithNonFinalObjects)
proc searchInstTypes*(key: PType): PType = proc searchInstTypes*(g: ModuleGraph; key: PType): PType =
let genericTyp = key[0] let genericTyp = key[0]
if not (genericTyp.kind == tyGenericBody and if not (genericTyp.kind == tyGenericBody and
key[0] == genericTyp and genericTyp.sym != nil): return key[0] == genericTyp and genericTyp.sym != nil): return
when not defined(nimNoNilSeqs): for inst in typeInstCacheItems(g, genericTyp.sym):
if genericTyp.sym.typeInstCache == nil: return
for inst in genericTyp.sym.typeInstCache:
if inst.id == key.id: return inst if inst.id == key.id: return inst
if inst.len < key.len: if inst.len < key.len:
# XXX: This happens for prematurely cached # XXX: This happens for prematurely cached
@ -57,14 +54,12 @@ proc searchInstTypes*(key: PType): PType =
return inst return inst
proc cacheTypeInst*(inst: PType) = proc cacheTypeInst(g: ModuleGraph; moduleId: int; inst: PType) =
# XXX: add to module's generics
# update the refcount
let gt = inst[0] let gt = inst[0]
let t = if gt.kind == tyGenericBody: gt.lastSon else: gt let t = if gt.kind == tyGenericBody: gt.lastSon else: gt
if t.kind in {tyStatic, tyError, tyGenericParam} + tyTypeClasses: if t.kind in {tyStatic, tyError, tyGenericParam} + tyTypeClasses:
return return
gt.sym.typeInstCache.add(inst) addToGenericCache(g, moduleId, gt.sym, inst)
type type
LayeredIdTable* = ref object LayeredIdTable* = ref object
@ -306,6 +301,7 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
result = t.exactReplica result = t.exactReplica
else: else:
result = copyType(t, nextTypeId(cl.c.idgen), t.owner) result = copyType(t, nextTypeId(cl.c.idgen), t.owner)
copyTypeProps(cl.c.graph, cl.c.idgen.module, result, t)
#cl.typeMap.topLayer.idTablePut(result, t) #cl.typeMap.topLayer.idTablePut(result, t)
if cl.allowMetaTypes: return if cl.allowMetaTypes: return
@ -332,7 +328,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
if cl.allowMetaTypes: if cl.allowMetaTypes:
result = PType(idTableGet(cl.localCache, t)) result = PType(idTableGet(cl.localCache, t))
else: else:
result = searchInstTypes(t) result = searchInstTypes(cl.c.graph, t)
if result != nil and sameFlags(result, t): if result != nil and sameFlags(result, t):
when defined(reportCacheHits): when defined(reportCacheHits):
@ -351,7 +347,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
if header != t: if header != t:
# search again after first pass: # search again after first pass:
result = searchInstTypes(header) result = searchInstTypes(cl.c.graph, header)
if result != nil and sameFlags(result, t): if result != nil and sameFlags(result, t):
when defined(reportCacheHits): when defined(reportCacheHits):
echo "Generic instantiation cached ", typeToString(result), " for ", echo "Generic instantiation cached ", typeToString(result), " for ",
@ -368,7 +364,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
# we need to add the candidate here, before it's fully instantiated for # we need to add the candidate here, before it's fully instantiated for
# recursive instantions: # recursive instantions:
if not cl.allowMetaTypes: if not cl.allowMetaTypes:
cacheTypeInst(result) cacheTypeInst(cl.c.graph, cl.c.module.position, result)
else: else:
idTablePut(cl.localCache, t, result) idTablePut(cl.localCache, t, result)
@ -408,13 +404,13 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
if newbody.isGenericAlias: newbody = newbody.skipGenericAlias if newbody.isGenericAlias: newbody = newbody.skipGenericAlias
rawAddSon(result, newbody) rawAddSon(result, newbody)
checkPartialConstructedType(cl.c.config, cl.info, newbody) checkPartialConstructedType(cl.c.config, cl.info, newbody)
let dc = newbody.attachedOps[attachedDeepCopy]
if not cl.allowMetaTypes: if not cl.allowMetaTypes:
if dc != nil and sfFromGeneric notin newbody.attachedOps[attachedDeepCopy].flags: let dc = cl.c.graph.getAttachedOp(newbody, attachedDeepCopy)
if dc != nil and sfFromGeneric notin dc.flags:
# 'deepCopy' needs to be instantiated for # 'deepCopy' needs to be instantiated for
# generics *when the type is constructed*: # generics *when the type is constructed*:
newbody.attachedOps[attachedDeepCopy] = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info, cl.c.graph.setAttachedOp(cl.c.module.position, newbody, attachedDeepCopy,
attachedDeepCopy, 1) cl.c.instTypeBoundOp(cl.c, dc, result, cl.info, attachedDeepCopy, 1))
if newbody.typeInst == nil: if newbody.typeInst == nil:
# doAssert newbody.typeInst == nil # doAssert newbody.typeInst == nil
newbody.typeInst = result newbody.typeInst = result
@ -436,8 +432,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
if tfFromGeneric notin mm.flags: if tfFromGeneric notin mm.flags:
# bug #5479, prevent endless recursions here: # bug #5479, prevent endless recursions here:
incl mm.flags, tfFromGeneric incl mm.flags, tfFromGeneric
let methods = mm.methods for col, meth in methodsForGeneric(cl.c.graph, mm):
for col, meth in items(methods):
# we instantiate the known methods belonging to that type, this causes # we instantiate the known methods belonging to that type, this causes
# them to be registered and that's enough, so we 'discard' the result. # them to be registered and that's enough, so we 'discard' the result.
discard cl.c.instTypeBoundOp(cl.c, meth, result, cl.info, discard cl.c.instTypeBoundOp(cl.c, meth, result, cl.info,

View file

@ -496,7 +496,7 @@ proc transformConv(c: PTransf, n: PNode): PNode =
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
result = transform(c, n[1]) result = transform(c, n[1])
#result = transformSons(c, n) #result = transformSons(c, n)
result.typ = takeType(n.typ, n[1].typ, c.idgen) result.typ = takeType(n.typ, n[1].typ, c.graph, c.idgen)
#echo n.info, " came here and produced ", typeToString(result.typ), #echo n.info, " came here and produced ", typeToString(result.typ),
# " from ", typeToString(n.typ), " and ", typeToString(n[1].typ) # " from ", typeToString(n.typ), " and ", typeToString(n[1].typ)
of tyCString: of tyCString:
@ -612,6 +612,8 @@ proc isSimpleIteratorVar(c: PTransf; iter: PSym): bool =
rec(getBody(c.graph, iter), iter, dangerousYields) rec(getBody(c.graph, iter), iter, dangerousYields)
result = dangerousYields == 0 result = dangerousYields == 0
template destructor(t: PType): PSym = getAttachedOp(c.graph, t, attachedDestructor)
proc transformFor(c: PTransf, n: PNode): PNode = proc transformFor(c: PTransf, n: PNode): PNode =
# generate access statements for the parameters (unless they are constant) # generate access statements for the parameters (unless they are constant)
# put mapping from formal parameters to actual parameters # put mapping from formal parameters to actual parameters

View file

@ -11,7 +11,7 @@
import import
intsets, ast, astalgo, trees, msgs, strutils, platform, renderer, options, intsets, ast, astalgo, trees, msgs, strutils, platform, renderer, options,
lineinfos, int128 lineinfos, int128, modulegraphs
type type
TPreferedDesc* = enum TPreferedDesc* = enum
@ -1307,11 +1307,12 @@ proc containsGenericTypeIter(t: PType, closure: RootRef): bool =
proc containsGenericType*(t: PType): bool = proc containsGenericType*(t: PType): bool =
result = iterOverType(t, containsGenericTypeIter, nil) result = iterOverType(t, containsGenericTypeIter, nil)
proc baseOfDistinct*(t: PType; idgen: IdGenerator): PType = proc baseOfDistinct*(t: PType; g: ModuleGraph; idgen: IdGenerator): PType =
if t.kind == tyDistinct: if t.kind == tyDistinct:
result = t[0] result = t[0]
else: else:
result = copyType(t, nextTypeId idgen, t.owner) result = copyType(t, nextTypeId idgen, t.owner)
copyTypeProps(g, idgen.module, result, t)
var parent: PType = nil var parent: PType = nil
var it = result var it = result
while it.kind in {tyPtr, tyRef, tyOwned}: while it.kind in {tyPtr, tyRef, tyOwned}:
@ -1449,7 +1450,7 @@ proc isEmptyContainer*(t: PType): bool =
of tyGenericInst, tyAlias, tySink: result = isEmptyContainer(t.lastSon) of tyGenericInst, tyAlias, tySink: result = isEmptyContainer(t.lastSon)
else: result = false else: result = false
proc takeType*(formal, arg: PType; idgen: IdGenerator): PType = proc takeType*(formal, arg: PType; g: ModuleGraph; idgen: IdGenerator): PType =
# param: openArray[string] = [] # param: openArray[string] = []
# [] is an array constructor of length 0 of type string! # [] is an array constructor of length 0 of type string!
if arg.kind == tyNil: if arg.kind == tyNil:
@ -1458,6 +1459,7 @@ proc takeType*(formal, arg: PType; idgen: IdGenerator): PType =
elif formal.kind in {tyOpenArray, tyVarargs, tySequence} and elif formal.kind in {tyOpenArray, tyVarargs, tySequence} and
arg.isEmptyContainer: arg.isEmptyContainer:
let a = copyType(arg.skipTypes({tyGenericInst, tyAlias}), nextTypeId(idgen), arg.owner) let a = copyType(arg.skipTypes({tyGenericInst, tyAlias}), nextTypeId(idgen), arg.owner)
copyTypeProps(g, idgen.module, a, arg)
a[ord(arg.kind == tyArray)] = formal[0] a[ord(arg.kind == tyArray)] = formal[0]
result = a result = a
elif formal.kind in {tyTuple, tySet} and arg.kind == formal.kind: elif formal.kind in {tyTuple, tySet} and arg.kind == formal.kind:
@ -1465,14 +1467,14 @@ proc takeType*(formal, arg: PType; idgen: IdGenerator): PType =
else: else:
result = arg result = arg
proc skipHiddenSubConv*(n: PNode; idgen: IdGenerator): PNode = proc skipHiddenSubConv*(n: PNode; g: ModuleGraph; idgen: IdGenerator): PNode =
if n.kind == nkHiddenSubConv: if n.kind == nkHiddenSubConv:
# param: openArray[string] = [] # param: openArray[string] = []
# [] is an array constructor of length 0 of type string! # [] is an array constructor of length 0 of type string!
let formal = n.typ let formal = n.typ
result = n[1] result = n[1]
let arg = result.typ let arg = result.typ
let dest = takeType(formal, arg, idgen) let dest = takeType(formal, arg, g, idgen)
if dest == arg and formal.kind != tyUntyped: if dest == arg and formal.kind != tyUntyped:
#echo n.info, " came here for ", formal.typeToString #echo n.info, " came here for ", formal.typeToString
result = n result = n

View file

@ -28,7 +28,7 @@
## See https://nim-lang.github.io/Nim/manual_experimental.html#view-types-algorithm ## See https://nim-lang.github.io/Nim/manual_experimental.html#view-types-algorithm
## for a high-level description of how borrow checking works. ## for a high-level description of how borrow checking works.
import ast, types, lineinfos, options, msgs, renderer, typeallowed import ast, types, lineinfos, options, msgs, renderer, typeallowed, modulegraphs
from trees import getMagic, isNoSideEffectPragma, stupidStmtListExpr from trees import getMagic, isNoSideEffectPragma, stupidStmtListExpr
from isolation_check import canAlias from isolation_check import canAlias
@ -99,7 +99,7 @@ type
unanalysableMutation: bool unanalysableMutation: bool
inAsgnSource, inConstructor, inNoSideEffectSection: int inAsgnSource, inConstructor, inNoSideEffectSection: int
owner: PSym owner: PSym
config: ConfigRef g: ModuleGraph
proc mutationAfterConnection(g: MutationInfo): bool {.inline.} = proc mutationAfterConnection(g: MutationInfo): bool {.inline.} =
#echo g.maxMutation.int, " ", g.minConnection.int, " ", g.param #echo g.maxMutation.int, " ", g.minConnection.int, " ", g.param
@ -509,11 +509,11 @@ proc borrowFrom(c: var Partitions; dest: PSym; src: PNode) =
let s = pathExpr(src, c.owner) let s = pathExpr(src, c.owner)
if s == nil: if s == nil:
localError(c.config, src.info, "cannot borrow from " & $src & ", it is not a path expression; " & url) localError(c.g.config, src.info, "cannot borrow from " & $src & ", it is not a path expression; " & url)
elif s.kind == nkSym: elif s.kind == nkSym:
if dest.kind == skResult: if dest.kind == skResult:
if s.sym.kind != skParam or s.sym.position != 0: if s.sym.kind != skParam or s.sym.position != 0:
localError(c.config, src.info, "'result' must borrow from the first parameter") localError(c.g.config, src.info, "'result' must borrow from the first parameter")
let vid = variableId(c, dest) let vid = variableId(c, dest)
if vid >= 0: if vid >= 0:
@ -539,13 +539,13 @@ proc borrowingCall(c: var Partitions; destType: PType; n: PNode; i: int) =
when false: when false:
let isView = directViewType(destType) == immutableView let isView = directViewType(destType) == immutableView
if n[0].kind == nkSym and n[0].sym.name.s == "[]=": 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..<n.len: for j in i+1..<n.len:
if getMagic(n[j]) == mSlice: if getMagic(n[j]) == mSlice:
borrowFrom(c, v.sym, n[j]) borrowFrom(c, v.sym, n[j])
else: else:
localError(c.config, n[i].info, "cannot determine the target of the borrow") localError(c.g.config, n[i].info, "cannot determine the target of the borrow")
proc borrowingAsgn(c: var Partitions; dest, src: PNode) = proc borrowingAsgn(c: var Partitions; dest, src: PNode) =
proc mutableParameter(n: PNode): bool {.inline.} = proc mutableParameter(n: PNode): bool {.inline.} =
@ -569,7 +569,7 @@ proc borrowingAsgn(c: var Partitions; dest, src: PNode) =
mutableParameter(dest[0][0]): mutableParameter(dest[0][0]):
discard "remains a mutable location anyhow" discard "remains a mutable location anyhow"
else: else:
localError(c.config, dest.info, "attempt to mutate a borrowed location from an immutable view") localError(c.g.config, dest.info, "attempt to mutate a borrowed location from an immutable view")
of noView: discard "nothing to do" of noView: discard "nothing to do"
proc containsPointer(t: PType): bool = proc containsPointer(t: PType): bool =
@ -603,7 +603,7 @@ proc deps(c: var Partitions; dest, src: PNode) =
for s in sources: for s in sources:
if s == d.sym: if s == d.sym:
discard "assignments like: it = it.next are fine" discard "assignments like: it = it.next are fine"
elif {sfGlobal, sfThread} * s.flags != {} or hasDisabledAsgn(s.typ): elif {sfGlobal, sfThread} * s.flags != {} or hasDisabledAsgn(c.g, s.typ):
# do not borrow from a global variable or from something with a # do not borrow from a global variable or from something with a
# disabled assignment operator. # disabled assignment operator.
c.s[vid].flags.incl preventCursor c.s[vid].flags.incl preventCursor
@ -826,8 +826,8 @@ proc computeLiveRanges(c: var Partitions; n: PNode) =
else: else:
for child in n: computeLiveRanges(c, child) for child in n: computeLiveRanges(c, child)
proc computeGraphPartitions*(s: PSym; n: PNode; config: ConfigRef; goals: set[Goal]): Partitions = proc computeGraphPartitions*(s: PSym; n: PNode; g: ModuleGraph; goals: set[Goal]): Partitions =
result = Partitions(owner: s, config: config, goals: goals) result = Partitions(owner: s, g: g, goals: goals)
if s.kind notin {skModule, skMacro}: if s.kind notin {skModule, skMacro}:
let params = s.typ.n let params = s.typ.n
for i in 1..<params.len: for i in 1..<params.len:
@ -892,8 +892,8 @@ proc checkBorrowedLocations*(par: var Partitions; body: PNode; config: ConfigRef
#if par.s[rid].con.kind == isRootOf and dangerousMutation(par.graphs[par.s[rid].con.graphIndex], par.s[i]): #if par.s[rid].con.kind == isRootOf and dangerousMutation(par.graphs[par.s[rid].con.graphIndex], par.s[i]):
# cannotBorrow(config, s, par.graphs[par.s[rid].con.graphIndex]) # cannotBorrow(config, s, par.graphs[par.s[rid].con.graphIndex])
proc computeCursors*(s: PSym; n: PNode; config: ConfigRef) = proc computeCursors*(s: PSym; n: PNode; g: ModuleGraph) =
var par = computeGraphPartitions(s, n, config, {cursorInference}) var par = computeGraphPartitions(s, n, g, {cursorInference})
for i in 0 ..< par.s.len: for i in 0 ..< par.s.len:
let v = addr(par.s[i]) let v = addr(par.s[i])
if v.flags * {ownsData, preventCursor} == {} and v.sym.kind notin {skParam, skResult} and if v.flags * {ownsData, preventCursor} == {} and v.sym.kind notin {skParam, skResult} and