summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ccgmerge.nim13
-rwxr-xr-xcompiler/ccgutils.nim6
-rwxr-xr-xcompiler/cgen.nim108
-rw-r--r--compiler/cgendata.nim6
-rwxr-xr-xcompiler/commands.nim1
-rwxr-xr-xcompiler/extccomp.nim17
-rwxr-xr-xcompiler/main.nim168
-rwxr-xr-xcompiler/rodread.nim2
-rw-r--r--compiler/service.nim2
9 files changed, 260 insertions, 63 deletions
diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim
index df7e7f68b..cb654cbb5 100644
--- a/compiler/ccgmerge.nim
+++ b/compiler/ccgmerge.nim
@@ -44,9 +44,12 @@ const
     cpsStmts: "NIM_merge_PROC_BODY"
   ]
   NimMergeEndMark = "/*\tNIM_merge_END:*/"
-  
+
+template mergeSectionsEnabled: expr =
+  {optCaasEnabled, optSymbolFiles} * gGlobalOptions != {}
+
 proc genSectionStart*(fs: TCFileSection): PRope =
-  if optSymbolFiles in gGlobalOptions:
+  if mergeSectionsEnabled:
     result = toRope(tnl)
     app(result, "/*\t")
     app(result, CFileSectionNames[fs])
@@ -54,11 +57,11 @@ proc genSectionStart*(fs: TCFileSection): PRope =
     app(result, tnl)
 
 proc genSectionEnd*(fs: TCFileSection): PRope =
-  if optSymbolFiles in gGlobalOptions:
+  if mergeSectionsEnabled:
     result = toRope(NimMergeEndMark & tnl)
 
 proc genSectionStart*(ps: TCProcSection): PRope =
-  if optSymbolFiles in gGlobalOptions:
+  if mergeSectionsEnabled:
     result = toRope(tnl)
     app(result, "/*\t")
     app(result, CProcSectionNames[ps])
@@ -66,7 +69,7 @@ proc genSectionStart*(ps: TCProcSection): PRope =
     app(result, tnl)
 
 proc genSectionEnd*(ps: TCProcSection): PRope =
-  if optSymbolFiles in gGlobalOptions:
+  if mergeSectionsEnabled:
     result = toRope(NimMergeEndMark & tnl)
 
 proc writeTypeCache(a: TIdTable, s: var string) =
diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim
index ad7f22bbc..90a1c5d81 100755
--- a/compiler/ccgutils.nim
+++ b/compiler/ccgutils.nim
@@ -59,6 +59,12 @@ var
 proc initTypeTables() = 
   for i in countup(low(TTypeKind), high(TTypeKind)): InitIdTable(gTypeTable[i])
 
+proc resetCaches* =
+  ## XXX: fix that more properly
+  initTypeTables()
+  for i in low(gCanonicalTypes)..high(gCanonicalTypes):
+    gCanonicalTypes[i] = nil
+
 when false:
   proc echoStats*() =
     for i in countup(low(TTypeKind), high(TTypeKind)): 
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index a92a2eaea..3a8df7bd8 100755
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -37,6 +37,10 @@ proc addForwardedProc(m: BModule, prc: PSym) =
   m.forwardedProcs.add(prc)
   inc(gForwardedProcsCounter)
 
+proc getCgenModule(s: PSym): BModule =
+  result = if s.position >= 0 and s.position < gModules.len: gModules[s.position]
+           else: nil
+
 proc findPendingModule(m: BModule, s: PSym): BModule = 
   var ms = getModule(s)
   result = gModules[ms.position]
@@ -1013,18 +1017,67 @@ proc rawNewModule(module: PSym, filename: string): BModule =
   result.nimTypesName = getTempName()
   result.PreventStackTrace = sfSystemModule in module.flags
 
