cpupara.pas 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. {
  2. $Id$
  3. Copyright (c) 2002 by Florian Klaempfl
  4. PowerPC specific calling conventions
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  16. ****************************************************************************
  17. }
  18. { PowerPC specific calling conventions are handled by this unit
  19. }
  20. unit cpupara;
  21. {$i fpcdefs.inc}
  22. interface
  23. uses
  24. globtype,
  25. cclasses,
  26. aasmtai,
  27. cpubase,cpuinfo,
  28. symconst,symbase,symtype,symdef,paramgr,cgbase;
  29. type
  30. tppcparamanager = class(tparamanager)
  31. function get_volatile_registers_int(calloption : tproccalloption):tcpuregisterset;override;
  32. function get_volatile_registers_fpu(calloption : tproccalloption):tcpuregisterset;override;
  33. function push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;override;
  34. function getintparaloc(calloption : tproccalloption; nr : longint) : tparalocation;override;
  35. function create_paraloc_info(p : tabstractprocdef; side: tcallercallee):longint;override;
  36. function create_varargs_paraloc_info(p : tabstractprocdef; varargspara:tvarargspara):longint;override;
  37. procedure create_funcret_paraloc_info(p : tabstractprocdef; side: tcallercallee);
  38. private
  39. procedure init_values(var curintreg, curfloatreg, curmmreg: tsuperregister; var cur_stack_offset: aword);
  40. function create_paraloc_info_intern(p : tabstractprocdef; side: tcallercallee; firstpara: tparaitem;
  41. var curintreg, curfloatreg, curmmreg: tsuperregister; var cur_stack_offset: aword):longint;
  42. function parseparaloc(p : tparaitem;const s : string) : boolean;override;
  43. end;
  44. implementation
  45. uses
  46. verbose,systems,
  47. procinfo,
  48. rgobj,
  49. defutil,symsym,cpupi;
  50. function tppcparamanager.get_volatile_registers_int(calloption : tproccalloption):tcpuregisterset;
  51. begin
  52. result := [RS_R3..RS_R12];
  53. end;
  54. function tppcparamanager.get_volatile_registers_fpu(calloption : tproccalloption):tcpuregisterset;
  55. begin
  56. case target_info.abi of
  57. abi_powerpc_aix:
  58. result := [RS_F0..RS_F13];
  59. abi_powerpc_sysv:
  60. {$warning: the 64bit sysv abi also uses RS_F0..RS_F13 like the aix abi above }
  61. result := [RS_F0..RS_F8];
  62. else
  63. internalerror(2003091401);
  64. end;
  65. end;
  66. function tppcparamanager.getintparaloc(calloption : tproccalloption; nr : longint) : tparalocation;
  67. begin
  68. fillchar(result,sizeof(tparalocation),0);
  69. result.lochigh:=LOC_INVALID;
  70. if nr<1 then
  71. internalerror(2002070801)
  72. else if nr<=8 then
  73. begin
  74. result.loc:=LOC_REGISTER;
  75. result.register:=newreg(R_INTREGISTER,RS_R2+nr,R_SUBWHOLE);
  76. end
  77. else
  78. begin
  79. result.loc:=LOC_REFERENCE;
  80. result.reference.index:=NR_STACK_POINTER_REG;
  81. result.reference.offset:=(nr-8)*4;
  82. end;
  83. result.size := OS_INT;
  84. end;
  85. function getparaloc(p : tdef) : tcgloc;
  86. begin
  87. { Later, the LOC_REFERENCE is in most cases changed into LOC_REGISTER
  88. if push_addr_param for the def is true
  89. }
  90. case p.deftype of
  91. orddef:
  92. getparaloc:=LOC_REGISTER;
  93. floatdef:
  94. getparaloc:=LOC_FPUREGISTER;
  95. enumdef:
  96. getparaloc:=LOC_REGISTER;
  97. pointerdef:
  98. getparaloc:=LOC_REGISTER;
  99. formaldef:
  100. getparaloc:=LOC_REGISTER;
  101. classrefdef:
  102. getparaloc:=LOC_REGISTER;
  103. recorddef:
  104. getparaloc:=LOC_REFERENCE;
  105. objectdef:
  106. if is_object(p) then
  107. getparaloc:=LOC_REFERENCE
  108. else
  109. getparaloc:=LOC_REGISTER;
  110. stringdef:
  111. if is_shortstring(p) or is_longstring(p) then
  112. getparaloc:=LOC_REFERENCE
  113. else
  114. getparaloc:=LOC_REGISTER;
  115. procvardef:
  116. if (po_methodpointer in tprocvardef(p).procoptions) then
  117. getparaloc:=LOC_REFERENCE
  118. else
  119. getparaloc:=LOC_REGISTER;
  120. filedef:
  121. getparaloc:=LOC_REGISTER;
  122. arraydef:
  123. getparaloc:=LOC_REFERENCE;
  124. setdef:
  125. if is_smallset(p) then
  126. getparaloc:=LOC_REGISTER
  127. else
  128. getparaloc:=LOC_REFERENCE;
  129. variantdef:
  130. getparaloc:=LOC_REFERENCE;
  131. { avoid problems with errornous definitions }
  132. errordef:
  133. getparaloc:=LOC_REGISTER;
  134. else
  135. internalerror(2002071001);
  136. end;
  137. end;
  138. function tppcparamanager.push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;
  139. begin
  140. { var,out always require address }
  141. if varspez in [vs_var,vs_out] then
  142. begin
  143. result:=true;
  144. exit;
  145. end;
  146. case def.deftype of
  147. recorddef:
  148. result:=true;
  149. arraydef:
  150. result:=(tarraydef(def).highrange>=tarraydef(def).lowrange) or
  151. is_open_array(def) or
  152. is_array_of_const(def) or
  153. is_array_constructor(def);
  154. setdef :
  155. result:=(tsetdef(def).settype<>smallset);
  156. stringdef :
  157. result:=tstringdef(def).string_typ in [st_shortstring,st_longstring];
  158. procvardef :
  159. result:=po_methodpointer in tprocvardef(def).procoptions;
  160. else
  161. result:=inherited push_addr_param(varspez,def,calloption);
  162. end;
  163. end;
  164. procedure tppcparamanager.init_values(var curintreg, curfloatreg, curmmreg: tsuperregister; var cur_stack_offset: aword);
  165. begin
  166. case target_info.abi of
  167. abi_powerpc_aix:
  168. cur_stack_offset:=24;
  169. abi_powerpc_sysv:
  170. cur_stack_offset:=8;
  171. else
  172. internalerror(2003051901);
  173. end;
  174. curintreg:=RS_R3;
  175. curfloatreg:=RS_F1;
  176. curmmreg:=RS_M1;
  177. end;
  178. procedure tppcparamanager.create_funcret_paraloc_info(p : tabstractprocdef; side: tcallercallee);
  179. var
  180. paraloc : tparalocation;
  181. begin
  182. fillchar(paraloc,sizeof(tparalocation),0);
  183. paraloc.size:=def_cgsize(p.rettype.def);
  184. paraloc.Alignment:= std_param_align;
  185. { Constructors return self }
  186. if (p.proctypeoption=potype_constructor) then
  187. begin
  188. paraloc.size:=OS_ADDR;
  189. paraloc.loc:=LOC_REGISTER;
  190. paraloc.register:=NR_FUNCTION_RESULT_REG;
  191. end
  192. else
  193. { Return in FPU register? }
  194. if p.rettype.def.deftype=floatdef then
  195. begin
  196. paraloc.loc:=LOC_FPUREGISTER;
  197. paraloc.register:=NR_FPU_RESULT_REG;
  198. end
  199. else
  200. { Return in register? }
  201. if not ret_in_param(p.rettype.def,p.proccalloption) then
  202. begin
  203. paraloc.loc:=LOC_REGISTER;
  204. {$ifndef cpu64bit}
  205. if paraloc.size in [OS_64,OS_S64] then
  206. begin
  207. paraloc.lochigh:=LOC_REGISTER;
  208. paraloc.register64.reglo:=NR_FUNCTION_RESULT64_LOW_REG;
  209. paraloc.register64.reghi:=NR_FUNCTION_RESULT64_HIGH_REG
  210. end
  211. else
  212. {$endif cpu64bit}
  213. begin
  214. paraloc.register:=NR_FUNCTION_RESULT_REG
  215. end;
  216. end
  217. else
  218. begin
  219. paraloc.loc:=LOC_REFERENCE;
  220. end;
  221. p.funcret_paraloc[side]:=paraloc;
  222. end;
  223. function tppcparamanager.create_paraloc_info(p : tabstractprocdef; side: tcallercallee):longint;
  224. var
  225. paraloc : tparalocation;
  226. cur_stack_offset: aword;
  227. curintreg, curfloatreg, curmmreg: tsuperregister;
  228. begin
  229. init_values(curintreg,curfloatreg,curmmreg,cur_stack_offset);
  230. result := create_paraloc_info_intern(p,side,tparaitem(p.para.first),curintreg,curfloatreg,curmmreg,cur_stack_offset);
  231. create_funcret_paraloc_info(p,side);
  232. end;
  233. function tppcparamanager.create_paraloc_info_intern(p : tabstractprocdef; side: tcallercallee; firstpara: tparaitem;
  234. var curintreg, curfloatreg, curmmreg: tsuperregister; var cur_stack_offset: aword):longint;
  235. var
  236. stack_offset: aword;
  237. nextintreg,nextfloatreg,nextmmreg, maxfpureg : tsuperregister;
  238. paradef : tdef;
  239. paraloc : tparalocation;
  240. hp : tparaitem;
  241. loc : tcgloc;
  242. is_64bit: boolean;
  243. procedure assignintreg;
  244. begin
  245. if nextintreg<=ord(NR_R10) then
  246. begin
  247. paraloc.loc:=LOC_REGISTER;
  248. paraloc.register:=newreg(R_INTREGISTER,nextintreg,R_SUBNONE);
  249. inc(nextintreg);
  250. if target_info.abi=abi_powerpc_aix then
  251. inc(stack_offset,4);
  252. end
  253. else
  254. begin
  255. paraloc.loc:=LOC_REFERENCE;
  256. paraloc.reference.index:=NR_STACK_POINTER_REG;
  257. paraloc.reference.offset:=stack_offset;
  258. inc(stack_offset,4);
  259. end;
  260. end;
  261. begin
  262. result:=0;
  263. nextintreg := curintreg;
  264. nextfloatreg := curfloatreg;
  265. nextmmreg := curmmreg;
  266. stack_offset := cur_stack_offset;
  267. case target_info.abi of
  268. abi_powerpc_aix:
  269. maxfpureg := RS_F13;
  270. abi_powerpc_sysv:
  271. maxfpureg := RS_F8;
  272. else internalerror(2004070912);
  273. end;
  274. hp:=firstpara;
  275. while assigned(hp) do
  276. begin
  277. { currently only support C-style array of const }
  278. if (p.proccalloption in [pocall_cdecl,pocall_cppdecl]) and
  279. is_array_of_const(hp.paratype.def) then
  280. begin
  281. { hack: the paraloc must be valid, but is not actually used }
  282. hp.paraloc[side].loc := LOC_REGISTER;
  283. hp.paraloc[side].lochigh := LOC_INVALID;
  284. hp.paraloc[side].register := NR_R0;
  285. hp.paraloc[side].size := OS_ADDR;
  286. break;
  287. end;
  288. if (hp.paratyp in [vs_var,vs_out]) then
  289. begin
  290. paradef := voidpointertype.def;
  291. loc := LOC_REGISTER;
  292. end
  293. else
  294. begin
  295. paradef := hp.paratype.def;
  296. loc:=getparaloc(paradef);
  297. end;
  298. { make sure all alignment bytes are 0 as well }
  299. fillchar(paraloc,sizeof(paraloc),0);
  300. paraloc.alignment:= std_param_align;
  301. paraloc.lochigh:=LOC_INVALID;
  302. case loc of
  303. LOC_REGISTER:
  304. begin
  305. paraloc.size := def_cgsize(paradef);
  306. { for things like formaldef }
  307. if paraloc.size = OS_NO then
  308. paraloc.size := OS_ADDR;
  309. is_64bit := paraloc.size in [OS_64,OS_S64];
  310. if nextintreg<=(RS_R10-ord(is_64bit)) then
  311. begin
  312. paraloc.loc:=LOC_REGISTER;
  313. if is_64bit then
  314. begin
  315. if odd(nextintreg-RS_R3) and (target_info.abi=abi_powerpc_sysv) Then
  316. inc(nextintreg);
  317. paraloc.registerhigh:=newreg(R_INTREGISTER,nextintreg,R_SUBNONE);
  318. paraloc.lochigh:=LOC_REGISTER;
  319. inc(nextintreg);
  320. if target_info.abi=abi_powerpc_aix then
  321. inc(stack_offset,4);
  322. end;
  323. paraloc.registerlow:=newreg(R_INTREGISTER,nextintreg,R_SUBNONE);
  324. inc(nextintreg);
  325. if target_info.abi=abi_powerpc_aix then
  326. inc(stack_offset,4);
  327. end
  328. else
  329. begin
  330. nextintreg:=RS_R11;
  331. paraloc.loc:=LOC_REFERENCE;
  332. paraloc.reference.index:=NR_STACK_POINTER_REG;
  333. paraloc.reference.offset:=stack_offset;
  334. if not is_64bit then
  335. inc(stack_offset,4)
  336. else
  337. inc(stack_offset,8);
  338. end;
  339. end;
  340. LOC_FPUREGISTER:
  341. begin
  342. paraloc.size:=def_cgsize(paradef);
  343. if nextfloatreg<=maxfpureg then
  344. begin
  345. paraloc.loc:=LOC_FPUREGISTER;
  346. paraloc.register:=newreg(R_FPUREGISTER,nextfloatreg,R_SUBWHOLE);
  347. inc(nextfloatreg);
  348. end
  349. else
  350. begin
  351. paraloc.loc:=LOC_REFERENCE;
  352. paraloc.reference.index:=NR_STACK_POINTER_REG;
  353. paraloc.reference.offset:=stack_offset;
  354. end;
  355. if target_info.abi=abi_powerpc_aix then
  356. begin
  357. if paraloc.size = OS_F32 then
  358. begin
  359. inc(stack_offset,4);
  360. if (nextintreg < RS_R11) then
  361. inc(nextintreg);
  362. end
  363. else
  364. begin
  365. inc(stack_offset,8);
  366. if (nextintreg < RS_R10) then
  367. inc(nextintreg,2)
  368. else
  369. nextintreg := RS_R11;
  370. end;
  371. end;
  372. end;
  373. LOC_REFERENCE:
  374. begin
  375. paraloc.size:=OS_ADDR;
  376. if push_addr_param(hp.paratyp,paradef,p.proccalloption) or
  377. is_open_array(paradef) or
  378. is_array_of_const(paradef) then
  379. assignintreg
  380. else
  381. begin
  382. paraloc.loc:=LOC_REFERENCE;
  383. paraloc.reference.index:=NR_STACK_POINTER_REG;
  384. paraloc.reference.offset:=stack_offset;
  385. inc(stack_offset,hp.paratype.def.size);
  386. end;
  387. end;
  388. else
  389. internalerror(2002071002);
  390. end;
  391. {
  392. this is filled in in ncgutil
  393. if side = calleeside then
  394. begin
  395. if (paraloc.loc = LOC_REFERENCE) then
  396. begin
  397. if (current_procinfo.procdef <> p) then
  398. internalerror(2003112201);
  399. inc(paraloc.reference.offset,current_procinfo.calc_stackframe_size);
  400. end;
  401. end;
  402. }
  403. hp.paraloc[side]:=paraloc;
  404. hp:=tparaitem(hp.next);
  405. end;
  406. curintreg:=nextintreg;
  407. curfloatreg:=nextfloatreg;
  408. curmmreg:=nextmmreg;
  409. cur_stack_offset:=stack_offset;
  410. result:=cur_stack_offset;
  411. end;
  412. function tppcparamanager.create_varargs_paraloc_info(p : tabstractprocdef; varargspara:tvarargspara):longint;
  413. var
  414. cur_stack_offset: aword;
  415. parasize, l: longint;
  416. curintreg, firstfloatreg, curfloatreg, curmmreg: tsuperregister;
  417. hp: tparaitem;
  418. paraloc: tparalocation;
  419. begin
  420. init_values(curintreg,curfloatreg,curmmreg,cur_stack_offset);
  421. firstfloatreg:=curfloatreg;
  422. result := create_paraloc_info_intern(p,callerside,tparaitem(p.para.first),curintreg,curfloatreg,curmmreg,cur_stack_offset);
  423. if (p.proccalloption in [pocall_cdecl,pocall_cppdecl]) then
  424. { just continue loading the parameters in the registers }
  425. result := create_paraloc_info_intern(p,callerside,tparaitem(varargspara.first),curintreg,curfloatreg,curmmreg,cur_stack_offset)
  426. else
  427. begin
  428. hp := tparaitem(varargspara.first);
  429. parasize := cur_stack_offset;
  430. while assigned(hp) do
  431. begin
  432. paraloc.size:=def_cgsize(hp.paratype.def);
  433. paraloc.lochigh:=LOC_INVALID;
  434. paraloc.loc:=LOC_REFERENCE;
  435. paraloc.alignment:=4;
  436. paraloc.reference.index:=NR_STACK_POINTER_REG;
  437. l:=push_size(hp.paratyp,hp.paratype.def,p.proccalloption);
  438. paraloc.reference.offset:=parasize;
  439. parasize:=parasize+l;
  440. hp.paraloc[callerside]:=paraloc;
  441. hp:=tparaitem(hp.next);
  442. end;
  443. result := parasize;
  444. end;
  445. if curfloatreg<>firstfloatreg then
  446. include(varargspara.varargsinfo,va_uses_float_reg);
  447. end;
  448. function tppcparamanager.parseparaloc(p : tparaitem;const s : string) : boolean;
  449. begin
  450. result:=false;
  451. case target_info.system of
  452. system_powerpc_morphos:
  453. begin
  454. p.paraloc[callerside].loc:=LOC_REFERENCE;
  455. p.paraloc[callerside].lochigh:=LOC_INVALID;
  456. p.paraloc[callerside].size:=def_cgsize(p.paratype.def);
  457. p.paraloc[callerside].alignment:=4;
  458. p.paraloc[callerside].reference.index:=NR_R2;
  459. { pattern is always uppercase'd }
  460. if s='D0' then
  461. p.paraloc[callerside].reference.offset:=0
  462. else if s='D1' then
  463. p.paraloc[callerside].reference.offset:=4
  464. else if s='D2' then
  465. p.paraloc[callerside].reference.offset:=8
  466. else if s='D3' then
  467. p.paraloc[callerside].reference.offset:=12
  468. else if s='D4' then
  469. p.paraloc[callerside].reference.offset:=16
  470. else if s='D5' then
  471. p.paraloc[callerside].reference.offset:=20
  472. else if s='D6' then
  473. p.paraloc[callerside].reference.offset:=24
  474. else if s='D7' then
  475. p.paraloc[callerside].reference.offset:=28
  476. else if s='A0' then
  477. p.paraloc[callerside].reference.offset:=32
  478. else if s='A1' then
  479. p.paraloc[callerside].reference.offset:=36
  480. else if s='A2' then
  481. p.paraloc[callerside].reference.offset:=40
  482. else if s='A3' then
  483. p.paraloc[callerside].reference.offset:=44
  484. else if s='A4' then
  485. p.paraloc[callerside].reference.offset:=48
  486. else if s='A5' then
  487. p.paraloc[callerside].reference.offset:=52
  488. { 'A6' (offset 56) is used by mossyscall as libbase, so API
  489. never passes parameters in it,
  490. Indeed, but this allows to declare libbase either explicitly
  491. or let the compiler insert it }
  492. else if s='A6' then
  493. p.paraloc[callerside].reference.offset:=56
  494. { 'A7' is the stack pointer on 68k, can't be overwritten
  495. by API calls, so it has no offset }
  496. else
  497. exit;
  498. p.paraloc[calleeside]:=p.paraloc[callerside];
  499. end;
  500. else
  501. internalerror(200404182);
  502. end;
  503. result:=true;
  504. end;
  505. begin
  506. paramanager:=tppcparamanager.create;
  507. end.
  508. {
  509. $Log$
  510. Revision 1.67 2004-07-19 19:15:50 florian
  511. * fixed funcret_paraloc writing in units
  512. Revision 1.66 2004/07/17 13:51:57 florian
  513. * function result location for syscalls on MOS hopefully correctly set now
  514. Revision 1.65 2004/07/09 21:45:24 jonas
  515. * fixed passing of fpu paras on the stack
  516. * fixed number of fpu parameters passed in registers
  517. * skip corresponding integer registers when using an fpu register for a
  518. parameter under the AIX abi
  519. Revision 1.64 2004/07/01 18:00:37 jonas
  520. * fix for broken TP-style constructor handling in the compiler
  521. Revision 1.63 2004/06/20 08:55:32 florian
  522. * logs truncated
  523. Revision 1.62 2004/05/01 22:05:02 florian
  524. + added lib support for Amiga/MorphOS syscalls
  525. Revision 1.61 2004/04/18 23:19:48 karoly
  526. * added correct offsets for PowerPC/MorphOS location support
  527. Revision 1.60 2004/04/18 15:22:24 florian
  528. + location support for arguments, currently PowerPC/MorphOS only
  529. Revision 1.59 2004/02/19 17:07:42 florian
  530. * fixed arg. area calculation
  531. Revision 1.58 2004/02/11 23:18:59 florian
  532. * fixed to compile the rtl again
  533. }