cpupara.pas 20 KB

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