cpupara.pas 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  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. private
  38. procedure init_values(var curintreg, curfloatreg, curmmreg: tsuperregister; var cur_stack_offset: aword);
  39. procedure create_funcret_paraloc_info(p : tabstractprocdef; side: tcallercallee);
  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 : 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. hp:=firstpara;
  268. while assigned(hp) do
  269. begin
  270. { currently only support C-style array of const }
  271. if (p.proccalloption in [pocall_cdecl,pocall_cppdecl]) and
  272. is_array_of_const(hp.paratype.def) then
  273. begin
  274. { hack: the paraloc must be valid, but is not actually used }
  275. hp.paraloc[side].loc := LOC_REGISTER;
  276. hp.paraloc[side].lochigh := LOC_INVALID;
  277. hp.paraloc[side].register := NR_R0;
  278. hp.paraloc[side].size := OS_ADDR;
  279. break;
  280. end;
  281. if (hp.paratyp in [vs_var,vs_out]) then
  282. begin
  283. paradef := voidpointertype.def;
  284. loc := LOC_REGISTER;
  285. end
  286. else
  287. begin
  288. paradef := hp.paratype.def;
  289. loc:=getparaloc(paradef);
  290. end;
  291. { make sure all alignment bytes are 0 as well }
  292. fillchar(paraloc,sizeof(paraloc),0);
  293. paraloc.alignment:= std_param_align;
  294. paraloc.lochigh:=LOC_INVALID;
  295. case loc of
  296. LOC_REGISTER:
  297. begin
  298. paraloc.size := def_cgsize(paradef);
  299. { for things like formaldef }
  300. if paraloc.size = OS_NO then
  301. paraloc.size := OS_ADDR;
  302. is_64bit := paraloc.size in [OS_64,OS_S64];
  303. if nextintreg<=(RS_R10-ord(is_64bit)) then
  304. begin
  305. paraloc.loc:=LOC_REGISTER;
  306. if is_64bit then
  307. begin
  308. if odd(nextintreg-RS_R3) and (target_info.abi=abi_powerpc_sysv) Then
  309. inc(nextintreg);
  310. paraloc.registerhigh:=newreg(R_INTREGISTER,nextintreg,R_SUBNONE);
  311. paraloc.lochigh:=LOC_REGISTER;
  312. inc(nextintreg);
  313. if target_info.abi=abi_powerpc_aix then
  314. inc(stack_offset,4);
  315. end;
  316. paraloc.registerlow:=newreg(R_INTREGISTER,nextintreg,R_SUBNONE);
  317. inc(nextintreg);
  318. if target_info.abi=abi_powerpc_aix then
  319. inc(stack_offset,4);
  320. end
  321. else
  322. begin
  323. nextintreg:=RS_R11;
  324. paraloc.loc:=LOC_REFERENCE;
  325. paraloc.reference.index:=NR_STACK_POINTER_REG;
  326. paraloc.reference.offset:=stack_offset;
  327. if not is_64bit then
  328. inc(stack_offset,4)
  329. else
  330. inc(stack_offset,8);
  331. end;
  332. end;
  333. LOC_FPUREGISTER:
  334. begin
  335. paraloc.size:=def_cgsize(paradef);
  336. if nextfloatreg<=RS_F10 then
  337. begin
  338. paraloc.loc:=LOC_FPUREGISTER;
  339. paraloc.register:=newreg(R_FPUREGISTER,nextfloatreg,R_SUBWHOLE);
  340. inc(nextfloatreg);
  341. if target_info.abi=abi_powerpc_aix then
  342. inc(stack_offset,8);
  343. end
  344. else
  345. begin
  346. {!!!!!!!}
  347. paraloc.size:=def_cgsize(paradef);
  348. internalerror(2002071004);
  349. end;
  350. end;
  351. LOC_REFERENCE:
  352. begin
  353. paraloc.size:=OS_ADDR;
  354. if push_addr_param(hp.paratyp,paradef,p.proccalloption) or
  355. is_open_array(paradef) or
  356. is_array_of_const(paradef) then
  357. assignintreg
  358. else
  359. begin
  360. paraloc.loc:=LOC_REFERENCE;
  361. paraloc.reference.index:=NR_STACK_POINTER_REG;
  362. paraloc.reference.offset:=stack_offset;
  363. inc(stack_offset,hp.paratype.def.size);
  364. end;
  365. end;
  366. else
  367. internalerror(2002071002);
  368. end;
  369. {
  370. this is filled in in ncgutil
  371. if side = calleeside then
  372. begin
  373. if (paraloc.loc = LOC_REFERENCE) then
  374. begin
  375. if (current_procinfo.procdef <> p) then
  376. internalerror(2003112201);
  377. inc(paraloc.reference.offset,current_procinfo.calc_stackframe_size);
  378. end;
  379. end;
  380. }
  381. hp.paraloc[side]:=paraloc;
  382. hp:=tparaitem(hp.next);
  383. end;
  384. curintreg:=nextintreg;
  385. curfloatreg:=nextfloatreg;
  386. curmmreg:=nextmmreg;
  387. cur_stack_offset:=stack_offset;
  388. result:=cur_stack_offset;
  389. end;
  390. function tppcparamanager.create_varargs_paraloc_info(p : tabstractprocdef; varargspara:tvarargspara):longint;
  391. var
  392. cur_stack_offset: aword;
  393. parasize, l: longint;
  394. curintreg, firstfloatreg, curfloatreg, curmmreg: tsuperregister;
  395. hp: tparaitem;
  396. paraloc: tparalocation;
  397. begin
  398. init_values(curintreg,curfloatreg,curmmreg,cur_stack_offset);
  399. firstfloatreg:=curfloatreg;
  400. result := create_paraloc_info_intern(p,callerside,tparaitem(p.para.first),curintreg,curfloatreg,curmmreg,cur_stack_offset);
  401. if (p.proccalloption in [pocall_cdecl,pocall_cppdecl]) then
  402. { just continue loading the parameters in the registers }
  403. result := create_paraloc_info_intern(p,callerside,tparaitem(varargspara.first),curintreg,curfloatreg,curmmreg,cur_stack_offset)
  404. else
  405. begin
  406. hp := tparaitem(varargspara.first);
  407. parasize := cur_stack_offset;
  408. while assigned(hp) do
  409. begin
  410. paraloc.size:=def_cgsize(hp.paratype.def);
  411. paraloc.lochigh:=LOC_INVALID;
  412. paraloc.loc:=LOC_REFERENCE;
  413. paraloc.alignment:=4;
  414. paraloc.reference.index:=NR_STACK_POINTER_REG;
  415. l:=push_size(hp.paratyp,hp.paratype.def,p.proccalloption);
  416. paraloc.reference.offset:=parasize;
  417. parasize:=parasize+l;
  418. hp.paraloc[callerside]:=paraloc;
  419. hp:=tparaitem(hp.next);
  420. end;
  421. result := parasize;
  422. end;
  423. if curfloatreg<>firstfloatreg then
  424. include(varargspara.varargsinfo,va_uses_float_reg);
  425. end;
  426. function tppcparamanager.parseparaloc(p : tparaitem;const s : string) : boolean;
  427. begin
  428. result:=false;
  429. case target_info.system of
  430. system_powerpc_morphos:
  431. begin
  432. p.paraloc[callerside].loc:=LOC_REFERENCE;
  433. p.paraloc[callerside].lochigh:=LOC_INVALID;
  434. p.paraloc[callerside].size:=def_cgsize(p.paratype.def);
  435. p.paraloc[callerside].alignment:=4;
  436. p.paraloc[callerside].reference.index:=NR_R2;
  437. { pattern is always uppercase'd }
  438. if s='D0' then
  439. p.paraloc[callerside].reference.offset:=0
  440. else if s='D1' then
  441. p.paraloc[callerside].reference.offset:=4
  442. else if s='D2' then
  443. p.paraloc[callerside].reference.offset:=8
  444. else if s='D3' then
  445. p.paraloc[callerside].reference.offset:=12
  446. else if s='D4' then
  447. p.paraloc[callerside].reference.offset:=16
  448. else if s='D5' then
  449. p.paraloc[callerside].reference.offset:=20
  450. else if s='D6' then
  451. p.paraloc[callerside].reference.offset:=24
  452. else if s='D7' then
  453. p.paraloc[callerside].reference.offset:=28
  454. else if s='A0' then
  455. p.paraloc[callerside].reference.offset:=32
  456. else if s='A1' then
  457. p.paraloc[callerside].reference.offset:=36
  458. else if s='A2' then
  459. p.paraloc[callerside].reference.offset:=40
  460. else if s='A3' then
  461. p.paraloc[callerside].reference.offset:=44
  462. else if s='A4' then
  463. p.paraloc[callerside].reference.offset:=48
  464. else if s='A5' then
  465. p.paraloc[callerside].reference.offset:=52
  466. { 'A6' (offset 56) is used by mossyscall as libbase, so API
  467. never passes parameters in it,
  468. Indeed, but this allows to declare libbase either explicitly
  469. or let the compiler insert it }
  470. else if s='A6' then
  471. p.paraloc[callerside].reference.offset:=56
  472. { 'A7' is the stack pointer on 68k, can't be overwritten
  473. by API calls, so it has no offset }
  474. else
  475. exit;
  476. p.paraloc[calleeside]:=p.paraloc[callerside];
  477. end;
  478. else
  479. internalerror(200404182);
  480. end;
  481. result:=true;
  482. end;
  483. begin
  484. paramanager:=tppcparamanager.create;
  485. end.
  486. {
  487. $Log$
  488. Revision 1.64 2004-07-01 18:00:37 jonas
  489. * fix for broken TP-style constructor handling in the compiler
  490. Revision 1.63 2004/06/20 08:55:32 florian
  491. * logs truncated
  492. Revision 1.62 2004/05/01 22:05:02 florian
  493. + added lib support for Amiga/MorphOS syscalls
  494. Revision 1.61 2004/04/18 23:19:48 karoly
  495. * added correct offsets for PowerPC/MorphOS location support
  496. Revision 1.60 2004/04/18 15:22:24 florian
  497. + location support for arguments, currently PowerPC/MorphOS only
  498. Revision 1.59 2004/02/19 17:07:42 florian
  499. * fixed arg. area calculation
  500. Revision 1.58 2004/02/11 23:18:59 florian
  501. * fixed to compile the rtl again
  502. }