first steps to C file merge operation for incremental compilation
This commit is contained in:
parent
7ebaf44897
commit
a6f90d4cdd
8 changed files with 441 additions and 118 deletions
|
|
@ -721,6 +721,11 @@ proc IdTableGet(t: TIdTable, key: int): PObject =
|
||||||
if index >= 0: result = t.data[index].val
|
if index >= 0: result = t.data[index].val
|
||||||
else: result = nil
|
else: result = nil
|
||||||
|
|
||||||
|
iterator pairs*(t: TIdTable): tuple[key: int, value: PObject] =
|
||||||
|
for i in 0..high(t.data):
|
||||||
|
if t.data[i].key != nil:
|
||||||
|
yield (t.data[i].key.id, t.data[i].val)
|
||||||
|
|
||||||
proc IdTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: PObject) =
|
proc IdTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: PObject) =
|
||||||
var h: THash
|
var h: THash
|
||||||
h = key.id and high(data)
|
h = key.id and high(data)
|
||||||
|
|
|
||||||
253
compiler/ccgmerge.nim
Normal file
253
compiler/ccgmerge.nim
Normal 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)
|
||||||
|
|
||||||
|
|
@ -15,7 +15,7 @@ import
|
||||||
options, intsets,
|
options, intsets,
|
||||||
nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os,
|
nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os,
|
||||||
times, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
|
times, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
|
||||||
rodutils, renderer, idgen
|
rodutils, renderer, idgen, cgendata, ccgmerge
|
||||||
|
|
||||||
when options.hasTinyCBackend:
|
when options.hasTinyCBackend:
|
||||||
import tccgen
|
import tccgen
|
||||||
|
|
@ -23,96 +23,6 @@ when options.hasTinyCBackend:
|
||||||
proc cgenPass*(): TPass
|
proc cgenPass*(): TPass
|
||||||
# implementation
|
# implementation
|
||||||
|
|
||||||
type
|
|
||||||
TLabel = PRope # for the C generator a label is just a rope
|
|
||||||
TCFileSection = enum # the sections a generated C file consists of
|
|
||||||
cfsHeaders, # section for C include file headers
|
|
||||||
cfsForwardTypes, # section for C forward typedefs
|
|
||||||
cfsTypes, # section for C typedefs
|
|
||||||
cfsSeqTypes, # section for sequence types only
|
|
||||||
# this is needed for strange type generation
|
|
||||||
# reasons
|
|
||||||
cfsFieldInfo, # section for field information
|
|
||||||
cfsTypeInfo, # section for type information
|
|
||||||
cfsProcHeaders, # section for C procs prototypes
|
|
||||||
cfsData, # section for C constant data
|
|
||||||
cfsVars, # section for C variable declarations
|
|
||||||
cfsProcs, # section for C procs that are not inline
|
|
||||||
cfsTypeInit1, # section 1 for declarations of type information
|
|
||||||
cfsTypeInit2, # section 2 for init of type information
|
|
||||||
cfsTypeInit3, # section 3 for init of type information
|
|
||||||
cfsDebugInit, # section for init of debug information
|
|
||||||
cfsDynLibInit, # section for init of dynamic library binding
|
|
||||||
cfsDynLibDeinit # section for deinitialization of dynamic
|
|
||||||
# libraries
|
|
||||||
TCTypeKind = enum # describes the type kind of a C type
|
|
||||||
ctVoid, ctChar, ctBool, ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64,
|
|
||||||
ctInt, ctInt8, ctInt16, ctInt32, ctInt64, ctFloat, ctFloat32, ctFloat64,
|
|
||||||
ctFloat128, ctArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc, ctCString
|
|
||||||
TCFileSections = array[TCFileSection, PRope] # represents a generated C file
|
|
||||||
TCProcSection = enum # the sections a generated C proc consists of
|
|
||||||
cpsLocals, # section of local variables for C proc
|
|
||||||
cpsInit, # section for init of variables for C proc
|
|
||||||
cpsStmts # section of local statements for C proc
|
|
||||||
TCProcSections = array[TCProcSection, PRope] # represents a generated C proc
|
|
||||||
BModule = ref TCGen
|
|
||||||
BProc = ref TCProc
|
|
||||||
TBlock{.final.} = object
|
|
||||||
id*: int # the ID of the label; positive means that it
|
|
||||||
# has been used (i.e. the label should be emitted)
|
|
||||||
nestedTryStmts*: int # how many try statements is it nested into
|
|
||||||
|
|
||||||
TCProc{.final.} = object # represents C proc that is currently generated
|
|
||||||
s: TCProcSections # the procs sections; short name for readability
|
|
||||||
prc: PSym # the Nimrod proc that this C proc belongs to
|
|
||||||
BeforeRetNeeded: bool # true iff 'BeforeRet' label for proc is needed
|
|
||||||
ThreadVarAccessed: bool # true if the proc already accessed some threadvar
|
|
||||||
nestedTryStmts: seq[PNode] # in how many nested try statements we are
|
|
||||||
# (the vars must be volatile then)
|
|
||||||
labels: Natural # for generating unique labels in the C proc
|
|
||||||
blocks: seq[TBlock] # nested blocks
|
|
||||||
options: TOptions # options that should be used for code
|
|
||||||
# generation; this is the same as prc.options
|
|
||||||
# unless prc == nil
|
|
||||||
frameLen: int # current length of frame descriptor
|
|
||||||
sendClosure: PType # closure record type that we pass
|
|
||||||
receiveClosure: PType # closure record type that we get
|
|
||||||
module: BModule # used to prevent excessive parameter passing
|
|
||||||
withinLoop: int # > 0 if we are within a loop
|
|
||||||
|
|
||||||
TTypeSeq = seq[PType]
|
|
||||||
TCGen = object of TPassContext # represents a C source file
|
|
||||||
module*: PSym
|
|
||||||
filename*: string
|
|
||||||
s*: TCFileSections # sections of the C file
|
|
||||||
PreventStackTrace: bool # true if stack traces need to be prevented
|
|
||||||
usesThreadVars: bool # true if the module uses a thread var
|
|
||||||
cfilename*: string # filename of the module (including path,
|
|
||||||
# without extension)
|
|
||||||
typeCache*: TIdTable # cache the generated types
|
|
||||||
forwTypeCache*: TIdTable # cache for forward declarations of types
|
|
||||||
declaredThings*: TIntSet # things we have declared in this .c file
|
|
||||||
declaredProtos*: TIntSet # prototypes we have declared in this .c file
|
|
||||||
headerFiles*: TLinkedList # needed headers to include
|
|
||||||
typeInfoMarker*: TIntSet # needed for generating type information
|
|
||||||
initProc*: BProc # code for init procedure
|
|
||||||
typeStack*: TTypeSeq # used for type generation
|
|
||||||
dataCache*: TNodeTable
|
|
||||||
forwardedProcs*: TSymSeq # keep forwarded procs here
|
|
||||||
typeNodes*, nimTypes*: int # used for type info generation
|
|
||||||
typeNodesName*, nimTypesName*: PRope # used for type info generation
|
|
||||||
labels*: natural # for generating unique module-scope names
|
|
||||||
|
|
||||||
var
|
|
||||||
mainModProcs, mainModInit: PRope # parts of the main module
|
|
||||||
gMapping: PRope # the generated mapping file (if requested)
|
|
||||||
gProcProfile: Natural # proc profile counter
|
|
||||||
gGeneratedSyms: TIntSet # set of ID's of generated symbols
|
|
||||||
gPendingModules: seq[BModule] = @[] # list of modules that are not
|
|
||||||
# finished with code generation
|
|
||||||
gForwardedProcsCounter: int = 0
|
|
||||||
gNimDat: BModule # generated global data
|
|
||||||
|
|
||||||
proc ropeff(cformat, llvmformat: string, args: openarray[PRope]): PRope =
|
proc ropeff(cformat, llvmformat: string, args: openarray[PRope]): PRope =
|
||||||
if gCmd == cmdCompileToLLVM: result = ropef(llvmformat, args)
|
if gCmd == cmdCompileToLLVM: result = ropef(llvmformat, args)
|
||||||
else: result = ropef(cformat, args)
|
else: result = ropef(cformat, args)
|
||||||
|
|
@ -157,15 +67,6 @@ proc fillLoc(a: var TLoc, k: TLocKind, typ: PType, r: PRope, s: TStorageLoc) =
|
||||||
a.s = s
|
a.s = s
|
||||||
if a.r == nil: a.r = r
|
if a.r == nil: a.r = r
|
||||||
|
|
||||||
proc newProc(prc: PSym, module: BModule): BProc =
|
|
||||||
new(result)
|
|
||||||
result.prc = prc
|
|
||||||
result.module = module
|
|
||||||
if prc != nil: result.options = prc.options
|
|
||||||
else: result.options = gOptions
|
|
||||||
result.blocks = @[]
|
|
||||||
result.nestedTryStmts = @[]
|
|
||||||
|
|
||||||
proc isSimpleConst(typ: PType): bool =
|
proc isSimpleConst(typ: PType): bool =
|
||||||
result = not (skipTypes(typ, abstractVar).kind in
|
result = not (skipTypes(typ, abstractVar).kind in
|
||||||
{tyTuple, tyObject, tyArray, tyArrayConstr, tySet, tySequence})
|
{tyTuple, tyObject, tyArray, tyArrayConstr, tySet, tySequence})
|
||||||
|
|
@ -864,31 +765,53 @@ proc genInitCode(m: BModule) =
|
||||||
# BUT: the generated init code might depend on a current frame, so
|
# BUT: the generated init code might depend on a current frame, so
|
||||||
# declare it nevertheless:
|
# declare it nevertheless:
|
||||||
getFrameDecl(m.initProc)
|
getFrameDecl(m.initProc)
|
||||||
if optStackTrace in m.initProc.options and not m.PreventStackTrace:
|
|
||||||
|
app(prc, genSectionStart(cpsLocals))
|
||||||
app(prc, m.initProc.s[cpsLocals])
|
app(prc, m.initProc.s[cpsLocals])
|
||||||
|
app(prc, genSectionEnd(cpsLocals))
|
||||||
|
|
||||||
|
app(prc, genSectionStart(cfsTypeInit1))
|
||||||
app(prc, m.s[cfsTypeInit1])
|
app(prc, m.s[cfsTypeInit1])
|
||||||
|
if optStackTrace in m.initProc.options and not m.PreventStackTrace:
|
||||||
var procname = CStringLit(m.initProc, prc, m.module.name.s)
|
var procname = CStringLit(m.initProc, prc, m.module.name.s)
|
||||||
var filename = CStringLit(m.initProc, prc, toFilename(m.module.info))
|
var filename = CStringLit(m.initProc, prc, toFilename(m.module.info))
|
||||||
app(prc, initFrame(m.initProc, procname, filename))
|
app(prc, initFrame(m.initProc, procname, filename))
|
||||||
else:
|
app(prc, genSectionEnd(cfsTypeInit1))
|
||||||
app(prc, m.initProc.s[cpsLocals])
|
|
||||||
app(prc, m.s[cfsTypeInit1])
|
for i in cfsTypeInit2..cfsDynLibInit:
|
||||||
app(prc, m.s[cfsTypeInit2])
|
app(prc, genSectionStart(i))
|
||||||
app(prc, m.s[cfsTypeInit3])
|
app(prc, m.s[i])
|
||||||
app(prc, m.s[cfsDebugInit])
|
app(prc, genSectionEnd(i))
|
||||||
app(prc, m.s[cfsDynLibInit])
|
|
||||||
|
app(prc, genSectionStart(cpsInit))
|
||||||
app(prc, m.initProc.s[cpsInit])
|
app(prc, m.initProc.s[cpsInit])
|
||||||
|
app(prc, genSectionEnd(cpsInit))
|
||||||
|
|
||||||
|
app(prc, genSectionStart(cpsStmts))
|
||||||
app(prc, m.initProc.s[cpsStmts])
|
app(prc, m.initProc.s[cpsStmts])
|
||||||
if optStackTrace in m.initProc.options and not m.PreventStackTrace:
|
if optStackTrace in m.initProc.options and not m.PreventStackTrace:
|
||||||
app(prc, deinitFrame(m.initProc))
|
app(prc, deinitFrame(m.initProc))
|
||||||
|
app(prc, genSectionEnd(cpsStmts))
|
||||||
appf(prc, "}$n$n")
|
appf(prc, "}$n$n")
|
||||||
app(m.s[cfsProcs], prc)
|
# 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``
|
||||||
|
app(m.s[cfsInitProc], prc)
|
||||||
|
|
||||||
proc genModule(m: BModule, cfilenoext: string): PRope =
|
proc genModule(m: BModule, cfilenoext: string): PRope =
|
||||||
result = getFileHeader(cfilenoext)
|
result = getFileHeader(cfilenoext)
|
||||||
|
result.app(genMergeInfo(m))
|
||||||
|
|
||||||
|
app(m.s[cfsHeaders], genSectionStart(cfsHeaders))
|
||||||
generateHeaders(m)
|
generateHeaders(m)
|
||||||
|
app(m.s[cfsHeaders], genSectionEnd(cfsHeaders))
|
||||||
|
|
||||||
generateThreadLocalStorage(m)
|
generateThreadLocalStorage(m)
|
||||||
for i in countup(low(TCFileSection), cfsProcs): app(result, m.s[i])
|
for i in countup(low(TCFileSection), cfsProcs):
|
||||||
|
app(result, genSectionStart(i))
|
||||||
|
app(result, m.s[i])
|
||||||
|
app(result, genSectionEnd(i))
|
||||||
|
app(result, m.s[cfsInitProc])
|
||||||
|
|
||||||
proc rawNewModule(module: PSym, filename: string): BModule =
|
proc rawNewModule(module: PSym, filename: string): BModule =
|
||||||
new(result)
|
new(result)
|
||||||
|
|
|
||||||
116
compiler/cgendata.nim
Normal file
116
compiler/cgendata.nim
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# The Nimrod Compiler
|
||||||
|
# (c) Copyright 2011 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## This module contains the data structures for the C code generation phase.
|
||||||
|
|
||||||
|
import
|
||||||
|
ast, astalgo, ropes, passes, options, intsets, lists, platform
|
||||||
|
|
||||||
|
type
|
||||||
|
TLabel* = PRope # for the C generator a label is just a rope
|
||||||
|
TCFileSection* = enum # the sections a generated C file consists of
|
||||||
|
cfsMergeInfo, # section containing merge information
|
||||||
|
cfsHeaders, # section for C include file headers
|
||||||
|
cfsForwardTypes, # section for C forward typedefs
|
||||||
|
cfsTypes, # section for C typedefs
|
||||||
|
cfsSeqTypes, # section for sequence types only
|
||||||
|
# this is needed for strange type generation
|
||||||
|
# reasons
|
||||||
|
cfsFieldInfo, # section for field information
|
||||||
|
cfsTypeInfo, # section for type information
|
||||||
|
cfsProcHeaders, # section for C procs prototypes
|
||||||
|
cfsData, # section for C constant data
|
||||||
|
cfsVars, # section for C variable declarations
|
||||||
|
cfsProcs, # section for C procs that are not inline
|
||||||
|
cfsInitProc, # section for the C init 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
|
||||||
|
cfsDebugInit, # section for init of debug information
|
||||||
|
cfsDynLibInit, # section for init of dynamic library binding
|
||||||
|
cfsDynLibDeinit # section for deinitialization of dynamic
|
||||||
|
# libraries
|
||||||
|
TCTypeKind* = enum # describes the type kind of a C type
|
||||||
|
ctVoid, ctChar, ctBool, ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64,
|
||||||
|
ctInt, ctInt8, ctInt16, ctInt32, ctInt64, ctFloat, ctFloat32, ctFloat64,
|
||||||
|
ctFloat128, ctArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc, ctCString
|
||||||
|
TCFileSections = array[TCFileSection, PRope] # represents a generated C file
|
||||||
|
TCProcSection* = enum # the sections a generated C proc consists of
|
||||||
|
cpsLocals, # section of local variables for C proc
|
||||||
|
cpsInit, # section for init of variables for C proc
|
||||||
|
cpsStmts # section of local statements for C proc
|
||||||
|
TCProcSections = array[TCProcSection, PRope] # represents a generated C proc
|
||||||
|
BModule* = ref TCGen
|
||||||
|
BProc* = ref TCProc
|
||||||
|
TBlock{.final.} = object
|
||||||
|
id*: int # the ID of the label; positive means that it
|
||||||
|
# has been used (i.e. the label should be emitted)
|
||||||
|
nestedTryStmts*: int # how many try statements is it nested into
|
||||||
|
|
||||||
|
TCProc{.final.} = object # represents C proc that is currently generated
|
||||||
|
s*: TCProcSections # the procs sections; short name for readability
|
||||||
|
prc*: PSym # the Nimrod proc that this C proc belongs to
|
||||||
|
BeforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed
|
||||||
|
ThreadVarAccessed*: bool # true if the proc already accessed some threadvar
|
||||||
|
nestedTryStmts*: seq[PNode] # in how many nested try statements we are
|
||||||
|
# (the vars must be volatile then)
|
||||||
|
labels*: Natural # for generating unique labels in the C proc
|
||||||
|
blocks*: seq[TBlock] # nested blocks
|
||||||
|
options*: TOptions # options that should be used for code
|
||||||
|
# generation; this is the same as prc.options
|
||||||
|
# unless prc == nil
|
||||||
|
frameLen*: int # current length of frame descriptor
|
||||||
|
sendClosure*: PType # closure record type that we pass
|
||||||
|
receiveClosure*: PType # closure record type that we get
|
||||||
|
module*: BModule # used to prevent excessive parameter passing
|
||||||
|
withinLoop*: int # > 0 if we are within a loop
|
||||||
|
|
||||||
|
TTypeSeq* = seq[PType]
|
||||||
|
TCGen = object of TPassContext # represents a C source file
|
||||||
|
module*: PSym
|
||||||
|
filename*: string
|
||||||
|
s*: TCFileSections # sections of the C file
|
||||||
|
PreventStackTrace*: bool # true if stack traces need to be prevented
|
||||||
|
usesThreadVars*: bool # true if the module uses a thread var
|
||||||
|
cfilename*: string # filename of the module (including path,
|
||||||
|
# without extension)
|
||||||
|
typeCache*: TIdTable # cache the generated types
|
||||||
|
forwTypeCache*: TIdTable # cache for forward declarations of types
|
||||||
|
declaredThings*: TIntSet # things we have declared in this .c file
|
||||||
|
declaredProtos*: TIntSet # prototypes we have declared in this .c file
|
||||||
|
headerFiles*: TLinkedList # needed headers to include
|
||||||
|
typeInfoMarker*: TIntSet # needed for generating type information
|
||||||
|
initProc*: BProc # code for init procedure
|
||||||
|
typeStack*: TTypeSeq # used for type generation
|
||||||
|
dataCache*: TNodeTable
|
||||||
|
forwardedProcs*: TSymSeq # keep forwarded procs here
|
||||||
|
typeNodes*, nimTypes*: int # used for type info generation
|
||||||
|
typeNodesName*, nimTypesName*: PRope # used for type info generation
|
||||||
|
labels*: natural # for generating unique module-scope names
|
||||||
|
|
||||||
|
var
|
||||||
|
mainModProcs*, mainModInit*: PRope # parts of the main module
|
||||||
|
gMapping*: PRope # the generated mapping file (if requested)
|
||||||
|
gProcProfile*: Natural # proc profile counter
|
||||||
|
gGeneratedSyms*: TIntSet # set of ID's of generated symbols
|
||||||
|
gPendingModules*: seq[BModule] = @[] # list of modules that are not
|
||||||
|
# finished with code generation
|
||||||
|
gForwardedProcsCounter*: int = 0
|
||||||
|
gNimDat*: BModule # generated global data
|
||||||
|
|
||||||
|
|
||||||
|
proc newProc*(prc: PSym, module: BModule): BProc =
|
||||||
|
new(result)
|
||||||
|
result.prc = prc
|
||||||
|
result.module = module
|
||||||
|
if prc != nil: result.options = prc.options
|
||||||
|
else: result.options = gOptions
|
||||||
|
result.blocks = @[]
|
||||||
|
result.nestedTryStmts = @[]
|
||||||
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
## This module contains a simple persistent id generator.
|
## This module contains a simple persistent id generator.
|
||||||
|
|
||||||
import idents, strutils, os
|
import idents, strutils, os, options
|
||||||
|
|
||||||
var gFrontEndId, gBackendId*: int
|
var gFrontEndId, gBackendId*: int
|
||||||
|
|
||||||
|
|
@ -40,15 +40,18 @@ proc setId*(id: int) {.inline.} =
|
||||||
proc IDsynchronizationPoint*(idRange: int) =
|
proc IDsynchronizationPoint*(idRange: int) =
|
||||||
gFrontEndId = (gFrontEndId div IdRange + 1) * IdRange + 1
|
gFrontEndId = (gFrontEndId div IdRange + 1) * IdRange + 1
|
||||||
|
|
||||||
|
proc toGid(f: string): string =
|
||||||
|
result = options.completeGeneratedFilePath(f.addFileExt("gid"))
|
||||||
|
|
||||||
proc saveMaxIds*(project: string) =
|
proc saveMaxIds*(project: string) =
|
||||||
var f = open(project.addFileExt("gid"), fmWrite)
|
var f = open(project.toGid, fmWrite)
|
||||||
f.writeln($gFrontEndId)
|
f.writeln($gFrontEndId)
|
||||||
f.writeln($gBackEndId)
|
f.writeln($gBackEndId)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
proc loadMaxIds*(project: string) =
|
proc loadMaxIds*(project: string) =
|
||||||
var f: TFile
|
var f: TFile
|
||||||
if open(f, project.addFileExt("gid"), fmRead):
|
if open(f, project.toGid, fmRead):
|
||||||
var frontEndId = parseInt(f.readLine)
|
var frontEndId = parseInt(f.readLine)
|
||||||
var backEndId = parseInt(f.readLine)
|
var backEndId = parseInt(f.readLine)
|
||||||
gFrontEndId = max(gFrontEndId, frontEndId)
|
gFrontEndId = max(gFrontEndId, frontEndId)
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ const
|
||||||
chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
|
||||||
# since negative numbers require a leading '-' they use up 1 byte. Thus we
|
# since negative numbers require a leading '-' they use up 1 byte. Thus we
|
||||||
# subtract/add vintDelta here to save space for little negative numbers
|
# subtract/add `vintDelta` here to save space for small negative numbers
|
||||||
# which are common in ROD files:
|
# which are common in ROD files:
|
||||||
const
|
const
|
||||||
vintDelta = 5
|
vintDelta = 5
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,12 @@ proc newRope(data: string = nil): PRope =
|
||||||
result.length = len(data)
|
result.length = len(data)
|
||||||
result.data = data
|
result.data = data
|
||||||
|
|
||||||
|
proc newMutableRope*(capacity = 30): PRope =
|
||||||
|
## creates a new rope that supports direct modifications of the rope's
|
||||||
|
## 'data' and 'length' fields.
|
||||||
|
new(result)
|
||||||
|
result.data = newStringOfCap(capacity)
|
||||||
|
|
||||||
var
|
var
|
||||||
cache: PRope # the root of the cache tree
|
cache: PRope # the root of the cache tree
|
||||||
misses, hits: int
|
misses, hits: int
|
||||||
|
|
|
||||||
21
todo.txt
21
todo.txt
|
|
@ -2,19 +2,33 @@ Version 0.8.14
|
||||||
==============
|
==============
|
||||||
|
|
||||||
- optimize unused constants away
|
- optimize unused constants away
|
||||||
- 'let x = y'; const ptr/ref
|
- 'let x = y'
|
||||||
- fix actors.nim
|
- fix actors.nim
|
||||||
- make threadvar efficient again on linux after testing
|
- make threadvar efficient again on linux after testing
|
||||||
- test the sort implementation again
|
- test the sort implementation again
|
||||||
- optional indentation for 'case' statement
|
- optional indentation for 'case' statement
|
||||||
- document & test splicing; don't forget to test negative indexes
|
- document & test splicing; don't forget to test negative indexes
|
||||||
- thread local vs. global raiseHook()
|
- thread local vs. global raiseHook()
|
||||||
- incremental compilation (!)
|
- make pegs support a compile-time option and make c2nim use regexes instead
|
||||||
|
per default
|
||||||
|
|
||||||
|
|
||||||
|
incremental compilation
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
- implement C file merge operation
|
||||||
|
- adapt thread var implementation to care about the new merge operation
|
||||||
|
- write test cases: needs test script support
|
||||||
|
- fix remaining bugs
|
||||||
|
- write documentation
|
||||||
|
- make the compiler output a warning if linking fails with --symbolFiles:on
|
||||||
|
(necessary?)
|
||||||
|
|
||||||
|
|
||||||
version 0.9.0
|
version 0.9.0
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
- const ptr/ref
|
||||||
- unsigned ints and bignums
|
- unsigned ints and bignums
|
||||||
- warning for implicit openArray -> varargs convention
|
- warning for implicit openArray -> varargs convention
|
||||||
- implement explicit varargs
|
- implement explicit varargs
|
||||||
|
|
@ -24,6 +38,8 @@ version 0.9.0
|
||||||
- make exceptions compatible with C++ exceptions
|
- make exceptions compatible with C++ exceptions
|
||||||
- ``=`` should be overloadable; requires specialization for ``=``
|
- ``=`` should be overloadable; requires specialization for ``=``
|
||||||
- 'const' objects including case objects
|
- 'const' objects including case objects
|
||||||
|
- os module should use Windows Unicode versions
|
||||||
|
- 64bit build for Windows
|
||||||
|
|
||||||
Bugs
|
Bugs
|
||||||
----
|
----
|
||||||
|
|
@ -141,5 +157,6 @@ Version 2
|
||||||
a full blown statement; a ``try`` expression might be a good idea to make
|
a full blown statement; a ``try`` expression might be a good idea to make
|
||||||
error handling more light-weight
|
error handling more light-weight
|
||||||
people also want ``inc a; inc b``
|
people also want ``inc a; inc b``
|
||||||
|
--> solved by providing an expr version of most control structures?
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue