diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2009-04-22 15:55:27 +0200 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2009-04-22 15:55:27 +0200 |
commit | e792940f5273bf8f8761c4cb29b241445e8b1d0b (patch) | |
tree | 8a6b224b8c0b3db14dbc20d89b7ca9ccb19b1f56 /nim | |
parent | 439aa2d04d5528b5aed288f70895515d1da2dc3d (diff) | |
download | Nim-e792940f5273bf8f8761c4cb29b241445e8b1d0b.tar.gz |
version 0.7.6
Diffstat (limited to 'nim')
43 files changed, 1045 insertions, 499 deletions
diff --git a/nim/ast.pas b/nim/ast.pas index c22385805..a57632468 100644 --- a/nim/ast.pas +++ b/nim/ast.pas @@ -93,12 +93,12 @@ type nkTypeDef, nkYieldStmt, nkTryStmt, nkFinally, nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt, nkBlockStmt, nkDiscardStmt, nkStmtList, nkImportStmt, - nkFromStmt, nkImportAs, nkIncludeStmt, nkAccessStmt, - nkCommentStmt, nkStmtListExpr, nkBlockExpr, nkStmtListType, - nkBlockType, nkVm, nkTypeOfExpr, nkObjectTy, - nkTupleTy, nkRecList, nkRecCase, nkRecWhen, - nkRefTy, nkPtrTy, nkVarTy, nkProcTy, - nkEnumTy, nkEnumFieldDef, nkReturnToken); + nkFromStmt, nkImportAs, nkIncludeStmt, nkCommentStmt, + nkStmtListExpr, nkBlockExpr, nkStmtListType, nkBlockType, + nkVm, nkTypeOfExpr, nkObjectTy, nkTupleTy, + nkRecList, nkRecCase, nkRecWhen, nkRefTy, + nkPtrTy, nkVarTy, nkProcTy, nkEnumTy, + nkEnumFieldDef, nkReturnToken); TNodeKinds = set of TNodeKind; const NodeKindToStr: array [TNodeKind] of string = ( @@ -128,12 +128,12 @@ const 'nkTypeDef', 'nkYieldStmt', 'nkTryStmt', 'nkFinally', 'nkRaiseStmt', 'nkReturnStmt', 'nkBreakStmt', 'nkContinueStmt', 'nkBlockStmt', 'nkDiscardStmt', 'nkStmtList', 'nkImportStmt', - 'nkFromStmt', 'nkImportAs', 'nkIncludeStmt', 'nkAccessStmt', - 'nkCommentStmt', 'nkStmtListExpr', 'nkBlockExpr', 'nkStmtListType', - 'nkBlockType', 'nkVm', 'nkTypeOfExpr', 'nkObjectTy', - 'nkTupleTy', 'nkRecList', 'nkRecCase', 'nkRecWhen', - 'nkRefTy', 'nkPtrTy', 'nkVarTy', 'nkProcTy', - 'nkEnumTy', 'nkEnumFieldDef', 'nkReturnToken'); + 'nkFromStmt', 'nkImportAs', 'nkIncludeStmt', 'nkCommentStmt', + 'nkStmtListExpr', 'nkBlockExpr', 'nkStmtListType', 'nkBlockType', + 'nkVm', 'nkTypeOfExpr', 'nkObjectTy', 'nkTupleTy', + 'nkRecList', 'nkRecCase', 'nkRecWhen', 'nkRefTy', + 'nkPtrTy', 'nkVarTy', 'nkProcTy', 'nkEnumTy', + 'nkEnumFieldDef', 'nkReturnToken'); type TSymFlag = ( sfUsed, sfStar, sfMinus, sfInInterface, @@ -327,7 +327,8 @@ type ); TLocFlag = ( - lfIndirect, // backend introduced a pointer + lfIndirect, // backend introduced a pointer + lfParamCopy, // backend introduced a parameter copy (LLVM) lfNoDeepCopy, // no need for a deep copy lfNoDecl, // do not declare it in C lfDynamicLib, // link symbol to dynamic library diff --git a/nim/ccgexprs.pas b/nim/ccgexprs.pas index 161804208..03de5c4de 100644 --- a/nim/ccgexprs.pas +++ b/nim/ccgexprs.pas @@ -1233,16 +1233,14 @@ begin app(r, '.Sup'); s := skipGeneric(s.sons[0]); end; - appf(p.s[cpsStmts], '$1.m_type = $2;$n', - [r, genTypeInfo(p.module, t)]) + appf(p.s[cpsStmts], '$1.m_type = $2;$n', [r, genTypeInfo(p.module, t)]) end; frEmbedded: begin // worst case for performance: useMagic(p.module, 'objectInit'); if takeAddr then r := addrLoc(a) else r := rdLoc(a); - appf(p.s[cpsStmts], 'objectInit($1, $2);$n', - [r, genTypeInfo(p.module, t)]) + appf(p.s[cpsStmts], 'objectInit($1, $2);$n', [r, genTypeInfo(p.module, t)]) end end end; @@ -1256,9 +1254,15 @@ begin refType := skipVarGenericRange(e.sons[1].typ); InitLocExpr(p, e.sons[1], a); initLoc(b, locExpr, a.t, OnHeap); - b.r := ropef('($1) newObj($2, sizeof($3))', - [getTypeDesc(p.module, reftype), genTypeInfo(p.module, refType), - getTypeDesc(p.module, skipGenericRange(reftype.sons[0]))]); + + if optBoehmGC in gGlobalOptions then + b.r := ropef('($1) newObj(sizeof($2))', + [getTypeDesc(p.module, reftype), + getTypeDesc(p.module, skipGenericRange(reftype.sons[0]))]) + else + b.r := ropef('($1) newObj($2, sizeof($3))', + [getTypeDesc(p.module, reftype), genTypeInfo(p.module, refType), + getTypeDesc(p.module, skipGenericRange(reftype.sons[0]))]); genAssignment(p, a, b, {@set}[]); // set the object type: bt := skipGenericRange(refType.sons[0]); @@ -1275,10 +1279,16 @@ begin InitLocExpr(p, e.sons[1], a); InitLocExpr(p, e.sons[2], b); initLoc(c, locExpr, a.t, OnHeap); - c.r := ropef('($1) newSeq($2, $3)', - [getTypeDesc(p.module, seqtype), - genTypeInfo(p.module, seqType), - rdLoc(b)]); + if optBoehmGC in gGlobalOptions then + c.r := ropef('($1) newSeq(sizeof($2), $3)', + [getTypeDesc(p.module, seqtype), + getTypeDesc(p.module, skipGenericRange(seqtype.sons[0])), + rdLoc(b)]) + else + c.r := ropef('($1) newSeq($2, $3)', + [getTypeDesc(p.module, seqtype), + genTypeInfo(p.module, seqType), + rdLoc(b)]); genAssignment(p, a, c, {@set}[]); end; @@ -1324,7 +1334,8 @@ begin refType := skipVarGenericRange(e.sons[1].typ); InitLocExpr(p, e.sons[1], a); - // This is a little hack: + // This is a little hack: + // XXX this is also a bug, if the finalizer expression produces side-effects oldModule := p.module; p.module := gmti; InitLocExpr(p, e.sons[2], f); @@ -2290,7 +2301,7 @@ begin len := sonsLen(n); result := toRope('{'+''); for i := 0 to len - 2 do - app(result, ropef('$1,$n', [genConstExpr(p, n.sons[i])])); + appf(result, '$1,$n', [genConstExpr(p, n.sons[i])]); if len > 0 then app(result, genConstExpr(p, n.sons[len-1])); app(result, '}' + tnl) end; @@ -2299,6 +2310,7 @@ function genConstExpr(p: BProc; n: PNode): PRope; var trans: PNode; cs: TBitSet; + d: TLoc; begin case n.Kind of nkHiddenStdConv, nkHiddenSubConv: result := genConstExpr(p, n.sons[1]); @@ -2317,7 +2329,10 @@ begin trans := n; result := genConstSimpleList(p, trans); end - else - result := genLiteral(p, n) + else begin + // result := genLiteral(p, n) + initLocExpr(p, n, d); + result := rdLoc(d) + end end end; diff --git a/nim/ccgstmts.pas b/nim/ccgstmts.pas index e611bbeea..7588f7e15 100644 --- a/nim/ccgstmts.pas +++ b/nim/ccgstmts.pas @@ -1,7 +1,7 @@ // // // The Nimrod Compiler -// (c) Copyright 2008 Andreas Rumpf +// (c) Copyright 2009 Andreas Rumpf // // See the file "copying.txt", included in this // distribution, for details about the copyright. @@ -18,25 +18,41 @@ begin line := toLinenumber(t.info); // BUGFIX if line < 0 then line := 0; // negative numbers are not allowed in #line if optLineDir in p.Options then - appf(p.s[cpsStmts], '#line $2 "$1"$n', + appff(p.s[cpsStmts], + '#line $2 "$1"$n', + '; line $2 "$1"$n', [toRope(toFilename(t.info)), toRope(line)]); if ([optStackTrace, optEndb] * p.Options = [optStackTrace, optEndb]) and ((p.prc = nil) or not (sfPure in p.prc.flags)) then begin useMagic(p.module, 'endb'); // new: endb support - appf(p.s[cpsStmts], 'endb($1);$n', [toRope(line)]) + appff(p.s[cpsStmts], 'endb($1);$n', + 'call void @endb(%NI $1)$n', + [toRope(line)]) end else if ([optLineTrace, optStackTrace] * p.Options = [optLineTrace, optStackTrace]) and ((p.prc = nil) or - not (sfPure in p.prc.flags)) then - appf(p.s[cpsStmts], 'F.line = $1;$n', [toRope(line)]) + not (sfPure in p.prc.flags)) then begin + inc(p.labels); + appff(p.s[cpsStmts], 'F.line = $1;$n', + '%LOC$2 = getelementptr %TF %F, %NI 2$n' + + 'store %NI $1, %NI* %LOC$2$n', + [toRope(line), toRope(p.labels)]) + end end; procedure finishTryStmt(p: BProc; howMany: int); var i: int; begin - for i := 1 to howMany do - app(p.s[cpsStmts], 'excHandler = excHandler->prev;' + tnl); + for i := 1 to howMany do begin + inc(p.labels, 3); + appff(p.s[cpsStmts], 'excHandler = excHandler->prev;$n', + '%LOC$1 = load %TSafePoint** @excHandler$n' + + '%LOC$2 = getelementptr %TSafePoint* %LOC$1, %NI 0$n' + + '%LOC$3 = load %TSafePoint** %LOC$2$n' + + 'store %TSafePoint* %LOC$3, %TSafePoint** @excHandler$n', + [toRope(p.labels), toRope(p.labels-1), toRope(p.labels-2)]); + end end; procedure genReturnStmt(p: BProc; t: PNode); @@ -45,7 +61,7 @@ begin genLineDir(p, t); if (t.sons[0] <> nil) then genStmts(p, t.sons[0]); finishTryStmt(p, p.nestedTryStmts); - app(p.s[cpsStmts], 'goto BeforeRet;' + tnl) + appff(p.s[cpsStmts], 'goto BeforeRet;$n', 'br label %BeforeRet$n', []) end; procedure initVariable(p: BProc; v: PSym); @@ -53,11 +69,29 @@ begin if containsGarbageCollectedRef(v.typ) or (v.ast = nil) then // Language change: always initialize variables if v.ast == nil! if not (skipVarGenericRange(v.typ).Kind in [tyArray, tyArrayConstr, tySet, - tyTuple, tyObject]) then - appf(p.s[cpsStmts], '$1 = 0;$n', [rdLoc(v.loc)]) - else - appf(p.s[cpsStmts], 'memset((void*)$1, 0, sizeof($2));$n', - [addrLoc(v.loc), rdLoc(v.loc)]) + tyTuple, tyObject]) then begin + if gCmd = cmdCompileToLLVM then + appf(p.s[cpsStmts], 'store $2 0, $2* $1$n', + [addrLoc(v.loc), getTypeDesc(p.module, v.loc.t)]) + else + appf(p.s[cpsStmts], '$1 = 0;$n', [rdLoc(v.loc)]) + end + else begin + if gCmd = cmdCompileToLLVM then begin + app(p.module.s[cfsProcHeaders], + 'declare void @llvm.memset.i32(i8*, i8, i32, i32)' + tnl); + inc(p.labels, 2); + appf(p.s[cpsStmts], + '%LOC$3 = getelementptr $2* null, %NI 1$n' + + '%LOC$4 = cast $2* %LOC$3 to i32$n' + + 'call void @llvm.memset.i32(i8* $1, i8 0, i32 %LOC$4, i32 0)$n', + [addrLoc(v.loc), getTypeDesc(p.module, v.loc.t), + toRope(p.labels), toRope(p.labels-1)]) + end + else + appf(p.s[cpsStmts], 'memset((void*)$1, 0, sizeof($2));$n', + [addrLoc(v.loc), rdLoc(v.loc)]) + end end; procedure genVarStmt(p: BProc; n: PNode); @@ -73,7 +107,7 @@ begin assert(a.sons[0].kind = nkSym); v := a.sons[0].sym; if sfGlobal in v.flags then - assignGlobalVar(p.module, v) + assignGlobalVar(p, v) else begin assignLocalVar(p, v); initVariable(p, v) // XXX: this is not required if a.sons[2] != nil, @@ -140,10 +174,14 @@ begin nkElifBranch: begin initLocExpr(p, it.sons[0], a); Lelse := getLabel(p); - appf(p.s[cpsStmts], 'if (!$1) goto $2;$n', [rdLoc(a), Lelse]); + inc(p.labels); + appff(p.s[cpsStmts], 'if (!$1) goto $2;$n', + 'br i1 $1, label %LOC$3, label %$2$n' + + 'LOC$3: $n', + [rdLoc(a), Lelse, toRope(p.labels)]); genStmts(p, it.sons[1]); if sonsLen(n) > 1 then - appf(p.s[cpsStmts], 'goto $1;$n', [Lend]); + appff(p.s[cpsStmts], 'goto $1;$n', 'br label %$1$n', [Lend]); fixLabel(p, Lelse); end; nkElse: begin @@ -857,7 +895,7 @@ begin nkCaseStmt: genCaseStmt(p, t); nkReturnStmt: genReturnStmt(p, t); nkBreakStmt: genBreakStmt(p, t); - nkCall: begin + nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand: begin genLineDir(p, t); initLocExpr(p, t, a); end; diff --git a/nim/ccgtypes.pas b/nim/ccgtypes.pas index f6eb64c08..ee7388e1c 100644 --- a/nim/ccgtypes.pas +++ b/nim/ccgtypes.pas @@ -1,7 +1,7 @@ // // // The Nimrod Compiler -// (c) Copyright 2008 Andreas Rumpf +// (c) Copyright 2009 Andreas Rumpf // // See the file "copying.txt", included in this // distribution, for details about the copyright. @@ -45,7 +45,19 @@ function mangleName(s: PSym): PRope; begin result := s.loc.r; if result = nil then begin - result := toRope(mangle(s.name.s)); + if gCmd = cmdCompileToLLVM then begin + case s.kind of + skProc, skConverter, skConst: result := toRope('@'+''); + skVar: begin + if (sfGlobal in s.flags) then result := toRope('@'+'') + else result := toRope('%'+''); + end; + skForVar, skTemp, skParam, skType, skEnumField, skModule: + result := toRope('%'+''); + else InternalError(s.info, 'mangleName'); + end; + end; + app(result, toRope(mangle(s.name.s))); app(result, '_'+''); app(result, toRope(s.id)); if optGenMapping in gGlobalOptions then @@ -58,15 +70,33 @@ end; function getTypeName(typ: PType): PRope; begin - if (typ.sym <> nil) and ([sfImportc, sfExportc] * typ.sym.flags <> []) then + if (typ.sym <> nil) and ([sfImportc, sfExportc] * typ.sym.flags <> []) + and (gCmd <> cmdCompileToLLVM) then result := typ.sym.loc.r else begin - if typ.loc.r = nil then typ.loc.r := con('TY', toRope(typ.id)); + if typ.loc.r = nil then + typ.loc.r := ropeff('TY$1', '%TY$1', [toRope(typ.id)]); result := typ.loc.r end; if result = nil then InternalError('getTypeName: ' + typeKindToStr[typ.kind]); end; +// ----------------------------- other helpers ---------------------------- +(* +function getSizeof(m: BModule; var labels: int; + var body: PRope; typ: PType): PRope; +begin + if (gCmd <> cmdCompileToLLVM) then + result := ropef('sizeof($1)', getTypeDesc(m, typ)) + else begin + inc(labels, 2); + result := ropef('%UOC$1', [toRope(labels)]); + appf(body, '%UOC$1 = getelementptr $3* null, %NI 1$n' + + '$2 = cast $3* %UOC$1 to i32$n', + [toRope(labels-1), result, getTypeDesc(m, typ)]); + end +end; *) + // ------------------------------ C type generator ------------------------ function mapType(typ: PType): TCTypeKind; @@ -104,10 +134,6 @@ begin tyPtr, tyVar, tyRef: begin case typ.sons[0].kind of tyOpenArray, tyArrayConstr, tyArray: result := ctArray; - (*tySet: begin - if mapType(typ.sons[0]) = ctArray then result := ctArray - else result := ctPtr - end*) else result := ctPtr end end; @@ -155,6 +181,10 @@ const // but one can //define it to what you want so there will no problem 'N_INLINE', 'N_NOINLINE', 'N_FASTCALL', 'N_CLOSURE', 'N_NOCONV'); + CallingConvToStrLLVM: array [TCallingConvention] of string = ('fastcc $1', + 'stdcall $1', 'ccc $1', 'safecall $1', 'syscall $1', + '$1 alwaysinline', '$1 noinline', 'fastcc $1', 'ccc $1', '$1'); + function CacheGetType(const tab: TIdTable; key: PType): PRope; begin // returns nil if we need to declare this type @@ -165,7 +195,13 @@ end; function getTempName(): PRope; begin - result := con('TMP', toRope(gId)); + result := ropeff('TMP$1', '%TMP$1', [toRope(gId)]); + inc(gId); +end; + +function getGlobalTempName(): PRope; +begin + result := ropeff('TMP$1', '@TMP$1', [toRope(gId)]); inc(gId); end; @@ -194,7 +230,8 @@ end; procedure fillResult(param: PSym); begin - fillLoc(param.loc, locParam, param.typ, toRope('Result'), OnStack); + fillLoc(param.loc, locParam, param.typ, ropeff('Result', '%Result', []), + OnStack); if (mapType(param.typ) <> ctArray) and IsInvalidReturnType(param.typ) then begin include(param.loc.flags, lfIndirect); @@ -232,7 +269,7 @@ begin if arr.kind = tyVar then arr := arr.sons[0]; j := 0; while arr.Kind = tyOpenArray do begin // need to pass hidden parameter: - appf(params, ', NI $1Len$2', [param.loc.r, toRope(j)]); + appff(params, ', NI $1Len$2', ', @NI $1Len$2', [param.loc.r, toRope(j)]); inc(j); arr := arr.sons[0] end; @@ -241,8 +278,9 @@ begin if (t.sons[0] <> nil) and isInvalidReturnType(t.sons[0]) then begin if params <> nil then app(params, ', '); app(params, getTypeDescAux(m, t.sons[0], check)); - if mapType(t.sons[0]) <> ctArray then app(params, '*'+''); - app(params, ' Result'); + if (mapType(t.sons[0]) <> ctArray) or (gCmd = cmdCompileToLLVM) then + app(params, '*'+''); + appff(params, ' Result', ' @Result', []); end; if t.callConv = ccClosure then begin if params <> nil then app(params, ', '); @@ -252,7 +290,7 @@ begin if params <> nil then app(params, ', '); app(params, '...') end; - if params = nil then + if (params = nil) and (gCmd <> cmdCompileToLLVM) then app(params, 'void)') else app(params, ')'+''); @@ -621,7 +659,7 @@ begin end end; -function getTypeDesc(m: BModule; typ: PType): PRope; +function getTypeDesc(m: BModule; typ: PType): PRope; overload; var check: TIntSet; begin @@ -629,6 +667,19 @@ begin result := getTypeDescAux(m, typ, check); end; +function getTypeDesc(m: BModule; const magic: string): PRope; overload; +var + sym: PSym; +begin + sym := magicsys.getCompilerProc(magic); + if sym <> nil then + result := getTypeDesc(m, sym.typ) + else begin + rawMessage(errSystemNeeds, magic); + result := nil + end +end; + procedure finishTypeDescriptions(m: BModule); var i: int; @@ -1012,4 +1063,4 @@ begin end end end; -*) \ No newline at end of file +*) diff --git a/nim/ccgutils.pas b/nim/ccgutils.pas index 97ef65f70..56eff6c9e 100644 --- a/nim/ccgutils.pas +++ b/nim/ccgutils.pas @@ -20,6 +20,7 @@ uses function toCChar(c: Char): string; function makeCString(const s: string): PRope; +function makeLLVMString(const s: string): PRope; function TableGetType(const tab: TIdTable; key: PType): PObject; function GetUniqueType(key: PType): PType; @@ -154,6 +155,33 @@ begin app(result, toRope(res)); end; +function makeLLVMString(const s: string): PRope; +const + MaxLineLength = 64; +var + i: int; + res: string; +begin + result := nil; + res := 'c"'; + for i := strStart to length(s)+strStart-1 do begin + if (i-strStart+1) mod MaxLineLength = 0 then begin + app(result, toRope(res)); + setLength(res, 0); + end; + case s[i] of + #0..#31, #128..#255, '"', '\': begin + addChar(res, '\'); + add(res, toHex(ord(s[i]), 2)); + end + else + addChar(res, s[i]) + end; + end; + add(res, '\00"'); + app(result, toRope(res)); +end; + begin InitTypeTables(); end. diff --git a/nim/cgen.pas b/nim/cgen.pas index 02713f902..5dcb7f50b 100644 --- a/nim/cgen.pas +++ b/nim/cgen.pas @@ -37,9 +37,9 @@ type // 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 - 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 2 for initialization of type information @@ -120,6 +120,7 @@ type 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 end; var @@ -132,6 +133,24 @@ var gForwardedProcsCounter: int = 0; gmti: BModule; // generated type info: no need to initialize: defaults fit +function ropeff(const cformat, llvmformat: string; + const args: array of PRope): PRope; +begin + if gCmd = cmdCompileToLLVM then + result := ropef(llvmformat, args) + else + result := ropef(cformat, args) +end; + +procedure appff(var dest: PRope; const cformat, llvmformat: string; + const args: array of PRope); +begin + if gCmd = cmdCompileToLLVM then + appf(dest, llvmformat, args) + else + appf(dest, cformat, args); +end; + procedure addForwardedProc(m: BModule; prc: PSym); var L: int; @@ -240,8 +259,12 @@ end; procedure getTemp(p: BProc; t: PType; var result: TLoc); begin inc(p.labels); - result.r := con('LOC', toRope(p.labels)); - appf(p.s[cpsLocals], '$1 $2;$n', [getTypeDesc(p.module, t), result.r]); + if gCmd = cmdCompileToLLVM then + result.r := con('%LOC', toRope(p.labels)) + else begin + result.r := con('LOC', toRope(p.labels)); + appf(p.s[cpsLocals], '$1 $2;$n', [getTypeDesc(p.module, t), result.r]); + end; result.k := locTemp; result.a := -1; result.t := getUniqueType(t); @@ -251,6 +274,86 @@ end; // -------------------------- Variable manager ---------------------------- +function cstringLit(p: BProc; var r: PRope; const s: string): PRope; overload; +begin + if gCmd = cmdCompileToLLVM then begin + inc(p.module.labels); + inc(p.labels); + result := ropef('%LOC$1', [toRope(p.labels)]); + appf(p.module.s[cfsData], '@C$1 = private constant [$2 x i8] $3$n', [ + toRope(p.module.labels), toRope(length(s)), makeLLVMString(s)]); + appf(r, '$1 = getelementptr [$2 x i8]* @C$3, %NI 0, %NI 0$n', + [result, toRope(length(s)), toRope(p.module.labels)]); + end + else + result := makeCString(s) +end; + +function cstringLit(m: BModule; var r: PRope; const s: string): PRope; overload; +begin + if gCmd = cmdCompileToLLVM then begin + inc(m.labels, 2); + result := ropef('%MOC$1', [toRope(m.labels-1)]); + appf(m.s[cfsData], '@MOC$1 = private constant [$2 x i8] $3$n', [ + toRope(m.labels), toRope(length(s)), makeLLVMString(s)]); + appf(r, '$1 = getelementptr [$2 x i8]* @MOC$3, %NI 0, %NI 0$n', + [result, toRope(length(s)), toRope(m.labels)]); + end + else + result := makeCString(s) +end; + +procedure allocParam(p: BProc; s: PSym); +var + tmp: PRope; +begin + assert(s.kind = skParam); + if not (lfParamCopy in s.loc.flags) then begin + inc(p.labels); + tmp := con('%LOC', toRope(p.labels)); + include(s.loc.flags, lfParamCopy); + include(s.loc.flags, lfIndirect); + appf(p.s[cpsInit], + '$1 = alloca $3$n' + + 'store $3 $2, $3* $1$n', [tmp, s.loc.r, getTypeDesc(p.module, s.loc.t)]); + s.loc.r := tmp + end; +end; + +procedure localDebugInfo(p: BProc; s: PSym); +var + name, a: PRope; +begin + if [optStackTrace, optEndb] * p.options <> [optStackTrace, optEndb] then exit; + if gCmd = cmdCompileToLLVM then begin + // "address" is the 0th field + // "typ" is the 1rst field + // "name" is the 2nd field + name := cstringLit(p, p.s[cpsInit], normalize(s.name.s)); + if (s.kind = skParam) and not ccgIntroducedPtr(s) then allocParam(p, s); + inc(p.labels, 3); + appf(p.s[cpsInit], + '%LOC$6 = getelementptr %TF* %F, %NI 0, $1, %NI 0$n' + + '%LOC$7 = getelementptr %TF* %F, %NI 0, $1, %NI 1$n' + + '%LOC$8 = getelementptr %TF* %F, %NI 0, $1, %NI 2$n' + + 'store i8* $2, i8** %LOC$6$n' + + 'store $3* $4, $3** %LOC$7$n' + + 'store i8* $5, i8** %LOC$8$n', + [toRope(p.frameLen), s.loc.r, getTypeDesc(p.module, 'TNimType'), + genTypeInfo(p.module, s.loc.t), name, toRope(p.labels), + toRope(p.labels-1), toRope(p.labels-2)]) + end + else begin + a := con('&'+'', s.loc.r); + if (s.kind = skParam) and ccgIntroducedPtr(s) then a := s.loc.r; + appf(p.s[cpsInit], + 'F.s[$1].address = (void*)$3; F.s[$1].typ = $4; F.s[$1].name = $2;$n', + [toRope(p.frameLen), makeCString(normalize(s.name.s)), a, + genTypeInfo(p.module, s.loc.t)]); + end; + inc(p.frameLen); +end; + procedure assignLocalVar(p: BProc; s: PSym); begin //assert(s.loc.k == locNone) // not yet assigned @@ -258,45 +361,54 @@ begin // for each module that uses them! if s.loc.k = locNone then fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack); - app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t)); - if sfRegister in s.flags then - app(p.s[cpsLocals], ' register'); - if (sfVolatile in s.flags) or (p.nestedTryStmts > 0) then - app(p.s[cpsLocals], ' volatile'); + if gCmd = cmdCompileToLLVM then begin + appf(p.s[cpsLocals], '$1 = alloca $2$n', + [s.loc.r, getTypeDesc(p.module, s.loc.t)]); + include(s.loc.flags, lfIndirect); + end + else begin + app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t)); + if sfRegister in s.flags then + app(p.s[cpsLocals], ' register'); + if (sfVolatile in s.flags) or (p.nestedTryStmts > 0) then + app(p.s[cpsLocals], ' volatile'); - appf(p.s[cpsLocals], ' $1;$n', [s.loc.r]); + appf(p.s[cpsLocals], ' $1;$n', [s.loc.r]); + end; // if debugging we need a new slot for the local variable: - if [optStackTrace, optEndb] * p.Options = [optStackTrace, optEndb] then begin - appf(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(p.module, s.loc.t)]); - inc(p.frameLen); - end + localDebugInfo(p, s); end; -procedure assignGlobalVar(m: BModule; s: PSym); +procedure assignGlobalVar(p: BProc; s: PSym); begin if s.loc.k = locNone then fillLoc(s.loc, locGlobalVar, s.typ, mangleName(s), OnHeap); - useHeader(m, s); - if lfNoDecl in s.loc.flags then exit; - if sfImportc in s.flags then app(m.s[cfsVars], 'extern '); - app(m.s[cfsVars], getTypeDesc(m, s.loc.t)); - if sfRegister in s.flags then - app(m.s[cfsVars], ' register'); - if sfVolatile in s.flags then - app(m.s[cfsVars], ' volatile'); - if sfThreadVar in s.flags then - app(m.s[cfsVars], ' NIM_THREADVAR'); - appf(m.s[cfsVars], ' $1;$n', [s.loc.r]); - if [optStackTrace, optEndb] * m.module.options = + if gCmd = cmdCompileToLLVM then begin + appf(p.module.s[cfsVars], '$1 = linkonce global $2 zeroinitializer$n', + [s.loc.r, getTypeDesc(p.module, s.loc.t)]); + include(s.loc.flags, lfIndirect); + end + else begin + useHeader(p.module, s); + if lfNoDecl in s.loc.flags then exit; + if sfImportc in s.flags then app(p.module.s[cfsVars], 'extern '); + app(p.module.s[cfsVars], getTypeDesc(p.module, s.loc.t)); + if sfRegister in s.flags then app(p.module.s[cfsVars], ' register'); + if sfVolatile in s.flags then app(p.module.s[cfsVars], ' volatile'); + if sfThreadVar in s.flags then app(p.module.s[cfsVars], ' NIM_THREADVAR'); + appf(p.module.s[cfsVars], ' $1;$n', [s.loc.r]); + end; + if [optStackTrace, optEndb] * p.module.module.options = [optStackTrace, optEndb] then begin - useMagic(m, 'dbgRegisterGlobal'); - appf(m.s[cfsDebugInit], + useMagic(p.module, 'dbgRegisterGlobal'); + appff(p.module.s[cfsDebugInit], 'dbgRegisterGlobal($1, &$2, $3);$n', - [makeCString(normalize(s.owner.name.s + '.' +{&} s.name.s)), s.loc.r, - genTypeInfo(m, s.typ)]) + 'call void @dbgRegisterGlobal(i8* $1, i8* $2, $4* $3)$n', + [cstringLit(p, p.module.s[cfsDebugInit], + normalize(s.owner.name.s + '.' +{&} s.name.s)), + s.loc.r, + genTypeInfo(p.module, s.typ), + getTypeDesc(p.module, 'TNimType')]); end; end; @@ -308,15 +420,9 @@ end; procedure assignParam(p: BProc; s: PSym); begin assert(s.loc.r <> nil); - if [optStackTrace, optEndb] * p.options = [optStackTrace, optEndb] then begin - appf(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(ccgIntroducedPtr(s), s.loc.r, con('&'+'', s.loc.r)), - genTypeInfo(p.module, s.loc.t)]); - inc(p.frameLen) - end + if (sfAddrTaken in s.flags) and (gCmd = cmdCompileToLLVM) then + allocParam(p, s); + localDebugInfo(p, s); end; procedure fillProcLoc(sym: PSym); @@ -359,19 +465,26 @@ begin assert(lib <> nil); if not lib.generated then begin lib.generated := true; + tmp := getGlobalTempName(); + assert(lib.name = nil); + lib.name := tmp; + // BUGFIX: useMagic has awful side-effects + appff(m.s[cfsVars], 'static void* $1;$n', + '$1 = linkonce global i8* zeroinitializer$n', [tmp]); + inc(m.labels); + appff(m.s[cfsDynLibInit], + '$1 = nimLoadLibrary((NimStringDesc*) &$2);$n', + '%MOC$4 = call i8* @nimLoadLibrary($3 $2)$n' + + 'store i8* %MOC$4, i8** $1$n', + [tmp, getStrLit(m, lib.path), getTypeDesc(m, getSysType(tyString)), + toRope(m.labels)]); + //appf(m.s[cfsDynLibDeinit], + // 'if ($1 != NIM_NIL) nimUnloadLibrary($1);$n', [tmp]); useMagic(m, 'nimLoadLibrary'); useMagic(m, 'nimUnloadLibrary'); useMagic(m, 'NimStringDesc'); - tmp := getTempName(); - appf(m.s[cfsVars], 'static void* $1;$n', [tmp]); - appf(m.s[cfsDynLibInit], - '$1 = nimLoadLibrary((NimStringDesc*) &$2);$n', - [tmp, getStrLit(m, lib.path)]); - appf(m.s[cfsDynLibDeinit], - 'if ($1 != NIM_NIL) nimUnloadLibrary($1);$n', [tmp]); - assert(lib.name = nil); - lib.name := tmp - end + end; + if lib.name = nil then InternalError('loadDynamicLib'); end; procedure SymInDynamicLib(m: BModule; sym: PSym); @@ -383,14 +496,25 @@ begin extname := sym.loc.r; loadDynamicLib(m, lib); useMagic(m, 'nimGetProcAddr'); - tmp := ropef('Dl_$1', [toRope(sym.id)]); + if gCmd = cmdCompileToLLVM then include(sym.loc.flags, lfIndirect); + + tmp := ropeff('Dl_$1', '@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 - appf(m.s[cfsDynLibInit], '$1 = ($2) nimGetProcAddr($3, $4);$n', - [tmp, getTypeDesc(m, sym.typ), lib.name, makeCString(ropeToStr(extname))]); - - app(m.s[cfsVars], getTypeDesc(m, sym.loc.t)); - appf(m.s[cfsVars], ' $1;$n', [sym.loc.r]); + inc(m.labels, 2); + appff(m.s[cfsDynLibInit], + '$1 = ($2) nimGetProcAddr($3, $4);$n', + '%MOC$5 = load i8* $3$n' + + '%MOC$6 = call $2 @nimGetProcAddr(i8* %MOC$5, i8* $4)$n' + + 'store $2 %MOC$6, $2* $1$n', + [tmp, getTypeDesc(m, sym.typ), lib.name, + cstringLit(m, m.s[cfsDynLibInit], ropeToStr(extname)), + toRope(m.labels), toRope(m.labels-1)]); + + appff(m.s[cfsVars], + '$2 $1;$n', + '$1 = linkonce global $2 zeroinitializer$n', + [sym.loc.r, getTypeDesc(m, sym.loc.t)]); end; // ----------------------------- sections --------------------------------- @@ -433,14 +557,23 @@ var begin if p.frameLen > 0 then begin useMagic(p.module, 'TVarSlot'); - slots := ropef(' TVarSlot s[$1];$n', [toRope(p.frameLen)]) + slots := ropeff(' TVarSlot s[$1];$n', + ', [$1 x %TVarSlot]', [toRope(p.frameLen)]) end else slots := nil; - appf(p.s[cpsLocals], 'volatile struct {TFrame* prev;' + + appff(p.s[cpsLocals], + 'volatile struct {TFrame* prev;' + 'NCSTRING procname;NI line;NCSTRING filename;' + - 'NI len;$n$1} F;$n', [slots]); - prepend(p.s[cpsInit], ropef('F.len = $1;$n', [toRope(p.frameLen)])) + 'NI len;$n$1} F;$n', + '%TF = type {%TFrame*, i8*, %NI, %NI$1}$n' + + '%F = alloca %TF$n', + [slots]); + inc(p.labels); + prepend(p.s[cpsInit], ropeff('F.len = $1;$n', + '%LOC$2 = getelementptr %TF %F, %NI 4$n' + + 'store %NI $1, %NI* %LOC$2$n', + [toRope(p.frameLen), toRope(p.labels)])) end; function retIsNotVoid(s: PSym): bool; @@ -448,10 +581,48 @@ begin result := (s.typ.sons[0] <> nil) and not isInvalidReturnType(s.typ.sons[0]) end; +function initFrame(p: BProc; procname, filename: PRope): PRope; +begin + inc(p.labels, 5); + result := ropeff( + 'F.procname = $1;$n' + + 'F.prev = framePtr;$n' + + 'F.filename = $2;$n' + + 'F.line = 0;$n' + + 'framePtr = (TFrame*)&F;$n', + + '%LOC$3 = getelementptr %TF %F, %NI 1$n' + + '%LOC$4 = getelementptr %TF %F, %NI 0$n' + + '%LOC$5 = getelementptr %TF %F, %NI 3$n' + + '%LOC$6 = getelementptr %TF %F, %NI 2$n' + + + 'store i8* $1, i8** %LOC$3$n' + + 'store %TFrame* @framePtr, %TFrame** %LOC$4$n' + + 'store i8* $2, i8** %LOC$5$n' + + 'store %NI 0, %NI* %LOC$6$n' + + + '%LOC$7 = bitcast %TF* %F to %TFrame*$n' + + 'store %TFrame* %LOC$7, %TFrame** @framePtr$n', + [procname, filename, toRope(p.labels), toRope(p.labels-1), + toRope(p.labels-2), toRope(p.labels-3), toRope(p.labels-4)]); +end; + +function deinitFrame(p: BProc): PRope; +begin + inc(p.labels, 3); + result := ropeff('framePtr = framePtr->prev;$n', + + '%LOC$1 = load %TFrame* @framePtr$n' + + '%LOC$2 = getelementptr %TFrame* %LOC$1, %NI 0$n' + + '%LOC$3 = load %TFrame** %LOC$2$n' + + 'store %TFrame* $LOC$3, %TFrame** @framePtr', [ + toRope(p.labels), toRope(p.labels-1), toRope(p.labels-2)]) +end; + procedure genProcAux(m: BModule; prc: PSym); var p: BProc; - generatedProc, header, returnStmt: PRope; + generatedProc, header, returnStmt, procname, filename: PRope; i: int; res, param: PSym; begin @@ -466,7 +637,7 @@ begin // declare the result symbol: assignLocalVar(p, res); assert(res.loc.r <> nil); - returnStmt := ropef('return $1;$n', [rdLoc(res.loc)]); + returnStmt := ropeff('return $1;$n', 'ret $1$n', [rdLoc(res.loc)]); end else begin fillResult(res); @@ -482,22 +653,21 @@ begin genStmts(p, prc.ast.sons[codePos]); // modifies p.locals, p.init, etc. if sfPure in prc.flags then - generatedProc := ropef('$1 {$n$2$3$4}$n', + generatedProc := ropeff('$1 {$n$2$3$4}$n', 'define $1 {$n$2$3$4}$n', [header, p.s[cpsLocals], p.s[cpsInit], p.s[cpsStmts]]) else begin - generatedProc := con(header, '{' + tnl); + generatedProc := ropeff('$1 {$n', 'define $1 {$n', [header]); if optStackTrace in prc.options then begin getFrameDecl(p); - prepend(p.s[cpsInit], ropef( - '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; - if optProfiler in prc.options then begin + app(generatedProc, p.s[cpsLocals]); + procname := CStringLit(p, generatedProc, + prc.owner.name.s +{&} '.' +{&} prc.name.s); + filename := CStringLit(p, generatedProc, toFilename(prc.info)); + app(generatedProc, initFrame(p, procname, filename)); + end + else + app(generatedProc, p.s[cpsLocals]); + if (optProfiler in prc.options) and (gCmd <> cmdCompileToLLVM) then begin if gProcProfile >= 64*1024 then // XXX: hard coded value! InternalError(prc.info, 'too many procedures for profiling'); useMagic(m, 'profileData'); @@ -511,12 +681,13 @@ begin end; prepend(p.s[cpsInit], toRope('NIM_profilingStart = getticks();' + tnl)); end; - app(generatedProc, con(p.s)); + app(generatedProc, p.s[cpsInit]); + app(generatedProc, p.s[cpsStmts]); if p.beforeRetNeeded then app(generatedProc, 'BeforeRet: ;' + tnl); if optStackTrace in prc.options then - app(generatedProc, 'framePtr = framePtr->prev;' + tnl); - if optProfiler in prc.options then + app(generatedProc, deinitFrame(p)); + if (optProfiler in prc.options) and (gCmd <> cmdCompileToLLVM) then appf(generatedProc, 'profileData[$1].total += elapsed(getticks(), NIM_profilingStart);$n', [toRope(prc.loc.a)]); @@ -533,8 +704,10 @@ begin if lfDynamicLib in sym.loc.Flags then begin if (sym.owner.id <> m.module.id) and not intSetContainsOrIncl(m.declaredThings, sym.id) then begin - appf(m.s[cfsVars], 'extern $1 Dl_$2;$n', - [getTypeDesc(m, sym.loc.t), toRope(sym.id)]) + appff(m.s[cfsVars], 'extern $1 Dl_$2;$n', + '@Dl_$2 = linkonce global $1 zeroinitializer$n', + [getTypeDesc(m, sym.loc.t), toRope(sym.id)]); + if gCmd = cmdCompileToLLVM then include(sym.loc.flags, lfIndirect); end end else begin @@ -586,15 +759,22 @@ begin if sym.owner.id <> m.module.id then begin // else we already have the symbol generated! assert(sym.loc.r <> nil); - app(m.s[cfsVars], 'extern '); - app(m.s[cfsVars], getTypeDesc(m, sym.loc.t)); - if sfRegister in sym.flags then - app(m.s[cfsVars], ' register'); - if sfVolatile in sym.flags then - app(m.s[cfsVars], ' volatile'); - if sfThreadVar in sym.flags then - app(m.s[cfsVars], ' NIM_THREADVAR'); - appf(m.s[cfsVars], ' $1;$n', [sym.loc.r]) + if gCmd = cmdCompileToLLVM then begin + include(sym.loc.flags, lfIndirect); + appf(m.s[cfsVars], '$1 = linkonce global $2 zeroinitializer$n', + [sym.loc.r, getTypeDesc(m, sym.loc.t)]); + end + else begin + app(m.s[cfsVars], 'extern '); + app(m.s[cfsVars], getTypeDesc(m, sym.loc.t)); + if sfRegister in sym.flags then + app(m.s[cfsVars], ' register'); + if sfVolatile in sym.flags then + app(m.s[cfsVars], ' volatile'); + if sfThreadVar in sym.flags then + app(m.s[cfsVars], ' NIM_THREADVAR'); + appf(m.s[cfsVars], ' $1;$n', [sym.loc.r]) + end end end; @@ -609,8 +789,9 @@ begin if sym.owner.id <> m.module.id then begin // else we already have the symbol generated! assert(sym.loc.r <> nil); - app(m.s[cfsData], 'extern '); - appf(m.s[cfsData], 'NIM_CONST $1 $2;$n', + appff(m.s[cfsData], + 'extern NIM_CONST $1 $2;$n', + '$1 = linkonce constant $2 zeroinitializer', [getTypeDesc(m, sym.loc.t), sym.loc.r]) end end; @@ -618,27 +799,36 @@ end; function getFileHeader(const cfilenoext: string): PRope; begin if optCompileOnly in gGlobalOptions then - result := ropef( + result := ropeff( '/* Generated by the Nimrod Compiler v$1 */$n' + '/* (c) 2008 Andreas Rumpf */$n', + '; Generated by the Nimrod Compiler v$1$n' + + '; (c) 2008 Andreas Rumpf$n', [toRope(versionAsString)]) else - result := ropef( + result := ropeff( '/* Generated by the Nimrod Compiler v$1 */$n' + '/* (c) 2008 Andreas Rumpf */$n' + '/* Compiled for: $2, $3, $4 */$n' + '/* Command for C compiler:$n $5 */$n', + '; Generated by the Nimrod Compiler v$1$n' + + '; (c) 2008 Andreas Rumpf$n' + + '; Compiled for: $2, $3, $4$n' + + '; Command for C compiler:$n $5$n', [toRope(versionAsString), toRope(platform.OS[targetOS].name), toRope(platform.CPU[targetCPU].name), toRope(extccomp.CC[extccomp.ccompiler].name), toRope(getCompileCFileCmd(cfilenoext))]); case platform.CPU[targetCPU].intSize of - 16: appf(result, '$ntypedef short int NI;$n' + - 'typedef unsigned short int NU;$n', []); - 32: appf(result, '$ntypedef long int NI;$n' + - 'typedef unsigned long int NU;$n', []); - 64: appf(result, '$ntypedef long long int NI;$n' + - 'typedef unsigned long long int NU;$n', []); + 16: appff(result, '$ntypedef short int NI;$n' + + 'typedef unsigned short int NU;$n', + '$n%NI = type i16$n', []); + 32: appff(result, '$ntypedef long int NI;$n' + + 'typedef unsigned long int NU;$n', + '$n%NI = type i32$n', []); + 64: appff(result, '$ntypedef long long int NI;$n' + + 'typedef unsigned long long int NU;$n', + '$n%NI = type i64$n', []); else begin end end end; @@ -651,18 +841,37 @@ const ' systemInit();$n' + '$1' + '$2'; + CommonMainBodyLLVM = + ' %MOC$3 = bitcast [8 x %NI]* %dummy to i8*$n' + + ' call void @setStackBottom(i8* %MOC$3)$n' + + ' call void @nim__datInit()$n' + + ' call void systemInit()$n' + + '$1' + + '$2'; PosixMain = - 'NI cmdCount;$n' + + 'int 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 = (NI)argc;$n' + + ' cmdCount = argc;$n' + ' gEnv = env;$n' +{&} CommonMainBody +{&} ' return 0;$n' + '}$n'; + PosixMainLLVM = + '@cmdCount = linkonce i32$n' + + '@cmdLine = linkonce i8**$n' + + '@gEnv = linkonce i8**$n' + + 'define i32 @main(i32 %argc, i8** %args, i8** %env) {$n' + + ' %dummy = alloca [8 x %NI]$n' + + ' store i8** %args, i8*** @cmdLine$n' + + ' store i32 %argc, i32* @cmdCount$n' + + ' store i8** %env, i8*** @gEnv$n' +{&} + CommonMainBodyLLVM +{&} + ' ret i32 0$n' + + '}$n'; WinMain = 'N_STDCALL(int, WinMain)(HINSTANCE hCurInstance, $n' + ' HINSTANCE hPrevInstance, $n' + @@ -671,6 +880,14 @@ const CommonMainBody +{&} ' return 0;$n' + '}$n'; + WinMainLLVM = + 'define stdcall i32 @WinMain(i32 %hCurInstance, $n' + + ' i32 %hPrevInstance, $n' + + ' i8* %lpCmdLine, i32 %nCmdShow) {$n' + + ' %dummy = alloca [8 x %NI]$n' +{&} + CommonMainBodyLLVM +{&} + ' ret i32 0$n' + + '}$n'; WinDllMain = 'BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $n' + ' LPVOID lpvReserved) {$n' + @@ -678,6 +895,13 @@ const CommonMainBody +{&} ' return 1;$n' + '}$n'; + WinDllMainLLVM = + 'define stdcall i32 @DllMain(i32 %hinstDLL, i32 %fwdreason, $n' + + ' i8* %lpvReserved) {$n' + + ' %dummy = alloca [8 x %NI]$n' +{&} + CommonMainBodyLLVM +{&} + ' ret i32 1$n' + + '}$n'; var frmt: TFormatStr; begin @@ -685,21 +909,28 @@ begin if (platform.targetOS = osWindows) and (gGlobalOptions * [optGenGuiApp, optGenDynLib] <> []) then begin if optGenGuiApp in gGlobalOptions then - frmt := WinMain + if gCmd = cmdCompileToLLVM then frmt := WinMainLLVM else frmt := WinMain else - frmt := WinDllMain; + if gCmd = cmdCompileToLLVM then + frmt := WinDllMainLLVM + else + frmt := WinDllMain; {@discard} lists.IncludeStr(m.headerFiles, '<windows.h>') end - else - frmt := PosixMain; + else + if gCmd = cmdCompileToLLVM then + frmt := PosixMainLLVM + else + frmt := PosixMain; if gBreakpoints <> nil then useMagic(m, 'dbgRegisterBreakpoint'); - appf(m.s[cfsProcs], frmt, [gBreakpoints, mainModInit]) + inc(m.labels); + appf(m.s[cfsProcs], frmt, [gBreakpoints, mainModInit, toRope(m.labels)]) end; function getInitName(m: PSym): PRope; begin - result := con(m.name.s, toRope('Init')); + result := ropeff('$1Init', '@$1Init', [toRope(m.name.s)]); end; procedure registerModuleToMain(m: PSym); @@ -707,14 +938,15 @@ var initname: PRope; begin initname := getInitName(m); - appf(mainModProcs, 'N_NOINLINE(void, $1)(void);$n', [initname]); + appff(mainModProcs, 'N_NOINLINE(void, $1)(void);$n', + 'declare void $1() noinline$n', [initname]); if not (sfSystemModule in m.flags) then - appf(mainModInit, '$1();$n', [initname]); + appff(mainModInit, '$1();$n', 'call void ()* $1$n', [initname]); end; procedure genInitCode(m: BModule); var - initname, prc: PRope; + initname, prc, procname, filename: PRope; begin if optProfiler in m.initProc.options then begin // This does not really belong here, but there is no good place for this @@ -723,31 +955,28 @@ begin {@discard} lists.IncludeStr(m.headerFiles, '<cycle.h>'); end; initname := getInitName(m.module); - prc := ropef('N_NOINLINE(void, $1)(void) {$n', [initname]); - + prc := ropeff('N_NOINLINE(void, $1)(void) {$n', + 'define void $1() noinline {$n', [initname]); if m.typeNodes > 0 then begin useMagic(m, 'TNimNode'); - appf(m.s[cfsTypeInit1], 'static TNimNode $1[$2];$n', + appff(m.s[cfsTypeInit1], 'static TNimNode $1[$2];$n', + '$1 = private alloca [$2 x @TNimNode]$n', [m.typeNodesName, toRope(m.typeNodes)]); end; if m.nimTypes > 0 then begin useMagic(m, 'TNimType'); - appf(m.s[cfsTypeInit1], 'static TNimType $1[$2];$n', + appff(m.s[cfsTypeInit1], 'static TNimType $1[$2];$n', + '$1 = private alloca [$2 x @TNimType]$n', [m.nimTypesName, toRope(m.nimTypes)]); end; if optStackTrace in m.initProc.options then begin getFrameDecl(m.initProc); app(prc, m.initProc.s[cpsLocals]); app(prc, m.s[cfsTypeInit1]); - appf(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))]) + + procname := CStringLit(m.initProc, prc, 'module ' +{&} m.module.name.s); + filename := CStringLit(m.initProc, prc, toFilename(m.module.info)); + app(prc, initFrame(m.initProc, procname, filename)); end else begin app(prc, m.initProc.s[cpsLocals]); @@ -760,7 +989,7 @@ begin 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, deinitFrame(m.initProc)); app(prc, '}' +{&} tnl +{&} tnl); app(m.s[cfsProcs], prc) end; @@ -817,7 +1046,8 @@ begin s := NewSym(skModule, getIdent(moduleName), nil); gmti := rawNewModule(s, joinPath(options.projectPath, moduleName)+'.nim'); addPendingModule(gmti); - appf(mainModProcs, 'N_NOINLINE(void, $1)(void);$n', [getInitName(s)]); + appff(mainModProcs, 'N_NOINLINE(void, $1)(void);$n', + 'declare void $1() noinline$n', [getInitName(s)]); end; function myOpen(module: PSym; const filename: string): PPassContext; diff --git a/nim/commands.pas b/nim/commands.pas index d87a8f084..8a4435eb0 100644 --- a/nim/commands.pas +++ b/nim/commands.pas @@ -77,14 +77,14 @@ const +{&} ' --debugger:on|off turn Embedded Nimrod Debugger ON|OFF' +{&} nl +{&} ' -x, --checks:on|off code generation for all runtime checks ON|OFF' +{&} nl +{&} ' --obj_checks:on|off code generation for obj conversion checks ON|OFF' +{&} nl -+{&} ' --field_checks:on|off code generation for case record fields ON|OFF' +{&} nl ++{&} ' --field_checks:on|off code generation for case variant fields ON|OFF' +{&} nl +{&} ' --range_checks:on|off code generation for range checks ON|OFF' +{&} nl +{&} ' --bound_checks:on|off code generation for bound checks ON|OFF' +{&} nl +{&} ' --overflow_checks:on|off code generation for over-/underflow checks ON|OFF' +{&} nl +{&} ' -a, --assertions:on|off code generation for assertions ON|OFF' +{&} nl +{&} ' --dead_code_elim:on|off whole program dead code elimination ON|OFF' +{&} nl +{&} ' --opt:none|speed|size optimize not at all or for speed|size' +{&} nl -+{&} ' --app:console|gui|lib generate a console|GUI application or a shared lib' +{&} nl ++{&} ' --app:console|gui generate a console|GUI application' +{&} nl +{&} ' -r, --run run the compiled program with given arguments' +{&} nl +{&} ' --advanced show advanced command line switches' +{&} nl +{&} ' -h, --help show this help' +{&} nl @@ -125,7 +125,6 @@ const +{&} ' --checkpoints:on|off turn on|off checkpoints; for debugging Nimrod' +{&} nl +{&} ' --skip_cfg do not read the general configuration file' +{&} nl +{&} ' --skip_proj_cfg do not read the project''s configuration file' +{&} nl -+{&} ' --import:MODULE_FILE import the given module implicitly for each module' +{&} nl +{&} ' --index:FILE use FILE to generate a documenation index file' +{&} nl +{&} ' --putenv:key=value set an environment variable' +{&} nl +{&} ' --list_cmd list the commands used to execute external programs' +{&} nl @@ -506,7 +505,7 @@ begin expectArg(switch, arg, pass, info); gErrorMax := parseInt(arg); end; - else if findSubStr('.', switch) >= strStart then + else if strutils.find(switch, '.') >= strStart then options.setConfigVar(switch, arg) else InvalidCmdLineOption(pass, switch, info) diff --git a/nim/debugids.pas b/nim/debugids.pas deleted file mode 100644 index fff9ed10b..000000000 --- a/nim/debugids.pas +++ /dev/null @@ -1,129 +0,0 @@ -// -// -// The Nimrod Compiler -// (c) Copyright 2008 Andreas Rumpf -// -// See the file "copying.txt", included in this -// distribution, for details about the copyright. -// -unit debugids; - -interface - -{$include 'config.inc'} - -uses - nsystem, nos, strutils, ast; - -const - idfile = 'debugids.txt'; - -// This module implements debugging facilities for the ID mechanism. -procedure registerID(s: PSym); - -procedure writeIDTable(); -procedure loadIDTable(); - -implementation - -type - TIdSymTuple = record{@tuple} // keep id from sym to better detect bugs - id: int; - s: PSym; - end; - TIdSymTupleSeq = array of TIdSymTuple; - TIdSymTable = record - counter: int; - data: TIdSymTupleSeq; - end; - -function TableRawGet(const t: TTable; key: PObject): int; -var - h: THash; -begin - h := hashNode(key) and high(t.data); // start with real hash value - while t.data[h].key <> nil do begin - if (t.data[h].key = key) then begin - result := h; exit - end; - h := nextTry(h, high(t.data)) - end; - result := -1 -end; - -function TableSearch(const t: TTable; key, closure: PObject; - comparator: TCmpProc): PObject; -var - h: THash; -begin - h := hashNode(key) and high(t.data); // start with real hash value - while t.data[h].key <> nil do begin - if (t.data[h].key = key) then - if comparator(t.data[h].val, closure) then begin // BUGFIX 1 - result := t.data[h].val; exit - end; - h := nextTry(h, high(t.data)) - end; - result := nil -end; - -function TableGet(const t: TTable; key: PObject): PObject; -var - index: int; -begin - index := TableRawGet(t, key); - if index >= 0 then result := t.data[index].val - else result := nil -end; - -procedure TableRawInsert(var data: TPairSeq; key, val: PObject); -var - h: THash; -begin - h := HashNode(key) and high(data); - while data[h].key <> nil do begin - assert(data[h].key <> key); - h := nextTry(h, high(data)) - end; - assert(data[h].key = nil); - data[h].key := key; - data[h].val := val; -end; - -procedure TableEnlarge(var t: TTable); -var - n: TPairSeq; - i: int; -begin -{@ignore} - n := emptySeq; - setLength(n, length(t.data) * growthFactor); - fillChar(n[0], length(n)*sizeof(n[0]), 0); -{@emit - newSeq(n, length(t.data) * growthFactor); } - for i := 0 to high(t.data) do - if t.data[i].key <> nil then - TableRawInsert(n, t.data[i].key, t.data[i].val); -{@ignore} - t.data := n; -{@emit - swap(t.data, n); -} -end; - -procedure TablePut(var t: TTable; key, val: PObject); -var - index: int; -begin - index := TableRawGet(t, key); - if index >= 0 then - t.data[index].val := val - else begin - if mustRehash(length(t.data), t.counter) then TableEnlarge(t); - TableRawInsert(t.data, key, val); - inc(t.counter) - end; -end; - - -end. diff --git a/nim/docgen.pas b/nim/docgen.pas index bd4613180..15969f51d 100644 --- a/nim/docgen.pas +++ b/nim/docgen.pas @@ -40,7 +40,7 @@ type modDesc: PRope; // module description dependsOn: PRope; // dependencies id: int; // for generating IDs - splitAfter: int; // split to long entries in the TOC + splitAfter: int; // split too long entries in the TOC tocPart: array of TTocEntry; hasToc: bool; toc, section: TSections; @@ -777,7 +777,7 @@ begin rnLineBlock: outer := '<p>$1</p>'; rnLineBlockItem: outer := '$1<br />'; - rnBlockQuote: outer := '<blockquote>$1</blockquote>$n'; + rnBlockQuote: outer := '<blockquote><p>$1</p></blockquote>$n'; rnTable, rnGridTable: outer := '<table border="1" class="docutils">$1</table>'; diff --git a/nim/extccomp.pas b/nim/extccomp.pas index 51cf009d1..f51e5f690 100644 --- a/nim/extccomp.pas +++ b/nim/extccomp.pas @@ -352,13 +352,13 @@ end; procedure addCompileOption(const option: string); begin - if strutils.findSubStr(option, compileOptions, strStart) < strStart then + if strutils.find(compileOptions, option, strStart) < strStart then addOpt(compileOptions, option) end; procedure addLinkOption(const option: string); begin - if findSubStr(option, linkOptions, strStart) < strStart then + if find(linkOptions, option, strStart) < strStart then addOpt(linkOptions, option) end; diff --git a/nim/idents.pas b/nim/idents.pas index 44957ba7a..c0e4c994f 100644 --- a/nim/idents.pas +++ b/nim/idents.pas @@ -1,7 +1,7 @@ // // // The Nimrod Compiler -// (c) Copyright 2008 Andreas Rumpf +// (c) Copyright 2009 Andreas Rumpf // // See the file "copying.txt", included in this // distribution, for details about the copyright. @@ -38,8 +38,15 @@ function getIdent(identifier: cstring; len: int; h: THash): PIdent; overload; // special version for the scanner; the scanner's buffering scheme makes // this horribly efficient. Most of the time no character copying is needed! +function IdentEq(id: PIdent; const name: string): bool; + implementation +function IdentEq(id: PIdent; const name: string): bool; +begin + result := id.id = getIdent(name).id; +end; + var buckets: array [0..4096*2-1] of PIdent; diff --git a/nim/llstream.pas b/nim/llstream.pas index 2d4336664..df4c823a6 100644 --- a/nim/llstream.pas +++ b/nim/llstream.pas @@ -22,7 +22,7 @@ type llsFile, // stream encapsulates a file llsStdIn); // stream encapsulates stdin TLLStream = object(NObject) - kind: TLLStreamKind; // exposed for low-level access (lexbase uses this) + kind: TLLStreamKind; // accessible for low-level access (lexbase uses this) f: TBinaryFile; s: string; pos: int; // for string streams diff --git a/nim/main.pas b/nim/main.pas index 565373685..c888e5c3c 100644 --- a/nim/main.pas +++ b/nim/main.pas @@ -119,7 +119,7 @@ procedure CompileProject(const filename: string); begin {@discard} CompileModule( JoinPath(options.libpath, appendFileExt('system', nimExt)), false, true); - {@discard} CompileModule(filename, true, false); + {@discard} CompileModule(appendFileExt(filename, nimExt), true, false); end; procedure semanticPasses; @@ -352,6 +352,11 @@ begin wantFile(filename); CommandCompileToEcmaScript(filename); end; + wCompileToLLVM: begin + gCmd := cmdCompileToLLVM; + wantFile(filename); + CommandCompileToC(filename); + end; wPretty: begin gCmd := cmdPretty; wantFile(filename); diff --git a/nim/msgs.pas b/nim/msgs.pas index a91c328ef..0eb1651d9 100644 --- a/nim/msgs.pas +++ b/nim/msgs.pas @@ -278,6 +278,7 @@ type errXRequiresOneArgument, errUnhandledExceptionX, errCyclicTree, + errXisNoMacroOrTemplate, errUser, warnCannotOpenFile, warnOctalEscape, @@ -491,7 +492,7 @@ const '$1 here not allowed', 'invalid control flow: $1', 'a type has no value', - '''$1'' is no type', + 'invalid type: ''$1''', '''^'' needs a pointer or reference type', 'invalid context for builtin ''$1''', 'invalid expression', @@ -536,6 +537,7 @@ const 'converter requires one parameter', 'unhandled exception: $1', 'macro returned a cyclic abstract syntax tree', + '''$1'' is no macro or template', '$1', 'cannot open ''$1'' [CannotOpenFile]', 'octal escape sequences do not exist; leading zero is ignored [OctalEscape]', @@ -645,8 +647,8 @@ const // this format is understood by many text editors: it is the same that procedure MessageOut(const s: string); procedure rawMessage(const msg: TMsgKind; const arg: string = ''); overload; -procedure rawMessage(const msg: TMsgKind; const args: array of string); overload; - +procedure rawMessage(const msg: TMsgKind; const args: array of string); overload; + procedure liMessage(const info: TLineInfo; const msg: TMsgKind; const arg: string = ''); @@ -840,37 +842,37 @@ begin getMessageStr(errInstantiationFrom, '')])); end; end; - -procedure rawMessage(const msg: TMsgKind; const args: array of string); -var - frmt: string; -begin - case msg of - errMin..errMax: begin - writeContext(); - frmt := rawErrorFormat; - end; - warnMin..warnMax: begin - if not (optWarns in gOptions) then exit; - if not (msg in gNotes) then exit; - frmt := rawWarningFormat; - inc(gWarnCounter); - end; - hintMin..hintMax: begin - if not (optHints in gOptions) then exit; - if not (msg in gNotes) then exit; - frmt := rawHintFormat; - inc(gHintCounter); - end; - else assert(false) // cannot happen - end; - MessageOut(Format(frmt, format(msgKindToString(msg), args))); - handleError(msg); -end; + +procedure rawMessage(const msg: TMsgKind; const args: array of string); +var + frmt: string; +begin + case msg of + errMin..errMax: begin + writeContext(); + frmt := rawErrorFormat; + end; + warnMin..warnMax: begin + if not (optWarns in gOptions) then exit; + if not (msg in gNotes) then exit; + frmt := rawWarningFormat; + inc(gWarnCounter); + end; + hintMin..hintMax: begin + if not (optHints in gOptions) then exit; + if not (msg in gNotes) then exit; + frmt := rawHintFormat; + inc(gHintCounter); + end; + else assert(false) // cannot happen + end; + MessageOut(Format(frmt, format(msgKindToString(msg), args))); + handleError(msg); +end; procedure rawMessage(const msg: TMsgKind; const arg: string = ''); -begin - rawMessage(msg, [arg]); +begin + rawMessage(msg, [arg]); end; procedure liMessage(const info: TLineInfo; const msg: TMsgKind; diff --git a/nim/nimconf.pas b/nim/nimconf.pas index 1a70abdbe..8f908bf62 100644 --- a/nim/nimconf.pas +++ b/nim/nimconf.pas @@ -9,9 +9,7 @@ unit nimconf; -// This module used to handle the reading of the config file. We now just -// read environment variables. This is easier to avoid bootstraping issues. - +// This module handles the reading of the config file. {$include 'config.inc'} interface @@ -258,7 +256,7 @@ begin addChar(s, '.'); confTok(L, tok); checkSymbol(L, tok); - s := s +{&} tokToStr(tok); + add(s, tokToStr(tok)); confTok(L, tok) end; if tok.tokType = tkBracketLe then begin @@ -266,7 +264,7 @@ begin // BUGFIX: do not copy '['! confTok(L, tok); checkSymbol(L, tok); - val := val +{&} tokToStr(tok); + add(val, tokToStr(tok)); confTok(L, tok); if tok.tokType = tkBracketRi then confTok(L, tok) else lexMessage(L, errTokenExpected, ''']'''); @@ -276,12 +274,12 @@ begin if length(val) > 0 then addChar(val, ':'); // BUGFIX confTok(L, tok); // skip ':' or '=' checkSymbol(L, tok); - val := val +{&} tokToStr(tok); + add(val, tokToStr(tok)); confTok(L, tok); // skip symbol while (tok.ident <> nil) and (tok.ident.id = getIdent('&'+'').id) do begin confTok(L, tok); checkSymbol(L, tok); - val := val +{&} tokToStr(tok); + add(val, tokToStr(tok)); confTok(L, tok) end end; diff --git a/nim/nimrod.pas b/nim/nimrod.pas index 99d9a9d0f..728325ccc 100644 --- a/nim/nimrod.pas +++ b/nim/nimrod.pas @@ -61,7 +61,7 @@ type procedure HandleCmdLine; var - command, filename: string; + command, filename, prog: string; start: TTime; begin {@emit start := getTime(); } @@ -92,13 +92,21 @@ begin toString(getTime() - start)]); } end; - if optRun in gGlobalOptions then - execExternalProgram(quoteIfContainsWhite(changeFileExt(filename, '')) +{&} - ' ' +{&} arguments) + if optRun in gGlobalOptions then begin + {$ifdef unix} + prog := './' + quoteIfContainsWhite(changeFileExt(filename, '')); + {$else} + prog := quoteIfContainsWhite(changeFileExt(filename, '')); + {$endif} + execExternalProgram(prog +{&} ' ' +{&} arguments) + end end end; begin +//{@emit +// GC_disableMarkAndSweep(); +//} cmdLineInfo := newLineInfo('command line', -1, -1); condsyms.InitDefines(); HandleCmdLine(); diff --git a/nim/nos.pas b/nim/nos.pas index 73b17ae58..4926c99b0 100644 --- a/nim/nos.pas +++ b/nim/nos.pas @@ -123,7 +123,7 @@ procedure createDir(const dir: string); var i: int; begin - for i := 1 to length(dir) do begin + for i := 2 to length(dir) do begin if dir[i] in [sep, altsep] then sysutils.createDir(ncopy(dir, 1, i-1)); end; sysutils.createDir(dir); diff --git a/nim/nsystem.pas b/nim/nsystem.pas index f476e09ca..51ca05605 100644 --- a/nim/nsystem.pas +++ b/nim/nsystem.pas @@ -43,6 +43,7 @@ type {$endif} EOutOfRange = class(Exception) end; + EOS = class(Exception) end; float32 = single; float64 = double; diff --git a/nim/nversion.pas b/nim/nversion.pas index 7d179bb35..de0ad2b79 100644 --- a/nim/nversion.pas +++ b/nim/nversion.pas @@ -31,10 +31,10 @@ const //cog.outl('VersionMinor = %s;' % ver[1]) //cog.outl('VersionPatch = %s;' % ver[2]) //]]] - VersionAsString = '0.7.4'; + VersionAsString = '0.7.6'; VersionMajor = 0; VersionMinor = 7; - VersionPatch = 4; + VersionPatch = 6; //[[[[end]]]] implementation diff --git a/nim/options.pas b/nim/options.pas index 5bbfbbbee..d6f6d14da 100644 --- a/nim/options.pas +++ b/nim/options.pas @@ -55,6 +55,7 @@ type cmdCompileToC, cmdCompileToCpp, cmdCompileToEcmaScript, + cmdCompileToLLVM, cmdInterpret, cmdPretty, cmdDoc, @@ -207,7 +208,15 @@ begin if startsWith(dir, prefix) then begin result := ncopy(dir, length(prefix) + strStart); exit end; - result := dir + result := dir; +end; + +function removeTrailingDirSep(const path: string): string; +begin + if (length(path) > 0) and (path[length(path)+strStart-1] = dirSep) then + result := ncopy(path, strStart, length(path)+strStart-2) + else + result := path end; function toGeneratedFile(const path, ext: string): string; @@ -215,7 +224,8 @@ var head, tail: string; begin splitPath(path, head, tail); - result := joinPath([projectPath, genSubDir, shortenDir(head +{&} dirSep), + if length(head) > 0 then head := shortenDir(head +{&} dirSep); + result := joinPath([projectPath, genSubDir, head, changeFileExt(tail, ext)]) end; @@ -225,10 +235,18 @@ var head, tail, subdir: string; begin splitPath(f, head, tail); - subdir := joinPath([projectPath, genSubDir, shortenDir(head +{&} dirSep)]); + if length(head) > 0 then + head := removeTrailingDirSep(shortenDir(head +{&} dirSep)); + subdir := joinPath([projectPath, genSubDir, head]); if createSubDir then begin - //Writeln(output, subdir); - createDir(subdir); + try + createDir(subdir); + except + on EOS do begin + writeln(output, 'cannot create directory: ' + subdir); + halt(1) + end + end end; result := joinPath(subdir, tail) end; diff --git a/nim/parsecfg.pas b/nim/parsecfg.pas index a99da6852..3c10cc8fc 100644 --- a/nim/parsecfg.pas +++ b/nim/parsecfg.pas @@ -350,7 +350,7 @@ begin addChar(result.key, '.'); rawGetTok(c, c.tok); if c.tok.kind = tkSymbol then begin - result.key := result.key +{&} c.tok.literal; + add(result.key, c.tok.literal); rawGetTok(c, c.tok); end else begin diff --git a/nim/passes.pas b/nim/passes.pas index 028cfc2a2..f5dff3559 100644 --- a/nim/passes.pas +++ b/nim/passes.pas @@ -57,7 +57,7 @@ function astNeeded(s: PSym): bool; // appropriate to free the procedure body's memory. This is important // to keep memory usage down. -// some passes (the semantic checker) need these: +// the semantic checker needs these: var gImportModule: function (const filename: string): PSym; gIncludeFile: function (const filename: string): PNode; diff --git a/nim/platform.pas b/nim/platform.pas index 8bf4f3d9b..9f8d30f60 100644 --- a/nim/platform.pas +++ b/nim/platform.pas @@ -12,7 +12,7 @@ unit platform; // and operating systems. // Note: Unfortunately if an OS or CPU is listed here this does not mean that // Nimrod has been tested on this platform or that the RTL has been ported. -// Feel free to test for your exentric platform! +// Feel free to test for your excentric platform! interface diff --git a/nim/pragmas.pas b/nim/pragmas.pas index 636a1198a..9a60e6bd3 100644 --- a/nim/pragmas.pas +++ b/nim/pragmas.pas @@ -228,7 +228,7 @@ procedure processDynLib(c: PContext; n: PNode; sym: PSym); var lib: PLib; begin - if sym = nil then + if (sym = nil) or (sym.kind = skModule) then POptionEntry(c.optionStack.tail).dynlib := getLib(c, libDynamic, expectStrLit(c, n)) else begin @@ -553,13 +553,6 @@ begin wPassL: extccomp.addLinkOption(expectStrLit(c, it)); wPassC: extccomp.addCompileOption(expectStrLit(c, it)); - // fixupSystem not even documented: - wFixupSystem: begin - if c.module = magicSys.SystemModule then - magicsys.FinishSystem(magicsys.SystemModule.tab) - else - invalidPragma(it) - end; wBreakpoint: PragmaBreakpoint(c, it); wCheckpoint: PragmaCheckpoint(c, it); @@ -585,7 +578,7 @@ begin processNote(c, n) end; end; - if sym <> nil then begin + if (sym <> nil) and (sym.kind <> skModule) then begin lib := POptionEntry(c.optionstack.tail).dynlib; if ([lfDynamicLib, lfHeader] * sym.loc.flags = []) and (sfImportc in sym.flags) and @@ -625,7 +618,7 @@ begin wHints, wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError, wFatal, wDefine, wUndef, wCompile, wLink, wLinkSys, wPure, - wPush, wPop, wFixupSystem, wBreakpoint, wCheckpoint, + wPush, wPop, wBreakpoint, wCheckpoint, wPassL, wPassC, wDeadCodeElim]); end; diff --git a/nim/rodread.pas b/nim/rodread.pas index 549cfec58..a34153ccf 100644 --- a/nim/rodread.pas +++ b/nim/rodread.pas @@ -1,7 +1,7 @@ // // // The Nimrod Compiler -// (c) Copyright 2008 Andreas Rumpf +// (c) Copyright 2009 Andreas Rumpf // // See the file "copying.txt", included in this // distribution, for details about the copyright. @@ -123,7 +123,7 @@ type files: TStringSeq; dataIdx: int; // offset of start of data section convertersIdx: int; // offset of start of converters section - initIdx, interfIdx, compilerProcsIdx: int; + initIdx, interfIdx, compilerProcsIdx, cgenIdx: int; filename: string; index, imports: TIndex; readerIndex: int; @@ -841,6 +841,10 @@ begin r.initIdx := r.pos+2; // "(\10" skipSection(r); end + else if section = 'CGEN' then begin + r.cgenIdx := r.pos+2; + skipSection(r); + end else begin MessageOut('skipping section: ' + toString(r.pos)); skipSection(r); diff --git a/nim/rodwrite.pas b/nim/rodwrite.pas index 637f69ff7..72d5c893d 100644 --- a/nim/rodwrite.pas +++ b/nim/rodwrite.pas @@ -530,6 +530,7 @@ begin nkVarSection: begin for i := 0 to sonsLen(n)-1 do begin a := n.sons[i]; + if a.kind = nkCommentStmt then continue; if a.kind <> nkIdentDefs then InternalError(a.info, 'rodwrite.process'); addInterfaceSym(w, a.sons[0].sym); end @@ -537,6 +538,7 @@ begin nkConstSection: begin for i := 0 to sonsLen(n)-1 do begin a := n.sons[i]; + if a.kind = nkCommentStmt then continue; if a.kind <> nkConstDef then InternalError(a.info, 'rodwrite.process'); addInterfaceSym(w, a.sons[0].sym); end @@ -544,6 +546,7 @@ begin nkTypeSection: begin for i := 0 to sonsLen(n)-1 do begin a := n.sons[i]; + if a.kind = nkCommentStmt then continue; if a.sons[0].kind <> nkSym then InternalError(a.info, 'rodwrite.process'); s := a.sons[0].sym; diff --git a/nim/ropes.pas b/nim/ropes.pas index a6ba2a11b..864afd5b8 100644 --- a/nim/ropes.pas +++ b/nim/ropes.pas @@ -1,7 +1,7 @@ // // // The Nimrod Compiler -// (c) Copyright 2008 Andreas Rumpf +// (c) Copyright 2009 Andreas Rumpf // // See the file "copying.txt", included in this // distribution, for details about the copyright. @@ -112,8 +112,6 @@ function ropef(const frmt: TFormatStr; const args: array of PRope): PRope; procedure appf(var c: PRope; const frmt: TFormatStr; const args: array of PRope); -procedure RopeSeqInsert(var rs: TRopeSeq; r: PRope; at: Natural); - function getCacheStats: string; function RopeEqualsFile(r: PRope; const f: string): Boolean; @@ -524,8 +522,7 @@ begin assert(RopeInvariant(result)); end; -procedure appf(var c: PRope; const frmt: TFormatStr; - const args: array of PRope); +procedure appf(var c: PRope; const frmt: TFormatStr; const args: array of PRope); begin app(c, ropef(frmt, args)) end; diff --git a/nim/rst.pas b/nim/rst.pas index 55c2c933a..0c5377646 100644 --- a/nim/rst.pas +++ b/nim/rst.pas @@ -1967,6 +1967,10 @@ begin initParser(q, p.s); q.filename := filename; getTokens(readFile(path), false, q.tok); + // workaround a GCC bug: + if find(q.tok[high(q.tok)].symbol, #0#1#2) > 0 then begin + InternalError('Too many binary zeros in include file'); + end; result := parseDoc(q); end end diff --git a/nim/scanner.pas b/nim/scanner.pas index a78f9c6ce..d035b973b 100644 --- a/nim/scanner.pas +++ b/nim/scanner.pas @@ -428,7 +428,7 @@ begin L.bufpos := pos; // restore position try - if (L.buf[pos] = '0') and (L.buf[pos+1] in ['x','X','b','B','o','O']) + if (L.buf[pos] = '0') and (L.buf[pos+1] in ['x','X','b','B','o','O','c','C']) then begin inc(pos, 2); xi := 0; @@ -451,7 +451,7 @@ begin end end end; - 'o': begin + 'o', 'c', 'C': begin result.base := base8; while true do begin case L.buf[pos] of diff --git a/nim/sem.pas b/nim/sem.pas index 6d97da3e8..3494754fd 100644 --- a/nim/sem.pas +++ b/nim/sem.pas @@ -1,7 +1,7 @@ // // // The Nimrod Compiler -// (c) Copyright 2008 Andreas Rumpf +// (c) Copyright 2009 Andreas Rumpf // // See the file "copying.txt", included in this // distribution, for details about the copyright. @@ -183,7 +183,7 @@ begin c := newContext(module, filename); if (c.p <> nil) then InternalError(module.info, 'sem.myOpen'); c.semConstExpr := semConstExpr; - c.p := newProcCon(nil); + c.p := newProcCon(module); pushOwner(c.module); openScope(c.tab); // scope for imported symbols SymTabAdd(c.tab, module); // a module knows itself diff --git a/nim/semdata.pas b/nim/semdata.pas index 9ffd41eac..3393ed4b3 100644 --- a/nim/semdata.pas +++ b/nim/semdata.pas @@ -1,7 +1,7 @@ // // // The Nimrod Compiler -// (c) Copyright 2008 Andreas Rumpf +// (c) Copyright 2009 Andreas Rumpf // // See the file "copying.txt", included in this // distribution, for details about the copyright. @@ -128,6 +128,7 @@ end; function newProcCon(owner: PSym): PProcCon; begin + if owner = nil then InternalError('owner is nil'); new(result); {@ignore} fillChar(result^, sizeof(result^), 0); diff --git a/nim/semexprs.pas b/nim/semexprs.pas index 3e95e3457..59d7f969a 100644 --- a/nim/semexprs.pas +++ b/nim/semexprs.pas @@ -1,7 +1,7 @@ // // // The Ethexor Morpork Compiler -// (c) Copyright 2008 Andreas Rumpf +// (c) Copyright 2009 Andreas Rumpf // // See the file "copying.txt", included in this // distribution, for details about the copyright. @@ -87,19 +87,23 @@ begin end end; -function isCastable(castDest, src: PType): Boolean; +function isCastable(dst, src: PType): Boolean; +//const +// castableTypeKinds = {@set}[tyInt, tyPtr, tyRef, tyCstring, tyString, +// tySequence, tyPointer, tyNil, tyOpenArray, +// tyProc, tySet, tyEnum, tyBool, tyChar]; var ds, ss: biggestInt; begin // this is very unrestrictive; cast is allowed if castDest.size >= src.size - ds := computeSize(castDest); + ds := computeSize(dst); ss := computeSize(src); if ds < 0 then result := false else if ss < 0 then result := false - else + else result := (ds >= ss) or - (castDest.kind in [tyInt..tyFloat128]) or - (src.kind in [tyInt..tyFloat128]) + (skipGeneric(dst).kind in [tyInt..tyFloat128]) or + (skipGeneric(src).kind in [tyInt..tyFloat128]) end; function semConv(c: PContext; n: PNode; s: PSym): PNode; @@ -620,6 +624,11 @@ begin end; end; +procedure checkDeprecated(n: PNode; s: PSym); +begin + if sfDeprecated in s.flags then liMessage(n.info, warnDeprecated, s.name.s); +end; + function semSym(c: PContext; n: PNode; s: PSym; flags: TExprFlags): PNode; begin result := newSymNode(s); @@ -652,7 +661,8 @@ begin end end else begin end - end + end; + checkDeprecated(n, s); end; function isTypeExpr(n: PNode): bool; @@ -787,6 +797,7 @@ begin result := newSymNode(f); result.info := n.info; result.typ := ty; + checkDeprecated(n, f); end else liMessage(n.sons[1].info, errEnumHasNoValueX, i.s); @@ -814,6 +825,7 @@ begin n.sons[0] := makeDeref(n.sons[0]); n.sons[1] := newSymNode(f); // we now have the correct field n.typ := f.typ; + checkDeprecated(n, f); if check = nil then result := n else begin check.sons[0] := n; @@ -831,14 +843,17 @@ begin n.sons[1] := newSymNode(f); n.typ := f.typ; result := n; + checkDeprecated(n, f); exit end end; // allow things like "".replace(...) // --> replace("", ...) f := SymTabGet(c.tab, i); - if (f <> nil) and (f.kind = skStub) then loadStub(f); - if (f <> nil) and (f.kind in [skProc, skIterator]) then begin + //if (f <> nil) and (f.kind = skStub) then loadStub(f); + // XXX ``loadStub`` is not correct here as we don't care for ``f`` really + if (f <> nil) then begin + // BUGFIX: do not check for (f.kind in [skProc, skIterator]) here result := newNodeI(nkDotCall, n.info); // This special node kind is to merge with the call handler in `semExpr`. addSon(result, newIdentNode(i, n.info)); @@ -1121,6 +1136,57 @@ begin result := semFieldAccess(c, n, flags); end; +function isCallExpr(n: PNode): bool; +begin + result := n.kind in [nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand]; +end; + +function semMacroStmt(c: PContext; n: PNode): PNode; +var + s: PSym; + a: PNode; + i: int; +begin + checkMinSonsLen(n, 2); + if isCallExpr(n.sons[0]) then + a := n.sons[0].sons[0] + else + a := n.sons[0]; + s := qualifiedLookup(c, a, false); + if (s <> nil) then begin + checkDeprecated(n, s); + case s.kind of + skMacro: begin + include(s.flags, sfUsed); + result := semMacroExpr(c, n, s); + end; + skTemplate: begin + include(s.flags, sfUsed); + // transform + // nkMacroStmt(nkCall(a...), stmt, b...) + // to + // nkCall(a..., stmt, b...) + result := newNodeI(nkCall, n.info); + addSon(result, a); + if isCallExpr(n.sons[0]) then begin + for i := 1 to sonsLen(n.sons[0])-1 do + addSon(result, n.sons[0].sons[i]); + end; + for i := 1 to sonsLen(n)-1 do + addSon(result, n.sons[i]); + pushInfoContext(n.info); + result := evalTemplate(c, result, s); + popInfoContext(); + end; + else + liMessage(n.info, errXisNoMacroOrTemplate, s.name.s); + end + end + else + liMessage(n.info, errInvalidExpressionX, + renderTree(a, {@set}[renderNoComments])); +end; + function semExpr(c: PContext; n: PNode; flags: TExprFlags = {@set}[]): PNode; var s: PSym; @@ -1174,6 +1240,7 @@ begin checkMinSonsLen(n, 1); s := qualifiedLookup(c, n.sons[0], false); if (s <> nil) then begin + checkDeprecated(n, s); case s.kind of skMacro: begin include(s.flags, sfUsed); @@ -1200,6 +1267,9 @@ begin end else result := semIndirectOp(c, n); end; + nkMacroStmt: begin + result := semMacroStmt(c, n); + end; nkBracketExpr: begin checkMinSonsLen(n, 1); s := qualifiedLookup(c, n.sons[0], false); @@ -1221,7 +1291,7 @@ begin nkPar: begin case checkPar(n) of paNone: result := nil; - paTuplePositions: result := semTuplePositionsConstr(c, n); + paTuplePositions: result := semTuplePositionsConstr(c, n); paTupleFields: result := semTupleFieldsConstr(c, n); paSingle: result := semExpr(c, n.sons[0]); end; diff --git a/nim/semfold.pas b/nim/semfold.pas index 422ddbd01..781c3b97d 100644 --- a/nim/semfold.pas +++ b/nim/semfold.pas @@ -406,6 +406,8 @@ begin else result := copyTree(s.ast); // BUGFIX end end + else if s.kind = skProc then // BUGFIX + result := n end; nkCharLit..nkNilLit: result := copyNode(n); nkIfExpr: result := getConstIfExpr(module, n); diff --git a/nim/seminst.pas b/nim/seminst.pas index 4c3d416d4..a49d8478e 100644 --- a/nim/seminst.pas +++ b/nim/seminst.pas @@ -58,10 +58,29 @@ begin end end; +procedure genericToConcreteTypeKind(t: PType); +var + body: PNode; +begin + if (t.kind = tyGeneric) and (t.sym <> nil) then begin + body := t.sym.ast.sons[2]; + case body.kind of + nkObjectTy: t.kind := tyObject; + nkTupleTy: t.kind := tyTuple; + nkRefTy: t.kind := tyRef; + nkPtrTy: t.kind := tyPtr; + nkVarTy: t.kind := tyVar; + nkProcTy: t.kind := tyProc; + else InternalError('genericToConcreteTypeKind'); + end + end +end; + function instantiateType(c: PInstantiateClosure; typ: PType): PType; var i: int; begin + if typ = nil then begin result := nil; exit end; result := PType(idTableGet(c.typeMap, typ)); if result <> nil then exit; //if typ.kind = tyOpenArray then @@ -73,6 +92,7 @@ begin result.sons[i] := instantiateType(c, result.sons[i]); if result.n <> nil then result.n := instTypeNode(c, result.n); + genericToConcreteTypeKind(result); end else result := typ; @@ -272,6 +292,20 @@ begin result := instantiateType(c, t); end; +function newInstantiateClosure(p: PContext; + const instantiator: TLineInfo): PInstantiateClosure; +begin + new(result); +{@ignore} + fillChar(result^, sizeof(result^), 0); +{@emit} + InitIdTable(result.typeMap); + InitIdTable(result.symMap); + result.fn := nil; + result.instantiator := instantiator; + result.module := p.module; +end; + function partialSpecialization(c: PContext; n: PNode; s: PSym): PNode; begin result := n; diff --git a/nim/semstmts.pas b/nim/semstmts.pas index 098b95072..ebf14693c 100644 --- a/nim/semstmts.pas +++ b/nim/semstmts.pas @@ -42,7 +42,7 @@ begin end; if result = nil then result := newNodeI(nkNilLit, n.info); // The ``when`` statement implements the mechanism for platform dependant - // code. Thus we try to ensure here consistent ID distribution after the + // code. Thus we try to ensure here consistent ID allocation after the // ``when`` statement. IDsynchronizationPoint(200); end; @@ -59,9 +59,11 @@ begin case it.kind of nkElifBranch: begin checkSonsLen(it, 2); + openScope(c.tab); it.sons[0] := semExprWithType(c, it.sons[0]); checkBool(it.sons[0]); - it.sons[1] := semStmtScope(c, it.sons[1]) + it.sons[1] := semStmt(c, it.sons[1]); + closeScope(c.tab); end; nkElse: begin if sonsLen(it) = 1 then it.sons[0] := semStmtScope(c, it.sons[0]) @@ -144,7 +146,7 @@ begin // now parse the string literal and substitute symbols: a := strStart; repeat - b := findSubStr(marker, str, a); + b := strutils.find(str, marker, a); if b < strStart then sub := ncopy(str, a) else @@ -153,7 +155,7 @@ begin addSon(result, newStrNode(nkStrLit, sub)); if b < strStart then break; - c := findSubStr(marker, str, b+1); + c := strutils.find(str, marker, b+1); if c < strStart then sub := ncopy(str, b+1) else @@ -179,11 +181,13 @@ function semWhile(c: PContext; n: PNode): PNode; begin result := n; checkSonsLen(n, 2); + openScope(c.tab); n.sons[0] := semExprWithType(c, n.sons[0]); CheckBool(n.sons[0]); inc(c.p.nestedLoopCounter); - n.sons[1] := semStmtScope(c, n.sons[1]); + n.sons[1] := semStmt(c, n.sons[1]); dec(c.p.nestedLoopCounter); + closeScope(c.tab); end; function semCase(c: PContext; n: PNode): PNode; @@ -197,6 +201,7 @@ begin // check selector: result := n; checkMinSonsLen(n, 2); + openScope(c.tab); n.sons[0] := semExprWithType(c, n.sons[0]); chckCovered := false; covered := 0; @@ -216,7 +221,7 @@ begin end; nkElifBranch: begin chckCovered := false; - checkSonsLen(n, 2); + checkSonsLen(x, 2); x.sons[0] := semExprWithType(c, x.sons[0]); checkBool(x.sons[0]); x.sons[1] := semStmtScope(c, x.sons[1]) @@ -231,6 +236,7 @@ begin end; if chckCovered and (covered <> lengthOrd(n.sons[0].typ)) then liMessage(n.info, errNotAllCasesCovered); + closeScope(c.tab); end; function semAsgn(c: PContext; n: PNode): PNode; @@ -388,7 +394,7 @@ begin if (a.kind <> nkIdentDefs) and (a.kind <> nkVarTuple) then IllFormedAst(a); checkMinSonsLen(a, 3); len := sonsLen(a); - if a.sons[len-2] <> nil then + if a.sons[len-2] <> nil then typ := semTypeNode(c, a.sons[len-2], nil) else typ := nil; @@ -401,6 +407,8 @@ begin end else def := nil; + if not typeAllowed(typ, skVar) then + liMessage(a.info, errXisNoType, typeToString(typ)); tup := skipGeneric(typ); if a.kind = nkVarTuple then begin if tup.kind <> tyTuple then liMessage(a.info, errXExpected, 'tuple'); @@ -408,7 +416,7 @@ begin liMessage(a.info, errWrongNumberOfVariables); end; for j := 0 to len-3 do begin - if c.p.owner = nil then begin + if (c.p.owner.kind = skModule) then begin v := semIdentWithPragma(c, skVar, a.sons[j], {@set}[sfStar, sfMinus]); include(v.flags, sfGlobal); end @@ -441,7 +449,7 @@ begin if a.kind = nkCommentStmt then continue; if (a.kind <> nkConstDef) then IllFormedAst(a); checkSonsLen(a, 3); - if (c.p.owner = nil) then begin + if (c.p.owner.kind = skModule) then begin v := semIdentWithPragma(c, skConst, a.sons[0], {@set}[sfStar, sfMinus]); include(v.flags, sfGlobal); end @@ -456,6 +464,8 @@ begin def := fitRemoveHiddenConv(c, typ, def); end else typ := def.typ; + if not typeAllowed(typ, skConst) then + liMessage(a.info, errXisNoType, typeToString(typ)); v.typ := typ; v.ast := def; // no need to copy @@ -480,6 +490,7 @@ begin result := n; checkMinSonsLen(n, 3); len := sonsLen(n); + openScope(c.tab); if n.sons[len-2].kind = nkRange then begin checkSonsLen(n.sons[len-2], 2); // convert ``in 3..5`` to ``in countup(3, 5)`` @@ -500,7 +511,6 @@ begin end; n.sons[len-2] := semExprWithType(c, n.sons[len-2]); iter := skipGeneric(n.sons[len-2].typ); - openScope(c.tab); if iter.kind <> tyTuple then begin if len <> 3 then liMessage(n.info, errWrongNumberOfVariables); v := newSymS(skForVar, n.sons[0], c); @@ -573,7 +583,7 @@ begin end; end; -procedure semGenericParamList(c: PContext; n: PNode); +procedure semGenericParamList(c: PContext; n: PNode; father: PType = nil); var i: int; s: PSym; @@ -590,6 +600,7 @@ begin s.typ := newTypeS(tyGenericParam, c); s.typ.sym := s; end; + if father <> nil then addSon(father, s.typ); s.position := i; n.sons[i] := newSymNode(s); addDecl(c, s); @@ -631,7 +642,7 @@ begin if a.kind = nkCommentStmt then continue; if (a.kind <> nkTypeDef) then IllFormedAst(a); checkSonsLen(a, 3); - if (c.p.owner = nil) then begin + if (c.p.owner.kind = skModule) then begin s := semIdentWithPragma(c, skType, a.sons[0], {@set}[sfStar, sfMinus]); include(s.flags, sfGlobal); end @@ -665,7 +676,8 @@ begin openScope(c.tab); pushOwner(s); s.typ.kind := tyGeneric; - semGenericParamList(c, a.sons[1]); + semGenericParamList(c, a.sons[1], s.typ); + addSon(s.typ, nil); // process the type body for symbol lookup of generic params // we can use the same algorithm as for template parameters: a.sons[2] := resolveTemplateParams(c, a.sons[2]); @@ -736,7 +748,7 @@ var begin result := n; checkSonsLen(n, codePos+1); - if c.p.owner <> nil then + if c.p.owner.kind <> skModule then liMessage(n.info, errIteratorNotAllowed); oldP := c.p; // restore later s := semIdentVis(c, skIterator, n.sons[0], {@set}[sfStar]); @@ -857,7 +869,7 @@ var begin result := n; checkSonsLen(n, codePos+1); - if c.p.owner = nil then begin + if c.p.owner.kind = skModule then begin s := semIdentVis(c, kind, n.sons[0], {@set}[sfStar]); include(s.flags, sfGlobal); end @@ -885,7 +897,7 @@ begin proto := SearchForProc(c, s, c.tab.tos-2); // -2 because we have a scope open // for parameters if proto = nil then begin - if oldP.owner <> nil then // we are in a nested proc + if oldP.owner.kind <> skModule then // we are in a nested proc s.typ.callConv := ccClosure else s.typ.callConv := lastOptionEntry(c).defaultCC; @@ -1025,7 +1037,7 @@ begin if nfSem in n.flags then exit; case n.kind of nkAsgn: result := semAsgn(c, n); - nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand: + nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkMacroStmt: result := semCommand(c, n); nkEmpty, nkCommentStmt, nkNilLit: begin end; nkBlockStmt: result := semBlock(c, n); @@ -1051,8 +1063,7 @@ begin nkDiscardStmt: result := semDiscard(c, n); nkWhileStmt: result := semWhile(c, n); nkTryStmt: result := semTry(c, n); - nkBreakStmt, nkContinueStmt: - result := semBreakOrContinue(c, n); + nkBreakStmt, nkContinueStmt: result := semBreakOrContinue(c, n); nkForStmt: result := semFor(c, n); nkCaseStmt: result := semCase(c, n); nkReturnStmt: result := semReturn(c, n); diff --git a/nim/semtempl.pas b/nim/semtempl.pas index c07a7bd13..ebc5e1ebb 100644 --- a/nim/semtempl.pas +++ b/nim/semtempl.pas @@ -164,7 +164,7 @@ var i, j, len, counter: int; params, p, paramKind: PNode; begin - if c.p.owner = nil then begin + if c.p.owner.kind = skModule then begin s := semIdentVis(c, skTemplate, n.sons[0], {@set}[sfStar]); include(s.flags, sfGlobal); end diff --git a/nim/semtypes.pas b/nim/semtypes.pas index 37958c4d0..014ff0216 100644 --- a/nim/semtypes.pas +++ b/nim/semtypes.pas @@ -51,10 +51,12 @@ begin e := newSymS(skEnumField, n.sons[i].sons[0], c); v := semConstExpr(c, n.sons[i].sons[1]); x := getOrdValue(v); - if (x <> counter) and (i <> 1) then - include(result.flags, tfEnumHasWholes); - if x < counter then - liMessage(n.sons[i].info, errInvalidOrderInEnumX, e.name.s); + if i <> 1 then begin + if (x <> counter) then + include(result.flags, tfEnumHasWholes); + if x < counter then + liMessage(n.sons[i].info, errInvalidOrderInEnumX, e.name.s); + end; counter := x; end; nkSym: e := n.sons[i].sym; @@ -247,7 +249,7 @@ begin end end end; - +(* function instGenericAux(c: PContext; templ, actual: PNode; sym: PSym): PNode; var @@ -271,34 +273,48 @@ begin result.sons[i] := instGenericAux(c, templ.sons[i], actual, sym); end end -end; +end; *) function semGeneric(c: PContext; n: PNode; s: PSym; prev: PType): PType; var i: int; elem: PType; inst: PNode; + cl: PInstantiateClosure; begin if (s.typ = nil) or (s.typ.kind <> tyGeneric) then liMessage(n.info, errCannotInstantiateX, s.name.s); result := newOrPrevType(tyGenericInst, prev, c); // new ID... result.containerID := s.typ.containerID; // ... but the same containerID result.sym := s; - if (s.typ.containerID = 0) then - InternalError(n.info, 'semtypes.semGeneric'); + if (s.typ.containerID = 0) then InternalError(n.info, 'semtypes.semGeneric'); + cl := newInstantiateClosure(c, n.info); + // check the number of supplied arguments suffices: + if sonsLen(n) <> sonsLen(s.typ) then begin + //MessageOut('n: ' +{&} toString(sonsLen(n)) +{&} ' s: ' + // +{&} toString(sonsLen(s.typ))); + liMessage(n.info, errWrongNumberOfTypeParams); + end; + // a generic type should be instantiated with itself: + // idTablePut(cl.typeMap, s.typ, result); + // iterate over arguments: for i := 1 to sonsLen(n)-1 do begin elem := semTypeNode(c, n.sons[i], nil); if elem.kind = tyGenericParam then - result.kind := tyGeneric; // prevend type from instantiation + result.kind := tyGeneric // prevend type from instantiation + else + idTablePut(cl.typeMap, s.typ.sons[i-1], elem); addSon(result, elem); end; if s.ast <> nil then begin if (result.kind = tyGenericInst) then begin - inst := instGenericAux(c, s.ast.sons[2], n, s); + // inst := instGenericAux(c, s.ast.sons[2], n, s); internalError(n.info, 'Generic containers not implemented'); // XXX: implementation does not work this way + // we need to do the following: + // traverse and copy the type and replace any tyGenericParam type // does checking of instantiated type for us: - elem := semTypeNode(c, inst, nil); + elem := instantiateType(cl, s.typ); //semTypeNode(c, inst, nil); elem.id := result.containerID; addSon(result, elem); end diff --git a/nim/sigmatch.pas b/nim/sigmatch.pas index 289a17673..ebcbb2529 100644 --- a/nim/sigmatch.pas +++ b/nim/sigmatch.pas @@ -80,7 +80,7 @@ var begin result := msgKindToString(errTypeMismatch); for i := 1 to sonsLen(n)-1 do begin - debug(n.sons[i].typ); + //debug(n.sons[i].typ); add(result, typeToString(n.sons[i].typ)); if i <> sonsLen(n)-1 then add(result, ', '); end; @@ -361,9 +361,7 @@ begin // is a subtype of f? tyProc: begin if (sonsLen(f) = sonsLen(a)) and (f.callconv = a.callconv) then begin // Note: We have to do unification for the parameters before the - // return type! Otherwise it'd be counter-intuitive for the standard - // Nimrod syntax. For the C-based syntax it IS counter-intuitive. - // But that is one of the reasons a standard syntax was picked. + // return type! result := isEqual; // start with maximum; also correct for no // params at all for i := 1 to sonsLen(f)-1 do begin @@ -433,8 +431,9 @@ begin // is a subtype of f? end; tyAnyEnum: begin case a.kind of - tyRange: result := typeRel(mapping, f, base(a)); - tyEnum: result := isSubtype; + tyRange: result := typeRel(mapping, f, base(a)); + tyEnum: result := isSubtype; + tyAnyEnum: result := isEqual; else begin end end end; @@ -726,11 +725,11 @@ begin end else begin setSon(m.call, formal.position+1, arg); - end; - inc(f); + end end end; inc(a); + inc(f); end; // iterate over all formal params and check all are provided: f := 1; diff --git a/nim/strtabs.pas b/nim/strtabs.pas index b07aefab1..4fcf32891 100644 --- a/nim/strtabs.pas +++ b/nim/strtabs.pas @@ -269,14 +269,14 @@ begin j := i+1; while (j <= length(f)+strStart-1) and (f[j] <> '}') do inc(j); key := ncopy(f, i+2+strStart-1, j-1+strStart-1); - result := result +{&} getValue(t, flags, key); + add(result, getValue(t, flags, key)); i := j+1 end; 'a'..'z', 'A'..'Z', #128..#255, '_': begin j := i+1; while (j <= length(f)+strStart-1) and (f[j] in PatternChars) do inc(j); key := ncopy(f, i+1+strStart-1, j-1+strStart-1); - result := result +{&} getValue(t, flags, key); + add(result, getValue(t, flags, key)); i := j end else begin diff --git a/nim/strutils.pas b/nim/strutils.pas index 71a428dbb..cd07105be 100644 --- a/nim/strutils.pas +++ b/nim/strutils.pas @@ -28,6 +28,7 @@ function cmp(const x, y: string): int; function cmpIgnoreCase(const x, y: string): int; function format(const f: string; const args: array of string): string; +procedure addf(var result: string; const f: string; args: array of string); function toHex(x: BiggestInt; len: int): string; function toOctal(value: Char): string; @@ -47,7 +48,7 @@ function ToString(b: Boolean): string; overload; function IntToStr(i: BiggestInt; minChars: int): string; -function findSubStr(const sub, s: string; start: int = 1): int; +function find(const s, sub: string; start: int = 1): int; overload; function replaceStr(const s, search, by: string): string; procedure deleteStr(var s: string; first, last: int); @@ -81,8 +82,8 @@ implementation function quoteIfContainsWhite(const s: string): string; begin - if ((findSubStr(' ', s) >= strStart) - or (findSubStr(#9, s) >= strStart)) and (s[strStart] <> '"') then + if ((find(s, ' ') >= strStart) + or (find(s, #9) >= strStart)) and (s[strStart] <> '"') then result := '"' +{&} s +{&} '"' else result := s @@ -247,7 +248,7 @@ begin result := result + s[i] end; -function findSubStr(const sub, s: string; start: int = 1): int; +function find(const s, sub: string; start: int = 1): int; var i, j, M, N: int; begin @@ -277,7 +278,7 @@ begin result := ''; i := 1; repeat - j := findSubStr(search, s, i); + j := find(s, search, i); if j = 0 then begin // copy the rest: result := result + copy(s, i, length(s) - i + 1); @@ -475,7 +476,7 @@ begin until false end; -function find(const x: string; const inArray: array of string): int; +function find(const x: string; const inArray: array of string): int; overload; var i: int; y: string; @@ -491,30 +492,29 @@ begin result := -1 end; -function format(const f: string; const args: array of string): string; +procedure addf(var result: string; const f: string; args: array of string); const PatternChars = ['a'..'z', 'A'..'Z', '0'..'9', '_', #128..#255]; var i, j, x: int; begin - result := ''; i := 1; while i <= length(f) do if f[i] = '$' then begin case f[i+1] of '$': begin - result := result + '$'; + addChar(result, '$'); inc(i, 2); end; '1'..'9': begin - result := result + args[ord(f[i+1]) - ord('0') - 1]; + add(result, args[ord(f[i+1]) - ord('0') - 1]); inc(i, 2); end; '{': begin j := i+1; while (j <= length(f)) and (f[j] <> '}') do inc(j); x := find(ncopy(f, i+2, j-1), args); - if (x >= 0) and (x < high(args)) then result := result + args[x+1] + if (x >= 0) and (x < high(args)) then add(result, args[x+1]) else raise EInvalidFormatStr.create(''); i := j+1 end; @@ -522,7 +522,7 @@ begin j := i+1; while (j <= length(f)) and (f[j] in PatternChars) do inc(j); x := find(ncopy(f, i+1, j-1), args); - if (x >= 0) and (x < high(args)) then result := result + args[x+1] + if (x >= 0) and (x < high(args)) then add(result, args[x+1]) else raise EInvalidFormatStr.create(ncopy(f, i+1, j-1)); i := j end @@ -530,11 +530,17 @@ begin end end else begin - result := result + f[i]; + addChar(result, f[i]); inc(i) end end; +function format(const f: string; const args: array of string): string; +begin + result := ''; + addf(result, f, args) +end; + {@ignore} {$ifopt Q-} {$Q+} {$else} {$define Q_off} diff --git a/nim/transf.pas b/nim/transf.pas index 98d1e89ea..5d9f44143 100644 --- a/nim/transf.pas +++ b/nim/transf.pas @@ -14,6 +14,7 @@ unit transf; // * inlines iterators // * inlines constants // * performes contant folding +// * introduces nkHiddenDeref, nkHiddenSubConv, etc. interface @@ -446,12 +447,51 @@ begin end; end; +function skipPassAsOpenArray(n: PNode): PNode; +begin + result := n; + while result.kind = nkPassAsOpenArray do + result := result.sons[0] +end; + +type + TPutArgInto = (paDirectMapping, paFastAsgn, paVarAsgn); + +function putArgInto(arg: PNode; formal: PType): TPutArgInto; +// This analyses how to treat the mapping "formal <-> arg" in an +// inline context. +var + i: int; +begin + if skipGeneric(formal).kind = tyOpenArray then begin + result := paDirectMapping; // XXX really correct? + // what if ``arg`` has side-effects? + exit + end; + case arg.kind of + nkEmpty..nkNilLit: result := paDirectMapping; + nkPar, nkCurly, nkBracket: begin + result := paFastAsgn; + for i := 0 to sonsLen(arg)-1 do + if putArgInto(arg.sons[i], formal) <> paDirectMapping then + exit; + result := paDirectMapping; + end; + else begin + if skipGeneric(formal).kind = tyVar then + result := paVarAsgn + else + result := paFastAsgn + end + end +end; + function transformFor(c: PTransf; n: PNode): PNode; // generate access statements for the parameters (unless they are constant) // put mapping from formal parameters to actual parameters var i, len: int; - call, e, v, body: PNode; + call, v, body, arg: PNode; newC: PTransCon; temp, formal: PSym; begin @@ -473,24 +513,24 @@ begin // generate access statements for the parameters (unless they are constant) pushTransCon(c, newC); for i := 1 to sonsLen(call)-1 do begin - e := getConstExpr(c.module, call.sons[i]); + arg := skipPassAsOpenArray(transform(c, call.sons[i])); formal := skipGeneric(newC.owner.typ).n.sons[i].sym; - if e <> nil then - IdNodeTablePut(newC.mapping, formal, e) - else if (skipConv(call.sons[i]).kind = nkSym) then begin - // since parameters cannot be modified, we can identify the formal and - // the actual params - IdNodeTablePut(newC.mapping, formal, call.sons[i]); - end - else begin - // generate a temporary and produce an assignment statement: - temp := newTemp(c, formal.typ, formal.info); - addVar(v, newSymNode(temp)); - // BUGFIX: do not copy call.sons[i], but transform it! - addSon(result, newAsgnStmt(c, newSymNode(temp), - transform(c, call.sons[i]))); - IdNodeTablePut(newC.mapping, formal, newSymNode(temp)); // BUGFIX - end + //if IdentEq(newc.Owner.name, 'items') then + // liMessage(arg.info, warnUser, 'items: ' + nodeKindToStr[arg.kind]); + case putArgInto(arg, formal.typ) of + paDirectMapping: IdNodeTablePut(newC.mapping, formal, arg); + paFastAsgn: begin + // generate a temporary and produce an assignment statement: + temp := newTemp(c, formal.typ, formal.info); + addVar(v, newSymNode(temp)); + addSon(result, newAsgnStmt(c, newSymNode(temp), arg)); + IdNodeTablePut(newC.mapping, formal, newSymNode(temp)); + end; + paVarAsgn: begin + assert(skipGeneric(formal.typ).kind = tyVar); + InternalError(arg.info, 'not implemented: pass to var parameter'); + end; + end; end; body := newC.owner.ast.sons[codePos]; pushInfoContext(n.info); @@ -668,7 +708,7 @@ function transformCase(c: PTransf; n: PNode): PNode; // removes `elif` branches of a case stmt var len, i, j: int; - ifs: PNode; + ifs, elsen: PNode; begin len := sonsLen(n); i := len-1; @@ -678,9 +718,11 @@ begin if (n.sons[i].kind <> nkOfBranch) then InternalError(n.sons[i].info, 'transformCase'); ifs := newNodeI(nkIfStmt, n.sons[i+1].info); + elsen := newNodeI(nkElse, ifs.info); for j := i+1 to len-1 do addSon(ifs, n.sons[j]); setLength(n.sons, i+2); - n.sons[i+1] := ifs; + addSon(elsen, ifs); + n.sons[i+1] := elsen; end; result := n; for j := 0 to sonsLen(n)-1 do result.sons[j] := transform(c, n.sons[j]); diff --git a/nim/types.pas b/nim/types.pas index 25ad54b33..af19a1671 100644 --- a/nim/types.pas +++ b/nim/types.pas @@ -1,12 +1,11 @@ // // // The Nimrod Compiler -// (c) Copyright 2008 Andreas Rumpf +// (c) Copyright 2009 Andreas Rumpf // // See the file "copying.txt", included in this // distribution, for details about the copyright. // - unit types; // this module contains routines for accessing and iterating over types @@ -47,7 +46,6 @@ function mutateType(t: PType; iter: TTypeMutator; closure: PObject): PType; // Returns result of `iter`. - function SameType(x, y: PType): Boolean; function SameTypeOrNil(a, b: PType): Boolean; @@ -115,6 +113,8 @@ function analyseObjectWithTypeField(t: PType): TTypeFieldResult; // made or intializing of the type field suffices or if there is no type field // at all in this type. +function typeAllowed(t: PType; kind: TSymKind): bool; + implementation function InvalidGenericInst(f: PType): bool; @@ -185,11 +185,13 @@ begin n := sym.typ.n; for i := 1 to sonsLen(n)-1 do begin p := n.sons[i]; - assert(p.kind = nkSym); - result := result +{&} p.sym.name.s +{&} ': ' +{&} typeToString(p.sym.typ); - if i <> sonsLen(n)-1 then result := result + ', '; + if (p.kind <> nkSym) then InternalError('getProcHeader'); + add(result, p.sym.name.s); + add(result, ': '); + add(result, typeToString(p.sym.typ)); + if i <> sonsLen(n)-1 then add(result, ', '); end; - result := result + ')'; + addChar(result, ')'); if n.sons[0].typ <> nil then result := result +{&} ': ' +{&} typeToString(n.sons[0].typ); end; @@ -928,6 +930,96 @@ begin end end; +function typeAllowedAux(var marker: TIntSet; t: PType; + kind: TSymKind): bool; forward; + +function typeAllowedNode(var marker: TIntSet; n: PNode; kind: TSymKind): bool; +var + i: int; +begin + result := true; + if n <> nil then begin + result := typeAllowedAux(marker, n.typ, kind); + if result then + case n.kind of + nkNone..nkNilLit: begin end; + else begin + for i := 0 to sonsLen(n)-1 do begin + result := typeAllowedNode(marker, n.sons[i], kind); + if not result then exit + end + end + end + end +end; + +function typeAllowedAux(var marker: TIntSet; t: PType; kind: TSymKind): bool; +var + i: int; +begin + assert(kind in [skVar, skConst, skParam]); + result := true; + if t = nil then exit; + // if we have already checked the type, return true, because we stop the + // evaluation if something is wrong: + if IntSetContainsOrIncl(marker, t.id) then exit; + case skipGeneric(t).kind of + tyVar: begin + case skipGeneric(t.sons[0]).kind of + tyVar: result := false; // ``var var`` is always an invalid type: + tyOpenArray: result := (kind = skParam) and + typeAllowedAux(marker, t.sons[0], kind); + else result := (kind <> skConst) and + typeAllowedAux(marker, t.sons[0], kind); + end + end; + tyProc: begin + for i := 1 to sonsLen(t)-1 do begin + result := typeAllowedAux(marker, t.sons[i], skParam); + if not result then exit; + end; + if t.sons[0] <> nil then + result := typeAllowedAux(marker, t.sons[0], skVar) + end; + tyGeneric, tyGenericParam, tyForward, tyNone: result := false; + tyEmpty, tyNil: result := kind = skConst; + tyString, tyBool, tyChar, tyEnum, tyInt..tyFloat128, tyCString, tyPointer: + result := true; + tyAnyEnum: result := kind = skParam; + tyGenericInst: result := typeAllowedAux(marker, lastSon(t), kind); + tyRange: result := skipGeneric(t.sons[0]).kind in + [tyChar, tyEnum, tyInt..tyFloat128]; + tyOpenArray: + result := (kind = skParam) and typeAllowedAux(marker, t.sons[0], skVar); + tySequence: result := (kind <> skConst) + and typeAllowedAux(marker, t.sons[0], skVar) + or (t.sons[0].kind = tyEmpty); + tyArray: result := typeAllowedAux(marker, t.sons[1], skVar); + tyPtr, tyRef: result := typeAllowedAux(marker, t.sons[0], skVar); + tyArrayConstr, tyTuple, tySet: begin + for i := 0 to sonsLen(t)-1 do begin + result := typeAllowedAux(marker, t.sons[i], kind); + if not result then exit + end; + end; + tyObject: begin + for i := 0 to sonsLen(t)-1 do begin + result := typeAllowedAux(marker, t.sons[i], skVar); + if not result then exit + end; + if t.n <> nil then result := typeAllowedNode(marker, t.n, skVar) + end + end +end; + +function typeAllowed(t: PType; kind: TSymKind): bool; +var + marker: TIntSet; +begin + IntSetInit(marker); + result := typeAllowedAux(marker, t, kind); +end; + function align(address, alignment: biggestInt): biggestInt; begin result := (address + (alignment-1)) and not (alignment-1); diff --git a/nim/wordrecg.pas b/nim/wordrecg.pas index 587005c2a..dd2df0047 100644 --- a/nim/wordrecg.pas +++ b/nim/wordrecg.pas @@ -1,7 +1,7 @@ // // // The Nimrod Compiler -// (c) Copyright 2008 Andreas Rumpf +// (c) Copyright 2009 Andreas Rumpf // // See the file "copying.txt", included in this // distribution, for details about the copyright. @@ -66,7 +66,7 @@ type wMerge, wLib, wDynlib, wCompilerproc, wCppmethod, wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef, wLinedir, wStacktrace, wLinetrace, wPragma, - wLink, wCompile, wLinksys, wFixupsystem, wDeprecated, wVarargs, + wLink, wCompile, wLinksys, wDeprecated, wVarargs, wByref, wCallconv, wBreakpoint, wDebugger, wNimcall, wStdcall, wCdecl, wSafecall, wSyscall, wInline, wNoInline, wFastcall, wClosure, wNoconv, wOn, wOff, wChecks, wRangechecks, wBoundchecks, @@ -84,7 +84,7 @@ type wMaxErr, wExpr, wStmt, wTypeDesc, wSubsChar, wAstCache, wAcyclic, wIndex, // commands: - wCompileToC, wCompileToCpp, wCompileToEcmaScript, + wCompileToC, wCompileToCpp, wCompileToEcmaScript, wCompileToLLVM, wPretty, wDoc, wPas, wGenDepend, wListDef, wCheck, wParse, wScan, wBoot, wLazy, wRst2html, wI, @@ -140,7 +140,7 @@ const 'merge', 'lib', 'dynlib', 'compilerproc', 'cppmethod', 'fatal', 'error', 'warning', 'hint', 'line', 'push', 'pop', 'define', 'undef', 'linedir', 'stacktrace', 'linetrace', 'pragma', - 'link', 'compile', 'linksys', 'fixupsystem', 'deprecated', 'varargs', + 'link', 'compile', 'linksys', 'deprecated', 'varargs', 'byref', 'callconv', 'breakpoint', 'debugger', 'nimcall', 'stdcall', 'cdecl', 'safecall', 'syscall', 'inline', 'noinline', 'fastcall', 'closure', 'noconv', 'on', 'off', 'checks', 'rangechecks', 'boundchecks', @@ -158,7 +158,7 @@ const 'maxerr', 'expr', 'stmt', 'typedesc', 'subschar', 'astcache', 'acyclic', 'index', // commands: - 'compiletoc', 'compiletocpp', 'compiletoecmascript', + 'compiletoc', 'compiletocpp', 'compiletoecmascript', 'compiletollvm', 'pretty', 'doc', 'pas', 'gendepend', 'listdef', 'check', 'parse', 'scan', 'boot', 'lazy', 'rst2html', 'i'+'', |