summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorcooldome <cdome@bk.ru>2018-12-30 10:28:12 +0000
committerAndreas Rumpf <rumpf_a@web.de>2018-12-30 11:28:12 +0100
commit82c009a2cbc5d07ab9a847f1c58228a20efaf219 (patch)
tree4eb145a07f09df58fd84702b4e22719495c47f22
parentcbbdcb266962df39bbdacb99ccd2a656ce9c3af7 (diff)
downloadNim-82c009a2cbc5d07ab9a847f1c58228a20efaf219.tar.gz
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
-rw-r--r--compiler/ccgmerge.nim1
-rw-r--r--compiler/cgen.nim247
-rw-r--r--compiler/cgendata.nim10
-rw-r--r--lib/system.nim9
-rw-r--r--tests/ccgbugs/tforward_decl_only.nim2
-rw-r--r--tests/manyloc/standalone/barebone.nim6
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..<m.config.m.fileInfos.len:
-    result.addf("dbgRegisterFilename($1);$N",
-      [m.config.m.fileInfos[i].projPath.string.makeCString])
-
 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
     # The use of a volatile function pointer to call Pre/NimMainInner
     # prevents inlining of the NimMainInner function and dependent
     # functions, which might otherwise merge their stack frames.
     PreMainBody =
       "void PreMainInner(void) {$N" &
-      "\tsystemInit000();$N" &
-      "$1" &
       "$2" &
       "$3" &
       "}$N$N" &
       "void PreMain(void) {$N" &
       "\tvoid (*volatile inner)(void);$N" &
-      "\tsystemDatInit000();$N" &
+      "$1" &
       "\tinner = PreMainInner;$N" &
       "$4$5" &
       "\t(*inner)();$N" &
@@ -1217,9 +1212,10 @@ proc genMainProc(m: BModule) =
   else:
     nimMain = PosixNimMain
     otherMain = PosixCMain
-  if m.g.breakpoints != nil: discard cgsym(m, "dbgRegisterBreakpoint")
   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 =
     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 registerModuleToMain(g: BModuleList; m: PSym) =
-  var
-    init = m.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", [datInit])
-  if sfSystemModule notin m.flags:
-    addf(g.mainDatInit, "\t$1();$N", [datInit])
+proc registerModuleToMain(g: BModuleList; m: BModule) =
+  if m.s[cfsInitProc].len > 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