IC: bugfixes (WIP) (#16836)

* minor improvements
* IC: added the required logic for compilerProcs
* LazySym ftw
* we need this testing logic
* reimplement the old way we use for module package creation
* fixes a regression; don't pick module names if you can avoid it
This commit is contained in:
Andreas Rumpf 2021-02-02 19:24:55 +01:00 • committed by GitHub
commit fb80d2ff85
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 283 additions and 98 deletions

View file

@ -51,10 +51,12 @@ proc addFileToLink(config: ConfigRef; m: PSym) =
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)
let objFile = completeCfilePath(config, toObjFile(config, cfile))
if fileExists(objFile):
var cf = Cfile(nimname: m.name.s, cname: cfile,
obj: objFile,
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")

View file

@ -9,18 +9,20 @@
## Dead code elimination (=DCE) for IC.
import std / intsets
import ".." / [ast, options, lineinfos]
import std / [intsets, tables]
import ".." / [ast, options, lineinfos, types]
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.
stack: seq[(int, TOptions, 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.
options: TOptions
compilerProcs: Table[string, (int, int32)]
proc isExportedToC(c: var AliveContext; g: PackedModuleGraph; symId: int32): bool =
## "Exported to C" procs are special (these are marked with '.exportc') because these
@ -36,6 +38,8 @@ proc isExportedToC(c: var AliveContext; g: PackedModuleGraph; symId: int32): boo
result = true
# XXX: This used to be a condition to:
# (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or
if sfCompilerProc in flags:
c.compilerProcs[g[c.thisModule].fromDisk.sh.strings[symPtr.name]] = (c.thisModule, symId)
template isNotGeneric(n: NodePos): bool = ithSon(tree, n, genericParamsPos).kind == nkEmpty
@ -45,7 +49,40 @@ proc followLater(c: var AliveContext; g: PackedModuleGraph; module: int; item: i
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)))
let opt = g[module].fromDisk.sh.syms[item].options
c.stack.add((module, opt, NodePos(body)))
proc requestCompilerProc(c: var AliveContext; g: PackedModuleGraph; name: string) =
let (module, item) = c.compilerProcs[name]
followLater(c, g, module, item)
proc loadTypeKind(t: PackedItemId; c: AliveContext; g: PackedModuleGraph; toSkip: set[TTypeKind]): TTypeKind =
template kind(t: ItemId): TTypeKind = g[t.module].fromDisk.sh.types[t.item].kind
var t2 = translateId(t, g, c.thisModule, c.decoder.config)
result = t2.kind
while result in toSkip:
t2 = translateId(g[t2.module].fromDisk.sh.types[t2.item].types[^1], g, t2.module, c.decoder.config)
result = t2.kind
proc rangeCheckAnalysis(c: var AliveContext; g: PackedModuleGraph; tree: PackedTree; n: NodePos) =
## Replicates the logic of `ccgexprs.genRangeChck`.
## XXX Refactor so that the duplicated logic is avoided. However, for now it's not clear
## the approach has enough merit.
var dest = loadTypeKind(n.typ, c, g, abstractVar)
if optRangeCheck notin c.options or dest in {tyUInt..tyUInt64}:
discard "no need to generate a check because it was disabled"
else:
let n0t = loadTypeKind(n.firstSon.typ, c, g, {})
if n0t in {tyUInt, tyUInt64}:
c.requestCompilerProc(g, "raiseRangeErrorNoArgs")
else:
let raiser =
case loadTypeKind(n.typ, c, g, abstractVarRange)
of tyUInt..tyUInt64, tyChar: "raiseRangeErrorU"
of tyFloat..tyFloat128: "raiseRangeErrorF"
else: "raiseRangeErrorI"
c.requestCompilerProc(g, raiser)
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
@ -71,6 +108,8 @@ proc aliveCode(c: var AliveContext; g: PackedModuleGraph; tree: PackedTree; n: N
discard
of nkVarSection, nkLetSection, nkConstSection:
discard
of nkChckRangeF, nkChckRange64, nkChckRange:
rangeCheckAnalysis(c, g, tree, n)
of nkProcDef, nkConverterDef, nkMethodDef, nkLambda, nkDo, nkFuncDef:
if n.firstSon.kind == nkSym and isNotGeneric(n):
if isExportedToC(c, g, n.firstSon.operand):
@ -85,14 +124,16 @@ 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()
let (modId, opt, ast) = c.stack.pop()
c.thisModule = modId
c.options = opt
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))
thisModule: -1, alive: newSeq[IntSet](g.len),
options: conf.options)
for i in countdown(high(g), 0):
if g[i].status != undefined:
c.thisModule = i

