Dead code elimination for entire modules and their init procs if empty (#10032)

* fixes #9798
* Change order of write modules
* Move datInit calls ahead of initStackBottom
This commit is contained in:
cooldome 2018-12-30 10:28:12 +00:00 • committed by Andreas Rumpf
commit 82c009a2cb
6 changed files with 176 additions and 99 deletions

View file

@ -31,6 +31,7 @@ const
cfsData: "NIM_merge_DATA", cfsData: "NIM_merge_DATA",
cfsProcs: "NIM_merge_PROCS", cfsProcs: "NIM_merge_PROCS",
cfsInitProc: "NIM_merge_INIT_PROC", cfsInitProc: "NIM_merge_INIT_PROC",
cfsDatInitProc: "NIM_merge_DATINIT_PROC",
cfsTypeInit1: "NIM_merge_TYPE_INIT1", cfsTypeInit1: "NIM_merge_TYPE_INIT1",
cfsTypeInit2: "NIM_merge_TYPE_INIT2", cfsTypeInit2: "NIM_merge_TYPE_INIT2",
cfsTypeInit3: "NIM_merge_TYPE_INIT3", cfsTypeInit3: "NIM_merge_TYPE_INIT3",

View file

@ -1087,28 +1087,23 @@ proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope =
result = getCopyright(conf, cfile) result = getCopyright(conf, cfile)
addIntTypes(result, conf) addIntTypes(result, conf)
proc genFilenames(m: BModule): Rope =
discard cgsym(m, "dbgRegisterFilename")
result = nil
for i in 0..<m.config.m.fileInfos.len:
result.addf("dbgRegisterFilename($1);$N",
[m.config.m.fileInfos[i].projPath.string.makeCString])
proc genMainProc(m: BModule) = proc genMainProc(m: BModule) =
## this function is called in cgenWriteModules after all modules are closed,
## it means raising dependency on the symbols is too late as it will not propogate
## into other modules, only simple rope manipulations are allowed
const const
# The use of a volatile function pointer to call Pre/NimMainInner # The use of a volatile function pointer to call Pre/NimMainInner
# prevents inlining of the NimMainInner function and dependent # prevents inlining of the NimMainInner function and dependent
# functions, which might otherwise merge their stack frames. # functions, which might otherwise merge their stack frames.
PreMainBody = PreMainBody =
"void PreMainInner(void) {$N" & "void PreMainInner(void) {$N" &
"\tsystemInit000();$N" &
"$1" &
"$2" & "$2" &
"$3" & "$3" &
"}$N$N" & "}$N$N" &
"void PreMain(void) {$N" & "void PreMain(void) {$N" &
"\tvoid (*volatile inner)(void);$N" & "\tvoid (*volatile inner)(void);$N" &
"\tsystemDatInit000();$N" & "$1" &
"\tinner = PreMainInner;$N" & "\tinner = PreMainInner;$N" &
"$4$5" & "$4$5" &
"\t(*inner)();$N" & "\t(*inner)();$N" &
@ -1217,9 +1212,10 @@ proc genMainProc(m: BModule) =
else: else:
nimMain = PosixNimMain nimMain = PosixNimMain
otherMain = PosixCMain otherMain = PosixCMain
if m.g.breakpoints != nil: discard cgsym(m, "dbgRegisterBreakpoint")
if optEndb in m.config.options: if optEndb in m.config.options:
m.g.breakpoints.add(m.genFilenames) for i in 0..<m.config.m.fileInfos.len:
m.g.breakpoints.addf("dbgRegisterFilename($1);$N",
[m.config.m.fileInfos[i].projPath.string.makeCString])
let initStackBottomCall = let initStackBottomCall =
if m.config.target.targetOS == osStandalone or m.config.selectedGC == gcNone: "".rope if m.config.target.targetOS == osStandalone or m.config.selectedGC == gcNone: "".rope
@ -1260,22 +1256,50 @@ proc getInitName(m: PSym): Rope =
proc getDatInitName(m: PSym): Rope = getSomeInitName(m, "DatInit000") proc getDatInitName(m: PSym): Rope = getSomeInitName(m, "DatInit000")
proc registerModuleToMain(g: BModuleList; m: PSym) = proc registerModuleToMain(g: BModuleList; m: BModule) =
var if m.s[cfsInitProc].len > 0:
init = m.getInitName let init = m.module.getInitName
datInit = m.getDatInitName addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init])
addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init])
addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit])
if sfSystemModule notin m.flags:
addf(g.mainDatInit, "\t$1();$N", [datInit])
let initCall = "\t$1();$N" % [init] let initCall = "\t$1();$N" % [init]
if sfMainModule in m.flags: if sfMainModule in m.module.flags:
add(g.mainModInit, initCall) add(g.mainModInit, initCall)
else: else:
add(g.otherModsInit, initCall) add(g.otherModsInit, initCall)
if m.s[cfsDatInitProc].len > 0:
let datInit = m.module.getDatInitName
addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit])
addf(g.mainDatInit, "\t$1();$N", [datInit])
proc genDatInitCode(m: BModule) =
## this function is called in cgenWriteModules after all modules are closed,
## it means raising dependency on the symbols is too late as it will not propogate
## into other modules, only simple rope manipulations are allowed
var moduleDatInitRequired = false
var prc = "N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N" %
[getDatInitName(m.module)]
for i in cfsTypeInit1..cfsDynLibInit:
if m.s[i].len != 0:
moduleDatInitRequired = true
add(prc, genSectionStart(i, m.config))
add(prc, m.s[i])
add(prc, genSectionEnd(i, m.config))
addf(prc, "}$N$N", [])
if moduleDatInitRequired:
add(m.s[cfsDatInitProc], prc)
proc genInitCode(m: BModule) = proc genInitCode(m: BModule) =
var initname = getInitName(m.module) ## this function is called in cgenWriteModules after all modules are closed,
## it means raising dependency on the symbols is too late as it will not propogate
## into other modules, only simple rope manipulations are allowed
var moduleInitRequired = false
let initname = getInitName(m.module)
var prc = "N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N" % [initname] var prc = "N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N" % [initname]
if m.typeNodes > 0: if m.typeNodes > 0:
appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n", appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n",
@ -1290,82 +1314,115 @@ proc genInitCode(m: BModule) =
# Keep a bogus frame in case the code needs one # Keep a bogus frame in case the code needs one
add(prc, ~"\tTFrame FR_; FR_.len = 0;$N") add(prc, ~"\tTFrame FR_; FR_.len = 0;$N")
if m.preInitProc.s(cpsLocals).len > 0:
moduleInitRequired = true
add(prc, genSectionStart(cpsLocals, m.config))
add(prc, m.preInitProc.s(cpsLocals))
add(prc, genSectionEnd(cpsLocals, m.config))
if m.preInitProc.s(cpsInit).len > 0:
moduleInitRequired = true
add(prc, genSectionStart(cpsInit, m.config))
add(prc, m.preInitProc.s(cpsInit))
add(prc, genSectionEnd(cpsInit, m.config))
if m.preInitProc.s(cpsStmts).len > 0:
moduleInitRequired = true
add(prc, genSectionStart(cpsStmts, m.config))
add(prc, m.preInitProc.s(cpsStmts))
add(prc, genSectionEnd(cpsStmts, m.config))
addf(prc, "}$N", [])
if m.initProc.gcFrameId > 0:
moduleInitRequired = true
add(prc, initGCFrame(m.initProc))
if m.initProc.s(cpsLocals).len > 0:
moduleInitRequired = true
add(prc, genSectionStart(cpsLocals, m.config)) add(prc, genSectionStart(cpsLocals, m.config))
add(prc, m.preInitProc.s(cpsLocals)) add(prc, m.initProc.s(cpsLocals))
add(prc, genSectionEnd(cpsLocals, m.config)) add(prc, genSectionEnd(cpsLocals, m.config))
if m.initProc.s(cpsInit).len > 0 or m.initProc.s(cpsStmts).len > 0:
moduleInitRequired = true
if optStackTrace in m.initProc.options and frameDeclared notin m.flags:
# BUT: the generated init code might depend on a current frame, so
# declare it nevertheless:
incl m.flags, frameDeclared
if preventStackTrace notin m.flags:
var procname = makeCString(m.module.name.s)
add(prc, initFrame(m.initProc, procname, quotedFilename(m.config, m.module.info)))
else:
add(prc, ~"\tTFrame FR_; FR_.len = 0;$N")
add(prc, genSectionStart(cpsInit, m.config)) add(prc, genSectionStart(cpsInit, m.config))
add(prc, m.preInitProc.s(cpsInit)) add(prc, m.initProc.s(cpsInit))
add(prc, genSectionEnd(cpsInit, m.config)) add(prc, genSectionEnd(cpsInit, m.config))
add(prc, genSectionStart(cpsStmts, m.config)) add(prc, genSectionStart(cpsStmts, m.config))
add(prc, m.preInitProc.s(cpsStmts)) add(prc, m.initProc.s(cpsStmts))
add(prc, genSectionEnd(cpsStmts, m.config)) add(prc, genSectionEnd(cpsStmts, m.config))
addf(prc, "}$N", [])
add(prc, initGCFrame(m.initProc)) if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
add(prc, deinitFrame(m.initProc))
add(prc, genSectionStart(cpsLocals, m.config)) if m.initProc.gcFrameId > 0:
add(prc, m.initProc.s(cpsLocals)) moduleInitRequired = true
add(prc, genSectionEnd(cpsLocals, m.config)) add(prc, deinitGCFrame(m.initProc))
if optStackTrace in m.initProc.options and frameDeclared notin m.flags:
# BUT: the generated init code might depend on a current frame, so
# declare it nevertheless:
incl m.flags, frameDeclared
if preventStackTrace notin m.flags:
var procname = makeCString(m.module.name.s)
add(prc, initFrame(m.initProc, procname, quotedFilename(m.config, m.module.info)))
else:
add(prc, ~"\tTFrame FR_; FR_.len = 0;$N")
add(prc, genSectionStart(cpsInit, m.config))
add(prc, m.initProc.s(cpsInit))
add(prc, genSectionEnd(cpsInit, m.config))
add(prc, genSectionStart(cpsStmts, m.config))
add(prc, m.initProc.s(cpsStmts))
add(prc, genSectionEnd(cpsStmts, m.config))
if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
add(prc, deinitFrame(m.initProc))
add(prc, deinitGCFrame(m.initProc))
addf(prc, "}$N$N", [])
prc.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N",
[getDatInitName(m.module)])
for i in cfsTypeInit1..cfsDynLibInit:
add(prc, genSectionStart(i, m.config))
add(prc, m.s[i])
add(prc, genSectionEnd(i, m.config))
addf(prc, "}$N$N", []) addf(prc, "}$N$N", [])
# we cannot simply add the init proc to ``m.s[cfsProcs]`` anymore because # we cannot simply add the init proc to ``m.s[cfsProcs]`` anymore because
# that would lead to a *nesting* of merge sections which the merger does # that would lead to a *nesting* of merge sections which the merger does
# not support. So we add it to another special section: ``cfsInitProc`` # not support. So we add it to another special section: ``cfsInitProc``
add(m.s[cfsInitProc], prc)
for i, el in pairs(m.extensionLoaders): for i, el in pairs(m.extensionLoaders):
if el != nil: if el != nil:
let ex = "NIM_EXTERNC N_NIMCALL(void, nimLoadProcs$1)(void) {$2}$N$N" % let ex = "NIM_EXTERNC N_NIMCALL(void, nimLoadProcs$1)(void) {$2}$N$N" %
[(i.ord - '0'.ord).rope, el] [(i.ord - '0'.ord).rope, el]
add(m.s[cfsInitProc], ex) moduleInitRequired = true
add(prc, ex)
if moduleInitRequired or sfMainModule in m.module.flags:
add(m.s[cfsInitProc], prc)
genDatInitCode(m)
registerModuleToMain(m.g, m)
proc genModule(m: BModule, cfile: Cfile): Rope = proc genModule(m: BModule, cfile: Cfile): Rope =
var moduleIsEmpty = true
result = getFileHeader(m.config, cfile) result = getFileHeader(m.config, cfile)
result.add(genMergeInfo(m)) result.add(genMergeInfo(m))
if m.config.cppCustomNamespace.len > 0:
result.add openNamespaceNim(m.config.cppCustomNamespace)
generateThreadLocalStorage(m) generateThreadLocalStorage(m)
generateHeaders(m) generateHeaders(m)
for i in countup(cfsHeaders, cfsProcs): add(result, genSectionStart(cfsHeaders, m.config))
add(result, genSectionStart(i, m.config)) add(result, m.s[cfsHeaders])
add(result, m.s[i]) add(result, genSectionEnd(cfsHeaders, m.config))
add(result, genSectionEnd(i, m.config))
if m.config.cppCustomNamespace.len > 0 and i == cfsHeaders: for i in countup(cfsForwardTypes, cfsProcs):
result.add openNamespaceNim(m.config.cppCustomNamespace) if m.s[i].len > 0:
add(result, m.s[cfsInitProc]) moduleIsEmpty = false
if m.config.cppCustomNamespace.len > 0: result.add closeNamespaceNim() add(result, genSectionStart(i, m.config))
add(result, m.s[i])
add(result, genSectionEnd(i, m.config))
if m.s[cfsInitProc].len > 0:
moduleIsEmpty = false
add(result, m.s[cfsInitProc])
if m.s[cfsDatInitProc].len > 0:
moduleIsEmpty = false
add(result, m.s[cfsDatInitProc])
if m.config.cppCustomNamespace.len > 0:
result.add closeNamespaceNim()
if moduleIsEmpty:
result = nil
proc newPreInitProc(m: BModule): BProc = proc newPreInitProc(m: BModule): BProc =
result = newProc(nil, m) result = newProc(nil, m)
@ -1522,27 +1579,30 @@ proc writeModule(m: BModule, pending: bool) =
finishTypeDescriptions(m) finishTypeDescriptions(m)
if sfMainModule in m.module.flags: if sfMainModule in m.module.flags:
# generate main file: # generate main file:
genMainProc(m)
add(m.s[cfsProcHeaders], m.g.mainModProcs) add(m.s[cfsProcHeaders], m.g.mainModProcs)
generateThreadVarsSize(m) generateThreadVarsSize(m)
var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {}) var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
var code = genModule(m, cf) var code = genModule(m, cf)
when hasTinyCBackend: if code != nil:
if conf.cmd == cmdRun: when hasTinyCBackend:
tccgen.compileCCode($code) if conf.cmd == cmdRun:
return tccgen.compileCCode($code)
return
if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached} if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached}
addFileToCompile(m.config, cf) addFileToCompile(m.config, cf)
elif pending and mergeRequired(m) and sfMainModule notin m.module.flags: elif pending and mergeRequired(m) and sfMainModule notin m.module.flags:
let cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {}) let cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
mergeFiles(cfile, m) mergeFiles(cfile, m)
genInitCode(m) genInitCode(m)
finishTypeDescriptions(m) finishTypeDescriptions(m)
var code = genModule(m, cf) var code = genModule(m, cf)
if not writeRope(code, cfile): if code != nil:
rawMessage(m.config, errCannotOpenFile, cfile.string) if not writeRope(code, cfile):
addFileToCompile(m.config, cf) rawMessage(m.config, errCannotOpenFile, cfile.string)
addFileToCompile(m.config, cf)
else: else:
# Consider: first compilation compiles ``system.nim`` and produces # Consider: first compilation compiles ``system.nim`` and produces
# ``system.c`` but then compilation fails due to an error. This means # ``system.c`` but then compilation fails due to an error. This means
@ -1560,13 +1620,16 @@ proc updateCachedModule(m: BModule) =
mergeFiles(cfile, m) mergeFiles(cfile, m)
genInitCode(m) genInitCode(m)
finishTypeDescriptions(m) finishTypeDescriptions(m)
var code = genModule(m, cf) var code = genModule(m, cf)
if not writeRope(code, cfile): if code != nil:
rawMessage(m.config, errCannotOpenFile, cfile.string) if not writeRope(code, cfile):
rawMessage(m.config, errCannotOpenFile, cfile.string)
addFileToCompile(m.config, cf)
else: else:
if sfMainModule notin m.module.flags:
genMainProc(m)
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 myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
result = n result = n
@ -1579,15 +1642,25 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
if n != nil: if n != nil:
m.initProc.options = initProcOptions(m) m.initProc.options = initProcOptions(m)
genStmts(m.initProc, n) genStmts(m.initProc, n)
# cached modules need to registered too:
registerModuleToMain(m.g, m.module)
if sfMainModule in m.module.flags: if sfMainModule in m.module.flags:
# raise dependencies on behalf of genMainProc
if m.config.target.targetOS != osStandalone and m.config.selectedGC != gcNone:
discard cgsym(m, "initStackBottomWith")
if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
discard cgsym(m, "initThreadVarsEmulation")
if m.g.breakpoints != nil:
discard cgsym(m, "dbgRegisterBreakpoint")
if optEndb in m.config.options:
discard cgsym(m, "dbgRegisterFilename")
if m.g.forwardedProcs.len == 0: if m.g.forwardedProcs.len == 0:
incl m.flags, objHasKidsValid incl m.flags, objHasKidsValid
let disp = generateMethodDispatchers(graph) let disp = generateMethodDispatchers(graph)
for x in disp: genProcAux(m, x.sym) for x in disp: genProcAux(m, x.sym)
genMainProc(m)
m.g.modules_closed.add m
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

