pararv.pas 19 KB

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