{ Copyright (c) 2006 by Florian Klaempfl This unit implements the common part of the code generator for the PowerPC 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 cgppc; {$i fpcdefs.inc} interface uses globtype,symtype,symdef, cgbase,cgobj, aasmbase,aasmdef,aasmcpu,aasmtai,aasmdata, cpubase,cpuinfo,cgutils,rgcpu, parabase; type tcgppcgen = class(tcg) procedure a_loadaddr_ref_cgpara(list : TAsmList;const r : treference;const paraloc : tcgpara); override; procedure a_bit_scan_reg_reg(list: TAsmList; reverse: boolean; srcsize, dstsize: tcgsize; src, dst: TRegister); override; procedure a_call_reg(list : TAsmList;reg: tregister); override; { stores the contents of register reg to the memory location described by ref } procedure a_load_reg_ref(list: TAsmList; fromsize, tosize: TCGSize; reg: tregister; const ref: treference); override; procedure a_loadaddr_ref_reg(list : TAsmList;const ref : treference;r : tregister);override; { fpu move instructions } procedure a_loadfpu_reg_reg(list: TAsmList; fromsize, tosize: tcgsize; reg1, reg2: tregister); override; procedure a_loadfpu_ref_reg(list: TAsmList; fromsize, tosize: tcgsize; const ref: treference; reg: tregister); override; procedure a_loadfpu_reg_ref(list: TAsmList; fromsize, tosize: tcgsize; reg: tregister; const ref: treference); override; { overflow checking } procedure g_overflowcheck(list: TAsmList; const l: tlocation; def: tdef);override; { entry code } procedure g_profilecode(list: TAsmList); override; procedure a_jmp_flags(list: TAsmList; const f: TResFlags; l: tasmlabel); override; procedure a_jmp_cond(list : TAsmList;cond : TOpCmp;l: tasmlabel); procedure g_maybe_got_init(list: TAsmList); override; procedure get_aix_toc_sym(list: TAsmList; const symname: string; const flags: tindsymflags; out ref: treference; force_direct_toc: boolean); procedure g_load_check_simple(list: TAsmList; const ref: treference; size: aint); procedure g_flags2reg(list: TAsmList; size: TCgSize; const f: TResFlags; reg: TRegister); override; { returns true if the offset of the given reference can not be } { represented by a 16 bit immediate as required by some PowerPC } { instructions } function hasLargeOffset(const ref : TReference) : Boolean; inline; function get_darwin_call_stub(const s: string; weak: boolean): tasmsymbol; protected function g_indirect_sym_load(list:TAsmList;const symname: string; const flags: tindsymflags): tregister; override; { Make sure ref is a valid reference for the PowerPC and sets the } { base to the value of the index if (base = R_NO). } { Returns true if the reference contained a base, index and an } { offset or symbol, in which case the base will have been changed } { to a tempreg (which has to be freed by the caller) containing } { the sum of part of the original reference } function fixref(list: TAsmList; var ref: treference): boolean; { contains the common code of a_load_reg_ref and a_load_ref_reg } procedure a_load_store(list:TAsmList;op: tasmop;reg:tregister;ref: treference);virtual; { creates the correct branch instruction for a given combination } { of asmcondflags and destination addressing mode } procedure a_jmp(list: TAsmList; op: tasmop; c: tasmcondflag; crval: longint; l: tasmlabel); function save_lr_in_prologue: boolean; function load_got_symbol(list : TAsmList; const symbol : string; const flags: tindsymflags) : tregister; end; TPPCAsmData = class(TAsmDataDef) private { number of entries in the TOC } fdirecttocentries, { number of fake TOC subsections we have created } ftocsections, { number of fake TOC entries in the current TOC subsection } fcurrenttocentries: longint; public procedure GetNextSmallTocEntry(out tocnr, entrynr: longint); property DirectTOCEntries: longint read fdirecttocentries write fdirecttocentries; end; TTOCAsmSymbol = class(TAsmSymbol) private { we split the toc into several sections of 32KB each, this number indicates which subsection this symbol is defined in } ftocsecnr: longint; public property TocSecNr: longint read ftocsecnr; end; const TOpCmp2AsmCond: Array[topcmp] of TAsmCondFlag = (C_NONE,C_EQ,C_GT, C_LT,C_GE,C_LE,C_NE,C_LE,C_LT,C_GE,C_GT); TocSecBaseName = 'toc_table'; {$ifdef extdebug} function ref2string(const ref : treference) : string; function cgop2string(const op : TOpCg) : String; {$endif extdebug} implementation uses {$ifdef extdebug}sysutils,{$endif} globals,verbose,systems,cutils, symconst,symsym,symtable,fmodule, rgobj,tgobj,cpupi,procinfo,paramgr; { We know that macos_direct_globals is a const boolean but we don't care about this warning } {$NOTE Is macos_direct_globals still useful?} {$WARN 6018 OFF} {$ifdef extdebug} function ref2string(const ref : treference) : string; begin result := 'base : ' + inttostr(ord(ref.base)) + ' index : ' + inttostr(ord(ref.index)) + ' refaddr : ' + inttostr(ord(ref.refaddr)) + ' offset : ' + inttostr(ref.offset) + ' symbol : '; if (assigned(ref.symbol)) then result := result + ref.symbol.name; end; function cgop2string(const op : TOpCg) : String; const opcg_strings : array[TOpCg] of string[6] = ( 'None', 'Move', 'Add', 'And', 'Div', 'IDiv', 'IMul', 'Mul', 'Neg', 'Not', 'Or', 'Sar', 'Shl', 'Shr', 'Sub', 'Xor', 'Rol', 'Ror' ); begin result := opcg_strings[op]; end; {$endif extdebug} function tcgppcgen.hasLargeOffset(const ref : TReference) : Boolean; begin result := aword(ref.offset-low(smallint)) > high(smallint)-low(smallint); end; function tcgppcgen.save_lr_in_prologue: boolean; begin result:= (not (po_assembler in current_procinfo.procdef.procoptions) and ((pi_do_call in current_procinfo.flags) or (cs_profile in init_settings.moduleswitches))) or ([cs_lineinfo,cs_debuginfo] * current_settings.moduleswitches <> []); end; procedure tcgppcgen.a_loadaddr_ref_cgpara(list : TAsmList;const r : treference;const paraloc : tcgpara); var ref: treference; tmpreg: tregister; begin paraloc.check_simple_location; paramanager.allocparaloc(list,paraloc.location); case paraloc.location^.loc of LOC_REGISTER,LOC_CREGISTER: a_loadaddr_ref_reg(list,r,paraloc.location^.register); LOC_REFERENCE: begin reference_reset(ref,paraloc.alignment,[]); ref.base := paraloc.location^.reference.index; ref.offset := paraloc.location^.reference.offset; tmpreg := rg[R_INTREGISTER].getregister(list,R_SUBWHOLE); a_loadaddr_ref_reg(list,r,tmpreg); a_load_reg_ref(list,OS_ADDR,OS_ADDR,tmpreg,ref); end; else internalerror(2002080701); end; end; procedure tcgppcgen.a_bit_scan_reg_reg(list: TAsmList; reverse: boolean; srcsize, dstsize: tcgsize; src, dst: TRegister); var tmpreg: tregister; cntlzop: tasmop; bitsizem1: longint; begin { we only have a cntlz(w|d) instruction, which corresponds to bsr(x) (well, regsize_in_bits - bsr(x), as x86 numbers bits in reverse). Fortunately, bsf(x) can be calculated easily based on that, see "Figure 5-13. Number of Powers of 2 Code Sequence" in the PowerPC Compiler Writer's Guide } if srcsize in [OS_64,OS_S64] then begin {$ifdef powerpc64} cntlzop:=A_CNTLZD; {$else} internalerror(2015022601); {$endif} bitsizem1:=63; end else begin cntlzop:=A_CNTLZW; bitsizem1:=31; end; if not reverse then begin { cntlzw(src and -src) } tmpreg:=getintregister(list,srcsize); { don't use a_op_reg_reg, as this will adjust the result after the neg in case of a non-32/64 bit operation, which is not necessary since we're only using it as an AND-mask } list.concat(taicpu.op_reg_reg(A_NEG,tmpreg,src)); a_op_reg_reg(list,OP_AND,srcsize,src,tmpreg); end else tmpreg:=src; { count leading zeroes } list.concat(taicpu.op_reg_reg(cntlzop,dst,tmpreg)); { (bitsize-1) - cntlz (which is 32/64 in case src was 0) } list.concat(taicpu.op_reg_reg_const(A_SUBFIC,dst,dst,bitsizem1)); { set to 255 is source was 0 } a_op_const_reg(list,OP_AND,dstsize,255,dst); end; procedure tcgppcgen.g_maybe_got_init(list: TAsmList); var instr: taicpu; cond: tasmcond; savedlr: boolean; begin if not(po_assembler in current_procinfo.procdef.procoptions) then begin if (cs_create_pic in current_settings.moduleswitches) and (pi_needs_got in current_procinfo.flags) then case target_info.system of system_powerpc_darwin, system_powerpc64_darwin: begin savedlr:=save_lr_in_prologue; if not savedlr then list.concat(taicpu.op_reg_reg(A_MFSPR,NR_R0,NR_LR)); fillchar(cond,sizeof(cond),0); cond.simple:=false; cond.bo:=20; cond.bi:=31; instr:=taicpu.op_sym(A_BCL,current_procinfo.CurrGOTLabel); instr.setcondition(cond); list.concat(instr); a_label(list,current_procinfo.CurrGOTLabel); a_reg_alloc(list,current_procinfo.got); list.concat(taicpu.op_reg_reg(A_MFSPR,current_procinfo.got,NR_LR)); if not savedlr or { in the following case lr is saved, but not restored } { (happens e.g. when generating debug info for leaf } { procedures) } not(pi_do_call in current_procinfo.flags) then list.concat(taicpu.op_reg_reg(A_MTSPR,NR_LR,NR_R0)); end; else ; end; end; end; function tcgppcgen.g_indirect_sym_load(list: TAsmList; const symname: string; const flags: tindsymflags): tregister; begin case target_info.system of system_powerpc_aix, system_powerpc64_aix: result:=load_got_symbol(list,symname,flags); else result:=inherited; end; end; function tcgppcgen.get_darwin_call_stub(const s: string; weak: boolean): tasmsymbol; var stubname: string; instr: taicpu; href: treference; l1: tasmsymbol; localgotlab: tasmlabel; cond: tasmcond; stubalign: byte; begin { function declared in the current unit? } { doesn't work correctly, because this will also return a hit if we } { previously took the address of an external procedure. It doesn't } { really matter, the linker will remove all unnecessary stubs. } stubname := 'L'+s+'$stub'; result := current_asmdata.getasmsymbol(stubname); if assigned(result) then exit; if current_asmdata.asmlists[al_imports]=nil then current_asmdata.asmlists[al_imports]:=TAsmList.create; if (cs_create_pic in current_settings.moduleswitches) then stubalign:=32 else stubalign:=16; new_section(current_asmdata.asmlists[al_imports],sec_stub,'',stubalign); result := current_asmdata.DefineAsmSymbol(stubname,AB_LOCAL,AT_FUNCTION,voidcodepointertype); current_asmdata.asmlists[al_imports].concat(Tai_symbol.Create(result,0)); { register as a weak symbol if necessary } if weak then current_asmdata.weakrefasmsymbol(s,AT_FUNCTION); current_asmdata.asmlists[al_imports].concat(tai_directive.create(asd_indirect_symbol,s)); l1 := current_asmdata.DefineAsmSymbol('L'+s+'$lazy_ptr',AB_LOCAL,AT_DATA,voidpointertype); reference_reset_symbol(href,l1,0,sizeof(pint),[]); href.refaddr := addr_higha; if (cs_create_pic in current_settings.moduleswitches) then begin current_asmdata.getjumplabel(localgotlab); href.relsymbol:=localgotlab; fillchar(cond,sizeof(cond),0); cond.simple:=false; cond.bo:=20; cond.bi:=31; current_asmdata.asmlists[al_imports].concat(taicpu.op_reg(A_MFLR,NR_R0)); instr:=taicpu.op_sym(A_BCL,localgotlab); instr.setcondition(cond); current_asmdata.asmlists[al_imports].concat(instr); a_label(current_asmdata.asmlists[al_imports],localgotlab); current_asmdata.asmlists[al_imports].concat(taicpu.op_reg(A_MFLR,NR_R11)); current_asmdata.asmlists[al_imports].concat(taicpu.op_reg_reg_ref(A_ADDIS,NR_R11,NR_R11,href)); current_asmdata.asmlists[al_imports].concat(taicpu.op_reg(A_MTLR,NR_R0)); end else current_asmdata.asmlists[al_imports].concat(taicpu.op_reg_ref(A_LIS,NR_R11,href)); href.refaddr := addr_low; href.base := NR_R11; {$ifndef cpu64bitaddr} current_asmdata.asmlists[al_imports].concat(taicpu.op_reg_ref(A_LWZU,NR_R12,href)); {$else cpu64bitaddr} { darwin/ppc64 uses a 32 bit absolute address here, strange... } current_asmdata.asmlists[al_imports].concat(taicpu.op_reg_ref(A_LDU,NR_R12,href)); {$endif cpu64bitaddr} current_asmdata.asmlists[al_imports].concat(taicpu.op_reg(A_MTCTR,NR_R12)); current_asmdata.asmlists[al_imports].concat(taicpu.op_none(A_BCTR)); new_section(current_asmdata.asmlists[al_imports],sec_data_lazy,'',sizeof(pint)); current_asmdata.asmlists[al_imports].concat(Tai_symbol.Create(l1,0)); current_asmdata.asmlists[al_imports].concat(tai_directive.create(asd_indirect_symbol,s)); current_asmdata.asmlists[al_imports].concat(tai_const.createname('dyld_stub_binding_helper',0)); end; procedure tcgppcgen.a_loadaddr_ref_reg(list : TAsmList;const ref : treference;r : tregister); var ref2, tmpref: treference; begin ref2 := ref; fixref(list,ref2); if assigned(ref2.symbol) then begin if target_info.system = system_powerpc_macosclassic then begin if macos_direct_globals then begin reference_reset(tmpref,ref2.alignment,ref2.volatility); tmpref.offset := ref2.offset; tmpref.symbol := ref2.symbol; tmpref.base := NR_NO; list.concat(taicpu.op_reg_reg_ref(A_ADDI,r,NR_RTOC,tmpref)); end else begin reference_reset(tmpref,ref2.alignment,ref2.volatility); tmpref.symbol := ref2.symbol; tmpref.offset := 0; tmpref.base := NR_RTOC; list.concat(taicpu.op_reg_ref(A_LWZ,r,tmpref)); if ref2.offset<>0 then a_op_const_reg(list,OP_ADD,OS_ADDR,ref2.offset,r); end; if ref2.base <> NR_NO then list.concat(taicpu.op_reg_reg_reg(A_ADD,r,r,ref2.base)); //list.concat(tai_comment.create(strpnew('*** a_loadaddr_ref_reg'))); end else begin { add the symbol's value to the base of the reference, and if the } { reference doesn't have a base, create one } reference_reset(tmpref,ref2.alignment,ref2.volatility); tmpref.offset := ref2.offset; tmpref.symbol := ref2.symbol; tmpref.relsymbol := ref2.relsymbol; tmpref.refaddr := addr_higha; if ref2.base<> NR_NO then begin list.concat(taicpu.op_reg_reg_ref(A_ADDIS,r, ref2.base,tmpref)); end else list.concat(taicpu.op_reg_ref(A_LIS,r,tmpref)); tmpref.base := NR_NO; tmpref.refaddr := addr_low; { can be folded with one of the next instructions by the } { optimizer probably } list.concat(taicpu.op_reg_reg_ref(A_ADDI,r,r,tmpref)); end end else if ref2.offset <> 0 Then if ref2.base <> NR_NO then a_op_const_reg_reg(list,OP_ADD,OS_ADDR,ref2.offset,ref2.base,r) { FixRef makes sure that "(ref.index <> R_NO) and (ref.offset <> 0)" never} { occurs, so now only ref.offset has to be loaded } else a_load_const_reg(list,OS_ADDR,ref2.offset,r) else if ref2.index <> NR_NO Then list.concat(taicpu.op_reg_reg_reg(A_ADD,r,ref2.base,ref2.index)) else if (ref2.base <> NR_NO) and (r <> ref2.base) then a_load_reg_reg(list,OS_ADDR,OS_ADDR,ref2.base,r) else list.concat(taicpu.op_reg_const(A_LI,r,0)); end; { calling a procedure by address } procedure tcgppcgen.a_call_reg(list : TAsmList;reg: tregister); var tmpref: treference; tmpreg: tregister; toc_offset: longint; begin tmpreg:=NR_NO; if target_info.system in systems_aix then begin { load function address in R0, and swap "reg" for R0 } reference_reset_base(tmpref,reg,0,ctempposinvalid,sizeof(pint),[]); a_load_ref_reg(list,OS_ADDR,OS_ADDR,tmpref,NR_R0); tmpreg:=reg; { no need to allocate/free R0, is already allocated by call node because it's a volatile register } reg:=NR_R0; { save current TOC } reference_reset_base(tmpref,NR_STACK_POINTER_REG,LA_RTOC_AIX,ctempposinvalid,sizeof(pint),[]); a_load_reg_ref(list,OS_ADDR,OS_ADDR,NR_RTOC,tmpref); end; list.concat(taicpu.op_reg(A_MTCTR,reg)); if target_info.system in systems_aix then begin { load target TOC and possible link register } reference_reset_base(tmpref,tmpreg,sizeof(pint),ctempposinvalid,sizeof(pint),[]); a_load_ref_reg(list,OS_ADDR,OS_ADDR,tmpref,NR_RTOC); tmpref.offset:=2*sizeof(pint); a_load_ref_reg(list,OS_ADDR,OS_ADDR,tmpref,NR_R11); end else if target_info.abi=abi_powerpc_elfv2 then begin { save current TOC } reference_reset_base(tmpref,NR_STACK_POINTER_REG,LA_RTOC_ELFV2,ctempposinvalid,sizeof(pint),[]); a_load_reg_ref(list,OS_ADDR,OS_ADDR,NR_RTOC,tmpref); { functions must be called via R12 for this ABI } if reg<>NR_R12 then begin getcpuregister(list,NR_R12); a_load_reg_reg(list,OS_ADDR,OS_ADDR,reg,NR_R12) end; end; list.concat(taicpu.op_none(A_BCTRL)); if (target_info.system in systems_aix) or (target_info.abi=abi_powerpc_elfv2) then begin if (target_info.abi=abi_powerpc_elfv2) and (reg<>NR_R12) then ungetcpuregister(list,NR_R12); { restore our TOC } if target_info.system in systems_aix then toc_offset:=LA_RTOC_AIX else toc_offset:=LA_RTOC_ELFV2; reference_reset_base(tmpref,NR_STACK_POINTER_REG,toc_offset,ctempposinvalid,sizeof(pint),[]); a_load_ref_reg(list,OS_ADDR,OS_ADDR,tmpref,NR_RTOC); end; include(current_procinfo.flags,pi_do_call); end; procedure tcgppcgen.a_load_reg_ref(list: TAsmList; fromsize, tosize: TCGSize; reg: tregister; const ref: treference); const StoreInstr: array[OS_8..OS_INT, boolean, boolean] of TAsmOp = { indexed? updating?} (((A_STB, A_STBU), (A_STBX, A_STBUX)), ((A_STH, A_STHU), (A_STHX, A_STHUX)), ((A_STW, A_STWU), (A_STWX, A_STWUX)) {$ifdef cpu64bitalu} , ((A_STD, A_STDU), (A_STDX, A_STDUX)) {$endif cpu64bitalu} ); var ref2: TReference; op: TAsmOp; begin if not (fromsize in [OS_8..OS_INT,OS_S8..OS_SINT]) then internalerror(2002090911); if not (tosize in [OS_8..OS_INT,OS_S8..OS_SINT]) then internalerror(2002090905); if tosize in [OS_S8..OS_SINT] then { storing is the same for signed and unsigned values } tosize := tcgsize(ord(tosize) - (ord(OS_S8) - ord(OS_8))); ref2 := ref; fixref(list, ref2); op := storeinstr[tcgsize2unsigned[tosize], ref2.index <> NR_NO, false]; a_load_store(list, op, reg, ref2); end; procedure tcgppcgen.a_loadfpu_reg_reg(list: TAsmList; fromsize, tosize: tcgsize; reg1, reg2: tregister); var op: tasmop; instr: taicpu; begin if not(fromsize in [OS_F32,OS_F64]) or not(tosize in [OS_F32,OS_F64]) then internalerror(2006123110); if (tosize < fromsize) then op:=A_FRSP else op:=A_FMR; instr := taicpu.op_reg_reg(op,reg2,reg1); list.concat(instr); if (op = A_FMR) then rg[R_FPUREGISTER].add_move_instruction(instr); end; procedure tcgppcgen.a_loadfpu_ref_reg(list: TAsmList; fromsize, tosize: tcgsize; const ref: treference; reg: tregister); const FpuLoadInstr: Array[OS_F32..OS_F64,boolean, boolean] of TAsmOp = { indexed? updating?} (((A_LFS,A_LFSU),(A_LFSX,A_LFSUX)), ((A_LFD,A_LFDU),(A_LFDX,A_LFDUX))); var op: tasmop; ref2: treference; begin if target_info.system in systems_aix then g_load_check_simple(list,ref,65536); if not(fromsize in [OS_F32,OS_F64]) or not(tosize in [OS_F32,OS_F64]) then internalerror(200201121); ref2 := ref; fixref(list,ref2); op := fpuloadinstr[fromsize,ref2.index <> NR_NO,false]; a_load_store(list,op,reg,ref2); if (fromsize > tosize) then a_loadfpu_reg_reg(list,fromsize,tosize,reg,reg); end; procedure tcgppcgen.a_loadfpu_reg_ref(list: TAsmList; fromsize, tosize: tcgsize; reg: tregister; const ref: treference); const FpuStoreInstr: Array[OS_F32..OS_F64,boolean, boolean] of TAsmOp = { indexed? updating?} (((A_STFS,A_STFSU),(A_STFSX,A_STFSUX)), ((A_STFD,A_STFDU),(A_STFDX,A_STFDUX))); var op: tasmop; ref2: treference; reg2: tregister; begin if not(fromsize in [OS_F32,OS_F64]) or not(tosize in [OS_F32,OS_F64]) then internalerror(200201122); ref2 := ref; fixref(list,ref2); op := fpustoreinstr[tosize,ref2.index <> NR_NO,false]; { some PPCs have a bug whereby storing a double to memory } { as single corrupts the value -> convert double to single } { first (bug confirmed on some G4s, but not on G5s) } if (tosize < fromsize) and (current_settings.cputype < cpu_PPC970) then begin reg2:=getfpuregister(list,tosize); a_loadfpu_reg_reg(list,fromsize,tosize,reg,reg2); reg:=reg2; end; a_load_store(list,op,reg,ref2); end; procedure tcgppcgen.g_overflowcheck(list: TAsmList; const l: tlocation; def: tdef); var hl : tasmlabel; flags : TResFlags; begin if not(cs_check_overflow in current_settings.localswitches) then exit; current_asmdata.getjumplabel(hl); if not ((def.typ=pointerdef) or ((def.typ=orddef) and (torddef(def).ordtype in [u64bit,u16bit,u32bit,u8bit,uchar, pasbool1,pasbool8,pasbool16,pasbool32,pasbool64]))) then begin if (current_settings.optimizecputype >= cpu_ppc970) or (current_settings.cputype >= cpu_ppc970) then begin { ... instructions setting overflow flag ... mfxerf R0 mtcrf 128, R0 ble cr0, label } list.concat(taicpu.op_reg(A_MFXER, NR_R0)); list.concat(taicpu.op_const_reg(A_MTCRF, 128, NR_R0)); flags.cr := RS_CR0; flags.flag := F_LE; a_jmp_flags(list, flags, hl); end else begin list.concat(taicpu.op_reg(A_MCRXR,NR_CR7)); a_jmp(list,A_BC,C_NO,7,hl) end; end else a_jmp_cond(list,OC_AE,hl); a_call_name(list,'FPC_OVERFLOW',false); a_label(list,hl); end; procedure tcgppcgen.g_profilecode(list: TAsmList); var paraloc1 : tcgpara; pd : tprocdef; begin if (target_info.system in [system_powerpc_darwin]) then begin pd:=search_system_proc('mcount'); paraloc1.init; paramanager.getcgtempparaloc(list,pd,1,paraloc1); a_load_reg_cgpara(list,OS_ADDR,NR_R0,paraloc1); paramanager.freecgpara(list,paraloc1); paraloc1.done; allocallcpuregisters(list); a_call_name(list,'mcount',false); deallocallcpuregisters(list); a_reg_dealloc(list,NR_R0); end; end; procedure tcgppcgen.a_jmp_flags(list: TAsmList; const f: TResFlags; l: tasmlabel); var c: tasmcond; f2: TResFlags; testbit: longint; begin f2:=f; testbit:=(f.cr-RS_CR0)*4; case f.flag of F_FA: f2.flag:=F_GT; F_FAE: begin list.concat(taicpu.op_const_const_const(A_CROR,testbit+1,testbit+1,testbit+2)); f2.flag:=F_GT; end; F_FB: f2.flag:=F_LT; F_FBE: begin list.concat(taicpu.op_const_const_const(A_CROR,testbit,testbit,testbit+2)); f2.flag:=F_LT; end; else ; end; c := flags_to_cond(f2); a_jmp(list,A_BC,c.cond,c.cr-RS_CR0,l); end; procedure tcgppcgen.a_jmp_cond(list : TAsmList;cond : TOpCmp; l: tasmlabel); begin a_jmp(list,A_BC,TOpCmp2AsmCond[cond],0,l); end; procedure tcgppcgen.a_jmp(list: TAsmList; op: tasmop; c: tasmcondflag; crval: longint; l: tasmlabel); var p: taicpu; begin p := taicpu.op_sym(op,l); if op <> A_B then create_cond_norm(c,crval,p.condition); p.is_jmp := true; list.concat(p) end; function tcgppcgen.load_got_symbol(list: TAsmList; const symbol : string; const flags: tindsymflags) : tregister; var l: tasmsymbol; ref: treference; begin if target_info.system=system_powerpc64_linux then begin l:=current_asmdata.getasmsymbol(symbol); reference_reset_symbol(ref,l,0,sizeof(pint),[]); ref.base:=NR_RTOC; ref.refaddr:=addr_pic; end else if target_info.system in systems_aix then get_aix_toc_sym(list,symbol,flags,ref,false) else internalerror(2007102010); result := getaddressregister(list); {$ifdef cpu64bitaddr} list.concat(taicpu.op_reg_ref(A_LD, result, ref)); {$else cpu64bitaddr} list.concat(taicpu.op_reg_ref(A_LWZ, result, ref)); {$endif cpu64bitaddr} end; procedure tcgppcgen.get_aix_toc_sym(list: TAsmList; const symname: string; const flags: tindsymflags; out ref: treference; force_direct_toc: boolean); const { The TOC on AIX is limited to 32KB worth of entries on AIX. If you need more entries, you have to add a level of indirection. In some cases, it's not possible to do this (e.g. assembler code). So by default, we use direct TOC entries until we're 500 from the maximum, and then start using indirect TOC entries. } AutoDirectTOCLimit = (high(smallint) div sizeof(pint)) - 500; var tmpref: treference; nlsymname: string; newsymname: ansistring; sym: TAsmSymbol; tocsym: TTOCAsmSymbol; tocnr, entrynr: longint; tmpreg: tregister; begin { all global symbol accesses always must be done via the TOC } nlsymname:='LC..'+symname; reference_reset_symbol(ref,current_asmdata.getasmsymbol(nlsymname),0,sizeof(pint),[]); if (assigned(ref.symbol) and not(ref.symbol is TTOCAsmSymbol)) or (not(ts_small_toc in current_settings.targetswitches) and (TPPCAsmData(current_asmdata).DirectTOCEntries skip } if assigned(ref.symbol) and (ref.symbol.bind<>AB_WEAK_EXTERNAL) then exit; if (ref.base=NR_STACK_POINTER_REG) or (ref.index=NR_STACK_POINTER_REG) or (assigned(current_procinfo) and ((ref.base=current_procinfo.framepointer) or (ref.index=current_procinfo.framepointer))) then exit; if assigned(ref.symbol) or (ref.offset<>0) or ((ref.base<>NR_NO) and (ref.index<>NR_NO)) then begin { can't allocate register, also used in wrappers and the like } reg:=NR_R0; a_reg_alloc(list,reg); a_loadaddr_ref_reg(list,ref,reg); end else if ref.base<>NR_NO then reg:=ref.base else reg:=ref.index; current_asmdata.getjumplabel(lab); if reg=NR_R0 then a_reg_dealloc(list,reg); a_cmp_const_reg_label(list,OS_ADDR,OC_A,size-1,reg,lab); a_call_name(list,'FPC_INVALIDPOINTER',false); a_label(list,lab); end; procedure tcgppcgen.g_flags2reg(list: TAsmList; size: TCgSize; const f: TResFlags; reg: TRegister); var testbit: byte; bitvalue: boolean; hreg: tregister; needsecondreg: boolean; begin hreg:=NR_NO; needsecondreg:=false; { get the bit to extract from the conditional register + its requested value (0 or 1) } testbit := ((f.cr - RS_CR0) * 4); case f.flag of F_EQ, F_NE: begin inc(testbit, 2); bitvalue := f.flag = F_EQ; end; F_LT, F_GE, F_FB: begin bitvalue := f.flag in [F_LT,F_FB]; end; F_GT, F_LE, F_FA: begin inc(testbit); bitvalue := f.flag in [F_GT,F_FA]; end; F_FAE: begin inc(testbit); bitvalue:=true; needsecondreg:=true; end; F_FBE: begin bitvalue:=true; needsecondreg:=true; end; else internalerror(200112261); end; { load the conditional register in the destination reg } list.concat(taicpu.op_reg(A_MFCR, reg)); { we will move the bit that has to be tested to bit 0 by rotating left } testbit := (testbit + 1) and 31; { for floating-point >= and <=, extract equality bit first } if needsecondreg then begin hreg:=getintregister(list,OS_INT); list.concat(taicpu.op_reg_reg_const_const_const( A_RLWINM,hreg,reg,(((f.cr-RS_CR0)*4)+3) and 31,31,31)); end; { extract bit } list.concat(taicpu.op_reg_reg_const_const_const( A_RLWINM,reg,reg,testbit,31,31)); if needsecondreg then list.concat(taicpu.op_reg_reg_reg(A_OR,reg,hreg,reg)) { if we need the inverse, xor with 1 } else if not bitvalue then list.concat(taicpu.op_reg_reg_const(A_XORI, reg, reg, 1)); end; function tcgppcgen.fixref(list: TAsmList; var ref: treference): boolean; var tmpreg: tregister; begin result := false; { Avoid recursion. } if (ref.refaddr in [addr_pic,addr_pic_no_got]) then exit; {$IFDEF EXTDEBUG} list.concat(tai_comment.create(strpnew('fixref0 ' + ref2string(ref)))); {$ENDIF EXTDEBUG} if (target_info.system in [system_powerpc_darwin,system_powerpc64_darwin]) and assigned(ref.symbol) and not assigned(ref.relsymbol) and ((ref.symbol.bind in [AB_EXTERNAL,AB_WEAK_EXTERNAL,AB_PRIVATE_EXTERN,AB_COMMON]) or (cs_create_pic in current_settings.moduleswitches))then begin if (ref.symbol.bind in [AB_EXTERNAL,AB_WEAK_EXTERNAL,AB_PRIVATE_EXTERN,AB_COMMON]) or ((target_info.system=system_powerpc64_darwin) and (ref.symbol.bind=AB_GLOBAL)) then begin tmpreg := g_indirect_sym_load(list,ref.symbol.name,asmsym2indsymflags(ref.symbol)); ref.symbol:=nil; end else begin include(current_procinfo.flags,pi_needs_got); tmpreg := getaddressregister(list); a_load_reg_reg(list,OS_ADDR,OS_ADDR,current_procinfo.got,tmpreg); if assigned(ref.relsymbol) then internalerror(2007093501); ref.relsymbol := current_procinfo.CurrGOTLabel; end; if (ref.base = NR_NO) then ref.base := tmpreg else if (ref.index = NR_NO) then ref.index := tmpreg else begin list.concat(taicpu.op_reg_reg_reg(A_ADD,tmpreg,ref.base,tmpreg)); ref.base := tmpreg; end; end; { if we have to create PIC, add the symbol to the TOC/GOT } if (((target_info.system = system_powerpc64_linux) and (cs_create_pic in current_settings.moduleswitches)) or (target_info.system in systems_aix)) and (assigned(ref.symbol) and not assigned(ref.relsymbol)) then begin tmpreg := load_got_symbol(list, ref.symbol.name, asmsym2indsymflags(ref.symbol)); if (ref.base = NR_NO) then ref.base := tmpreg else if (ref.index = NR_NO) then ref.index := tmpreg else begin a_op_reg_reg_reg(list, OP_ADD, OS_ADDR, ref.base, tmpreg, tmpreg); ref.base := tmpreg; end; ref.symbol := nil; {$IFDEF EXTDEBUG} list.concat(tai_comment.create(strpnew('fixref-pic ' + ref2string(ref)))); {$ENDIF EXTDEBUG} end; if (ref.base = NR_NO) then begin ref.base := ref.index; ref.index := NR_NO; end; if (ref.base <> NR_NO) then begin if (ref.index <> NR_NO) and ((ref.offset <> 0) or assigned(ref.symbol)) then begin result := true; tmpreg := rg[R_INTREGISTER].getregister(list,R_SUBWHOLE); list.concat(taicpu.op_reg_reg_reg( A_ADD,tmpreg,ref.base,ref.index)); ref.index := NR_NO; ref.base := tmpreg; end end; if (ref.index <> NR_NO) and (assigned(ref.symbol) or (ref.offset <> 0)) then internalerror(200208102); {$IFDEF EXTDEBUG} list.concat(tai_comment.create(strpnew('fixref1 ' + ref2string(ref)))); {$ENDIF EXTDEBUG} end; procedure tcgppcgen.a_load_store(list:TAsmList;op: tasmop;reg:tregister; ref: treference); var tmpreg: tregister; {$ifdef cpu64bitaddr} tmpreg2: tregister; {$endif cpu64bitaddr} tmpref: treference; largeOffset: Boolean; begin tmpreg := NR_NO; largeOffset:= hasLargeOffset(ref); if target_info.system in ([system_powerpc_macosclassic]+systems_aix) then begin if assigned(ref.symbol) and (ref.refaddr<>addr_pic_no_got) then begin {Load symbol's value} tmpreg := rg[R_INTREGISTER].getregister(list,R_SUBWHOLE); reference_reset(tmpref,sizeof(pint),[]); tmpref.symbol := ref.symbol; tmpref.base := NR_RTOC; tmpref.refaddr := addr_pic_no_got; if macos_direct_globals then list.concat(taicpu.op_reg_ref(A_LA,tmpreg,tmpref)) else {$ifdef cpu64bitaddr} list.concat(taicpu.op_reg_ref(A_LD,tmpreg,tmpref)); {$else cpu64bitaddr} list.concat(taicpu.op_reg_ref(A_LWZ,tmpreg,tmpref)); {$endif cpu64bitaddr} end; if largeOffset then begin {Add hi part of offset} reference_reset(tmpref,ref.alignment,[]); {$ifdef cpu64bitaddr} if (ref.offset < low(longint)) or (ref.offset > high(longint)) then begin { load upper 32 bits of the offset, adjusted for adding the lower 32 bits later } tmpreg2:=getintregister(list,OS_ADDR); a_load_const_reg(list,OS_ADDR,(ref.offset and $ffffffff00000000) + ord(longint(ref.offset)<0),tmpreg2); if tmpreg=NR_NO then tmpreg:=tmpreg2 else a_op_reg_reg(list,OP_ADD,OS_ADDR,tmpreg2,tmpreg); ref.offset:=longint(ref.offset); end; {$endif cpu64bitaddr} {Compensate when lo part is negative} tmpref.offset := Smallint(ref.offset >> 16) + ord(Smallint(ref.offset) < 0); if (tmpreg <> NR_NO) then list.concat(taicpu.op_reg_reg_const(A_ADDIS,tmpreg, tmpreg,tmpref.offset)) else begin tmpreg := getintregister(list,OS_ADDR); list.concat(taicpu.op_reg_const(A_LIS,tmpreg,tmpref.offset)); end; end; if (tmpreg <> NR_NO) then begin {Add content of base register} if ref.base <> NR_NO then list.concat(taicpu.op_reg_reg_reg(A_ADD,tmpreg, ref.base,tmpreg)); {Make ref ready to be used by op} ref.symbol:= nil; ref.base:= tmpreg; if largeOffset then ref.offset := Smallint(ref.offset); list.concat(taicpu.op_reg_ref(op,reg,ref)); //list.concat(tai_comment.create(strpnew('*** a_load_store indirect global'))); end else list.concat(taicpu.op_reg_ref(op,reg,ref)); end else {if target_info.system <> system_powerpc_macosclassic} begin if assigned(ref.symbol) or largeOffset then begin // TODO: offsets > 32 bit tmpreg := rg[R_INTREGISTER].getregister(list,R_SUBWHOLE); reference_reset(tmpref,ref.alignment,[]); tmpref.symbol := ref.symbol; tmpref.relsymbol := ref.relsymbol; tmpref.offset := ref.offset; tmpref.refaddr := addr_higha; if ref.base <> NR_NO then list.concat(taicpu.op_reg_reg_ref(A_ADDIS,tmpreg, ref.base,tmpref)) else list.concat(taicpu.op_reg_ref(A_LIS,tmpreg,tmpref)); ref.base := tmpreg; ref.refaddr := addr_low; list.concat(taicpu.op_reg_ref(op,reg,ref)); end else list.concat(taicpu.op_reg_ref(op,reg,ref)); end; end; { TPPCAsmData } procedure TPPCAsmData.GetNextSmallTocEntry(out tocnr, entrynr: longint); begin if fcurrenttocentries>(high(word) div sizeof(pint)) then begin fcurrenttocentries:=0; inc(ftocsections); end; tocnr:=ftocsections; entrynr:=fcurrenttocentries; inc(fcurrenttocentries); end; begin casmdata:=TPPCAsmData; end.