cgppc.pas 46 KB

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