fpc/compiler/llvm/nllvmcon.pas
Jonas Maebe 9bd33f7a45 + support for LLVM metadata constant string parameters
o they are implemented as a new metadata register class, whereby the
     subregister indicates the metadata type (currently always a string)
     and the superregister is an index in the metadata array (which
     contains the strings). LLVM metadata can only be passed as parameters
     to intrinsics in bitcode, so moves of metadata into other registers
     triggers internal errors and when moving them into parameters, we
     replace the parameter's register with the metadata register (and look
     up the corresponding string when writing out the bitcode)

git-svn-id: trunk@43816 -
2019-12-30 15:04:57 +00:00

222 lines
8.5 KiB
ObjectPascal

{
Copyright (c) 2013 by Jonas Maebe, member of the Free Pascal Compiler
development team
Generate llvm bitcode for constants
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.
****************************************************************************
}
unit nllvmcon;
{$i fpcdefs.inc}
interface
uses
symtype,
node,ncgcon;
type
tllvmrealconstnode = class(tcgrealconstnode)
function pass_1 : tnode;override;
procedure pass_generate_code;override;
end;
tllvmstringconstnode = class(tcgstringconstnode)
constructor createpchar(s: pchar; l: longint; def: tdef); override;
function pass_typecheck: tnode; override;
function pass_1: tnode; override;
procedure pass_generate_code; override;
protected
procedure load_dynstring(const strpointerdef: tdef; const elementdef: tdef; const winlikewidestring: boolean); override;
end;
implementation
uses
globtype,globals,verbose,cutils,
symbase,symtable,symconst,symdef,symsym,defutil,
aasmbase,aasmdata,aasmcnst,
ncon,
llvmbase,aasmllvm,aasmllvmmetadata,hlcgobj,
cgbase,cgutils,
cpubase;
{*****************************************************************************
tllvmstringconstnode
*****************************************************************************}
constructor tllvmstringconstnode.createpchar(s: pchar; l: longint; def: tdef);
begin
inherited;
if def=llvm_metadatatype then
begin
{ astringdef is only used if the constant type is ansitring }
cst_type:=cst_ansistring;
astringdef:=def;
end;
end;
function tllvmstringconstnode.pass_typecheck: tnode;
begin
if astringdef<>llvm_metadatatype then
begin
result:=inherited;
exit;
end;
resultdef:=llvm_metadatatype;
result:=nil;
end;
function tllvmstringconstnode.pass_1: tnode;
begin
if astringdef<>llvm_metadatatype then
begin
result:=inherited;
exit;
end;
expectloc:=LOC_CREGISTER;
result:=nil;
end;
procedure tllvmstringconstnode.pass_generate_code;
var
datadef, resptrdef: tdef;
hreg: tregister;
begin
if astringdef=llvm_metadatatype then
begin
location_reset(location,LOC_CREGISTER,OS_ADDR);
location.register:=tllvmmetadata.getpcharreg(value_str,len);
exit;
end;
inherited pass_generate_code;
if cst_type in [cst_conststring,cst_shortstring] then
begin
if location.loc<>LOC_CREFERENCE then
internalerror(2014071202);
case cst_type of
cst_conststring:
{ this kind of string const is used both for array of char
constants (-> excludes terminating #0) and pchars (-> includes
terminating #0). The resultdef excludes the #0 while the data
includes it -> insert typecast from datadef to resultdef }
datadef:=carraydef.getreusable(cansichartype,len+1);
cst_shortstring:
{ the resultdef of the string constant is the type of the
string to which it is assigned, which can be longer or shorter
than the length of the string itself -> typecast it to the
correct string type }
datadef:=carraydef.getreusable(cansichartype,min(len,255)+1);
else
internalerror(2014071203);
end;
{ get address of array as pchar }
resptrdef:=cpointerdef.getreusable(resultdef);
hreg:=hlcg.getaddressregister(current_asmdata.CurrAsmList,resptrdef);
hlcg.a_loadaddr_ref_reg(current_asmdata.CurrAsmList,datadef,resptrdef,location.reference,hreg);
hlcg.reference_reset_base(location.reference,resptrdef,hreg,0,location.reference.temppos,location.reference.alignment,location.reference.volatility);
end;
end;
procedure tllvmstringconstnode.load_dynstring(const strpointerdef: tdef; const elementdef: tdef; const winlikewidestring: boolean);
var
stringtype: tstringtype;
strrecdef: trecorddef;
offset: pint;
field: tfieldvarsym;
llvmfield: tllvmshadowsymtableentry;
dataptrdef: tdef;
reg: tregister;
href: treference;
begin
case cst_type of
cst_ansistring:
stringtype:=st_ansistring;
cst_unicodestring:
stringtype:=st_unicodestring;
cst_widestring:
stringtype:=st_widestring;
else
internalerror(2014040804);
end;
{ get the recorddef for this string constant }
strrecdef:=ctai_typedconstbuilder.get_dynstring_rec(stringtype,winlikewidestring,len);
{ offset in the record of the the string data }
offset:=ctai_typedconstbuilder.get_string_symofs(stringtype,winlikewidestring);
{ field corresponding to this offset }
field:=trecordsymtable(strrecdef.symtable).findfieldbyoffset(offset);
llvmfield:=trecordsymtable(strrecdef.symtable).llvmst[field];
if llvmfield.fieldoffset<>field.fieldoffset then
internalerror(2015061001);
{ pointerdef to the string data array }
dataptrdef:=cpointerdef.getreusable(field.vardef);
{ load the address of the string data }
reg:=hlcg.getaddressregister(current_asmdata.CurrAsmList,dataptrdef);
reference_reset_symbol(href,lab_str,0,const_align(strpointerdef.size),[]);
current_asmdata.CurrAsmList.concat(
taillvm.getelementptr_reg_size_ref_size_const(reg,cpointerdef.getreusable(strrecdef),href,
s32inttype,field.llvmfieldnr,true));
{ convert into a pointer to the individual elements }
hlcg.a_load_reg_reg(current_asmdata.CurrAsmList,dataptrdef,strpointerdef,reg,location.register);
end;
{*****************************************************************************
tllvmrealconstnode
*****************************************************************************}
function tllvmrealconstnode.pass_1 : tnode;
begin
result:=nil;
expectloc:=LOC_FPUREGISTER;
end;
procedure tllvmrealconstnode.pass_generate_code;
begin
{ llvm supports floating point constants directly }
location_reset(location,LOC_FPUREGISTER,def_cgsize(resultdef));
location.register:=hlcg.getfpuregister(current_asmdata.CurrAsmList,resultdef);
case tfloatdef(resultdef).floattype of
s32real,s64real:
current_asmdata.CurrAsmList.concat(taillvm.op_reg_size_fpconst_size(la_bitcast,location.register,resultdef,value_real,resultdef));
{ comp and currency are handled as int64 at the llvm level }
s64comp:
{ sc80floattype instead of resultdef, see comment in thlcgllvm.a_loadfpu_ref_reg }
current_asmdata.CurrAsmList.concat(taillvm.op_reg_size_const_size(la_sitofp,location.register,s64inttype,trunc(value_real),sc80floattype));
s64currency:
{ sc80floattype instead of resultdef, see comment in thlcgllvm.a_loadfpu_ref_reg }
current_asmdata.CurrAsmList.concat(taillvm.op_reg_size_const_size(la_sitofp,location.register,s64inttype,round(value_real),sc80floattype));
{$ifdef cpuextended}
s80real,sc80real:
current_asmdata.CurrAsmList.concat(taillvm.op_reg_size_fpconst80_size(la_bitcast,location.register,resultdef,value_real,resultdef));
{$endif cpuextended}
else
internalerror(2013102501);
end;
end;
begin
cstringconstnode:=tllvmstringconstnode;
crealconstnode:=tllvmrealconstnode;
end.