cgppc.pas 49 KB

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