cpupara.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. {
  2. Copyright (c) 2002 by Florian Klaempfl
  3. PowerPC64 specific calling conventions
  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 cpupara;
  18. {$I fpcdefs.inc}
  19. interface
  20. uses
  21. globtype,
  22. aasmtai,aasmdata,
  23. cpubase,
  24. symconst, symtype, symdef, symsym,
  25. paramgr, parabase, cgbase, cgutils;
  26. type
  27. tppcparamanager = class(tparamanager)
  28. function get_volatile_registers_int(calloption: tproccalloption):
  29. tcpuregisterset; override;
  30. function get_volatile_registers_fpu(calloption: tproccalloption):
  31. tcpuregisterset; override;
  32. function push_addr_param(varspez: tvarspez; def: tdef; calloption:
  33. tproccalloption): boolean; override;
  34. procedure getintparaloc(calloption: tproccalloption; nr: longint; def: tdef; var cgpara: tcgpara); override;
  35. function create_paraloc_info(p: tabstractprocdef; side: tcallercallee): longint; override;
  36. function create_varargs_paraloc_info(p: tabstractprocdef; varargspara:
  37. tvarargsparalist): longint; override;
  38. function get_funcretloc(p : tabstractprocdef; side: tcallercallee; def: tdef): tcgpara;override;
  39. procedure create_funcretloc_info(p: tabstractprocdef; side: tcallercallee);
  40. private
  41. procedure init_values(var curintreg, curfloatreg, curmmreg: tsuperregister;
  42. var cur_stack_offset: aword);
  43. function create_paraloc_info_intern(p: tabstractprocdef; side:
  44. tcallercallee; paras: tparalist;
  45. var curintreg, curfloatreg, curmmreg: tsuperregister; var
  46. cur_stack_offset: aword; isVararg : boolean): longint;
  47. function parseparaloc(p: tparavarsym; const s: string): boolean; override;
  48. end;
  49. implementation
  50. uses
  51. verbose, systems,
  52. defutil,symtable,
  53. procinfo, cpupi;
  54. function tppcparamanager.get_volatile_registers_int(calloption:
  55. tproccalloption): tcpuregisterset;
  56. begin
  57. result := [RS_R0,RS_R3..RS_R12];
  58. if (target_info.system = system_powerpc64_darwin) then
  59. include(result,RS_R2);
  60. end;
  61. function tppcparamanager.get_volatile_registers_fpu(calloption:
  62. tproccalloption): tcpuregisterset;
  63. begin
  64. result := [RS_F0..RS_F13];
  65. end;
  66. procedure tppcparamanager.getintparaloc(calloption: tproccalloption; nr: longint; def : tdef; var cgpara: tcgpara);
  67. var
  68. paraloc: pcgparalocation;
  69. begin
  70. cgpara.reset;
  71. cgpara.size := def_cgsize(def);
  72. cgpara.intsize := tcgsize2size[cgpara.size];
  73. cgpara.alignment := get_para_align(calloption);
  74. cgpara.def:=def;
  75. paraloc := cgpara.add_location;
  76. with paraloc^ do begin
  77. size := OS_INT;
  78. if (nr <= 8) then begin
  79. if (nr = 0) then
  80. internalerror(200309271);
  81. loc := LOC_REGISTER;
  82. register := newreg(R_INTREGISTER, RS_R2 + nr, R_SUBWHOLE);
  83. end else begin
  84. loc := LOC_REFERENCE;
  85. paraloc^.reference.index := NR_STACK_POINTER_REG;
  86. reference.offset := sizeof(aint) * (nr - 8);
  87. end;
  88. end;
  89. end;
  90. function getparaloc(p: tdef): tcgloc;
  91. begin
  92. { Later, the LOC_REFERENCE is in most cases changed into LOC_REGISTER
  93. if push_addr_param for the def is true
  94. }
  95. case p.typ of
  96. orddef:
  97. result := LOC_REGISTER;
  98. floatdef:
  99. result := LOC_FPUREGISTER;
  100. enumdef:
  101. result := LOC_REGISTER;
  102. pointerdef:
  103. result := LOC_REGISTER;
  104. formaldef:
  105. result := LOC_REGISTER;
  106. classrefdef:
  107. result := LOC_REGISTER;
  108. procvardef,
  109. recorddef:
  110. result := LOC_REGISTER;
  111. objectdef:
  112. if is_object(p) then
  113. result := LOC_REFERENCE
  114. else
  115. result := LOC_REGISTER;
  116. stringdef:
  117. if is_shortstring(p) or is_longstring(p) then
  118. result := LOC_REFERENCE
  119. else
  120. result := LOC_REGISTER;
  121. filedef:
  122. result := LOC_REGISTER;
  123. arraydef:
  124. result := LOC_REFERENCE;
  125. setdef:
  126. if is_smallset(p) then
  127. result := LOC_REGISTER
  128. else
  129. result := LOC_REFERENCE;
  130. variantdef:
  131. result := LOC_REFERENCE;
  132. { avoid problems with errornous definitions }
  133. errordef:
  134. result := LOC_REGISTER;
  135. else
  136. internalerror(2002071001);
  137. end;
  138. end;
  139. function tppcparamanager.push_addr_param(varspez: tvarspez; def: tdef;
  140. calloption: tproccalloption): boolean;
  141. begin
  142. result := false;
  143. { var,out,constref always require address }
  144. if varspez in [vs_var, vs_out, vs_constref] then
  145. begin
  146. result := true;
  147. exit;
  148. end;
  149. case def.typ of
  150. variantdef,
  151. formaldef:
  152. result := true;
  153. procvardef,
  154. recorddef:
  155. result :=
  156. ((varspez = vs_const) and
  157. (
  158. (not (calloption in [pocall_cdecl, pocall_cppdecl]) and
  159. (def.size > 8))
  160. ) or
  161. (calloption = pocall_mwpascal)
  162. );
  163. arraydef:
  164. result := (tarraydef(def).highrange >= tarraydef(def).lowrange) or
  165. is_open_array(def) or
  166. is_array_of_const(def) or
  167. is_array_constructor(def);
  168. objectdef:
  169. result := is_object(def);
  170. setdef:
  171. result := not is_smallset(def);
  172. stringdef:
  173. result := tstringdef(def).stringtype in [st_shortstring, st_longstring];
  174. end;
  175. end;
  176. procedure tppcparamanager.init_values(var curintreg, curfloatreg, curmmreg:
  177. tsuperregister; var cur_stack_offset: aword);
  178. begin
  179. { register parameter save area begins at 48(r2) }
  180. cur_stack_offset := 48;
  181. curintreg := RS_R3;
  182. curfloatreg := RS_F1;
  183. curmmreg := RS_M2;
  184. end;
  185. procedure tppcparamanager.create_funcretloc_info(p: tabstractprocdef; side:
  186. tcallercallee);
  187. begin
  188. p.funcretloc[side]:=get_funcretloc(p,side,p.returndef);
  189. end;
  190. function tppcparamanager.get_funcretloc(p : tabstractprocdef; side:
  191. tcallercallee; def: tdef): tcgpara;
  192. var
  193. paraloc : pcgparalocation;
  194. retcgsize : tcgsize;
  195. begin
  196. if set_common_funcretloc_info(p,def,retcgsize,result) then
  197. exit;
  198. paraloc:=result.add_location;
  199. { Return in FPU register? }
  200. if def.typ=floatdef then
  201. begin
  202. paraloc^.loc:=LOC_FPUREGISTER;
  203. paraloc^.register:=NR_FPU_RESULT_REG;
  204. paraloc^.size:=retcgsize;
  205. end
  206. else
  207. { Return in register }
  208. begin
  209. paraloc^.loc:=LOC_REGISTER;
  210. if side=callerside then
  211. paraloc^.register:=newreg(R_INTREGISTER,RS_FUNCTION_RESULT_REG,cgsize2subreg(R_INTREGISTER,retcgsize))
  212. else
  213. paraloc^.register:=newreg(R_INTREGISTER,RS_FUNCTION_RETURN_REG,cgsize2subreg(R_INTREGISTER,retcgsize));
  214. paraloc^.size:=retcgsize;
  215. end;
  216. end;
  217. function tppcparamanager.create_paraloc_info(p: tabstractprocdef; side:
  218. tcallercallee): longint;
  219. var
  220. cur_stack_offset: aword;
  221. curintreg, curfloatreg, curmmreg : tsuperregister;
  222. begin
  223. init_values(curintreg, curfloatreg, curmmreg, cur_stack_offset);
  224. result := create_paraloc_info_intern(p, side, p.paras, curintreg, curfloatreg,
  225. curmmreg, cur_stack_offset, false);
  226. create_funcretloc_info(p, side);
  227. end;
  228. function tppcparamanager.create_paraloc_info_intern(p: tabstractprocdef; side:
  229. tcallercallee; paras: tparalist;
  230. var curintreg, curfloatreg, curmmreg: tsuperregister; var cur_stack_offset:
  231. aword; isVararg : boolean): longint;
  232. var
  233. fsym: tfieldvarsym;
  234. stack_offset: longint;
  235. paralen: aint;
  236. nextintreg, nextfloatreg, nextmmreg : tsuperregister;
  237. paradef: tdef;
  238. paraloc: pcgparalocation;
  239. i: integer;
  240. hp: tparavarsym;
  241. loc: tcgloc;
  242. paracgsize: tcgsize;
  243. parashift : byte;
  244. adjusttail: boolean;
  245. begin
  246. {$IFDEF extdebug}
  247. if po_explicitparaloc in p.procoptions then
  248. internalerror(200411141);
  249. {$ENDIF extdebug}
  250. result := 0;
  251. nextintreg := curintreg;
  252. nextfloatreg := curfloatreg;
  253. nextmmreg := curmmreg;
  254. stack_offset := cur_stack_offset;
  255. for i := 0 to paras.count - 1 do begin
  256. parashift := 0;
  257. hp := tparavarsym(paras[i]);
  258. paradef := hp.vardef;
  259. { Syscall for Morphos can have already a paraloc set; not supported on ppc64 }
  260. if (vo_has_explicit_paraloc in hp.varoptions) then begin
  261. internalerror(200412153);
  262. end;
  263. hp.paraloc[side].reset;
  264. { currently only support C-style array of const }
  265. if (p.proccalloption in [pocall_cdecl, pocall_cppdecl]) and
  266. is_array_of_const(paradef) then begin
  267. paraloc := hp.paraloc[side].add_location;
  268. { hack: the paraloc must be valid, but is not actually used }
  269. paraloc^.loc := LOC_REGISTER;
  270. paraloc^.register := NR_R0;
  271. paraloc^.size := OS_ADDR;
  272. break;
  273. end;
  274. if push_addr_param(hp.varspez, paradef, p.proccalloption) then begin
  275. paradef := getpointerdef(paradef);
  276. loc := LOC_REGISTER;
  277. paracgsize := OS_ADDR;
  278. paralen := tcgsize2size[OS_ADDR];
  279. end else begin
  280. if not is_special_array(paradef) then
  281. paralen := paradef.size
  282. else
  283. paralen := tcgsize2size[def_cgsize(paradef)];
  284. if (paradef.typ = recorddef) and
  285. (hp.varspez in [vs_value, vs_const]) then begin
  286. { if a record has only one field and that field is }
  287. { non-composite (not array or record), it must be }
  288. { passed according to the rules of that type. }
  289. if tabstractrecordsymtable(tabstractrecorddef(hp.vardef).symtable).has_single_field(fsym) and
  290. ((fsym.vardef.typ = floatdef) or
  291. (not(target_info.system in systems_aix) and
  292. (fsym.vardef.typ in [orddef, enumdef]))) then begin
  293. paradef := fsym.vardef;
  294. loc := getparaloc(paradef);
  295. paracgsize := def_cgsize(paradef);
  296. end else begin
  297. loc := LOC_REGISTER;
  298. paracgsize := int_cgsize(paralen);
  299. if (paralen in [3,5,6,7]) then
  300. parashift := (8-paralen) * 8;
  301. end;
  302. end else begin
  303. loc := getparaloc(paradef);
  304. paracgsize := def_cgsize(paradef);
  305. { for things like formaldef }
  306. if (paracgsize = OS_NO) then begin
  307. paracgsize := OS_ADDR;
  308. paralen := tcgsize2size[OS_ADDR];
  309. end;
  310. end
  311. end;
  312. { patch FPU values into integer registers if we currently have
  313. to pass them as vararg parameters
  314. }
  315. if (isVararg) and (paradef.typ = floatdef) then begin
  316. loc := LOC_REGISTER;
  317. if paracgsize = OS_F64 then
  318. paracgsize := OS_64
  319. else
  320. paracgsize := OS_32;
  321. end;
  322. hp.paraloc[side].alignment := std_param_align;
  323. hp.paraloc[side].size := paracgsize;
  324. hp.paraloc[side].intsize := paralen;
  325. hp.paraloc[side].def := paradef;
  326. if (paralen = 0) then
  327. if (paradef.typ = recorddef) then begin
  328. paraloc := hp.paraloc[side].add_location;
  329. paraloc^.loc := LOC_VOID;
  330. end else
  331. internalerror(2005011310);
  332. adjusttail:=paralen>8;
  333. { can become < 0 for e.g. 3-byte records }
  334. while (paralen > 0) do begin
  335. paraloc := hp.paraloc[side].add_location;
  336. { In case of po_delphi_nested_cc, the parent frame pointer
  337. is always passed on the stack. }
  338. if (loc = LOC_REGISTER) and
  339. (nextintreg <= RS_R10) and
  340. (not(vo_is_parentfp in hp.varoptions) or
  341. not(po_delphi_nested_cc in p.procoptions)) then begin
  342. paraloc^.loc := loc;
  343. paraloc^.shiftval := parashift;
  344. { make sure we don't lose whether or not the type is signed }
  345. if (paracgsize <> OS_NO) and (paradef.typ <> orddef) then
  346. paracgsize := int_cgsize(paralen);
  347. { aix requires that record data (including partial data) stored in
  348. parameter registers is left-aligned. Other targets only do this if
  349. the total size of the parameter was > 8 bytes. }
  350. if (((target_info.system in systems_aix) and
  351. (paradef.typ = recorddef)) or
  352. adjusttail) and
  353. (paralen < sizeof(aint)) then
  354. begin
  355. paraloc^.shiftval := (sizeof(aint)-paralen)*(-8);
  356. paraloc^.size := OS_INT;
  357. end
  358. else if (paracgsize in [OS_NO,OS_128,OS_S128]) then
  359. paraloc^.size := OS_INT
  360. else
  361. paraloc^.size := paracgsize;
  362. paraloc^.register := newreg(R_INTREGISTER, nextintreg, R_SUBNONE);
  363. inc(nextintreg);
  364. dec(paralen, tcgsize2size[paraloc^.size]);
  365. inc(stack_offset, sizeof(pint));
  366. end else if (loc = LOC_FPUREGISTER) and
  367. (nextfloatreg <= RS_F13) then begin
  368. paraloc^.loc := loc;
  369. paraloc^.size := paracgsize;
  370. paraloc^.register := newreg(R_FPUREGISTER, nextfloatreg, R_SUBWHOLE);
  371. { the PPC64 ABI says that the GPR index is increased for every parameter, no matter
  372. which type it is stored in }
  373. inc(nextintreg);
  374. inc(nextfloatreg);
  375. dec(paralen, tcgsize2size[paraloc^.size]);
  376. inc(stack_offset, tcgsize2size[OS_FLOAT]);
  377. end else if (loc = LOC_MMREGISTER) then begin
  378. { Altivec not supported }
  379. internalerror(200510192);
  380. end else begin
  381. { either LOC_REFERENCE, or one of the above which must be passed on the
  382. stack because of insufficient registers }
  383. paraloc^.loc := LOC_REFERENCE;
  384. case loc of
  385. LOC_FPUREGISTER:
  386. paraloc^.size:=int_float_cgsize(paralen);
  387. LOC_REGISTER,
  388. LOC_REFERENCE:
  389. paraloc^.size:=int_cgsize(paralen);
  390. else
  391. internalerror(2006011101);
  392. end;
  393. if (side = callerside) then
  394. paraloc^.reference.index := NR_STACK_POINTER_REG
  395. else begin
  396. { during procedure entry, NR_OLD_STACK_POINTER_REG contains the old stack pointer }
  397. paraloc^.reference.index := NR_OLD_STACK_POINTER_REG;
  398. tppcprocinfo(current_procinfo).needs_frame_pointer := true;
  399. end;
  400. paraloc^.reference.offset := stack_offset;
  401. { align temp contents to next register size }
  402. inc(stack_offset, align(paralen, 8));
  403. paralen := 0;
  404. end;
  405. end;
  406. end;
  407. curintreg := nextintreg;
  408. curfloatreg := nextfloatreg;
  409. curmmreg := nextmmreg;
  410. cur_stack_offset := stack_offset;
  411. result := stack_offset;
  412. end;
  413. function tppcparamanager.create_varargs_paraloc_info(p: tabstractprocdef;
  414. varargspara: tvarargsparalist): longint;
  415. var
  416. cur_stack_offset: aword;
  417. parasize, l: longint;
  418. curintreg, firstfloatreg, curfloatreg, curmmreg: tsuperregister;
  419. i: integer;
  420. hp: tparavarsym;
  421. paraloc: pcgparalocation;
  422. begin
  423. init_values(curintreg, curfloatreg, curmmreg, cur_stack_offset);
  424. firstfloatreg := curfloatreg;
  425. result := create_paraloc_info_intern(p, callerside, p.paras, curintreg,
  426. curfloatreg, curmmreg, cur_stack_offset, false);
  427. if (p.proccalloption in [pocall_cdecl, pocall_cppdecl, pocall_mwpascal]) then begin
  428. { just continue loading the parameters in the registers }
  429. result := create_paraloc_info_intern(p, callerside, varargspara, curintreg,
  430. curfloatreg, curmmreg, cur_stack_offset, true);
  431. { varargs routines have to reserve at least 64 bytes for the PPC64 ABI }
  432. if (result < 64) then
  433. result := 64;
  434. end else begin
  435. parasize := cur_stack_offset;
  436. for i := 0 to varargspara.count - 1 do begin
  437. hp := tparavarsym(varargspara[i]);
  438. hp.paraloc[callerside].alignment := 8;
  439. paraloc := hp.paraloc[callerside].add_location;
  440. paraloc^.loc := LOC_REFERENCE;
  441. paraloc^.size := def_cgsize(hp.vardef);
  442. paraloc^.reference.index := NR_STACK_POINTER_REG;
  443. l := push_size(hp.varspez, hp.vardef, p.proccalloption);
  444. paraloc^.reference.offset := parasize;
  445. parasize := parasize + l;
  446. end;
  447. result := parasize;
  448. end;
  449. if curfloatreg <> firstfloatreg then
  450. include(varargspara.varargsinfo, va_uses_float_reg);
  451. end;
  452. function tppcparamanager.parseparaloc(p: tparavarsym; const s: string): boolean;
  453. begin
  454. { not supported/required for PowerPC64-linux target }
  455. internalerror(200404182);
  456. result := true;
  457. end;
  458. begin
  459. paramanager := tppcparamanager.create;
  460. end.