diff options
author | Araq <rumpf_a@web.de> | 2018-02-21 00:48:23 +0100 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2018-02-21 00:48:23 +0100 |
commit | 046ed4ed22095d9381d7317beaf215cf67ea4996 (patch) | |
tree | cb8d3a8f9736e1e9d86d6436c719372bb3be47ac | |
parent | 1d1ba4481fb72350f0ee9ea17fa23a04c425629a (diff) | |
download | Nim-046ed4ed22095d9381d7317beaf215cf67ea4996.tar.gz |
symbol files: implemented accurate module dependency tracking
-rw-r--r-- | compiler/modulegraphs.nim | 5 | ||||
-rw-r--r-- | compiler/modules.nim | 2 | ||||
-rw-r--r-- | compiler/msgs.nim | 9 | ||||
-rw-r--r-- | compiler/passes.nim | 2 | ||||
-rw-r--r-- | compiler/rod.nim | 4 | ||||
-rw-r--r-- | compiler/rodimpl.nim | 103 | ||||
-rw-r--r-- | compiler/rodwrite.nim | 2 |
7 files changed, 105 insertions, 22 deletions
diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index c081a099a..2c59a9097 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -25,7 +25,7 @@ ## - Its dependent module stays the same. ## -import ast, intsets, tables, options +import ast, intsets, tables, options, rod type ModuleGraph* = ref object @@ -81,6 +81,8 @@ proc getModule*(g: ModuleGraph; fileIdx: int32): PSym = proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b proc addDep*(g: ModuleGraph; m: PSym, dep: int32) = + assert m.position == m.info.fileIndex + addModuleDep(m.info.fileIndex, dep, isIncludeFile = false) if suggestMode: deps.incl m.position.dependsOn(dep) # we compute the transitive closure later when quering the graph lazily. @@ -88,6 +90,7 @@ proc addDep*(g: ModuleGraph; m: PSym, dep: int32) = #invalidTransitiveClosure = true proc addIncludeDep*(g: ModuleGraph; module, includeFile: int32) = + addModuleDep(module, includeFile, isIncludeFile = true) discard hasKeyOrPut(inclToMod, includeFile, module) proc parentModule*(g: ModuleGraph; fileIdx: int32): int32 = diff --git a/compiler/modules.nim b/compiler/modules.nim index ede10a0f4..a3be5a518 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -177,7 +177,7 @@ proc compileModule*(graph: ModuleGraph; fileIdx: int32; cache: IdentCache, flags return else: discard - result.id = getModuleId(toFullPath(fileIdx)) + result.id = getModuleId(fileIdx, toFullPath(fileIdx)) discard processModule(graph, result, if sfMainModule in flags and gProjectIsStdin: stdin.llStreamOpen else: nil, rd, cache) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 6439d47ee..ac4242e67 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -491,6 +491,7 @@ type dirtyfile: string # the file that is actually read into memory # and parsed; usually 'nil' but is used # for 'nimsuggest' + hash*: string # the checksum of the file TLineInfo* = object # This is designed to be as small as possible, # because it is used @@ -719,6 +720,14 @@ proc setDirtyFile*(fileIdx: int32; filename: string) = assert fileIdx >= 0 fileInfos[fileIdx].dirtyFile = filename +proc setHash*(fileIdx: int32; hash: string) = + assert fileIdx >= 0 + shallowCopy(fileInfos[fileIdx].hash, hash) + +proc getHash*(fileIdx: int32): string = + assert fileIdx >= 0 + shallowCopy(result, fileInfos[fileIdx].hash) + proc toFullPathConsiderDirty*(fileIdx: int32): string = if fileIdx < 0: result = "???" diff --git a/compiler/passes.nim b/compiler/passes.nim index 1b4cd6fec..f079100ea 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -194,6 +194,8 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream, while doContinue: let n = loadNode(module, stmtIndex) if n == nil or graph.stopCompile(): break + #if n.kind == nkImportStmt: + # echo "yes and it's ", n inc stmtIndex var m = n for i in 0..<gPassesLen: diff --git a/compiler/rod.nim b/compiler/rod.nim index 360466b8f..bb9beff54 100644 --- a/compiler/rod.nim +++ b/compiler/rod.nim @@ -16,7 +16,9 @@ when not defined(nimSymbolfiles): template storeNode*(module: PSym; n: PNode) = discard template loadNode*(module: PSym; index: var int): PNode = PNode(nil) - template getModuleId*(fullpath: string): int = getID() + template getModuleId*(fileIdx: int32; fullpath: string): int = getID() + + template addModuleDep*(module, fileIdx: int32; isIncludeFile: bool) = discard else: include rodimpl diff --git a/compiler/rodimpl.nim b/compiler/rodimpl.nim index a4074e2c3..767929aa5 100644 --- a/compiler/rodimpl.nim +++ b/compiler/rodimpl.nim @@ -12,9 +12,40 @@ import strutils, os, intsets, tables, ropes, db_sqlite, msgs, options, types, renderer, rodutils, std / sha1, idents, astalgo, magicsys +## Todo: +## - Implement the 'import' replay logic so that the codegen runs over +## dependent modules. +## - Test multi methods. +## - Implement the limited VM support based on sets. +## - Depencency computation should use signature hashes in order to +## avoid recompiling dependent modules. + var db: DbConn -proc getModuleId*(fullpath: string): int = +proc hashFileCached(fileIdx: int32; fullpath: string): string = + result = msgs.getHash(fileIdx) + if result.len == 0: + result = $secureHashFile(fullpath) + msgs.setHash(fileIdx, result) + +proc needsRecompile(fileIdx: int32; fullpath: string; cycleCheck: var IntSet): bool = + let root = db.getRow(sql"select id, fullhash from filenames where fullpath = ?", + fullpath) + if root[0].len == 0: return true + if root[1] != hashFileCached(fileIdx, fullpath): + return true + # cycle detection: assume "not changed" is correct. + if cycleCheck.containsOrIncl(fileIdx): + return false + # check dependencies (recursively): + for row in db.fastRows(sql"select fullpath from filenames where id in (select dependency from deps where module = ?)", + root[0]): + let dep = row[0] + if needsRecompile(dep.fileInfoIdx, dep, cycleCheck): + return true + return false + +proc getModuleId*(fileIdx: int32; fullpath: string): int = if gSymbolFiles != v2Sf: return getID() let module = db.getRow( sql"select id, fullHash from modules where fullpath = ?", fullpath) @@ -28,13 +59,15 @@ proc getModuleId*(fullpath: string): int = # not changed, so use the cached AST (even if it might be wrong # due to its dependencies): doAssert(result != 0) - result = -result - else: - db.exec(sql"update modules set fullHash = ? where id = ?", currentFullhash, module[0]) - db.exec(sql"delete from types where module = ?", module[0]) - db.exec(sql"delete from syms where module = ?", module[0]) - db.exec(sql"delete from toplevelstmts where module = ?", module[0]) - db.exec(sql"delete from statics where module = ?", module[0]) + var cycleCheck = initIntSet() + if not needsRecompile(fileIdx, fullpath, cycleCheck): + return -result + db.exec(sql"update modules set fullHash = ? where id = ?", currentFullhash, module[0]) + db.exec(sql"delete from deps where module = ?", module[0]) + db.exec(sql"delete from types where module = ?", module[0]) + db.exec(sql"delete from syms where module = ?", module[0]) + db.exec(sql"delete from toplevelstmts where module = ?", module[0]) + db.exec(sql"delete from statics where module = ?", module[0]) type TRodWriter = object @@ -74,15 +107,23 @@ proc pushSym(w: PRodWriter, s: PSym) = if not containsOrIncl(w.smarks, s.id): w.sstack.add(s) -proc toDbFileId(fullpath: string): int = - let id = db.getValue(sql"select id from filenames where fullpath = ?", +proc toDbFileId(fileIdx: int32): int = + if fileIdx == -1: return -1 + let fullpath = fileIdx.toFullPath + let row = db.getRow(sql"select id, fullhash from filenames where fullpath = ?", fullpath) + let id = row[0] + let fullhash = hashFileCached(fileIdx, fullpath) if id.len == 0: - result = int db.insertID(sql"insert into filenames(fullpath) values (?)", fullpath) + result = int db.insertID(sql"insert into filenames(fullpath, fullhash) values (?, ?)", + fullpath, fullhash) else: + if row[1] != fullhash: + db.exec(sql"update filenames set fullhash = ? where fullpath = ?", fullhash, fullpath) result = parseInt(id) proc fromDbFileId(dbId: int): int32 = + if dbId == -1: return -1 let fullpath = db.getValue(sql"select fullpath from filenames where id = ?", dbId) doAssert fullpath.len > 0, "cannot find file name for DB ID " & $dbId result = fileInfoIdx(fullpath) @@ -104,7 +145,7 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, result.add(',') encodeVInt(n.info.line, result) result.add(',') - encodeVInt(toDbFileId(n.info.toFullPath), result) + encodeVInt(toDbFileId(n.info.fileIndex), result) elif fInfo.line != n.info.line: result.add('?') encodeVInt(n.info.col, result) @@ -282,7 +323,7 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) = result.add(',') if s.info.line != -1'i16: encodeVInt(s.info.line, result) result.add(',') - encodeVInt(toDbFileId(s.info.toFullPath), result) + encodeVInt(toDbFileId(s.info.fileIndex), result) if s.owner != nil: result.add('*') encodeVInt(s.owner.id, result) @@ -764,16 +805,29 @@ proc loadModuleSymTab(r; module: PSym) = magicsys.systemModule = module proc loadNode*(module: PSym; index: var int): PNode = + assert gSymbolFiles == v2Sf if index == 0: loadModuleSymTab(gr, module) index = parseInt db.getValue( sql"select min(id) from toplevelstmts where module = ?", abs module.id) var b = BlobReader(pos: 0) - b.s = db.getValue(sql"select data from toplevelstmts where id = ?", index) - if b.s.len == 0: return nil # end marker + b.s = db.getValue(sql"select data from toplevelstmts where id = ? and module = ?", + index, abs module.id) + if b.s.len == 0: + db.exec(sql"insert into controlblock(idgen) values (?)", gFrontEndId) + return nil # end marker gr.module = module result = decodeNode(gr, b, module.info) +proc addModuleDep*(module, fileIdx: int32; isIncludeFile: bool) = + if gSymbolFiles != v2Sf: return + + let a = toDbFileId(module) + let b = toDbFileId(fileIdx) + + db.exec(sql"insert into deps(module, dependency, isIncludeFile) values (?, ?, ?)", + a, b, ord(isIncludeFile)) + # --------------- Database model --------------------------------------------- proc createDb() = @@ -786,7 +840,8 @@ proc createDb() = db.exec(sql""" create table if not exists filenames( id integer primary key, - fullpath varchar(8000) not null + fullpath varchar(8000) not null, + fullHash varchar(256) not null ); """) db.exec sql"create index if not exists FilenameIx on filenames(fullpath);" @@ -803,6 +858,17 @@ proc createDb() = db.exec(sql"""create unique index if not exists SymNameIx on modules(fullpath);""") db.exec(sql""" + create table if not exists deps( + id integer primary key, + module integer not null, + dependency integer not null, + isIncludeFile integer not null, + foreign key (module) references filenames(id), + foreign key (dependency) references filenames(id) + );""") + db.exec(sql"""create index if not exists DepsIx on deps(module);""") + + db.exec(sql""" create table if not exists types( id integer primary key, nimid integer not null, @@ -865,5 +931,6 @@ proc setupModuleCache* = db.exec(sql"pragma journal_mode=off") db.exec(sql"pragma SYNCHRONOUS=off") db.exec(sql"pragma LOCKING_MODE=exclusive") - idgen.setId(parseInt db.getValue( - sql"select max(idgen) from controlblock")) + let lastId = db.getValue(sql"select max(idgen) from controlblock") + if lastId.len > 0: + idgen.setId(parseInt lastId) diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim index 59a709295..96deb1d5a 100644 --- a/compiler/rodwrite.nim +++ b/compiler/rodwrite.nim @@ -642,7 +642,7 @@ proc process(c: PPassContext, n: PNode): PNode = proc myOpen(g: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = if module.id < 0: internalError("rodwrite: module ID not set") - var w = newRodWriter(module.fileIdx.getHash, module, cache) + var w = newRodWriter(rodread.getHash module.fileIdx, module, cache) rawAddInterfaceSym(w, module) result = w |