pararv.pas 21 KB

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