cgppc.pas 46 KB

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