mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-07 22:27:57 +02:00

the abi does not support branches between functions located in different tocs which might happen for inter-module branches
266 lines
9.6 KiB
ObjectPascal
266 lines
9.6 KiB
ObjectPascal
{
|
||
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(2000061310);
|
||
{ 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_hidden(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 <external_method>
|
||
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.
|
||
|