View file

@ -32,6 +32,7 @@ type
cfsData, # section for C constant data cfsData, # section for C constant data
cfsProcs, # section for C procs that are not inline cfsProcs, # section for C procs that are not inline
cfsInitProc, # section for the C init proc cfsInitProc, # section for the C init proc
cfsDatInitProc, # section for the C datInit proc
cfsTypeInit1, # section 1 for declarations of type information cfsTypeInit1, # section 1 for declarations of type information
cfsTypeInit2, # section 2 for init of type information cfsTypeInit2, # section 2 for init of type information
cfsTypeInit3, # section 3 for init of type information cfsTypeInit3, # section 3 for init of type information
@ -112,6 +113,7 @@ type
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope
mapping*: Rope # the generated mapping file (if requested) mapping*: Rope # the generated mapping file (if requested)
modules*: seq[BModule] # list of all compiled modules modules*: seq[BModule] # list of all compiled modules
modules_closed*: seq[BModule] # list of the same compiled modules, but in the order they were closed
forwardedProcs*: seq[PSym] # proc:s that did not yet have a body forwardedProcs*: seq[PSym] # proc:s that did not yet have a body
generatedHeader*: BModule generatedHeader*: BModule
breakPointId*: int breakPointId*: int
@ -191,8 +193,6 @@ proc newModuleList*(g: ModuleGraph): BModuleList =
graph: g, nimtvDeclared: initIntSet()) graph: g, nimtvDeclared: initIntSet())
iterator cgenModules*(g: BModuleList): BModule = iterator cgenModules*(g: BModuleList): BModule =
for m in g.modules: for m in g.modules_closed:
# ultimately, we are iterating over the file ids here. # iterate modules in the order they were closed
# some "files" won't have an associated cgen module (like stdin) yield m
# and we must skip over them.
if m != nil: yield m