+proc nullify[T](arr: var T) =
+  for i in low(arr)..high(arr):
+    arr[i] = nil
+
+proc resetModule*(m: var BModule) =
+  # between two compilations in CAAS mode, we can throw
+  # away all the data that was written to disk
+  InitLinkedList(m.headerFiles)
+  m.declaredProtos = initIntSet()
+  initIdTable(m.forwTypeCache)
+  m.initProc = newProc(nil, m)
+  m.initProc.options = gOptions
+  m.preInitProc = newProc(nil, m)
+  initNodeTable(m.dataCache)
+  m.typeStack = @[]
+  m.forwardedProcs = @[]
+  m.typeNodesName = getTempName()
+  m.nimTypesName = getTempName()
+  m.PreventStackTrace = sfSystemModule in m.module.flags
+  nullify m.s
+  m.usesThreadVars = false
+  m.typeNodes = 0
+  m.nimTypes = 0
+  nullify m.extensionLoaders
+  
+  # indicate that this is now cached module
+  # the cache will be invalidated by nullifying gModules
+  m.fromCache = true
+  
+  # we keep only the "merge info" information for the module
+  # and the properties that can't change:
+  # m.filename
+  # m.cfilename
+  # m.isHeaderFile
+  # m.module ?
+  # m.typeCache
+  # m.declaredThings
+  # m.typeInfoMarker
+  # m.labels
+  # m.FrameDeclared
+
+proc resetCgenModules* =
+  for m in cgenModules(): resetModule(m)
+
 proc rawNewModule(module: PSym): BModule =
   result = rawNewModule(module, module.filename)
 
 proc newModule(module: PSym): BModule =
-  result = rawNewModule(module)
-  if gModules.len <= module.position: gModules.setLen(module.position + 1)
-  gModules[module.position] = result
+  result = getCgenModule(module)
+  if result == nil:
+    result = rawNewModule(module)
+    growCache gModules, module.position
+    gModules[module.position] = result
+
+    if (optDeadCodeElim in gGlobalOptions): 
+      if (sfDeadCodeElim in module.flags): 
+        InternalError("added pending module twice: " & module.filename)
+  else:
+    echo "CGEN CACHED MODULE: ", result.filename
+    assert optCaasEnabled in gGlobalOptions
 
-  if (optDeadCodeElim in gGlobalOptions): 
-    if (sfDeadCodeElim in module.flags): 
-      InternalError("added pending module twice: " & module.filename)
-  
 proc myOpen(module: PSym): PPassContext = 
   result = newModule(module)
   if optGenIndex in gGlobalOptions and generatedHeader == nil:
@@ -1056,7 +1109,8 @@ proc writeHeader(m: BModule) =
 proc getCFile(m: BModule): string =
   result = changeFileExt(completeCFilePath(m.cfilename), cExt)
 
-proc myOpenCached(module: PSym, rd: PRodReader): PPassContext = 
+proc myOpenCached(module: PSym, rd: PRodReader): PPassContext =
+  assert optSymbolFiles in gGlobalOptions
   var m = newModule(module)
   readMergeInfo(getCFile(m), m)
   result = m
@@ -1130,8 +1184,28 @@ proc writeModule(m: BModule, pending: bool) =
     # ``system.c`` but then compilation fails due to an error. This means
     # that ``system.o`` is missing, so we need to call the C compiler for it:
     addFileToCompile(cfilenoext)
+  
   addFileToLink(cfilenoext)
 
+proc updateCachedModule(m: BModule) =
+  let cfile = getCFile(m)
+  let cfilenoext = changeFileExt(cfile, "")
+  
+  if mergeRequired(m):
+    echo "MERGE REQUIRED FOR ", m.filename
+    mergeFiles(cfile, m)
+    genInitCode(m)
+    finishTypeDescriptions(m)
+    var code = genModule(m, cfilenoext)
+    writeRope(code, cfile)
+    addFileToCompile(cfilenoext)
+
+  addFileToLink(cfilenoext)
+
+proc cgenCaasUpdate* =
+  for m in cgenModules():
+    if m.fromCache: m.updateCachedModule
+
 proc myClose(b: PPassContext, n: PNode): PNode = 
   result = n
   if b == nil or passes.skipCodegen(n): return 
@@ -1150,17 +1224,13 @@ proc myClose(b: PPassContext, n: PNode): PNode =
     # deps are allowed (and the system module is processed in the wrong
     # order anyway)
     if generatedHeader != nil: finishModule(generatedHeader)
-    while gForwardedProcsCounter > 0: 
-      for i in countup(0, high(gModules)):
-        # some modules (like stdin) may exist only in memory
-        # they won't have a cgen BModule for them and we must
-        # skip them
-        if gModules[i] != nil:
-          finishModule(gModules[i])
-    for i in countup(0, high(gModules)): 
-      # see above
-      if gModules[i] != nil:
-        writeModule(gModules[i], pending=true)
+    while gForwardedProcsCounter > 0:
+      for m in cgenModules():
+        if not m.fromCache:
+          finishModule(m)
+    for m in cgenModules():
+      if not m.fromCache:
+        writeModule(m, pending=true)
     writeMapping(gMapping)
     if generatedHeader != nil: writeHeader(generatedHeader)
 
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index d0cc07097..846acbd2c 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -141,3 +141,9 @@ proc newProc*(prc: PSym, module: BModule): BProc =
   newSeq(result.blocks, 1)
   result.nestedTryStmts = @[]
 