View file

@ -62,6 +62,7 @@ type
position*: int
offset*: int
externalName*: LitId # instead of TLoc
locFlags*: TLocFlags
annex*: PackedLib
when hasFFI:
cname*: LitId

View file

@ -127,3 +127,19 @@ proc replayGenericCacheInformation*(g: ModuleGraph; module: int) =
let sym = loadSymFromId(g.config, g.cache, g.packed, module,
PackedItemId(module: LitId(0), item: it))
methodDef(g, g.idgen, sym)
for it in mitems(g.packed[module].fromDisk.compilerProcs):
let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it[1]))
g.lazyCompilerprocs[g.packed[module].fromDisk.sh.strings[it[0]]] = symId
for it in mitems(g.packed[module].fromDisk.converters):
let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it))
g.ifaces[module].converters.add LazySym(id: symId, sym: nil)
for it in mitems(g.packed[module].fromDisk.trmacros):
let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it))
g.ifaces[module].patterns.add LazySym(id: symId, sym: nil)
for it in mitems(g.packed[module].fromDisk.pureEnums):
let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it))
g.ifaces[module].pureEnums.add LazySym(id: symId, sym: nil)

View file

@ -32,8 +32,8 @@ type
#producedGenerics*: Table[GenericKey, SymId]
exports*: seq[(LitId, int32)]
reexports*: seq[(LitId, PackedItemId)]
compilerProcs*, trmacros*, converters*, pureEnums*: seq[(LitId, int32)]
methods*: seq[int32]
compilerProcs*: seq[(LitId, int32)]
converters*, methods*, trmacros*, pureEnums*: seq[int32]
macroUsages*: seq[(PackedItemId, PackedLineInfo)]
typeInstCache*: seq[(PackedItemId, PackedItemId)]
@ -154,17 +154,14 @@ proc addExported*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
m.exports.add((nameId, s.itemId.item))
proc addConverter*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
let nameId = getOrIncl(m.sh.strings, s.name.s)
m.converters.add((nameId, s.itemId.item))
m.converters.add(s.itemId.item)
proc addTrmacro*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
let nameId = getOrIncl(m.sh.strings, s.name.s)
m.trmacros.add((nameId, s.itemId.item))
m.trmacros.add(s.itemId.item)
proc addPureEnum*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
let nameId = getOrIncl(m.sh.strings, s.name.s)
assert s.kind == skType
m.pureEnums.add((nameId, s.itemId.item))
m.pureEnums.add(s.itemId.item)
proc addMethod*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
m.methods.add s.itemId.item
@ -349,6 +346,7 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId
p.alignment = s.alignment
p.externalName = toLitId(if s.loc.r.isNil: "" else: $s.loc.r, m)
p.locFlags = s.loc.flags
c.addMissing s.typ
p.typ = s.typ.storeType(c, m)
c.addMissing s.owner
@ -453,7 +451,7 @@ proc toPackedNodeTopLevel*(n: PNode, encoder: var PackedEncoder; m: var PackedMo
flush encoder, m
proc loadError(err: RodFileError; filename: AbsoluteFile) =
echo "Error: ", $err, "\nloading file: ", filename.string
echo "Error: ", $err, " loading file: ", filename.string
proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef): RodFileError =
m.sh = Shared()
@ -751,6 +749,7 @@ proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
let externalName = g[si].fromDisk.sh.strings[s.externalName]
if externalName != "":
result.loc.r = rope externalName
result.loc.flags = s.locFlags
proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; s: PackedItemId): PSym =
if s == nilItemId:
@ -819,6 +818,17 @@ proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; t
result = g[si].types[t.item]
assert result.itemId.item > 0
proc newPackage(config: ConfigRef; cache: IdentCache; fileIdx: FileIndex): PSym =
let filename = AbsoluteFile toFullPath(config, fileIdx)
let name = getIdent(cache, splitFile(filename).name)
let info = newLineInfo(fileIdx, 1, 1)
let
pck = getPackageName(config, filename.string)
pck2 = if pck.len > 0: pck else: "unknown"
pack = getIdent(cache, pck2)
result = newSym(skPackage, getIdent(cache, pck2),
ItemId(module: PackageModuleId, item: int32(fileIdx)), nil, info)
proc setupLookupTables(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
fileIdx: FileIndex; m: var LoadedModule) =
m.iface = initTable[PIdent, seq[PackedItemId]]()
@ -836,6 +846,7 @@ proc setupLookupTables(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCa
name: getIdent(cache, splitFile(filename).name),
info: newLineInfo(fileIdx, 1, 1),
position: int(fileIdx))
m.module.owner = newPackage(conf, cache, fileIdx)
proc loadToReplayNodes(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
fileIdx: FileIndex; m: var LoadedModule) =
@ -851,7 +862,7 @@ proc loadToReplayNodes(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCa
m.module.ast.add loadNodes(decoder, g, int(fileIdx), m.fromDisk.toReplay, p)
proc needsRecompile(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
fileIdx: FileIndex): bool =
fileIdx: FileIndex; cachedModules: var seq[FileIndex]): bool =
# Does the file belong to the fileIdx need to be recompiled?
let m = int(fileIdx)
if m >= g.len:
@ -870,11 +881,12 @@ proc needsRecompile(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache
let fid = toFileIndex(dep, g[m].fromDisk, conf)
# Warning: we need to traverse the full graph, so
# do **not use break here**!
if needsRecompile(g, conf, cache, fid):
if needsRecompile(g, conf, cache, fid, cachedModules):
result = true
if not result:
setupLookupTables(g, conf, cache, fileIdx, g[m])
cachedModules.add fileIdx
g[m].status = if result: outdated else: loaded
else:
loadError(err, rod)
@ -887,14 +899,16 @@ proc needsRecompile(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache
result = true
proc moduleFromRodFile*(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
fileIdx: FileIndex): PSym =
fileIdx: FileIndex; cachedModules: var seq[FileIndex]): PSym =
## Returns 'nil' if the module needs to be recompiled.
if needsRecompile(g, conf, cache, fileIdx):
if needsRecompile(g, conf, cache, fileIdx, cachedModules):
result = nil
else:
result = g[int fileIdx].module
assert result != nil
loadToReplayNodes(g, conf, cache, fileIdx, g[int fileIdx])
assert result.position == int(fileIdx)
for m in cachedModules:
loadToReplayNodes(g, conf, cache, m, g[int m])
template setupDecoder() {.dirty.} =
var decoder = PackedDecoder(
@ -919,7 +933,10 @@ proc loadProcBody*(config: ConfigRef, cache: IdentCache;
proc loadTypeFromId*(config: ConfigRef, cache: IdentCache;
g: var PackedModuleGraph; module: int; id: PackedItemId): PType =
result = g[module].types[id.item]
if id.item < g[module].types.len:
result = g[module].types[id.item]
else:
result = nil
if result == nil:
var decoder = PackedDecoder(
lastModule: int32(-1),
@ -931,7 +948,10 @@ proc loadTypeFromId*(config: ConfigRef, cache: IdentCache;
proc loadSymFromId*(config: ConfigRef, cache: IdentCache;
g: var PackedModuleGraph; module: int; id: PackedItemId): PSym =
result = g[module].syms[id.item]
if id.item < g[module].syms.len:
result = g[module].syms[id.item]
else:
result = nil
if result == nil:
var decoder = PackedDecoder(
lastModule: int32(-1),