pararv.pas 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. {
  2. Copyright (c) 2002 by Florian Klaempfl
  3. RiscV 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 pararv;
  18. {$I fpcdefs.inc}
  19. interface
  20. uses
  21. globtype,
  22. aasmdata,
  23. symconst,symtype,symdef,
  24. cgbase,cgutils,
  25. parabase,paramgr;
  26. type
  27. trvparamanager = class(tparamanager)
  28. function get_volatile_registers_int(calloption: tproccalloption): tcpuregisterset; override;
  29. function get_volatile_registers_fpu(calloption: tproccalloption): tcpuregisterset; override;
  30. function get_saved_registers_fpu(calloption: tproccalloption): tcpuregisterarray;override;
  31. function get_saved_registers_int(calloption: tproccalloption): tcpuregisterarray;override;
  32. function get_funcretloc(p: tabstractprocdef; side: tcallercallee; forcetempdef: tdef): tcgpara;override;
  33. procedure getcgtempparaloc(list: TAsmList; pd: tabstractprocdef; nr: longint; var cgpara: tcgpara);override;
  34. function create_paraloc_info_intern(p: tabstractprocdef; side: tcallercallee; paras: tparalist; var curintreg, curfloatreg, curmmreg: tsuperregister; var cur_stack_offset: aword; isVararg : boolean): longint;virtual;
  35. procedure create_paraloc_for_def(var para: TCGPara; varspez: tvarspez; paradef: tdef; var nextfloatreg, nextintreg: tsuperregister; var stack_offset: aword; const isVararg, forceintmem: boolean; const side: tcallercallee; const p: tabstractprocdef);virtual;
  36. function create_varargs_paraloc_info(p: tabstractprocdef; side: tcallercallee; varargspara: tvarargsparalist): longint;override;
  37. function push_addr_param(varspez: tvarspez; def: tdef; calloption: tproccalloption): boolean;override;
  38. function create_paraloc_info(p: tabstractprocdef; side: tcallercallee): longint;override;
  39. protected
  40. procedure init_values(var curintreg, curfloatreg, curmmreg: tsuperregister; var cur_stack_offset: aword);
  41. end;
  42. function getparaloc(p : tdef) : tcgloc;
  43. implementation
  44. uses
  45. verbose,
  46. globals,
  47. systems,
  48. cpuinfo,
  49. symsym,
  50. symtable,
  51. defutil,
  52. cpubase,
  53. procinfo,cpupi;
  54. function getparaloc(p : tdef) : tcgloc;
  55. begin
  56. case p.typ of
  57. orddef:
  58. result:=LOC_REGISTER;
  59. floatdef:
  60. if (cs_fp_emulation in current_settings.moduleswitches) or
  61. (current_settings.fputype in [fpu_soft]) then
  62. result := LOC_REGISTER
  63. else
  64. result := LOC_FPUREGISTER;
  65. enumdef:
  66. result:=LOC_REGISTER;
  67. pointerdef:
  68. result:=LOC_REGISTER;
  69. formaldef:
  70. result:=LOC_REGISTER;
  71. classrefdef:
  72. result:=LOC_REGISTER;
  73. procvardef:
  74. { method pointers fit into two registers, so pass procedure variables always in registers }
  75. result:=LOC_REGISTER;
  76. recorddef:
  77. if (p.size > sizeof(pint)*2) then
  78. result:=LOC_REFERENCE
  79. else
  80. result:=LOC_REGISTER;
  81. objectdef:
  82. if is_object(p) then
  83. result:=LOC_REFERENCE
  84. else
  85. result:=LOC_REGISTER;
  86. stringdef:
  87. if is_shortstring(p) or is_longstring(p) then
  88. result:=LOC_REFERENCE
  89. else
  90. result:=LOC_REGISTER;
  91. filedef:
  92. result:=LOC_REGISTER;
  93. arraydef:
  94. if is_dynamic_array(p) then
  95. getparaloc:=LOC_REGISTER
  96. else
  97. result:=LOC_REFERENCE;
  98. setdef:
  99. if is_smallset(p) then
  100. result:=LOC_REGISTER
  101. else
  102. result:=LOC_REFERENCE;
  103. variantdef:
  104. result:=LOC_REFERENCE;
  105. { avoid problems with errornous definitions }
  106. errordef:
  107. result:=LOC_REGISTER;
  108. else
  109. internalerror(2002071001);
  110. end;
  111. end;
  112. function trvparamanager.get_volatile_registers_int(calloption: tproccalloption): tcpuregisterset;
  113. begin
  114. result:=[RS_X0..RS_X31]-[RS_X2,RS_X8..RS_X9,RS_X18..RS_X27];
  115. end;
  116. function trvparamanager.get_volatile_registers_fpu(calloption: tproccalloption): tcpuregisterset;
  117. begin
  118. result:=[RS_F0..RS_F31];
  119. if target_info.abi in [abi_riscv_hf,abi_riscv_ilp32f,abi_riscv_ilp32d,abi_riscv_lp64f,abi_riscv_lp64d] then
  120. result:=result-[RS_F8..RS_F9,RS_F18..RS_F27];
  121. end;
  122. function trvparamanager.get_saved_registers_int(calloption : tproccalloption):tcpuregisterarray;
  123. const
  124. 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);
  125. begin
  126. result:=saved_regs;
  127. end;
  128. function trvparamanager.get_saved_registers_fpu(calloption : tproccalloption):tcpuregisterarray;
  129. const
  130. 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);
  131. empty_regs: tcpuregisterarray = ();
  132. begin
  133. if target_info.abi in [abi_riscv_hf,abi_riscv_ilp32f,abi_riscv_ilp32d,abi_riscv_lp64f,abi_riscv_lp64d] then
  134. result:=saved_regs
  135. else
  136. result:=empty_regs;
  137. end;
  138. procedure trvparamanager.getcgtempparaloc(list: TAsmList; pd : tabstractprocdef; nr : longint; var cgpara : tcgpara);
  139. var
  140. paraloc : pcgparalocation;
  141. psym : tparavarsym;
  142. pdef : tdef;
  143. begin
  144. psym:=tparavarsym(pd.paras[nr-1]);
  145. pdef:=psym.vardef;
  146. if push_addr_param(psym.varspez,pdef,pd.proccalloption) then
  147. pdef:=cpointerdef.getreusable_no_free(pdef);
  148. cgpara.reset;
  149. cgpara.size:=def_cgsize(pdef);
  150. cgpara.intsize:=tcgsize2size[cgpara.size];
  151. cgpara.alignment:=get_para_align(pd.proccalloption);
  152. cgpara.def:=pdef;
  153. paraloc:=cgpara.add_location;
  154. with paraloc^ do
  155. begin
  156. size:=def_cgsize(pdef);
  157. def:=pdef;
  158. if (nr<=8) then
  159. begin
  160. if nr=0 then
  161. internalerror(2024121501);
  162. loc:=LOC_REGISTER;
  163. register:=newreg(R_INTREGISTER,RS_X10+nr-1,R_SUBWHOLE);
  164. end
  165. else
  166. begin
  167. loc:=LOC_REFERENCE;
  168. paraloc^.reference.index:=NR_STACK_POINTER_REG;
  169. reference.offset:=sizeof(pint)*nr;
  170. end;
  171. end;
  172. end;
  173. function trvparamanager.push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;
  174. begin
  175. result:=false;
  176. { var,out,constref always require address }
  177. if varspez in [vs_var,vs_out,vs_constref] then
  178. begin
  179. result:=true;
  180. exit;
  181. end;
  182. case def.typ of
  183. variantdef,
  184. formaldef :
  185. result:=true;
  186. { regular procvars must be passed by value, because you cannot pass
  187. the address of a local stack location when calling e.g.
  188. pthread_create with the address of a function (first of all it
  189. expects the address of the function to execute and not the address
  190. of a memory location containing that address, and secondly if you
  191. first store the address on the stack and then pass the address of
  192. this stack location, then this stack location may no longer be
  193. valid when the newly started thread accesses it.
  194. However, for "procedure of object" we must use the same calling
  195. convention as for "8 byte record" due to the need for
  196. interchangeability with the TMethod record type.
  197. }
  198. procvardef,
  199. recorddef:
  200. result := not(def.size in [0..sizeof(aint)*2]) or (varspez = vs_const);
  201. arraydef:
  202. result:=(tarraydef(def).highrange>=tarraydef(def).lowrange) or
  203. is_open_array(def) or
  204. is_array_of_const(def) or
  205. is_array_constructor(def);
  206. objectdef :
  207. result:=is_object(def);
  208. setdef :
  209. result:=not is_smallset(def);
  210. stringdef :
  211. result:=tstringdef(def).stringtype in [st_shortstring,st_longstring];
  212. else
  213. ;
  214. end;
  215. end;
  216. function trvparamanager.get_funcretloc(p : tabstractprocdef; side: tcallercallee; forcetempdef: tdef): tcgpara;
  217. var
  218. paraloc : pcgparalocation;
  219. retcgsize : tcgsize;
  220. nextintreg, nextfloatreg, nextmmreg: tsuperregister;
  221. stack_offset: aword;
  222. begin
  223. if set_common_funcretloc_info(p,forcetempdef,retcgsize,result) then
  224. exit;
  225. init_values(nextintreg,nextfloatreg,nextmmreg,stack_offset);
  226. create_paraloc_for_def(result,vs_value,result.def,nextfloatreg,nextintreg,stack_offset,false,false,side,p);
  227. end;
  228. procedure trvparamanager.init_values(var curintreg, curfloatreg, curmmreg: tsuperregister; var cur_stack_offset: aword);
  229. begin
  230. { register parameter save area begins at 48(r2) }
  231. cur_stack_offset := 0;
  232. curintreg := RS_X10;
  233. curfloatreg := RS_F10;
  234. curmmreg := RS_NO;
  235. end;
  236. function trvparamanager.create_varargs_paraloc_info(p : tabstractprocdef; side: tcallercallee; varargspara:tvarargsparalist):longint;
  237. var
  238. cur_stack_offset: aword;
  239. parasize, l: longint;
  240. curintreg, firstfloatreg, curfloatreg, curmmreg: tsuperregister;
  241. i : integer;
  242. hp: tparavarsym;
  243. paraloc: pcgparalocation;
  244. begin
  245. init_values(curintreg,curfloatreg,curmmreg,cur_stack_offset);
  246. firstfloatreg:=curfloatreg;
  247. result:=create_paraloc_info_intern(p,side,p.paras,curintreg,curfloatreg,curmmreg,cur_stack_offset,false);
  248. if (p.proccalloption in cstylearrayofconst) then
  249. { just continue loading the parameters in the registers }
  250. begin
  251. if assigned(varargspara) then
  252. begin
  253. if side=callerside then
  254. result:=create_paraloc_info_intern(p,side,varargspara,curintreg,curfloatreg,curmmreg,cur_stack_offset,true)
  255. else
  256. internalerror(2019021919);
  257. if curfloatreg<>firstfloatreg then
  258. include(varargspara.varargsinfo,va_uses_float_reg);
  259. { not sure if this applies to RiscV 32 as well ... }
  260. {$ifdef RISCV64}
  261. { varargs routines have to reserve at least 64 bytes for the RiscV ABI }
  262. if (result < 64) then
  263. result := 64;
  264. {$endif RISCV64}
  265. end;
  266. end
  267. else
  268. internalerror(2019021912);
  269. create_funcretloc_info(p,side);
  270. end;
  271. function trvparamanager.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 trvparamanager.create_paraloc_info_intern(p: tabstractprocdef; side: tcallercallee; paras: tparalist; var curintreg, curfloatreg, curmmreg: tsuperregister; var cur_stack_offset: aword; isVararg : boolean): longint;
  281. var
  282. nextintreg, nextfloatreg, nextmmreg : tsuperregister;
  283. i: integer;
  284. hp: tparavarsym;
  285. paraloc: pcgparalocation;
  286. delphi_nestedfp: boolean;
  287. begin
  288. {$IFDEF extdebug}
  289. if po_explicitparaloc in p.procoptions then
  290. internalerror(200411141);
  291. {$ENDIF extdebug}
  292. result := 0;
  293. nextintreg := curintreg;
  294. nextfloatreg := curfloatreg;
  295. nextmmreg := curmmreg;
  296. for i := 0 to paras.count - 1 do
  297. begin
  298. hp := tparavarsym(paras[i]);
  299. if (vo_has_explicit_paraloc in hp.varoptions) then
  300. internalerror(2024122201);
  301. { currently only support C-style array of const }
  302. if (p.proccalloption in [pocall_cdecl, pocall_cppdecl]) and
  303. is_array_of_const(hp.vardef) then 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. delphi_nestedfp:=(vo_is_parentfp in hp.varoptions) and (po_delphi_nested_cc in p.procoptions);
  313. create_paraloc_for_def(hp.paraloc[side], hp.varspez, hp.vardef,
  314. nextfloatreg, nextintreg, cur_stack_offset, isVararg, delphi_nestedfp, side, p);
  315. end;
  316. curintreg := nextintreg;
  317. curfloatreg := nextfloatreg;
  318. curmmreg := nextmmreg;
  319. result := cur_stack_offset;
  320. end;
  321. procedure trvparamanager.create_paraloc_for_def(var para: TCGPara; varspez: tvarspez; paradef: tdef; var nextfloatreg, nextintreg: tsuperregister; var stack_offset: aword; const isVararg, forceintmem: boolean; const side: tcallercallee; const p: tabstractprocdef);
  322. var
  323. paracgsize: tcgsize;
  324. loc: tcgloc;
  325. paraloc: pcgparalocation;
  326. { def to use for all paralocs if <> nil }
  327. alllocdef,
  328. { def to use for the current paraloc }
  329. locdef,
  330. tmpdef: tdef;
  331. paralen: aint;
  332. firstparaloc,
  333. paraaligned: boolean;
  334. begin
  335. alllocdef:=nil;
  336. locdef:=nil;
  337. para.reset;
  338. { have we ensured that the next parameter location will be aligned to the
  339. next 8 byte boundary? }
  340. paraaligned:=false;
  341. if push_addr_param(varspez, paradef, p.proccalloption) then
  342. begin
  343. paradef := cpointerdef.getreusable_no_free(paradef);
  344. loc := LOC_REGISTER;
  345. paracgsize := OS_ADDR;
  346. paralen := tcgsize2size[OS_ADDR];
  347. end
  348. else
  349. begin
  350. if not is_special_array(paradef) then
  351. paralen := paradef.size
  352. else
  353. paralen := tcgsize2size[def_cgsize(paradef)];
  354. if (paradef.typ=recorddef) and not(is_implicit_pointer_object_type(paradef)) and
  355. tabstractrecordsymtable(tabstractrecorddef(paradef).symtable).has_single_field(tmpdef) and
  356. (tmpdef.typ=floatdef) then
  357. begin
  358. paradef:=tmpdef;
  359. loc:=getparaloc(paradef);
  360. paracgsize:=def_cgsize(paradef)
  361. end
  362. else if (((paradef.typ=arraydef) and not
  363. is_special_array(paradef)) or
  364. (paradef.typ=recorddef)) then
  365. begin
  366. loc:=LOC_REGISTER;
  367. paracgsize:=int_cgsize(paralen);
  368. end
  369. else
  370. begin
  371. loc:=getparaloc(paradef);
  372. paracgsize:=def_cgsize(paradef);
  373. { for things like formaldef }
  374. if (paracgsize=OS_NO) then
  375. begin
  376. paracgsize:=OS_ADDR;
  377. paralen:=tcgsize2size[OS_ADDR];
  378. end;
  379. end
  380. end;
  381. { patch FPU values into integer registers if we are processing varargs }
  382. if (isVararg) and (paradef.typ = floatdef) then
  383. begin
  384. loc := LOC_REGISTER;
  385. if paracgsize = OS_F64 then
  386. paracgsize := OS_64
  387. else
  388. paracgsize := OS_32;
  389. end;
  390. para.alignment := std_param_align;
  391. para.size := paracgsize;
  392. para.intsize := paralen;
  393. para.def := paradef;
  394. if (paralen = 0) then
  395. if (paradef.typ = recorddef) then
  396. begin
  397. paraloc := para.add_location;
  398. paraloc^.loc := LOC_VOID;
  399. end
  400. else
  401. internalerror(2024121401);
  402. if not assigned(alllocdef) then
  403. locdef:=paradef
  404. else
  405. begin
  406. locdef:=alllocdef;
  407. paracgsize:=def_cgsize(locdef);
  408. end;
  409. firstparaloc:=true;
  410. { Parameters passed in 2 registers are passed in a register starting with an even number. }
  411. if isVararg and
  412. (paralen > sizeof(AInt)) and
  413. (loc = LOC_REGISTER) and
  414. (nextintreg <= RS_X17) and
  415. odd(nextintreg) then
  416. inc(nextintreg);
  417. { can become < 0 for e.g. 3-byte records }
  418. while (paralen > 0) do begin
  419. paraloc := para.add_location;
  420. { In case of po_delphi_nested_cc, the parent frame pointer
  421. is always passed on the stack. }
  422. if (loc = LOC_REGISTER) and
  423. (nextintreg <= RS_X17) and
  424. not forceintmem then
  425. begin
  426. paraloc^.loc := loc;
  427. { make sure we don't lose whether or not the type is signed }
  428. if (paracgsize <> OS_NO) and
  429. (paradef.typ <> orddef) and
  430. not assigned(alllocdef) then
  431. begin
  432. paracgsize := int_cgsize(paralen);
  433. locdef:=get_paraloc_def(paradef, paralen, firstparaloc);
  434. end;
  435. if (paracgsize in [OS_NO, OS_SPAIR, OS_PAIR]) then
  436. begin
  437. if (paralen > 4) then
  438. begin
  439. paraloc^.size := OS_INT;
  440. paraloc^.def := osuinttype;
  441. end
  442. else
  443. begin
  444. { for 3-byte records aligned in the lower bits of register }
  445. paraloc^.size := OS_32;
  446. paraloc^.def := u32inttype;
  447. end;
  448. end
  449. else
  450. begin
  451. paraloc^.size := paracgsize;
  452. paraloc^.def := locdef;
  453. end;
  454. paraloc^.register := newreg(R_INTREGISTER, nextintreg, R_SUBNONE);
  455. inc(nextintreg);
  456. dec(paralen, tcgsize2size[paraloc^.size]);
  457. end
  458. else if (loc = LOC_FPUREGISTER) and
  459. (nextfloatreg <= RS_F17) then
  460. begin
  461. paraloc^.loc := loc;
  462. paraloc^.size := paracgsize;
  463. paraloc^.def := locdef;
  464. paraloc^.register := newreg(R_FPUREGISTER, nextfloatreg, R_SUBWHOLE);
  465. { the RiscV ABI says that the GPR index is increased for every parameter, no matter
  466. which type it is stored in
  467. not really, https://github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.md#hardware-floating-point-calling-convention says
  468. otherwise, gcc doesn't do it either }
  469. inc(nextfloatreg);
  470. dec(paralen, tcgsize2size[paraloc^.size]);
  471. end
  472. else if (loc = LOC_MMREGISTER) then
  473. { no mm registers }
  474. internalerror(2018072601)
  475. else
  476. begin
  477. { either LOC_REFERENCE, or one of the above which must be passed on the
  478. stack because of insufficient registers }
  479. paraloc^.loc := LOC_REFERENCE;
  480. case loc of
  481. LOC_FPUREGISTER:
  482. begin
  483. paraloc^.size:=int_float_cgsize(paralen);
  484. case paraloc^.size of
  485. OS_F32: paraloc^.def:=s32floattype;
  486. OS_F64: paraloc^.def:=s64floattype;
  487. else
  488. internalerror(2013060122);
  489. end;
  490. end;
  491. LOC_REGISTER,
  492. LOC_REFERENCE:
  493. begin
  494. paraloc^.size:=int_cgsize(paralen);
  495. paraloc^.def:=get_paraloc_def(paradef, paralen, firstparaloc);
  496. end;
  497. else
  498. internalerror(2006011101);
  499. end;
  500. if (side = callerside) then
  501. paraloc^.reference.index := NR_STACK_POINTER_REG
  502. else
  503. begin
  504. { during procedure entry, NR_OLD_STACK_POINTER_REG contains the old stack pointer }
  505. paraloc^.reference.index := NR_FRAME_POINTER_REG;
  506. { create_paraloc_info_intern might be also called when being outside of
  507. code generation so current_procinfo might be not set }
  508. {$ifdef RISCV64}
  509. if assigned(current_procinfo) then
  510. trv64procinfo(current_procinfo).needs_frame_pointer := true;
  511. {$endif RISCV64}
  512. {$ifdef RISCV32}
  513. if assigned(current_procinfo) then
  514. trv32procinfo(current_procinfo).needs_frame_pointer := true;
  515. {$endif RISCV32}
  516. end;
  517. paraloc^.reference.offset := stack_offset;
  518. { align temp contents to next register size }
  519. if not paraaligned then
  520. inc(stack_offset, align(paralen, sizeof(AInt)))
  521. else
  522. inc(stack_offset, paralen);
  523. paralen := 0;
  524. end;
  525. firstparaloc:=false;
  526. end;
  527. end;
  528. end.