From fb27dff638d97a9a1409e7db7c1384e31e375f03 Mon Sep 17 00:00:00 2001 From: Jonas Maebe Date: Sat, 14 Mar 2015 18:35:31 +0000 Subject: [PATCH] * 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 - --- compiler/aasmtai.pas | 4 ++-- compiler/aggas.pas | 12 +++++++--- compiler/ncgcal.pas | 27 ++++++++++++++++++---- compiler/powerpc64/cgcpu.pas | 26 +++++++++++++++++++++ compiler/powerpc64/nppccal.pas | 38 +++++++++++++++++++++++++++++-- compiler/powerpc64/rappcgas.pas | 40 +++++++++++++++++++++++++++++++++ compiler/ppcgen/agppcgas.pas | 2 ++ rtl/powerpc64/math.inc | 6 ++++- 8 files changed, 143 insertions(+), 12 deletions(-) diff --git a/compiler/aasmtai.pas b/compiler/aasmtai.pas index 1305cce6c6..f3a575defa 100644 --- a/compiler/aasmtai.pas +++ b/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 diff --git a/compiler/aggas.pas b/compiler/aggas.pas index 90fd751e6e..365317f62c 100644 --- a/compiler/aggas.pas +++ b/compiler/aggas.pas @@ -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 diff --git a/compiler/ncgcal.pas b/compiler/ncgcal.pas index 9471e1e107..270deb0eba 100644 --- a/compiler/ncgcal.pas +++ b/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 diff --git a/compiler/powerpc64/cgcpu.pas b/compiler/powerpc64/cgcpu.pas index 524936f6c9..eb9e920536 100644 --- a/compiler/powerpc64/cgcpu.pas +++ b/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); diff --git a/compiler/powerpc64/nppccal.pas b/compiler/powerpc64/nppccal.pas index 8588fe43cd..8f9cbd74fc 100644 --- a/compiler/powerpc64/nppccal.pas +++ b/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 diff --git a/compiler/powerpc64/rappcgas.pas b/compiler/powerpc64/rappcgas.pas index 903a8154b0..998555d915 100644 --- a/compiler/powerpc64/rappcgas.pas +++ b/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; diff --git a/compiler/ppcgen/agppcgas.pas b/compiler/ppcgen/agppcgas.pas index 17190b60d0..bf03b4e946 100644 --- a/compiler/ppcgen/agppcgas.pas +++ b/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 diff --git a/rtl/powerpc64/math.inc b/rtl/powerpc64/math.inc index d69552cf96..b9e369847a 100644 --- a/rtl/powerpc64/math.inc +++ b/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;