From 82c009a2cbc5d07ab9a847f1c58228a20efaf219 Mon Sep 17 00:00:00 2001 From: cooldome Date: Sun, 30 Dec 2018 10:28:12 +0000 Subject: 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 --- compiler/ccgmerge.nim | 1 + compiler/cgen.nim | 247 ++++++++++++++++++++++------------ compiler/cgendata.nim | 10 +- lib/system.nim | 9 +- tests/ccgbugs/tforward_decl_only.nim | 2 + tests/manyloc/standalone/barebone.nim | 6 +- 6 files changed, 176 insertions(+), 99 deletions(-) diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim index 144a45816..2d68a198e 100644 --- a/compiler/ccgmerge.nim +++ b/compiler/ccgmerge.nim @@ -31,6 +31,7 @@ const cfsData: "NIM_merge_DATA", cfsProcs: "NIM_merge_PROCS", cfsInitProc: "NIM_merge_INIT_PROC", + cfsDatInitProc: "NIM_merge_DATINIT_PROC", cfsTypeInit1: "NIM_merge_TYPE_INIT1", cfsTypeInit2: "NIM_merge_TYPE_INIT2", cfsTypeInit3: "NIM_merge_TYPE_INIT3", diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 457a6e176..3d76be254 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1087,28 +1087,23 @@ proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope = result = getCopyright(conf, cfile) addIntTypes(result, conf) -proc genFilenames(m: BModule): Rope = - discard cgsym(m, "dbgRegisterFilename") - result = nil - for i in 0.. 0: + let init = m.module.getInitName + addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init]) let initCall = "\t$1();$N" % [init] - if sfMainModule in m.flags: + if sfMainModule in m.module.flags: add(g.mainModInit, initCall) else: 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) = - 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] if m.typeNodes > 0: 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 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, m.preInitProc.s(cpsLocals)) + add(prc, m.initProc.s(cpsLocals)) 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, m.preInitProc.s(cpsInit)) + add(prc, m.initProc.s(cpsInit)) add(prc, genSectionEnd(cpsInit, 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)) - addf(prc, "}$N", []) - - add(prc, initGCFrame(m.initProc)) - add(prc, genSectionStart(cpsLocals, m.config)) - add(prc, m.initProc.s(cpsLocals)) - add(prc, genSectionEnd(cpsLocals, m.config)) + if optStackTrace in m.initProc.options and preventStackTrace notin m.flags: + add(prc, deinitFrame(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)) + if m.initProc.gcFrameId > 0: + moduleInitRequired = true + add(prc, deinitGCFrame(m.initProc)) - 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", []) # 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 # not support. So we add it to another special section: ``cfsInitProc`` - add(m.s[cfsInitProc], prc) for i, el in pairs(m.extensionLoaders): if el != nil: let ex = "NIM_EXTERNC N_NIMCALL(void, nimLoadProcs$1)(void) {$2}$N$N" % [(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 = + var moduleIsEmpty = true + result = getFileHeader(m.config, cfile) result.add(genMergeInfo(m)) + if m.config.cppCustomNamespace.len > 0: + result.add openNamespaceNim(m.config.cppCustomNamespace) + generateThreadLocalStorage(m) generateHeaders(m) - for i in countup(cfsHeaders, cfsProcs): - add(result, genSectionStart(i, m.config)) - add(result, m.s[i]) - add(result, genSectionEnd(i, m.config)) - if m.config.cppCustomNamespace.len > 0 and i == cfsHeaders: - result.add openNamespaceNim(m.config.cppCustomNamespace) - add(result, m.s[cfsInitProc]) - if m.config.cppCustomNamespace.len > 0: result.add closeNamespaceNim() + add(result, genSectionStart(cfsHeaders, m.config)) + add(result, m.s[cfsHeaders]) + add(result, genSectionEnd(cfsHeaders, m.config)) + + for i in countup(cfsForwardTypes, cfsProcs): + if m.s[i].len > 0: + moduleIsEmpty = false + 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 = result = newProc(nil, m) @@ -1522,27 +1579,30 @@ proc writeModule(m: BModule, pending: bool) = finishTypeDescriptions(m) if sfMainModule in m.module.flags: # generate main file: + genMainProc(m) add(m.s[cfsProcHeaders], m.g.mainModProcs) generateThreadVarsSize(m) var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {}) var code = genModule(m, cf) - when hasTinyCBackend: - if conf.cmd == cmdRun: - tccgen.compileCCode($code) - return - - if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached} - addFileToCompile(m.config, cf) + if code != nil: + when hasTinyCBackend: + if conf.cmd == cmdRun: + tccgen.compileCCode($code) + return + + if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached} + addFileToCompile(m.config, cf) 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: {}) mergeFiles(cfile, m) genInitCode(m) finishTypeDescriptions(m) var code = genModule(m, cf) - if not writeRope(code, cfile): - rawMessage(m.config, errCannotOpenFile, cfile.string) - addFileToCompile(m.config, cf) + if code != nil: + if not writeRope(code, cfile): + rawMessage(m.config, errCannotOpenFile, cfile.string) + addFileToCompile(m.config, cf) else: # Consider: first compilation compiles ``system.nim`` and produces # ``system.c`` but then compilation fails due to an error. This means @@ -1560,13 +1620,16 @@ proc updateCachedModule(m: BModule) = mergeFiles(cfile, m) genInitCode(m) finishTypeDescriptions(m) - var code = genModule(m, cf) - if not writeRope(code, cfile): - rawMessage(m.config, errCannotOpenFile, cfile.string) + if code != nil: + if not writeRope(code, cfile): + rawMessage(m.config, errCannotOpenFile, cfile.string) + addFileToCompile(m.config, cf) else: + if sfMainModule notin m.module.flags: + genMainProc(m) cf.flags = {CfileFlag.Cached} - addFileToCompile(m.config, cf) + addFileToCompile(m.config, cf) proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = result = n @@ -1579,15 +1642,25 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = if n != nil: m.initProc.options = initProcOptions(m) genStmts(m.initProc, n) - # cached modules need to registered too: - registerModuleToMain(m.g, m.module) 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: incl m.flags, objHasKidsValid let disp = generateMethodDispatchers(graph) for x in disp: genProcAux(m, x.sym) - genMainProc(m) + + m.g.modules_closed.add m proc genForwardedProcs(g: BModuleList) = # Forward declared proc:s lack bodies when first encountered, so they're given diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 28e36364e..50b484e31 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -32,6 +32,7 @@ type cfsData, # section for C constant data cfsProcs, # section for C procs that are not inline cfsInitProc, # section for the C init proc + cfsDatInitProc, # section for the C datInit proc cfsTypeInit1, # section 1 for declarations of type information cfsTypeInit2, # section 2 for init of type information cfsTypeInit3, # section 3 for init of type information @@ -112,6 +113,7 @@ type mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope mapping*: Rope # the generated mapping file (if requested) 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 generatedHeader*: BModule breakPointId*: int @@ -191,8 +193,6 @@ proc newModuleList*(g: ModuleGraph): BModuleList = graph: g, nimtvDeclared: initIntSet()) iterator cgenModules*(g: BModuleList): BModule = - for m in g.modules: - # ultimately, we are iterating over the file ids here. - # some "files" won't have an associated cgen module (like stdin) - # and we must skip over them. - if m != nil: yield m + for m in g.modules_closed: + # iterate modules in the order they were closed + yield m diff --git a/lib/system.nim b/lib/system.nim index bcd5fe05a..b9f86f549 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1479,12 +1479,9 @@ const # for string literals, it allows for some optimizations. {.push profiler: off.} -when defined(nimKnowsNimvm): - let nimvm* {.magic: "Nimvm".}: bool = false - ## may be used only in "when" expression. - ## It is true in Nim VM context and false otherwise -else: - const nimvm*: bool = false +let nimvm* {.magic: "Nimvm", compileTime.}: bool = false + ## may be used only in "when" expression. + ## It is true in Nim VM context and false otherwise {.pop.} proc compileOption*(option: string): bool {. diff --git a/tests/ccgbugs/tforward_decl_only.nim b/tests/ccgbugs/tforward_decl_only.nim index 2a867bc3b..74fbae303 100644 --- a/tests/ccgbugs/tforward_decl_only.nim +++ b/tests/ccgbugs/tforward_decl_only.nim @@ -1,5 +1,7 @@ discard """ ccodecheck: "\\i !@('struct tyObject_MyRefObject'[0-z]+' {')" +ccodecheck: "\\i !@('mymoduleInit')" +ccodecheck: "\\i @('mymoduleDatInit')" output: "hello" """ diff --git a/tests/manyloc/standalone/barebone.nim b/tests/manyloc/standalone/barebone.nim index 9d75f8f2e..0b38616b2 100644 --- a/tests/manyloc/standalone/barebone.nim +++ b/tests/manyloc/standalone/barebone.nim @@ -1,4 +1,8 @@ - +discard """ +ccodecheck: "\\i !@('systemInit')" +ccodecheck: "\\i !@('systemDatInit')" +output: "hello" +""" # bug #2041: Macros need to be available for os:standalone! import macros -- cgit 1.4.1-2-gfad0