{ Copyright (c) 2002 by Florian Klaempfl Generates the argument location information for 680x0 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 cpupara; {$i fpcdefs.inc} interface uses globtype, cpubase, aasmdata, symconst,symtype,symdef,symsym, parabase,paramgr,cgbase,cgutils; type { Returns the location for the nr-st 32 Bit int parameter if every parameter before is an 32 Bit int parameter as well and if the calling conventions for the helper routines of the rtl are used. } tcpuparamanager = class(tparamanager) function ret_in_param(def:tdef;pd:tabstractprocdef):boolean;override; function param_use_paraloc(const cgpara:tcgpara):boolean;override; function create_paraloc_info(p : tabstractprocdef; side: tcallercallee):longint;override; function push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;override; function get_funcretloc(p : tabstractprocdef; side: tcallercallee; forcetempdef: tdef): tcgpara;override; procedure createtempparaloc(list: TAsmList;calloption : tproccalloption;parasym : tparavarsym;can_use_final_stack_loc : boolean;var cgpara:TCGPara);override; function create_varargs_paraloc_info(p : tabstractprocdef; varargspara:tvarargsparalist):longint;override; function parsefuncretloc(p : tabstractprocdef; const s : string) : boolean;override; function get_volatile_registers_int(calloption:tproccalloption):tcpuregisterset;override; function get_volatile_registers_address(calloption:tproccalloption):tcpuregisterset;override; function get_volatile_registers_fpu(calloption:tproccalloption):tcpuregisterset;override; function get_saved_registers_int(calloption:tproccalloption):tcpuregisterarray;override; function get_saved_registers_address(calloption:tproccalloption):tcpuregisterarray;override; function get_saved_registers_fpu(calloption:tproccalloption):tcpuregisterarray;override; function get_para_align(calloption : tproccalloption):byte;override; private function parse_loc_string_to_register(var locreg: tregister; const s : string): boolean; function create_stdcall_paraloc_info(p : tabstractprocdef; side: tcallercallee; paras: tparalist; var cur_stack_offset: aword):longint; function create_register_paraloc_info(p : tabstractprocdef; side: tcallercallee; paras: tparalist; var cur_stack_offset: aword):longint; end; implementation uses verbose, globals, systems, cpuinfo, defutil, cutils, hlcgobj; const intparasupregs : array[0..1] of tsuperregister = (RS_D0,RS_D1); addrparasupregs : array[0..1] of tsuperregister = (RS_A0,RS_A1); floatparasupregs : array[0..1] of tsuperregister = (RS_FP0,RS_FP1); function tcpuparamanager.get_volatile_registers_int(calloption:tproccalloption):tcpuregisterset; begin { d0 and d1 are considered volatile } Result:=VOLATILE_INTREGISTERS; if target_info.system in [system_m68k_palmos] then include(result,RS_D2); end; function tcpuparamanager.get_volatile_registers_address(calloption:tproccalloption):tcpuregisterset; begin { a0 and a1 are considered volatile } Result:=VOLATILE_ADDRESSREGISTERS; if target_info.system in [system_m68k_palmos] then include(result,RS_A2); end; function tcpuparamanager.get_volatile_registers_fpu(calloption:tproccalloption):tcpuregisterset; begin { fp0 and fp1 are considered volatile } Result:=VOLATILE_FPUREGISTERS; end; function tcpuparamanager.get_saved_registers_int(calloption:tproccalloption):tcpuregisterarray; const saved_regs: array[0..5] of tsuperregister = (RS_D2,RS_D3,RS_D4,RS_D5,RS_D6,RS_D7); begin result:=saved_regs; end; function tcpuparamanager.get_saved_registers_address(calloption:tproccalloption):tcpuregisterarray; const saved_addr_regs: array[0..4] of tsuperregister = (RS_A2,RS_A3,RS_A4,RS_A5,RS_A6); begin result:=saved_addr_regs; end; function tcpuparamanager.get_saved_registers_fpu(calloption:tproccalloption):tcpuregisterarray; const saved_fpu_regs: array[0..5] of tsuperregister = (RS_FP2,RS_FP3,RS_FP4,RS_FP5,RS_FP6,RS_FP7); begin result:=saved_fpu_regs; end; function tcpuparamanager.get_para_align(calloption : tproccalloption):byte; begin result:=target_info.stackalign; end; function tcpuparamanager.param_use_paraloc(const cgpara:tcgpara):boolean; var paraloc : pcgparalocation; begin if not assigned(cgpara.location) then internalerror(200410102); result:=true; { All locations are LOC_REFERENCE } paraloc:=cgpara.location; while assigned(paraloc) do begin if (paraloc^.loc<>LOC_REFERENCE) then begin result:=false; exit; end; paraloc:=paraloc^.next; end; end; { TODO: copied from ppc cg, needs work} function tcpuparamanager.push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean; begin result:=false; { var,out,constref always require address } if varspez in [vs_var,vs_out,vs_constref] then begin result:=true; exit; end; case def.typ of variantdef, formaldef : result:=true; recorddef: result:=(calloption in [pocall_register]) and (varspez in [vs_const]); arraydef: result:=(tarraydef(def).highrange>=tarraydef(def).lowrange) or is_open_array(def) or is_array_of_const(def) or is_array_constructor(def); objectdef : result:=is_object(def); setdef : result:=not is_smallset(def); stringdef : result:=tstringdef(def).stringtype in [st_shortstring,st_longstring]; procvardef : { Handling of methods must match that of records } result:=false; end; end; function tcpuparamanager.ret_in_param(def:tdef;pd:tabstractprocdef):boolean; begin if handle_common_ret_in_param(def,pd,result) then exit; case def.typ of recorddef: if def.size in [1,2,4] then begin result:=false; exit; end; end; result:=inherited ret_in_param(def,pd); end; function tcpuparamanager.get_funcretloc(p : tabstractprocdef; side: tcallercallee; forcetempdef: tdef): tcgpara; var paraloc : pcgparalocation; retcgsize : tcgsize; retregtype : tregistertype; begin if set_common_funcretloc_info(p,forcetempdef,retcgsize,result) then exit; { always use the whole 32 bit register when returning values } if (side=calleeside) and (result.intsize>0) and (result.intsizerecorddef) then begin paracgsize:=OS_ADDR; paralen := tcgsize2size[OS_ADDR]; end; end; hp.paraloc[side].alignment:=paraalign; hp.paraloc[side].size:=paracgsize; hp.paraloc[side].intsize:=paralen; hp.paraloc[side].def:=paradef; if (paralen = 0) then if (paradef.typ = recorddef) then begin paraloc:=hp.paraloc[side].add_location; paraloc^.loc := LOC_VOID; end else internalerror(200506052); firstparaloc:=true; { can become < 0 for e.g. 3-byte records } while (paralen > 0) do begin paraloc:=hp.paraloc[side].add_location; paraloc^.def:=get_paraloc_def(paradef,paralen,firstparaloc); if (not (cs_fp_emulation in current_settings.moduleswitches)) and (paradef.typ=floatdef) then paraloc^.size:=int_float_cgsize(paralen) else paraloc^.size:=int_cgsize(paralen); { various m68k based C ABIs used in the Unix world use a register to return a struct by address. we will probably need some kind of a switch to support these various ABIs when generating cdecl calls (KB) } if ((vo_is_funcret in hp.varoptions) and (tabstractprocdef(p).proccalloption in [pocall_cdecl,pocall_cppdecl]) and (target_info.system in [system_m68k_linux]) and (tabstractprocdef(p).returndef.typ = recorddef)) then begin paraloc^.loc:=LOC_REGISTER; paraloc^.register:=NR_M68K_STRUCT_RESULT_REG; paralen:=0; continue; end; paraloc^.loc:=LOC_REFERENCE; paraloc^.reference.offset:=cur_stack_offset; if (side = callerside) then paraloc^.reference.index:=NR_STACK_POINTER_REG else begin paraloc^.reference.index:=NR_FRAME_POINTER_REG; inc(paraloc^.reference.offset,target_info.first_parm_offset); end; { M68K is a big-endian target } if (paralen0) do begin paraloc:=hp.paraloc[side].add_location; paraloc^.loc:=LOC_REFERENCE; { Extended and double need a single location } if (paracgsize in [OS_F64,OS_F32]) then begin paraloc^.size:=paracgsize; paraloc^.def:=paradef; l:=paralen; end else begin { We can allocate at maximum 32 bits per location } if paralen>sizeof(aint) then begin l:=sizeof(aint); paraloc^.def:=uinttype; end else begin l:=paralen; paraloc^.def:=get_paraloc_def(paradef,l,firstparaloc); end; paraloc^.size:=int_cgsize(l); end; if side=callerside then paraloc^.reference.index:=NR_STACK_POINTER_REG else paraloc^.reference.index:=NR_FRAME_POINTER_REG; varalign:=used_align(size_2_align(l),paraalign,paraalign); paraloc^.reference.offset:=cur_stack_offset; { M68K is a big-endian target } if (paralen NR_NO) and (locreg <> NR_SP); end; function tcpuparamanager.parsefuncretloc(p : tabstractprocdef; const s : string) : boolean; begin case target_info.system of system_m68k_amiga: result:=parse_loc_string_to_register(p.exp_funcretloc, s); else internalerror(2005121801); end; end; procedure tcpuparamanager.createtempparaloc(list: TAsmList;calloption : tproccalloption;parasym : tparavarsym;can_use_final_stack_loc : boolean;var cgpara:TCGPara); begin { Never a need for temps when value is pushed (calls inside parameters will simply allocate even more stack space for their parameters) } if not(use_fixed_stack) then can_use_final_stack_loc:=true; inherited createtempparaloc(list,calloption,parasym,can_use_final_stack_loc,cgpara); end; function tcpuparamanager.create_varargs_paraloc_info(p : tabstractprocdef; varargspara:tvarargsparalist):longint; var cur_stack_offset: aword; begin cur_stack_offset:=0; result:=create_stdcall_paraloc_info(p,callerside,p.paras,cur_stack_offset); if (p.proccalloption in cstylearrayofconst) then { just continue loading the parameters in the registers } result:=create_stdcall_paraloc_info(p,callerside,varargspara,cur_stack_offset) else internalerror(200410231); end; begin paramanager:=tcpuparamanager.create; end.