cpupara.pas 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  1. {
  2. Copyright (c) 2002 by Florian Klaempfl
  3. Generates the argument location information for 680x0
  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. cpubase,
  23. aasmdata,
  24. symconst,symtype,symdef,symsym,
  25. parabase,paramgr,cgbase,cgutils;
  26. type
  27. { Returns the location for the nr-st 32 Bit int parameter
  28. if every parameter before is an 32 Bit int parameter as well
  29. and if the calling conventions for the helper routines of the
  30. rtl are used.
  31. }
  32. tcpuparamanager = class(tparamanager)
  33. function ret_in_param(def:tdef;pd:tabstractprocdef):boolean;override;
  34. function param_use_paraloc(const cgpara:tcgpara):boolean;override;
  35. function create_paraloc_info(p : tabstractprocdef; side: tcallercallee):longint;override;
  36. function push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;override;
  37. function get_funcretloc(p : tabstractprocdef; side: tcallercallee; forcetempdef: tdef): tcgpara;override;
  38. procedure createtempparaloc(list: TAsmList;calloption : tproccalloption;parasym : tparavarsym;can_use_final_stack_loc : boolean;var cgpara:TCGPara);override;
  39. function create_varargs_paraloc_info(p : tabstractprocdef; varargspara:tvarargsparalist):longint;override;
  40. function parsefuncretloc(p : tabstractprocdef; const s : string) : boolean;override;
  41. function get_volatile_registers_int(calloption:tproccalloption):tcpuregisterset;override;
  42. function get_volatile_registers_address(calloption:tproccalloption):tcpuregisterset;override;
  43. function get_volatile_registers_fpu(calloption:tproccalloption):tcpuregisterset;override;
  44. private
  45. function parse_loc_string_to_register(var locreg: tregister; const s : string): boolean;
  46. function create_stdcall_paraloc_info(p : tabstractprocdef; side: tcallercallee; paras: tparalist;
  47. var cur_stack_offset: aword):longint;
  48. function create_register_paraloc_info(p : tabstractprocdef; side: tcallercallee; paras: tparalist;
  49. var cur_stack_offset: aword):longint;
  50. end;
  51. implementation
  52. uses
  53. verbose,
  54. globals,
  55. systems,
  56. cpuinfo,
  57. defutil,
  58. cutils,
  59. hlcgobj;
  60. const
  61. intparasupregs : array[0..1] of tsuperregister = (RS_D0,RS_D1);
  62. addrparasupregs : array[0..1] of tsuperregister = (RS_A0,RS_A1);
  63. floatparasupregs : array[0..1] of tsuperregister = (RS_FP0,RS_FP1);
  64. function tcpuparamanager.get_volatile_registers_int(calloption:tproccalloption):tcpuregisterset;
  65. begin
  66. { d0 and d1 are considered volatile }
  67. Result:=VOLATILE_INTREGISTERS;
  68. end;
  69. function tcpuparamanager.get_volatile_registers_address(calloption:tproccalloption):tcpuregisterset;
  70. begin
  71. { a0 and a1 are considered volatile }
  72. Result:=VOLATILE_ADDRESSREGISTERS;
  73. end;
  74. function tcpuparamanager.get_volatile_registers_fpu(calloption:tproccalloption):tcpuregisterset;
  75. begin
  76. { fp0 and fp1 are considered volatile }
  77. Result:=VOLATILE_FPUREGISTERS;
  78. end;
  79. function tcpuparamanager.param_use_paraloc(const cgpara:tcgpara):boolean;
  80. var
  81. paraloc : pcgparalocation;
  82. begin
  83. if not assigned(cgpara.location) then
  84. internalerror(200410102);
  85. result:=true;
  86. { All locations are LOC_REFERENCE }
  87. paraloc:=cgpara.location;
  88. while assigned(paraloc) do
  89. begin
  90. if (paraloc^.loc<>LOC_REFERENCE) then
  91. begin
  92. result:=false;
  93. exit;
  94. end;
  95. paraloc:=paraloc^.next;
  96. end;
  97. end;
  98. { TODO: copied from ppc cg, needs work}
  99. function tcpuparamanager.push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;
  100. begin
  101. result:=false;
  102. { var,out,constref always require address }
  103. if varspez in [vs_var,vs_out,vs_constref] then
  104. begin
  105. result:=true;
  106. exit;
  107. end;
  108. case def.typ of
  109. variantdef,
  110. formaldef :
  111. result:=true;
  112. recorddef:
  113. result:=(calloption in [pocall_register]) and
  114. (varspez in [vs_const]);
  115. arraydef:
  116. result:=(tarraydef(def).highrange>=tarraydef(def).lowrange) or
  117. is_open_array(def) or
  118. is_array_of_const(def) or
  119. is_array_constructor(def);
  120. objectdef :
  121. result:=is_object(def);
  122. setdef :
  123. result:=not is_smallset(def);
  124. stringdef :
  125. result:=tstringdef(def).stringtype in [st_shortstring,st_longstring];
  126. procvardef :
  127. { Handling of methods must match that of records }
  128. result:=false;
  129. end;
  130. end;
  131. function tcpuparamanager.ret_in_param(def:tdef;pd:tabstractprocdef):boolean;
  132. begin
  133. if handle_common_ret_in_param(def,pd,result) then
  134. exit;
  135. case def.typ of
  136. recorddef:
  137. if def.size in [1,2,4] then
  138. begin
  139. result:=false;
  140. exit;
  141. end;
  142. end;
  143. result:=inherited ret_in_param(def,pd);
  144. end;
  145. function tcpuparamanager.get_funcretloc(p : tabstractprocdef; side: tcallercallee; forcetempdef: tdef): tcgpara;
  146. var
  147. paraloc : pcgparalocation;
  148. retcgsize : tcgsize;
  149. begin
  150. if set_common_funcretloc_info(p,forcetempdef,retcgsize,result) then
  151. exit;
  152. { always use the whole 32 bit register when returning values }
  153. if (side=calleeside) and
  154. (result.intsize>0) and
  155. (result.intsize<sizeof(aint)) then
  156. begin
  157. result.def:=sinttype;
  158. result.intsize:=sizeof(aint);
  159. retcgsize:=OS_SINT;
  160. result.size:=retcgsize;
  161. end;
  162. paraloc:=result.add_location;
  163. { Return in FPU register? }
  164. if not (cs_fp_emulation in current_settings.moduleswitches) and
  165. not (current_settings.fputype=fpu_soft) and (result.def.typ=floatdef) then
  166. begin
  167. paraloc^.loc:=LOC_FPUREGISTER;
  168. paraloc^.register:=NR_FPU_RESULT_REG;
  169. paraloc^.size:=retcgsize;
  170. paraloc^.def:=result.def;
  171. end
  172. else
  173. { Return in register }
  174. begin
  175. if retcgsize in [OS_64,OS_S64] then
  176. begin
  177. { low 32bits }
  178. paraloc^.loc:=LOC_REGISTER;
  179. paraloc^.size:=OS_32;
  180. paraloc^.def:=u32inttype;
  181. if side=callerside then
  182. paraloc^.register:=NR_FUNCTION_RESULT64_LOW_REG
  183. else
  184. paraloc^.register:=NR_FUNCTION_RETURN64_LOW_REG;
  185. { high 32bits }
  186. paraloc:=result.add_location;
  187. paraloc^.loc:=LOC_REGISTER;
  188. paraloc^.size:=OS_32;
  189. paraloc^.def:=u32inttype;
  190. if side=calleeside then
  191. paraloc^.register:=NR_FUNCTION_RESULT64_HIGH_REG
  192. else
  193. paraloc^.register:=NR_FUNCTION_RETURN64_HIGH_REG;
  194. end
  195. else
  196. begin
  197. paraloc^.loc:=LOC_REGISTER;
  198. paraloc^.size:=retcgsize;
  199. paraloc^.def:=result.def;
  200. if side=callerside then
  201. paraloc^.register:=newreg(R_INTREGISTER,RS_FUNCTION_RESULT_REG,cgsize2subreg(R_INTREGISTER,retcgsize))
  202. else
  203. paraloc^.register:=newreg(R_INTREGISTER,RS_FUNCTION_RETURN_REG,cgsize2subreg(R_INTREGISTER,retcgsize));
  204. end;
  205. end;
  206. end;
  207. function tcpuparamanager.create_paraloc_info(p : tabstractprocdef; side: tcallercallee):longint;
  208. var
  209. cur_stack_offset: aword;
  210. begin
  211. cur_stack_offset:=0;
  212. case p.proccalloption of
  213. pocall_register :
  214. result:=create_register_paraloc_info(p,side,p.paras,cur_stack_offset);
  215. else
  216. result:=create_stdcall_paraloc_info(p,side,p.paras,cur_stack_offset);
  217. end;
  218. create_funcretloc_info(p,side);
  219. end;
  220. function tcpuparamanager.create_stdcall_paraloc_info(p : tabstractprocdef; side: tcallercallee; paras: tparalist;
  221. var cur_stack_offset: aword):longint;
  222. var
  223. paraloc : pcgparalocation;
  224. hp : tparavarsym;
  225. paracgsize : tcgsize;
  226. paralen : aint;
  227. paradef : tdef;
  228. i : longint;
  229. firstparaloc : boolean;
  230. begin
  231. result:=0;
  232. for i:=0 to paras.count-1 do
  233. begin
  234. hp:=tparavarsym(paras[i]);
  235. paradef:=hp.vardef;
  236. { syscall for AmigaOS can have already a paraloc set }
  237. if (vo_has_explicit_paraloc in hp.varoptions) then
  238. begin
  239. if not(vo_is_syscall_lib in hp.varoptions) then
  240. internalerror(200506051);
  241. continue;
  242. end;
  243. hp.paraloc[side].reset;
  244. { currently only support C-style array of const }
  245. if (p.proccalloption in cstylearrayofconst) and
  246. is_array_of_const(paradef) then
  247. begin
  248. paraloc:=hp.paraloc[side].add_location;
  249. { hack: the paraloc must be valid, but is not actually used }
  250. paraloc^.loc:=LOC_REGISTER;
  251. paraloc^.register:=NR_D0;
  252. paraloc^.size:=OS_ADDR;
  253. paraloc^.def:=voidpointertype;
  254. break;
  255. end;
  256. if push_addr_param(hp.varspez,paradef,p.proccalloption) then
  257. begin
  258. paradef:=cpointerdef.getreusable_no_free(paradef);
  259. paracgsize := OS_ADDR;
  260. paralen := tcgsize2size[OS_ADDR];
  261. end
  262. else
  263. begin
  264. if not is_special_array(paradef) then
  265. paralen:=paradef.size
  266. else
  267. paralen:=tcgsize2size[def_cgsize(paradef)];
  268. paracgsize:=def_cgsize(paradef);
  269. { for things like formaldef }
  270. if (paracgsize=OS_NO) and (paradef.typ<>recorddef) then
  271. begin
  272. paracgsize:=OS_ADDR;
  273. paralen := tcgsize2size[OS_ADDR];
  274. end;
  275. end;
  276. hp.paraloc[side].alignment:=target_info.stackalign; //std_param_align;
  277. hp.paraloc[side].size:=paracgsize;
  278. hp.paraloc[side].intsize:=paralen;
  279. hp.paraloc[side].def:=paradef;
  280. if (paralen = 0) then
  281. if (paradef.typ = recorddef) then
  282. begin
  283. paraloc:=hp.paraloc[side].add_location;
  284. paraloc^.loc := LOC_VOID;
  285. end
  286. else
  287. internalerror(200506052);
  288. firstparaloc:=true;
  289. { can become < 0 for e.g. 3-byte records }
  290. while (paralen > 0) do
  291. begin
  292. paraloc:=hp.paraloc[side].add_location;
  293. paraloc^.loc:=LOC_REFERENCE;
  294. paraloc^.def:=get_paraloc_def(paradef,paralen,firstparaloc);
  295. if (not (cs_fp_emulation in current_settings.moduleswitches)) and
  296. (paradef.typ=floatdef) then
  297. paraloc^.size:=int_float_cgsize(paralen)
  298. else
  299. paraloc^.size:=int_cgsize(paralen);
  300. paraloc^.reference.offset:=cur_stack_offset;
  301. if (side = callerside) then
  302. paraloc^.reference.index:=NR_STACK_POINTER_REG
  303. else
  304. begin
  305. paraloc^.reference.index:=NR_FRAME_POINTER_REG;
  306. inc(paraloc^.reference.offset,target_info.first_parm_offset);
  307. { M68K is a big-endian target }
  308. if (paralen<target_info.stackalign{tcgsize2size[OS_INT]}) then
  309. inc(paraloc^.reference.offset,target_info.stackalign-paralen);
  310. end;
  311. inc(cur_stack_offset,align(paralen,target_info.stackalign));
  312. paralen := 0;
  313. firstparaloc:=false;
  314. end;
  315. end;
  316. result:=cur_stack_offset;
  317. end;
  318. function tcpuparamanager.create_register_paraloc_info(p : tabstractprocdef; side: tcallercallee;paras:tparalist;
  319. var cur_stack_offset: aword): longint;
  320. var
  321. hp : tparavarsym;
  322. paradef : tdef;
  323. paraloc : pcgparalocation;
  324. paracgsize : tcgsize;
  325. i : integer;
  326. l,
  327. paralen,
  328. parareg: longint;
  329. addrparareg: longint;
  330. floatparareg: longint;
  331. varalign : longint;
  332. paraalign : shortint;
  333. pass : byte;
  334. firstparaloc,
  335. pushaddr : boolean;
  336. rt: tregistertype;
  337. begin
  338. result:=0;
  339. parareg:=0;
  340. addrparareg:=0;
  341. floatparareg:=0;
  342. if paras.count=0 then
  343. exit;
  344. paraalign:=get_para_align(p.proccalloption);
  345. { clean up here so we can later detect properly if a parameter has been
  346. assigned or not
  347. }
  348. for i:=0 to paras.count-1 do
  349. tparavarsym(paras[i]).paraloc[side].reset;
  350. { Register parameters are assigned from left to right,
  351. stack parameters from right to left so assign first the
  352. register parameters in a first pass, in the second
  353. pass all unhandled parameters are done }
  354. for pass:=1 to 2 do
  355. begin
  356. if pass=1 then
  357. i:=0
  358. else
  359. i:=paras.count-1;
  360. while true do
  361. begin
  362. hp:=tparavarsym(paras[i]);
  363. paradef:=hp.vardef;
  364. if not(assigned(hp.paraloc[side].location)) then
  365. begin
  366. pushaddr:=push_addr_param(hp.varspez,hp.vardef,p.proccalloption);
  367. if pushaddr then
  368. begin
  369. paralen:=sizeof(aint);
  370. paracgsize:=OS_ADDR;
  371. paradef:=cpointerdef.getreusable_no_free(paradef);
  372. end
  373. else
  374. begin
  375. paralen:=push_size(hp.varspez,hp.vardef,p.proccalloption);
  376. paracgsize:=def_cgsize(hp.vardef);
  377. end;
  378. hp.paraloc[side].size:=paracgsize;
  379. hp.paraloc[side].intsize:=paralen;
  380. hp.paraloc[side].Alignment:=paraalign;
  381. hp.paraloc[side].def:=paradef;
  382. {
  383. In case of po_delphi_nested_cc, the parent frame pointer
  384. is also always passed on the stack.
  385. }
  386. rt:=chlcgobj.def2regtyp(paradef);
  387. if (rt=R_FPUREGISTER) and
  388. (floatparareg<=high(floatparasupregs)) and
  389. (not pushaddr) then
  390. begin
  391. if pass=1 then
  392. begin
  393. paraloc:=hp.paraloc[side].add_location;
  394. paraloc^.size:=paracgsize;
  395. paraloc^.def:=paradef;
  396. paraloc^.loc:=LOC_FPUREGISTER;
  397. paraloc^.register:=newreg(R_FPUREGISTER,floatparasupregs[floatparareg],R_SUBWHOLE);
  398. inc(floatparareg);
  399. end;
  400. end
  401. else
  402. if (((rt=R_INTREGISTER) and (parareg<=high(intparasupregs))) or
  403. ((rt=R_ADDRESSREGISTER) and (addrparareg<=high(addrparasupregs)))) and
  404. (paralen<=sizeof(aint)) and
  405. (not(hp.vardef.typ in [floatdef,recorddef,arraydef]) or
  406. pushaddr or
  407. is_dynamic_array(hp.vardef)) and
  408. (not(vo_is_parentfp in hp.varoptions) or
  409. not(po_delphi_nested_cc in p.procoptions)) then
  410. begin
  411. if pass=1 then
  412. begin
  413. paraloc:=hp.paraloc[side].add_location;
  414. paraloc^.size:=paracgsize;
  415. paraloc^.def:=paradef;
  416. paraloc^.loc:=LOC_REGISTER;
  417. if (rt=R_ADDRESSREGISTER) and (addrparareg<=high(addrparasupregs)) then
  418. begin
  419. paraloc^.register:=newreg(R_ADDRESSREGISTER,addrparasupregs[addrparareg],R_SUBWHOLE);
  420. inc(addrparareg);
  421. end
  422. else
  423. if (rt=R_INTREGISTER) and (parareg<=high(intparasupregs)) then
  424. begin
  425. paraloc^.register:=newreg(R_INTREGISTER,intparasupregs[parareg],cgsize2subreg(R_INTREGISTER,paracgsize));
  426. inc(parareg);
  427. end
  428. else
  429. internalerror(2016051801);
  430. end;
  431. end
  432. else
  433. if pass=2 then
  434. begin
  435. { Copy to stack? }
  436. if (use_fixed_stack) or
  437. (paracgsize=OS_NO) then
  438. begin
  439. paraloc:=hp.paraloc[side].add_location;
  440. paraloc^.loc:=LOC_REFERENCE;
  441. paraloc^.size:=paracgsize;
  442. paraloc^.def:=paradef;
  443. if side=callerside then
  444. paraloc^.reference.index:=NR_STACK_POINTER_REG
  445. else
  446. paraloc^.reference.index:=NR_FRAME_POINTER_REG;
  447. varalign:=used_align(size_2_align(paralen),paraalign,paraalign);
  448. paraloc^.reference.offset:=cur_stack_offset;
  449. if side=calleeside then
  450. inc(paraloc^.reference.offset,target_info.first_parm_offset);
  451. cur_stack_offset:=align(cur_stack_offset+paralen,varalign);
  452. end
  453. else
  454. begin
  455. if paralen=0 then
  456. internalerror(200501163);
  457. firstparaloc:=true;
  458. while (paralen>0) do
  459. begin
  460. paraloc:=hp.paraloc[side].add_location;
  461. paraloc^.loc:=LOC_REFERENCE;
  462. { Extended and double need a single location }
  463. if (paracgsize in [OS_F64,OS_F32]) then
  464. begin
  465. paraloc^.size:=paracgsize;
  466. paraloc^.def:=paradef;
  467. l:=paralen;
  468. end
  469. else
  470. begin
  471. { We can allocate at maximum 32 bits per location }
  472. if paralen>sizeof(aint) then
  473. begin
  474. l:=sizeof(aint);
  475. paraloc^.def:=uinttype;
  476. end
  477. else
  478. begin
  479. l:=paralen;
  480. paraloc^.def:=get_paraloc_def(paradef,l,firstparaloc);
  481. end;
  482. paraloc^.size:=int_cgsize(l);
  483. end;
  484. if side=callerside then
  485. paraloc^.reference.index:=NR_STACK_POINTER_REG
  486. else
  487. paraloc^.reference.index:=NR_FRAME_POINTER_REG;
  488. varalign:=used_align(size_2_align(l),paraalign,paraalign);
  489. paraloc^.reference.offset:=cur_stack_offset;
  490. { M68K is a big-endian target }
  491. if (paralen<tcgsize2size[OS_INT]) then
  492. inc(paraloc^.reference.offset,4-paralen);
  493. if side=calleeside then
  494. inc(paraloc^.reference.offset,target_info.first_parm_offset);
  495. cur_stack_offset:=align(cur_stack_offset+l,varalign);
  496. dec(paralen,l);
  497. firstparaloc:=false;
  498. end;
  499. end;
  500. end;
  501. end;
  502. case pass of
  503. 1:
  504. begin
  505. if i=paras.count-1 then
  506. break;
  507. inc(i);
  508. end;
  509. 2:
  510. begin
  511. if i=0 then
  512. break;
  513. dec(i);
  514. end;
  515. end;
  516. end;
  517. end;
  518. result:=cur_stack_offset;
  519. end;
  520. function tcpuparamanager.parse_loc_string_to_register(var locreg: tregister; const s : string): boolean;
  521. begin
  522. locreg:=std_regnum_search(lower(s));
  523. result:=(locreg <> NR_NO) and (locreg <> NR_SP);
  524. end;
  525. function tcpuparamanager.parsefuncretloc(p : tabstractprocdef; const s : string) : boolean;
  526. begin
  527. case target_info.system of
  528. system_m68k_amiga:
  529. result:=parse_loc_string_to_register(p.exp_funcretloc, s);
  530. else
  531. internalerror(2005121801);
  532. end;
  533. end;
  534. procedure tcpuparamanager.createtempparaloc(list: TAsmList;calloption : tproccalloption;parasym : tparavarsym;can_use_final_stack_loc : boolean;var cgpara:TCGPara);
  535. begin
  536. { Never a need for temps when value is pushed (calls inside parameters
  537. will simply allocate even more stack space for their parameters) }
  538. if not(use_fixed_stack) then
  539. can_use_final_stack_loc:=true;
  540. inherited createtempparaloc(list,calloption,parasym,can_use_final_stack_loc,cgpara);
  541. end;
  542. function tcpuparamanager.create_varargs_paraloc_info(p : tabstractprocdef; varargspara:tvarargsparalist):longint;
  543. var
  544. cur_stack_offset: aword;
  545. begin
  546. cur_stack_offset:=0;
  547. result:=create_stdcall_paraloc_info(p,callerside,p.paras,cur_stack_offset);
  548. if (p.proccalloption in cstylearrayofconst) then
  549. { just continue loading the parameters in the registers }
  550. result:=create_stdcall_paraloc_info(p,callerside,varargspara,cur_stack_offset)
  551. else
  552. internalerror(200410231);
  553. end;
  554. begin
  555. paramanager:=tcpuparamanager.create;
  556. end.