cgppc.pas 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220
  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,aasmdef,aasmcpu,aasmtai,aasmdata,
  24. cpubase,cpuinfo,cgutils,rgcpu,
  25. parabase;
  26. type
  27. tcgppcgen = class(tcg)
  28. procedure a_loadaddr_ref_cgpara(list : TAsmList;const r : treference;const paraloc : tcgpara); override;
  29. procedure a_bit_scan_reg_reg(list: TAsmList; reverse,not_zero: boolean; srcsize, dstsize: tcgsize; src, dst: TRegister); override;
  30. procedure a_call_reg(list : TAsmList;reg: tregister); override;
  31. { stores the contents of register reg to the memory location described by
  32. ref }
  33. procedure a_load_reg_ref(list: TAsmList; fromsize, tosize: TCGSize;
  34. reg: tregister; const ref: treference); override;
  35. procedure a_loadaddr_ref_reg(list : TAsmList;const ref : treference;r : tregister);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_flags(list: TAsmList; const f: TResFlags; l: tasmlabel); override;
  45. procedure a_jmp_cond(list : TAsmList;cond : TOpCmp;l: tasmlabel);
  46. procedure g_maybe_got_init(list: TAsmList); override;
  47. procedure get_aix_toc_sym(list: TAsmList; const symname: string; const flags: tindsymflags; out ref: treference; force_direct_toc: boolean);
  48. procedure g_load_check_simple(list: TAsmList; const ref: treference; size: aint);
  49. procedure g_flags2reg(list: TAsmList; size: TCgSize; const f: TResFlags; reg: TRegister); override;
  50. { returns true if the offset of the given reference can not be }
  51. { represented by a 16 bit immediate as required by some PowerPC }
  52. { instructions }
  53. function hasLargeOffset(const ref : TReference) : Boolean; inline;
  54. function get_darwin_call_stub(const s: string; weak: boolean): tasmsymbol;
  55. protected
  56. function g_indirect_sym_load(list:TAsmList;const symname: string; const flags: tindsymflags): tregister; override;
  57. { Make sure ref is a valid reference for the PowerPC and sets the }
  58. { base to the value of the index if (base = R_NO). }
  59. { Returns true if the reference contained a base, index and an }
  60. { offset or symbol, in which case the base will have been changed }
  61. { to a tempreg (which has to be freed by the caller) containing }
  62. { the sum of part of the original reference }
  63. function fixref(list: TAsmList; var ref: treference): boolean;
  64. { contains the common code of a_load_reg_ref and a_load_ref_reg }
  65. procedure a_load_store(list:TAsmList;op: tasmop;reg:tregister;ref: treference);virtual;
  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);
  70. function get_rtoc_offset: longint;
  71. function save_lr_in_prologue: boolean;
  72. function load_got_symbol(list : TAsmList; const symbol : string; const flags: tindsymflags) : tregister;
  73. end;
  74. TPPCAsmData = class(TAsmDataDef)
  75. private
  76. { number of entries in the TOC }
  77. fdirecttocentries,
  78. { number of fake TOC subsections we have created }
  79. ftocsections,
  80. { number of fake TOC entries in the current TOC subsection }
  81. fcurrenttocentries: longint;
  82. public
  83. procedure GetNextSmallTocEntry(out tocnr, entrynr: longint);
  84. property DirectTOCEntries: longint read fdirecttocentries write fdirecttocentries;
  85. end;
  86. TTOCAsmSymbol = class(TAsmSymbol)
  87. private
  88. { we split the toc into several sections of 32KB each, this number
  89. indicates which subsection this symbol is defined in }
  90. ftocsecnr: longint;
  91. public
  92. property TocSecNr: longint read ftocsecnr;
  93. end;
  94. const
  95. TOpCmp2AsmCond: Array[topcmp] of TAsmCondFlag = (C_NONE,C_EQ,C_GT,
  96. C_LT,C_GE,C_LE,C_NE,C_LE,C_LT,C_GE,C_GT);
  97. TocSecBaseName = 'toc_table';
  98. {$ifdef extdebug}
  99. function ref2string(const ref : treference) : string;
  100. function cgop2string(const op : TOpCg) : String;
  101. {$endif extdebug}
  102. implementation
  103. uses
  104. {$ifdef extdebug}sysutils,{$endif}
  105. globals,verbose,systems,cutils,
  106. symconst,symsym,symtable,fmodule,
  107. rgobj,tgobj,cpupi,procinfo,paramgr;
  108. { We know that macos_direct_globals is a const boolean
  109. but we don't care about this warning }
  110. {$NOTE Is macos_direct_globals still useful?}
  111. {$WARN 6018 OFF}
  112. {$ifdef extdebug}
  113. function ref2string(const ref : treference) : string;
  114. begin
  115. result := 'base : ' + inttostr(ord(ref.base)) + ' index : ' + inttostr(ord(ref.index)) + ' refaddr : ' + inttostr(ord(ref.refaddr)) + ' offset : ' + inttostr(ref.offset) + ' symbol : ';
  116. if (assigned(ref.symbol)) then
  117. result := result + ref.symbol.name;
  118. end;
  119. function cgop2string(const op : TOpCg) : String;
  120. const
  121. opcg_strings : array[TOpCg] of string[6] = (
  122. 'None', 'Move', 'Add', 'And', 'Div', 'IDiv', 'IMul', 'Mul',
  123. 'Neg', 'Not', 'Or', 'Sar', 'Shl', 'Shr', 'Sub', 'Xor', 'Rol', 'Ror'
  124. );
  125. begin
  126. result := opcg_strings[op];
  127. end;
  128. {$endif extdebug}
  129. function tcgppcgen.hasLargeOffset(const ref : TReference) : Boolean;
  130. begin
  131. result := aword(ref.offset-low(smallint)) > high(smallint)-low(smallint);
  132. end;
  133. function tcgppcgen.save_lr_in_prologue: boolean;
  134. begin
  135. result:=
  136. (not (po_assembler in current_procinfo.procdef.procoptions) and
  137. ((pi_do_call in current_procinfo.flags) or
  138. (cs_profile in init_settings.moduleswitches))) or
  139. ([cs_lineinfo,cs_debuginfo] * current_settings.moduleswitches <> []);
  140. end;
  141. procedure tcgppcgen.a_loadaddr_ref_cgpara(list : TAsmList;const r : treference;const paraloc : tcgpara);
  142. var
  143. ref: treference;
  144. tmpreg: tregister;
  145. begin
  146. paraloc.check_simple_location;
  147. paramanager.allocparaloc(list,paraloc.location);
  148. case paraloc.location^.loc of
  149. LOC_REGISTER,LOC_CREGISTER:
  150. a_loadaddr_ref_reg(list,r,paraloc.location^.register);
  151. LOC_REFERENCE:
  152. begin
  153. reference_reset(ref,paraloc.alignment,[]);
  154. ref.base := paraloc.location^.reference.index;
  155. ref.offset := paraloc.location^.reference.offset;
  156. tmpreg := rg[R_INTREGISTER].getregister(list,R_SUBWHOLE);
  157. a_loadaddr_ref_reg(list,r,tmpreg);
  158. a_load_reg_ref(list,OS_ADDR,OS_ADDR,tmpreg,ref);
  159. end;
  160. else
  161. internalerror(2002080701);
  162. end;
  163. end;
  164. procedure tcgppcgen.a_bit_scan_reg_reg(list: TAsmList; reverse,not_zero: boolean; srcsize, dstsize: tcgsize; src, dst: TRegister);
  165. var
  166. tmpreg: tregister;
  167. cntlzop: tasmop;
  168. bitsizem1: longint;
  169. begin
  170. { we only have a cntlz(w|d) instruction, which corresponds to bsr(x)
  171. (well, regsize_in_bits - bsr(x), as x86 numbers bits in reverse).
  172. Fortunately, bsf(x) can be calculated easily based on that, see
  173. "Figure 5-13. Number of Powers of 2 Code Sequence" in the PowerPC
  174. Compiler Writer's Guide
  175. }
  176. if srcsize in [OS_64,OS_S64] then
  177. begin
  178. {$ifdef powerpc64}
  179. cntlzop:=A_CNTLZD;
  180. {$else}
  181. internalerror(2015022601);
  182. {$endif}
  183. bitsizem1:=63;
  184. end
  185. else
  186. begin
  187. cntlzop:=A_CNTLZW;
  188. bitsizem1:=31;
  189. end;
  190. if not reverse then
  191. begin
  192. { cntlzw(src and -src) }
  193. tmpreg:=getintregister(list,srcsize);
  194. { don't use a_op_reg_reg, as this will adjust the result
  195. after the neg in case of a non-32/64 bit operation, which
  196. is not necessary since we're only using it as an
  197. AND-mask }
  198. list.concat(taicpu.op_reg_reg(A_NEG,tmpreg,src));
  199. a_op_reg_reg(list,OP_AND,srcsize,src,tmpreg);
  200. end
  201. else
  202. tmpreg:=src;
  203. { count leading zeroes }
  204. list.concat(taicpu.op_reg_reg(cntlzop,dst,tmpreg));
  205. { (bitsize-1) - cntlz (which is 32/64 in case src was 0) }
  206. list.concat(taicpu.op_reg_reg_const(A_SUBFIC,dst,dst,bitsizem1));
  207. { set to 255 is source was 0 }
  208. a_op_const_reg(list,OP_AND,dstsize,255,dst);
  209. end;
  210. procedure tcgppcgen.g_maybe_got_init(list: TAsmList);
  211. var
  212. instr: taicpu;
  213. cond: tasmcond;
  214. savedlr: boolean;
  215. begin
  216. if not(po_assembler in current_procinfo.procdef.procoptions) then
  217. begin
  218. if (cs_create_pic in current_settings.moduleswitches) and
  219. (pi_needs_got in current_procinfo.flags) then
  220. case target_info.system of
  221. system_powerpc_darwin,
  222. system_powerpc64_darwin:
  223. begin
  224. savedlr:=save_lr_in_prologue;
  225. if not savedlr then
  226. list.concat(taicpu.op_reg_reg(A_MFSPR,NR_R0,NR_LR));
  227. fillchar(cond,sizeof(cond),0);
  228. cond.simple:=false;
  229. cond.bo:=20;
  230. cond.bi:=31;
  231. instr:=taicpu.op_sym(A_BCL,current_procinfo.CurrGOTLabel);
  232. instr.setcondition(cond);
  233. list.concat(instr);
  234. a_label(list,current_procinfo.CurrGOTLabel);
  235. a_reg_alloc(list,current_procinfo.got);
  236. list.concat(taicpu.op_reg_reg(A_MFSPR,current_procinfo.got,NR_LR));
  237. if not savedlr or
  238. { in the following case lr is saved, but not restored }
  239. { (happens e.g. when generating debug info for leaf }
  240. { procedures) }
  241. not(pi_do_call in current_procinfo.flags) then
  242. list.concat(taicpu.op_reg_reg(A_MTSPR,NR_LR,NR_R0));
  243. end;
  244. else
  245. ;
  246. end;
  247. end;
  248. end;
  249. function tcgppcgen.g_indirect_sym_load(list: TAsmList; const symname: string; const flags: tindsymflags): tregister;
  250. begin
  251. case target_info.system of
  252. system_powerpc_macosclassic,
  253. system_powerpc_aix,
  254. system_powerpc64_aix:
  255. result:=load_got_symbol(list,symname,flags);
  256. else
  257. result:=inherited;
  258. end;
  259. end;
  260. function tcgppcgen.get_darwin_call_stub(const s: string; weak: boolean): tasmsymbol;
  261. var
  262. stubname: string;
  263. instr: taicpu;
  264. href: treference;
  265. l1: tasmsymbol;
  266. localgotlab: tasmlabel;
  267. cond: tasmcond;
  268. stubalign: byte;
  269. begin
  270. { function declared in the current unit? }
  271. { doesn't work correctly, because this will also return a hit if we }
  272. { previously took the address of an external procedure. It doesn't }
  273. { really matter, the linker will remove all unnecessary stubs. }
  274. stubname := 'L'+s+'$stub';
  275. result := current_asmdata.getasmsymbol(stubname);
  276. if assigned(result) then
  277. exit;
  278. if current_asmdata.asmlists[al_imports]=nil then
  279. current_asmdata.asmlists[al_imports]:=TAsmList.create;
  280. if (cs_create_pic in current_settings.moduleswitches) then
  281. stubalign:=32
  282. else
  283. stubalign:=16;
  284. new_section(current_asmdata.asmlists[al_imports],sec_stub,'',stubalign);
  285. result := current_asmdata.DefineAsmSymbol(stubname,AB_LOCAL,AT_FUNCTION,voidcodepointertype);
  286. current_asmdata.asmlists[al_imports].concat(Tai_symbol.Create(result,0));
  287. { register as a weak symbol if necessary }
  288. if weak then
  289. current_asmdata.weakrefasmsymbol(s,AT_FUNCTION);
  290. current_asmdata.asmlists[al_imports].concat(tai_directive.create(asd_indirect_symbol,s));
  291. l1 := current_asmdata.DefineAsmSymbol('L'+s+'$lazy_ptr',AB_LOCAL,AT_DATA,voidpointertype);
  292. reference_reset_symbol(href,l1,0,sizeof(pint),[]);
  293. href.refaddr := addr_higha;
  294. if (cs_create_pic in current_settings.moduleswitches) then
  295. begin
  296. current_asmdata.getjumplabel(localgotlab);
  297. href.relsymbol:=localgotlab;
  298. fillchar(cond,sizeof(cond),0);
  299. cond.simple:=false;
  300. cond.bo:=20;
  301. cond.bi:=31;
  302. current_asmdata.asmlists[al_imports].concat(taicpu.op_reg(A_MFLR,NR_R0));
  303. instr:=taicpu.op_sym(A_BCL,localgotlab);
  304. instr.setcondition(cond);
  305. current_asmdata.asmlists[al_imports].concat(instr);
  306. a_label(current_asmdata.asmlists[al_imports],localgotlab);
  307. current_asmdata.asmlists[al_imports].concat(taicpu.op_reg(A_MFLR,NR_R11));
  308. current_asmdata.asmlists[al_imports].concat(taicpu.op_reg_reg_ref(A_ADDIS,NR_R11,NR_R11,href));
  309. current_asmdata.asmlists[al_imports].concat(taicpu.op_reg(A_MTLR,NR_R0));
  310. end
  311. else
  312. current_asmdata.asmlists[al_imports].concat(taicpu.op_reg_ref(A_LIS,NR_R11,href));
  313. href.refaddr := addr_low;
  314. href.base := NR_R11;
  315. {$ifndef cpu64bitaddr}
  316. current_asmdata.asmlists[al_imports].concat(taicpu.op_reg_ref(A_LWZU,NR_R12,href));
  317. {$else cpu64bitaddr}
  318. { darwin/ppc64 uses a 32 bit absolute address here, strange... }
  319. current_asmdata.asmlists[al_imports].concat(taicpu.op_reg_ref(A_LDU,NR_R12,href));
  320. {$endif cpu64bitaddr}
  321. current_asmdata.asmlists[al_imports].concat(taicpu.op_reg(A_MTCTR,NR_R12));
  322. current_asmdata.asmlists[al_imports].concat(taicpu.op_none(A_BCTR));
  323. new_section(current_asmdata.asmlists[al_imports],sec_data_lazy,'',sizeof(pint));
  324. current_asmdata.asmlists[al_imports].concat(Tai_symbol.Create(l1,0));
  325. current_asmdata.asmlists[al_imports].concat(tai_directive.create(asd_indirect_symbol,s));
  326. current_asmdata.asmlists[al_imports].concat(tai_const.createname('dyld_stub_binding_helper',0));
  327. end;
  328. procedure tcgppcgen.a_loadaddr_ref_reg(list : TAsmList;const ref : treference;r : tregister);
  329. var
  330. ref2, tmpref: treference;
  331. begin
  332. ref2 := ref;
  333. fixref(list,ref2);
  334. if assigned(ref2.symbol) then
  335. begin
  336. if target_info.system = system_powerpc_macosclassic then
  337. begin
  338. if macos_direct_globals then
  339. begin
  340. reference_reset(tmpref,ref2.alignment,ref2.volatility);
  341. tmpref.offset := ref2.offset;
  342. tmpref.symbol := ref2.symbol;
  343. tmpref.base := NR_NO;
  344. list.concat(taicpu.op_reg_reg_ref(A_ADDI,r,NR_RTOC,tmpref));
  345. end
  346. else
  347. begin
  348. reference_reset(tmpref,ref2.alignment,ref2.volatility);
  349. tmpref.symbol := ref2.symbol;
  350. tmpref.offset := 0;
  351. tmpref.base := NR_RTOC;
  352. list.concat(taicpu.op_reg_ref(A_LWZ,r,tmpref));
  353. if ref2.offset<>0 then
  354. a_op_const_reg(list,OP_ADD,OS_ADDR,ref2.offset,r);
  355. end;
  356. if ref2.base <> NR_NO then
  357. list.concat(taicpu.op_reg_reg_reg(A_ADD,r,r,ref2.base));
  358. //list.concat(tai_comment.create(strpnew('*** a_loadaddr_ref_reg')));
  359. end
  360. else
  361. begin
  362. { add the symbol's value to the base of the reference, and if the }
  363. { reference doesn't have a base, create one }
  364. reference_reset(tmpref,ref2.alignment,ref2.volatility);
  365. tmpref.offset := ref2.offset;
  366. tmpref.symbol := ref2.symbol;
  367. tmpref.relsymbol := ref2.relsymbol;
  368. tmpref.refaddr := addr_higha;
  369. if ref2.base<> NR_NO then
  370. begin
  371. list.concat(taicpu.op_reg_reg_ref(A_ADDIS,r,
  372. ref2.base,tmpref));
  373. end
  374. else
  375. list.concat(taicpu.op_reg_ref(A_LIS,r,tmpref));
  376. tmpref.base := NR_NO;
  377. tmpref.refaddr := addr_low;
  378. { can be folded with one of the next instructions by the }
  379. { optimizer probably }
  380. list.concat(taicpu.op_reg_reg_ref(A_ADDI,r,r,tmpref));
  381. end
  382. end
  383. else if ref2.offset <> 0 Then
  384. if ref2.base <> NR_NO then
  385. a_op_const_reg_reg(list,OP_ADD,OS_ADDR,ref2.offset,ref2.base,r)
  386. { FixRef makes sure that "(ref.index <> R_NO) and (ref.offset <> 0)" never}
  387. { occurs, so now only ref.offset has to be loaded }
  388. else
  389. a_load_const_reg(list,OS_ADDR,ref2.offset,r)
  390. else if ref2.index <> NR_NO Then
  391. list.concat(taicpu.op_reg_reg_reg(A_ADD,r,ref2.base,ref2.index))
  392. else if (ref2.base <> NR_NO) and
  393. (r <> ref2.base) then
  394. a_load_reg_reg(list,OS_ADDR,OS_ADDR,ref2.base,r)
  395. else
  396. list.concat(taicpu.op_reg_const(A_LI,r,0));
  397. end;
  398. { calling a procedure by address }
  399. procedure tcgppcgen.a_call_reg(list : TAsmList;reg: tregister);
  400. var
  401. tmpref: treference;
  402. tmpreg: tregister;
  403. toc_offset: longint;
  404. begin
  405. tmpreg:=NR_NO;
  406. if target_info.system in systems_aix then
  407. begin
  408. { load function address in R0, and swap "reg" for R0 }
  409. reference_reset_base(tmpref,reg,0,ctempposinvalid,sizeof(pint),[]);
  410. a_load_ref_reg(list,OS_ADDR,OS_ADDR,tmpref,NR_R0);
  411. tmpreg:=reg;
  412. { no need to allocate/free R0, is already allocated by call node
  413. because it's a volatile register }
  414. reg:=NR_R0;
  415. end;
  416. list.concat(taicpu.op_reg(A_MTCTR,reg));
  417. if target_info.system in systems_aix then
  418. begin
  419. { load target TOC and possible link register }
  420. reference_reset_base(tmpref,tmpreg,sizeof(pint),ctempposinvalid,sizeof(pint),[]);
  421. a_load_ref_reg(list,OS_ADDR,OS_ADDR,tmpref,NR_RTOC);
  422. tmpref.offset:=2*sizeof(pint);
  423. a_load_ref_reg(list,OS_ADDR,OS_ADDR,tmpref,NR_R11);
  424. end
  425. else if target_info.abi=abi_powerpc_elfv2 then
  426. begin
  427. { functions must be called via R12 for this ABI }
  428. if reg<>NR_R12 then
  429. begin
  430. getcpuregister(list,NR_R12);
  431. a_load_reg_reg(list,OS_ADDR,OS_ADDR,reg,NR_R12)
  432. end;
  433. end;
  434. list.concat(taicpu.op_none(A_BCTRL));
  435. if target_info.abi in abis_ppc_toc then
  436. begin
  437. if (target_info.abi=abi_powerpc_elfv2) and
  438. (reg<>NR_R12) then
  439. ungetcpuregister(list,NR_R12);
  440. { restore our TOC }
  441. toc_offset:=get_rtoc_offset;
  442. reference_reset_base(tmpref,NR_STACK_POINTER_REG,toc_offset,ctempposinvalid,sizeof(pint),[]);
  443. a_load_ref_reg(list,OS_ADDR,OS_ADDR,tmpref,NR_RTOC);
  444. end;
  445. include(current_procinfo.flags,pi_do_call);
  446. end;
  447. procedure tcgppcgen.a_load_reg_ref(list: TAsmList; fromsize, tosize: TCGSize;
  448. reg: tregister; const ref: treference);
  449. const
  450. StoreInstr: array[OS_8..OS_INT, boolean, boolean] of TAsmOp =
  451. { indexed? updating?}
  452. (((A_STB, A_STBU), (A_STBX, A_STBUX)),
  453. ((A_STH, A_STHU), (A_STHX, A_STHUX)),
  454. ((A_STW, A_STWU), (A_STWX, A_STWUX))
  455. {$ifdef cpu64bitalu}
  456. ,
  457. ((A_STD, A_STDU), (A_STDX, A_STDUX))
  458. {$endif cpu64bitalu}
  459. );
  460. var
  461. ref2: TReference;
  462. op: TAsmOp;
  463. begin
  464. if not (fromsize in [OS_8..OS_INT,OS_S8..OS_SINT]) then
  465. internalerror(2002090911);
  466. if not (tosize in [OS_8..OS_INT,OS_S8..OS_SINT]) then
  467. internalerror(2002090905);
  468. if tosize in [OS_S8..OS_SINT] then
  469. { storing is the same for signed and unsigned values }
  470. tosize := tcgsize(ord(tosize) - (ord(OS_S8) - ord(OS_8)));
  471. ref2 := ref;
  472. fixref(list, ref2);
  473. op := storeinstr[tcgsize2unsigned[tosize], ref2.index <> NR_NO, false];
  474. a_load_store(list, op, reg, ref2);
  475. end;
  476. procedure tcgppcgen.a_loadfpu_reg_reg(list: TAsmList; fromsize, tosize: tcgsize; reg1, reg2: tregister);
  477. var
  478. op: tasmop;
  479. instr: taicpu;
  480. begin
  481. if not(fromsize in [OS_F32,OS_F64]) or
  482. not(tosize in [OS_F32,OS_F64]) then
  483. internalerror(2006123110);
  484. if (tosize < fromsize) then
  485. op:=A_FRSP
  486. else
  487. op:=A_FMR;
  488. instr := taicpu.op_reg_reg(op,reg2,reg1);
  489. list.concat(instr);
  490. if (op = A_FMR) then
  491. rg[R_FPUREGISTER].add_move_instruction(instr);
  492. end;
  493. procedure tcgppcgen.a_loadfpu_ref_reg(list: TAsmList; fromsize, tosize: tcgsize; const ref: treference; reg: tregister);
  494. const
  495. FpuLoadInstr: Array[OS_F32..OS_F64,boolean, boolean] of TAsmOp =
  496. { indexed? updating?}
  497. (((A_LFS,A_LFSU),(A_LFSX,A_LFSUX)),
  498. ((A_LFD,A_LFDU),(A_LFDX,A_LFDUX)));
  499. var
  500. op: tasmop;
  501. ref2: treference;
  502. begin
  503. if target_info.system in systems_aix then
  504. g_load_check_simple(list,ref,65536);
  505. if not(fromsize in [OS_F32,OS_F64]) or
  506. not(tosize in [OS_F32,OS_F64]) then
  507. internalerror(200201121);
  508. ref2 := ref;
  509. fixref(list,ref2);
  510. op := fpuloadinstr[fromsize,ref2.index <> NR_NO,false];
  511. a_load_store(list,op,reg,ref2);
  512. if (fromsize > tosize) then
  513. a_loadfpu_reg_reg(list,fromsize,tosize,reg,reg);
  514. end;
  515. procedure tcgppcgen.a_loadfpu_reg_ref(list: TAsmList; fromsize, tosize: tcgsize; reg: tregister; const ref: treference);
  516. const
  517. FpuStoreInstr: Array[OS_F32..OS_F64,boolean, boolean] of TAsmOp =
  518. { indexed? updating?}
  519. (((A_STFS,A_STFSU),(A_STFSX,A_STFSUX)),
  520. ((A_STFD,A_STFDU),(A_STFDX,A_STFDUX)));
  521. var
  522. op: tasmop;
  523. ref2: treference;
  524. reg2: tregister;
  525. begin
  526. if not(fromsize in [OS_F32,OS_F64]) or
  527. not(tosize in [OS_F32,OS_F64]) then
  528. internalerror(200201122);
  529. ref2 := ref;
  530. fixref(list,ref2);
  531. op := fpustoreinstr[tosize,ref2.index <> NR_NO,false];
  532. { some PPCs have a bug whereby storing a double to memory }
  533. { as single corrupts the value -> convert double to single }
  534. { first (bug confirmed on some G4s, but not on G5s) }
  535. if (tosize < fromsize) and
  536. (current_settings.cputype < cpu_PPC970) then
  537. begin
  538. reg2:=getfpuregister(list,tosize);
  539. a_loadfpu_reg_reg(list,fromsize,tosize,reg,reg2);
  540. reg:=reg2;
  541. end;
  542. a_load_store(list,op,reg,ref2);
  543. end;
  544. procedure tcgppcgen.g_overflowcheck(list: TAsmList; const l: tlocation; def: tdef);
  545. var
  546. hl : tasmlabel;
  547. flags : TResFlags;
  548. begin
  549. if not(cs_check_overflow in current_settings.localswitches) then
  550. exit;
  551. current_asmdata.getjumplabel(hl);
  552. if not ((def.typ=pointerdef) or
  553. ((def.typ=orddef) and
  554. (torddef(def).ordtype in [u64bit,u16bit,u32bit,u8bit,uchar,
  555. pasbool1,pasbool8,pasbool16,pasbool32,pasbool64]))) then
  556. begin
  557. if (current_settings.optimizecputype >= cpu_ppc970) or
  558. (current_settings.cputype >= cpu_ppc970) then
  559. begin
  560. { ... instructions setting overflow flag ...
  561. mfxerf R0
  562. mtcrf 128, R0
  563. ble cr0, label }
  564. list.concat(taicpu.op_reg(A_MFXER, NR_R0));
  565. list.concat(taicpu.op_const_reg(A_MTCRF, 128, NR_R0));
  566. flags.cr := RS_CR0;
  567. flags.flag := F_LE;
  568. a_jmp_flags(list, flags, hl);
  569. end
  570. else
  571. begin
  572. list.concat(taicpu.op_reg(A_MCRXR,NR_CR7));
  573. a_jmp(list,A_BC,C_NO,7,hl)
  574. end;
  575. end
  576. else
  577. a_jmp_cond(list,OC_AE,hl);
  578. a_call_name(list,'FPC_OVERFLOW',false);
  579. a_label(list,hl);
  580. end;
  581. procedure tcgppcgen.g_profilecode(list: TAsmList);
  582. var
  583. paraloc1 : tcgpara;
  584. pd : tprocdef;
  585. begin
  586. if (target_info.system in [system_powerpc_darwin]) then
  587. begin
  588. pd:=search_system_proc('mcount');
  589. paraloc1.init;
  590. paramanager.getcgtempparaloc(list,pd,1,paraloc1);
  591. a_load_reg_cgpara(list,OS_ADDR,NR_R0,paraloc1);
  592. paramanager.freecgpara(list,paraloc1);
  593. paraloc1.done;
  594. allocallcpuregisters(list);
  595. a_call_name(list,'mcount',false);
  596. deallocallcpuregisters(list);
  597. a_reg_dealloc(list,NR_R0);
  598. end;
  599. end;
  600. procedure tcgppcgen.a_jmp_flags(list: TAsmList; const f: TResFlags; l: tasmlabel);
  601. var
  602. c: tasmcond;
  603. f2: TResFlags;
  604. testbit: longint;
  605. begin
  606. f2:=f;
  607. testbit:=(f.cr-RS_CR0)*4;
  608. case f.flag of
  609. F_FA:
  610. f2.flag:=F_GT;
  611. F_FAE:
  612. begin
  613. list.concat(taicpu.op_const_const_const(A_CROR,testbit+1,testbit+1,testbit+2));
  614. f2.flag:=F_GT;
  615. end;
  616. F_FB:
  617. f2.flag:=F_LT;
  618. F_FBE:
  619. begin
  620. list.concat(taicpu.op_const_const_const(A_CROR,testbit,testbit,testbit+2));
  621. f2.flag:=F_LT;
  622. end;
  623. else
  624. ;
  625. end;
  626. c := flags_to_cond(f2);
  627. a_jmp(list,A_BC,c.cond,c.cr-RS_CR0,l);
  628. end;
  629. procedure tcgppcgen.a_jmp_cond(list : TAsmList;cond : TOpCmp; l: tasmlabel);
  630. begin
  631. a_jmp(list,A_BC,TOpCmp2AsmCond[cond],0,l);
  632. end;
  633. procedure tcgppcgen.a_jmp(list: TAsmList; op: tasmop; c: tasmcondflag;
  634. crval: longint; l: tasmlabel);
  635. var
  636. p: taicpu;
  637. begin
  638. p := taicpu.op_sym(op,l);
  639. if op <> A_B then
  640. create_cond_norm(c,crval,p.condition);
  641. p.is_jmp := true;
  642. list.concat(p)
  643. end;
  644. function tcgppcgen.get_rtoc_offset: longint;
  645. begin
  646. case target_info.abi of
  647. abi_powerpc_aix:
  648. result:=LA_RTOC_AIX;
  649. {$ifdef powerpc64}
  650. { no TOC on Linux/ppc32 }
  651. abi_powerpc_elfv1:
  652. result:=LA_RTOC_SYSV;
  653. {$endif}
  654. abi_powerpc_elfv2:
  655. result:=LA_RTOC_ELFV2;
  656. else
  657. internalerror(2015021001);
  658. end;
  659. end;
  660. function tcgppcgen.load_got_symbol(list: TAsmList; const symbol : string; const flags: tindsymflags) : tregister;
  661. var
  662. l: tasmsymbol;
  663. ref: treference;
  664. begin
  665. if target_info.system=system_powerpc64_linux then
  666. begin
  667. l:=current_asmdata.getasmsymbol(symbol);
  668. reference_reset_symbol(ref,l,0,sizeof(pint),[]);
  669. ref.base:=NR_RTOC;
  670. ref.refaddr:=addr_pic;
  671. end
  672. else if target_info.system in systems_aix+[system_powerpc_macosclassic] then
  673. get_aix_toc_sym(list,symbol,flags,ref,false)
  674. else
  675. internalerror(2007102010);
  676. result := getaddressregister(list);
  677. {$ifdef cpu64bitaddr}
  678. list.concat(taicpu.op_reg_ref(A_LD, result, ref));
  679. {$else cpu64bitaddr}
  680. list.concat(taicpu.op_reg_ref(A_LWZ, result, ref));
  681. {$endif cpu64bitaddr}
  682. end;
  683. procedure tcgppcgen.get_aix_toc_sym(list: TAsmList; const symname: string; const flags: tindsymflags; out ref: treference; force_direct_toc: boolean);
  684. const
  685. { The TOC on AIX is limited to 32KB worth of entries on AIX. If you need
  686. more entries, you have to add a level of indirection. In some cases,
  687. it's not possible to do this (e.g. assembler code). So by default, we
  688. use direct TOC entries until we're 500 from the maximum, and then start
  689. using indirect TOC entries. }
  690. AutoDirectTOCLimit = (high(smallint) div sizeof(pint)) - 500;
  691. var
  692. tmpref: treference;
  693. nlsymname: string;
  694. newsymname: ansistring;
  695. sym: TAsmSymbol;
  696. tocsym: TTOCAsmSymbol;
  697. tocnr,
  698. entrynr: longint;
  699. tmpreg: tregister;
  700. begin
  701. { all global symbol accesses always must be done via the TOC }
  702. nlsymname:='LC..'+symname;
  703. reference_reset_symbol(ref,current_asmdata.getasmsymbol(nlsymname),0,sizeof(pint),[]);
  704. if (assigned(ref.symbol) and
  705. not(ref.symbol is TTOCAsmSymbol)) or
  706. (not(ts_small_toc in current_settings.targetswitches) and
  707. (TPPCAsmData(current_asmdata).DirectTOCEntries<AutoDirectTOCLimit)) or
  708. force_direct_toc then
  709. begin
  710. ref.refaddr:=addr_pic_no_got;
  711. ref.base:=NR_RTOC;
  712. if not assigned(ref.symbol) then
  713. begin
  714. TPPCAsmData(current_asmdata).DirectTOCEntries:=TPPCAsmData(current_asmdata).DirectTOCEntries+1;
  715. new_section(current_asmdata.AsmLists[al_picdata],sec_toc,'',sizeof(pint));
  716. ref.symbol:=current_asmdata.DefineAsmSymbol(nlsymname,AB_LOCAL,AT_DATA,voidpointertype);
  717. ref.symbol.increfs;
  718. current_asmdata.asmlists[al_picdata].concat(tai_symbol.create(ref.symbol,0));
  719. { do not assign the result of these statements to ref.symbol: the
  720. access must be done via the LC..symname symbol; these are just
  721. to define the symbol that's being accessed as either weak or
  722. not }
  723. if not(is_weak in flags) then
  724. sym:=current_asmdata.RefAsmSymbol(symname,AT_DATA)
  725. else if is_data in flags then
  726. sym:=current_asmdata.WeakRefAsmSymbol(symname,AT_DATA)
  727. else
  728. sym:=current_asmdata.WeakRefAsmSymbol('.'+symname,AT_DATA);
  729. sym.increfs;
  730. newsymname:=ApplyAsmSymbolRestrictions(symname);
  731. current_asmdata.asmlists[al_picdata].concat(tai_directive.Create(asd_toc_entry,newsymname+'[TC],'+newsymname));
  732. end;
  733. end
  734. else
  735. begin
  736. if not assigned(ref.symbol) then
  737. begin
  738. TPPCAsmData(current_asmdata).GetNextSmallTocEntry(tocnr,entrynr);
  739. { new TOC entry? }
  740. if entrynr=0 then
  741. begin
  742. { create new toc entry that contains the address of the next
  743. table of addresses }
  744. get_aix_toc_sym(list,'tocsubtable'+tostr(tocnr),[is_data],tmpref,true);
  745. sym:=tmpref.symbol;
  746. sym.increfs;
  747. { base address for this batch of toc table entries that we'll
  748. put in a data block instead }
  749. new_section(current_asmdata.AsmLists[al_indirectpicdata],sec_rodata,'',sizeof(pint));
  750. sym:=current_asmdata.DefineAsmSymbol('tocsubtable'+tostr(tocnr),AB_LOCAL,AT_DATA,voidpointertype);
  751. current_asmdata.asmlists[al_indirectpicdata].concat(tai_symbol.create(sym,0));
  752. sym.increfs;
  753. end;
  754. { add the reference to the actual symbol inside the tocsubtable }
  755. if not(is_weak in flags) then
  756. current_asmdata.RefAsmSymbol(symname,AT_DATA)
  757. else if is_data in flags then
  758. current_asmdata.WeakRefAsmSymbol(symname,AT_DATA)
  759. else
  760. current_asmdata.WeakRefAsmSymbol('.'+symname,AT_DATA);
  761. tocsym:=TTOCAsmSymbol(current_asmdata.DefineAsmSymbolByClass(TTOCAsmSymbol,nlsymname,AB_LOCAL,AT_DATA,voidpointertype));
  762. ref.symbol:=tocsym;
  763. tocsym.increfs;
  764. tocsym.ftocsecnr:=tocnr;
  765. current_asmdata.asmlists[al_indirectpicdata].concat(tai_symbol.create(tocsym,0));
  766. newsymname:=ApplyAsmSymbolRestrictions(symname);
  767. sym:=current_asmdata.RefAsmSymbol(newsymname,AT_DATA);
  768. sym.increfs;
  769. current_asmdata.asmlists[al_indirectpicdata].concat(tai_const.Create_sym(sym));
  770. end;
  771. { first load the address of the table from the TOC }
  772. get_aix_toc_sym(list,'tocsubtable'+tostr(TTOCAsmSymbol(ref.symbol).ftocsecnr),[is_data],tmpref,true);
  773. tmpreg:=getaddressregister(list);
  774. a_load_ref_reg(list,OS_ADDR,OS_ADDR,tmpref,tmpreg);
  775. { and now set up the address of the entry, relative to the start of
  776. the table }
  777. ref.base:=tmpreg;
  778. ref.refaddr:=addr_pic;
  779. ref.relsymbol:=current_asmdata.GetAsmSymbol('tocsubtable'+tostr(TTOCAsmSymbol(ref.symbol).ftocsecnr));
  780. end;
  781. end;
  782. procedure tcgppcgen.g_load_check_simple(list: TAsmList; const ref: treference; size: aint);
  783. var
  784. reg: tregister;
  785. lab: tasmlabel;
  786. begin
  787. if not(cs_check_low_addr_load in current_settings.localswitches) then
  788. exit;
  789. { this is mainly for AIX, which does not trap loads from address 0. A
  790. global symbol (if not weak) will always map to a proper address, and
  791. the same goes for stack addresses -> skip }
  792. if assigned(ref.symbol) and
  793. (ref.symbol.bind<>AB_WEAK_EXTERNAL) then
  794. exit;
  795. if (ref.base=NR_STACK_POINTER_REG) or
  796. (ref.index=NR_STACK_POINTER_REG) or
  797. (assigned(current_procinfo) and
  798. ((ref.base=current_procinfo.framepointer) or
  799. (ref.index=current_procinfo.framepointer))) then
  800. exit;
  801. if assigned(ref.symbol) or
  802. (ref.offset<>0) or
  803. ((ref.base<>NR_NO) and (ref.index<>NR_NO)) then
  804. begin
  805. { can't allocate register, also used in wrappers and the like }
  806. reg:=NR_R0;
  807. a_reg_alloc(list,reg);
  808. a_loadaddr_ref_reg(list,ref,reg);
  809. end
  810. else if ref.base<>NR_NO then
  811. reg:=ref.base
  812. else
  813. reg:=ref.index;
  814. current_asmdata.getjumplabel(lab);
  815. if reg=NR_R0 then
  816. a_reg_dealloc(list,reg);
  817. a_cmp_const_reg_label(list,OS_ADDR,OC_A,size-1,reg,lab);
  818. a_call_name(list,'FPC_INVALIDPOINTER',false);
  819. a_label(list,lab);
  820. end;
  821. procedure tcgppcgen.g_flags2reg(list: TAsmList; size: TCgSize; const f: TResFlags; reg: TRegister);
  822. var
  823. testbit: byte;
  824. bitvalue: boolean;
  825. hreg: tregister;
  826. needsecondreg: boolean;
  827. begin
  828. hreg:=NR_NO;
  829. needsecondreg:=false;
  830. { get the bit to extract from the conditional register + its requested value (0 or 1) }
  831. testbit := ((f.cr - RS_CR0) * 4);
  832. case f.flag of
  833. F_EQ, F_NE:
  834. begin
  835. inc(testbit, 2);
  836. bitvalue := f.flag = F_EQ;
  837. end;
  838. F_LT, F_GE, F_FB:
  839. begin
  840. bitvalue := f.flag in [F_LT,F_FB];
  841. end;
  842. F_GT, F_LE, F_FA:
  843. begin
  844. inc(testbit);
  845. bitvalue := f.flag in [F_GT,F_FA];
  846. end;
  847. F_FAE:
  848. begin
  849. inc(testbit);
  850. bitvalue:=true;
  851. needsecondreg:=true;
  852. end;
  853. F_FBE:
  854. begin
  855. bitvalue:=true;
  856. needsecondreg:=true;
  857. end;
  858. else
  859. internalerror(200112261);
  860. end;
  861. { load the conditional register in the destination reg }
  862. list.concat(taicpu.op_reg(A_MFCR, reg));
  863. { we will move the bit that has to be tested to bit 0 by rotating left }
  864. testbit := (testbit + 1) and 31;
  865. { for floating-point >= and <=, extract equality bit first }
  866. if needsecondreg then
  867. begin
  868. hreg:=getintregister(list,OS_INT);
  869. list.concat(taicpu.op_reg_reg_const_const_const(
  870. A_RLWINM,hreg,reg,(((f.cr-RS_CR0)*4)+3) and 31,31,31));
  871. end;
  872. { extract bit }
  873. list.concat(taicpu.op_reg_reg_const_const_const(
  874. A_RLWINM,reg,reg,testbit,31,31));
  875. if needsecondreg then
  876. list.concat(taicpu.op_reg_reg_reg(A_OR,reg,hreg,reg))
  877. { if we need the inverse, xor with 1 }
  878. else if not bitvalue then
  879. list.concat(taicpu.op_reg_reg_const(A_XORI, reg, reg, 1));
  880. end;
  881. function tcgppcgen.fixref(list: TAsmList; var ref: treference): boolean;
  882. var
  883. tmpreg: tregister;
  884. begin
  885. result := false;
  886. { Avoid recursion. }
  887. if (ref.refaddr in [addr_pic,addr_pic_no_got]) then
  888. exit;
  889. {$IFDEF EXTDEBUG}
  890. list.concat(tai_comment.create(strpnew('fixref0 ' + ref2string(ref))));
  891. {$ENDIF EXTDEBUG}
  892. if (target_info.system in [system_powerpc_darwin,system_powerpc64_darwin]) and
  893. assigned(ref.symbol) and
  894. not assigned(ref.relsymbol) and
  895. ((ref.symbol.bind in [AB_EXTERNAL,AB_WEAK_EXTERNAL,AB_PRIVATE_EXTERN,AB_COMMON]) or
  896. (cs_create_pic in current_settings.moduleswitches))then
  897. begin
  898. if (ref.symbol.bind in [AB_EXTERNAL,AB_WEAK_EXTERNAL,AB_PRIVATE_EXTERN,AB_COMMON]) or
  899. ((target_info.system=system_powerpc64_darwin) and
  900. (ref.symbol.bind=AB_GLOBAL)) then
  901. begin
  902. tmpreg := g_indirect_sym_load(list,ref.symbol.name,asmsym2indsymflags(ref.symbol));
  903. ref.symbol:=nil;
  904. end
  905. else
  906. begin
  907. include(current_procinfo.flags,pi_needs_got);
  908. tmpreg := getaddressregister(list);
  909. a_load_reg_reg(list,OS_ADDR,OS_ADDR,current_procinfo.got,tmpreg);
  910. if assigned(ref.relsymbol) then
  911. internalerror(2007093501);
  912. ref.relsymbol := current_procinfo.CurrGOTLabel;
  913. end;
  914. if (ref.base = NR_NO) then
  915. ref.base := tmpreg
  916. else if (ref.index = NR_NO) then
  917. ref.index := tmpreg
  918. else
  919. begin
  920. list.concat(taicpu.op_reg_reg_reg(A_ADD,tmpreg,ref.base,tmpreg));
  921. ref.base := tmpreg;
  922. end;
  923. end;
  924. { if we have to create PIC, add the symbol to the TOC/GOT }
  925. if (((target_info.system = system_powerpc64_linux) and
  926. (cs_create_pic in current_settings.moduleswitches)) or
  927. (target_info.system in systems_aix+[system_powerpc_macosclassic])) and
  928. (assigned(ref.symbol) and
  929. not assigned(ref.relsymbol)) then
  930. begin
  931. include(current_procinfo.flags,pi_needs_got);
  932. tmpreg := load_got_symbol(list, ref.symbol.name, asmsym2indsymflags(ref.symbol));
  933. if (ref.base = NR_NO) then
  934. ref.base := tmpreg
  935. else if (ref.index = NR_NO) then
  936. ref.index := tmpreg
  937. else begin
  938. a_op_reg_reg_reg(list, OP_ADD, OS_ADDR, ref.base, tmpreg, tmpreg);
  939. ref.base := tmpreg;
  940. end;
  941. ref.symbol := nil;
  942. {$IFDEF EXTDEBUG}
  943. list.concat(tai_comment.create(strpnew('fixref-pic ' + ref2string(ref))));
  944. {$ENDIF EXTDEBUG}
  945. end;
  946. if (ref.base = NR_NO) then
  947. begin
  948. ref.base := ref.index;
  949. ref.index := NR_NO;
  950. end;
  951. if (ref.base <> NR_NO) then
  952. begin
  953. if (ref.index <> NR_NO) and
  954. ((ref.offset <> 0) or assigned(ref.symbol)) then
  955. begin
  956. result := true;
  957. tmpreg := rg[R_INTREGISTER].getregister(list,R_SUBWHOLE);
  958. list.concat(taicpu.op_reg_reg_reg(
  959. A_ADD,tmpreg,ref.base,ref.index));
  960. ref.index := NR_NO;
  961. ref.base := tmpreg;
  962. end
  963. end;
  964. if (ref.index <> NR_NO) and
  965. (assigned(ref.symbol) or
  966. (ref.offset <> 0)) then
  967. internalerror(200208102);
  968. {$IFDEF EXTDEBUG}
  969. list.concat(tai_comment.create(strpnew('fixref1 ' + ref2string(ref))));
  970. {$ENDIF EXTDEBUG}
  971. end;
  972. procedure tcgppcgen.a_load_store(list:TAsmList;op: tasmop;reg:tregister;
  973. ref: treference);
  974. var
  975. tmpreg: tregister;
  976. {$ifdef cpu64bitaddr}
  977. tmpreg2: tregister;
  978. {$endif cpu64bitaddr}
  979. tmpref: treference;
  980. largeOffset: Boolean;
  981. begin
  982. tmpreg := NR_NO;
  983. largeOffset:= hasLargeOffset(ref);
  984. if target_info.system in ([system_powerpc_macosclassic]+systems_aix) then
  985. begin
  986. if assigned(ref.symbol) and
  987. (ref.refaddr<>addr_pic_no_got) then
  988. begin {Load symbol's value}
  989. tmpreg := rg[R_INTREGISTER].getregister(list,R_SUBWHOLE);
  990. reference_reset(tmpref,sizeof(pint),[]);
  991. tmpref.symbol := ref.symbol;
  992. tmpref.base := NR_RTOC;
  993. tmpref.refaddr := addr_pic_no_got;
  994. if macos_direct_globals then
  995. list.concat(taicpu.op_reg_ref(A_LA,tmpreg,tmpref))
  996. else
  997. {$ifdef cpu64bitaddr}
  998. list.concat(taicpu.op_reg_ref(A_LD,tmpreg,tmpref));
  999. {$else cpu64bitaddr}
  1000. list.concat(taicpu.op_reg_ref(A_LWZ,tmpreg,tmpref));
  1001. {$endif cpu64bitaddr}
  1002. end;
  1003. if largeOffset then
  1004. begin {Add hi part of offset}
  1005. reference_reset(tmpref,ref.alignment,[]);
  1006. {$ifdef cpu64bitaddr}
  1007. if (ref.offset < low(longint)) or
  1008. (ref.offset > high(longint)) then
  1009. begin
  1010. { load upper 32 bits of the offset, adjusted for adding
  1011. the lower 32 bits later }
  1012. tmpreg2:=getintregister(list,OS_ADDR);
  1013. a_load_const_reg(list,OS_ADDR,(ref.offset and $ffffffff00000000) + ord(longint(ref.offset)<0),tmpreg2);
  1014. if tmpreg=NR_NO then
  1015. tmpreg:=tmpreg2
  1016. else
  1017. a_op_reg_reg(list,OP_ADD,OS_ADDR,tmpreg2,tmpreg);
  1018. ref.offset:=longint(ref.offset);
  1019. end;
  1020. {$endif cpu64bitaddr}
  1021. {Compensate when lo part is negative}
  1022. tmpref.offset := Smallint(ref.offset >> 16) + ord(Smallint(ref.offset) < 0);
  1023. if (tmpreg <> NR_NO) then
  1024. list.concat(taicpu.op_reg_reg_const(A_ADDIS,tmpreg, tmpreg,tmpref.offset))
  1025. else
  1026. begin
  1027. tmpreg := getintregister(list,OS_ADDR);
  1028. list.concat(taicpu.op_reg_const(A_LIS,tmpreg,tmpref.offset));
  1029. end;
  1030. end;
  1031. if (tmpreg <> NR_NO) then
  1032. begin
  1033. {Add content of base register}
  1034. if ref.base <> NR_NO then
  1035. list.concat(taicpu.op_reg_reg_reg(A_ADD,tmpreg,
  1036. ref.base,tmpreg));
  1037. {Make ref ready to be used by op}
  1038. ref.symbol:= nil;
  1039. ref.base:= tmpreg;
  1040. if largeOffset then
  1041. ref.offset := Smallint(ref.offset);
  1042. list.concat(taicpu.op_reg_ref(op,reg,ref));
  1043. //list.concat(tai_comment.create(strpnew('*** a_load_store indirect global')));
  1044. end
  1045. else
  1046. list.concat(taicpu.op_reg_ref(op,reg,ref));
  1047. end
  1048. else {if target_info.system <> system_powerpc_macosclassic}
  1049. begin
  1050. if assigned(ref.symbol) or
  1051. largeOffset then
  1052. begin
  1053. // TODO: offsets > 32 bit
  1054. tmpreg := rg[R_INTREGISTER].getregister(list,R_SUBWHOLE);
  1055. reference_reset(tmpref,ref.alignment,[]);
  1056. tmpref.symbol := ref.symbol;
  1057. tmpref.relsymbol := ref.relsymbol;
  1058. tmpref.offset := ref.offset;
  1059. tmpref.refaddr := addr_higha;
  1060. if ref.base <> NR_NO then
  1061. list.concat(taicpu.op_reg_reg_ref(A_ADDIS,tmpreg,
  1062. ref.base,tmpref))
  1063. else
  1064. list.concat(taicpu.op_reg_ref(A_LIS,tmpreg,tmpref));
  1065. ref.base := tmpreg;
  1066. ref.refaddr := addr_low;
  1067. list.concat(taicpu.op_reg_ref(op,reg,ref));
  1068. end
  1069. else
  1070. list.concat(taicpu.op_reg_ref(op,reg,ref));
  1071. end;
  1072. end;
  1073. { TPPCAsmData }
  1074. procedure TPPCAsmData.GetNextSmallTocEntry(out tocnr, entrynr: longint);
  1075. begin
  1076. if fcurrenttocentries>(high(word) div sizeof(pint)) then
  1077. begin
  1078. fcurrenttocentries:=0;
  1079. inc(ftocsections);
  1080. end;
  1081. tocnr:=ftocsections;
  1082. entrynr:=fcurrenttocentries;
  1083. inc(fcurrenttocentries);
  1084. end;
  1085. begin
  1086. casmdata:=TPPCAsmData;
  1087. end.