cpupara.pas 21 KB

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