cgppc.pas 50 KB

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