summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorViktor Kirilov <vik.kirilov@gmail.com>2019-03-23 15:48:47 +0200
committerAndreas Rumpf <rumpf_a@web.de>2019-03-23 14:48:47 +0100
commitf8146dfd845e8d9a8f19ae59ef9c9350cd9db453 (patch)
tree44c10936f119d2090a403d62f97c39ef185af1bc
parent0b2a3f6f7f7dfc40e08d287f41f7ce7c2e51fd9c (diff)
downloadNim-f8146dfd845e8d9a8f19ae59ef9c9350cd9db453.tar.gz
improvements on the hot code reloading support (#10892)
* calling the "_actual" versions of functions when defined within the same module - slowdown for the snappy compression is now down from x6 to x4-x5 when HCR is ON
* dynamically linking to the runtime for VS when HCR is on - binaries are smaller
* compilerProcs are also called using the _actual direct version within the module they are defined (system)!
* updated comments & goals
* handling VS-compatible compilers on Windows in a cleaner way
* now the .dll/.so files end up in the nimcache even when --nimcache isn't explicitly stated
-rw-r--r--compiler/ccgcalls.nim10
-rw-r--r--compiler/cgen.nim2
-rw-r--r--compiler/commands.nim4
-rw-r--r--compiler/extccomp.nim17
-rw-r--r--lib/nimhcr.nim21
-rw-r--r--lib/system/timers.nim2
-rw-r--r--tests/dll/nimhcr_integration.nim2
7 files changed, 42 insertions, 16 deletions
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index cefa89289..dd126161d 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -174,6 +174,11 @@ template genParamLoop(params) {.dirty.} =
     if params != nil: add(params, ~", ")
     add(params, genArgNoParam(p, ri.sons[i]))
 
+proc addActualPrefixForHCR(res: var Rope, module: PSym, sym: PSym) =
+  if sym.flags * {sfImportc, sfNonReloadable} == {} and
+      (sym.typ.callConv == ccInline or sym.owner.id == module.id):
+    res = res & "_actual".rope
+
 proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
   var op: TLoc
   # this is a hotspot in the compiler
@@ -186,7 +191,10 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
   var length = sonsLen(ri)
   for i in countup(1, length - 1):
     genParamLoop(params)
-  fixupCall(p, le, ri, d, rdLoc(op), params)
+  var callee = rdLoc(op)
+  if p.hcrOn and ri.sons[0].kind == nkSym:
+    callee.addActualPrefixForHCR(p.module.module, ri.sons[0].sym)
+  fixupCall(p, le, ri, d, callee, params)
 
 proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
 
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 1436880d9..25921e9f3 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -706,6 +706,8 @@ proc cgsym(m: BModule, name: string): Rope =
     # we're picky here for the system module too:
     rawMessage(m.config, errGenerated, "system module needs: " & name)
   result = sym.loc.r
+  if m.hcrOn and sym != nil and sym.kind in skProc..skIterator:
+    result.addActualPrefixForHCR(m.module, sym)
 
 proc generateHeaders(m: BModule) =
   add(m.s[cfsHeaders], "\L#include \"nimbase.h\"\L")
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 3a18e69ed..e029ed9f5 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -494,6 +494,10 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     if conf.hcrOn:
       defineSymbol(conf.symbols, "hotcodereloading")
       defineSymbol(conf.symbols, "useNimRtl")
+      # hardcoded linking with dynamic runtime for MSVC for smaller binaries
+      # should do the same for all compilers (wherever applicable)
+      if isVSCompatible(conf):
+        extccomp.addCompileOptionCmd(conf, "/MD")
     else:
       undefSymbol(conf.symbols, "hotcodereloading")
       undefSymbol(conf.symbols, "useNimRtl")
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index 5a1d33ade..ab42f4f52 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -223,7 +223,6 @@ compiler bcc:
     props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard,
             hasAttribute})
 
