fpc/compiler/ppcgen/hlcgppc.pas
florian 12f48c230b * use high level interface wrappers for powerpc64 if pic is generated as
the abi does not support branches between functions located in different
    tocs which might happen for inter-module branches
2021-11-27 23:33:22 +01:00

266 lines
9.6 KiB
ObjectPascal
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
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.