{ Copyright (c) 1998-2010 by Florian Klaempfl and Jonas Maebe Member of the Free Pascal development team This unit contains routines high-level code generator support shared by ppc32 and ppc64 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 hlcgppc; {$i fpcdefs.inc} interface uses globtype,globals, aasmdata, symtype,symdef, cgbase,cgutils,hlcgobj,hlcg2ll; type thlcgppcgen = class(thlcg2ll) protected procedure a_load_subsetref_regs_noindex(list: TAsmList; subsetsize: tdef; loadbitsize: byte; const sref: tsubsetreference; valuereg, extra_value_reg: tregister); override; public procedure g_intf_wrapper(list: TAsmList; procdef: tprocdef; const labelname: string; ioffset: longint);override; procedure a_jmp_external_name(list: TAsmList; const externalname: TSymStr); override; procedure gen_load_para_value(list: TAsmList); override; end; implementation uses verbose, systems,fmodule, symconst, aasmbase,aasmtai,aasmcpu, cpubase, procinfo,cpupi,cgobj,cgppc, defutil; { thlcgppc } procedure thlcgppcgen.a_load_subsetref_regs_noindex(list: TAsmList; subsetsize: tdef; loadbitsize: byte; const sref: tsubsetreference; valuereg, extra_value_reg: tregister); var fromsreg, tosreg: tsubsetregister; restbits: byte; begin { the code below is only valid for big endian } if target_info.endian=endian_little then begin inherited; exit end; restbits:=(sref.bitlen-(loadbitsize-sref.startbit)); if is_signed(subsetsize) then begin { sign extend } a_op_const_reg(list,OP_SHL,osuinttype,AIntBits-loadbitsize+sref.startbit,valuereg); a_op_const_reg(list,OP_SAR,osuinttype,AIntBits-sref.bitlen,valuereg); end else begin a_op_const_reg(list,OP_SHL,osuinttype,restbits,valuereg); { mask other bits } if (sref.bitlen<>AIntBits) then a_op_const_reg(list,OP_AND,osuinttype,(aword(1) shl sref.bitlen)-1,valuereg); end; { use subsetreg routine, it may have been overridden with an optimized version } fromsreg.subsetreg:=extra_value_reg; fromsreg.subsetregsize:=OS_INT; { subsetregs always count bits from right to left } fromsreg.startbit:=loadbitsize-restbits; fromsreg.bitlen:=restbits; tosreg.subsetreg:=valuereg; tosreg.subsetregsize:=OS_INT; tosreg.startbit:=0; tosreg.bitlen:=restbits; a_load_subsetreg_subsetreg(list,subsetsize,subsetsize,fromsreg,tosreg); end; procedure thlcgppcgen.g_intf_wrapper(list: TAsmList; procdef: tprocdef; const labelname: string; ioffset: longint); procedure loadvmttor11; var href : treference; begin reference_reset_base(href,voidpointertype,NR_R3,0,ctempposinvalid,sizeof(pint),[]); cg.a_load_ref_reg(list,OS_ADDR,OS_ADDR,href,NR_R11); end; procedure op_onr11methodaddr; var href : treference; begin if (procdef.extnumber=$ffff) then Internalerror(200006139); { call/jmp vmtoffs(%eax) ; method offs } reference_reset_base(href,voidpointertype,NR_R11,tobjectdef(procdef.struct).vmtmethodoffset(procdef.extnumber),ctempposinvalid,sizeof(pint),[]); if tcgppcgen(cg).hasLargeOffset(href) then begin {$ifdef cpu64bitaddr} if (longint(href.offset) <> href.offset) then { add support for offsets > 32 bit } internalerror(200510201); {$endif cpu64bitaddr} list.concat(taicpu.op_reg_reg_const(A_ADDIS,NR_R11,NR_R11, smallint((href.offset shr 16)+ord(smallint(href.offset and $ffff) < 0)))); href.offset := smallint(href.offset and $ffff); end; { use R12 for dispatch because most ABIs don't care and ELFv2 requires it } cg.a_load_ref_reg(list,OS_ADDR,OS_ADDR,href,NR_R12); if (target_info.system in systems_aix) or ((target_info.system = system_powerpc64_linux) and (target_info.abi=abi_powerpc_sysv)) then begin reference_reset_base(href, voidpointertype, NR_R12, 0, ctempposinvalid, sizeof(pint),[]); cg.a_load_ref_reg(list, OS_ADDR, OS_ADDR, href, NR_R12); end; list.concat(taicpu.op_reg(A_MTCTR,NR_R12)); list.concat(taicpu.op_none(A_BCTR)); if (target_info.system in ([system_powerpc64_linux]+systems_aix)) then list.concat(taicpu.op_none(A_NOP)); end; var make_global : boolean; begin if not(procdef.proctypeoption in [potype_function,potype_procedure]) then Internalerror(200006137); if not assigned(procdef.struct) or (procdef.procoptions*[po_classmethod, po_staticmethod, po_methodpointer, po_interrupt, po_iocheck]<>[]) then Internalerror(200006138); if procdef.owner.symtabletype<>ObjectSymtable then Internalerror(200109191); make_global:=false; if (not current_module.is_unit) or create_smartlink or (procdef.owner.defowner.owner.symtabletype=globalsymtable) then make_global:=true; if make_global then List.concat(Tai_symbol.Createname_global(labelname,AT_FUNCTION,0,procdef)) else List.concat(Tai_symbol.Createname(labelname,AT_FUNCTION,0,procdef)); { set param1 interface to self } g_adjust_self_value(list,procdef,ioffset); { case 4 } if (po_virtualmethod in procdef.procoptions) and not is_objectpascal_helper(procdef.struct) then begin loadvmttor11; op_onr11methodaddr; end { case 0 } else case target_info.system of system_powerpc_darwin, system_powerpc64_darwin: list.concat(taicpu.op_sym(A_B,tcgppcgen(cg).get_darwin_call_stub(procdef.mangledname,false))); else begin if use_dotted_functions then {$note ts:todo add GOT change?? - think not needed :) } list.concat(taicpu.op_sym(A_B,current_asmdata.RefAsmSymbol('.' + procdef.mangledname,AT_FUNCTION))) else list.concat(taicpu.op_sym(A_B,current_asmdata.RefAsmSymbol(procdef.mangledname,AT_FUNCTION))); if (target_info.system in ([system_powerpc64_linux]+systems_aix)) then list.concat(taicpu.op_none(A_NOP)); end; end; List.concat(Tai_symbol_end.Createname(labelname)); end; procedure thlcgppcgen.a_jmp_external_name(list: TAsmList; const externalname: TSymStr); var href : treference; begin if not(target_info.system in ([system_powerpc64_linux]+systems_aix)) then begin inherited; exit; end; { for ppc64/linux and aix emit correct code which sets up a stack frame and then calls the external method normally to ensure that the GOT/TOC will be loaded correctly if required. The resulting code sequence looks as follows: mflr r0 stw/d r0, 16(r1) stw/du r1, -112(r1) bl nop addi r1, r1, 112 lwz/d r0, 16(r1) mtlr r0 blr } list.concat(taicpu.op_reg(A_MFLR, NR_R0)); if target_info.abi=abi_powerpc_sysv then reference_reset_base(href, voidstackpointertype, NR_STACK_POINTER_REG, LA_LR_SYSV, ctempposinvalid, 8, []) else reference_reset_base(href, voidstackpointertype, NR_STACK_POINTER_REG, LA_LR_AIX, ctempposinvalid, 8, []); cg.a_load_reg_ref(list,OS_ADDR,OS_ADDR,NR_R0,href); reference_reset_base(href, voidstackpointertype, NR_STACK_POINTER_REG, -MINIMUM_STACKFRAME_SIZE, ctempposinvalid, 8, []); list.concat(taicpu.op_reg_ref({$ifdef cpu64bitaddr}A_STDU{$else}A_STWU{$endif}, NR_STACK_POINTER_REG, href)); cg.a_call_name(list,externalname,false); list.concat(taicpu.op_reg_reg_const(A_ADDI, NR_STACK_POINTER_REG, NR_STACK_POINTER_REG, MINIMUM_STACKFRAME_SIZE)); if target_info.abi=abi_powerpc_sysv then reference_reset_base(href, voidstackpointertype, NR_STACK_POINTER_REG, LA_LR_SYSV, ctempposinvalid, 8, []) else reference_reset_base(href, voidstackpointertype, NR_STACK_POINTER_REG, LA_LR_AIX, ctempposinvalid, 8, []); cg.a_load_ref_reg(list,OS_ADDR,OS_ADDR,href,NR_R0); list.concat(taicpu.op_reg(A_MTLR, NR_R0)); list.concat(taicpu.op_none(A_BLR)); end; procedure thlcgppcgen.gen_load_para_value(list: TAsmList); begin { get the register that contains the stack pointer before the procedure entry, which is used to access the parameters in their original callee-side location } if (tcpuprocinfo(current_procinfo).needs_frame_pointer) then getcpuregister(list,NR_OLD_STACK_POINTER_REG); inherited; { free it again } if (tcpuprocinfo(current_procinfo).needs_frame_pointer) then ungetcpuregister(list,NR_OLD_STACK_POINTER_REG); end; end.