2
Copyright (c) 2006 by Florian Klaempfl
4
This unit implements the common part of the code generator for the PowerPC
6
This program is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2 of the License, or
9
(at your option) any later version.
11
This program is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
GNU General Public License for more details.
16
You should have received a copy of the GNU General Public License
17
along with this program; if not, write to the Free Software
18
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
****************************************************************************
29
globtype,symtype,symdef,
31
aasmbase,aasmcpu,aasmtai,aasmdata,
32
cpubase,cpuinfo,cgutils,rgcpu,
36
tcgppcgen = class(tcg)
37
procedure a_param_const(list: TAsmList; size: tcgsize; a: aint; const paraloc : tcgpara); override;
38
procedure a_paramaddr_ref(list : TAsmList;const r : treference;const paraloc : tcgpara); override;
40
procedure a_call_reg(list : TAsmList;reg: tregister); override;
41
procedure a_call_ref(list : TAsmList;ref: treference); override;
43
{ stores the contents of register reg to the memory location described by
45
procedure a_load_reg_ref(list: TAsmList; fromsize, tosize: TCGSize;
46
reg: tregister; const ref: treference); override;
48
{ fpu move instructions }
49
procedure a_loadfpu_reg_reg(list: TAsmList; fromsize, tosize: tcgsize; reg1, reg2: tregister); override;
50
procedure a_loadfpu_ref_reg(list: TAsmList; fromsize, tosize: tcgsize; const ref: treference; reg: tregister); override;
51
procedure a_loadfpu_reg_ref(list: TAsmList; fromsize, tosize: tcgsize; reg: tregister; const ref: treference); override;
54
procedure g_overflowcheck(list: TAsmList; const l: tlocation; def: tdef);override;
57
procedure g_profilecode(list: TAsmList); override;
59
procedure a_jmp_cond(list : TAsmList;cond : TOpCmp;l: tasmlabel);
61
function get_darwin_call_stub(const s: string): tasmsymbol;
62
procedure a_load_subsetref_regs_noindex(list: TAsmList; subsetsize: tcgsize; loadbitsize: byte; const sref: tsubsetreference; valuereg, extra_value_reg: tregister); override;
63
function fixref(list: TAsmList; var ref: treference): boolean; virtual; abstract;
64
procedure a_load_store(list:TAsmList;op: tasmop;reg:tregister;ref: treference);virtual;abstract;
66
{ creates the correct branch instruction for a given combination }
67
{ of asmcondflags and destination addressing mode }
68
procedure a_jmp(list: TAsmList; op: tasmop;
69
c: tasmcondflag; crval: longint; l: tasmlabel);
73
TOpCmp2AsmCond: Array[topcmp] of TAsmCondFlag = (C_NONE,C_EQ,C_GT,
74
C_LT,C_GE,C_LE,C_NE,C_LE,C_LT,C_GE,C_GT);
80
globals,verbose,systems,cutils,
81
symconst,symsym,fmodule,
82
rgobj,tgobj,cpupi,procinfo,paramgr;
85
procedure tcgppcgen.a_param_const(list: TAsmList; size: tcgsize; a: aint; const
90
paraloc.check_simple_location;
91
case paraloc.location^.loc of
92
LOC_REGISTER, LOC_CREGISTER:
93
a_load_const_reg(list, size, a, paraloc.location^.register);
97
ref.base := paraloc.location^.reference.index;
98
ref.offset := paraloc.location^.reference.offset;
99
a_load_const_ref(list, size, a, ref);
102
internalerror(2002081101);
107
procedure tcgppcgen.a_paramaddr_ref(list : TAsmList;const r : treference;const paraloc : tcgpara);
113
paraloc.check_simple_location;
114
case paraloc.location^.loc of
115
LOC_REGISTER,LOC_CREGISTER:
116
a_loadaddr_ref_reg(list,r,paraloc.location^.register);
119
reference_reset(ref);
120
ref.base := paraloc.location^.reference.index;
121
ref.offset := paraloc.location^.reference.offset;
122
tmpreg := rg[R_INTREGISTER].getregister(list,R_SUBWHOLE);
123
a_loadaddr_ref_reg(list,r,tmpreg);
124
a_load_reg_ref(list,OS_ADDR,OS_ADDR,tmpreg,ref);
127
internalerror(2002080701);
132
function tcgppcgen.get_darwin_call_stub(const s: string): tasmsymbol;
138
{ function declared in the current unit? }
139
{ doesn't work correctly, because this will also return a hit if we }
140
{ previously took the address of an external procedure. It doesn't }
141
{ really matter, the linker will remove all unnecessary stubs. }
142
stubname := 'L'+s+'$stub';
143
result := current_asmdata.getasmsymbol(stubname);
144
if assigned(result) then
147
if current_asmdata.asmlists[al_imports]=nil then
148
current_asmdata.asmlists[al_imports]:=TAsmList.create;
150
current_asmdata.asmlists[al_imports].concat(Tai_section.create(sec_stub,'',0));
151
current_asmdata.asmlists[al_imports].concat(Tai_align.Create(16));
152
result := current_asmdata.RefAsmSymbol(stubname);
153
current_asmdata.asmlists[al_imports].concat(Tai_symbol.Create(result,0));
154
current_asmdata.asmlists[al_imports].concat(tai_directive.create(asd_indirect_symbol,s));
155
l1 := current_asmdata.RefAsmSymbol('L'+s+'$lazy_ptr');
156
reference_reset_symbol(href,l1,0);
157
href.refaddr := addr_hi;
158
current_asmdata.asmlists[al_imports].concat(taicpu.op_reg_ref(A_LIS,NR_R11,href));
159
href.refaddr := addr_lo;
162
current_asmdata.asmlists[al_imports].concat(taicpu.op_reg_ref(A_LWZU,NR_R12,href));
164
{ darwin/ppc64 uses a 32 bit absolute address here, strange... }
165
current_asmdata.asmlists[al_imports].concat(taicpu.op_reg_ref(A_LDU,NR_R12,href));
167
current_asmdata.asmlists[al_imports].concat(taicpu.op_reg(A_MTCTR,NR_R12));
168
current_asmdata.asmlists[al_imports].concat(taicpu.op_none(A_BCTR));
169
current_asmdata.asmlists[al_imports].concat(tai_directive.create(asd_lazy_symbol_pointer,''));
170
current_asmdata.asmlists[al_imports].concat(Tai_symbol.Create(l1,0));
171
current_asmdata.asmlists[al_imports].concat(tai_directive.create(asd_indirect_symbol,s));
172
current_asmdata.asmlists[al_imports].concat(tai_const.createname('dyld_stub_binding_helper',0));
176
{ calling a procedure by address }
177
procedure tcgppcgen.a_call_reg(list : TAsmList;reg: tregister);
179
list.concat(taicpu.op_reg(A_MTCTR,reg));
180
list.concat(taicpu.op_none(A_BCTRL));
181
include(current_procinfo.flags,pi_do_call);
185
procedure tcgppcgen.a_call_ref(list : TAsmList;ref: treference);
189
tempreg := getintregister(list, OS_ADDR);
190
a_load_ref_reg(list,OS_ADDR,OS_ADDR,ref,tempreg);
191
a_call_reg(list,tempreg);
195
procedure tcgppcgen.a_load_reg_ref(list: TAsmList; fromsize, tosize: TCGSize;
196
reg: tregister; const ref: treference);
199
StoreInstr: array[OS_8..OS_INT, boolean, boolean] of TAsmOp =
200
{ indexed? updating?}
201
(((A_STB, A_STBU), (A_STBX, A_STBUX)),
202
((A_STH, A_STHU), (A_STHX, A_STHUX)),
203
((A_STW, A_STWU), (A_STWX, A_STWUX))
206
((A_STD, A_STDU), (A_STDX, A_STDUX))
213
if not (fromsize in [OS_8..OS_INT,OS_S8..OS_SINT]) then
214
internalerror(2002090903);
215
if not (tosize in [OS_8..OS_INT,OS_S8..OS_SINT]) then
216
internalerror(2002090905);
220
if tosize in [OS_S8..OS_SINT] then
221
{ storing is the same for signed and unsigned values }
222
tosize := tcgsize(ord(tosize) - (ord(OS_S8) - ord(OS_8)));
223
op := storeinstr[tcgsize2unsigned[tosize], ref2.index <> NR_NO, false];
224
a_load_store(list, op, reg, ref2);
229
procedure tcgppcgen.a_loadfpu_reg_reg(list: TAsmList; fromsize, tosize: tcgsize; reg1, reg2: tregister);
235
if not(fromsize in [OS_F32,OS_F64]) or
236
not(tosize in [OS_F32,OS_F64]) then
237
internalerror(2006123110);
238
if (tosize < fromsize) then
242
instr := taicpu.op_reg_reg(op,reg2,reg1);
245
rg[R_FPUREGISTER].add_move_instruction(instr);
249
procedure tcgppcgen.a_loadfpu_ref_reg(list: TAsmList; fromsize, tosize: tcgsize; const ref: treference; reg: tregister);
252
FpuLoadInstr: Array[OS_F32..OS_F64,boolean, boolean] of TAsmOp =
253
{ indexed? updating?}
254
(((A_LFS,A_LFSU),(A_LFSX,A_LFSUX)),
255
((A_LFD,A_LFDU),(A_LFDX,A_LFDUX)));
261
if not(fromsize in [OS_F32,OS_F64]) or
262
not(tosize in [OS_F32,OS_F64]) then
263
internalerror(200201121);
266
op := fpuloadinstr[fromsize,ref2.index <> NR_NO,false];
267
a_load_store(list,op,reg,ref2);
268
if (fromsize > tosize) then
269
a_loadfpu_reg_reg(list,fromsize,tosize,reg,reg);
273
procedure tcgppcgen.a_loadfpu_reg_ref(list: TAsmList; fromsize, tosize: tcgsize; reg: tregister; const ref: treference);
276
FpuStoreInstr: Array[OS_F32..OS_F64,boolean, boolean] of TAsmOp =
277
{ indexed? updating?}
278
(((A_STFS,A_STFSU),(A_STFSX,A_STFSUX)),
279
((A_STFD,A_STFDU),(A_STFDX,A_STFDUX)));
288
if not(fromsize in [OS_F32,OS_F64]) or
289
not(tosize in [OS_F32,OS_F64]) then
290
internalerror(200201122);
293
op := fpustoreinstr[tosize,ref2.index <> NR_NO,false];
295
{ some ppc's have a bug whereby storing a double to memory }
296
{ as single corrupts the value -> convert double to single }
298
if (tosize < fromsize) then
300
reg2:=getfpuregister(list,tosize);
301
a_loadfpu_reg_reg(list,fromsize,tosize,reg,reg2);
304
{$endif not cpu64bit}
305
a_load_store(list,op,reg,ref2);
309
procedure tcgppcgen.a_load_subsetref_regs_noindex(list: TAsmList; subsetsize: tcgsize; loadbitsize: byte; const sref: tsubsetreference; valuereg, extra_value_reg: tregister);
311
fromsreg, tosreg: tsubsetregister;
314
restbits := (sref.bitlen - (loadbitsize - sref.startbit));
315
if (subsetsize in [OS_S8..OS_S128]) then
318
a_op_const_reg(list,OP_SHL,OS_INT,AIntBits-loadbitsize+sref.startbit,valuereg);
319
a_op_const_reg(list,OP_SAR,OS_INT,AIntBits-sref.bitlen,valuereg);
323
a_op_const_reg(list,OP_SHL,OS_INT,restbits,valuereg);
325
if (sref.bitlen <> AIntBits) then
326
a_op_const_reg(list,OP_AND,OS_INT,(aword(1) shl sref.bitlen)-1,valuereg);
328
{ use subsetreg routine, it may have been overridden with an optimized version }
329
fromsreg.subsetreg := extra_value_reg;
330
fromsreg.subsetregsize := OS_INT;
331
{ subsetregs always count bits from right to left }
332
fromsreg.startbit := loadbitsize-restbits;
333
fromsreg.bitlen := restbits;
335
tosreg.subsetreg := valuereg;
336
tosreg.subsetregsize := OS_INT;
337
tosreg.startbit := 0;
338
tosreg.bitlen := restbits;
340
a_load_subsetreg_subsetreg(list,subsetsize,subsetsize,fromsreg,tosreg);
344
procedure tcgppcgen.g_overflowcheck(list: TAsmList; const l: tlocation; def: tdef);
349
if not(cs_check_overflow in current_settings.localswitches) then
351
current_asmdata.getjumplabel(hl);
352
if not ((def.typ=pointerdef) or
353
((def.typ=orddef) and
354
(torddef(def).ordtype in [u64bit,u16bit,u32bit,u8bit,uchar,
355
bool8bit,bool16bit,bool32bit,bool64bit]))) then
357
if (current_settings.optimizecputype >= cpu_ppc970) or
358
(current_settings.cputype >= cpu_ppc970) then
360
{ ... instructions setting overflow flag ...
364
list.concat(taicpu.op_reg(A_MFXER, NR_R0));
365
list.concat(taicpu.op_const_reg(A_MTCRF, 128, NR_R0));
368
a_jmp_flags(list, flags, hl);
372
list.concat(taicpu.op_reg(A_MCRXR,NR_CR7));
373
a_jmp(list,A_BC,C_NO,7,hl)
377
a_jmp_cond(list,OC_AE,hl);
378
a_call_name(list,'FPC_OVERFLOW');
383
procedure tcgppcgen.g_profilecode(list: TAsmList);
388
if (target_info.system in [system_powerpc_darwin]) then
391
paramanager.getintparaloc(pocall_cdecl,1,paraloc1);
392
a_param_reg(list,OS_ADDR,NR_R0,paraloc1);
393
paramanager.freeparaloc(list,paraloc1);
395
allocallcpuregisters(list);
396
a_call_name(list,'mcount');
397
deallocallcpuregisters(list);
398
a_reg_dealloc(list,NR_R0);
403
procedure tcgppcgen.a_jmp_cond(list : TAsmList;cond : TOpCmp; l: tasmlabel);
405
a_jmp(list,A_BC,TOpCmp2AsmCond[cond],0,l);
409
procedure tcgppcgen.a_jmp(list: TAsmList; op: tasmop; c: tasmcondflag;
410
crval: longint; l: tasmlabel);
415
p := taicpu.op_sym(op,l);
417
create_cond_norm(c,crval,p.condition);