* generate ".abiversion 2" directive when targeting ppc64/ELFv2

* generate code to load the TOC register if it's required, and
    emit the the ".localentry" directive to indicate to the linker
    where the actual function body starts
  * support the ".localentry" directive in the ppc64 assembler reader
   o disable the fpc_qword_to_double() assembler implementation for
     ELFv2, because we can't manually insert the .localentry directive
     before the stack allocation code
  * perform indirect calls on ppc64 via R12 in ncgcal, as R12 needs to
    contain the function address on entry on ppc64/ELFv2 (and it's
    a volatile register, so there's no problem with always using
    it)

git-svn-id: trunk@30198 -
This commit is contained in:
Jonas Maebe 2015-03-14 18:35:31 +00:00
parent 8445381929
commit fb27dff638
8 changed files with 143 additions and 12 deletions

View File

@ -359,7 +359,7 @@ interface
ash_savereg,ash_savexmm,ash_pushframe
);
TSymbolPairKind = (spk_set, spk_thumb_set);
TSymbolPairKind = (spk_set, spk_thumb_set, spk_localentry);
const
@ -393,7 +393,7 @@ interface
'.seh_savereg','.seh_savexmm','.seh_pushframe'
);
symbolpairkindstr: array[TSymbolPairKind] of string[11]=(
'.set', '.thumb_set'
'.set', '.thumb_set', '.localentry'
);
type

View File

