{ 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. **************************************************************************** } { Generates the argument location information for 680x0. } unit cpupara; {$i fpcdefs.inc} interface uses globtype, cpubase, aasmdata, symconst,symtype,symdef,symsym, parabase,paramgr,cgbase; 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. } tm68kparamanager = class(tparamanager) procedure getintparaloc(calloption : tproccalloption; nr : longint;var cgpara : TCGPara);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; def: tdef): tcgpara;override; procedure createtempparaloc(list: TAsmList;calloption : tproccalloption;parasym : tparavarsym;can_use_final_stack_loc : boolean;var cgpara:TCGPara); procedure create_funcretloc_info(p : tabstractprocdef; side: tcallercallee); private procedure init_values(var curintreg, curfloatreg: tsuperregister; var cur_stack_offset: aword); function create_paraloc_info_intern(p : tabstractprocdef; side: tcallercallee; paras: tparalist; var curintreg, curfloatreg: tsuperregister; var cur_stack_offset: aword):longint; function parseparaloc(p : tparavarsym;const s : string) : boolean;override; function parsefuncretloc(p : tabstractprocdef; const s : string) : boolean;override; end; implementation uses verbose, globals, systems, cpuinfo,cgutils, defutil; procedure tm68kparamanager.getintparaloc(calloption : tproccalloption; nr : longint;var cgpara : TCGPara); var paraloc : pcgparalocation; begin if nr<1 then internalerror(2002070801); cgpara.reset; cgpara.size:=OS_INT; cgpara.alignment:=std_param_align; paraloc:=cgpara.add_location; with paraloc^ do begin { warning : THIS ONLY WORKS WITH INTERNAL ROUTINES, WHICH MUST ALWAYS PASS 4-BYTE PARAMETERS!! } loc:=LOC_REFERENCE; reference.index:=NR_STACK_POINTER_REG; reference.offset:=target_info.first_parm_offset+nr*4; end; end; function getparaloc(p : tdef) : tcgloc; begin result:=LOC_REFERENCE; { Later, the LOC_REFERENCE is in most cases changed into LOC_REGISTER if push_addr_param for the def is true case p.typ of orddef: result:=LOC_REGISTER; floatdef: result:=LOC_FPUREGISTER; enumdef: result:=LOC_REGISTER; pointerdef: result:=LOC_REGISTER; formaldef: result:=LOC_REGISTER; classrefdef: result:=LOC_REGISTER; recorddef: if (target_info.abi<>abi_powerpc_aix) then result:=LOC_REFERENCE else result:=LOC_REGISTER; objectdef: if is_object(p) then result:=LOC_REFERENCE else result:=LOC_REGISTER; stringdef: if is_shortstring(p) or is_longstring(p) then result:=LOC_REFERENCE else result:=LOC_REGISTER; procvardef: if (po_methodpointer in tprocvardef(p).procoptions) then result:=LOC_REFERENCE else result:=LOC_REGISTER; filedef: result:=LOC_REGISTER; arraydef: result:=LOC_REFERENCE; setdef: if is_smallset(p) then result:=LOC_REGISTER else result:=LOC_REFERENCE; variantdef: result:=LOC_REFERENCE; { avoid problems with errornous definitions } errordef: result:=LOC_REGISTER; else internalerror(2002071001); end; } end; { TODO: copied from ppc cg, needs work} function tm68kparamanager.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:=true; 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 : result:=po_methodpointer in tprocvardef(def).procoptions; end; end; procedure tm68kparamanager.init_values(var curintreg, curfloatreg: tsuperregister; var cur_stack_offset: aword); begin cur_stack_offset:=8; curintreg:=RS_D0; curfloatreg:=RS_FP0; end; procedure tm68kparamanager.create_funcretloc_info(p: tabstractprocdef; side: tcallercallee); begin p.funcretloc[side]:=get_funcretloc(p,side,p.returndef); end; function tm68kparamanager.get_funcretloc(p : tabstractprocdef; side: tcallercallee; def: tdef): tcgpara; var paraloc : pcgparalocation; retcgsize : tcgsize; begin result.init; result.alignment:=get_para_align(p.proccalloption); { void has no location } if is_void(def) then begin paraloc:=result.add_location; result.size:=OS_NO; result.intsize:=0; paraloc^.size:=OS_NO; paraloc^.loc:=LOC_VOID; exit; end; { Constructors return self instead of a boolean } if (p.proctypeoption=potype_constructor) then begin retcgsize:=OS_ADDR; result.intsize:=sizeof(pint); end else begin retcgsize:=def_cgsize(def); result.intsize:=def.size; end; result.size:=retcgsize; { Return is passed as var parameter } if ret_in_param(def,p.proccalloption) then begin paraloc:=result.add_location; paraloc^.loc:=LOC_REFERENCE; paraloc^.size:=retcgsize; exit; end; paraloc:=result.add_location; { Return in FPU register? } if not(cs_fp_emulation in current_settings.moduleswitches) and (p.returndef.typ=floatdef) then begin paraloc^.loc:=LOC_FPUREGISTER; paraloc^.register:=NR_FPU_RESULT_REG; paraloc^.size:=retcgsize; end else { Return in register } begin if retcgsize in [OS_64,OS_S64] then begin { low 32bits } paraloc^.loc:=LOC_REGISTER; paraloc^.size:=OS_32; if side=callerside then paraloc^.register:=NR_FUNCTION_RESULT64_LOW_REG else paraloc^.register:=NR_FUNCTION_RETURN64_LOW_REG; { high 32bits } paraloc:=result.add_location; paraloc^.loc:=LOC_REGISTER; paraloc^.size:=OS_32; if side=calleeside then paraloc^.register:=NR_FUNCTION_RESULT64_HIGH_REG else paraloc^.register:=NR_FUNCTION_RETURN64_HIGH_REG; end else begin paraloc^.loc:=LOC_REGISTER; paraloc^.size:=retcgsize; if side=callerside then paraloc^.register:=newreg(R_INTREGISTER,RS_FUNCTION_RESULT_REG,cgsize2subreg(R_INTREGISTER,retcgsize)) else paraloc^.register:=newreg(R_INTREGISTER,RS_FUNCTION_RETURN_REG,cgsize2subreg(R_INTREGISTER,retcgsize)); end; end; end; function tm68kparamanager.create_paraloc_info(p : tabstractprocdef; side: tcallercallee):longint; var cur_stack_offset: aword; curintreg, curfloatreg: tsuperregister; begin init_values(curintreg,curfloatreg,cur_stack_offset); result:=create_paraloc_info_intern(p,side,p.paras,curintreg,curfloatreg,cur_stack_offset); create_funcretloc_info(p,side); end; function tm68kparamanager.create_paraloc_info_intern(p : tabstractprocdef; side: tcallercallee; paras: tparalist; var curintreg, curfloatreg: tsuperregister; var cur_stack_offset: aword):longint; var paraloc : pcgparalocation; hp : tparavarsym; paracgsize : tcgsize; paralen : aint; parasize : longint; paradef : tdef; i : longint; loc : tcgloc; nextintreg, nextfloatreg : tsuperregister; stack_offset : longint; begin result:=0; nextintreg:=curintreg; nextfloatreg:=curfloatreg; stack_offset:=cur_stack_offset; parasize:=0; for i:=0 to p.paras.count-1 do begin hp:=tparavarsym(paras[i]); paradef:=hp.vardef; { syscall for AmigaOS can have already a paraloc set } if (vo_has_explicit_paraloc in hp.varoptions) then begin if not(vo_is_syscall_lib in hp.varoptions) then internalerror(200506051); continue; end; hp.paraloc[side].reset; { currently only support C-style array of const } if (p.proccalloption in [pocall_cdecl,pocall_cppdecl]) and is_array_of_const(paradef) then begin {$ifdef DEBUG_CHARLIE} writeln('loc register'); {$endif DEBUG_CHARLIE} paraloc:=hp.paraloc[side].add_location; { hack: the paraloc must be valid, but is not actually used } paraloc^.loc:=LOC_REGISTER; paraloc^.register:=NR_D0; paraloc^.size:=OS_ADDR; break; end; if (hp.varspez in [vs_var,vs_out]) or push_addr_param(hp.varspez,paradef,p.proccalloption) or is_open_array(paradef) or is_array_of_const(paradef) then begin {$ifdef DEBUG_CHARLIE} writeln('loc register'); {$endif DEBUG_CHARLIE} paradef:=voidpointertype; loc:=LOC_REGISTER; paracgsize := OS_ADDR; paralen := tcgsize2size[OS_ADDR]; end else begin if not is_special_array(paradef) then paralen:=paradef.size else paralen:=tcgsize2size[def_cgsize(paradef)]; loc:=getparaloc(paradef); paracgsize:=def_cgsize(paradef); { for things like formaldef } if (paracgsize=OS_NO) then begin paracgsize:=OS_ADDR; paralen := tcgsize2size[OS_ADDR]; end; end; hp.paraloc[side].alignment:=std_param_align; hp.paraloc[side].size:=paracgsize; hp.paraloc[side].intsize:=paralen; if (paralen = 0) then if (paradef.typ = recorddef) then begin paraloc:=hp.paraloc[side].add_location; paraloc^.loc := LOC_VOID; end else internalerror(200506052); { can become < 0 for e.g. 3-byte records } while (paralen > 0) do begin paraloc:=hp.paraloc[side].add_location; { by default, the m68k doesn't know any register parameters (FK) if (loc = LOC_REGISTER) and (nextintreg <= RS_D2) then begin //writeln('loc register'); paraloc^.loc := loc; { make sure we don't lose whether or not the type is signed } if (paradef.typ <> orddef) then paracgsize := int_cgsize(paralen); if (paracgsize in [OS_NO,OS_64,OS_S64]) then paraloc^.size := OS_INT else paraloc^.size := paracgsize; paraloc^.register:=newreg(R_INTREGISTER,nextintreg,R_SUBNONE); inc(nextintreg); dec(paralen,tcgsize2size[paraloc^.size]); end else if (loc = LOC_FPUREGISTER) and (nextfloatreg <= RS_FP2) then begin // writeln('loc fpuregister'); paraloc^.loc:=loc; paraloc^.size := paracgsize; paraloc^.register:=newreg(R_FPUREGISTER,nextfloatreg,R_SUBWHOLE); inc(nextfloatreg); dec(paralen,tcgsize2size[paraloc^.size]); end else { LOC_REFERENCE } } begin {$ifdef DEBUG_CHARLIE} writeln('loc reference'); {$endif DEBUG_CHARLIE} paraloc^.loc:=LOC_REFERENCE; paraloc^.size:=int_cgsize(paralen); if (side = callerside) then paraloc^.reference.index:=NR_STACK_POINTER_REG else paraloc^.reference.index:=NR_FRAME_POINTER_REG; paraloc^.reference.offset:=stack_offset; inc(stack_offset,align(paralen,4)); paralen := 0; end; end; end; result:=stack_offset; // writeln('stack offset:',stack_offset); end; { if push_addr_param(hp.varspez,paradef,p.proccalloption) then paracgsize:=OS_ADDR else begin paracgsize:=def_cgsize(paradef); if paracgsize=OS_NO then paracgsize:=OS_ADDR; end; hp.paraloc[side].size:=paracgsize; hp.paraloc[side].Alignment:=std_param_align; paraloc:=hp.paraloc[side].add_location; paraloc^.size:=paracgsize; paraloc^.loc:=LOC_REFERENCE; if side=callerside then paraloc^.reference.index:=NR_STACK_POINTER_REG else paraloc^.reference.index:=NR_FRAME_POINTER_REG; paraloc^.reference.offset:=target_info.first_parm_offset+parasize; end; create_funcretloc_info(p,side); result:=parasize; end; } function tm68kparamanager.parsefuncretloc(p : tabstractprocdef; const s : string) : boolean; begin result:=false; case target_info.system of system_m68k_amiga: begin if s='D0' then p.exp_funcretloc:=NR_D0 else if s='D1' then p.exp_funcretloc:=NR_D1 else if s='D2' then p.exp_funcretloc:=NR_D2 else if s='D3' then p.exp_funcretloc:=NR_D3 else if s='D4' then p.exp_funcretloc:=NR_D4 else if s='D5' then p.exp_funcretloc:=NR_D5 else if s='D6' then p.exp_funcretloc:=NR_D6 else if s='D7' then p.exp_funcretloc:=NR_D7 else if s='A0' then p.exp_funcretloc:=NR_A0 else if s='A1' then p.exp_funcretloc:=NR_A1 else if s='A2' then p.exp_funcretloc:=NR_A2 else if s='A3' then p.exp_funcretloc:=NR_A3 else if s='A4' then p.exp_funcretloc:=NR_A4 else if s='A5' then p.exp_funcretloc:=NR_A5 { 'A6' is problematic, since it's the frame pointer in fpc, so it should be saved before a call! } else if s='A6' then p.exp_funcretloc:=NR_A6 { 'A7' is the stack pointer on 68k, can't be overwritten by API calls } else p.exp_funcretloc:=NR_NO; if p.exp_funcretloc<>NR_NO then result:=true; end; else internalerror(2005121801); end; end; function tm68kparamanager.parseparaloc(p : tparavarsym;const s : string) : boolean; var paraloc : pcgparalocation; begin result:=false; case target_info.system of system_m68k_amiga: begin p.paraloc[callerside].alignment:=4; paraloc:=p.paraloc[callerside].add_location; paraloc^.loc:=LOC_REGISTER; paraloc^.size:=def_cgsize(p.vardef); { pattern is always uppercase'd } if s='D0' then paraloc^.register:=NR_D0 else if s='D1' then paraloc^.register:=NR_D1 else if s='D2' then paraloc^.register:=NR_D2 else if s='D3' then paraloc^.register:=NR_D3 else if s='D4' then paraloc^.register:=NR_D4 else if s='D5' then paraloc^.register:=NR_D5 else if s='D6' then paraloc^.register:=NR_D6 else if s='D7' then paraloc^.register:=NR_D7 else if s='A0' then paraloc^.register:=NR_A0 else if s='A1' then paraloc^.register:=NR_A1 else if s='A2' then paraloc^.register:=NR_A2 else if s='A3' then paraloc^.register:=NR_A3 else if s='A4' then paraloc^.register:=NR_A4 else if s='A5' then paraloc^.register:=NR_A5 { 'A6' is problematic, since it's the frame pointer in fpc, so it should be saved before a call! } else if s='A6' then paraloc^.register:=NR_A6 { 'A7' is the stack pointer on 68k, can't be overwritten by API calls } else exit; { copy to callee side } p.paraloc[calleeside].add_location^:=paraloc^; end; else internalerror(200405092); end; result:=true; end; procedure tm68kparamanager.createtempparaloc(list: TAsmList;calloption : tproccalloption;parasym : tparavarsym;can_use_final_stack_loc : boolean;var cgpara:TCGPara); var paraloc : pcgparalocation; begin paraloc:=parasym.paraloc[callerside].location; { 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; begin paramanager:=tm68kparamanager.create; end.