cgcpu.pas 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673
  1. {
  2. Copyright (c) 1998-2002 by Florian Klaempfl
  3. This unit implements 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 cgcpu;
  18. {$I fpcdefs.inc}
  19. interface
  20. uses
  21. globtype, symtype, symdef,
  22. cgbase, cgobj,
  23. aasmbase, aasmcpu, aasmtai,
  24. cpubase, cpuinfo, cgutils, rgcpu,
  25. parabase;
  26. type
  27. tcgppc = class(tcg)
  28. procedure init_register_allocators; override;
  29. procedure done_register_allocators; override;
  30. { passing parameters, per default the parameter is pushed }
  31. { nr gives the number of the parameter (enumerated from }
  32. { left to right), this allows to move the parameter to }
  33. { register, if the cpu supports register calling }
  34. { conventions }
  35. procedure a_param_const(list: taasmoutput; size: tcgsize; a: aint; const
  36. paraloc: tcgpara); override;
  37. procedure a_param_ref(list: taasmoutput; size: tcgsize; const r: treference;
  38. const paraloc: tcgpara); override;
  39. procedure a_paramaddr_ref(list: taasmoutput; const r: treference; const
  40. paraloc: tcgpara); override;
  41. procedure a_call_name(list: taasmoutput; const s: string); override;
  42. procedure a_call_reg(list: taasmoutput; reg: tregister); override;
  43. procedure a_op_const_reg(list: taasmoutput; Op: TOpCG; size: TCGSize; a:
  44. aint; reg: TRegister); override;
  45. procedure a_op_reg_reg(list: taasmoutput; Op: TOpCG; size: TCGSize; src,
  46. dst: TRegister); override;
  47. procedure a_op_const_reg_reg(list: taasmoutput; op: TOpCg;
  48. size: tcgsize; a: aint; src, dst: tregister); override;
  49. procedure a_op_reg_reg_reg(list: taasmoutput; op: TOpCg;
  50. size: tcgsize; src1, src2, dst: tregister); override;
  51. { move instructions }
  52. procedure a_load_const_reg(list: taasmoutput; size: tcgsize; a: aint; reg:
  53. tregister); override;
  54. { stores the contents of register reg to the memory location described by
  55. ref }
  56. procedure a_load_reg_ref(list: taasmoutput; fromsize, tosize: tcgsize; reg:
  57. tregister; const ref: treference); override;
  58. { loads the memory pointed to by ref into register reg }
  59. procedure a_load_ref_reg(list: taasmoutput; fromsize, tosize: tcgsize; const
  60. Ref: treference; reg: tregister); override;
  61. procedure a_load_reg_reg(list: taasmoutput; fromsize, tosize: tcgsize; reg1,
  62. reg2: tregister); override;
  63. { fpu move instructions }
  64. procedure a_loadfpu_reg_reg(list: taasmoutput; size: tcgsize; reg1, reg2:
  65. tregister); override;
  66. procedure a_loadfpu_ref_reg(list: taasmoutput; size: tcgsize; const ref:
  67. treference; reg: tregister); override;
  68. procedure a_loadfpu_reg_ref(list: taasmoutput; size: tcgsize; reg:
  69. tregister; const ref: treference); override;
  70. { comparison operations }
  71. procedure a_cmp_const_reg_label(list: taasmoutput; size: tcgsize; cmp_op:
  72. topcmp; a: aint; reg: tregister;
  73. l: tasmlabel); override;
  74. procedure a_cmp_reg_reg_label(list: taasmoutput; size: tcgsize; cmp_op:
  75. topcmp; reg1, reg2: tregister; l: tasmlabel); override;
  76. procedure a_jmp_name(list: taasmoutput; const s: string); override;
  77. procedure a_jmp_always(list: taasmoutput; l: tasmlabel); override;
  78. procedure a_jmp_flags(list: taasmoutput; const f: TResFlags; l: tasmlabel);
  79. override;
  80. procedure g_flags2reg(list: taasmoutput; size: TCgSize; const f: TResFlags;
  81. reg: TRegister); override;
  82. procedure g_proc_entry(list: taasmoutput; localsize: longint; nostackframe:
  83. boolean); override;
  84. procedure g_proc_exit(list: taasmoutput; parasize: longint; nostackframe:
  85. boolean); override;
  86. procedure g_save_standard_registers(list: Taasmoutput); override;
  87. procedure g_restore_standard_registers(list: Taasmoutput); override;
  88. procedure a_loadaddr_ref_reg(list: taasmoutput; const ref: treference; r:
  89. tregister); override;
  90. procedure g_concatcopy(list: taasmoutput; const source, dest: treference;
  91. len: aint); override;
  92. procedure g_overflowcheck(list: taasmoutput; const l: tlocation; def: tdef);
  93. override;
  94. procedure a_jmp_cond(list: taasmoutput; cond: TOpCmp; l: tasmlabel);
  95. procedure g_intf_wrapper(list: TAAsmoutput; procdef: tprocdef; const
  96. labelname: string; ioffset: longint); override;
  97. private
  98. { Make sure ref is a valid reference for the PowerPC and sets the }
  99. { base to the value of the index if (base = R_NO). }
  100. { Returns true if the reference contained a base, index and an }
  101. { offset or symbol, in which case the base will have been changed }
  102. { to a tempreg (which has to be freed by the caller) containing }
  103. { the sum of part of the original reference }
  104. function fixref(list: taasmoutput; var ref: treference; const size : TCgsize): boolean;
  105. { returns whether a reference can be used immediately in a powerpc }
  106. { instruction }
  107. function issimpleref(const ref: treference): boolean;
  108. { contains the common code of a_load_reg_ref and a_load_ref_reg }
  109. procedure a_load_store(list: taasmoutput; op: tasmop; reg: tregister;
  110. ref: treference);
  111. { creates the correct branch instruction for a given combination }
  112. { of asmcondflags and destination addressing mode }
  113. procedure a_jmp(list: taasmoutput; op: tasmop;
  114. c: tasmcondflag; crval: longint; l: tasmlabel);
  115. { returns the lowest numbered FP register in use, and the number of used FP registers
  116. for the current procedure }
  117. procedure calcFirstUsedFPR(out firstfpr : TSuperRegister; out fprcount : aint);
  118. { returns the lowest numbered GP register in use, and the number of used GP registers
  119. for the current procedure }
  120. procedure calcFirstUsedGPR(out firstgpr : TSuperRegister; out gprcount : aint);
  121. { returns true if the offset of the given reference can not be represented by a 16 bit
  122. immediate as required by some PowerPC instructions }
  123. function hasLargeOffset(const ref : TReference) : Boolean; inline;
  124. procedure a_call_name_direct(list: taasmoutput; s: string; prependDot : boolean; addNOP : boolean);
  125. end;
  126. const
  127. TOpCG2AsmOpConstLo: array[topcg] of TAsmOp = (A_NONE, A_ADDI, A_ANDI_,
  128. A_DIVWU,
  129. A_DIVW, A_MULLW, A_MULLW, A_NONE, A_NONE, A_ORI,
  130. A_SRAWI, A_SLWI, A_SRWI, A_SUBI, A_XORI);
  131. TOpCG2AsmOpConstHi: array[topcg] of TAsmOp = (A_NONE, A_ADDIS, A_ANDIS_,
  132. A_DIVWU, A_DIVW, A_MULLW, A_MULLW, A_NONE, A_NONE,
  133. A_ORIS, A_NONE, A_NONE, A_NONE, A_SUBIS, A_XORIS);
  134. TShiftOpCG2AsmOpConst32 : array[OP_SAR..OP_SHR] of TAsmOp = (A_SRAWI, A_SLWI, A_SRWI);
  135. TShiftOpCG2AsmOpConst64 : array[OP_SAR..OP_SHR] of TAsmOp = (A_SRADI, A_SLDI, A_SRDI);
  136. TOpCmp2AsmCond: array[topcmp] of TAsmCondFlag = (C_NONE, C_EQ, C_GT,
  137. C_LT, C_GE, C_LE, C_NE, C_LE, C_LT, C_GE, C_GT);
  138. implementation
  139. uses
  140. sysutils,
  141. globals, verbose, systems, cutils,
  142. symconst, symsym, fmodule,
  143. rgobj, tgobj, cpupi, procinfo, paramgr;
  144. procedure tcgppc.init_register_allocators;
  145. begin
  146. inherited init_register_allocators;
  147. rg[R_INTREGISTER] := trgcpu.create(R_INTREGISTER, R_SUBWHOLE,
  148. [RS_R3, RS_R4, RS_R5, RS_R6, RS_R7, RS_R8,
  149. RS_R9, RS_R10, RS_R11, RS_R12, RS_R31, RS_R30, RS_R29,
  150. RS_R28, RS_R27, RS_R26, RS_R25, RS_R24, RS_R23, RS_R22,
  151. RS_R21, RS_R20, RS_R19, RS_R18, RS_R17, RS_R16, RS_R15,
  152. RS_R14, RS_R13], first_int_imreg, []);
  153. rg[R_FPUREGISTER] := trgcpu.create(R_FPUREGISTER, R_SUBNONE,
  154. [RS_F0, RS_F1, RS_F2, RS_F3, RS_F4, RS_F5, RS_F6, RS_F7, RS_F8, RS_F9,
  155. RS_F10, RS_F11, RS_F12, RS_F13, RS_F31, RS_F30, RS_F29, RS_F28, RS_F27,
  156. RS_F26, RS_F25, RS_F24, RS_F23, RS_F22, RS_F21, RS_F20, RS_F19, RS_F18,
  157. RS_F17, RS_F16, RS_F15, RS_F14], first_fpu_imreg, []);
  158. {$WARNING FIX ME}
  159. rg[R_MMREGISTER] := trgcpu.create(R_MMREGISTER, R_SUBNONE,
  160. [RS_M0, RS_M1, RS_M2], first_mm_imreg, []);
  161. end;
  162. procedure tcgppc.done_register_allocators;
  163. begin
  164. rg[R_INTREGISTER].free;
  165. rg[R_FPUREGISTER].free;
  166. rg[R_MMREGISTER].free;
  167. inherited done_register_allocators;
  168. end;
  169. procedure tcgppc.a_param_const(list: taasmoutput; size: tcgsize; a: aint; const
  170. paraloc: tcgpara);
  171. var
  172. ref: treference;
  173. begin
  174. paraloc.check_simple_location;
  175. case paraloc.location^.loc of
  176. LOC_REGISTER, LOC_CREGISTER:
  177. a_load_const_reg(list, size, a, paraloc.location^.register);
  178. LOC_REFERENCE:
  179. begin
  180. reference_reset(ref);
  181. ref.base := paraloc.location^.reference.index;
  182. ref.offset := paraloc.location^.reference.offset;
  183. a_load_const_ref(list, size, a, ref);
  184. end;
  185. else
  186. internalerror(2002081101);
  187. end;
  188. end;
  189. procedure tcgppc.a_param_ref(list: taasmoutput; size: tcgsize; const r:
  190. treference; const paraloc: tcgpara);
  191. var
  192. tmpref, ref: treference;
  193. location: pcgparalocation;
  194. sizeleft: aint;
  195. begin
  196. location := paraloc.location;
  197. tmpref := r;
  198. sizeleft := paraloc.intsize;
  199. while assigned(location) do
  200. begin
  201. case location^.loc of
  202. LOC_REGISTER, LOC_CREGISTER:
  203. begin
  204. a_load_ref_reg(list, location^.size, location^.size, tmpref,
  205. location^.register);
  206. end;
  207. LOC_REFERENCE:
  208. begin
  209. reference_reset_base(ref, location^.reference.index,
  210. location^.reference.offset);
  211. g_concatcopy(list, tmpref, ref, sizeleft);
  212. if assigned(location^.next) then
  213. internalerror(2005010710);
  214. end;
  215. LOC_FPUREGISTER, LOC_CFPUREGISTER:
  216. case location^.size of
  217. OS_F32, OS_F64:
  218. a_loadfpu_ref_reg(list, location^.size, tmpref, location^.register);
  219. else
  220. internalerror(2002072801);
  221. end;
  222. LOC_VOID:
  223. { nothing to do }
  224. ;
  225. else
  226. internalerror(2002081103);
  227. end;
  228. inc(tmpref.offset, tcgsize2size[location^.size]);
  229. dec(sizeleft, tcgsize2size[location^.size]);
  230. location := location^.next;
  231. end;
  232. end;
  233. procedure tcgppc.a_paramaddr_ref(list: taasmoutput; const r: treference; const
  234. paraloc: tcgpara);
  235. var
  236. ref: treference;
  237. tmpreg: tregister;
  238. begin
  239. paraloc.check_simple_location;
  240. case paraloc.location^.loc of
  241. LOC_REGISTER, LOC_CREGISTER:
  242. a_loadaddr_ref_reg(list, r, paraloc.location^.register);
  243. LOC_REFERENCE:
  244. begin
  245. reference_reset(ref);
  246. ref.base := paraloc.location^.reference.index;
  247. ref.offset := paraloc.location^.reference.offset;
  248. tmpreg := rg[R_INTREGISTER].getregister(list, R_SUBWHOLE);
  249. a_loadaddr_ref_reg(list, r, tmpreg);
  250. a_load_reg_ref(list, OS_ADDR, OS_ADDR, tmpreg, ref);
  251. end;
  252. else
  253. internalerror(2002080701);
  254. end;
  255. end;
  256. { calling a procedure by name }
  257. procedure tcgppc.a_call_name(list: taasmoutput; const s: string);
  258. begin
  259. a_call_name_direct(list, s, true, true);
  260. end;
  261. procedure tcgppc.a_call_name_direct(list: taasmoutput; s: string; prependDot : boolean; addNOP : boolean);
  262. begin
  263. if (prependDot) then
  264. s := '.' + s;
  265. list.concat(taicpu.op_sym(A_BL, objectlibrary.newasmsymbol(s, AB_EXTERNAL,
  266. AT_FUNCTION)));
  267. if (addNOP) then
  268. list.concat(taicpu.op_none(A_NOP));
  269. {
  270. the compiler does not properly set this flag anymore in pass 1, and
  271. for now we only need it after pass 2 (I hope) (JM)
  272. if not(pi_do_call in current_procinfo.flags) then
  273. internalerror(2003060703);
  274. }
  275. include(current_procinfo.flags, pi_do_call);
  276. end;
  277. { calling a procedure by address }
  278. procedure tcgppc.a_call_reg(list: taasmoutput; reg: tregister);
  279. var
  280. tmpref: treference;
  281. begin
  282. if (not (cs_littlesize in aktglobalswitches)) then begin
  283. { load actual function entry (reg contains the reference to the function descriptor)
  284. into R0 }
  285. reference_reset_base(tmpref, reg, 0);
  286. a_load_ref_reg(list, OS_ADDR, OS_ADDR, tmpref, NR_R0);
  287. { save TOC pointer in stackframe }
  288. reference_reset_base(tmpref, NR_STACK_POINTER_REG, LA_RTOC_ELF);
  289. a_load_reg_ref(list, OS_ADDR, OS_ADDR, NR_RTOC, tmpref);
  290. { move actual function pointer to CTR register }
  291. list.concat(taicpu.op_reg(A_MTCTR, NR_R0));
  292. { load new TOC pointer from function descriptor into RTOC register }
  293. reference_reset_base(tmpref, reg, tcgsize2size[OS_ADDR]);
  294. a_load_ref_reg(list, OS_ADDR, OS_ADDR, tmpref, NR_RTOC);
  295. { load new environment pointer from function descriptor into R11 register }
  296. reference_reset_base(tmpref, reg, 2*tcgsize2size[OS_ADDR]);
  297. a_load_ref_reg(list, OS_ADDR, OS_ADDR, tmpref, NR_R11);
  298. { call function }
  299. list.concat(taicpu.op_none(A_BCTRL));
  300. end else begin
  301. { call ptrgl helper routine which expects the pointer to the function descriptor
  302. in R11 }
  303. a_load_reg_reg(list, OS_ADDR, OS_ADDR, reg, NR_R11);
  304. a_call_name_direct(list, 'ptrgl', true, false);
  305. end;
  306. { we need to load the old RTOC from stackframe because we changed it}
  307. reference_reset_base(tmpref, NR_STACK_POINTER_REG, LA_RTOC_ELF);
  308. a_load_ref_reg(list, OS_ADDR, OS_ADDR, tmpref, NR_RTOC);
  309. include(current_procinfo.flags, pi_do_call);
  310. end;
  311. {********************** load instructions ********************}
  312. procedure tcgppc.a_load_const_reg(list: taasmoutput; size: TCGSize; a: aint;
  313. reg: TRegister);
  314. { loads a 32 bit constant into the given register, using an optimal instruction sequence.
  315. This is either LIS, LI or LI+ADDIS.
  316. Returns true if during these operations the upper 32 bits were filled with 1 bits (e.g.
  317. sign extension was performed) }
  318. function load32bitconstant(list : taasmoutput; size : TCGSize; a : longint;
  319. reg : TRegister) : boolean;
  320. var
  321. is_half_signed : byte;
  322. begin
  323. { if the lower 16 bits are zero, do a single LIS }
  324. if (smallint(a) = 0) and ((a shr 16) <> 0) then begin
  325. list.concat(taicpu.op_reg_const(A_LIS, reg, smallint(hi(a))));
  326. load32bitconstant := longint(a) < 0;
  327. end else begin
  328. is_half_signed := ord(smallint(lo(a)) < 0);
  329. list.concat(taicpu.op_reg_const(A_LI, reg, smallint(a and $ffff)));
  330. if smallint(hi(a) + is_half_signed) <> 0 then begin
  331. list.concat(taicpu.op_reg_reg_const(A_ADDIS, reg, reg, smallint(hi(a) + is_half_signed)));
  332. end;
  333. load32bitconstant := (smallint(a) < 0) or (a < 0);
  334. end;
  335. end;
  336. { R0-safe version of the above (ADDIS doesn't work the same way with R0 as base), without
  337. the return value }
  338. procedure load32bitconstantR0(list : taasmoutput; size : TCGSize; a : longint;
  339. reg : TRegister);
  340. begin
  341. { only 16 bit constant? (-2^15 <= a <= +2^15-1) }
  342. if (a >= low(smallint)) and (a <= high(smallint)) then begin
  343. list.concat(taicpu.op_reg_const(A_LI, reg, smallint(a)));
  344. end else begin
  345. { check if we have to start with LI or LIS, load as 32 bit constant }
  346. if ((a and $FFFF) <> 0) then begin
  347. list.concat(taicpu.op_reg_const(A_LIS, reg, smallint(a shr 16)));
  348. list.concat(taicpu.op_reg_reg_const(A_ORI, reg, reg, word(a)));
  349. end else begin
  350. list.concat(taicpu.op_reg_const(A_LIS, reg, smallint(a shr 16)));
  351. end;
  352. end;
  353. end;
  354. var
  355. extendssign : boolean;
  356. {$IFDEF EXTDEBUG}
  357. astring : string;
  358. {$ENDIF EXTDEBUG}
  359. begin
  360. {$IFDEF EXTDEBUG}
  361. astring := 'a_load_const reg ' + inttostr(hi(a)) + ' ' + inttostr(lo(a)) + ' ' + inttostr(ord(size)) + ' ' + inttostr(tcgsize2size[size]);
  362. list.concat(tai_comment.create(strpnew(astring)));
  363. {$ENDIF EXTDEBUG}
  364. if not (size in [OS_8, OS_S8, OS_16, OS_S16, OS_32, OS_S32, OS_64, OS_S64]) then
  365. internalerror(2002090902);
  366. if (lo(a) = 0) and (hi(a) <> 0) then begin
  367. { load only upper 32 bits, and shift }
  368. load32bitconstant(list, size, hi(a), reg);
  369. list.concat(taicpu.op_reg_reg_const(A_SLDI, reg, reg, 32));
  370. end else begin
  371. { load lower 32 bits }
  372. extendssign := load32bitconstant(list, size, lo(a), reg);
  373. if (extendssign) and (hi(a) = 0) then
  374. { if upper 32 bits are zero, but loading the lower 32 bit resulted in automatic
  375. sign extension, clear those bits }
  376. a_load_reg_reg(list, OS_32, OS_64, reg, reg)
  377. else if (not
  378. ((extendssign and (longint(hi(a)) = -1)) or
  379. ((not extendssign) and (hi(a)=0)))
  380. ) then begin
  381. { only load the upper 32 bits, if the automatic sign extension is not okay,
  382. that is, _not_ if
  383. - loading the lower 32 bits resulted in -1 in the upper 32 bits, and the upper
  384. 32 bits should contain -1
  385. - loading the lower 32 bits resulted in 0 in the upper 32 bits, and the upper
  386. 32 bits should contain 0 }
  387. load32bitconstantR0(list, size, hi(a), NR_R0);
  388. { combine both registers }
  389. list.concat(taicpu.op_reg_reg_const_const(A_RLDIMI, reg, NR_R0, 32, 0));
  390. end;
  391. end;
  392. end;
  393. procedure tcgppc.a_load_reg_ref(list: taasmoutput; fromsize, tosize: TCGSize;
  394. reg: tregister; const ref: treference);
  395. const
  396. StoreInstr: array[OS_8..OS_64, boolean, boolean] of TAsmOp =
  397. { indexed? updating?}
  398. (((A_STB, A_STBU), (A_STBX, A_STBUX)),
  399. ((A_STH, A_STHU), (A_STHX, A_STHUX)),
  400. ((A_STW, A_STWU), (A_STWX, A_STWUX)),
  401. ((A_STD, A_STDU), (A_STDX, A_STDUX))
  402. );
  403. var
  404. op: TAsmOp;
  405. ref2: TReference;
  406. begin
  407. ref2 := ref;
  408. fixref(list, ref2, tosize);
  409. if tosize in [OS_S8..OS_S64] then
  410. { storing is the same for signed and unsigned values }
  411. tosize := tcgsize(ord(tosize) - (ord(OS_S8) - ord(OS_8)));
  412. op := storeinstr[tcgsize2unsigned[tosize], ref2.index <> NR_NO, false];
  413. a_load_store(list, op, reg, ref2);
  414. end;
  415. procedure tcgppc.a_load_ref_reg(list: taasmoutput; fromsize, tosize: tcgsize;
  416. const ref: treference; reg: tregister);
  417. const
  418. LoadInstr: array[OS_8..OS_S64, boolean, boolean] of TAsmOp =
  419. { indexed? updating? }
  420. (((A_LBZ, A_LBZU), (A_LBZX, A_LBZUX)),
  421. ((A_LHZ, A_LHZU), (A_LHZX, A_LHZUX)),
  422. ((A_LWZ, A_LWZU), (A_LWZX, A_LWZUX)),
  423. ((A_LD, A_LDU), (A_LDX, A_LDUX)),
  424. { 128bit stuff too }
  425. ((A_NONE, A_NONE), (A_NONE, A_NONE)),
  426. { there's no load-byte-with-sign-extend :( }
  427. ((A_LBZ, A_LBZU), (A_LBZX, A_LBZUX)),
  428. ((A_LHA, A_LHAU), (A_LHAX, A_LHAUX)),
  429. { there's no load-word-arithmetic-indexed with update, simulate it in code :( }
  430. ((A_LWA, A_LWAU), (A_LWAX, A_LWAUX)),
  431. ((A_LD, A_LDU), (A_LDX, A_LDUX))
  432. );
  433. var
  434. op: tasmop;
  435. ref2: treference;
  436. begin
  437. if not (fromsize in [OS_8, OS_S8, OS_16, OS_S16, OS_32, OS_S32, OS_64, OS_S64]) then
  438. internalerror(2002090902);
  439. ref2 := ref;
  440. fixref(list, ref2, tosize);
  441. { the caller is expected to have adjusted the reference already
  442. in this case }
  443. if (TCGSize2Size[fromsize] >= TCGSize2Size[tosize]) then
  444. fromsize := tosize;
  445. op := loadinstr[fromsize, ref2.index <> NR_NO, false];
  446. { there is no LWAU instruction, simulate using ADDI and LWA }
  447. if (op = A_LWAU) then begin
  448. list.concat(taicpu.op_reg_reg_const(A_ADDI, reg, reg, ref2.offset));
  449. ref2.offset := 0;
  450. op := A_LWA;
  451. end;
  452. a_load_store(list, op, reg, ref2);
  453. { sign extend shortint if necessary, since there is no
  454. load instruction that does that automatically (JM) }
  455. if fromsize = OS_S8 then
  456. list.concat(taicpu.op_reg_reg(A_EXTSB, reg, reg));
  457. end;
  458. procedure tcgppc.a_load_reg_reg(list: taasmoutput; fromsize, tosize: tcgsize;
  459. reg1, reg2: tregister);
  460. const
  461. movemap : array[OS_8..OS_S128, OS_8..OS_S128] of tasmop = (
  462. { to -> OS_8 OS_16 OS_32 OS_64 OS_128 OS_S8 OS_S16 OS_S32 OS_S64 OS_S128 }
  463. { from }
  464. { OS_8 } (A_MR, A_RLDICL, A_RLDICL, A_RLDICL, A_NONE, A_RLDICL, A_RLDICL, A_RLDICL, A_RLDICL, A_NOP ),
  465. { OS_16 } (A_RLDICL, A_MR, A_RLDICL, A_RLDICL, A_NONE, A_RLDICL, A_RLDICL, A_RLDICL, A_RLDICL, A_NOP ),
  466. { OS_32 } (A_RLDICL, A_RLDICL, A_MR, A_RLDICL, A_NONE, A_RLDICL, A_RLDICL, A_RLDICL, A_RLDICL, A_NOP ),
  467. { OS_64 } (A_RLDICL, A_RLDICL, A_RLDICL, A_MR, A_NONE, A_RLDICL, A_RLDICL, A_RLDICL, A_RLDICL, A_NOP ),
  468. { OS_128 } (A_NONE, A_NONE, A_NONE, A_NONE, A_NONE, A_NONE, A_NONE, A_NONE, A_NONE, A_NOP ),
  469. { OS_S8 } (A_EXTSB, A_EXTSB, A_EXTSB, A_EXTSB, A_NONE, A_MR, A_EXTSB, A_EXTSB, A_EXTSB, A_NOP ),
  470. { OS_S16 } (A_RLDICL, A_EXTSH, A_EXTSH, A_EXTSH, A_NONE, A_EXTSB, A_MR, A_EXTSH, A_EXTSH, A_NOP ),
  471. { OS_S32 } (A_RLDICL, A_RLDICL, A_EXTSW, A_EXTSW, A_NONE, A_EXTSB, A_EXTSH, A_MR, A_EXTSW, A_NOP ),
  472. { OS_S64 } (A_RLDICL, A_RLDICL, A_RLDICL, A_MR, A_NONE, A_EXTSB, A_EXTSH, A_EXTSW, A_MR, A_NOP ),
  473. { OS_S128 } (A_NONE, A_NONE, A_NONE, A_NONE, A_NONE, A_NONE, A_NONE, A_NONE, A_NONE, A_NOP )
  474. );
  475. var
  476. instr: taicpu;
  477. op : tasmop;
  478. begin
  479. op := movemap[fromsize, tosize];
  480. case op of
  481. A_MR, A_EXTSB, A_EXTSH, A_EXTSW : instr := taicpu.op_reg_reg(op, reg2, reg1);
  482. A_RLDICL : instr := taicpu.op_reg_reg_const_const(A_RLDICL, reg2, reg1, 0, (8-tcgsize2size[fromsize])*8);
  483. else
  484. internalerror(2002090901);
  485. end;
  486. list.concat(instr);
  487. rg[R_INTREGISTER].add_move_instruction(instr);
  488. end;
  489. procedure tcgppc.a_loadfpu_reg_reg(list: taasmoutput; size: tcgsize; reg1, reg2:
  490. tregister);
  491. var
  492. instr: taicpu;
  493. begin
  494. instr := taicpu.op_reg_reg(A_FMR, reg2, reg1);
  495. list.concat(instr);
  496. rg[R_FPUREGISTER].add_move_instruction(instr);
  497. end;
  498. procedure tcgppc.a_loadfpu_ref_reg(list: taasmoutput; size: tcgsize; const ref:
  499. treference; reg: tregister);
  500. const
  501. FpuLoadInstr: array[OS_F32..OS_F64, boolean, boolean] of TAsmOp =
  502. { indexed? updating?}
  503. (((A_LFS, A_LFSU), (A_LFSX, A_LFSUX)),
  504. ((A_LFD, A_LFDU), (A_LFDX, A_LFDUX)));
  505. var
  506. op: tasmop;
  507. ref2: treference;
  508. begin
  509. { several functions call this procedure with OS_32 or OS_64
  510. so this makes life easier (FK) }
  511. case size of
  512. OS_32, OS_F32:
  513. size := OS_F32;
  514. OS_64, OS_F64, OS_C64:
  515. size := OS_F64;
  516. else
  517. internalerror(200201121);
  518. end;
  519. ref2 := ref;
  520. fixref(list, ref2, size);
  521. op := fpuloadinstr[size, ref2.index <> NR_NO, false];
  522. a_load_store(list, op, reg, ref2);
  523. end;
  524. procedure tcgppc.a_loadfpu_reg_ref(list: taasmoutput; size: tcgsize; reg:
  525. tregister; const ref: treference);
  526. const
  527. FpuStoreInstr: array[OS_F32..OS_F64, boolean, boolean] of TAsmOp =
  528. { indexed? updating? }
  529. (((A_STFS, A_STFSU), (A_STFSX, A_STFSUX)),
  530. ((A_STFD, A_STFDU), (A_STFDX, A_STFDUX)));
  531. var
  532. op: tasmop;
  533. ref2: treference;
  534. begin
  535. if not (size in [OS_F32, OS_F64]) then
  536. internalerror(200201122);
  537. ref2 := ref;
  538. fixref(list, ref2, size);
  539. op := fpustoreinstr[size, ref2.index <> NR_NO, false];
  540. a_load_store(list, op, reg, ref2);
  541. end;
  542. procedure tcgppc.a_op_const_reg(list: taasmoutput; Op: TOpCG; size: TCGSize; a:
  543. aint; reg: TRegister);
  544. begin
  545. a_op_const_reg_reg(list, op, size, a, reg, reg);
  546. end;
  547. procedure tcgppc.a_op_reg_reg(list: taasmoutput; Op: TOpCG; size: TCGSize; src,
  548. dst: TRegister);
  549. begin
  550. a_op_reg_reg_reg(list, op, size, src, dst, dst);
  551. end;
  552. procedure tcgppc.a_op_const_reg_reg(list: taasmoutput; op: TOpCg;
  553. size: tcgsize; a: aint; src, dst: tregister);
  554. var
  555. l1, l2: longint;
  556. oplo, ophi: tasmop;
  557. scratchreg: tregister;
  558. useReg : boolean;
  559. shiftmask : longint;
  560. procedure do_lo_hi;
  561. begin
  562. usereg := false;
  563. if (size in [OS_64, OS_S64]) then begin
  564. { ts: use register method for 64 bit consts. Sloooooow }
  565. usereg := true;
  566. end else if (size in [OS_32, OS_S32]) then begin
  567. list.concat(taicpu.op_reg_reg_const(oplo, dst, src, word(a)));
  568. list.concat(taicpu.op_reg_reg_const(ophi, dst, dst, word(a shr 16)));
  569. end else begin
  570. list.concat(taicpu.op_reg_reg_const(oplo, dst, src, word(a)));
  571. end;
  572. end;
  573. begin
  574. if op = OP_SUB then begin
  575. a_op_const_reg_reg(list, OP_ADD, size, -a, src, dst);
  576. exit;
  577. end;
  578. ophi := TOpCG2AsmOpConstHi[op];
  579. oplo := TOpCG2AsmOpConstLo[op];
  580. { peephole optimizations for AND, OR, XOR - can't this be done at
  581. some higher level, independent of architecture? }
  582. if (op in [OP_AND, OP_OR, OP_XOR]) then begin
  583. if (a = 0) then begin
  584. if op = OP_AND then
  585. list.concat(taicpu.op_reg_const(A_LI, dst, 0))
  586. else
  587. a_load_reg_reg(list, size, size, src, dst);
  588. exit;
  589. end else if (a = -1) then begin
  590. case op of
  591. OP_OR:
  592. list.concat(taicpu.op_reg_const(A_LI, dst, -1));
  593. OP_XOR:
  594. list.concat(taicpu.op_reg_reg(A_NOT, dst, src));
  595. OP_AND:
  596. a_load_reg_reg(list, size, size, src, dst);
  597. end;
  598. exit;
  599. end;
  600. { optimization for add }
  601. end else if (op = OP_ADD) then
  602. if a = 0 then begin
  603. a_load_reg_reg(list, size, size, src, dst);
  604. exit;
  605. end else if (a >= low(smallint)) and (a <= high(smallint)) then begin
  606. list.concat(taicpu.op_reg_reg_const(A_ADDI, dst, src, smallint(a)));
  607. exit;
  608. end;
  609. { otherwise, the instructions we can generate depend on the operation }
  610. useReg := false;
  611. case op of
  612. OP_DIV, OP_IDIV:
  613. if (a = 0) then
  614. internalerror(200208103)
  615. else if (a = 1) then begin
  616. a_load_reg_reg(list, OS_INT, OS_INT, src, dst);
  617. exit
  618. end else if false {and ispowerof2(a, l1)} then begin
  619. internalerror(200208103);
  620. case op of
  621. OP_DIV: begin
  622. list.concat(taicpu.op_reg_reg_const(A_SRDI, dst, src, l1));
  623. end;
  624. OP_IDIV:
  625. begin
  626. list.concat(taicpu.op_reg_reg_const(A_SRADI, dst, src, l1));
  627. list.concat(taicpu.op_reg_reg(A_ADDZE, dst, dst));
  628. end;
  629. end;
  630. exit;
  631. end else
  632. usereg := true;
  633. OP_IMUL, OP_MUL:
  634. if (a = 0) then begin
  635. list.concat(taicpu.op_reg_const(A_LI, dst, 0));
  636. exit
  637. end else if (a = -1) then begin
  638. list.concat(taicpu.op_reg_reg(A_NEG, dst, dst));
  639. end else if (a = 1) then begin
  640. a_load_reg_reg(list, OS_INT, OS_INT, src, dst);
  641. exit
  642. end else if ispowerof2(a, l1) then
  643. list.concat(taicpu.op_reg_reg_const(A_SLDI, dst, src, l1))
  644. else if (a >= low(smallint)) and (a <= high(smallint)) then
  645. list.concat(taicpu.op_reg_reg_const(A_MULLI, dst, src,
  646. smallint(a)))
  647. else
  648. usereg := true;
  649. OP_ADD:
  650. {$todo ts:optimize}
  651. useReg := true;
  652. OP_OR:
  653. do_lo_hi;
  654. OP_AND:
  655. useReg := true;
  656. OP_XOR:
  657. do_lo_hi;
  658. OP_SHL, OP_SHR, OP_SAR:
  659. begin
  660. {$note ts: cleanup todo, fix remaining bugs}
  661. if (size in [OS_64, OS_S64]) then begin
  662. if (a and 63) <> 0 then
  663. list.concat(taicpu.op_reg_reg_const(
  664. TShiftOpCG2AsmOpConst64[Op], dst, src, a and 63))
  665. else
  666. a_load_reg_reg(list, size, size, src, dst);
  667. if (a shr 6) <> 0 then
  668. internalError(68991);
  669. end else begin
  670. if (a and 31) <> 0 then
  671. list.concat(taicpu.op_reg_reg_const(
  672. TShiftOpCG2AsmOpConst32[Op], dst, src, a and 31))
  673. else
  674. a_load_reg_reg(list, size, size, src, dst);
  675. if (a shr 5) <> 0 then
  676. internalError(68991);
  677. end;
  678. end
  679. else
  680. internalerror(200109091);
  681. end;
  682. { if all else failed, load the constant in a register and then }
  683. { perform the operation }
  684. if useReg then begin
  685. scratchreg := rg[R_INTREGISTER].getregister(list, R_SUBWHOLE);
  686. a_load_const_reg(list, size, a, scratchreg);
  687. a_op_reg_reg_reg(list, op, size, scratchreg, src, dst);
  688. end;
  689. end;
  690. procedure tcgppc.a_op_reg_reg_reg(list: taasmoutput; op: TOpCg;
  691. size: tcgsize; src1, src2, dst: tregister);
  692. const
  693. op_reg_reg_opcg2asmop32: array[TOpCG] of tasmop =
  694. (A_NONE, A_ADD, A_AND, A_DIVWU, A_DIVW, A_MULLW, A_MULLW, A_NEG, A_NOT, A_OR,
  695. A_SRAW, A_SLW, A_SRW, A_SUB, A_XOR);
  696. op_reg_reg_opcg2asmop64: array[TOpCG] of tasmop =
  697. (A_NONE, A_ADD, A_AND, A_DIVDU, A_DIVD, A_MULLD, A_MULLD, A_NEG, A_NOT, A_OR,
  698. A_SRAD, A_SLD, A_SRD, A_SUB, A_XOR);
  699. begin
  700. case op of
  701. OP_NEG, OP_NOT:
  702. begin
  703. list.concat(taicpu.op_reg_reg(op_reg_reg_opcg2asmop64[op], dst, src1));
  704. if (op = OP_NOT) and
  705. not (size in [OS_64, OS_S64]) then
  706. { zero/sign extend result again, fromsize is not important here }
  707. a_load_reg_reg(list, OS_S64, size, dst, dst)
  708. end;
  709. else
  710. {$NOTE ts:testme}
  711. if (size in [OS_64, OS_S64]) then begin
  712. list.concat(taicpu.op_reg_reg_reg(op_reg_reg_opcg2asmop64[op], dst, src2,
  713. src1));
  714. end else begin
  715. list.concat(taicpu.op_reg_reg_reg(op_reg_reg_opcg2asmop32[op], dst, src2,
  716. src1));
  717. end;
  718. end;
  719. end;
  720. {*************** compare instructructions ****************}
  721. procedure tcgppc.a_cmp_const_reg_label(list: taasmoutput; size: tcgsize; cmp_op:
  722. topcmp; a: aint; reg: tregister;
  723. l: tasmlabel);
  724. var
  725. scratch_register: TRegister;
  726. signed: boolean;
  727. begin
  728. { todo: use 32 bit compares? }
  729. signed := cmp_op in [OC_GT, OC_LT, OC_GTE, OC_LTE];
  730. { in the following case, we generate more efficient code when }
  731. { signed is true }
  732. if (cmp_op in [OC_EQ, OC_NE]) and
  733. (aword(a) > $FFFF) then
  734. signed := true;
  735. if signed then
  736. if (a >= low(smallint)) and (a <= high(smallint)) then
  737. list.concat(taicpu.op_reg_reg_const(A_CMPDI, NR_CR0, reg, a))
  738. else begin
  739. scratch_register := rg[R_INTREGISTER].getregister(list, R_SUBWHOLE);
  740. a_load_const_reg(list, OS_64, a, scratch_register);
  741. list.concat(taicpu.op_reg_reg_reg(A_CMPD, NR_CR0, reg, scratch_register));
  742. end
  743. else if (aword(a) <= $FFFF) then
  744. list.concat(taicpu.op_reg_reg_const(A_CMPLDI, NR_CR0, reg, aword(a)))
  745. else begin
  746. scratch_register := rg[R_INTREGISTER].getregister(list, R_SUBWHOLE);
  747. a_load_const_reg(list, OS_64, a, scratch_register);
  748. list.concat(taicpu.op_reg_reg_reg(A_CMPLD, NR_CR0, reg,
  749. scratch_register));
  750. end;
  751. a_jmp(list, A_BC, TOpCmp2AsmCond[cmp_op], 0, l);
  752. end;
  753. procedure tcgppc.a_cmp_reg_reg_label(list: taasmoutput; size: tcgsize; cmp_op:
  754. topcmp;
  755. reg1, reg2: tregister; l: tasmlabel);
  756. var
  757. op: tasmop;
  758. begin
  759. if cmp_op in [OC_GT, OC_LT, OC_GTE, OC_LTE] then
  760. if (size in [OS_64, OS_S64]) then
  761. op := A_CMPD
  762. else
  763. op := A_CMPW
  764. else
  765. if (size in [OS_64, OS_S64]) then
  766. op := A_CMPLD
  767. else
  768. op := A_CMPLW;
  769. list.concat(taicpu.op_reg_reg_reg(op, NR_CR0, reg2, reg1));
  770. a_jmp(list, A_BC, TOpCmp2AsmCond[cmp_op], 0, l);
  771. end;
  772. procedure tcgppc.a_jmp_cond(list: taasmoutput; cond: TOpCmp; l: tasmlabel);
  773. begin
  774. a_jmp(list, A_BC, TOpCmp2AsmCond[cond], 0, l);
  775. end;
  776. procedure tcgppc.a_jmp_name(list: taasmoutput; const s: string);
  777. var
  778. p: taicpu;
  779. begin
  780. p := taicpu.op_sym(A_B, objectlibrary.newasmsymbol(s, AB_EXTERNAL,
  781. AT_LABEL));
  782. p.is_jmp := true;
  783. list.concat(p)
  784. end;
  785. procedure tcgppc.a_jmp_always(list: taasmoutput; l: tasmlabel);
  786. begin
  787. a_jmp(list, A_B, C_None, 0, l);
  788. end;
  789. procedure tcgppc.a_jmp_flags(list: taasmoutput; const f: TResFlags; l:
  790. tasmlabel);
  791. var
  792. c: tasmcond;
  793. begin
  794. c := flags_to_cond(f);
  795. a_jmp(list, A_BC, c.cond, c.cr - RS_CR0, l);
  796. end;
  797. procedure tcgppc.g_flags2reg(list: taasmoutput; size: TCgSize; const f:
  798. TResFlags; reg: TRegister);
  799. var
  800. testbit: byte;
  801. bitvalue: boolean;
  802. begin
  803. { get the bit to extract from the conditional register + its requested value (0 or 1) }
  804. testbit := ((f.cr - RS_CR0) * 4);
  805. case f.flag of
  806. F_EQ, F_NE:
  807. begin
  808. inc(testbit, 2);
  809. bitvalue := f.flag = F_EQ;
  810. end;
  811. F_LT, F_GE:
  812. begin
  813. bitvalue := f.flag = F_LT;
  814. end;
  815. F_GT, F_LE:
  816. begin
  817. inc(testbit);
  818. bitvalue := f.flag = F_GT;
  819. end;
  820. else
  821. internalerror(200112261);
  822. end;
  823. { load the conditional register in the destination reg }
  824. list.concat(taicpu.op_reg(A_MFCR, reg));
  825. { we will move the bit that has to be tested to bit 0 by rotating left }
  826. testbit := (testbit + 1) and 31;
  827. { extract bit }
  828. list.concat(taicpu.op_reg_reg_const_const_const(
  829. A_RLWINM,reg,reg,testbit,31,31));
  830. { if we need the inverse, xor with 1 }
  831. if not bitvalue then
  832. list.concat(taicpu.op_reg_reg_const(A_XORI, reg, reg, 1));
  833. end;
  834. { *********** entry/exit code and address loading ************ }
  835. procedure tcgppc.g_save_standard_registers(list: Taasmoutput);
  836. begin
  837. { this work is done in g_proc_entry }
  838. end;
  839. procedure tcgppc.g_restore_standard_registers(list: Taasmoutput);
  840. begin
  841. { this work is done in g_proc_exit }
  842. end;
  843. procedure tcgppc.calcFirstUsedFPR(out firstfpr : TSuperRegister; out fprcount : aint);
  844. var
  845. reg : TSuperRegister;
  846. begin
  847. fprcount := 0;
  848. firstfpr := RS_F31;
  849. if not (po_assembler in current_procinfo.procdef.procoptions) then begin
  850. for reg := RS_F14 to RS_F31 do begin
  851. if reg in rg[R_FPUREGISTER].used_in_proc then begin
  852. fprcount := ord(RS_F31)-ord(reg)+1;
  853. firstfpr := reg;
  854. break;
  855. end;
  856. end;
  857. end;
  858. end;
  859. procedure tcgppc.calcFirstUsedGPR(out firstgpr : TSuperRegister; out gprcount : aint);
  860. var
  861. reg : TSuperRegister;
  862. begin
  863. gprcount := 0;
  864. firstgpr := RS_R31;
  865. if not (po_assembler in current_procinfo.procdef.procoptions) then begin
  866. for reg := RS_R14 to RS_R31 do begin
  867. if reg in rg[R_INTREGISTER].used_in_proc then begin
  868. gprcount := ord(RS_R31)-ord(reg)+1;
  869. firstgpr := reg;
  870. break;
  871. end;
  872. end;
  873. end;
  874. end;
  875. procedure tcgppc.g_proc_entry(list: taasmoutput; localsize: longint;
  876. nostackframe: boolean);
  877. { generated the entry code of a procedure/function. Note: localsize is the
  878. sum of the size necessary for local variables and the maximum possible
  879. combined size of ALL the parameters of a procedure called by the current
  880. one.
  881. This procedure may be called before, as well as after g_return_from_proc
  882. is called. NOTE registers are not to be allocated through the register
  883. allocator here, because the register colouring has already occured !! }
  884. var
  885. firstregfpu, firstreggpr: TSuperRegister;
  886. href: treference;
  887. needslinkreg: boolean;
  888. regcount : TSuperRegister;
  889. fprcount, gprcount : aint;
  890. begin
  891. { CR and LR only have to be saved in case they are modified by the current
  892. procedure, but currently this isn't checked, so save them always
  893. following is the entry code as described in "Altivec Programming
  894. Interface Manual", bar the saving of AltiVec registers }
  895. a_reg_alloc(list, NR_STACK_POINTER_REG);
  896. a_reg_alloc(list, NR_R0);
  897. calcFirstUsedFPR(firstregfpu, fprcount);
  898. calcFirstUsedGPR(firstreggpr, gprcount);
  899. { calculate real stack frame size }
  900. localsize := tppcprocinfo(current_procinfo).calc_stackframe_size(
  901. gprcount, fprcount);
  902. { determine whether we need to save the link register }
  903. needslinkreg := ((not (po_assembler in current_procinfo.procdef.procoptions)) and
  904. (pi_do_call in current_procinfo.flags));
  905. { move link register to r0 }
  906. if (needslinkreg) then begin
  907. list.concat(taicpu.op_reg(A_MFLR, NR_R0));
  908. end;
  909. { save old stack frame pointer }
  910. if (localsize > 0) then begin
  911. a_reg_alloc(list, NR_R12);
  912. list.concat(taicpu.op_reg_reg(A_MR, NR_R12, NR_STACK_POINTER_REG));
  913. end;
  914. { save registers, FPU first, then GPR }
  915. reference_reset_base(href, NR_STACK_POINTER_REG, -8);
  916. if (fprcount > 0) then begin
  917. for regcount := RS_F31 downto firstregfpu do begin
  918. a_loadfpu_reg_ref(list, OS_FLOAT, newreg(R_FPUREGISTER, regcount,
  919. R_SUBNONE), href);
  920. dec(href.offset, tcgsize2size[OS_FLOAT]);
  921. end;
  922. end;
  923. if (gprcount > 0) then begin
  924. for regcount := RS_R31 downto firstreggpr do begin
  925. a_load_reg_ref(list, OS_INT, OS_INT, newreg(R_INTREGISTER, regcount,
  926. R_SUBNONE), href);
  927. dec(href.offset, tcgsize2size[OS_INT]);
  928. end;
  929. end;
  930. { VMX registers not supported by FPC atm }
  931. { we may need to store R0 (=LR) ourselves }
  932. if (needslinkreg) then begin
  933. reference_reset_base(href, NR_STACK_POINTER_REG, LA_LR_ELF);
  934. list.concat(taicpu.op_reg_ref(A_STD, NR_R0, href));
  935. end;
  936. { create stack frame }
  937. if (not nostackframe) and (localsize > 0) then begin
  938. if (localsize <= high(smallint)) then begin
  939. reference_reset_base(href, NR_STACK_POINTER_REG, -localsize);
  940. a_load_store(list, A_STDU, NR_STACK_POINTER_REG, href);
  941. end else begin
  942. reference_reset_base(href, NR_NO, -localsize);
  943. { use R0 for loading the constant (which is definitely > 32k when entering
  944. this branch)
  945. Inlined at this position because it must not use temp registers because
  946. register allocations have already been done :( }
  947. { Code template:
  948. lis r0,ofs@highest
  949. ori r0,r0,ofs@higher
  950. sldi r0,r0,32
  951. oris r0,r0,ofs@h
  952. ori r0,r0,ofs@l
  953. }
  954. list.concat(taicpu.op_reg_const(A_LIS, NR_R0, word(href.offset shr 48)));
  955. list.concat(taicpu.op_reg_reg_const(A_ORI, NR_R0, NR_R0, word(href.offset shr 32)));
  956. list.concat(taicpu.op_reg_reg_const(A_SLDI, NR_R0, NR_R0, 32));
  957. list.concat(taicpu.op_reg_reg_const(A_ORIS, NR_R0, NR_R0, word(href.offset shr 16)));
  958. list.concat(taicpu.op_reg_reg_const(A_ORI, NR_R0, NR_R0, word(href.offset)));
  959. list.concat(taicpu.op_reg_reg_reg(A_STDUX, NR_R1, NR_R1, NR_R0));
  960. end;
  961. end;
  962. { CR register not used by FPC atm }
  963. { keep R1 allocated??? }
  964. a_reg_dealloc(list, NR_R0);
  965. end;
  966. procedure tcgppc.g_proc_exit(list: taasmoutput; parasize: longint; nostackframe:
  967. boolean);
  968. { This procedure may be called before, as well as after g_stackframe_entry }
  969. { is called. NOTE registers are not to be allocated through the register }
  970. { allocator here, because the register colouring has already occured !! }
  971. var
  972. regcount, firstregfpu, firstreggpr: TSuperRegister;
  973. href: treference;
  974. needslinkreg : boolean;
  975. localsize,
  976. fprcount, gprcount: aint;
  977. begin
  978. calcFirstUsedFPR(firstregfpu, fprcount);
  979. calcFirstUsedGPR(firstreggpr, gprcount);
  980. { determine whether we need to restore the link register }
  981. needslinkreg := ((not (po_assembler in current_procinfo.procdef.procoptions)) and
  982. (pi_do_call in current_procinfo.flags));
  983. { calculate stack frame }
  984. localsize := tppcprocinfo(current_procinfo).calc_stackframe_size(
  985. gprcount, fprcount);
  986. { CR register not supported }
  987. { restore stack pointer }
  988. if (not nostackframe) and (localsize > 0) then begin
  989. if (localsize <= high(smallint)) then begin
  990. list.concat(taicpu.op_reg_reg_const(A_ADDI, NR_STACK_POINTER_REG, NR_STACK_POINTER_REG, localsize));
  991. end else begin
  992. reference_reset_base(href, NR_NO, localsize);
  993. { use R0 for loading the constant (which is definitely > 32k when entering
  994. this branch)
  995. Inlined because it must not use temp registers because register allocations
  996. have already been done :( }
  997. { Code template:
  998. lis r0,ofs@highest
  999. ori r0,ofs@higher
  1000. sldi r0,r0,32
  1001. oris r0,r0,ofs@h
  1002. ori r0,r0,ofs@l
  1003. }
  1004. list.concat(taicpu.op_reg_const(A_LIS, NR_R0, word(href.offset shr 48)));
  1005. list.concat(taicpu.op_reg_reg_const(A_ORI, NR_R0, NR_R0, word(href.offset shr 32)));
  1006. list.concat(taicpu.op_reg_reg_const(A_SLDI, NR_R0, NR_R0, 32));
  1007. list.concat(taicpu.op_reg_reg_const(A_ORIS, NR_R0, NR_R0, word(href.offset shr 16)));
  1008. list.concat(taicpu.op_reg_reg_const(A_ORI, NR_R0, NR_R0, word(href.offset)));
  1009. list.concat(taicpu.op_reg_reg_reg(A_ADD, NR_R1, NR_R1, NR_R0));
  1010. end;
  1011. end;
  1012. { load registers, FPR first, then GPR }
  1013. {$note ts:todo change order of loading}
  1014. reference_reset_base(href, NR_STACK_POINTER_REG, -tcgsize2size[OS_FLOAT]);
  1015. if (fprcount > 0) then begin
  1016. for regcount := RS_F31 downto firstregfpu do begin
  1017. a_loadfpu_ref_reg(list, OS_FLOAT, href, newreg(R_FPUREGISTER, regcount,
  1018. R_SUBNONE));
  1019. dec(href.offset, tcgsize2size[OS_FLOAT]);
  1020. end;
  1021. end;
  1022. if (gprcount > 0) then begin
  1023. for regcount := RS_R31 downto firstreggpr do begin
  1024. a_load_ref_reg(list, OS_INT, OS_INT, href, newreg(R_INTREGISTER, regcount,
  1025. R_SUBNONE));
  1026. dec(href.offset, tcgsize2size[OS_INT]);
  1027. end;
  1028. end;
  1029. { VMX not supported... }
  1030. { restore LR (if needed) }
  1031. if (needslinkreg) then begin
  1032. reference_reset_base(href, NR_STACK_POINTER_REG, LA_LR_ELF);
  1033. list.concat(taicpu.op_reg_ref(A_LD, NR_R0, href));
  1034. list.concat(taicpu.op_reg(A_MTLR, NR_R0));
  1035. end;
  1036. { generate return instruction }
  1037. list.concat(taicpu.op_none(A_BLR));
  1038. end;
  1039. procedure tcgppc.a_loadaddr_ref_reg(list: taasmoutput; const ref: treference; r:
  1040. tregister);
  1041. var
  1042. ref2, tmpref: treference;
  1043. { register used to construct address }
  1044. tempreg : TRegister;
  1045. begin
  1046. ref2 := ref;
  1047. fixref(list, ref2, OS_64);
  1048. { load a symbol }
  1049. if assigned(ref2.symbol) or (hasLargeOffset(ref2)) then begin
  1050. { add the symbol's value to the base of the reference, and if the }
  1051. { reference doesn't have a base, create one }
  1052. reference_reset(tmpref);
  1053. tmpref.offset := ref2.offset;
  1054. tmpref.symbol := ref2.symbol;
  1055. tmpref.relsymbol := ref2.relsymbol;
  1056. { load 64 bit reference into r. If the reference already has a base register,
  1057. first load the 64 bit value into a temp register, then add it to the result
  1058. register rD }
  1059. if (ref2.base <> NR_NO) then begin
  1060. { already have a base register, so allocate a new one }
  1061. tempreg := rg[R_INTREGISTER].getregister(list, R_SUBWHOLE);
  1062. end else begin
  1063. tempreg := r;
  1064. end;
  1065. { code for loading a reference from a symbol into a register rD }
  1066. (*
  1067. lis rX,SYM@highest
  1068. ori rX,SYM@higher
  1069. sldi rX,rX,32
  1070. oris rX,rX,SYM@h
  1071. ori rX,rX,SYM@l
  1072. *)
  1073. tmpref.refaddr := addr_highest;
  1074. list.concat(taicpu.op_reg_ref(A_LIS, tempreg, tmpref));
  1075. tmpref.refaddr := addr_higher;
  1076. list.concat(taicpu.op_reg_reg_ref(A_ORI, tempreg, tempreg, tmpref));
  1077. list.concat(taicpu.op_reg_reg_const(A_SLDI, tempreg, tempreg, 32));
  1078. tmpref.refaddr := addr_high;
  1079. list.concat(taicpu.op_reg_reg_ref(A_ORIS, tempreg, tempreg, tmpref));
  1080. tmpref.refaddr := addr_low;
  1081. list.concat(taicpu.op_reg_reg_ref(A_ORI, tempreg, tempreg, tmpref));
  1082. { if there's already a base register, add the temp register contents to
  1083. the base register }
  1084. if (ref2.base <> NR_NO) then begin
  1085. list.concat(taicpu.op_reg_reg_reg(A_ADD, r, tempreg, ref2.base));
  1086. end;
  1087. end else if ref2.offset <> 0 then begin
  1088. { no symbol, but offset <> 0 }
  1089. if ref2.base <> NR_NO then begin
  1090. a_op_const_reg_reg(list, OP_ADD, OS_64, ref2.offset, ref2.base, r)
  1091. { FixRef makes sure that "(ref.index <> R_NO) and (ref.offset <> 0)" never
  1092. occurs, so now only ref.offset has to be loaded }
  1093. end else begin
  1094. a_load_const_reg(list, OS_64, ref2.offset, r)
  1095. end;
  1096. end else if ref.index <> NR_NO then
  1097. list.concat(taicpu.op_reg_reg_reg(A_ADD, r, ref2.base, ref2.index))
  1098. else if (ref2.base <> NR_NO) and
  1099. (r <> ref2.base) then
  1100. a_load_reg_reg(list, OS_ADDR, OS_ADDR, ref2.base, r)
  1101. else begin
  1102. list.concat(taicpu.op_reg_const(A_LI, r, 0));
  1103. end;
  1104. end;
  1105. { ************* concatcopy ************ }
  1106. const
  1107. maxmoveunit = 8;
  1108. procedure tcgppc.g_concatcopy(list: taasmoutput; const source, dest: treference;
  1109. len: aint);
  1110. var
  1111. countreg, tempreg: TRegister;
  1112. src, dst: TReference;
  1113. lab: tasmlabel;
  1114. count, count2: longint;
  1115. size: tcgsize;
  1116. begin
  1117. {$IFDEF extdebug}
  1118. if len > high(aint) then
  1119. internalerror(2002072704);
  1120. {$ENDIF extdebug}
  1121. { make sure short loads are handled as optimally as possible }
  1122. if (len <= maxmoveunit) and
  1123. (byte(len) in [1, 2, 4, 8]) then
  1124. begin
  1125. if len < 8 then
  1126. begin
  1127. size := int_cgsize(len);
  1128. a_load_ref_ref(list, size, size, source, dest);
  1129. end
  1130. else
  1131. begin
  1132. a_reg_alloc(list, NR_F0);
  1133. a_loadfpu_ref_reg(list, OS_F64, source, NR_F0);
  1134. a_loadfpu_reg_ref(list, OS_F64, NR_F0, dest);
  1135. a_reg_dealloc(list, NR_F0);
  1136. end;
  1137. exit;
  1138. end;
  1139. count := len div maxmoveunit;
  1140. reference_reset(src);
  1141. reference_reset(dst);
  1142. { load the address of source into src.base }
  1143. if (count > 4) or
  1144. not issimpleref(source) or
  1145. ((source.index <> NR_NO) and
  1146. ((source.offset + len) > high(smallint))) then begin
  1147. src.base := rg[R_INTREGISTER].getregister(list, R_SUBWHOLE);
  1148. a_loadaddr_ref_reg(list, source, src.base);
  1149. end else begin
  1150. src := source;
  1151. end;
  1152. { load the address of dest into dst.base }
  1153. if (count > 4) or
  1154. not issimpleref(dest) or
  1155. ((dest.index <> NR_NO) and
  1156. ((dest.offset + len) > high(smallint))) then begin
  1157. dst.base := rg[R_INTREGISTER].getregister(list, R_SUBWHOLE);
  1158. a_loadaddr_ref_reg(list, dest, dst.base);
  1159. end else begin
  1160. dst := dest;
  1161. end;
  1162. { generate a loop }
  1163. if count > 4 then begin
  1164. { the offsets are zero after the a_loadaddress_ref_reg and just
  1165. have to be set to 8. I put an Inc there so debugging may be
  1166. easier (should offset be different from zero here, it will be
  1167. easy to notice in the generated assembler }
  1168. inc(dst.offset, 8);
  1169. inc(src.offset, 8);
  1170. list.concat(taicpu.op_reg_reg_const(A_SUBI, src.base, src.base, 8));
  1171. list.concat(taicpu.op_reg_reg_const(A_SUBI, dst.base, dst.base, 8));
  1172. countreg := rg[R_INTREGISTER].getregister(list, R_SUBWHOLE);
  1173. a_load_const_reg(list, OS_32, count, countreg);
  1174. { explicitely allocate R_0 since it can be used safely here
  1175. (for holding date that's being copied) }
  1176. a_reg_alloc(list, NR_F0);
  1177. objectlibrary.getjumplabel(lab);
  1178. a_label(list, lab);
  1179. list.concat(taicpu.op_reg_reg_const(A_SUBIC_, countreg, countreg, 1));
  1180. list.concat(taicpu.op_reg_ref(A_LFDU, NR_F0, src));
  1181. list.concat(taicpu.op_reg_ref(A_STFDU, NR_F0, dst));
  1182. a_jmp(list, A_BC, C_NE, 0, lab);
  1183. a_reg_dealloc(list, NR_F0);
  1184. len := len mod 8;
  1185. end;
  1186. count := len div 8;
  1187. { unrolled loop }
  1188. if count > 0 then begin
  1189. a_reg_alloc(list, NR_F0);
  1190. for count2 := 1 to count do begin
  1191. a_loadfpu_ref_reg(list, OS_F64, src, NR_F0);
  1192. a_loadfpu_reg_ref(list, OS_F64, NR_F0, dst);
  1193. inc(src.offset, 8);
  1194. inc(dst.offset, 8);
  1195. end;
  1196. a_reg_dealloc(list, NR_F0);
  1197. len := len mod 8;
  1198. end;
  1199. if (len and 4) <> 0 then begin
  1200. a_reg_alloc(list, NR_R0);
  1201. a_load_ref_reg(list, OS_32, OS_32, src, NR_R0);
  1202. a_load_reg_ref(list, OS_32, OS_32, NR_R0, dst);
  1203. inc(src.offset, 4);
  1204. inc(dst.offset, 4);
  1205. a_reg_dealloc(list, NR_R0);
  1206. end;
  1207. { copy the leftovers }
  1208. if (len and 2) <> 0 then begin
  1209. a_reg_alloc(list, NR_R0);
  1210. a_load_ref_reg(list, OS_16, OS_16, src, NR_R0);
  1211. a_load_reg_ref(list, OS_16, OS_16, NR_R0, dst);
  1212. inc(src.offset, 2);
  1213. inc(dst.offset, 2);
  1214. a_reg_dealloc(list, NR_R0);
  1215. end;
  1216. if (len and 1) <> 0 then begin
  1217. a_reg_alloc(list, NR_R0);
  1218. a_load_ref_reg(list, OS_8, OS_8, src, NR_R0);
  1219. a_load_reg_ref(list, OS_8, OS_8, NR_R0, dst);
  1220. a_reg_dealloc(list, NR_R0);
  1221. end;
  1222. end;
  1223. procedure tcgppc.g_overflowcheck(list: taasmoutput; const l: tlocation; def:
  1224. tdef);
  1225. var
  1226. hl: tasmlabel;
  1227. flags : TResFlags;
  1228. begin
  1229. if not (cs_check_overflow in aktlocalswitches) then
  1230. exit;
  1231. objectlibrary.getjumplabel(hl);
  1232. if not ((def.deftype = pointerdef) or
  1233. ((def.deftype = orddef) and
  1234. (torddef(def).typ in [u64bit, u16bit, u32bit, u8bit, uchar,
  1235. bool8bit, bool16bit, bool32bit]))) then
  1236. begin
  1237. { ... instructions setting overflow flag ...
  1238. mfxerf R0
  1239. mtcrf 128, R0
  1240. ble cr0, label }
  1241. list.concat(taicpu.op_reg(A_MFXER, NR_R0));
  1242. list.concat(taicpu.op_const_reg(A_MTCRF, 128, NR_R0));
  1243. flags.cr := RS_CR0;
  1244. flags.flag := F_LE;
  1245. a_jmp_flags(list, flags, hl);
  1246. end else
  1247. a_jmp_cond(list, OC_AE, hl);
  1248. a_call_name(list, 'FPC_OVERFLOW');
  1249. a_label(list, hl);
  1250. end;
  1251. procedure tcgppc.g_intf_wrapper(list: TAAsmoutput; procdef: tprocdef; const
  1252. labelname: string; ioffset: longint);
  1253. procedure loadvmttor11;
  1254. var
  1255. href: treference;
  1256. begin
  1257. reference_reset_base(href, NR_R3, 0);
  1258. cg.a_load_ref_reg(list, OS_ADDR, OS_ADDR, href, NR_R11);
  1259. end;
  1260. procedure op_onr11methodaddr;
  1261. var
  1262. href: treference;
  1263. begin
  1264. if (procdef.extnumber = $FFFF) then
  1265. Internalerror(200006139);
  1266. { call/jmp vmtoffs(%eax) ; method offs }
  1267. reference_reset_base(href, NR_R11,
  1268. procdef._class.vmtmethodoffset(procdef.extnumber));
  1269. if not (hasLargeOffset(href)) then begin
  1270. list.concat(taicpu.op_reg_reg_const(A_ADDIS, NR_R11, NR_R11,
  1271. smallint((href.offset shr 16) + ord(smallint(href.offset and $FFFF) <
  1272. 0))));
  1273. href.offset := smallint(href.offset and $FFFF);
  1274. end else
  1275. { add support for offsets > 16 bit }
  1276. internalerror(200510201);
  1277. list.concat(taicpu.op_reg_ref(A_LD, NR_R11, href));
  1278. { the loaded reference is a function descriptor reference, so deref again
  1279. (at ofs 0 there's the real pointer) }
  1280. {$warning ts:TODO: update GOT reference}
  1281. reference_reset_base(href, NR_R11, 0);
  1282. list.concat(taicpu.op_reg_ref(A_LD, NR_R11, href));
  1283. list.concat(taicpu.op_reg(A_MTCTR, NR_R11));
  1284. list.concat(taicpu.op_none(A_BCTR));
  1285. { NOP needed for the linker...? }
  1286. list.concat(taicpu.op_none(A_NOP));
  1287. end;
  1288. var
  1289. make_global: boolean;
  1290. begin
  1291. if (not (procdef.proctypeoption in [potype_function, potype_procedure])) then
  1292. Internalerror(200006137);
  1293. if not assigned(procdef._class) or
  1294. (procdef.procoptions * [po_classmethod, po_staticmethod,
  1295. po_methodpointer, po_interrupt, po_iocheck] <> []) then
  1296. Internalerror(200006138);
  1297. if procdef.owner.symtabletype <> objectsymtable then
  1298. Internalerror(200109191);
  1299. make_global := false;
  1300. if (not current_module.is_unit) or
  1301. (cs_create_smart in aktmoduleswitches) or
  1302. (procdef.owner.defowner.owner.symtabletype = globalsymtable) then
  1303. make_global := true;
  1304. if make_global then
  1305. List.concat(Tai_symbol.Createname_global(labelname, AT_FUNCTION, 0))
  1306. else
  1307. List.concat(Tai_symbol.Createname(labelname, AT_FUNCTION, 0));
  1308. { set param1 interface to self }
  1309. g_adjust_self_value(list, procdef, ioffset);
  1310. if po_virtualmethod in procdef.procoptions then begin
  1311. loadvmttor11;
  1312. op_onr11methodaddr;
  1313. end else
  1314. {$note ts:todo add GOT change?? - think not needed :) }
  1315. list.concat(taicpu.op_sym(A_B,
  1316. objectlibrary.newasmsymbol('.' + procdef.mangledname, AB_EXTERNAL,
  1317. AT_FUNCTION)));
  1318. List.concat(Tai_symbol_end.Createname(labelname));
  1319. end;
  1320. {***************** This is private property, keep out! :) *****************}
  1321. function tcgppc.issimpleref(const ref: treference): boolean;
  1322. begin
  1323. if (ref.base = NR_NO) and
  1324. (ref.index <> NR_NO) then
  1325. internalerror(200208101);
  1326. result :=
  1327. not (assigned(ref.symbol)) and
  1328. (((ref.index = NR_NO) and
  1329. (ref.offset >= low(smallint)) and
  1330. (ref.offset <= high(smallint))) or
  1331. ((ref.index <> NR_NO) and
  1332. (ref.offset = 0)));
  1333. end;
  1334. function tcgppc.fixref(list: taasmoutput; var ref: treference; const size : TCgsize): boolean;
  1335. var
  1336. tmpreg: tregister;
  1337. needsAlign : boolean;
  1338. begin
  1339. result := false;
  1340. needsAlign := size in [OS_S32, OS_64, OS_S64];
  1341. if (ref.base = NR_NO) then begin
  1342. ref.base := ref.index;
  1343. ref.index := NR_NO;
  1344. end;
  1345. if (ref.base <> NR_NO) and (ref.index <> NR_NO) and
  1346. ((ref.offset <> 0) or assigned(ref.symbol)) then begin
  1347. result := true;
  1348. tmpreg := rg[R_INTREGISTER].getregister(list, R_SUBWHOLE);
  1349. a_op_reg_reg_reg(list, OP_ADD, size, ref.base, ref.index, tmpreg);
  1350. ref.index := NR_NO;
  1351. ref.base := tmpreg;
  1352. end;
  1353. end;
  1354. procedure tcgppc.a_load_store(list: taasmoutput; op: tasmop; reg: tregister;
  1355. ref: treference);
  1356. var
  1357. tmpreg, tmpreg2: tregister;
  1358. tmpref: treference;
  1359. largeOffset: Boolean;
  1360. begin
  1361. { at this point there must not be a combination of values in the ref treference
  1362. which is not possible to directly map to instructions of the PowerPC architecture }
  1363. if (ref.index <> NR_NO) and ((ref.offset <> 0) or (assigned(ref.symbol))) then
  1364. internalerror(200310131);
  1365. { for some instructions we need to check that the offset is divisible by at
  1366. least four. If not, add the bytes which are "off" to the base register and
  1367. adjust the offset accordingly }
  1368. case op of
  1369. A_LD, A_LDU, A_STD, A_STDU, A_LWA, A_LWAU :
  1370. if ((ref.offset mod 4) <> 0) then begin
  1371. tmpreg := rg[R_INTREGISTER].getregister(list, R_SUBWHOLE);
  1372. if (ref.base <> NR_NO) then begin
  1373. a_op_const_reg_reg(list, OP_ADD, OS_ADDR, ref.offset mod 4, ref.base, tmpreg);
  1374. ref.base := tmpreg;
  1375. end else begin
  1376. list.concat(taicpu.op_reg_const(A_LI, tmpreg, ref.offset mod 4));
  1377. ref.base := tmpreg;
  1378. end;
  1379. ref.offset := (ref.offset div 4) * 4;
  1380. end;
  1381. end;
  1382. { if we have to load/store from a symbol or large addresses, use a temporary register
  1383. containing the address }
  1384. if assigned(ref.symbol) or (hasLargeOffset(ref)) then begin
  1385. tmpreg := rg[R_INTREGISTER].getregister(list, R_SUBWHOLE);
  1386. if (hasLargeOffset(ref) and (ref.base = NR_NO)) then begin
  1387. ref.base := rg[R_INTREGISTER].getregister(list, R_SUBWHOLE);
  1388. a_load_const_reg(list, OS_ADDR, ref.offset, ref.base);
  1389. ref.offset := 0;
  1390. end;
  1391. reference_reset(tmpref);
  1392. tmpref.symbol := ref.symbol;
  1393. tmpref.relsymbol := ref.relsymbol;
  1394. tmpref.offset := ref.offset;
  1395. if (ref.base <> NR_NO) then begin
  1396. { As long as the TOC isn't working we try to achieve highest speed (in this
  1397. case by allowing instructions execute in parallel) as possible at the cost
  1398. of using another temporary register. So the code template when there is
  1399. a base register and an offset is the following:
  1400. lis rT1, SYM+offs@highest
  1401. ori rT1, rT1, SYM+offs@higher
  1402. lis rT2, SYM+offs@hi
  1403. ori rT2, SYM+offs@lo
  1404. rldimi rT2, rT1, 32
  1405. <op>X reg, base, rT2
  1406. }
  1407. tmpreg2 := rg[R_INTREGISTER].getregister(list, R_SUBWHOLE);
  1408. tmpref.refaddr := addr_highest;
  1409. list.concat(taicpu.op_reg_ref(A_LIS, tmpreg, tmpref));
  1410. tmpref.refaddr := addr_higher;
  1411. list.concat(taicpu.op_reg_reg_ref(A_ORI, tmpreg, tmpreg, tmpref));
  1412. tmpref.refaddr := addr_high;
  1413. list.concat(taicpu.op_reg_ref(A_LIS, tmpreg2, tmpref));
  1414. tmpref.refaddr := addr_low;
  1415. list.concat(taicpu.op_reg_reg_ref(A_ORI, tmpreg2, tmpreg2, tmpref));
  1416. list.concat(taicpu.op_reg_reg_const_const(A_RLDIMI, tmpreg2, tmpreg, 32, 0));
  1417. reference_reset(tmpref);
  1418. tmpref.base := ref.base;
  1419. tmpref.index := tmpreg2;
  1420. case op of
  1421. { the code generator doesn't generate update instructions anyway }
  1422. A_LBZ : op := A_LBZX;
  1423. A_LHZ : op := A_LHZX;
  1424. A_LWZ : op := A_LWZX;
  1425. A_LD : op := A_LDX;
  1426. A_LHA : op := A_LHAX;
  1427. A_LWA : op := A_LWAX;
  1428. A_LFS : op := A_LFSX;
  1429. A_LFD : op := A_LFDX;
  1430. A_STB : op := A_STBX;
  1431. A_STH : op := A_STHX;
  1432. A_STW : op := A_STWX;
  1433. A_STD : op := A_STDX;
  1434. A_STFS : op := A_STFSX;
  1435. A_STFD : op := A_STFDX;
  1436. else
  1437. { unknown load/store opcode }
  1438. internalerror(2005101302);
  1439. end;
  1440. list.concat(taicpu.op_reg_ref(op, reg, tmpref));
  1441. end else begin
  1442. { when accessing value from a reference without a base register, use the
  1443. following code template:
  1444. lis rT,SYM+offs@highesta
  1445. ori rT,SYM+offs@highera
  1446. sldi rT,rT,32
  1447. oris rT,rT,SYM+offs@ha
  1448. ld rD,SYM+offs@l(rT)
  1449. }
  1450. tmpref.refaddr := addr_highesta;
  1451. list.concat(taicpu.op_reg_ref(A_LIS, tmpreg, tmpref));
  1452. tmpref.refaddr := addr_highera;
  1453. list.concat(taicpu.op_reg_reg_ref(A_ORI, tmpreg, tmpreg, tmpref));
  1454. list.concat(taicpu.op_reg_reg_const(A_SLDI, tmpreg, tmpreg, 32));
  1455. tmpref.refaddr := addr_higha;
  1456. list.concat(taicpu.op_reg_reg_ref(A_ORIS, tmpreg, tmpreg, tmpref));
  1457. tmpref.base := tmpreg;
  1458. tmpref.refaddr := addr_low;
  1459. list.concat(taicpu.op_reg_ref(op, reg, tmpref));
  1460. end;
  1461. end else begin
  1462. list.concat(taicpu.op_reg_ref(op, reg, ref));
  1463. end;
  1464. end;
  1465. procedure tcgppc.a_jmp(list: taasmoutput; op: tasmop; c: tasmcondflag;
  1466. crval: longint; l: tasmlabel);
  1467. var
  1468. p: taicpu;
  1469. begin
  1470. p := taicpu.op_sym(op, objectlibrary.newasmsymbol(l.name, AB_EXTERNAL,
  1471. AT_LABEL));
  1472. if op <> A_B then
  1473. create_cond_norm(c, crval, p.condition);
  1474. p.is_jmp := true;
  1475. list.concat(p)
  1476. end;
  1477. function tcgppc.hasLargeOffset(const ref : TReference) : Boolean;
  1478. begin
  1479. { this rather strange calculation is required because offsets of TReferences are unsigned }
  1480. result := aword(ref.offset-low(smallint)) > high(smallint)-low(smallint);
  1481. end;
  1482. begin
  1483. cg := tcgppc.create;
  1484. end.