first steps to C file merge operation for incremental compilation

This commit is contained in:
Araq 2011-10-21 01:06:24 +02:00
commit a6f90d4cdd
8 changed files with 441 additions and 118 deletions

253
compiler/ccgmerge.nim Normal file
View file

@ -0,0 +1,253 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements the merge operation of 2 different C files. This
## is needed for incremental compilation.
import
ast, astalgo, ropes, options, strutils, lexbase, msgs, cgendata, rodutils,
intsets, platform, llstream
# Careful! Section marks need to contain a tabulator so that they cannot
# be part of C string literals.
const
CFileSectionNames: array[TCFileSection, string] = [
cfsMergeInfo: "",
cfsHeaders: "NIM_merge_HEADERS",
cfsForwardTypes: "NIM_merge_FORWARD_TYPES",
cfsTypes: "NIM_merge_TYPES",
cfsSeqTypes: "NIM_merge_SEQ_TYPES",
cfsFieldInfo: "NIM_merge_FIELD_INFO",
cfsTypeInfo: "NIM_merge_TYPE_INFO",
cfsProcHeaders: "NIM_merge_PROC_HEADERS",
cfsData: "NIM_merge_DATA",
cfsVars: "NIM_merge_VARS",
cfsProcs: "NIM_merge_PROCS",
cfsInitProc: "NIM_merge_INIT_PROC",
cfsTypeInit1: "NIM_merge_TYPE_INIT1",
cfsTypeInit2: "NIM_merge_TYPE_INIT2",
cfsTypeInit3: "NIM_merge_TYPE_INIT3",
cfsDebugInit: "NIM_merge_DEBUG_INIT",
cfsDynLibInit: "NIM_merge_DYNLIB_INIT",
cfsDynLibDeinit: "NIM_merge_DYNLIB_DEINIT",
]
CProcSectionNames: array[TCProcSection, string] = [
cpsLocals: "NIM_merge_PROC_LOCALS",
cpsInit: "NIM_merge_PROC_INIT",
cpsStmts: "NIM_merge_PROC_BODY"
]
proc genSectionStart*(fs: TCFileSection): PRope =
if optSymbolFiles in gGlobalOptions:
result = toRope(tnl)
app(result, "/*\t")
app(result, CFileSectionNames[fs])
app(result, "*/")
app(result, tnl)
proc genSectionEnd*(fs: TCFileSection): PRope =
if optSymbolFiles in gGlobalOptions:
result = toRope("/*\tNIM_merge_END*/" & tnl)
proc genSectionStart*(ps: TCProcSection): PRope =
if optSymbolFiles in gGlobalOptions:
result = toRope(tnl)
app(result, "/*\t")
app(result, CProcSectionNames[ps])
app(result, "*/")
app(result, tnl)
proc genSectionEnd*(ps: TCProcSection): PRope =
if optSymbolFiles in gGlobalOptions:
result = toRope("/*\tNIM_merge_END*/" & tnl)
proc writeTypeCache(a: TIdTable, s: var string) =
var i = 0
for id, value in pairs(a):
if i == 10:
i = 0
s.add(tnl)
else:
s.add(' ')
encodeVInt(id, s)
s.add(':')
encodeStr(PRope(value).ropeToStr, s)
inc i
s.add('}')
proc writeIntSet(a: TIntSet, s: var string) =
var i = 0
for x in items(a):
if i == 10:
i = 0
s.add(tnl)
else:
s.add(' ')
encodeVInt(x, s)
inc i
s.add('}')
proc genMergeInfo*(m: BModule): PRope =
if optSymbolFiles notin gGlobalOptions: return nil
var s = "/*\tNIM_merge_INFO:"
s.add(tnl)
s.add("typeCache:{")
writeTypeCache(m.typeCache, s)
s.add("declared:{")
writeIntSet(m.declaredThings, s)
s.add("typeInfo:{")
writeIntSet(m.typeInfoMarker, s)
s.add("labels:")
encodeVInt(m.labels, s)
s.add(tnl)
s.add("*/")
result = s.toRope
template `^`(pos: expr): expr = L.buf[pos]
proc skipWhite(L: var TBaseLexer) =
var pos = L.bufpos
while true:
case ^pos
of CR: pos = lexbase.HandleCR(L, pos)
of LF: pos = lexbase.HandleLF(L, pos)
of ' ': inc pos
else: break
L.bufpos = pos
proc skipUntilCmd(L: var TBaseLexer) =
var pos = L.bufpos
while true:
case ^pos
of CR: pos = lexbase.HandleCR(L, pos)
of LF: pos = lexbase.HandleLF(L, pos)
of '\0': break
of '/':
if ^(pos+1) == '*' and ^(pos+2) == '\t':
inc pos, 3
break
inc pos
else: inc pos
L.bufpos = pos
proc readVerbatimSection(L: var TBaseLexer): PRope =
const section = "/*\tNIM_merge_END*/"
var pos = L.bufpos
var buf = L.buf
result = newMutableRope(30_000)
while true:
case buf[pos]
of CR:
pos = lexbase.HandleCR(L, pos)
buf = L.buf
result.data.add(tnl)
of LF:
pos = lexbase.HandleLF(L, pos)
buf = L.buf
result.data.add(tnl)
of '\0': break
else: nil
if buf[pos] == section[0]:
var s = 0
while buf[pos+1] == section[s+1]:
inc s
inc pos
if section[s] != '\0':
# reset:
dec pos, s
else:
break
result.data.add(buf[pos])
inc pos
L.bufpos = pos
result.length = result.data.len
proc readKey(L: var TBaseLexer): string =
var pos = L.bufpos
var buf = L.buf
while buf[pos] in IdentChars:
result.add(buf[pos])
inc pos
if buf[pos] != ':': internalError("ccgmerge: ':' expected")
L.bufpos = pos + 1 # skip ':'
proc NewFakeType(id: int): PType =
new(result)
result.id = id
proc readTypeCache(L: var TBaseLexer, result: var TIdTable) =
if ^L.bufpos != '{': internalError("ccgmerge: '{' expected")
inc L.bufpos
while ^L.bufpos != '}':
skipWhite(L)
var key = decodeVInt(L.buf, L.bufpos)
if ^L.bufpos != ':': internalError("ccgmerge: ':' expected")
inc L.bufpos
var value = decodeStr(L.buf, L.bufpos)
# XXX little hack: we create a "fake" type object with the correct Id
# better would be to adapt the data structure to not even store the
# object as key, but only the Id
IdTablePut(result, newFakeType(key), value.toRope)
inc L.bufpos
proc readIntSet(L: var TBaseLexer, result: var TIntSet) =
if ^L.bufpos != '{': internalError("ccgmerge: '{' expected")
inc L.bufpos
while ^L.bufpos != '}':
skipWhite(L)
var key = decodeVInt(L.buf, L.bufpos)
result.incl(key)
inc L.bufpos
proc processMergeInfo(L: var TBaseLexer, m: BModule) =
while true:
skipWhite(L)
if ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
inc(L.bufpos, 2)
break
var k = readKey(L)
case k
of "typeCache": readTypeCache(L, m.typeCache)
of "declared": readIntSet(L, m.declaredThings)
of "typeInfo": readIntSet(L, m.typeInfoMarker)
of "labels": m.labels = decodeVInt(L.buf, L.bufpos)
else: InternalError("ccgmerge: unkown key: " & k)
proc readMergeInfo*(cfilename: string, m: BModule) =
## reads the merge information into `m`.
var s = LLStreamOpen(cfilename, fmRead)
if s == nil: return
var L: TBaseLexer
openBaseLexer(L, s)
while true:
skipUntilCmd(L)
if ^L.bufpos == '\0': break
var k = readKey(L)
if k == "NIM_merge_INFO":
processMergeInfo(L, m)
elif ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
inc(L.bufpos, 2)
# read back into section
skipWhite(L)
var verbatim = readVerbatimSection(L)
skipWhite(L)
var sectionA = CFileSectionNames.find(k)
if sectionA > 0 and sectionA <= high(TCFileSection).int:
m.s[TCFileSection(sectionA)] = verbatim
else:
var sectionB = CProcSectionNames.find(k)
if sectionB >= 0 and sectionB <= high(TCProcSection).int:
m.initProc.s[TCProcSection(sectionB)] = verbatim
else:
InternalError("ccgmerge: unknown section: " & k)
else:
InternalError("ccgmerge: */ expected")
closeBaseLexer(L)