cpupara.pas 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. {
  2. Copyright (c) 2002 by Florian Klaempfl
  3. Risc-V32 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. tcpuparamanager = class(tparamanager)
  28. function get_volatile_registers_int(calloption : tproccalloption):tcpuregisterset;override;
  29. function get_volatile_registers_fpu(calloption : tproccalloption):tcpuregisterset;override;
  30. function push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;override;
  31. procedure getintparaloc(list: TAsmList; pd : tabstractprocdef; nr : longint; var cgpara : tcgpara);override;
  32. function create_paraloc_info(p : tabstractprocdef; side: tcallercallee):longint;override;
  33. function create_varargs_paraloc_info(p : tabstractprocdef; side: tcallercallee; varargspara:tvarargsparalist):longint;override;
  34. function get_funcretloc(p : tabstractprocdef; side: tcallercallee; forcetempdef: tdef): tcgpara;override;
  35. private
  36. procedure init_values(var curintreg, curfloatreg, curmmreg: tsuperregister; var cur_stack_offset: aword);
  37. function create_paraloc_info_intern(p : tabstractprocdef; side: tcallercallee; paras:tparalist;
  38. var curintreg, curfloatreg, curmmreg: tsuperregister; var cur_stack_offset: aword; varargsparas: boolean):longint;
  39. end;
  40. implementation
  41. uses
  42. cpuinfo,globals,
  43. verbose,systems,
  44. defutil,symtable,
  45. procinfo,cpupi;
  46. function tcpuparamanager.get_volatile_registers_int(calloption : tproccalloption):tcpuregisterset;
  47. begin
  48. result:=[RS_X0..RS_X31]-[RS_X2,RS_X8..RS_X9,RS_X18..RS_X27];
  49. end;
  50. function tcpuparamanager.get_volatile_registers_fpu(calloption : tproccalloption):tcpuregisterset;
  51. begin
  52. result:=[RS_F0..RS_F31]-[RS_F8..RS_F9,RS_F18..RS_F27];
  53. end;
  54. procedure tcpuparamanager.getintparaloc(list: TAsmList; pd : tabstractprocdef; nr : longint; var cgpara : tcgpara);
  55. var
  56. paraloc : pcgparalocation;
  57. psym : tparavarsym;
  58. pdef : tdef;
  59. begin
  60. psym:=tparavarsym(pd.paras[nr-1]);
  61. pdef:=psym.vardef;
  62. if push_addr_param(psym.varspez,pdef,pd.proccalloption) then
  63. pdef:=cpointerdef.getreusable_no_free(pdef);
  64. cgpara.reset;
  65. cgpara.size:=def_cgsize(pdef);
  66. cgpara.intsize:=tcgsize2size[cgpara.size];
  67. cgpara.alignment:=get_para_align(pd.proccalloption);
  68. cgpara.def:=pdef;
  69. paraloc:=cgpara.add_location;
  70. with paraloc^ do
  71. begin
  72. size:=def_cgsize(pdef);
  73. def:=pdef;
  74. if (nr<=8) then
  75. begin
  76. if nr=0 then
  77. internalerror(200309271);
  78. loc:=LOC_REGISTER;
  79. register:=newreg(R_INTREGISTER,RS_X10+nr,R_SUBWHOLE);
  80. end
  81. else
  82. begin
  83. loc:=LOC_REFERENCE;
  84. paraloc^.reference.index:=NR_STACK_POINTER_REG;
  85. reference.offset:=sizeof(pint)*(nr);
  86. end;
  87. end;
  88. end;
  89. function getparaloc(p : tdef) : tcgloc;
  90. begin
  91. { Later, the LOC_REFERENCE is in most cases changed into LOC_REGISTER
  92. if push_addr_param for the def is true
  93. }
  94. case p.typ of
  95. orddef:
  96. result:=LOC_REGISTER;
  97. floatdef:
  98. if (cs_fp_emulation in current_settings.moduleswitches) or
  99. (current_settings.fputype in [fpu_soft]) then
  100. result := LOC_REGISTER
  101. else
  102. result := LOC_FPUREGISTER;
  103. enumdef:
  104. result:=LOC_REGISTER;
  105. pointerdef:
  106. result:=LOC_REGISTER;
  107. formaldef:
  108. result:=LOC_REGISTER;
  109. classrefdef:
  110. result:=LOC_REGISTER;
  111. procvardef:
  112. if (p.size = sizeof(pint)) then
  113. result:=LOC_REGISTER
  114. else
  115. result:=LOC_REFERENCE;
  116. recorddef:
  117. if (p.size > 4) then
  118. result:=LOC_REFERENCE
  119. else
  120. result:=LOC_REGISTER;
  121. objectdef:
  122. if is_object(p) then
  123. result:=LOC_REFERENCE
  124. else
  125. result:=LOC_REGISTER;
  126. stringdef:
  127. if is_shortstring(p) or is_longstring(p) then
  128. result:=LOC_REFERENCE
  129. else
  130. result:=LOC_REGISTER;
  131. filedef:
  132. result:=LOC_REGISTER;
  133. arraydef:
  134. if is_dynamic_array(p) then
  135. getparaloc:=LOC_REGISTER
  136. else
  137. result:=LOC_REFERENCE;
  138. setdef:
  139. if is_smallset(p) then
  140. result:=LOC_REGISTER
  141. else
  142. result:=LOC_REFERENCE;
  143. variantdef:
  144. result:=LOC_REFERENCE;
  145. { avoid problems with errornous definitions }
  146. errordef:
  147. result:=LOC_REGISTER;
  148. else
  149. internalerror(2002071001);
  150. end;
  151. end;
  152. function tcpuparamanager.push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;
  153. begin
  154. result:=false;
  155. { var,out,constref always require address }
  156. if varspez in [vs_var,vs_out,vs_constref] then
  157. begin
  158. result:=true;
  159. exit;
  160. end;
  161. case def.typ of
  162. variantdef,
  163. formaldef :
  164. result:=true;
  165. { regular procvars must be passed by value, because you cannot pass
  166. the address of a local stack location when calling e.g.
  167. pthread_create with the address of a function (first of all it
  168. expects the address of the function to execute and not the address
  169. of a memory location containing that address, and secondly if you
  170. first store the address on the stack and then pass the address of
  171. this stack location, then this stack location may no longer be
  172. valid when the newly started thread accesses it.
  173. However, for "procedure of object" we must use the same calling
  174. convention as for "8 byte record" due to the need for
  175. interchangeability with the TMethod record type.
  176. }
  177. procvardef :
  178. result:=
  179. (def.size <> sizeof(pint));
  180. recorddef :
  181. result := (def.size > 8) or (varspez = vs_const);
  182. arraydef:
  183. result:=(tarraydef(def).highrange>=tarraydef(def).lowrange) or
  184. is_open_array(def) or
  185. is_array_of_const(def) or
  186. is_array_constructor(def);
  187. objectdef :
  188. result:=is_object(def);
  189. setdef :
  190. result:=not is_smallset(def);
  191. stringdef :
  192. result:=tstringdef(def).stringtype in [st_shortstring,st_longstring];
  193. end;
  194. end;
  195. procedure tcpuparamanager.init_values(var curintreg, curfloatreg, curmmreg: tsuperregister; var cur_stack_offset: aword);
  196. begin
  197. cur_stack_offset:=0;
  198. curintreg:=RS_X10;
  199. curfloatreg:=RS_F10;
  200. curmmreg:=RS_NO;
  201. end;
  202. function tcpuparamanager.get_funcretloc(p : tabstractprocdef; side: tcallercallee; forcetempdef: tdef): tcgpara;
  203. var
  204. paraloc : pcgparalocation;
  205. retcgsize : tcgsize;
  206. begin
  207. if set_common_funcretloc_info(p,forcetempdef,retcgsize,result) then
  208. exit;
  209. paraloc:=result.add_location;
  210. { Return in FPU register? }
  211. if result.def.typ=floatdef then
  212. begin
  213. paraloc^.loc:=LOC_FPUREGISTER;
  214. paraloc^.register:=NR_FPU_RESULT_REG;
  215. paraloc^.size:=retcgsize;
  216. paraloc^.def:=result.def;
  217. end
  218. else
  219. { Return in register }
  220. begin
  221. if retcgsize in [OS_64,OS_S64] then
  222. begin
  223. { low 32bits }
  224. paraloc^.loc:=LOC_REGISTER;
  225. if side=callerside then
  226. paraloc^.register:=NR_FUNCTION_RESULT64_HIGH_REG
  227. else
  228. paraloc^.register:=NR_FUNCTION_RETURN64_HIGH_REG;
  229. paraloc^.size:=OS_32;
  230. paraloc^.def:=u32inttype;
  231. { high 32bits }
  232. paraloc:=result.add_location;
  233. paraloc^.loc:=LOC_REGISTER;
  234. if side=callerside then
  235. paraloc^.register:=NR_FUNCTION_RESULT64_LOW_REG
  236. else
  237. paraloc^.register:=NR_FUNCTION_RETURN64_LOW_REG;
  238. paraloc^.size:=OS_32;
  239. paraloc^.def:=u32inttype;
  240. end
  241. else
  242. begin
  243. paraloc^.loc:=LOC_REGISTER;
  244. if side=callerside then
  245. paraloc^.register:=newreg(R_INTREGISTER,RS_FUNCTION_RESULT_REG,cgsize2subreg(R_INTREGISTER,retcgsize))
  246. else
  247. paraloc^.register:=newreg(R_INTREGISTER,RS_FUNCTION_RETURN_REG,cgsize2subreg(R_INTREGISTER,retcgsize));
  248. paraloc^.size:=retcgsize;
  249. paraloc^.def:=result.def;
  250. end;
  251. end;
  252. end;
  253. function tcpuparamanager.create_paraloc_info(p : tabstractprocdef; side: tcallercallee):longint;
  254. var
  255. cur_stack_offset: aword;
  256. curintreg, curfloatreg, curmmreg: tsuperregister;
  257. begin
  258. init_values(curintreg,curfloatreg,curmmreg,cur_stack_offset);
  259. result := create_paraloc_info_intern(p,side,p.paras,curintreg,curfloatreg,curmmreg,cur_stack_offset,false);
  260. create_funcretloc_info(p,side);
  261. end;
  262. function tcpuparamanager.create_paraloc_info_intern(p : tabstractprocdef; side: tcallercallee; paras:tparalist;
  263. var curintreg, curfloatreg, curmmreg: tsuperregister; var cur_stack_offset: aword; varargsparas: boolean):longint;
  264. var
  265. stack_offset: longint;
  266. paralen: aint;
  267. nextintreg,nextfloatreg,nextmmreg, maxfpureg : tsuperregister;
  268. locdef,
  269. fdef,
  270. paradef : tdef;
  271. paraloc : pcgparalocation;
  272. i : integer;
  273. hp : tparavarsym;
  274. loc : tcgloc;
  275. paracgsize: tcgsize;
  276. firstparaloc: boolean;
  277. begin
  278. {$ifdef extdebug}
  279. if po_explicitparaloc in p.procoptions then
  280. internalerror(200411141);
  281. {$endif extdebug}
  282. result:=0;
  283. nextintreg := curintreg;
  284. nextfloatreg := curfloatreg;
  285. nextmmreg := curmmreg;
  286. stack_offset := cur_stack_offset;
  287. maxfpureg := RS_F17;
  288. for i:=0 to paras.count-1 do
  289. begin
  290. hp:=tparavarsym(paras[i]);
  291. paradef := hp.vardef;
  292. { Syscall for Morphos can have already a paraloc set }
  293. if (vo_has_explicit_paraloc in hp.varoptions) then
  294. begin
  295. if not(vo_is_syscall_lib in hp.varoptions) then
  296. internalerror(200412153);
  297. continue;
  298. end;
  299. hp.paraloc[side].reset;
  300. { currently only support C-style array of const }
  301. if (p.proccalloption in cstylearrayofconst) and
  302. is_array_of_const(paradef) then
  303. begin
  304. paraloc:=hp.paraloc[side].add_location;
  305. { hack: the paraloc must be valid, but is not actually used }
  306. paraloc^.loc := LOC_REGISTER;
  307. paraloc^.register := NR_X0;
  308. paraloc^.size := OS_ADDR;
  309. paraloc^.def:=voidpointertype;
  310. break;
  311. end;
  312. if push_addr_param(hp.varspez,paradef,p.proccalloption) then
  313. begin
  314. paradef:=cpointerdef.getreusable_no_free(paradef);
  315. loc:=LOC_REGISTER;
  316. paracgsize := OS_ADDR;
  317. paralen := tcgsize2size[OS_ADDR];
  318. end
  319. else
  320. begin
  321. if not is_special_array(paradef) then
  322. paralen := paradef.size
  323. else
  324. paralen := tcgsize2size[def_cgsize(paradef)];
  325. paracgsize:=def_cgsize(paradef);
  326. { for things like formaldef }
  327. if (paracgsize=OS_NO) then
  328. begin
  329. paracgsize:=OS_ADDR;
  330. paralen := tcgsize2size[OS_ADDR];
  331. end;
  332. end;
  333. loc := getparaloc(paradef);
  334. hp.paraloc[side].alignment:=std_param_align;
  335. hp.paraloc[side].size:=paracgsize;
  336. hp.paraloc[side].intsize:=paralen;
  337. hp.paraloc[side].def:=paradef;
  338. {$ifndef cpu64bitaddr}
  339. if (is_64bit(paradef)) and
  340. odd(nextintreg-RS_X10) then
  341. inc(nextintreg);
  342. {$endif not cpu64bitaddr}
  343. if (paralen = 0) then
  344. if (paradef.typ = recorddef) then
  345. begin
  346. paraloc:=hp.paraloc[side].add_location;
  347. paraloc^.loc := LOC_VOID;
  348. end
  349. else
  350. internalerror(2005011310);
  351. locdef:=paradef;
  352. firstparaloc:=true;
  353. { can become < 0 for e.g. 3-byte records }
  354. while (paralen > 0) do
  355. begin
  356. paraloc:=hp.paraloc[side].add_location;
  357. { In case of po_delphi_nested_cc, the parent frame pointer
  358. is always passed on the stack. }
  359. if (loc = LOC_REGISTER) and
  360. (nextintreg <= RS_X17) and
  361. (not(vo_is_parentfp in hp.varoptions) or
  362. not(po_delphi_nested_cc in p.procoptions)) then
  363. begin
  364. paraloc^.loc := loc;
  365. { make sure we don't lose whether or not the type is signed }
  366. if (paradef.typ<>orddef) then
  367. begin
  368. paracgsize:=int_cgsize(paralen);
  369. locdef:=get_paraloc_def(paradef,paralen,firstparaloc);
  370. end;
  371. if (paracgsize in [OS_NO,OS_64,OS_S64,OS_128,OS_S128]) then
  372. begin
  373. paraloc^.size:=OS_INT;
  374. paraloc^.def:=u32inttype;
  375. end
  376. else
  377. begin
  378. paraloc^.size:=paracgsize;
  379. paraloc^.def:=locdef;
  380. end;
  381. { aix requires that record data stored in parameter
  382. registers is left-aligned }
  383. if (target_info.system in systems_aix) and
  384. (paradef.typ = recorddef) and
  385. (paralen < sizeof(aint)) then
  386. begin
  387. paraloc^.shiftval := (sizeof(aint)-paralen)*(-8);
  388. paraloc^.size := OS_INT;
  389. paraloc^.def := u32inttype;
  390. end;
  391. paraloc^.register:=newreg(R_INTREGISTER,nextintreg,R_SUBNONE);
  392. inc(nextintreg);
  393. dec(paralen,tcgsize2size[paraloc^.size]);
  394. end
  395. else if (loc = LOC_FPUREGISTER) and
  396. (nextintreg <= RS_X17) then
  397. begin
  398. paraloc^.loc:=loc;
  399. paraloc^.size := paracgsize;
  400. paraloc^.def := paradef;
  401. paraloc^.register:=newreg(R_FPUREGISTER,nextintreg,R_SUBWHOLE);
  402. inc(nextintreg);
  403. dec(paralen,tcgsize2size[paraloc^.size]);
  404. end
  405. else { LOC_REFERENCE }
  406. begin
  407. paraloc^.loc:=LOC_REFERENCE;
  408. case loc of
  409. LOC_FPUREGISTER:
  410. begin
  411. paraloc^.size:=int_float_cgsize(paralen);
  412. case paraloc^.size of
  413. OS_F32: paraloc^.def:=s32floattype;
  414. OS_F64: paraloc^.def:=s64floattype;
  415. else
  416. internalerror(2013060124);
  417. end;
  418. end;
  419. LOC_REGISTER,
  420. LOC_REFERENCE:
  421. begin
  422. paraloc^.size:=int_cgsize(paralen);
  423. if paraloc^.size<>OS_NO then
  424. paraloc^.def:=cgsize_orddef(paraloc^.size)
  425. else
  426. paraloc^.def:=carraydef.getreusable_no_free(u8inttype,paralen);
  427. end;
  428. else
  429. internalerror(2006011101);
  430. end;
  431. if (side = callerside) then
  432. paraloc^.reference.index:=NR_STACK_POINTER_REG
  433. else
  434. begin
  435. paraloc^.reference.index:=NR_FRAME_POINTER_REG;
  436. { create_paraloc_info_intern might be also called when being outside of
  437. code generation so current_procinfo might be not set }
  438. if assigned(current_procinfo) then
  439. trv32procinfo(current_procinfo).needs_frame_pointer := true;
  440. end;
  441. paraloc^.reference.offset:=stack_offset;
  442. inc(stack_offset,align(paralen,4));
  443. while (paralen > 0) and
  444. (nextintreg < RS_X18) do
  445. begin
  446. inc(nextintreg);
  447. dec(paralen,sizeof(pint));
  448. end;
  449. paralen := 0;
  450. end;
  451. firstparaloc:=false;
  452. end;
  453. end;
  454. curintreg:=nextintreg;
  455. curfloatreg:=nextfloatreg;
  456. curmmreg:=nextmmreg;
  457. cur_stack_offset:=stack_offset;
  458. result:=stack_offset;
  459. end;
  460. function tcpuparamanager.create_varargs_paraloc_info(p : tabstractprocdef; side: tcallercallee; varargspara:tvarargsparalist):longint;
  461. var
  462. cur_stack_offset: aword;
  463. parasize, l: longint;
  464. curintreg, firstfloatreg, curfloatreg, curmmreg: tsuperregister;
  465. i : integer;
  466. hp: tparavarsym;
  467. paraloc: pcgparalocation;
  468. begin
  469. init_values(curintreg,curfloatreg,curmmreg,cur_stack_offset);
  470. firstfloatreg:=curfloatreg;
  471. result:=create_paraloc_info_intern(p,side,p.paras,curintreg,curfloatreg,curmmreg,cur_stack_offset, false);
  472. if (p.proccalloption in cstylearrayofconst) then
  473. { just continue loading the parameters in the registers }
  474. begin
  475. if assigned(varargspara) then
  476. begin
  477. if side=callerside then
  478. result:=create_paraloc_info_intern(p,side,varargspara,curintreg,curfloatreg,curmmreg,cur_stack_offset,true)
  479. else
  480. internalerror(2019021919);
  481. if curfloatreg<>firstfloatreg then
  482. include(varargspara.varargsinfo,va_uses_float_reg);
  483. end;
  484. end
  485. else
  486. internalerror(2019021912);
  487. create_funcretloc_info(p,side);
  488. end;
  489. begin
  490. paramanager:=tcpuparamanager.create;
  491. end.