View file

@ -1479,12 +1479,9 @@ const
# for string literals, it allows for some optimizations. # for string literals, it allows for some optimizations.
{.push profiler: off.} {.push profiler: off.}
when defined(nimKnowsNimvm): let nimvm* {.magic: "Nimvm", compileTime.}: bool = false
let nimvm* {.magic: "Nimvm".}: bool = false ## may be used only in "when" expression.
## may be used only in "when" expression. ## It is true in Nim VM context and false otherwise
## It is true in Nim VM context and false otherwise
else:
const nimvm*: bool = false
{.pop.} {.pop.}
proc compileOption*(option: string): bool {. proc compileOption*(option: string): bool {.

View file

@ -1,5 +1,7 @@
discard """ discard """
ccodecheck: "\\i !@('struct tyObject_MyRefObject'[0-z]+' {')" ccodecheck: "\\i !@('struct tyObject_MyRefObject'[0-z]+' {')"
ccodecheck: "\\i !@('mymoduleInit')"
ccodecheck: "\\i @('mymoduleDatInit')"
output: "hello" output: "hello"
""" """

View file

@ -1,4 +1,8 @@
discard """
ccodecheck: "\\i !@('systemInit')"
ccodecheck: "\\i !@('systemDatInit')"
output: "hello"
"""
# bug #2041: Macros need to be available for os:standalone! # bug #2041: Macros need to be available for os:standalone!
import macros import macros