{ Copyright (c) 2013 by Jonas Maebe This unit implements some LLVM type helper routines. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. **************************************************************************** } {$i fpcdefs.inc} unit llvmdef; interface uses globtype, symbase,symtype,symdef; { Encode a type into the internal format used by LLVM. } function llvmencodetype(def: tdef): TSymStr; { incremental version of llvmencodetype(). "inaggregate" indicates whether this was a recursive call to get the type of an entity part of an aggregate type (array, record, ...) } procedure llvmaddencodedtype(def: tdef; inaggregate: boolean; var encodedstr: TSymStr); function llvmencodeproctype(def: tabstractprocdef; withprocname, withparanames: boolean): TSymStr; procedure llvmaddencodedproctype(def: tabstractprocdef; withprocname, withparanames: boolean; var encodedstr: TSymStr); implementation uses cutils,cclasses,constexp, verbose,systems, fmodule, symtable,symconst,symsym, llvmsym, defutil,cgbase,parabase,paramgr; {****************************************************************** Type encoding *******************************************************************} procedure llvmaddencodedtype(def: tdef; inaggregate: boolean; var encodedstr: TSymStr); begin case def.typ of stringdef : begin case tstringdef(def).stringtype of st_widestring, st_unicodestring: { the variable does not point to the header, but to a null-terminated string/array with undefined bounds } encodedstr:=encodedstr+'[0 x i16]'; st_ansistring: encodedstr:=encodedstr+'[0 x i8]'; st_shortstring: { length byte followed by string bytes } if tstringdef(def).len>0 then encodedstr:=encodedstr+'{i8, ['+tostr(tstringdef(def).len)+' x i8]}' else encodedstr:=encodedstr+'{i8, [0 x i8]}'; else internalerror(2013100201); end; end; enumdef: begin encodedstr:=encodedstr+'i'+tostr(def.size*8); end; orddef : begin if is_void(def) then encodedstr:=encodedstr+'void' { mainly required because comparison operations return i1, and otherwise we always have to immediatel extend them to i8 for no good reason; besides, Pascal booleans can only contain 0 or 1 in valid code anyway (famous last words...) } else if torddef(def).ordtype=pasbool8 then encodedstr:=encodedstr+'i1' else encodedstr:=encodedstr+'i'+tostr(def.size*8); end; pointerdef : begin if is_voidpointer(def) then encodedstr:=encodedstr+'i8*' else begin llvmaddencodedtype(tpointerdef(def).pointeddef,false,encodedstr); encodedstr:=encodedstr+'*'; end; end; floatdef : begin case tfloatdef(def).floattype of s32real: encodedstr:=encodedstr+'float'; s64real: encodedstr:=encodedstr+'double'; { necessary to be able to force our own size/alignment } s80real: { prevent llvm from allocating the standard ABI size for extended } if inaggregate then encodedstr:=encodedstr+'[10 x i8]' else encodedstr:=encodedstr+'x86_fp80'; sc80real: encodedstr:=encodedstr+'x86_fp80'; s64comp, s64currency: encodedstr:=encodedstr+'i64'; s128real: {$if defined(powerpc) or defined(powerpc128)} encodedstr:=encodedstr+'ppc_fp128'; {$else} encodedstr:=encodedstr+'fp128'; {$endif} else internalerror(2013100202); end; end; filedef : begin case tfiledef(def).filetyp of ft_text : llvmaddencodedtype(search_system_type('TEXTREC').typedef,false,encodedstr); ft_typed, ft_untyped : llvmaddencodedtype(search_system_type('FILEREC').typedef,false,encodedstr); else internalerror(2013100203); end; end; recorddef : begin { for now don't encode the individual fields, because handling variant records is a pain. As far as correctness is concerned, the types of the fields only matter for the parameters and function result types, but for those we have to use what the parameter manager calculates anyway (because e.g. a record with two floats has to be passed in an SSE register on x86-64) } encodedstr:=encodedstr+'{[i8 x '+tostr(def.size)+']}' end; variantdef : begin llvmaddencodedtype(search_system_type('TVARDATA').typedef,false,encodedstr); end; classrefdef : begin { todo: define proper type for VMT and use that } encodedstr:=encodedstr+'i8*'; end; setdef : begin { just an array as far as llvm is concerned; don't use a "packed array of i1" or so, this requires special support in backends and guarantees nothing about the internal format } encodedstr:=encodedstr+'['+tostr(tsetdef(def).size)+' x i8]'; end; formaldef : begin { var/const/out x } encodedstr:=encodedstr+'i8*'; end; arraydef : begin if is_array_of_const(def) then begin encodedstr:=encodedstr+'[0 x '; llvmaddencodedtype(search_system_type('TVARREC').typedef,true,encodedstr); encodedstr:=encodedstr+']'; end else if is_open_array(def) then begin encodedstr:=encodedstr+'[0 x '; llvmaddencodedtype(tarraydef(def).elementdef,true,encodedstr); encodedstr:=encodedstr+']'; end else if is_dynamic_array(def) then begin llvmaddencodedtype(tarraydef(def).elementdef,false,encodedstr); encodedstr:=encodedstr+'*'; end else if is_packed_array(def) then begin encodedstr:=encodedstr+'['+tostr(tarraydef(def).size div tarraydef(def).elementdef.packedbitsize)+' x '; { encode as an array of integers with the size on which we perform the packedbits operations } llvmaddencodedtype(cgsize_orddef(int_cgsize(packedbitsloadsize(tarraydef(def).elementdef.packedbitsize))),true,encodedstr); encodedstr:=encodedstr+']'; end else begin encodedstr:=encodedstr+'['+tostr(tarraydef(def).elecount)+' x '; llvmaddencodedtype(tarraydef(def).elementdef,true,encodedstr); encodedstr:=encodedstr+']'; end; end; procvardef : begin if tprocvardef(def).is_addressonly then begin llvmaddencodedproctype(tprocdef(def),false,false,encodedstr); encodedstr:=encodedstr+'*'; end else begin encodedstr:=encodedstr+'{'; { code pointer } llvmaddencodedproctype(tprocvardef(def),false,false,encodedstr); { data pointer (maybe todo: generate actual layout if available) } encodedstr:=encodedstr+'*, i8*}'; end; end; objectdef : case tobjectdef(def).objecttype of odt_class, odt_objcclass, odt_object, odt_cppclass: begin { for now don't handle fields yet } encodedstr:=encodedstr+'{[i8 x '+tostr(def.size)+']}'; if is_implicit_pointer_object_type(def) then encodedstr:=encodedstr+'*' end; odt_interfacecom, odt_interfacecom_function, odt_interfacecom_property, odt_interfacecorba, odt_dispinterface, odt_objcprotocol: begin { opaque for now } encodedstr:=encodedstr+'i8*' end; else internalerror(2013100601); end; undefineddef, errordef : internalerror(2013100604); procdef : begin llvmaddencodedproctype(tprocdef(def),true,false,encodedstr); end; else internalerror(2013100603); end; end; procedure llvmrefineordinaldef(paradef, paralocdef: tdef; out usedef: tdef; out signextstr: TSymStr); begin { implicit zero/sign extension for ABI compliance? (yes, if the size of a paraloc is larger than the size of the entire parameter) } if is_ordinal(paradef) and is_ordinal(paralocdef) and (paradef.size'', there should be only one paraloc -> no need to clear (reason: it means that the paraloc is larger than the original parameter) } if assigned(hp) then encodedstr:=encodedstr+signextstr; if assigned(hp) then begin { sret: hidden pointer for structured function result } if vo_is_funcret in hp.varoptions then encodedstr:=encodedstr+' sret' end; if withparaname then encodedstr:=encodedstr+' '+paraloc^.llvmloc.name; paraloc:=paraloc^.next; until not assigned(paraloc); if closestruct then encodedstr:=encodedstr+'}' end; function llvmencodeproctype(def: tabstractprocdef; withprocname, withparanames: boolean): TSymStr; begin result:=''; llvmaddencodedproctype(def,withprocname,withparanames,result); end; procedure llvmaddencodedproctype(def: tabstractprocdef; withprocname, withparanames: boolean; var encodedstr: TSymStr); var paranr: longint; para: tcgpara; hp: tparavarsym; first: boolean; begin def.init_paraloc_info(calleeside); first:=true; { function result (return-by-ref is handled explicitly) } if not paramanager.ret_in_param(def.returndef,def) then llvmaddencodedparaloctype(nil,def.funcretloc[calleeside],def.proccalloption,false,first,encodedstr) else llvmaddencodedtype(voidtype,false,encodedstr); encodedstr:=encodedstr+' '; if withprocname and (def.typ=procdef) then encodedstr:=encodedstr+tprocdef(def).mangledname; encodedstr:=encodedstr+'('; { parameters } first:=true; for paranr:=0 to def.paras.count-1 do begin hp:=tparavarsym(def.paras[paranr]); llvmaddencodedparaloctype(hp,hp.paraloc[calleeside],def.proccalloption,withparanames,first,encodedstr); end; encodedstr:=encodedstr+')' end; function llvmencodetype(def: tdef): TSymStr; begin result:=''; llvmaddencodedtype(def,false,result); end; end.