123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975 |
- {
- 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,aasmcpu,aasmtai,aasmdata,
- cpubase,cpuinfo,cgutils,rgcpu,
- parabase;
- type
- tcgppcgen = class(tcg)
- procedure a_param_const(list: TAsmList; size: tcgsize; a: aint; const paraloc : tcgpara); override;
- procedure a_paramaddr_ref(list : TAsmList;const r : treference;const paraloc : tcgpara); override;
- procedure a_call_reg(list : TAsmList;reg: tregister); override;
- procedure a_call_ref(list : TAsmList;ref: treference); 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_cond(list : TAsmList;cond : TOpCmp;l: tasmlabel);
- procedure g_intf_wrapper(list: TAsmList; procdef: tprocdef; const labelname: string; ioffset: longint);override;
- procedure g_maybe_got_init(list: TAsmList); override;
- protected
- function get_darwin_call_stub(const s: string): tasmsymbol;
- procedure a_load_subsetref_regs_noindex(list: TAsmList; subsetsize: tcgsize; loadbitsize: byte; const sref: tsubsetreference; valuereg, extra_value_reg: 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);
- { 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 save_lr_in_prologue: boolean;
- function load_got_symbol(list : TAsmList; symbol : string) : tregister;
- 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);
- {$ifdef extdebug}
- function ref2string(const ref : treference) : string;
- function cgsize2string(const size : TCgSize) : string;
- function cgop2string(const op : TOpCg) : String;
- {$endif extdebug}
- implementation
- uses
- {$ifdef extdebug}sysutils,{$endif}
- globals,verbose,systems,cutils,
- symconst,symsym,fmodule,
- rgobj,tgobj,cpupi,procinfo,paramgr;
- {$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 cgsize2string(const size : TCgSize) : string;
- const
- cgsize_strings : array[TCgSize] of string[8] = (
- 'OS_NO', 'OS_8', 'OS_16', 'OS_32', 'OS_64', 'OS_128', 'OS_S8', 'OS_S16', 'OS_S32',
- 'OS_S64', 'OS_S128', 'OS_F32', 'OS_F64', 'OS_F80', 'OS_C64', 'OS_F128',
- 'OS_M8', 'OS_M16', 'OS_M32', 'OS_M64', 'OS_M128', 'OS_MS8', 'OS_MS16', 'OS_MS32',
- 'OS_MS64', 'OS_MS128');
- begin
- result := cgsize_strings[size];
- 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'
- );
- 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_param_const(list: TAsmList; size: tcgsize; a: aint; const
- paraloc: tcgpara);
- var
- ref: treference;
- begin
- paraloc.check_simple_location;
- case paraloc.location^.loc of
- LOC_REGISTER, LOC_CREGISTER:
- a_load_const_reg(list, size, a, paraloc.location^.register);
- LOC_REFERENCE:
- begin
- reference_reset(ref);
- ref.base := paraloc.location^.reference.index;
- ref.offset := paraloc.location^.reference.offset;
- a_load_const_ref(list, size, a, ref);
- end;
- else
- internalerror(2002081101);
- end;
- end;
- procedure tcgppcgen.a_paramaddr_ref(list : TAsmList;const r : treference;const paraloc : tcgpara);
- var
- ref: treference;
- tmpreg: tregister;
- begin
- paraloc.check_simple_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);
- 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.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;
- end;
- end;
- end;
- function tcgppcgen.get_darwin_call_stub(const s: string): 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;
- current_asmdata.asmlists[al_imports].concat(Tai_section.create(sec_stub,'',0));
- if (cs_create_pic in current_settings.moduleswitches) then
- stubalign:=32
- else
- stubalign:=16;
- current_asmdata.asmlists[al_imports].concat(Tai_align.Create(stubalign));
- result := current_asmdata.RefAsmSymbol(stubname);
- current_asmdata.asmlists[al_imports].concat(Tai_symbol.Create(result,0));
- current_asmdata.asmlists[al_imports].concat(tai_directive.create(asd_indirect_symbol,s));
- l1 := current_asmdata.RefAsmSymbol('L'+s+'$lazy_ptr');
- reference_reset_symbol(href,l1,0);
- 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));
- current_asmdata.asmlists[al_imports].concat(tai_directive.create(asd_lazy_symbol_pointer,''));
- 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_macos then
- begin
- if macos_direct_globals then
- begin
- reference_reset(tmpref);
- 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);
- 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
- begin
- reference_reset(tmpref);
- tmpref.offset := ref2.offset;
- tmpref.base:= r;
- list.concat(taicpu.op_reg_ref(A_LA,r,tmpref));
- end;
- 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);
- 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);
- begin
- list.concat(taicpu.op_reg(A_MTCTR,reg));
- list.concat(taicpu.op_none(A_BCTRL));
- include(current_procinfo.flags,pi_do_call);
- end;
- procedure tcgppcgen.a_call_ref(list : TAsmList;ref: treference);
- var
- tempreg : TRegister;
- begin
- tempreg := getintregister(list, OS_ADDR);
- a_load_ref_reg(list,OS_ADDR,OS_ADDR,ref,tempreg);
- a_call_reg(list,tempreg);
- 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;
- tmpreg: tregister;
- op: TAsmOp;
- begin
- if not (fromsize in [OS_8..OS_INT,OS_S8..OS_SINT]) then
- internalerror(2002090903);
- 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);
- { unaligned 64 bit accesses are much slower than unaligned }
- { 32 bit accesses because they cause a hardware exception }
- { (which isn't handled by linux, so there you even get a }
- { crash) }
- if (ref2.alignment<>0) and
- (tosize in [OS_64,OS_S64]) and
- (ref.alignment<4) then
- begin
- if (ref2.base<>NR_NO) and
- (ref2.index<>NR_NO) then
- begin
- tmpreg:=getintregister(list,OS_64);
- a_op_reg_reg_reg(list,OP_SHR,OS_64,ref2.base,ref2.index,tmpreg);
- ref2.base:=tmpreg;
- ref2.index:=NR_NO;
- end;
- tmpreg:=getintregister(list,OS_64);
- a_op_const_reg_reg(list,OP_SHR,OS_64,32,reg,tmpreg);
- inc(ref2.offset,4);
- a_load_reg_ref(list,OS_32,OS_32,reg,ref2);
- dec(ref2.offset,4);
- a_load_reg_ref(list,OS_32,OS_32,tmpreg,ref2);
- exit;
- end;
- 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 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.a_load_subsetref_regs_noindex(list: TAsmList; subsetsize: tcgsize; loadbitsize: byte; const sref: tsubsetreference; valuereg, extra_value_reg: tregister);
- var
- fromsreg, tosreg: tsubsetregister;
- restbits: byte;
- begin
- restbits := (sref.bitlen - (loadbitsize - sref.startbit));
- if (subsetsize in [OS_S8..OS_S128]) then
- begin
- { sign extend }
- a_op_const_reg(list,OP_SHL,OS_INT,AIntBits-loadbitsize+sref.startbit,valuereg);
- a_op_const_reg(list,OP_SAR,OS_INT,AIntBits-sref.bitlen,valuereg);
- end
- else
- begin
- a_op_const_reg(list,OP_SHL,OS_INT,restbits,valuereg);
- { mask other bits }
- if (sref.bitlen <> AIntBits) then
- a_op_const_reg(list,OP_AND,OS_INT,(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 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,pasbool]))) 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');
- a_label(list,hl);
- end;
- procedure tcgppcgen.g_profilecode(list: TAsmList);
- var
- paraloc1 : tcgpara;
- begin
- if (target_info.system in [system_powerpc_darwin]) then
- begin
- paraloc1.init;
- paramanager.getintparaloc(pocall_cdecl,1,paraloc1);
- a_param_reg(list,OS_ADDR,NR_R0,paraloc1);
- paramanager.freeparaloc(list,paraloc1);
- paraloc1.done;
- allocallcpuregisters(list);
- a_call_name(list,'mcount');
- deallocallcpuregisters(list);
- a_reg_dealloc(list,NR_R0);
- end;
- 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;
- procedure tcgppcgen.g_intf_wrapper(list: TAsmList; procdef: tprocdef; const labelname: string; ioffset: longint);
- procedure loadvmttor11;
- var
- href : treference;
- begin
- reference_reset_base(href,NR_R3,0);
- 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(200006139);
- { call/jmp vmtoffs(%eax) ; method offs }
- reference_reset_base(href,NR_R11,procdef._class.vmtmethodoffset(procdef.extnumber));
- if hasLargeOffset(href) then
- begin
- {$ifdef cpu64}
- if (longint(href.offset) <> href.offset) then
- { add support for offsets > 32 bit }
- internalerror(200510201);
- {$endif cpu64}
- 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;
- a_load_ref_reg(list,OS_ADDR,OS_ADDR,href,NR_R11);
- if (target_info.system = system_powerpc64_linux) then
- begin
- reference_reset_base(href, NR_R11, 0);
- a_load_ref_reg(list, OS_ADDR, OS_ADDR, href, NR_R11);
- end;
- list.concat(taicpu.op_reg(A_MTCTR,NR_R11));
- list.concat(taicpu.op_none(A_BCTR));
- if (target_info.system = system_powerpc64_linux) 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._class) 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))
- else
- List.concat(Tai_symbol.Createname(labelname,AT_FUNCTION,0));
- { set param1 interface to self }
- g_adjust_self_value(list,procdef,ioffset);
- { case 4 }
- if po_virtualmethod in procdef.procoptions 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,get_darwin_call_stub(procdef.mangledname)));
- system_powerpc64_linux:
- {$note ts:todo add GOT change?? - think not needed :) }
- list.concat(taicpu.op_sym(A_B,current_asmdata.RefAsmSymbol('.' + procdef.mangledname)));
- else
- list.concat(taicpu.op_sym(A_B,current_asmdata.RefAsmSymbol(procdef.mangledname)))
- end;
- List.concat(Tai_symbol_end.Createname(labelname));
- end;
- function tcgppcgen.load_got_symbol(list: TAsmList; symbol : string) : tregister;
- var
- l: tasmsymbol;
- ref: treference;
- begin
- if (target_info.system <> system_powerpc64_linux) then
- internalerror(2007102010);
- l:=current_asmdata.getasmsymbol(symbol);
- reference_reset_symbol(ref,l,0);
- ref.base := NR_R2;
- ref.refaddr := addr_pic;
-
- result := rg[R_INTREGISTER].getregister(list, R_SUBWHOLE);
- {$IFDEF EXTDEBUG}
- list.concat(tai_comment.create(strpnew('loading got reference for ' + symbol)));
- {$ENDIF EXTDEBUG}
- // cg.a_load_ref_reg(list,OS_ADDR,OS_ADDR,ref,result);
-
- {$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;
-
-
- function tcgppcgen.fixref(list: TAsmList; var ref: treference): boolean;
- var
- tmpreg: tregister;
- begin
- result := false;
- { Avoid recursion. }
- if (ref.refaddr = addr_pic) 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 = AB_EXTERNAL) or
- (cs_create_pic in current_settings.moduleswitches))then
- begin
- if (ref.symbol.bind = AB_EXTERNAL) or
- ((cs_create_pic in current_settings.moduleswitches) and
- (ref.symbol.bind in [AB_COMMON,AB_GLOBAL])) then
- begin
- tmpreg := g_indirect_sym_load(list,ref.symbol.name);
- ref.symbol:=nil;
- end
- else
- begin
- include(current_procinfo.flags,pi_needs_got);
- tmpreg := current_procinfo.got;
- 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) and
- (assigned(ref.symbol)) then
- begin
- tmpreg := load_got_symbol(list, ref.symbol.name);
- 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;
- tmpref: treference;
- largeOffset: Boolean;
- begin
- tmpreg := NR_NO;
- largeOffset:= hasLargeOffset(ref);
- if target_info.system = system_powerpc_macos then
- begin
- if assigned(ref.symbol) then
- begin {Load symbol's value}
- tmpreg := rg[R_INTREGISTER].getregister(list,R_SUBWHOLE);
- reference_reset(tmpref);
- tmpref.symbol := ref.symbol;
- tmpref.base := NR_RTOC;
- if macos_direct_globals then
- list.concat(taicpu.op_reg_ref(A_LA,tmpreg,tmpref))
- else
- list.concat(taicpu.op_reg_ref(A_LWZ,tmpreg,tmpref));
- end;
- if largeOffset then
- begin {Add hi part of offset}
- reference_reset(tmpref);
- if Smallint(Lo(ref.offset)) < 0 then
- tmpref.offset := Hi(ref.offset) + 1 {Compensate when lo part is negative}
- else
- tmpref.offset := Hi(ref.offset);
- if (tmpreg <> NR_NO) then
- list.concat(taicpu.op_reg_reg_ref(A_ADDIS,tmpreg, tmpreg,tmpref))
- else
- begin
- tmpreg := rg[R_INTREGISTER].getregister(list,R_SUBWHOLE);
- list.concat(taicpu.op_reg_ref(A_LIS,tmpreg,tmpref));
- 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(Lo(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_macos}
- begin
- if assigned(ref.symbol) or
- largeOffset then
- begin
- tmpreg := rg[R_INTREGISTER].getregister(list,R_SUBWHOLE);
- reference_reset(tmpref);
- 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;
- end.
|