-
 # Digital Mars C Compiler
 compiler dmc:
   result = (
@@ -376,6 +375,11 @@ proc nameToCC*(name: string): TSystemCC =
       return i
   result = ccNone
 
+proc isVSCompatible*(conf: ConfigRef): bool =
+  return conf.cCompiler == ccVcc or 
+          conf.cCompiler == ccClangCl or
+          (conf.cCompiler == ccIcl and conf.target.hostOS in osDos..osWindows)
+
 proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
   # use ``cpu.os.cc`` for cross compilation, unless ``--compileOnly`` is given
   # for niminst support
@@ -734,8 +738,9 @@ proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile,
     # way of being able to debug and rebuild the program at the same time. This
     # is accomplished using the /PDB:<filename> flag (there also exists the
     # /PDBALTPATH:<filename> flag). The only downside is that the .pdb files are
-    # atleast 5-10mb big and will quickly accumulate. There is a hacky solution:
-    # we could try to delete all .pdb files with a pattern and swallow exceptions.
+    # atleast 300kb big (when linking statically to the runtime - or else 5mb+) 
+    # and will quickly accumulate. There is a hacky solution: we could try to
+    # delete all .pdb files with a pattern and swallow exceptions.
     #
     # links about .pdb files and hot code reloading:
     # https://ourmachinery.com/post/dll-hot-reloading-in-theory-and-practice/
@@ -747,7 +752,7 @@ proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile,
     # and a bit about the .pdb format in case that is ever needed:
     # https://github.com/crosire/blink
     # http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles
-    if conf.hcrOn and conf.cCompiler == ccVcc:
+    if conf.hcrOn and isVSCompatible(conf):
       let t = now()
       let pdb = output.string & "." & format(t, "MMMM-yyyy-HH-mm-") & $t.nanosecond & ".pdb"
       result.add " /link /PDB:" & pdb
@@ -838,7 +843,7 @@ proc hcrLinkTargetName(conf: ConfigRef, objFile: string, isMain = false): Absolu
   let basename = splitFile(objFile).name
   let targetName = if isMain: basename & ".exe"
                    else: platform.OS[conf.target.targetOS].dllFrmt % basename
-  result = conf.nimcacheDir / RelativeFile(targetName)
+  result = conf.getNimcacheDir / RelativeFile(targetName)
 
 proc callCCompiler*(conf: ConfigRef) =
   var
@@ -883,7 +888,7 @@ proc callCCompiler*(conf: ConfigRef) =
         add(cmds, getLinkCmd(conf, linkTarget, objfiles & " " & quoteShell(objFile), buildDll))
         # try to remove all .pdb files for the current binary so they don't accumulate endlessly in the nimcache
         # for more info check the comment inside of getLinkCmd() where the /PDB:<filename> MSVC flag is used
-        if conf.cCompiler == ccVcc:
+        if isVSCompatible(conf):
           for pdb in walkFiles(objFile & ".*.pdb"):
             discard tryRemoveFile(pdb)
       # execute link commands in parallel - output will be a bit different
diff --git a/lib/nimhcr.nim b/lib/nimhcr.nim
index 7a2152f96..37331b825 100644
--- a/lib/nimhcr.nim
+++ b/lib/nimhcr.nim
@@ -105,13 +105,12 @@
 #   - the handlers for a reloaded module are always removed when reloading and then
 #     registered when the top-level scope is executed (thanks to `executeOnReload`)
 #
-# TODO - after first merge in upstream Nim:
+# TODO next:
 #
-# - profile
-#   - build speed with and without hot code reloading - difference should be small
-#   - runtime degradation of HCR-enabled code - important!!!
+# - implement the before/after handlers and hasModuleChanged for the javascript target
 # - ARM support for the trampolines
 # - investigate:
+#   - soon the system module might be importing other modules - the init order...?
 #   - rethink the closure iterators
 #     - ability to keep old versions of dynamic libraries alive
 #       - because of async server code
@@ -132,7 +131,7 @@
 #     - currently building with useNimRtl is problematic - lots of problems...
 #     - how to supply the nimrtl/nimhcr shared objects to all test binaries...?
 #     - think about building to C++ instead of only to C - added type safety
-#   - run tests through valgrind and the sanitizers! of HUGE importance!
+#   - run tests through valgrind and the sanitizers!
 #
 # TODO - nice to have cool stuff:
 #
@@ -148,7 +147,6 @@
 # - pragma annotations for files - to be excluded from dll shenanigans
 #   - for such file-global pragmas look at codeReordering or injectStmt
 #   - how would the initialization order be kept? messy...
-#   - per function exclude pragmas would be TOO messy and hard...
 # - C code calling stable exportc interface of nim code (for bindings)
 #   - generate proxy functions with the stable names
 #     - in a non-reloadable part (the main binary) that call the function pointers
@@ -160,9 +158,18 @@
 #   - issue an error
 #     - or let the user handle this by transferring the state properly
 #       - perhaps in the before/afterCodeReload handlers
-# - optimization: calls to procs within a module (+inlined) to use the _actual versions
 # - implement executeOnReload for global vars too - not just statements (and document!)
 # - cleanup at shutdown - freeing all globals
+# - fallback mechanism if the program crashes (the program should detect crashes
+#   by itself using SEH/signals on Windows/Unix) - should be able to revert to
+#   previous versions of the .dlls by calling some function from HCR
+# - improve runtime performance - possibilities
+#   - implement a way for multiple .nim files to be bundled into the same dll
+#     and have all calls within that domain to use the "_actual" versions of
+#     procs so there are no indirections (or the ability to just bundle everything
+#     except for a few unreloadable modules into a single mega reloadable dll)
+#   - try to load the .dlls at specific addresses of memory (close to each other)
+#     allocated with execution flags - check this: https://github.com/fancycode/MemoryModule
 #
 # TODO - unimportant:
 #
diff --git a/lib/system/timers.nim b/lib/system/timers.nim
index 4cd2fe840..b0882ffa1 100644
--- a/lib/system/timers.nim
+++ b/lib/system/timers.nim
@@ -8,7 +8,7 @@
 #
 
 ## Timer support for the realtime GC. Based on
-## `<https://github.com/jckarter/clay/blob/master/compiler/src/hirestimer.cpp>`_
+## `<https://github.com/jckarter/clay/blob/master/compiler/hirestimer.cpp>`_
 
 type
   Ticks = distinct int64
diff --git a/tests/dll/nimhcr_integration.nim b/tests/dll/nimhcr_integration.nim
index daabe918f..40ba90f72 100644
--- a/tests/dll/nimhcr_integration.nim
+++ b/tests/dll/nimhcr_integration.nim
@@ -72,7 +72,7 @@ done
 ## executing:
 ##   <this_file>.exe nim c --hotCodeReloading:on --nimCache:<folder> <this_file>.nim
 
-import os, osproc, times, strutils, hotcodereloading
+import os, osproc, strutils, hotcodereloading
 
 import nimhcr_0 # getInt() - the only thing we continually call from the main module