+iterator cgenModules*: var BModule =
+  for i in 0..high(gModules):
+    # some modules (like stdin) may exist only in memory.
+    # they won't have a cgen BModule for them and we must skip them.
+    if gModules[i] != nil: yield gModules[i]
+
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 979da3db8..5043a60ec 100755
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -23,6 +23,7 @@ type
 
 proc ProcessCommand*(switch: string, pass: TCmdLinePass)
 proc processSwitch*(switch, arg: string, pass: TCmdlinePass, info: TLineInfo)
+
 # implementation
 
 const
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index 7b3dc0b3e..11e89fee0 100755
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -379,9 +379,18 @@ proc toObjFile*(filenameWithoutExt: string): string =
   # Object file for compilation
   result = changeFileExt(filenameWithoutExt, cc[ccompiler].objExt)
 
-proc addFileToCompile*(filename: string) = 
+proc addFileToCompile*(filename: string) =
   appendStr(toCompile, filename)
 
+proc resetCompilationLists* =
+  initLinkedList(toCompile)
+  ## XXX: we must associate these with their originating module
+  # when the module is loaded/unloaded it adds/removes its items
+  # That's because we still need to CRC check the external files
+  # Maybe we can do that in checkDep on the other hand?
+  initLinkedList(externalToCompile)
+  initLinkedList(toLink)
+
 proc footprint(filename: string): TCrc32 =
   result = crcFromFile(filename) >< 
       platform.OS[targetOS].name ><
@@ -405,11 +414,11 @@ proc externalFileChanged(filename: string): bool =
       f.writeln($currentCrc)
       close(f)
 
-proc addExternalFileToCompile*(filename: string) = 
+proc addExternalFileToCompile*(filename: string) =
   if optForceFullMake in gGlobalOptions or externalFileChanged(filename):
     appendStr(externalToCompile, filename)
 
-proc addFileToLink*(filename: string) = 
+proc addFileToLink*(filename: string) =
   prependStr(toLink, filename) 
   # BUGFIX: was ``appendStr``
 
@@ -540,7 +549,7 @@ proc CompileCFile(list: TLinkedList, script: var PRope, cmds: var TStringSeq,
       app(script, tnl)
     it = PStrEntry(it.next)
 
-proc CallCCompiler*(projectfile: string) = 
+proc CallCCompiler*(projectfile: string) =
   var 
     linkCmd, buildgui, builddll: string
   if gGlobalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}: 
diff --git a/compiler/main.nim b/compiler/main.nim
index 9cb4c22d9..b5ba04245 100755
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -14,9 +14,9 @@ import
   llstream, strutils, ast, astalgo, lexer, syntaxes, renderer, options, msgs, 
   os, lists, condsyms, rodread, rodwrite, ropes, trees, times,
   wordrecg, sem, semdata, idents, passes, docgen, extccomp,
-  cgen, ecmasgen,
+  cgen, ecmasgen, cgendata,
   platform, nimconf, importer, passaux, depends, evals, types, idgen,
-  tables, docgen2, service, magicsys, parser, crc
+  tables, docgen2, service, magicsys, parser, crc, ccgutils
 
 const
   has_LLVM_Backend = false
@@ -29,11 +29,15 @@ proc MainCommand*()
 # ------------------ module handling -----------------------------------------
 
 type
+  TNeedRecompile = enum Maybe, No, Yes, Probing, Recompiled
+  TCrcStatus = enum crcNotTaken, crcCached, crcHasChanged, crcNotChanged
+
   TModuleInMemory = object
     compiledAt: float
-    crc: int
+    crc: TCrc32
     deps: seq[int32] ## XXX: slurped files are not currently tracked
-    needsRecompile: bool
+    needsRecompile: TNeedRecompile
+    crcStatus: TCrcStatus
 
 var
   gCompiledModules: seq[PSym] = @[]
@@ -53,24 +57,76 @@ template compiledAt(x: PSym): expr =
 template crc(x: PSym): expr =
   gMemCacheData[x.position].crc
 
