123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- {
- Copyright (c) 2013 by Jonas Maebe
- Includes the llvm parameter manager
- 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 llvmpara;
- {$i fpcdefs.inc}
- interface
- uses
- globtype,aasmdata,
- symconst,symtype,symdef,symsym,
- parabase,
- cpupara;
- type
- { LLVM stands for "low level code generator", and regarding parameter
- handling it is indeed very low level. We are responsible for decomposing
- aggregate parameters into multiple simple parameters in case they have
- to be passed in special registers (such as floating point or SSE), and
- also for indicating whether e.g. 8 bit parameters need to be sign or
- zero exntended. This corresponds to pretty much what we do when creating
- parameter locations, so we reuse the original parameter manager and then
- process its output.
- The future will tell whether we can do this without
- architecture-specific code, or whether we will have to integrate parts
- into the various tcpuparamanager classes }
- tllvmparamanager = class(tcpuparamanager)
- function param_use_paraloc(const cgpara: tcgpara): boolean; override;
- procedure createtempparaloc(list: TAsmList; calloption: tproccalloption; parasym: tparavarsym; can_use_final_stack_loc: boolean; var cgpara: TCGPara); override;
- function create_paraloc_info(p: tabstractprocdef; side: tcallercallee): longint; override;
- function get_funcretloc(p: tabstractprocdef; side: tcallercallee; forcetempdef: tdef): tcgpara; override;
- private
- procedure set_llvm_paraloc_name(p: tabstractprocdef; hp: tparavarsym; var para: tcgpara);
- procedure add_llvm_callee_paraloc_names(p: tabstractprocdef);
- end;
- implementation
- uses
- aasmbase,
- llvmsym,
- paramgr,
- cgbase,hlcgobj;
- { tllvmparamanager }
- function tllvmparamanager.param_use_paraloc(const cgpara: tcgpara): boolean;
- begin
- { we can use the paraloc on the callee side if the SSA property is
- guaranteed, i.e., if it is a constant location (and if it's not been
- split up into multiple locations for ABI reasons). We can't deduce that
- from the paraloc though, we need the parasym for that. Potential
- future optimisation, although llvm will probably optimise away the
- temps we create anyway }
- result:=false;
- end;
- procedure tllvmparamanager.createtempparaloc(list: TAsmList; calloption: tproccalloption; parasym: tparavarsym; can_use_final_stack_loc: boolean; var cgpara: TCGPara);
- begin
- inherited;
- end;
- function tllvmparamanager.create_paraloc_info(p: tabstractprocdef; side: tcallercallee): longint;
- begin
- result:=inherited create_paraloc_info(p, side);
- { on the calleeside, llvm declares the parameters similar to Pascal or C
- (a list of parameters and their types), but they correspond more
- closely to parameter locations than to parameters -> add names to the
- locations }
- if side=calleeside then
- add_llvm_callee_paraloc_names(p)
- end;
- { hp non-nil: parasym to check
- hp nil: function result
- }
- procedure tllvmparamanager.set_llvm_paraloc_name(p: tabstractprocdef; hp: tparavarsym; var para: tcgpara);
- { the default for llvm is to pass aggregates in integer registers or
- on the stack (as the ABI prescribes). Records that require special
- handling, e.g. (partly) passing in fpu registers, have to be handled
- explicitly. This function returns whether an aggregate is handled
- specially }
- function has_non_default_paraloc: boolean;
- var
- loc: PCGParaLocation;
- begin
- loc:=para.Location;
- result:=true;
- while assigned(loc) do
- begin
- if not(loc^.loc in [LOC_REGISTER,LOC_REFERENCE]) then
- exit;
- end;
- result:=false;
- end;
- var
- paraloc: PCGParaLocation;
- signextstr: TSymStr;
- usedef: tdef;
- paralocnr: longint;
- begin
- { byval: a pointer to a type that should actually be passed by
- value (e.g. a record that should be passed on the stack) }
- if (assigned(hp) and
- (hp.vardef.typ in [arraydef,recorddef,objectdef]) and
- not paramanager.push_addr_param(hp.varspez,hp.vardef,p.proccalloption) and
- not has_non_default_paraloc) or
- (not assigned(hp) and
- ret_in_param(para.def,p)) then
- begin
- { the first location is the name of the "byval" parameter }
- paraloc:=para.location;
- if assigned(hp) then
- begin
- paraloc^.llvmloc:=current_asmdata.DefineAsmSymbol(llvmparaname(hp,0),AB_LOCAL,AT_DATA);
- paraloc^.llvmvalueloc:=false;
- end
- else
- begin
- paraloc^.llvmloc:=current_asmdata.DefineAsmSymbol('%f.result',AB_LOCAL,AT_DATA);
- paraloc^.llvmvalueloc:=true;
- end;
- { the other locations, if any, are not represented individually; llvm
- takes care of them behind the scenes }
- while assigned(paraloc^.next) do
- begin
- paraloc:=paraloc^.next;
- paraloc^.llvmloc:=nil;
- end;
- exit;
- end;
- paraloc:=hp.paraloc[calleeside].location;
- paralocnr:=0;
- repeat
- paraloc^.llvmloc:=current_asmdata.DefineAsmSymbol(llvmparaname(hp,paralocnr),AB_LOCAL,AT_DATA);
- paraloc^.llvmvalueloc:=true;
- paraloc:=paraloc^.next;
- inc(paralocnr);
- until not assigned(paraloc);
- end;
- function tllvmparamanager.get_funcretloc(p: tabstractprocdef; side: tcallercallee; forcetempdef: tdef): tcgpara;
- begin
- result:=inherited;
- { TODO: initialise result.llvmloc }
- end;
- procedure tllvmparamanager.add_llvm_callee_paraloc_names(p: tabstractprocdef);
- var
- paranr: longint;
- para: tcgpara;
- hp: tparavarsym;
- first: boolean;
- begin
- first:=true;
- for paranr:=0 to p.paras.count-1 do
- begin
- hp:=tparavarsym(p.paras[paranr]);
- set_llvm_paraloc_name(p,hp,hp.paraloc[calleeside]);
- end;
- end;
- begin
- { replace the native code generator. Maybe this has to be moved to a procedure
- like the creations of the code generators, but possibly not since we still
- call the original paramanager }
- paramanager.free;
- paramanager:=tllvmparamanager.create;
- end.
|