summary refs log tree commit diff stats
path: root/nim/cgen.pas
diff options
context:
space:
mode:
Diffstat (limited to 'nim/cgen.pas')
-rwxr-xr-xnim/cgen.pas759
1 files changed, 759 insertions, 0 deletions
diff --git a/nim/cgen.pas b/nim/cgen.pas
new file mode 100755
index 000000000..cb89cd910
--- /dev/null
+++ b/nim/cgen.pas
@@ -0,0 +1,759 @@
+//
+//
+//           The Nimrod Compiler
+//        (c) Copyright 2008 Andreas Rumpf
+//
+//    See the file "copying.txt", included in this
+//    distribution, for details about the copyright.
+//
+
+unit cgen;
+
+// This is the new C code generator; much cleaner and faster
+// than the old one. It also generates better code.
+
+interface
+
+{$include 'config.inc'}
+
+uses
+  nsystem, ast, astalgo, strutils, hashes, trees, platform, magicsys,
+  extccomp, options, nversion, nimsets, msgs, crc, bitsets, idents,
+  lists, types, ccgutils, nos, ntime, ropes, nmath, backends,
+  wordrecg, rnimsyn;
+
+function CBackend(b: PBackend; module: PSym; const filename: string): PBackend;
+
+implementation
+
+type
+  TLabel = PRope;      // for the C generator a label is just a rope
+
+  TCFileSection = (    // 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
+    cfsData,           // section for C constant data
+    cfsVars,           // section for C variable declarations
+    cfsProcHeaders,    // section for C procs prototypes
+    cfsProcs,          // section for C procs that are not inline
+    cfsTypeInit1,      // section 1 for declarations of type information
+    cfsTypeInit2,      // section A for initialization of type information
+    cfsTypeInit3,      // section B for init of type information
+    cfsDebugInit,      // section for initialization of debug information
+    cfsDynLibInit,     // section for initialization of dynamic library binding
+    cfsDynLibDeinit    // section for deinitialization of dynamic libraries
+  );
+
+
+  TCFileSections = array [TCFileSection] of PRope;
+    // TCFileSections represents a generated C file
+  TCProcSection = (    // the sections a generated C proc consists of
+    cpsLocals,         // section of local variables for C proc
+    cpsInit,           // section for initialization of variables for C proc
+    cpsStmts           // section of local statements for C proc
+  );
+
+
+  TCProcSections = array [TCProcSection] of PRope;
+    // TCProcSections represents a generated C proc
+
+  BModule = ^TCGen;
+  BProc = ^TCProc;
+
+  TCProc = record            // 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
+    inTryStmt: Natural;      // wether we are in a try statement
+                             // (the vars must be volatile then)
+    unique: Natural;         // for generating unique names in the C proc
+    blocks: array of int;    // the ID of the label; positive means that it
+                             // has been used (i.e. the label should be emitted)
+    locals: array of TLoc;   // locNone means slot is free again
+    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
+  end;
+
+  TCGen = object(TBackend)   // represents a C source file
+    s: TCFileSections;       // sections of the C file
+    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
+    debugDeclared: TIntSet;  // for debugging purposes
+    headerFiles: TLinkedList; // needed headers to include
+    unique: Natural;         // for generating unique names
+    typeInfoMarker: TIntSet; // needed for generating type information
+    initProc: BProc;         // code for init procedure
+  end;
+
+var
+  currMod: BModule; // currently compiled module
+                    // a global so that this needs not to be
+                    // passed to every proc
+  mainModProcs, mainModInit: PRope; // parts of the main module
+  gMapping: PRope;  // the generated mapping file (if requested)
+
+  constTok: PRope; // either 'const ' or nil depending on gCmd
+
+function initLoc(k: TLocKind; typ: PType): TLoc;
+begin
+  result.k := k;
+  result.t := typ;
+  result.r := nil;
+  result.a := -1;
+  result.indirect := 0;
+  result.flags := {@set}[]
+end;
+
+procedure fillLoc(var a: TLoc; k: TLocKind; typ: PType; r: PRope;
+                  flags: TLocFlags);
+begin
+  // fills the loc if it is not already initialized
+  if a.k = locNone then begin
+    a.k := k;
+    if typ.kind = tyGenericInst then a.t := lastSon(typ) else a.t := typ;
+    a.a := -1;
+    if a.r = nil then a.r := r;
+    a.flags := a.flags + flags
+  end
+end;
+
+procedure inheritStorage(var dest: TLoc; const src: TLoc);
+begin
+  dest.flags := src.flags * [lfOnStack, lfOnHeap, lfOnData, lfOnUnknown]
+end;
+
+function newProc(prc: PSym): BProc;
+begin
+  new(result);
+{@ignore}
+  fillChar(result^, sizeof(result^), 0);
+{@emit}
+  result.prc := prc;
+  if prc <> nil then
+    result.options := prc.options
+  else
+    result.options := gOptions;
+{@ignore}
+  setLength(result.blocks, 0);
+  setLength(result.locals, 0);
+{@emit
+  result.blocks := [];}
+{@emit
+  result.locals := [];}
+end;
+
+function isSimpleConst(typ: PType): bool;
+begin
+  result := not (skipAbstract(typ).kind in [tyRecord, tyRecordConstr,
+                                            tyObject, tyArray,
+                                            tyArrayConstr, tySet, tySequence])
+end;
+
+procedure useHeader(sym: PSym);
+begin
+  if lfHeader in sym.loc.Flags then begin
+    assert(sym.annex <> nil);
+    {@discard} lists.IncludeStr(currMod.headerFiles, PLib(sym.annex).path)
+  end
+end;
+
+procedure UseMagic(const name: string); forward;
+
+// ----------------------------- name mangling
+// +++++++++++++++++++++++++++++ type generation
+// +++++++++++++++++++++++++++++ type info generation
+{$include 'ccgtypes.pas'}
+
+// ------------------------------ Manager of temporaries ------------------
+
+function beEqualTypes(a, b: PType): bool;
+begin
+  // returns whether two type are equal for the backend
+  result := sameType(skipAbstract(a), skipAbstract(b))
+end;
+
+function getTemp(p: BProc; t: PType): TLoc;
+var
+  i, index: int;
+  name: PRope;
+begin
+  for i := 0 to high(p.locals) do begin
+    assert(i = p.locals[i].a);
+    if (p.locals[i].k = locNone) and beEqualTypes(p.locals[i].t, t) then begin
+      // free slot of the appropriate type?
+      p.locals[i].k := locTemp; // is filled again
+      result := p.locals[i];
+      exit
+    end
+  end;
+  // not found:
+  index := length(p.locals);
+  setLength(p.locals, index+1);
+  // declare the new temporary:
+  name := con('Loc', toRope(index));
+  appRopeFormat(p.s[cpsLocals], '$1 $2; /* temporary */$n',
+                [getTypeDesc(t), name]);
+  p.locals[index].k := locTemp;
+  p.locals[index].a := index;
+  p.locals[index].r := name;
+  p.locals[index].t := t;
+  p.locals[index].flags := {@set}[lfOnStack];
+  result := p.locals[index] // BUGFIX!
+end;
+
+procedure freeTemp(p: BProc; const temp: TLoc);
+begin
+  if (temp.a >= 0) and (temp.a < length(p.locals)) and
+                    (p.locals[temp.a].k = locTemp) then
+    p.locals[temp.a].k := locNone
+end;
+
+// -------------------------- Variable manager ----------------------------
+
+procedure declareGlobalVar(s: PSym);
+begin
+  if not IntSetContainsOrIncl(currMod.declaredThings, s.id) then begin
+    app(currMod.s[cfsVars], getTypeDesc(s.loc.t));
+    if sfRegister in s.flags then
+      app(currMod.s[cfsVars], ' register');
+    if sfVolatile in s.flags then
+      app(currMod.s[cfsVars], ' volatile');
+    appRopeFormat(currMod.s[cfsVars], ' $1; /* $2 */$n',
+      [s.loc.r, toRope(s.name.s)])
+  end
+end;
+
+procedure assignLocalVar(p: BProc; s: PSym);
+begin
+  //assert(s.loc.k == locNone) // not yet assigned
+  // this need not be fullfilled for inline procs; they are regenerated
+  // for each module that uses them!
+  fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), {@set}[lfOnStack]);
+  app(p.s[cpsLocals], getTypeDesc(s.loc.t));
+  if sfRegister in s.flags then
+    app(p.s[cpsLocals], ' register');
+  if (sfVolatile in s.flags) or (p.inTryStmt > 0) then
+    app(p.s[cpsLocals], ' volatile');
+
+  appRopeFormat(p.s[cpsLocals], ' $1; /* $2 */$n',
+    [s.loc.r, toRope(s.name.s)]);
+  // if debugging we need a new slot for the local variable:
+  if [optStackTrace, optEndb] * p.Options = [optStackTrace, optEndb] then begin
+    appRopeFormat(p.s[cpsInit],
+      'F.s[$1].name = $2; F.s[$1].address = (void*)&$3; F.s[$1].typ = $4;$n',
+      [toRope(p.frameLen), makeCString(normalize(s.name.s)), s.loc.r,
+      genTypeInfo(currMod, s.loc.t)]);
+    inc(p.frameLen);
+  end
+end;
+
+procedure assignGlobalVar(s: PSym);
+begin
+  fillLoc(s.loc, locGlobalVar, s.typ, mangleName(s), {@set}[lfOnData]);
+  useHeader(s);
+  if lfNoDecl in s.loc.flags then exit;
+  if sfImportc in s.flags then app(currMod.s[cfsVars], 'extern ');
+  declareGlobalVar(s);
+  if [optStackTrace, optEndb] * currMod.module.options =
+     [optStackTrace, optEndb] then begin
+    useMagic('dbgRegisterGlobal');
+    appRopeFormat(currMod.s[cfsDebugInit],
+      'dbgRegisterGlobal($1, &$2, $3);$n',
+      [makeCString(normalize(s.owner.name.s + '.' +{&} s.name.s)), s.loc.r,
+      genTypeInfo(currMod, s.typ)])
+  end;
+end;
+
+function iff(cond: bool; the, els: PRope): PRope;
+begin
+  if cond then result := the else result := els
+end;
+
+procedure assignParam(p: BProc; s: PSym);
+begin
+  assert(s.loc.r <> nil);
+  if [optStackTrace, optEndb] * p.options = [optStackTrace, optEndb] then begin
+    appRopeFormat(p.s[cpsInit],
+      'F.s[$1].name = $2; F.s[$1].address = (void*)$3; ' +
+      'F.s[$1].typ = $4;$n',
+      [toRope(p.frameLen), makeCString(normalize(s.name.s)),
+      iff(usePtrPassing(s), s.loc.r, con('&'+'', s.loc.r)),
+      genTypeInfo(currMod, s.loc.t)]);
+    inc(p.frameLen)
+  end
+end;
+
+// -------------------------- label manager -------------------------------
+
+// note that a label is a location too
+function getLabel(p: BProc): TLabel;
+begin
+  inc(p.unique);
+  result := con('L'+'', toRope(p.unique))
+end;
+
+procedure fixLabel(p: BProc; labl: TLabel);
+begin
+  appRopeFormat(p.s[cpsStmts], '$1: ;$n', [labl])
+end;
+
+procedure genProcPrototype(sym: PSym); forward;
+procedure genVarPrototype(sym: PSym); forward;
+procedure genConstPrototype(sym: PSym); forward;
+procedure genProc(prc: PSym); forward;
+procedure genStmts(p: BProc; t: PNode); forward;
+
+{$include 'ccgexprs.pas'}
+{$include 'ccgstmts.pas'}
+
+// ----------------------------- dynamic library handling -----------------
+
+// We don't finalize dynamic libs as this does the OS for us.
+
+procedure loadDynamicLib(lib: PLib);
+var
+  tmp: PRope;
+begin
+  assert(lib <> nil);
+  if lib.kind = libDynamic then begin
+    lib.kind := libDynamicGenerated;
+    useMagic('nimLoadLibrary');
+    useMagic('nimUnloadLibrary');
+    tmp := getTempName();
+    appRopeFormat(currMod.s[cfsVars], 'static void* $1;$n', [tmp]);
+    appRopeFormat(currMod.s[cfsDynLibInit],
+      '$1 = nimLoadLibrary((string) &$2);$n',
+      [tmp, getStrLit(lib.path)]);
+    appRopeFormat(currMod.s[cfsDynLibDeinit],
+      'if ($1 != NIM_NIL) nimUnloadLibrary($1);$n', [tmp]);
+    assert(lib.name = nil);
+    lib.name := tmp
+  end
+end;
+
+procedure SymInDynamicLib(sym: PSym);
+var
+  lib: PLib;
+  extname, tmp: PRope;
+begin
+  lib := PLib(sym.annex);
+  extname := sym.loc.r;
+  loadDynamicLib(lib);
+  useMagic('nimGetProcAddr');
+  tmp := ropeFormat('Dl_$1', [toRope(sym.id)]);
+  sym.loc.r := tmp; // from now on we only need the internal name
+  sym.typ.sym := nil; // generate a new name
+  appRopeFormat(currMod.s[cfsDynLibInit],
+    '$1 = ($2) nimGetProcAddr($3, $4);$n',
+    [tmp, getTypeDesc(sym.typ), lib.name,
+    makeCString(ropeToStr(extname))]);
+  declareGlobalVar(sym)
+end;
+
+// ----------------------------- sections ---------------------------------
+
+procedure UseMagic(const name: string);
+var
+  sym: PSym;
+begin
+  if (sfSystemModule in currMod.module.flags) then exit;
+  // we don't know the magic symbols in the system module, but they will be
+  // there anyway, because that is the way the code generator works
+  sym := magicsys.getCompilerProc(name);
+  case sym.kind of
+    skProc: genProcPrototype(sym);
+    skVar: genVarPrototype(sym);
+    skType: {@discard} getTypeDesc(sym.typ);
+    else InternalError('useMagic: ' + name)
+  end
+end;
+
+procedure generateHeaders();
+var
+  it: PStrEntry;
+begin
+  app(currMod.s[cfsHeaders], '#include "nimbase.h"' +{&} tnl +{&} tnl);
+  it := PStrEntry(currMod.headerFiles.head);
+  while it <> nil do begin
+    if not (it.data[strStart] in ['"', '<']) then
+      appRopeFormat(currMod.s[cfsHeaders],
+        '#include "$1"$n', [toRope(it.data)])
+    else
+      appRopeFormat(currMod.s[cfsHeaders], '#include $1$n', [toRope(it.data)]);
+    it := PStrEntry(it.Next)
+  end
+end;
+
+procedure getFrameDecl(p: BProc);
+var
+  slots: PRope;
+begin
+  if p.frameLen > 0 then begin
+    useMagic('TVarSlot');
+    slots := ropeFormat('  TVarSlot s[$1];$n', [toRope(p.frameLen)])
+  end
+  else
+    slots := nil;
+  appRopeFormat(p.s[cpsLocals], 'volatile struct {TFrame* prev;' +
+    'NCSTRING procname;NS line;NCSTRING filename;' +
+    'NS len;$n$1} F;$n', [slots]);
+  prepend(p.s[cpsInit], ropeFormat('F.len = $1;$n', [toRope(p.frameLen)]))
+end;
+
+function retIsNotVoid(s: PSym): bool;
+begin
+  result := (s.typ.sons[0] <> nil) and not isInvalidReturnType(s.typ.sons[0])
+end;
+
+procedure genProc(prc: PSym);
+var
+  p: BProc;
+  generatedProc, header, returnStmt: PRope;
+  i: int;
+  res, param: PSym;
+begin
+  useHeader(prc);
+  fillLoc(prc.loc, locProc, prc.typ, mangleName(prc), {@set}[lfOnData]);
+  if (lfNoDecl in prc.loc.Flags) then exit;
+  if lfDynamicLib in prc.loc.flags then
+    SymInDynamicLib(prc)
+  else if not (sfImportc in prc.flags) then begin
+    // we have a real proc here:
+    p := newProc(prc);
+    header := genProcHeader(prc);
+    if (sfCompilerProc in prc.flags)
+    and (sfSystemModule in currMod.module.flags)
+    and not IntSetContains(currMod.declaredThings, prc.id) then
+      appRopeFormat(currMod.s[cfsProcHeaders], '$1;$n', [header]);
+    intSetIncl(currMod.declaredThings, prc.id);
+    returnStmt := nil;
+    assert(prc.ast <> nil);
+
+    if not (sfPure in prc.flags) then begin
+      if not isInvalidReturnType(prc.typ.sons[0]) then begin
+        res := prc.ast.sons[resultPos].sym; // get result symbol
+        // declare the result symbol:
+        assignLocalVar(p, res);
+        assert(res.loc.r <> nil);
+        initVariable(p, res);
+        genObjectInit(p, res);
+        returnStmt := ropeFormat('return $1;$n', [rdLoc(res.loc)]);
+      end
+      else if (prc.typ.sons[0] <> nil) then begin
+        res := prc.ast.sons[resultPos].sym; // get result symbol
+        fillResult(res);
+        assignParam(p, res)
+      end
+    end;
+    for i := 1 to sonsLen(prc.typ.n)-1 do begin
+      param := prc.typ.n.sons[i].sym;
+      assignParam(p, param)
+    end;
+
+    genStmts(p, prc.ast.sons[codePos]); // modifies p.locals, p.init, etc.
+    if sfPure in prc.flags then
+      generatedProc := ropeFormat('$1 {$n$2$3$4}$n',
+        [header, p.s[cpsLocals], p.s[cpsInit], p.s[cpsStmts]])
+    else begin
+      generatedProc := con(header, '{' + tnl);
+      if optStackTrace in prc.options then begin
+        getFrameDecl(p);
+        prepend(p.s[cpsInit], ropeFormat(
+          'F.procname = $1;$n' +
+          'F.prev = framePtr;$n' +
+          'F.filename = $2;$n' +
+          'F.line = 0;$n' +
+          'framePtr = (TFrame*)&F;$n',
+          [makeCString(prc.owner.name.s +{&} '.' +{&} prc.name.s),
+          makeCString(toFilename(prc.info))]));
+      end;
+      app(generatedProc, con(p.s));
+      if p.beforeRetNeeded then
+        app(generatedProc, 'BeforeRet: ;' + tnl);
+      if optStackTrace in prc.options then
+        app(generatedProc, 'framePtr = framePtr->prev;' + tnl);
+      app(generatedProc, returnStmt);
+      app(generatedProc, '}' + tnl);
+      // only now we can free the syntax tree:
+      //if prc.typ.callConv <> ccInline then
+      //  prc.ast.sons[codePos] := nil;
+    end;
+    app(currMod.s[cfsProcs], generatedProc);
+  end
+end;
+
+procedure genVarPrototype(sym: PSym);
+begin
+  assert(sfGlobal in sym.flags);
+  useHeader(sym);
+  fillLoc(sym.loc, locGlobalVar, sym.typ, mangleName(sym), {@set}[lfOnData]);
+  if (lfNoDecl in sym.loc.Flags) or
+      intSetContainsOrIncl(currMod.declaredThings, sym.id) then
+    exit;
+  if sym.owner.id <> currMod.module.id then begin
+    // else we already have the symbol generated!
+    assert(sym.loc.r <> nil);
+    app(currMod.s[cfsVars], 'extern ');
+    app(currMod.s[cfsVars], getTypeDesc(sym.loc.t));
+    if sfRegister in sym.flags then
+      app(currMod.s[cfsVars], ' register');
+    if sfVolatile in sym.flags then
+      app(currMod.s[cfsVars], ' volatile');
+    appRopeFormat(currMod.s[cfsVars], ' $1; /* $2 */$n',
+      [sym.loc.r, toRope(sym.name.s)])
+  end
+end;
+
+procedure genConstPrototype(sym: PSym);
+begin
+  useHeader(sym);
+  fillLoc(sym.loc, locData, sym.typ, mangleName(sym), {@set}[lfOnData]);
+  if (lfNoDecl in sym.loc.Flags) or
+      intSetContainsOrIncl(currMod.declaredThings, sym.id) then
+    exit;
+  if sym.owner.id <> currMod.module.id then begin
+    // else we already have the symbol generated!
+    assert(sym.loc.r <> nil);
+    app(currMod.s[cfsData], 'extern ');
+    appRopeFormat(currMod.s[cfsData], '$1$2 $3; /* $4 */$n',
+      [constTok, getTypeDesc(sym.loc.t), sym.loc.r, toRope(sym.name.s)])
+  end
+end;
+
+procedure genProcPrototype(sym: PSym);
+begin
+  useHeader(sym);
+  fillLoc(sym.loc, locProc, sym.typ, mangleName(sym), {@set}[lfOnData]);
+  if lfDynamicLib in sym.loc.Flags then begin
+    // it is a proc variable!
+    if (sym.owner.id <> currMod.module.id) and
+        not intSetContainsOrIncl(currMod.declaredThings, sym.id) then begin
+      app(currMod.s[cfsVars], 'extern ');
+      // BUGFIX: declareGlobalVar() inlined, because of intSetContainsOrIncl
+      // check
+      app(currMod.s[cfsVars], getTypeDesc(sym.loc.t));
+      appRopeFormat(currMod.s[cfsVars], ' $1; /* $2 */$n',
+        [sym.loc.r, toRope(sym.name.s)])
+    end
+  end
+  else begin
+    // it is a proc:
+    if (lfNoDecl in sym.loc.Flags) then exit;
+    if intSetContainsOrIncl(currMod.declaredThings, sym.id) then exit;
+    appRopeFormat(currMod.s[cfsProcHeaders], '$1;$n', [genProcHeader(sym)]);
+    if (sym.typ.callConv = ccInline)
+    and (sym.owner.id <> currMod.module.id) then
+      genProc(sym) // generate the code again!
+//    else
+//      IntSetIncl(currMod.declaredThings, sym.id)
+  end
+end;
+
+function getFileHeader: PRope;
+begin
+  result := ropeFormat(
+    '/* Generated by the Nimrod Compiler v$1 */$n' +
+    '/*   (c) 2008 Andreas Rumpf */$n' +
+    '/* Compiled for: $2, $3, $4 */$n',
+    [toRope(versionAsString), toRope(platform.OS[targetOS].name),
+    toRope(platform.CPU[targetCPU].name),
+    toRope(extccomp.CC[extccomp.ccompiler].name)])
+end;
+
+procedure genMainProc(m: BModule);
+const
+  CommonMainBody =
+    '  setStackBottom(dummy);$n' +
+    '  systemInit();$n' +
+    '$1' +
+    '$2';
+  PosixMain =
+    'NS cmdCount;$n' +
+    'char** cmdLine;$n' +
+    'char** gEnv;$n' +
+    'int main(int argc, char** args, char** env) {$n' +
+    '  int dummy[8];$n' +
+    '  cmdLine = args;$n' +
+    '  cmdCount = (NS)argc;$n' +
+    '  gEnv = env;$n' +{&}
+    CommonMainBody +{&}
+    '  return 0;$n' +
+    '}$n';
+  WinMain =
+    'N_STDCALL(int, WinMain)(HINSTANCE hCurInstance, $n' +
+    '                        HINSTANCE hPrevInstance, $n' +
+    '                        LPSTR lpCmdLine, int nCmdShow) {$n' +
+    '  int dummy[8];$n' +{&}
+    CommonMainBody +{&}
+    '  return 0;$n' +
+    '}$n';
+  WinDllMain =
+    'BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $n' +
+    '                    LPVOID lpvReserved) {$n' +
+    '  int dummy[8];$n' +{&}
+    CommonMainBody +{&}
+    '  return 1;$n' +
+    '}$n';
+var
+  frmt: TFormatStr;
+begin
+  useMagic('setStackBottom');
+  if (platform.targetOS = osWindows) and
+      (gGlobalOptions * [optGenGuiApp, optGenDynLib] <> []) then begin
+    if optGenGuiApp in gGlobalOptions then
+      frmt := WinMain
+    else
+      frmt := WinDllMain;
+    {@discard} lists.IncludeStr(m.headerFiles, '<windows.h>')
+  end
+  else
+    frmt := PosixMain;
+  if gBreakpoints <> nil then
+    useMagic('dbgRegisterBreakpoint');
+  appRopeFormat(m.s[cfsProcs], frmt, [gBreakpoints, mainModInit])
+end;
+
+procedure genInitCode(m: BModule);
+var
+  initname, prc: PRope;
+begin
+  initname := con(m.module.name.s, toRope('Init'));
+  appRopeFormat(mainModProcs, 'N_NIMCALL(void, $1)(void);$n',
+    [initname]);
+  if not (sfSystemModule in m.module.flags) then
+    appRopeFormat(mainModInit, '$1();$n', [initname]);
+  prc := ropeFormat('N_NIMCALL(void, $1)(void) {$n', [initname]);
+  if optStackTrace in m.initProc.options then begin
+    prepend(m.initProc.s[cpsLocals], toRope('volatile TFrame F;' + tnl));
+    app(prc, m.initProc.s[cpsLocals]);
+    app(prc, m.s[cfsTypeInit1]);
+    appRopeFormat(prc,
+      'F.len = 0;$n' + // IMPORTANT: else the debugger crashes!
+      'F.procname = $1;$n' +
+      'F.prev = framePtr;$n' +
+      'F.filename = $2;$n' +
+      'F.line = 0;$n' +
+      'framePtr = (TFrame*)&F;$n',
+      [makeCString('module ' + m.module.name.s),
+      makeCString(toFilename(m.module.info))])
+  end
+  else begin
+    app(prc, m.initProc.s[cpsLocals]);
+    app(prc, m.s[cfsTypeInit1]);
+  end;
+  app(prc, m.s[cfsTypeInit2]);
+  app(prc, m.s[cfsTypeInit3]);
+  app(prc, m.s[cfsDebugInit]);
+  app(prc, m.s[cfsDynLibInit]);
+  app(prc, m.initProc.s[cpsInit]);
+  app(prc, m.initProc.s[cpsStmts]);
+  if optStackTrace in m.initProc.options then
+    app(prc, 'framePtr = framePtr->prev;' + tnl);
+  app(prc, '}' +{&} tnl +{&} tnl);
+  app(m.s[cfsProcs], prc)
+end;
+
+function genModule(m: BModule): PRope;
+var
+  i: TCFileSection;
+begin
+  result := getFileHeader();
+  generateHeaders();
+  for i := low(TCFileSection) to cfsProcs do app(result, m.s[i])
+end;
+
+function newModule(module: PSym; const filename: string): BModule;
+begin
+  new(result);
+{@ignore}
+  fillChar(result^, sizeof(result^), 0);
+{@emit}
+  InitLinkedList(result.headerFiles);
+  intSetInit(result.declaredThings);
+  intSetInit(result.debugDeclared);
+  result.cfilename := filename;
+  initIdTable(result.typeCache);
+  initIdTable(result.forwTypeCache);
+  result.module := module;
+  if gCmd <> cmdCompileToCpp then
+    constTok := toRope('const ');
+  intSetInit(result.typeInfoMarker);
+  result.initProc := newProc(nil);
+  result.initProc.options := gOptions;
+end;
+
+function shouldRecompile(code: PRope; const cfile, cfilenoext: string): bool;
+var
+  objFile: string;
+begin
+  result := true;
+  if optCFileCache in gGlobalOptions then begin
+    objFile := toObjFile(cfilenoext);
+    if writeRopeIfNotEqual(code, cfile) then exit;
+    if ExistsFile(objFile) then result := false
+//    and ropeEqualsFile(code, cfile)
+//    and FileNewer(objFile, cfile) then
+  end
+  else
+    writeRope(code, cfile);
+end;
+
+procedure finishModule(b: PBackend; n: PNode);
+var
+  cfile, cfilenoext: string;
+  m: BModule;
+  code: PRope;
+begin
+  m := BModule(b);
+  currMod := m;
+  currMod.initProc.options := gOptions;
+  genStmts(currMod.initProc, n);
+  // generate code for the init statements of the module:
+  genInitCode(m);
+  if sfMainModule in m.module.flags then begin
+    // generate mapping file (if requested):
+    if gMapping <> nil then
+      WriteRope(gMapping, ChangeFileExt(cfile + '_map', 'txt'));
+
+    // generate main file:
+    app(currMod.s[cfsProcHeaders], mainModProcs);
+    genMainProc(currMod);
+  end;
+  cfile := completeCFilePath(m.cfilename);
+  cfilenoext := changeFileExt(cfile, '');
+  code := genModule(m);
+  if shouldRecompile(code, changeFileExt(cfile, cExt), cfilenoext) then begin
+    addFileToCompile(cfilenoext); // is to compile
+  end;
+  addFileToLink(cfilenoext);
+  currMod := nil // free the memory
+end;
+
+function CBackend(b: PBackend; module: PSym; const filename: string): PBackend;
+var
+  g: BModule;
+begin
+  g := newModule(module, filename);
+  g.backendCreator := CBackend;
+  g.eventMask := {@set}[eAfterModule];
+  g.afterModuleEvent := finishModule;
+  currMod := g;
+  result := g;
+end;
+
+initialization
+  intSetInit(gTypeInfoGenerated);
+end.