-template addDep(x: Psym, dep: int32) =
-  gMemCacheData[x.position].deps.add(dep)
-
-proc checkDepMem(fileIdx: int32): bool =
+proc crcChanged(fileIdx: int32): bool =
+  InternalAssert fileIdx >= 0 and fileIdx < gMemCacheData.len
+  
+  template updateStatus =
+    gMemCacheData[fileIdx].crcStatus = if result: crcHasChanged
+                                       else: crcNotChanged
+    # echo "TESTING CRC: ", fileIdx.toFilename, " ", result
+    
+  case gMemCacheData[fileIdx].crcStatus:
+  of crcHasChanged:
+    result = true
+  of crcNotChanged:
+    result = false
+  of crcCached:
+    let newCrc = crcFromFile(fileIdx.toFilename)
+    result = newCrc != gMemCacheData[fileIdx].crc
+    gMemCacheData[fileIdx].crc = newCrc
+    updateStatus()
+  of crcNotTaken:
+    gMemCacheData[fileIdx].crc = crcFromFile(fileIdx.toFilename)
+    result = true
+    updateStatus()
+
+proc doCRC(fileIdx: int32) =
+  if gMemCacheData[fileIdx].crcStatus == crcNotTaken:
+    # echo "FIRST CRC: ", fileIdx.ToFilename
+    gMemCacheData[fileIdx].crc = crcFromFile(fileIdx.toFilename)
+
+proc safeAdd*[T](x: var seq[T], y: T) {.noSideEffect.} =
+  if x == nil: x = @[y]
+  else: x.add(y)
+
+proc safeAdd*(x: var string, y: char) =
+  if x == nil: x = ""
+  x.add(y)
+
+proc safeAdd*(x: var string, y: string) =
+  if x == nil: x = y
+  else: x.add(y)
+
+proc addDep(x: Psym, dep: int32) =
+  growCache gMemCacheData, dep
+  gMemCacheData[x.position].deps.safeAdd(dep)
+
+proc checkDepMem(fileIdx: int32): TNeedRecompile  =
   template markDirty =
-    gMemCacheData[fileIdx].needsRecompile = true
-    return true
+    echo "HARD RESETTING ", fileIdx.toFilename
+    gMemCacheData[fileIdx].needsRecompile = Yes
+    gCompiledModules[fileIdx] = nil
+    cgendata.gModules[fileIdx] = nil
+      
+    return Yes
+
+  if gMemCacheData[fileIdx].needsRecompile != Maybe:
+    return gMemCacheData[fileIdx].needsRecompile
 
   if optForceFullMake in gGlobalOptions or
-     curCaasCmd != lastCaasCmd: markDirty
+     curCaasCmd != lastCaasCmd or
+     crcChanged(fileIdx): markDirty
   
-  let crc = crcFromFile(fileIdx.toFilename)
-  if crc != gMemCacheData[fileIdx].crc: markDirty
-
-  for dep in gMemCacheData[fileIdx].deps:
-    if checkDepMem(dep): markDirty
-
-  return false
+  if gMemCacheData[fileIdx].deps != nil:
+    gMemCacheData[fileIdx].needsRecompile = Probing
+    for dep in gMemCacheData[fileIdx].deps:
+      let d = checkDepMem(dep)
+      if d in { Yes, Recompiled }:
+        echo fileIdx.toFilename, " depends on ", dep.toFilename, " ", d
+        markDirty
+  
+  gMemCacheData[fileIdx].needsRecompile = No
+  return No
 
 proc newModule(fileIdx: int32): PSym =
   # We cannot call ``newSym`` here, because we have to circumvent the ID
@@ -94,10 +150,12 @@ proc newModule(fileIdx: int32): PSym =
   incl(result.flags, sfUsed)
   initStrTable(result.tab)
   StrTableAdd(result.tab, result) # a module knows itself
-  
+
 proc compileModule(fileIdx: int32, flags: TSymFlags): PSym =
   result = getModule(fileIdx)
   if result == nil:
+    growCache gMemCacheData, fileIdx
+    gMemCacheData[fileIdx].needsRecompile = Probing
     result = newModule(fileIdx)
     var rd = handleSymbolFile(result)
     result.flags = result.flags + flags
@@ -109,12 +167,12 @@ proc compileModule(fileIdx: int32, flags: TSymFlags): PSym =
     else:
       result.id = getID()
     processModule(result, nil, rd)
-    gMemCacheData[fileIdx].compiledAt = gLastCmdTime
-    gMemCacheData[fileIdx].needsRecompile = false
+    if optCaasEnabled in gGlobalOptions:
+      gMemCacheData[fileIdx].compiledAt = gLastCmdTime
+      gMemCacheData[fileIdx].needsRecompile = Recompiled
+      doCRC fileIdx
   else:
-    InternalAssert optCaasEnabled in gGlobalOptions
-    if checkDepMem(fileIdx):
-      gCompiledModules[fileIdx] = nil
+    if checkDepMem(fileIdx) == Yes:
       result = CompileModule(fileIdx, flags)
     else:
       result = gCompiledModules[fileIdx]
