瀏覽代碼

* 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 -

Jonas Maebe 10 年之前
父節點
當前提交
fb27dff638

+ 2 - 2
compiler/aasmtai.pas

@@ -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

+ 8 - 2
compiler/aggas.pas

@@ -1359,10 +1359,16 @@ implementation
                AsmWrite(#9);
                AsmWrite(symbolpairkindstr[tai_symbolpair(hp).kind]);
                AsmWrite(' ');
+               if tai_symbolpair(hp).kind<>spk_localentry then
+                 s:=', '
+               else
+                 { 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^)+', '+ReplaceForbiddenAsmSymbolChars(tai_symbolpair(hp).value^))
+                 AsmWriteLn(ReplaceForbiddenAsmSymbolChars(tai_symbolpair(hp).sym^)+s+ReplaceForbiddenAsmSymbolChars(tai_symbolpair(hp).value^))
                else
-                 AsmWriteLn(tai_symbolpair(hp).sym^+', '+tai_symbolpair(hp).value^);
+                 AsmWriteLn(tai_symbolpair(hp).sym^+s+tai_symbolpair(hp).value^);
              end;
            ait_weak:
              begin

+ 23 - 4
compiler/ncgcal.pas

@@ -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

+ 26 - 0
compiler/powerpc64/cgcpu.pas

@@ -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);
 

+ 36 - 2
compiler/powerpc64/nppccal.pas

@@ -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 

+ 40 - 0
compiler/powerpc64/rappcgas.pas

@@ -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;

+ 2 - 0
compiler/ppcgen/agppcgas.pas

@@ -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

+ 5 - 1
rtl/powerpc64/math.inc

@@ -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;