/compiler/mips/cpupara.pas

https://github.com/slibre/freepascal · Pascal · 578 lines · 433 code · 45 blank · 100 comment · 48 complexity · 2a7d7c8cb5a48e0e112c26427d9469cc MD5 · raw file

  1. {
  2. Copyright (c) 1998-2002 by Florian Klaempfl and David Zhang
  3. Calling conventions for the MIPSEL
  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. unit cpupara;
  17. {$i fpcdefs.inc}
  18. interface
  19. uses
  20. globtype,
  21. cclasses,
  22. aasmtai,
  23. cpubase,cpuinfo,
  24. symconst,symbase,symsym,symtype,symdef,paramgr,parabase,cgbase,cgutils;
  25. const
  26. MIPS_MAX_OFFSET = 20;
  27. { The value below is OK for O32 and N32 calling conventions }
  28. MIPS_MAX_REGISTERS_USED_IN_CALL = 6;
  29. { All ABI seem to start with $4 i.e. $a0 }
  30. MIPS_FIRST_REGISTER_USED_IN_CALL = RS_R4;
  31. { O32 ABI uses $a0 to $a3, i.e R4 to R7 }
  32. MIPS_LAST_REGISTER_USED_IN_CALL_ABI_O32 = RS_R7;
  33. { N32 ABI uses also R8 and R9 }
  34. MIPS_LAST_REGISTER_USED_IN_CALL_ABI_N32 = RS_R9;
  35. { The calculation below is based on the assumption
  36. that all registers used for ABI calls are
  37. ordered and follow each other }
  38. MIPS_NB_REGISTERS_USED_IN_CALL_O32 =
  39. MIPS_LAST_REGISTER_USED_IN_CALL_ABI_O32
  40. - MIPS_FIRST_REGISTER_USED_IN_CALL + 1;
  41. MIPS_NB_REGISTERS_USED_IN_CALL_N32 =
  42. MIPS_LAST_REGISTER_USED_IN_CALL_ABI_N32
  43. - MIPS_FIRST_REGISTER_USED_IN_CALL + 1;
  44. { Set O32 ABI as default }
  45. const
  46. mips_nb_used_registers : longint = MIPS_NB_REGISTERS_USED_IN_CALL_O32;
  47. { Might need to be changed if we support N64 ABI later }
  48. mips_sizeof_register_param : longint = 4;
  49. type
  50. tparasupregs = array[0..MIPS_MAX_REGISTERS_USED_IN_CALL-1] of tsuperregister;
  51. tparasupregsused = array[0..MIPS_MAX_REGISTERS_USED_IN_CALL-1] of boolean;
  52. tparasupregsize = array[0..MIPS_MAX_REGISTERS_USED_IN_CALL-1] of tcgsize;
  53. tparasuprename = array[0..MIPS_MAX_REGISTERS_USED_IN_CALL-1] of shortstring;
  54. tparasupregsoffset = array[0..MIPS_MAX_REGISTERS_USED_IN_CALL-1] of longint;
  55. const
  56. parasupregs : tparasupregs = (RS_R4, RS_R5, RS_R6, RS_R7, RS_R8, RS_R9);
  57. type
  58. TMIPSParaManager=class(TParaManager)
  59. function push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;override;
  60. function get_volatile_registers_int(calloption : tproccalloption):TCpuRegisterSet;override;
  61. function get_volatile_registers_fpu(calloption : tproccalloption):TCpuRegisterSet;override;
  62. {Returns a structure giving the information on the storage of the parameter
  63. (which must be an integer parameter)
  64. @param(nr Parameter number of routine, starting from 1)}
  65. procedure getintparaloc(pd : tabstractprocdef; nr : longint; var cgpara : tcgpara);override;
  66. function create_paraloc_info(p : TAbstractProcDef; side: tcallercallee):longint;override;
  67. function create_varargs_paraloc_info(p : TAbstractProcDef; varargspara:tvarargsparalist):longint;override;
  68. function get_funcretloc(p : tabstractprocdef; side: tcallercallee; forcetempdef: tdef): tcgpara;override;
  69. private
  70. intparareg,
  71. intparasize : longint;
  72. can_use_float : boolean;
  73. function is_abi_record(def: tdef): boolean;
  74. procedure create_paraloc_info_intern(p : tabstractprocdef; side: tcallercallee; paras: tparalist);
  75. end;
  76. implementation
  77. uses
  78. cutils,verbose,systems,
  79. defutil, cpupi, procinfo,
  80. cgobj;
  81. function TMIPSParaManager.get_volatile_registers_int(calloption : tproccalloption):TCpuRegisterSet;
  82. begin
  83. { O32 ABI values }
  84. result:=[RS_R1..RS_R15,RS_R24..RS_R25,RS_R31];
  85. end;
  86. function TMIPSParaManager.get_volatile_registers_fpu(calloption : tproccalloption):TCpuRegisterSet;
  87. begin
  88. { O32 ABI values }
  89. result:=[RS_F0..RS_F19];
  90. end;
  91. { whether "def" must be treated as record when used as function result,
  92. i.e. its address passed in a0 }
  93. function TMIPSParaManager.is_abi_record(def: tdef): boolean;
  94. begin
  95. result:=(def.typ=recorddef) or
  96. ((def.typ=procvardef) and not tprocvardef(def).is_addressonly);
  97. end;
  98. procedure TMIPSParaManager.GetIntParaLoc(pd : tabstractprocdef; nr : longint; var cgpara : tcgpara);
  99. var
  100. paraloc : pcgparalocation;
  101. def : tdef;
  102. begin
  103. if nr<1 then
  104. InternalError(2002100806);
  105. def:=tparavarsym(pd.paras[nr-1]).vardef;
  106. cgpara.reset;
  107. cgpara.size:=def_cgsize(def);
  108. cgpara.intsize:=tcgsize2size[cgpara.size];
  109. cgpara.alignment:=std_param_align;
  110. cgpara.def:=def;
  111. paraloc:=cgpara.add_location;
  112. with paraloc^ do
  113. begin
  114. { MIPS: ABI dependent number of first parameters
  115. are passed into registers }
  116. dec(nr);
  117. if nr<mips_nb_used_registers then
  118. begin
  119. loc:=LOC_REGISTER;
  120. register:=newreg(R_INTREGISTER,parasupregs[nr],R_SUBWHOLE);
  121. end
  122. else
  123. begin
  124. { The other parameters are passed on the stack }
  125. loc:=LOC_REFERENCE;
  126. reference.index:=NR_STACK_POINTER_REG;
  127. reference.offset:=nr*mips_sizeof_register_param;
  128. end;
  129. size:=OS_INT;
  130. { Be sure to reserve enough stack space tp cope with
  131. that parameter }
  132. if assigned(current_procinfo) then
  133. TMIPSProcinfo(current_procinfo).allocate_push_parasize((nr+1)*mips_sizeof_register_param);
  134. end;
  135. end;
  136. { true if a parameter is too large to copy and only the address is pushed }
  137. function TMIPSParaManager.push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;
  138. begin
  139. result:=false;
  140. { var,out,constref always require address }
  141. if varspez in [vs_var,vs_out,vs_constref] then
  142. begin
  143. result:=true;
  144. exit;
  145. end;
  146. case def.typ of
  147. recorddef:
  148. { According to 032 ABI we should have }
  149. result:=false;
  150. arraydef:
  151. result:=true; {(tarraydef(def).highrange>=tarraydef(def).lowrange) or
  152. is_open_array(def) or
  153. is_array_of_const(def) or
  154. is_array_constructor(def);}
  155. variantdef,
  156. formaldef :
  157. result:=true;
  158. objectdef :
  159. result:=is_object(def);
  160. stringdef :
  161. result:=(tstringdef(def).stringtype in [st_shortstring,st_longstring]);
  162. procvardef :
  163. { If we always push records by value, we have to handle methodpointers that way too. }
  164. result:=false; {not tprocvardef(def).is_addressonly;}
  165. setdef :
  166. result:=not(is_smallset(def));
  167. end;
  168. end;
  169. function TMIPSParaManager.get_funcretloc(p : tabstractprocdef; side: tcallercallee; forcetempdef: tdef): tcgpara;
  170. var
  171. paraloc : pcgparalocation;
  172. retcgsize : tcgsize;
  173. retdef : tdef;
  174. begin
  175. if set_common_funcretloc_info(p,forcetempdef,retcgsize,result) then
  176. begin
  177. { Return is passed as var parameter,
  178. in this case we use the first register R4 for it }
  179. if assigned(forcetempdef) then
  180. retdef:=forcetempdef
  181. else
  182. retdef:=p.returndef;
  183. if ret_in_param(retdef,p) and
  184. is_abi_record(retdef) then
  185. begin
  186. if intparareg=0 then
  187. inc(intparareg);
  188. if side=calleeside then
  189. begin
  190. result.reset;
  191. paraloc:=result.add_location;
  192. paraloc^.loc:=LOC_REFERENCE;
  193. paraloc^.reference.index:=NR_STACK_POINTER_REG;
  194. { return is at offset zero }
  195. paraloc^.reference.offset:=0;
  196. paraloc^.size:=retcgsize;
  197. { Reserve first register for ret_in_param }
  198. if assigned(current_procinfo) then
  199. begin
  200. TMIPSProcInfo(current_procinfo).register_used[0]:=true;
  201. TMIPSProcInfo(current_procinfo).register_size[0]:=retcgsize;
  202. TMIPSProcInfo(current_procinfo).register_name[0]:='ret_in_param_result';
  203. TMIPSProcInfo(current_procinfo).register_offset[0]:=0;
  204. end;
  205. end
  206. else
  207. begin
  208. getIntParaLoc(p,1,result);
  209. result.def:=retdef;
  210. end;
  211. // This is now done in set_common_funcretloc_info already
  212. // result.def:=getpointerdef(result.def);
  213. end;
  214. exit;
  215. end;
  216. paraloc:=result.add_location;
  217. { Return in FPU register? }
  218. if result.def.typ=floatdef then
  219. begin
  220. paraloc^.loc:=LOC_FPUREGISTER;
  221. paraloc^.register:=NR_FPU_RESULT_REG;
  222. if retcgsize=OS_F64 then
  223. setsubreg(paraloc^.register,R_SUBFD);
  224. paraloc^.size:=retcgsize;
  225. end
  226. else
  227. { Return in register }
  228. begin
  229. {$ifndef cpu64bitalu}
  230. if retcgsize in [OS_64,OS_S64] then
  231. begin
  232. { low }
  233. paraloc^.loc:=LOC_REGISTER;
  234. if side=callerside then
  235. paraloc^.register:=NR_FUNCTION_RESULT64_LOW_REG
  236. else
  237. paraloc^.register:=NR_FUNCTION_RETURN64_LOW_REG;
  238. paraloc^.size:=OS_32;
  239. { high }
  240. paraloc:=result.add_location;
  241. paraloc^.loc:=LOC_REGISTER;
  242. if side=callerside then
  243. paraloc^.register:=NR_FUNCTION_RESULT64_HIGH_REG
  244. else
  245. paraloc^.register:=NR_FUNCTION_RETURN64_HIGH_REG;
  246. paraloc^.size:=OS_32;
  247. end
  248. else
  249. {$endif cpu64bitalu}
  250. begin
  251. paraloc^.loc:=LOC_REGISTER;
  252. paraloc^.size:=retcgsize;
  253. if side=callerside then
  254. paraloc^.register:=newreg(R_INTREGISTER,RS_FUNCTION_RESULT_REG,cgsize2subreg(R_INTREGISTER,retcgsize))
  255. else
  256. paraloc^.register:=newreg(R_INTREGISTER,RS_FUNCTION_RETURN_REG,cgsize2subreg(R_INTREGISTER,retcgsize));
  257. end;
  258. end
  259. end;
  260. procedure TMIPSParaManager.create_paraloc_info_intern(p : tabstractprocdef; side: tcallercallee;paras:tparalist);
  261. var
  262. paraloc : pcgparalocation;
  263. i : integer;
  264. hp : tparavarsym;
  265. paracgsize : tcgsize;
  266. paralen : longint;
  267. paradef : tdef;
  268. fpparareg : integer;
  269. reg : tsuperregister;
  270. alignment : longint;
  271. tmp : longint;
  272. begin
  273. fpparareg := 0;
  274. for i:=0 to paras.count-1 do
  275. begin
  276. hp:=tparavarsym(paras[i]);
  277. paradef := hp.vardef;
  278. { currently only support C-style array of const }
  279. if (p.proccalloption in cstylearrayofconst) and
  280. is_array_of_const(paradef) then
  281. begin
  282. paraloc:=hp.paraloc[side].add_location;
  283. { hack: the paraloc must be valid, but is not actually used }
  284. paraloc^.loc:=LOC_REGISTER;
  285. paraloc^.register:=NR_R0;
  286. paraloc^.size:=OS_ADDR;
  287. break;
  288. end;
  289. if push_addr_param(hp.varspez,paradef,p.proccalloption) then
  290. begin
  291. paracgsize := OS_ADDR;
  292. paralen := tcgsize2size[paracgsize];
  293. paradef := getpointerdef(paradef);
  294. end
  295. else
  296. begin
  297. paracgsize := def_cgsize(paradef);
  298. { for things like formaldef }
  299. if (paracgsize=OS_NO) and (paradef.typ <> recorddef) then
  300. begin
  301. paracgsize:=OS_ADDR;
  302. paradef:=voidpointertype;
  303. end;
  304. if not is_special_array(paradef) then
  305. paralen := paradef.size
  306. else
  307. paralen := tcgsize2size[paracgsize];
  308. end;
  309. if (paracgsize in [OS_64, OS_S64, OS_F64]) or (paradef.alignment = 8) then
  310. alignment := 8
  311. else
  312. alignment := 4;
  313. //writeln('para: ',hp.Name,' typ=',hp.vardef.typ,' paracgsize=',paracgsize,' align=',hp.vardef.alignment);
  314. hp.paraloc[side].reset;
  315. hp.paraloc[side].Alignment:=alignment;
  316. if paracgsize=OS_NO then
  317. begin
  318. paracgsize:=OS_32;
  319. paralen:=align(paralen,4);
  320. end
  321. else
  322. paralen:=tcgsize2size[paracgsize];
  323. hp.paraloc[side].intsize:=paralen;
  324. hp.paraloc[side].size:=paracgsize;
  325. hp.paraloc[side].def:=paradef;
  326. { check the alignment, mips O32ABI require a nature alignment }
  327. tmp := align(intparasize, alignment) - intparasize;
  328. while tmp > 0 do
  329. begin
  330. inc(intparareg);
  331. inc(intparasize,4);
  332. dec(tmp,4);
  333. end;
  334. { any non-float args will disable the use the floating regs }
  335. { up to two fp args }
  336. if (not(paracgsize in [OS_F32, OS_F64])) or (fpparareg = 2) then
  337. can_use_float := false;
  338. while paralen>0 do
  339. begin
  340. paraloc:=hp.paraloc[side].add_location;
  341. { We can allocate at maximum 32 bits per register }
  342. if (paracgsize in [OS_64,OS_S64]) or
  343. ((paracgsize in [OS_F32,OS_F64]) and
  344. not(can_use_float)) then
  345. paraloc^.size:=OS_32
  346. else
  347. paraloc^.size:=paracgsize;
  348. { big-endian targets require that record data stored in parameter
  349. registers is left-aligned }
  350. if (target_info.endian=endian_big) and
  351. (paradef.typ = recorddef) and
  352. (tcgsize2size[paraloc^.size] <> sizeof(aint)) then
  353. begin
  354. paraloc^.shiftval := (sizeof(aint)-tcgsize2size[paraloc^.size])*(-8);
  355. paraloc^.size := OS_INT;
  356. end;
  357. { ret in param? }
  358. if (vo_is_funcret in hp.varoptions) and
  359. is_abi_record(hp.vardef) then
  360. begin
  361. { This should be the first parameter }
  362. if (side=calleeside) and assigned(current_procinfo) then
  363. begin
  364. TMIPSProcInfo(current_procinfo).register_used[0]:=true;
  365. TMIPSProcInfo(current_procinfo).register_name[0]:='result';
  366. TMIPSProcInfo(current_procinfo).register_size[0]:=paraloc^.size;
  367. TMIPSProcInfo(current_procinfo).register_offset[0]:=0;
  368. end;
  369. //if (intparareg<>1) then
  370. // Comment(V_Warning,'intparareg should be one for funcret in TMipsParaManager.create_paraloc_info_intern');
  371. if side=callerside then
  372. begin
  373. paraloc^.loc:=LOC_REGISTER;
  374. paraloc^.register:=newreg(R_INTREGISTER,parasupregs[0],R_SUBWHOLE);
  375. end
  376. else
  377. begin
  378. paraloc^.loc:=LOC_REFERENCE;
  379. if (po_nostackframe in p.procoptions) then
  380. paraloc^.reference.index := NR_STACK_POINTER_REG
  381. else
  382. begin
  383. paraloc^.reference.index := NR_FRAME_POINTER_REG;
  384. if assigned(current_procinfo) then
  385. TMIPSProcinfo(current_procinfo).needs_frame_pointer := true;
  386. end;
  387. paraloc^.reference.offset:=0;
  388. end;
  389. inc(intparasize,align(tcgsize2size[paraloc^.size],sizeof(aint)));
  390. end
  391. { In case of po_delphi_nested_cc, the parent frame pointer
  392. is always passed on the stack. }
  393. else if (intparareg<mips_nb_used_registers) and
  394. (not(vo_is_parentfp in hp.varoptions) or
  395. not(po_delphi_nested_cc in p.procoptions)) then
  396. begin
  397. if (can_use_float) then
  398. begin
  399. paraloc^.loc:=LOC_FPUREGISTER;
  400. if (fpparareg = 0) then
  401. reg := RS_F12
  402. else
  403. reg := RS_F14;
  404. if (paraloc^.size = OS_F64) then
  405. begin
  406. paraloc^.register:=newreg(R_FPUREGISTER, reg, R_SUBFD);
  407. inc(fpparareg);
  408. inc(intparareg);
  409. inc(intparareg);
  410. inc(intparasize,8);
  411. end
  412. else
  413. begin
  414. paraloc^.register:=newreg(R_FPUREGISTER, reg, R_SUBFS);
  415. inc(fpparareg);
  416. inc(intparareg);
  417. inc(intparasize,sizeof(aint));
  418. end;
  419. end
  420. else { not can use float }
  421. begin
  422. if (side=calleeside) and assigned(current_procinfo) then
  423. begin
  424. TMIPSProcInfo(current_procinfo).register_used[intparareg]:=true;
  425. TMIPSProcInfo(current_procinfo).register_name[intparareg]:=hp.prettyname;
  426. TMIPSProcInfo(current_procinfo).register_size[intparareg]:=paraloc^.size;
  427. TMIPSProcInfo(current_procinfo).register_offset[intparareg]:=intparareg*mips_sizeof_register_param;
  428. end;
  429. if side=callerside then
  430. begin
  431. paraloc^.loc:=LOC_REGISTER;
  432. paraloc^.register:=newreg(R_INTREGISTER,parasupregs[intparareg],R_SUBWHOLE);
  433. end
  434. else
  435. begin
  436. paraloc^.loc:=LOC_REFERENCE;
  437. if (po_nostackframe in p.procoptions) then
  438. paraloc^.reference.index := NR_STACK_POINTER_REG
  439. else
  440. begin
  441. paraloc^.reference.index := NR_FRAME_POINTER_REG;
  442. if assigned(current_procinfo) then
  443. TMIPSProcinfo(current_procinfo).needs_frame_pointer := true;
  444. end;
  445. paraloc^.reference.offset:=intparareg*mips_sizeof_register_param;
  446. end;
  447. inc(intparareg);
  448. inc(intparasize,align(tcgsize2size[paraloc^.size],mips_sizeof_register_param));
  449. end;
  450. end
  451. else
  452. begin
  453. paraloc^.loc:=LOC_REFERENCE;
  454. paraloc^.size:=int_cgsize(paralen);
  455. { Force size to multiple of 4 for records passed by value,
  456. to obtain correct memory layout for big endian }
  457. (*
  458. if (paradef.typ = recorddef) and
  459. (tcgsize2size[paraloc^.size] < tcgsize2size[OS_32]) then
  460. begin
  461. inc(paralen,tcgsize2size[OS_32]-tcgsize2size[paraloc^.size]);
  462. paraloc^.size := OS_32;
  463. end;
  464. *)
  465. if side=callerside then
  466. begin
  467. paraloc^.reference.index := NR_STACK_POINTER_REG;
  468. paraloc^.reference.offset:=intparasize;
  469. end
  470. else
  471. begin
  472. if (po_nostackframe in p.procoptions) then
  473. paraloc^.reference.index := NR_STACK_POINTER_REG
  474. else
  475. begin
  476. paraloc^.reference.index := NR_FRAME_POINTER_REG;
  477. if assigned(current_procinfo) then
  478. TMIPSProcinfo(current_procinfo).needs_frame_pointer := true;
  479. end;
  480. paraloc^.reference.offset:=intparasize;
  481. end;
  482. inc(intparasize,align(paralen,mips_sizeof_register_param));
  483. paralen:=0;
  484. end;
  485. dec(paralen,tcgsize2size[paraloc^.size]);
  486. end;
  487. end;
  488. { O32 ABI reqires at least 16 bytes }
  489. if (intparasize < 16) then
  490. intparasize := 16;
  491. { Increase maxpushparasize, but only if not examining itself }
  492. //if assigned(current_procinfo) and (side=callerside) and
  493. // (current_procinfo.procdef <> p) then
  494. // begin
  495. // TMIPSProcinfo(current_procinfo).allocate_push_parasize(intparasize);
  496. // end;
  497. end;
  498. function TMIPSParaManager.create_varargs_paraloc_info(p : tabstractprocdef; varargspara:tvarargsparalist):longint;
  499. begin
  500. intparareg:=0;
  501. intparasize:=0;
  502. can_use_float := true;
  503. { Create Function result paraloc }
  504. create_funcretloc_info(p,callerside);
  505. { calculate the registers for the normal parameters }
  506. create_paraloc_info_intern(p,callerside,p.paras);
  507. { append the varargs }
  508. can_use_float := false;
  509. { restore correct intparasize value }
  510. if intparareg < 4 then
  511. intparasize:=intparareg * 4;
  512. create_paraloc_info_intern(p,callerside,varargspara);
  513. { We need to return the size allocated on the stack }
  514. result:=intparasize;
  515. end;
  516. function TMIPSParaManager.create_paraloc_info(p : tabstractprocdef; side: tcallercallee):longint;
  517. begin
  518. intparareg:=0;
  519. intparasize:=0;
  520. can_use_float := true;
  521. { Create Function result paraloc }
  522. create_funcretloc_info(p,side);
  523. create_paraloc_info_intern(p,side,p.paras);
  524. { We need to return the size allocated on the stack }
  525. result:=intparasize;
  526. end;
  527. begin
  528. ParaManager:=TMIPSParaManager.create;
  529. end.