cgppc.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. {
  2. Copyright (c) 2006 by Florian Klaempfl
  3. This unit implements the common part of the code generator for the PowerPC
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. ****************************************************************************
  16. }
  17. unit cgppc;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. globtype,symtype,symdef,
  22. cgbase,cgobj,
  23. aasmbase,aasmcpu,aasmtai,aasmdata,
  24. cpubase,cpuinfo,cgutils,rgcpu,
  25. parabase;
  26. type
  27. tcgppcgen = class(tcg)
  28. procedure a_param_const(list: TAsmList; size: tcgsize; a: aint; const paraloc : tcgpara); override;
  29. procedure a_paramaddr_ref(list : TAsmList;const r : treference;const paraloc : tcgpara); override;
  30. procedure a_call_reg(list : TAsmList;reg: tregister); override;
  31. procedure a_call_ref(list : TAsmList;ref: treference); override;
  32. { stores the contents of register reg to the memory location described by
  33. ref }
  34. procedure a_load_reg_ref(list: TAsmList; fromsize, tosize: TCGSize;
  35. reg: tregister; const ref: treference); override;
  36. { fpu move instructions }
  37. procedure a_loadfpu_reg_reg(list: TAsmList; fromsize, tosize: tcgsize; reg1, reg2: tregister); override;
  38. procedure a_loadfpu_ref_reg(list: TAsmList; fromsize, tosize: tcgsize; const ref: treference; reg: tregister); override;
  39. procedure a_loadfpu_reg_ref(list: TAsmList; fromsize, tosize: tcgsize; reg: tregister; const ref: treference); override;
  40. { overflow checking }
  41. procedure g_overflowcheck(list: TAsmList; const l: tlocation; def: tdef);override;
  42. { entry code }
  43. procedure g_profilecode(list: TAsmList); override;
  44. procedure a_jmp_cond(list : TAsmList;cond : TOpCmp;l: tasmlabel);
  45. protected
  46. function get_darwin_call_stub(const s: string): tasmsymbol;
  47. procedure a_load_subsetref_regs_noindex(list: TAsmList; subsetsize: tcgsize; loadbitsize: byte; const sref: tsubsetreference; valuereg, extra_value_reg: tregister); override;
  48. function fixref(list: TAsmList; var ref: treference): boolean; virtual; abstract;
  49. procedure a_load_store(list:TAsmList;op: tasmop;reg:tregister;ref: treference);virtual;abstract;
  50. { creates the correct branch instruction for a given combination }
  51. { of asmcondflags and destination addressing mode }
  52. procedure a_jmp(list: TAsmList; op: tasmop;
  53. c: tasmcondflag; crval: longint; l: tasmlabel);
  54. end;
  55. const
  56. TOpCmp2AsmCond: Array[topcmp] of TAsmCondFlag = (C_NONE,C_EQ,C_GT,
  57. C_LT,C_GE,C_LE,C_NE,C_LE,C_LT,C_GE,C_GT);
  58. implementation
  59. uses
  60. globals,verbose,systems,cutils,
  61. symconst,symsym,fmodule,
  62. rgobj,tgobj,cpupi,procinfo,paramgr;
  63. procedure tcgppcgen.a_param_const(list: TAsmList; size: tcgsize; a: aint; const
  64. paraloc: tcgpara);
  65. var
  66. ref: treference;
  67. begin
  68. paraloc.check_simple_location;
  69. case paraloc.location^.loc of
  70. LOC_REGISTER, LOC_CREGISTER:
  71. a_load_const_reg(list, size, a, paraloc.location^.register);
  72. LOC_REFERENCE:
  73. begin
  74. reference_reset(ref);
  75. ref.base := paraloc.location^.reference.index;
  76. ref.offset := paraloc.location^.reference.offset;
  77. a_load_const_ref(list, size, a, ref);
  78. end;
  79. else
  80. internalerror(2002081101);
  81. end;
  82. end;
  83. procedure tcgppcgen.a_paramaddr_ref(list : TAsmList;const r : treference;const paraloc : tcgpara);
  84. var
  85. ref: treference;
  86. tmpreg: tregister;
  87. begin
  88. paraloc.check_simple_location;
  89. case paraloc.location^.loc of
  90. LOC_REGISTER,LOC_CREGISTER:
  91. a_loadaddr_ref_reg(list,r,paraloc.location^.register);
  92. LOC_REFERENCE:
  93. begin
  94. reference_reset(ref);
  95. ref.base := paraloc.location^.reference.index;
  96. ref.offset := paraloc.location^.reference.offset;
  97. tmpreg := rg[R_INTREGISTER].getregister(list,R_SUBWHOLE);
  98. a_loadaddr_ref_reg(list,r,tmpreg);
  99. a_load_reg_ref(list,OS_ADDR,OS_ADDR,tmpreg,ref);
  100. end;
  101. else
  102. internalerror(2002080701);
  103. end;
  104. end;
  105. function tcgppcgen.get_darwin_call_stub(const s: string): tasmsymbol;
  106. var
  107. stubname: string;
  108. href: treference;
  109. l1: tasmsymbol;
  110. begin
  111. { function declared in the current unit? }
  112. { doesn't work correctly, because this will also return a hit if we }
  113. { previously took the address of an external procedure. It doesn't }
  114. { really matter, the linker will remove all unnecessary stubs. }
  115. stubname := 'L'+s+'$stub';
  116. result := current_asmdata.getasmsymbol(stubname);
  117. if assigned(result) then
  118. exit;
  119. if current_asmdata.asmlists[al_imports]=nil then
  120. current_asmdata.asmlists[al_imports]:=TAsmList.create;
  121. current_asmdata.asmlists[al_imports].concat(Tai_section.create(sec_stub,'',0));
  122. current_asmdata.asmlists[al_imports].concat(Tai_align.Create(16));
  123. result := current_asmdata.RefAsmSymbol(stubname);
  124. current_asmdata.asmlists[al_imports].concat(Tai_symbol.Create(result,0));
  125. current_asmdata.asmlists[al_imports].concat(tai_directive.create(asd_indirect_symbol,s));
  126. l1 := current_asmdata.RefAsmSymbol('L'+s+'$lazy_ptr');
  127. reference_reset_symbol(href,l1,0);
  128. href.refaddr := addr_hi;
  129. current_asmdata.asmlists[al_imports].concat(taicpu.op_reg_ref(A_LIS,NR_R11,href));
  130. href.refaddr := addr_lo;
  131. href.base := NR_R11;
  132. {$ifndef cpu64bit}
  133. current_asmdata.asmlists[al_imports].concat(taicpu.op_reg_ref(A_LWZU,NR_R12,href));
  134. {$else cpu64bit}
  135. { darwin/ppc64 uses a 32 bit absolute address here, strange... }
  136. current_asmdata.asmlists[al_imports].concat(taicpu.op_reg_ref(A_LDU,NR_R12,href));
  137. {$endif cpu64bit}
  138. current_asmdata.asmlists[al_imports].concat(taicpu.op_reg(A_MTCTR,NR_R12));
  139. current_asmdata.asmlists[al_imports].concat(taicpu.op_none(A_BCTR));
  140. current_asmdata.asmlists[al_imports].concat(tai_directive.create(asd_lazy_symbol_pointer,''));
  141. current_asmdata.asmlists[al_imports].concat(Tai_symbol.Create(l1,0));
  142. current_asmdata.asmlists[al_imports].concat(tai_directive.create(asd_indirect_symbol,s));
  143. current_asmdata.asmlists[al_imports].concat(tai_const.createname('dyld_stub_binding_helper',0));
  144. end;
  145. { calling a procedure by address }
  146. procedure tcgppcgen.a_call_reg(list : TAsmList;reg: tregister);
  147. begin
  148. list.concat(taicpu.op_reg(A_MTCTR,reg));
  149. list.concat(taicpu.op_none(A_BCTRL));
  150. include(current_procinfo.flags,pi_do_call);
  151. end;
  152. procedure tcgppcgen.a_call_ref(list : TAsmList;ref: treference);
  153. var
  154. tempreg : TRegister;
  155. begin
  156. tempreg := getintregister(list, OS_ADDR);
  157. a_load_ref_reg(list,OS_ADDR,OS_ADDR,ref,tempreg);
  158. a_call_reg(list,tempreg);
  159. end;
  160. procedure tcgppcgen.a_load_reg_ref(list: TAsmList; fromsize, tosize: TCGSize;
  161. reg: tregister; const ref: treference);
  162. const
  163. StoreInstr: array[OS_8..OS_INT, boolean, boolean] of TAsmOp =
  164. { indexed? updating?}
  165. (((A_STB, A_STBU), (A_STBX, A_STBUX)),
  166. ((A_STH, A_STHU), (A_STHX, A_STHUX)),
  167. ((A_STW, A_STWU), (A_STWX, A_STWUX))
  168. {$ifdef cpu64bit}
  169. ,
  170. ((A_STD, A_STDU), (A_STDX, A_STDUX))
  171. {$endif cpu64bit}
  172. );
  173. var
  174. op: TAsmOp;
  175. ref2: TReference;
  176. begin
  177. if not (fromsize in [OS_8..OS_INT,OS_S8..OS_SINT]) then
  178. internalerror(2002090903);
  179. if not (tosize in [OS_8..OS_INT,OS_S8..OS_SINT]) then
  180. internalerror(2002090905);
  181. ref2 := ref;
  182. fixref(list, ref2);
  183. if tosize in [OS_S8..OS_SINT] then
  184. { storing is the same for signed and unsigned values }
  185. tosize := tcgsize(ord(tosize) - (ord(OS_S8) - ord(OS_8)));
  186. op := storeinstr[tcgsize2unsigned[tosize], ref2.index <> NR_NO, false];
  187. a_load_store(list, op, reg, ref2);
  188. end;
  189. procedure tcgppcgen.a_loadfpu_reg_reg(list: TAsmList; fromsize, tosize: tcgsize; reg1, reg2: tregister);
  190. var
  191. op: tasmop;
  192. instr: taicpu;
  193. begin
  194. if not(fromsize in [OS_F32,OS_F64]) or
  195. not(tosize in [OS_F32,OS_F64]) then
  196. internalerror(2006123110);
  197. if (tosize < fromsize) then
  198. op:=A_FRSP
  199. else
  200. op:=A_FMR;
  201. instr := taicpu.op_reg_reg(op,reg2,reg1);
  202. list.concat(instr);
  203. if (op = A_FMR) then
  204. rg[R_FPUREGISTER].add_move_instruction(instr);
  205. end;
  206. procedure tcgppcgen.a_loadfpu_ref_reg(list: TAsmList; fromsize, tosize: tcgsize; const ref: treference; reg: tregister);
  207. const
  208. FpuLoadInstr: Array[OS_F32..OS_F64,boolean, boolean] of TAsmOp =
  209. { indexed? updating?}
  210. (((A_LFS,A_LFSU),(A_LFSX,A_LFSUX)),
  211. ((A_LFD,A_LFDU),(A_LFDX,A_LFDUX)));
  212. var
  213. op: tasmop;
  214. ref2: treference;
  215. begin
  216. if not(fromsize in [OS_F32,OS_F64]) or
  217. not(tosize in [OS_F32,OS_F64]) then
  218. internalerror(200201121);
  219. ref2 := ref;
  220. fixref(list,ref2);
  221. op := fpuloadinstr[fromsize,ref2.index <> NR_NO,false];
  222. a_load_store(list,op,reg,ref2);
  223. if (fromsize > tosize) then
  224. a_loadfpu_reg_reg(list,fromsize,tosize,reg,reg);
  225. end;
  226. procedure tcgppcgen.a_loadfpu_reg_ref(list: TAsmList; fromsize, tosize: tcgsize; reg: tregister; const ref: treference);
  227. const
  228. FpuStoreInstr: Array[OS_F32..OS_F64,boolean, boolean] of TAsmOp =
  229. { indexed? updating?}
  230. (((A_STFS,A_STFSU),(A_STFSX,A_STFSUX)),
  231. ((A_STFD,A_STFDU),(A_STFDX,A_STFDUX)));
  232. var
  233. op: tasmop;
  234. ref2: treference;
  235. {$ifndef cpu64bit}
  236. reg2: tregister;
  237. {$endif cpu64bit}
  238. begin
  239. if not(fromsize in [OS_F32,OS_F64]) or
  240. not(tosize in [OS_F32,OS_F64]) then
  241. internalerror(200201122);
  242. ref2 := ref;
  243. fixref(list,ref2);
  244. op := fpustoreinstr[tosize,ref2.index <> NR_NO,false];
  245. {$ifndef cpu64bit}
  246. { some ppc's have a bug whereby storing a double to memory }
  247. { as single corrupts the value -> convert double to single }
  248. { first }
  249. if (tosize < fromsize) then
  250. begin
  251. reg2:=getfpuregister(list,tosize);
  252. a_loadfpu_reg_reg(list,fromsize,tosize,reg,reg2);
  253. reg:=reg2;
  254. end;
  255. {$endif not cpu64bit}
  256. a_load_store(list,op,reg,ref2);
  257. end;
  258. procedure tcgppcgen.a_load_subsetref_regs_noindex(list: TAsmList; subsetsize: tcgsize; loadbitsize: byte; const sref: tsubsetreference; valuereg, extra_value_reg: tregister);
  259. var
  260. fromsreg, tosreg: tsubsetregister;
  261. restbits: byte;
  262. begin
  263. restbits := (sref.bitlen - (loadbitsize - sref.startbit));
  264. if (subsetsize in [OS_S8..OS_S128]) then
  265. begin
  266. { sign extend }
  267. a_op_const_reg(list,OP_SHL,OS_INT,AIntBits-loadbitsize+sref.startbit,valuereg);
  268. a_op_const_reg(list,OP_SAR,OS_INT,AIntBits-sref.bitlen,valuereg);
  269. end
  270. else
  271. begin
  272. a_op_const_reg(list,OP_SHL,OS_INT,restbits,valuereg);
  273. { mask other bits }
  274. if (sref.bitlen <> AIntBits) then
  275. a_op_const_reg(list,OP_AND,OS_INT,(aword(1) shl sref.bitlen)-1,valuereg);
  276. end;
  277. { use subsetreg routine, it may have been overridden with an optimized version }
  278. fromsreg.subsetreg := extra_value_reg;
  279. fromsreg.subsetregsize := OS_INT;
  280. { subsetregs always count bits from right to left }
  281. fromsreg.startbit := loadbitsize-restbits;
  282. fromsreg.bitlen := restbits;
  283. tosreg.subsetreg := valuereg;
  284. tosreg.subsetregsize := OS_INT;
  285. tosreg.startbit := 0;
  286. tosreg.bitlen := restbits;
  287. a_load_subsetreg_subsetreg(list,subsetsize,subsetsize,fromsreg,tosreg);
  288. end;
  289. procedure tcgppcgen.g_overflowcheck(list: TAsmList; const l: tlocation; def: tdef);
  290. var
  291. hl : tasmlabel;
  292. flags : TResFlags;
  293. begin
  294. if not(cs_check_overflow in current_settings.localswitches) then
  295. exit;
  296. current_asmdata.getjumplabel(hl);
  297. if not ((def.typ=pointerdef) or
  298. ((def.typ=orddef) and
  299. (torddef(def).ordtype in [u64bit,u16bit,u32bit,u8bit,uchar,
  300. bool8bit,bool16bit,bool32bit,bool64bit]))) then
  301. begin
  302. if (current_settings.optimizecputype >= cpu_ppc970) or
  303. (current_settings.cputype >= cpu_ppc970) then
  304. begin
  305. { ... instructions setting overflow flag ...
  306. mfxerf R0
  307. mtcrf 128, R0
  308. ble cr0, label }
  309. list.concat(taicpu.op_reg(A_MFXER, NR_R0));
  310. list.concat(taicpu.op_const_reg(A_MTCRF, 128, NR_R0));
  311. flags.cr := RS_CR0;
  312. flags.flag := F_LE;
  313. a_jmp_flags(list, flags, hl);
  314. end
  315. else
  316. begin
  317. list.concat(taicpu.op_reg(A_MCRXR,NR_CR7));
  318. a_jmp(list,A_BC,C_NO,7,hl)
  319. end;
  320. end
  321. else
  322. a_jmp_cond(list,OC_AE,hl);
  323. a_call_name(list,'FPC_OVERFLOW');
  324. a_label(list,hl);
  325. end;
  326. procedure tcgppcgen.g_profilecode(list: TAsmList);
  327. var
  328. paraloc1 : tcgpara;
  329. reg: tregister;
  330. begin
  331. if (target_info.system in [system_powerpc_darwin]) then
  332. begin
  333. paraloc1.init;
  334. paramanager.getintparaloc(pocall_cdecl,1,paraloc1);
  335. a_param_reg(list,OS_ADDR,NR_R0,paraloc1);
  336. paramanager.freeparaloc(list,paraloc1);
  337. paraloc1.done;
  338. allocallcpuregisters(list);
  339. a_call_name(list,'mcount');
  340. deallocallcpuregisters(list);
  341. a_reg_dealloc(list,NR_R0);
  342. end;
  343. end;
  344. procedure tcgppcgen.a_jmp_cond(list : TAsmList;cond : TOpCmp; l: tasmlabel);
  345. begin
  346. a_jmp(list,A_BC,TOpCmp2AsmCond[cond],0,l);
  347. end;
  348. procedure tcgppcgen.a_jmp(list: TAsmList; op: tasmop; c: tasmcondflag;
  349. crval: longint; l: tasmlabel);
  350. var
  351. p: taicpu;
  352. begin
  353. p := taicpu.op_sym(op,l);
  354. if op <> A_B then
  355. create_cond_norm(c,crval,p.condition);
  356. p.is_jmp := true;
  357. list.concat(p)
  358. end;
  359. end.