@@ -124,17 +182,17 @@ proc compileModule(filename: string, flags: TSymFlags): PSym =
 
 proc importModule(s: PSym, fileIdx: int32): PSym =
   # this is called by the semantic checking phase
-  result = getModule(fileIdx)
-  if result == nil: 
-    # compile the module
-    result = compileModule(fileIdx, {})
-    if optCaasEnabled in gGlobalOptions: addDep(s, fileIdx)
-  elif sfSystemModule in result.flags: 
+  result = compileModule(fileIdx, {})
+  if optCaasEnabled in gGlobalOptions: addDep(s, fileIdx)
+  if sfSystemModule in result.flags:
     LocalError(result.info, errAttemptToRedefine, result.Name.s)
 
 proc includeModule(s: PSym, fileIdx: int32): PNode =
   result = syntaxes.parseFile(fileIdx)
-  if optCaasEnabled in gGlobalOptions: addDep(s, fileIdx)
+  if optCaasEnabled in gGlobalOptions:
+    growCache gMemCacheData, fileIdx
+    addDep(s, fileIdx)
+    doCrc(fileIdx)
 
 proc `==^`(a, b: string): bool =
   try:
@@ -191,11 +249,55 @@ proc CommandCompileToC =
   registerPass(cgenPass)
   rodPass()
   #registerPass(cleanupPass())
+  if optCaasEnabled in gGlobalOptions:
+    # echo "BEFORE CHECK DEP"
+    # discard checkDepMem(gProjectMainIdx)
+    # echo "CHECK DEP COMPLETE"
+
   compileProject()
+
+  if optCaasEnabled in gGlobalOptions:
+    cgenCaasUpdate()
+
   if gCmd != cmdRun:
     extccomp.CallCCompiler(changeFileExt(gProjectFull, ""))
-  # caas will keep track only of the compilation commands
-  lastCaasCmd = curCaasCmd
+
+  if optCaasEnabled in gGlobalOptions:
+    # caas will keep track only of the compilation commands
+    lastCaasCmd = curCaasCmd
+    resetCgenModules()
+    for i in 0 .. <gMemCacheData.len:
+      gMemCacheData[i].crcStatus = crcCached
+      gMemCacheData[i].needsRecompile = Maybe
+
+      # XXX: clean these global vars
+      # ccgstmts.gBreakpoints
+      # ccgthreadvars.nimtv
+      # ccgthreadvars.nimtVDeps
+      # ccgthreadvars.nimtvDeclared
+      # cgendata
+      # cgmeth?
+      # condsyms?
+      # depends?
+      # lexer.gLinesCompiled
+      # msgs - error counts
+      # magicsys, when system.nim changes
+      # rodread.rodcompilerProcs
+      # rodread.gTypeTable
+      # rodread.gMods
+      
+      # !! ropes.cache
+      # !! semdata.gGenericsCache
+      # semthreads.computed?
+      #
+      # suggest.usageSym
+      #
+      # XXX: can we run out of IDs?
+      # XXX: detect config reloading (implement as error/require restart)
+      # XXX: options are appended (they will accumulate over time)
+    resetCompilationLists()
+    ccgutils.resetCaches()
+    GC_fullCollect()
 
 when has_LLVM_Backend:
   proc CommandCompileToLLVM =
diff --git a/compiler/rodread.nim b/compiler/rodread.nim
index 96ecf5f25..2d6399438 100755
--- a/compiler/rodread.nim
+++ b/compiler/rodread.nim
@@ -795,7 +795,7 @@ proc GetCRC*(fileIdx: int32): TCrc32 =
   gMods[fileIdx].crc = result
 
 template growCache*(cache, pos) =
-  if cache.len <= fileIdx: cache.setLen(pos+1)
+  if cache.len <= pos: cache.setLen(pos+1)
 
 proc checkDep(fileIdx: int32): TReasonForRecompile =
   assert fileIdx != InvalidFileIDX
diff --git a/compiler/service.nim b/compiler/service.nim
index c31a0eb21..defdbceb7 100644
--- a/compiler/service.nim
+++ b/compiler/service.nim
@@ -12,7 +12,7 @@
 import 
   sockets,
   times, commands, options, msgs, nimconf,
-  extccomp, strutils, os, platform, main, parseopt
+  extccomp, strutils, os, platform, parseopt
 
 # We cache modules and the dependency graph. However, we don't check for
 # file changes but expect the client to tell us about them, otherwise the