mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-01 10:52:14 +00:00
1083 lines
34 KiB
ObjectPascal
1083 lines
34 KiB
ObjectPascal
//
|
|
//
|
|
// The Nimrod Compiler
|
|
// (c) Copyright 2009 Andreas Rumpf
|
|
//
|
|
// See the file "copying.txt", included in this
|
|
// distribution, for details about the copyright.
|
|
//
|
|
|
|
//var
|
|
// newDummyVar: int; // just to check the symbol file mechanism
|
|
|
|
// ------------------------- Name Mangling --------------------------------
|
|
|
|
function mangle(const name: string): string;
|
|
var
|
|
i: int;
|
|
begin
|
|
case name[strStart] of
|
|
'a'..'z': begin
|
|
result := '';
|
|
addChar(result, chr(ord(name[strStart]) - ord('a') + ord('A')));
|
|
end;
|
|
'0'..'9', 'A'..'Z': begin
|
|
result := '';
|
|
addChar(result, name[strStart]);
|
|
end;
|
|
else
|
|
result := 'HEX' + toHex(ord(name[strStart]), 2);
|
|
end;
|
|
for i := strStart+1 to length(name) + strStart-1 do begin
|
|
case name[i] of
|
|
'A'..'Z': addChar(result, chr(ord(name[i]) - ord('A') + ord('a')));
|
|
'_': begin end;
|
|
'a'..'z', '0'..'9': addChar(result, name[i]);
|
|
else begin
|
|
add(result, 'HEX');
|
|
add(result, toHex(ord(name[i]), 2))
|
|
end
|
|
end
|
|
end
|
|
end;
|
|
|
|
function mangleName(s: PSym): PRope;
|
|
begin
|
|
result := s.loc.r;
|
|
if result = nil then begin
|
|
if gCmd = cmdCompileToLLVM then begin
|
|
case s.kind of
|
|
skProc, skMethod, 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
|
|
if s.owner <> nil then
|
|
appf(gMapping, 'r"$1.$2": $3$n',
|
|
[toRope(s.owner.Name.s), toRope(s.name.s), result]);
|
|
s.loc.r := result;
|
|
end
|
|
end;
|
|
|
|
function getTypeName(typ: PType): PRope;
|
|
begin
|
|
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 := 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;
|
|
begin
|
|
case typ.kind of
|
|
tyNone: result := ctVoid;
|
|
tyBool: result := ctBool;
|
|
tyChar: result := ctChar;
|
|
tySet: begin
|
|
case int(getSize(typ)) of
|
|
1: result := ctInt8;
|
|
2: result := ctInt16;
|
|
4: result := ctInt32;
|
|
8: result := ctInt64;
|
|
else result := ctArray
|
|
end
|
|
end;
|
|
tyOpenArray, tyArrayConstr, tyArray: result := ctArray;
|
|
tyObject, tyTuple: result := ctStruct;
|
|
tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal:
|
|
result := mapType(lastSon(typ));
|
|
tyEnum: begin
|
|
if firstOrd(typ) < 0 then
|
|
result := ctInt32
|
|
else begin
|
|
case int(getSize(typ)) of
|
|
1: result := ctUInt8;
|
|
2: result := ctUInt16;
|
|
4: result := ctInt32;
|
|
8: result := ctInt64;
|
|
else internalError('mapType');
|
|
end
|
|
end
|
|
end;
|
|
tyRange: result := mapType(typ.sons[0]);
|
|
tyPtr, tyVar, tyRef: begin
|
|
case typ.sons[0].kind of
|
|
tyOpenArray, tyArrayConstr, tyArray: result := ctArray;
|
|
else result := ctPtr
|
|
end
|
|
end;
|
|
tyPointer: result := ctPtr;
|
|
tySequence: result := ctNimSeq;
|
|
tyProc: result := ctProc;
|
|
tyString: result := ctNimStr;
|
|
tyCString: result := ctCString;
|
|
tyInt..tyFloat128:
|
|
result := TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt));
|
|
else InternalError('mapType');
|
|
end
|
|
end;
|
|
|
|
function mapReturnType(typ: PType): TCTypeKind;
|
|
begin
|
|
if skipTypes(typ, abstractInst).kind = tyArray then result := ctPtr
|
|
else result := mapType(typ)
|
|
end;
|
|
|
|
function getTypeDescAux(m: BModule; typ: PType;
|
|
var check: TIntSet): PRope; forward;
|
|
|
|
function needsComplexAssignment(typ: PType): bool;
|
|
begin
|
|
result := containsGarbageCollectedRef(typ);
|
|
end;
|
|
|
|
function isInvalidReturnType(rettype: PType): bool;
|
|
begin
|
|
// Arrays and sets cannot be returned by a C procedure, because C is
|
|
// such a poor programming language.
|
|
// We exclude records with refs too. This enhances efficiency and
|
|
// is necessary for proper code generation of assignments.
|
|
if rettype = nil then
|
|
result := true
|
|
else begin
|
|
case mapType(rettype) of
|
|
ctArray:
|
|
result := not (skipTypes(rettype, abstractInst).kind in [tyVar, tyRef, tyPtr]);
|
|
ctStruct:
|
|
result := needsComplexAssignment(skipTypes(rettype, abstractInst));
|
|
else result := false;
|
|
end
|
|
end
|
|
end;
|
|
|
|
const
|
|
CallingConvToStr: array [TCallingConvention] of string = ('N_NIMCALL',
|
|
'N_STDCALL', 'N_CDECL', 'N_SAFECALL', 'N_SYSCALL',
|
|
// this is probably not correct for all platforms,
|
|
// 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
|
|
// since types are now unique via the ``GetUniqueType`` mechanism, this slow
|
|
// linear search is not necessary anymore:
|
|
result := PRope(IdTableGet(tab, key))
|
|
end;
|
|
|
|
function getTempName(): PRope;
|
|
begin
|
|
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;
|
|
|
|
function ccgIntroducedPtr(s: PSym): bool;
|
|
var
|
|
pt: PType;
|
|
begin
|
|
pt := s.typ;
|
|
assert(not (sfResult in s.flags));
|
|
case pt.Kind of
|
|
tyObject: begin
|
|
// XXX quick hack floatSize*2 for the pegs module under 64bit
|
|
if (optByRef in s.options) or (getSize(pt) > platform.floatSize*2) then
|
|
result := true // requested anyway
|
|
else if (tfFinal in pt.flags) and (pt.sons[0] = nil) then
|
|
result := false // no need, because no subtyping possible
|
|
else
|
|
result := true; // ordinary objects are always passed by reference,
|
|
// otherwise casting doesn't work
|
|
end;
|
|
tyTuple:
|
|
result := (getSize(pt) > platform.floatSize) or (optByRef in s.options);
|
|
else
|
|
result := false
|
|
end
|
|
end;
|
|
|
|
procedure fillResult(param: PSym);
|
|
begin
|
|
fillLoc(param.loc, locParam, param.typ, ropeff('Result', '%Result', []),
|
|
OnStack);
|
|
if (mapReturnType(param.typ) <> ctArray)
|
|
and IsInvalidReturnType(param.typ) then
|
|
begin
|
|
include(param.loc.flags, lfIndirect);
|
|
param.loc.s := OnUnknown
|
|
end
|
|
end;
|
|
|
|
procedure genProcParams(m: BModule; t: PType; out rettype, params: PRope;
|
|
var check: TIntSet);
|
|
var
|
|
i, j: int;
|
|
param: PSym;
|
|
arr: PType;
|
|
begin
|
|
params := nil;
|
|
if (t.sons[0] = nil) or isInvalidReturnType(t.sons[0]) then
|
|
// C cannot return arrays (what a poor language...)
|
|
rettype := toRope('void')
|
|
else
|
|
rettype := getTypeDescAux(m, t.sons[0], check);
|
|
for i := 1 to sonsLen(t.n)-1 do begin
|
|
if t.n.sons[i].kind <> nkSym then InternalError(t.n.info, 'genProcParams');
|
|
param := t.n.sons[i].sym;
|
|
fillLoc(param.loc, locParam, param.typ, mangleName(param), OnStack);
|
|
app(params, getTypeDescAux(m, param.typ, check));
|
|
if ccgIntroducedPtr(param) then begin
|
|
app(params, '*'+'');
|
|
include(param.loc.flags, lfIndirect);
|
|
param.loc.s := OnUnknown;
|
|
end;
|
|
app(params, ' '+'');
|
|
app(params, param.loc.r);
|
|
// declare the len field for open arrays:
|
|
arr := param.typ;
|
|
if arr.kind = tyVar then arr := arr.sons[0];
|
|
j := 0;
|
|
while arr.Kind = tyOpenArray do begin // need to pass hidden parameter:
|
|
appff(params, ', NI $1Len$2', ', @NI $1Len$2', [param.loc.r, toRope(j)]);
|
|
inc(j);
|
|
arr := arr.sons[0]
|
|
end;
|
|
if i < sonsLen(t.n)-1 then app(params, ', ');
|
|
end;
|
|
if (t.sons[0] <> nil) and isInvalidReturnType(t.sons[0]) then begin
|
|
if params <> nil then app(params, ', ');
|
|
arr := t.sons[0];
|
|
app(params, getTypeDescAux(m, arr, check));
|
|
if (mapReturnType(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, ', ');
|
|
app(params, 'void* ClPart')
|
|
end;
|
|
if tfVarargs in t.flags then begin
|
|
if params <> nil then app(params, ', ');
|
|
app(params, '...')
|
|
end;
|
|
if (params = nil) and (gCmd <> cmdCompileToLLVM) then
|
|
app(params, 'void)')
|
|
else
|
|
app(params, ')'+'');
|
|
params := con('('+'', params);
|
|
end;
|
|
|
|
function isImportedType(t: PType): bool;
|
|
begin
|
|
result := (t.sym <> nil) and (sfImportc in t.sym.flags)
|
|
end;
|
|
|
|
function typeNameOrLiteral(t: PType; const literal: string): PRope;
|
|
begin
|
|
if (t.sym <> nil) and (sfImportc in t.sym.flags) and
|
|
(t.sym.magic = mNone) then
|
|
result := getTypeName(t)
|
|
else
|
|
result := toRope(literal)
|
|
end;
|
|
|
|
function getSimpleTypeDesc(m: BModule; typ: PType): PRope;
|
|
const
|
|
NumericalTypeToStr: array [tyInt..tyFloat128] of string = (
|
|
'NI', 'NI8', 'NI16', 'NI32', 'NI64', 'NF', 'NF32', 'NF64', 'NF128');
|
|
begin
|
|
case typ.Kind of
|
|
tyPointer: result := typeNameOrLiteral(typ, 'void*');
|
|
tyEnum: begin
|
|
if firstOrd(typ) < 0 then
|
|
result := typeNameOrLiteral(typ, 'NI32')
|
|
else begin
|
|
case int(getSize(typ)) of
|
|
1: result := typeNameOrLiteral(typ, 'NU8');
|
|
2: result := typeNameOrLiteral(typ, 'NU16');
|
|
4: result := typeNameOrLiteral(typ, 'NI32');
|
|
8: result := typeNameOrLiteral(typ, 'NI64');
|
|
else begin
|
|
internalError(typ.sym.info,
|
|
'getSimpleTypeDesc: ' + toString(getSize(typ)));
|
|
result := nil
|
|
end
|
|
end
|
|
end
|
|
end;
|
|
tyString: begin
|
|
useMagic(m, 'NimStringDesc');
|
|
result := typeNameOrLiteral(typ, 'NimStringDesc*');
|
|
end;
|
|
tyCstring: result := typeNameOrLiteral(typ, 'NCSTRING');
|
|
tyBool: result := typeNameOrLiteral(typ, 'NIM_BOOL');
|
|
tyChar: result := typeNameOrLiteral(typ, 'NIM_CHAR');
|
|
tyNil: result := typeNameOrLiteral(typ, '0'+'');
|
|
tyInt..tyFloat128:
|
|
result := typeNameOrLiteral(typ, NumericalTypeToStr[typ.Kind]);
|
|
tyRange: result := getSimpleTypeDesc(m, typ.sons[0]);
|
|
else result := nil;
|
|
end
|
|
end;
|
|
|
|
function getTypePre(m: BModule; typ: PType): PRope;
|
|
begin
|
|
if typ = nil then
|
|
result := toRope('void')
|
|
else begin
|
|
result := getSimpleTypeDesc(m, typ);
|
|
if result = nil then
|
|
result := CacheGetType(m.typeCache, typ)
|
|
end
|
|
end;
|
|
|
|
function getForwardStructFormat(): string;
|
|
begin
|
|
if gCmd = cmdCompileToCpp then result := 'struct $1;$n'
|
|
else result := 'typedef struct $1 $1;$n'
|
|
end;
|
|
|
|
function getTypeForward(m: BModule; typ: PType): PRope;
|
|
begin
|
|
result := CacheGetType(m.forwTypeCache, typ);
|
|
if result <> nil then exit;
|
|
result := getTypePre(m, typ);
|
|
if result <> nil then exit;
|
|
case typ.kind of
|
|
tySequence, tyTuple, tyObject: begin
|
|
result := getTypeName(typ);
|
|
if not isImportedType(typ) then
|
|
appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result]);
|
|
IdTablePut(m.forwTypeCache, typ, result)
|
|
end
|
|
else
|
|
InternalError('getTypeForward(' + typeKindToStr[typ.kind] + ')')
|
|
end
|
|
end;
|
|
|
|
function mangleRecFieldName(field: PSym; rectype: PType): PRope;
|
|
begin
|
|
if (rectype.sym <> nil)
|
|
and ([sfImportc, sfExportc] * rectype.sym.flags <> []) then
|
|
result := field.loc.r
|
|
else
|
|
result := toRope(mangle(field.name.s));
|
|
if result = nil then InternalError(field.info, 'mangleRecFieldName');
|
|
end;
|
|
|
|
function genRecordFieldsAux(m: BModule; n: PNode; accessExpr: PRope;
|
|
rectype: PType; var check: TIntSet): PRope;
|
|
var
|
|
i: int;
|
|
ae, uname, sname, a: PRope;
|
|
k: PNode;
|
|
field: PSym;
|
|
begin
|
|
result := nil;
|
|
case n.kind of
|
|
nkRecList: begin
|
|
for i := 0 to sonsLen(n)-1 do begin
|
|
app(result, genRecordFieldsAux(m, n.sons[i], accessExpr,
|
|
rectype, check));
|
|
end
|
|
end;
|
|
nkRecCase: begin
|
|
if (n.sons[0].kind <> nkSym) then
|
|
InternalError(n.info, 'genRecordFieldsAux');
|
|
app(result, genRecordFieldsAux(m, n.sons[0], accessExpr, rectype, check));
|
|
uname := toRope(mangle(n.sons[0].sym.name.s)+ 'U');
|
|
if accessExpr <> nil then ae := ropef('$1.$2', [accessExpr, uname])
|
|
else ae := uname;
|
|
app(result, 'union {'+tnl);
|
|
for i := 1 to sonsLen(n)-1 do begin
|
|
case n.sons[i].kind of
|
|
nkOfBranch, nkElse: begin
|
|
k := lastSon(n.sons[i]);
|
|
if k.kind <> nkSym then begin
|
|
sname := con('S'+'', toRope(i));
|
|
a := genRecordFieldsAux(m, k, ropef('$1.$2', [ae, sname]),
|
|
rectype, check);
|
|
if a <> nil then begin
|
|
app(result, 'struct {');
|
|
app(result, a);
|
|
appf(result, '} $1;$n', [sname]);
|
|
end
|
|
end
|
|
else app(result, genRecordFieldsAux(m, k, ae, rectype, check));
|
|
end;
|
|
else internalError('genRecordFieldsAux(record case branch)');
|
|
end;
|
|
end;
|
|
appf(result, '} $1;$n', [uname])
|
|
end;
|
|
nkSym: begin
|
|
field := n.sym;
|
|
assert(field.ast = nil);
|
|
sname := mangleRecFieldName(field, rectype);
|
|
if accessExpr <> nil then ae := ropef('$1.$2', [accessExpr, sname])
|
|
else ae := sname;
|
|
fillLoc(field.loc, locField, field.typ, ae, OnUnknown);
|
|
appf(result, '$1 $2;$n', [getTypeDescAux(m, field.loc.t, check), sname])
|
|
end;
|
|
else internalError(n.info, 'genRecordFieldsAux()');
|
|
end
|
|
end;
|
|
|
|
function getRecordFields(m: BModule; typ: PType; var check: TIntSet): PRope;
|
|
begin
|
|
result := genRecordFieldsAux(m, typ.n, nil, typ, check);
|
|
end;
|
|
|
|
function getRecordDesc(m: BModule; typ: PType; name: PRope;
|
|
var check: TIntSet): PRope;
|
|
var
|
|
desc: PRope;
|
|
hasField: bool;
|
|
begin
|
|
// declare the record:
|
|
hasField := false;
|
|
if typ.kind = tyObject then begin
|
|
useMagic(m, 'TNimType');
|
|
if typ.sons[0] = nil then begin
|
|
if (typ.sym <> nil) and (sfPure in typ.sym.flags)
|
|
or (tfFinal in typ.flags) then
|
|
result := ropef('struct $1 {$n', [name])
|
|
else begin
|
|
result := ropef('struct $1 {$nTNimType* m_type;$n', [name]);
|
|
hasField := true
|
|
end
|
|
end
|
|
else if gCmd = cmdCompileToCpp then begin
|
|
result := ropef('struct $1 : public $2 {$n',
|
|
[name, getTypeDescAux(m, typ.sons[0], check)]);
|
|
hasField := true
|
|
end
|
|
else begin
|
|
result := ropef('struct $1 {$n $2 Sup;$n',
|
|
[name, getTypeDescAux(m, typ.sons[0], check)]);
|
|
hasField := true
|
|
end
|
|
end
|
|
else
|
|
result := ropef('struct $1 {$n', [name]);
|
|
desc := getRecordFields(m, typ, check);
|
|
if (desc = nil) and not hasField then
|
|
// no fields in struct are not valid in C, so generate a dummy:
|
|
appf(result, 'char dummy;$n', [])
|
|
else
|
|
app(result, desc);
|
|
app(result, '};' + tnl);
|
|
end;
|
|
|
|
function getTupleDesc(m: BModule; typ: PType; name: PRope;
|
|
var check: TIntSet): PRope;
|
|
var
|
|
desc: PRope;
|
|
i: int;
|
|
begin
|
|
result := ropef('struct $1 {$n', [name]);
|
|
desc := nil;
|
|
for i := 0 to sonsLen(typ)-1 do
|
|
appf(desc, '$1 Field$2;$n',
|
|
[getTypeDescAux(m, typ.sons[i], check), toRope(i)]);
|
|
if (desc = nil) then app(result, 'char dummy;' + tnl)
|
|
else app(result, desc);
|
|
app(result, '};' + tnl);
|
|
end;
|
|
|
|
procedure pushType(m: BModule; typ: PType);
|
|
var
|
|
L: int;
|
|
begin
|
|
L := length(m.typeStack);
|
|
setLength(m.typeStack, L+1);
|
|
m.typeStack[L] := typ;
|
|
end;
|
|
|
|
function getTypeDescAux(m: BModule; typ: PType; var check: TIntSet): PRope;
|
|
// returns only the type's name
|
|
var
|
|
name, rettype, desc, recdesc: PRope;
|
|
n: biggestInt;
|
|
t, et: PType;
|
|
begin
|
|
t := getUniqueType(typ);
|
|
if t = nil then InternalError('getTypeDescAux: t == nil');
|
|
if t.sym <> nil then useHeader(m, t.sym);
|
|
result := getTypePre(m, t);
|
|
if result <> nil then exit;
|
|
if IntSetContainsOrIncl(check, t.id) then begin
|
|
InternalError('cannot generate C type for: ' + typeToString(typ));
|
|
// XXX: this BUG is hard to fix -> we need to introduce helper structs,
|
|
// but determining when this needs to be done is hard. We should split
|
|
// C type generation into an analysis and a code generation phase somehow.
|
|
end;
|
|
case t.Kind of
|
|
tyRef, tyPtr, tyVar: begin
|
|
et := getUniqueType(t.sons[0]);
|
|
if et.kind in [tyArrayConstr, tyArray, tyOpenArray] then
|
|
et := getUniqueType(elemType(et));
|
|
case et.Kind of
|
|
tyObject, tyTuple: begin
|
|
// no restriction! We have a forward declaration for structs
|
|
name := getTypeForward(m, et);
|
|
result := con(name, '*'+'');
|
|
IdTablePut(m.typeCache, t, result);
|
|
pushType(m, et);
|
|
end;
|
|
tySequence: begin
|
|
// no restriction! We have a forward declaration for structs
|
|
name := getTypeForward(m, et);
|
|
result := con(name, '**');
|
|
IdTablePut(m.typeCache, t, result);
|
|
pushType(m, et);
|
|
end;
|
|
else begin
|
|
// else we have a strong dependency :-(
|
|
result := con(getTypeDescAux(m, et, check), '*'+'');
|
|
IdTablePut(m.typeCache, t, result)
|
|
end
|
|
end
|
|
end;
|
|
tyOpenArray: begin
|
|
et := getUniqueType(t.sons[0]);
|
|
result := con(getTypeDescAux(m, et, check), '*'+'');
|
|
IdTablePut(m.typeCache, t, result)
|
|
end;
|
|
tyProc: begin
|
|
result := getTypeName(t);
|
|
IdTablePut(m.typeCache, t, result);
|
|
genProcParams(m, t, rettype, desc, check);
|
|
if not isImportedType(t) then begin
|
|
if t.callConv <> ccClosure then
|
|
appf(m.s[cfsTypes], 'typedef $1_PTR($2, $3) $4;$n',
|
|
[toRope(CallingConvToStr[t.callConv]), rettype, result, desc])
|
|
else // procedure vars may need a closure!
|
|
appf(m.s[cfsTypes], 'typedef struct $1 {$n' +
|
|
'N_CDECL_PTR($2, PrcPart) $3;$n' +
|
|
'void* ClPart;$n};$n',
|
|
[result, rettype, desc]);
|
|
end
|
|
end;
|
|
tySequence: begin
|
|
// we cannot use getTypeForward here because then t would be associated
|
|
// with the name of the struct, not with the pointer to the struct:
|
|
result := CacheGetType(m.forwTypeCache, t);
|
|
if result = nil then begin
|
|
result := getTypeName(t);
|
|
if not isImportedType(t) then
|
|
appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result]);
|
|
IdTablePut(m.forwTypeCache, t, result);
|
|
end;
|
|
assert(CacheGetType(m.typeCache, t) = nil);
|
|
IdTablePut(m.typeCache, t, con(result, '*'+''));
|
|
if not isImportedType(t) then begin
|
|
useMagic(m, 'TGenericSeq');
|
|
if skipTypes(t.sons[0], abstractInst).kind <> tyEmpty then
|
|
appf(m.s[cfsSeqTypes],
|
|
'struct $2 {$n' +
|
|
' TGenericSeq Sup;$n' +
|
|
' $1 data[SEQ_DECL_SIZE];$n' +
|
|
'};$n', [getTypeDescAux(m, t.sons[0], check), result])
|
|
else
|
|
result := toRope('TGenericSeq')
|
|
end;
|
|
app(result, '*'+'');
|
|
end;
|
|
tyArrayConstr, tyArray: begin
|
|
n := lengthOrd(t);
|
|
if n <= 0 then n := 1; // make an array of at least one element
|
|
result := getTypeName(t);
|
|
IdTablePut(m.typeCache, t, result);
|
|
if not isImportedType(t) then
|
|
appf(m.s[cfsTypes], 'typedef $1 $2[$3];$n',
|
|
[getTypeDescAux(m, t.sons[1], check), result, ToRope(n)])
|
|
end;
|
|
tyObject, tyTuple: begin
|
|
result := CacheGetType(m.forwTypeCache, t);
|
|
if result = nil then begin
|
|
result := getTypeName(t);
|
|
if not isImportedType(t) then
|
|
appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result]);
|
|
IdTablePut(m.forwTypeCache, t, result)
|
|
end;
|
|
IdTablePut(m.typeCache, t, result);
|
|
// always call for sideeffects:
|
|
if t.n <> nil then
|
|
recdesc := getRecordDesc(m, t, result, check)
|
|
else
|
|
recdesc := getTupleDesc(m, t, result, check);
|
|
if not isImportedType(t) then app(m.s[cfsTypes], recdesc);
|
|
end;
|
|
tySet: begin
|
|
case int(getSize(t)) of
|
|
1: result := toRope('NU8');
|
|
2: result := toRope('NU16');
|
|
4: result := toRope('NU32');
|
|
8: result := toRope('NU64');
|
|
else begin
|
|
result := getTypeName(t);
|
|
IdTablePut(m.typeCache, t, result);
|
|
if not isImportedType(t) then
|
|
appf(m.s[cfsTypes], 'typedef NU8 $1[$2];$n',
|
|
[result, toRope(getSize(t))])
|
|
end
|
|
end
|
|
end;
|
|
tyGenericInst, tyDistinct, tyOrdinal:
|
|
result := getTypeDescAux(m, lastSon(t), check);
|
|
else begin
|
|
InternalError('getTypeDescAux(' + typeKindToStr[t.kind] + ')');
|
|
result := nil
|
|
end
|
|
end
|
|
end;
|
|
|
|
function getTypeDesc(m: BModule; typ: PType): PRope; overload;
|
|
var
|
|
check: TIntSet;
|
|
begin
|
|
IntSetInit(check);
|
|
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;
|
|
begin
|
|
i := 0;
|
|
while i < length(m.typeStack) do begin
|
|
{@discard} getTypeDesc(m, m.typeStack[i]);
|
|
inc(i);
|
|
end;
|
|
end;
|
|
|
|
function genProcHeader(m: BModule; prc: PSym): PRope;
|
|
var
|
|
rettype, params: PRope;
|
|
check: TIntSet;
|
|
begin
|
|
// using static is needed for inline procs
|
|
if (prc.typ.callConv = ccInline) then
|
|
result := toRope('static ')
|
|
else
|
|
result := nil;
|
|
IntSetInit(check);
|
|
fillLoc(prc.loc, locProc, prc.typ, mangleName(prc), OnUnknown);
|
|
genProcParams(m, prc.typ, rettype, params, check);
|
|
appf(result, '$1($2, $3)$4',
|
|
[toRope(CallingConvToStr[prc.typ.callConv]),
|
|
rettype, prc.loc.r, params])
|
|
end;
|
|
|
|
// ----------------------- type information ----------------------------------
|
|
|
|
function genTypeInfo(m: BModule; typ: PType): PRope; forward;
|
|
|
|
function getNimNode(m: BModule): PRope;
|
|
begin
|
|
result := ropef('$1[$2]', [m.typeNodesName, toRope(m.typeNodes)]);
|
|
inc(m.typeNodes);
|
|
end;
|
|
|
|
function getNimType(m: BModule): PRope;
|
|
begin
|
|
result := ropef('$1[$2]', [m.nimTypesName, toRope(m.nimTypes)]);
|
|
inc(m.nimTypes);
|
|
end;
|
|
|
|
procedure allocMemTI(m: BModule; typ: PType; name: PRope);
|
|
var
|
|
tmp: PRope;
|
|
begin
|
|
tmp := getNimType(m);
|
|
appf(m.s[cfsTypeInit2], '$2 = &$1;$n', [tmp, name]);
|
|
end;
|
|
|
|
procedure genTypeInfoAuxBase(m: BModule; typ: PType; name, base: PRope);
|
|
var
|
|
nimtypeKind, flags: int;
|
|
begin
|
|
allocMemTI(m, typ, name);
|
|
if (typ.kind = tyObject) and (tfFinal in typ.flags)
|
|
and (typ.sons[0] = nil) then
|
|
nimtypeKind := ord(high(TTypeKind))+1 // tyPureObject
|
|
else
|
|
nimtypeKind := ord(typ.kind);
|
|
appf(m.s[cfsTypeInit3],
|
|
'$1->size = sizeof($2);$n' +
|
|
'$1->kind = $3;$n' +
|
|
'$1->base = $4;$n', [
|
|
name, getTypeDesc(m, typ), toRope(nimtypeKind), base]);
|
|
// compute type flags for GC optimization
|
|
flags := 0;
|
|
if not containsGarbageCollectedRef(typ) then flags := flags or 1;
|
|
if not canFormAcycle(typ) then flags := flags or 2;
|
|
//else MessageOut('can contain a cycle: ' + typeToString(typ));
|
|
if flags <> 0 then
|
|
appf(m.s[cfsTypeInit3], '$1->flags = $2;$n', [name, toRope(flags)]);
|
|
appf(m.s[cfsVars], 'TNimType* $1; /* $2 */$n',
|
|
[name, toRope(typeToString(typ))]);
|
|
end;
|
|
|
|
procedure genTypeInfoAux(m: BModule; typ: PType; name: PRope);
|
|
var
|
|
base: PRope;
|
|
begin
|
|
if (sonsLen(typ) > 0) and (typ.sons[0] <> nil) then
|
|
base := genTypeInfo(m, typ.sons[0])
|
|
else
|
|
base := toRope('0'+'');
|
|
genTypeInfoAuxBase(m, typ, name, base);
|
|
end;
|
|
|
|
procedure genObjectFields(m: BModule; typ: PType; n: PNode; expr: PRope);
|
|
var
|
|
tmp, tmp2: PRope;
|
|
len, i, j, x, y: int;
|
|
field: PSym;
|
|
b: PNode;
|
|
begin
|
|
case n.kind of
|
|
nkRecList: begin
|
|
len := sonsLen(n);
|
|
if len = 1 then // generates more compact code!
|
|
genObjectFields(m, typ, n.sons[0], expr)
|
|
else if len > 0 then begin
|
|
tmp := getTempName();
|
|
appf(m.s[cfsTypeInit1], 'static TNimNode* $1[$2];$n',
|
|
[tmp, toRope(len)]);
|
|
for i := 0 to len-1 do begin
|
|
tmp2 := getNimNode(m);
|
|
appf(m.s[cfsTypeInit3], '$1[$2] = &$3;$n', [tmp, toRope(i), tmp2]);
|
|
genObjectFields(m, typ, n.sons[i], tmp2);
|
|
end;
|
|
appf(m.s[cfsTypeInit3],
|
|
'$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n', [
|
|
expr, toRope(len), tmp]);
|
|
end
|
|
else
|
|
appf(m.s[cfsTypeInit3],
|
|
'$1.len = $2; $1.kind = 2;$n', [expr, toRope(len)]);
|
|
end;
|
|
nkRecCase: begin
|
|
len := sonsLen(n);
|
|
assert(n.sons[0].kind = nkSym);
|
|
field := n.sons[0].sym;
|
|
tmp := getTempName();
|
|
useMagic(m, 'chckNil');
|
|
appf(m.s[cfsTypeInit3], '$1.kind = 3;$n' +
|
|
'$1.offset = offsetof($2, $3);$n' +
|
|
'$1.typ = $4;$n' +
|
|
'chckNil($1.typ);$n' +
|
|
'$1.name = $5;$n' +
|
|
'$1.sons = &$6[0];$n' +
|
|
'$1.len = $7;$n',
|
|
[expr, getTypeDesc(m, typ), field.loc.r,
|
|
genTypeInfo(m, field.typ),
|
|
makeCString(field.name.s), tmp,
|
|
toRope(lengthOrd(field.typ))]);
|
|
appf(m.s[cfsTypeInit1], 'static TNimNode* $1[$2];$n',
|
|
[tmp, toRope(lengthOrd(field.typ)+1)]);
|
|
for i := 1 to len-1 do begin
|
|
b := n.sons[i]; // branch
|
|
tmp2 := getNimNode(m);
|
|
genObjectFields(m, typ, lastSon(b), tmp2);
|
|
case b.kind of
|
|
nkOfBranch: begin
|
|
if sonsLen(b) < 2 then
|
|
internalError(b.info, 'genObjectFields; nkOfBranch broken');
|
|
for j := 0 to sonsLen(b)-2 do begin
|
|
if b.sons[j].kind = nkRange then begin
|
|
x := int(getOrdValue(b.sons[j].sons[0]));
|
|
y := int(getOrdValue(b.sons[j].sons[1]));
|
|
while x <= y do begin
|
|
appf(m.s[cfsTypeInit3], '$1[$2] = &$3;$n',
|
|
[tmp, toRope(x), tmp2]);
|
|
inc(x);
|
|
end;
|
|
end
|
|
else
|
|
appf(m.s[cfsTypeInit3], '$1[$2] = &$3;$n',
|
|
[tmp, toRope(getOrdValue(b.sons[j])), tmp2])
|
|
end
|
|
end;
|
|
nkElse: begin
|
|
appf(m.s[cfsTypeInit3], '$1[$2] = &$3;$n',
|
|
[tmp, toRope(lengthOrd(field.typ)), tmp2]);
|
|
end
|
|
else
|
|
internalError(n.info, 'genObjectFields(nkRecCase)');
|
|
end
|
|
end
|
|
end;
|
|
nkSym: begin
|
|
field := n.sym;
|
|
useMagic(m, 'chckNil');
|
|
appf(m.s[cfsTypeInit3], '$1.kind = 1;$n' +
|
|
'$1.offset = offsetof($2, $3);$n' +
|
|
'$1.typ = $4;$n' +
|
|
'chckNil($1.typ);$n' +
|
|
'$1.name = $5;$n',
|
|
[expr, getTypeDesc(m, typ), field.loc.r,
|
|
genTypeInfo(m, field.typ),
|
|
makeCString(field.name.s)]);
|
|
end;
|
|
else internalError(n.info, 'genObjectFields');
|
|
end
|
|
end;
|
|
|
|
procedure genObjectInfo(m: BModule; typ: PType; name: PRope);
|
|
var
|
|
tmp: PRope;
|
|
begin
|
|
if typ.kind = tyObject then genTypeInfoAux(m, typ, name)
|
|
else genTypeInfoAuxBase(m, typ, name, toRope('0'+''));
|
|
tmp := getNimNode(m);
|
|
genObjectFields(m, typ, typ.n, tmp);
|
|
appf(m.s[cfsTypeInit3], '$1->node = &$2;$n', [name, tmp]);
|
|
end;
|
|
|
|
procedure genTupleInfo(m: BModule; typ: PType; name: PRope);
|
|
var
|
|
tmp, expr, tmp2: PRope;
|
|
i, len: int;
|
|
a: PType;
|
|
begin
|
|
genTypeInfoAuxBase(m, typ, name, toRope('0'+''));
|
|
expr := getNimNode(m);
|
|
len := sonsLen(typ);
|
|
if len > 0 then begin
|
|
tmp := getTempName();
|
|
appf(m.s[cfsTypeInit1], 'static TNimNode* $1[$2];$n', [tmp, toRope(len)]);
|
|
for i := 0 to len-1 do begin
|
|
a := typ.sons[i];
|
|
tmp2 := getNimNode(m);
|
|
appf(m.s[cfsTypeInit3], '$1[$2] = &$3;$n', [tmp, toRope(i), tmp2]);
|
|
useMagic(m, 'chckNil');
|
|
appf(m.s[cfsTypeInit3], '$1.kind = 1;$n' +
|
|
'$1.offset = offsetof($2, Field$3);$n' +
|
|
'$1.typ = $4;$n' +
|
|
'chckNil($1.typ);$n' +
|
|
'$1.name = "Field$3";$n',
|
|
[tmp2, getTypeDesc(m, typ), toRope(i),
|
|
genTypeInfo(m, a)]);
|
|
end;
|
|
appf(m.s[cfsTypeInit3],
|
|
'$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n', [
|
|
expr, toRope(len), tmp]);
|
|
end
|
|
else
|
|
appf(m.s[cfsTypeInit3],
|
|
'$1.len = $2; $1.kind = 2;$n', [expr, toRope(len)]);
|
|
appf(m.s[cfsTypeInit3], '$1->node = &$2;$n', [name, tmp]);
|
|
end;
|
|
|
|
procedure genEnumInfo(m: BModule; typ: PType; name: PRope);
|
|
var
|
|
nodePtrs, elemNode, enumNames, enumArray, counter, specialCases: PRope;
|
|
len, i, firstNimNode: int;
|
|
field: PSym;
|
|
begin
|
|
// Type information for enumerations is quite heavy, so we do some
|
|
// optimizations here: The ``typ`` field is never set, as it is redundant
|
|
// anyway. We generate a cstring array and a loop over it. Exceptional
|
|
// positions will be reset after the loop.
|
|
genTypeInfoAux(m, typ, name);
|
|
nodePtrs := getTempName();
|
|
len := sonsLen(typ.n);
|
|
appf(m.s[cfsTypeInit1], 'static TNimNode* $1[$2];$n',
|
|
[nodePtrs, toRope(len)]);
|
|
enumNames := nil;
|
|
specialCases := nil;
|
|
firstNimNode := m.typeNodes;
|
|
for i := 0 to len-1 do begin
|
|
assert(typ.n.sons[i].kind = nkSym);
|
|
field := typ.n.sons[i].sym;
|
|
elemNode := getNimNode(m);
|
|
app(enumNames, makeCString(field.name.s));
|
|
if i < len-1 then app(enumNames, ', '+tnl);
|
|
if field.position <> i then
|
|
appf(specialCases, '$1.offset = $2;$n', [elemNode, toRope(field.position)]);
|
|
end;
|
|
enumArray := getTempName();
|
|
counter := getTempName();
|
|
appf(m.s[cfsTypeInit1], 'NI $1;$n', [counter]);
|
|
appf(m.s[cfsTypeInit1], 'static char* NIM_CONST $1[$2] = {$n$3};$n',
|
|
[enumArray, toRope(len), enumNames]);
|
|
appf(m.s[cfsTypeInit3], 'for ($1 = 0; $1 < $2; $1++) {$n' +
|
|
'$3[$1+$4].kind = 1;$n' +
|
|
'$3[$1+$4].offset = $1;$n' +
|
|
'$3[$1+$4].name = $5[$1];$n' +
|
|
'$6[$1] = &$3[$1+$4];$n' +
|
|
'}$n',
|
|
[counter, toRope(len), m.typeNodesName, toRope(firstNimNode),
|
|
enumArray, nodePtrs]);
|
|
app(m.s[cfsTypeInit3], specialCases);
|
|
appf(m.s[cfsTypeInit3],
|
|
'$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n$4->node = &$1;$n', [
|
|
getNimNode(m), toRope(len), nodePtrs, name]);
|
|
end;
|
|
|
|
procedure genSetInfo(m: BModule; typ: PType; name: PRope);
|
|
var
|
|
tmp: PRope;
|
|
begin
|
|
assert(typ.sons[0] <> nil);
|
|
genTypeInfoAux(m, typ, name);
|
|
tmp := getNimNode(m);
|
|
appf(m.s[cfsTypeInit3],
|
|
'$1.len = $2; $1.kind = 0;$n' +
|
|
'$3->node = &$1;$n', [tmp, toRope(firstOrd(typ)), name]);
|
|
end;
|
|
|
|
procedure genArrayInfo(m: BModule; typ: PType; name: PRope);
|
|
begin
|
|
genTypeInfoAuxBase(m, typ, name, genTypeInfo(m, typ.sons[1]));
|
|
end;
|
|
|
|
var
|
|
gToTypeInfoId: TIiTable;
|
|
|
|
(* // this does not work any longer thanks to separate compilation:
|
|
function getTypeInfoName(t: PType): PRope;
|
|
begin
|
|
result := ropef('NTI$1', [toRope(t.id)]);
|
|
end;*)
|
|
|
|
function genTypeInfo(m: BModule; typ: PType): PRope;
|
|
var
|
|
t: PType;
|
|
id: int;
|
|
dataGenerated: bool;
|
|
begin
|
|
t := getUniqueType(typ);
|
|
id := IiTableGet(gToTypeInfoId, t.id);
|
|
if id = invalidKey then begin
|
|
dataGenerated := false;
|
|
id := t.id; // getID();
|
|
IiTablePut(gToTypeInfoId, t.id, id);
|
|
end
|
|
else
|
|
dataGenerated := true;
|
|
result := ropef('NTI$1', [toRope(id)]);
|
|
if not IntSetContainsOrIncl(m.typeInfoMarker, id) then begin
|
|
// declare type information structures:
|
|
useMagic(m, 'TNimType');
|
|
useMagic(m, 'TNimNode');
|
|
appf(m.s[cfsVars], 'extern TNimType* $1; /* $2 */$n',
|
|
[result, toRope(typeToString(t))]);
|
|
end;
|
|
if dataGenerated then exit;
|
|
case t.kind of
|
|
tyEmpty: result := toRope('0'+'');
|
|
tyPointer, tyProc, tyBool, tyChar, tyCString, tyString,
|
|
tyInt..tyFloat128, tyVar:
|
|
genTypeInfoAuxBase(gNimDat, t, result, toRope('0'+''));
|
|
tyRef, tyPtr, tySequence, tyRange: genTypeInfoAux(gNimDat, t, result);
|
|
tyArrayConstr, tyArray: genArrayInfo(gNimDat, t, result);
|
|
tySet: genSetInfo(gNimDat, t, result);
|
|
tyEnum: genEnumInfo(gNimDat, t, result);
|
|
tyObject: genObjectInfo(gNimDat, t, result);
|
|
tyTuple: begin
|
|
if t.n <> nil then genObjectInfo(gNimDat, t, result)
|
|
else genTupleInfo(gNimDat, t, result);
|
|
end;
|
|
else InternalError('genTypeInfo(' + typekindToStr[t.kind] + ')');
|
|
end
|
|
end;
|
|
|
|
procedure genTypeSection(m: BModule; n: PNode);
|
|
begin
|
|
end;
|
|
|
|
(*
|
|
procedure genTypeSection(m: BModule; n: PNode);
|
|
var
|
|
i: int;
|
|
a: PNode;
|
|
t: PType;
|
|
begin
|
|
if not (optDeadCodeElim in gGlobalOptions) then 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, 'genTypeSection');
|
|
t := a.sons[0].sym.typ;
|
|
if (a.sons[2] = nil)
|
|
or not (a.sons[2].kind in [nkSym, nkIdent, nkAccQuoted]) then
|
|
if t <> nil then
|
|
case t.kind of
|
|
tyEnum, tyBool: begin
|
|
useMagic(m, 'TNimType');
|
|
useMagic(m, 'TNimNode');
|
|
genEnumInfo(m, t, ropef('NTI$1', [toRope(t.id)]));
|
|
end;
|
|
tyObject: begin
|
|
if not isPureObject(t) then begin
|
|
useMagic(m, 'TNimType');
|
|
useMagic(m, 'TNimNode');
|
|
genObjectInfo(m, t, ropef('NTI$1', [toRope(t.id)]));
|
|
end
|
|
end
|
|
else begin end
|
|
end
|
|
end
|
|
end
|
|
end;
|
|
*)
|