cgppc.pas 47 KB

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