diff options
Diffstat (limited to 'compiler/paramgr.pas')
-rw-r--r-- | compiler/paramgr.pas | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/compiler/paramgr.pas b/compiler/paramgr.pas new file mode 100644 index 0000000000..1d0a0159e6 --- /dev/null +++ b/compiler/paramgr.pas @@ -0,0 +1,385 @@ +{ + Copyright (c) 2002 by Florian Klaempfl + + Generic calling convention handling + + 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. + **************************************************************************** +} +{# Parameter passing manager. Used to manage how + parameters are passed to routines. +} +unit paramgr; + +{$i fpcdefs.inc} + + interface + + uses + cclasses,globtype, + cpubase,cgbase, + parabase, + aasmtai, + symconst,symtype,symsym,symdef; + + type + {# This class defines some methods to take care of routine + parameters. It should be overriden for each new processor + } + tparamanager = class + { true if the location in paraloc can be reused as localloc } + function param_use_paraloc(const cgpara:tcgpara):boolean;virtual; + {# Returns true if the return value is actually a parameter + pointer. + } + function ret_in_param(def : tdef;calloption : tproccalloption) : boolean;virtual; + + function push_high_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;virtual; + + { Returns true if a parameter is too large to copy and only + the address is pushed + } + function push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;virtual;abstract; + { return the size of a push } + function push_size(varspez:tvarspez;def : tdef;calloption : tproccalloption) : longint; + {# Returns a structure giving the information on + the storage of the parameter (which must be + an integer parameter). This is only used when calling + internal routines directly, where all parameters must + be 4-byte values. + + In case the location is a register, this register is allocated. + Call freeintparaloc() after the call to free the locations again. + Default implementation: don't do anything at all (in case you don't + use register parameter passing) + + @param(list Current assembler list) + @param(nr Parameter number of routine, starting from 1) + } + function get_para_align(calloption : tproccalloption):byte;virtual; + function get_volatile_registers_int(calloption : tproccalloption):tcpuregisterset;virtual; + function get_volatile_registers_fpu(calloption : tproccalloption):tcpuregisterset;virtual; + function get_volatile_registers_flags(calloption : tproccalloption):tcpuregisterset;virtual; + function get_volatile_registers_mm(calloption : tproccalloption):tcpuregisterset;virtual; + + procedure getintparaloc(calloption : tproccalloption; nr : longint;var cgpara:TCGPara);virtual;abstract; + + {# allocate a parameter location created with create_paraloc_info + + @param(list Current assembler list) + @param(loc Parameter location) + } + procedure allocparaloc(list: taasmoutput; const cgpara: TCGPara); virtual; + + {# free a parameter location allocated with alloccgpara + + @param(list Current assembler list) + @param(loc Parameter location) + } + procedure freeparaloc(list: taasmoutput; const cgpara: TCGPara); virtual; + + { This is used to populate the location information on all parameters + for the routine as seen in either the caller or the callee. It returns + the size allocated on the stack + } + function create_paraloc_info(p : tabstractprocdef; side: tcallercallee):longint;virtual;abstract; + + { This is used to populate the location information on all parameters + for the routine when it is being inlined. It returns + the size allocated on the stack + } + function create_inline_paraloc_info(p : tabstractprocdef):longint;virtual; + + { This is used to populate the location information on all parameters + for the routine that are passed as varargs. It returns + the size allocated on the stack (including the normal parameters) + } + function create_varargs_paraloc_info(p : tabstractprocdef; varargspara:tvarargsparalist):longint;virtual;abstract; + + procedure createtempparaloc(list: taasmoutput;calloption : tproccalloption;parasym : tparavarsym;var cgpara:TCGPara);virtual; + procedure duplicateparaloc(list: taasmoutput;calloption : tproccalloption;parasym : tparavarsym;var cgpara:TCGPara); + + function parseparaloc(parasym : tparavarsym;const s : string) : boolean;virtual;abstract; + end; + + + var + paramanager : tparamanager; + + +implementation + + uses + systems, + cgobj,tgobj,cgutils, + defutil,verbose; + + { true if the location in paraloc can be reused as localloc } + function tparamanager.param_use_paraloc(const cgpara:tcgpara):boolean; + begin + result:=false; + end; + + + { true if uses a parameter as return value } + function tparamanager.ret_in_param(def : tdef;calloption : tproccalloption) : boolean; + begin + ret_in_param:=((def.deftype=arraydef) and not(is_dynamic_array(def))) or + (def.deftype=recorddef) or + ((def.deftype=stringdef) and (tstringdef(def).string_typ in [st_shortstring,st_longstring])) or + ((def.deftype=procvardef) and (po_methodpointer in tprocvardef(def).procoptions)) or + ((def.deftype=objectdef) and is_object(def)) or + (def.deftype=variantdef) or + ((def.deftype=setdef) and (tsetdef(def).settype<>smallset)); + end; + + + function tparamanager.push_high_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean; + begin + push_high_param:=not(calloption in [pocall_cdecl,pocall_cppdecl]) and + ( + is_open_array(def) or + is_open_string(def) or + is_array_of_const(def) + ); + end; + + + { return the size of a push } + function tparamanager.push_size(varspez:tvarspez;def : tdef;calloption : tproccalloption) : longint; + begin + push_size:=-1; + case varspez of + vs_out, + vs_var : + push_size:=sizeof(aint); + vs_value, + vs_const : + begin + if push_addr_param(varspez,def,calloption) then + push_size:=sizeof(aint) + else + begin + { special array are normally pushed by addr, only for + cdecl array of const it comes here and the pushsize + is unknown } + if is_array_of_const(def) then + push_size:=0 + else + push_size:=def.size; + end; + end; + end; + end; + + + function tparamanager.get_para_align(calloption : tproccalloption):byte; + begin + result:=std_param_align; + end; + + + function tparamanager.get_volatile_registers_int(calloption : tproccalloption):tcpuregisterset; + begin + result:=[]; + end; + + + function tparamanager.get_volatile_registers_fpu(calloption : tproccalloption):tcpuregisterset; + begin + result:=[]; + end; + + + function tparamanager.get_volatile_registers_flags(calloption : tproccalloption):tcpuregisterset; + begin + result:=[]; + end; + + + function tparamanager.get_volatile_registers_mm(calloption : tproccalloption):tcpuregisterset; + begin + result:=[]; + end; + + + procedure tparamanager.allocparaloc(list: taasmoutput; const cgpara: TCGPara); + var + paraloc : pcgparalocation; + begin + paraloc:=cgpara.location; + while assigned(paraloc) do + begin + case paraloc^.loc of + LOC_REGISTER, + LOC_CREGISTER: + begin + if getsupreg(paraloc^.register)<first_int_imreg then + cg.getcpuregister(list,paraloc^.register); + end; + LOC_FPUREGISTER, + LOC_CFPUREGISTER: + begin + if getsupreg(paraloc^.register)<first_fpu_imreg then + cg.getcpuregister(list,paraloc^.register); + end; + LOC_MMREGISTER, + LOC_CMMREGISTER : + begin + if getsupreg(paraloc^.register)<first_mm_imreg then + cg.getcpuregister(list,paraloc^.register); + end; + end; + paraloc:=paraloc^.next; + end; + end; + + + procedure tparamanager.freeparaloc(list: taasmoutput; const cgpara: TCGPara); + var + paraloc : Pcgparalocation; +{$ifdef cputargethasfixedstack} + href : treference; +{$endif cputargethasfixedstack} + begin + paraloc:=cgpara.location; + while assigned(paraloc) do + begin + case paraloc^.loc of + LOC_VOID: + ; + LOC_REGISTER, + LOC_CREGISTER: + begin + if getsupreg(paraloc^.register)<first_int_imreg then + cg.ungetcpuregister(list,paraloc^.register); + end; + LOC_FPUREGISTER, + LOC_CFPUREGISTER: + begin + if getsupreg(paraloc^.register)<first_fpu_imreg then + cg.ungetcpuregister(list,paraloc^.register); + end; + LOC_MMREGISTER, + LOC_CMMREGISTER : + begin + if getsupreg(paraloc^.register)<first_mm_imreg then + cg.ungetcpuregister(list,paraloc^.register); + end; + LOC_REFERENCE, + LOC_CREFERENCE : + begin +{$ifdef cputargethasfixedstack} + { don't use reference_reset_base, because that will depend on cgobj } + fillchar(href,sizeof(href),0); + href.base:=paraloc^.reference.index; + href.offset:=paraloc^.reference.offset; + tg.ungettemp(list,href); +{$endif cputargethasfixedstack} + end; + else + internalerror(2004110212); + end; + paraloc:=paraloc^.next; + end; + end; + + + procedure tparamanager.createtempparaloc(list: taasmoutput;calloption : tproccalloption;parasym : tparavarsym;var cgpara:TCGPara); + var + href : treference; + len : aint; + paraloc, + newparaloc : pcgparalocation; + begin + cgpara.reset; + cgpara.size:=parasym.paraloc[callerside].size; + cgpara.intsize:=parasym.paraloc[callerside].intsize; + cgpara.alignment:=parasym.paraloc[callerside].alignment; +{$ifdef powerpc} + cgpara.composite:=parasym.paraloc[callerside].composite; +{$endif powerpc} + paraloc:=parasym.paraloc[callerside].location; + while assigned(paraloc) do + begin + if paraloc^.size=OS_NO then + len:=push_size(parasym.varspez,parasym.vartype.def,calloption) + else + len:=tcgsize2size[paraloc^.size]; + newparaloc:=cgpara.add_location; + newparaloc^.size:=paraloc^.size; +{$warning maybe release this optimization for all targets?} +{$ifdef sparc} + { Does it fit a register? } + if len<=sizeof(aint) then + newparaloc^.loc:=LOC_REGISTER + else +{$endif sparc} + newparaloc^.loc:=paraloc^.loc; + case newparaloc^.loc of + LOC_REGISTER : + newparaloc^.register:=cg.getintregister(list,paraloc^.size); + LOC_FPUREGISTER : + newparaloc^.register:=cg.getfpuregister(list,paraloc^.size); + LOC_MMREGISTER : + newparaloc^.register:=cg.getmmregister(list,paraloc^.size); + LOC_REFERENCE : + begin + tg.gettemp(list,len,tt_persistent,href); + newparaloc^.reference.index:=href.base; + newparaloc^.reference.offset:=href.offset; + end; + end; + paraloc:=paraloc^.next; + end; + end; + + + procedure tparamanager.duplicateparaloc(list: taasmoutput;calloption : tproccalloption;parasym : tparavarsym;var cgpara:TCGPara); + var + paraloc, + newparaloc : pcgparalocation; + begin + cgpara.reset; + cgpara.size:=parasym.paraloc[callerside].size; + cgpara.intsize:=parasym.paraloc[callerside].intsize; + cgpara.alignment:=parasym.paraloc[callerside].alignment; +{$ifdef powerpc} + cgpara.composite:=parasym.paraloc[callerside].composite; +{$endif powerpc} + paraloc:=parasym.paraloc[callerside].location; + while assigned(paraloc) do + begin + newparaloc:=cgpara.add_location; + move(paraloc^,newparaloc^,sizeof(newparaloc^)); + newparaloc^.next:=nil; + paraloc:=paraloc^.next; + end; + end; + + + function tparamanager.create_inline_paraloc_info(p : tabstractprocdef):longint; + begin + { We need to return the size allocated } + create_paraloc_info(p,callerside); + result:=create_paraloc_info(p,calleeside); + end; + + +initialization + ; +finalization + paramanager.free; +end. |