@ -1359,10 +1359,16 @@ implementation
AsmWrite(#9);
AsmWrite(symbolpairkindstr[tai_symbolpair(hp).kind]);
AsmWrite(' ');
if replaceforbidden then
AsmWriteLn(ReplaceForbiddenAsmSymbolChars(tai_symbolpair(hp).sym^)+', '+ReplaceForbiddenAsmSymbolChars(tai_symbolpair(hp).value^))
if tai_symbolpair(hp).kind<>spk_localentry then
s:=', '
else
AsmWriteLn(tai_symbolpair(hp).sym^+', '+tai_symbolpair(hp).value^);
{ the .localentry directive has to specify the size from the
start till here of the non-local entry code as second argument }
s:=', .-';
if replaceforbidden then
AsmWriteLn(ReplaceForbiddenAsmSymbolChars(tai_symbolpair(hp).sym^)+s+ReplaceForbiddenAsmSymbolChars(tai_symbolpair(hp).value^))
else
AsmWriteLn(tai_symbolpair(hp).sym^+s+tai_symbolpair(hp).value^);
end;
ait_weak:
begin

View File

@ -28,7 +28,8 @@ interface
uses
cpubase,
globtype,
parabase,cgbase,cgutils,
parabase,cgutils,
aasmdata,cgbase,
symdef,node,ncal;
type
@ -97,6 +98,9 @@ interface
procedure do_call_ref(ref: treference);virtual;
procedure load_block_invoke(toreg: tregister);virtual;
function get_call_reg(list: TAsmList): tregister; virtual;
procedure unget_call_reg(list: TAsmList; reg: tregister); virtual;
public
procedure pass_generate_code;override;
destructor destroy;override;
@ -111,7 +115,7 @@ implementation
cpuinfo,
symconst,symbase,symtable,symtype,symsym,defutil,paramgr,
pass_2,
aasmbase,aasmtai,aasmdata,
aasmbase,aasmtai,
nbas,nmem,nld,ncnv,nutils,
ncgutil,blockutl,
cgobj,tgobj,hlcgobj,
@ -471,6 +475,18 @@ implementation
end;
function tcgcallnode.get_call_reg(list: TAsmList): tregister;
begin
result:=hlcg.getaddressregister(current_asmdata.CurrAsmList,procdefinition.address_type);
end;
procedure tcgcallnode.unget_call_reg(list: TAsmList; reg: tregister);
begin
{ nothing to do by default }
end;
procedure tcgcallnode.set_result_location(realresdef: tstoreddef);
begin
if realresdef.is_intregable or
@ -947,7 +963,7 @@ implementation
callref:=can_call_ref(href);
if not callref then
begin
pvreg:=hlcg.getaddressregister(current_asmdata.CurrAsmList,proc_addr_voidptrdef);
pvreg:=get_call_reg(current_asmdata.CurrAsmList);
cg.a_load_ref_reg(current_asmdata.CurrAsmList,proc_addr_size,proc_addr_size,href,pvreg);
end;
@ -977,7 +993,10 @@ implementation
if callref then
do_call_ref(href)
else
hlcg.a_call_reg(current_asmdata.CurrAsmList,tabstractprocdef(procdefinition),pvreg);
begin
hlcg.a_call_reg(current_asmdata.CurrAsmList,tabstractprocdef(procdefinition),pvreg);
unget_call_reg(current_asmdata.CurrAsmList,pvreg);
end;
extra_post_call_code;
end

View File

@ -1167,7 +1167,33 @@ var
var
href: treference;
lab: tasmlabel;
procmangledname: TSymStr;
begin
{ In ELFv2 the function is required to initialise the TOC register itself
if necessary. Additionally, it has to mark the end of this TOC
initialisation code with a .localfunc directive, which will be used as
local entry code by the linker (when it knows the TOC value is the same
for the caller and callee). It must load the TOC in a PIC-way, which it
can do easily because R12 is guaranteed to hold the address of this function
on entry. }
if (target_info.abi=abi_powerpc_elfv2) and
(pi_needs_got in current_procinfo.flags) and
not nostackframe then
begin
current_asmdata.getlabel(lab,alt_addr);
getcpuregister(list,NR_R12);
getcpuregister(list,NR_R2);
cg.a_label(list,lab);
reference_reset_symbol(href,current_asmdata.RefAsmSymbol('.TOC.',AT_DATA),0,sizeof(PInt));
href.relsymbol:=lab;
href.refaddr:=addr_higha;
list.concat(taicpu.op_reg_reg_ref(a_addis,NR_R2,NR_R12,href));
href.refaddr:=addr_low;
list.concat(taicpu.op_reg_reg_ref(a_addi,NR_R2,NR_R2,href));
procmangledname:=current_procinfo.procdef.mangledname;
list.concat(tai_symbolpair.create(spk_localentry,procmangledname,procmangledname));
end;
calcFirstUsedFPR(firstregfpu, fprcount);
calcFirstUsedGPR(firstreggpr, gprcount);

View File

@ -26,6 +26,7 @@ unit nppccal;
interface
uses
aasmdata, cgbase,
symdef, node, ncal, ncgcal;
type
@ -34,6 +35,11 @@ type
end;
tppccallnode = class(tcgcallnode)
protected
function get_call_reg(list: TAsmList): tregister; override;
procedure unget_call_reg(list: TAsmList; reg: tregister); override;
public
function pass_1: tnode; override;
procedure do_syscall; override;
end;
@ -43,12 +49,40 @@ uses
globtype, systems,
cutils, verbose, globals,
symconst, symbase, symsym, symtable, defutil, paramgr, parabase,
cgbase, pass_2,
cpuinfo, cpubase, aasmbase, aasmtai,aasmdata, aasmcpu,
pass_2,
cpuinfo, cpubase, aasmbase, aasmtai, aasmcpu,
nmem, nld, ncnv,
ncgutil, cgutils, cgobj, tgobj, regvars, rgobj, rgcpu,
cgcpu, cpupi, procinfo;
function tppccallnode.get_call_reg(list: TAsmList): tregister;
begin
{ on the ppc64/ELFv2 abi, all indirect calls must go via R12, so that the
called function can use R12 as PIC base register }
cg.getcpuregister(list,NR_R12);
result:=NR_R12;
end;
procedure tppccallnode.unget_call_reg(list: TAsmList; reg: tregister);
begin
cg.ungetcpuregister(list,NR_R12);
end;
function tppccallnode.pass_1: tnode;
begin
result:=inherited;
if assigned(result) then
exit;
{ for ELFv2, we must load the TOC/GOT register in case this routine may
call an external routine (because the lookup of the target address is
TOC-based). Maybe needs to be extended to non-ELFv2 too }
if target_info.abi=abi_powerpc_elfv2 then
include(current_procinfo.flags,pi_needs_got);
end;
procedure tppccallnode.do_syscall;
begin
{ no MorphOS style syscalls supported. Only implemented to avoid abstract

View File

@ -39,6 +39,8 @@ type
procedure ReadAt(oper: tppcoperand);
procedure ReadSym(oper: tppcoperand);
procedure ConvertCalljmp(instr: tppcinstruction);
function is_targetdirective(const s: string): boolean; override;
procedure HandleTargetDirective; override;
end;
implementation
@ -773,6 +775,44 @@ begin
instr.Operands[1].opr.symbol:=current_asmdata.DefineAsmSymbol('.'+instr.Operands[1].opr.symbol.name,instr.Operands[1].opr.symbol.bind,AT_FUNCTION);
end;
function tppcattreader.is_targetdirective(const s: string): boolean;
begin
if (target_info.abi=abi_powerpc_elfv2) and
(s='.localentry') then
result:=true
else
result:=inherited;
end;
procedure tppcattreader.HandleTargetDirective;
var
symname,
symval : String;
val : aint;
symtyp : TAsmsymtype;
begin
if (target_info.abi=abi_powerpc_elfv2) and
(actasmpattern='.localentry') then
begin
{ .localentry funcname, .-funcname }
consume(AS_TARGET_DIRECTIVE);
BuildConstSymbolExpression(true,false,false, val,symname,symtyp);
Consume(AS_COMMA);
{ we need a '.', but these are parsed as identifiers -> if the current
pattern is different from a '.' try to consume AS_DOT so we'll get
the correct error message, otherwise consume this '.' identifier }
if actasmpattern<>'.' then
Consume(AS_DOT)
else
Consume(AS_ID);
Consume(AS_MINUS);
BuildConstSymbolExpression(true,false,false, val,symval,symtyp);
curList.concat(tai_symbolpair.create(spk_localentry,symname,symval));
end
else
inherited;
end;
procedure tppcattreader.handleopcode;
var
instr: tppcinstruction;

View File

@ -411,6 +411,8 @@ unit agppcgas;
var
i : longint;
begin
if target_info.abi = abi_powerpc_elfv2 then
AsmWriteln(#9'.abiversion 2');
for i:=0 to 31 do
AsmWriteln(#9'.set'#9'r'+tostr(i)+','+tostr(i));
for i:=0 to 31 do

View File

@ -74,7 +74,11 @@ asm
fcfid f0,f0 // convert to fpu int
end;
{$ifndef aix}
{ we wouls have to generate the .localfunc directive for ELFv2, and moreover it
must appear at the start right after setting up the TOC pointer, but the local
variables will cause the code generator to already insert the stack allocation
before that... -> disable this routine for ELFv2 }
{$if not defined(aix) and (not defined(linux) or (defined(_ELF_CALL) and (_ELF_CALL = 1))) }
{$define FPC_SYSTEM_HAS_QWORD_TO_DOUBLE}
function fpc_qword_to_double(q: qword): double; compilerproc;assembler;