cpupara.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. {
  2. Copyright (c) 1998-2002 by Florian Klaempfl
  3. Calling conventions for the SPARC64
  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,aasmdata,
  23. cpubase,cpuinfo,
  24. sppara,
  25. symconst,symbase,symsym,symtype,symdef,paramgr,parabase,cgbase,cgutils;
  26. type
  27. tcpuparamanager=class(TSparcParaManager)
  28. procedure create_paraloc_info_intern(p : tabstractprocdef; side : tcallercallee; paras : tparalist; var curintreg : LongInt;
  29. curfloatreg : tsuperregister; var cur_stack_offset : aword);override;
  30. function push_addr_param(varspez : tvarspez; def : tdef; calloption : tproccalloption) : boolean;override;
  31. function ret_in_param(def : tdef; pd : tabstractprocdef) : boolean;override;
  32. function get_funcretloc(p : tabstractprocdef; side : tcallercallee; forcetempdef : tdef) : tcgpara;override;
  33. private
  34. function push_addr_param_intern(varspez : tvarspez; def : tdef; calloption : tproccalloption; recsizelimit : aword) : boolean;
  35. procedure create_paraloc1_info_intern(p : tabstractprocdef; side : tcallercallee; paradef : tdef; var loc : TCGPara; varspez : tvarspez; varoptions : tvaroptions; recsizelimit : aword;
  36. var curintreg : LongInt; var curfloatreg : tsuperregister; var cur_stack_offset : aword);
  37. end;
  38. implementation
  39. uses
  40. cutils,verbose,systems,
  41. defutil,
  42. cgobj;
  43. { true if a parameter is too large to copy and only the address is pushed }
  44. function tcpuparamanager.push_addr_param_intern(varspez:tvarspez;def : tdef;calloption : tproccalloption;recsizelimit : aword) : boolean;
  45. begin
  46. result:=false;
  47. { var,out,constref always require address }
  48. if varspez in [vs_var,vs_out,vs_constref] then
  49. begin
  50. result:=true;
  51. exit;
  52. end;
  53. case def.typ of
  54. arraydef:
  55. result:=(tarraydef(def).highrange>=tarraydef(def).lowrange) or
  56. is_open_array(def) or
  57. is_array_of_const(def) or
  58. is_array_constructor(def);
  59. { Fix codegen problem for empty record by always passing by address a zero-sized record }
  60. recorddef:
  61. result:=(def.size>recsizelimit) or (def.size=0);
  62. variantdef:
  63. result:=false;
  64. formaldef :
  65. result:=true;
  66. objectdef :
  67. result:=(is_object(def) and (def.size>recsizelimit));
  68. stringdef :
  69. result:=(tstringdef(def).stringtype in [st_shortstring,st_longstring]);
  70. procvardef :
  71. result:=false;
  72. setdef :
  73. result:=not is_smallset(def);
  74. else
  75. ;
  76. end;
  77. end;
  78. { true if a parameter is too large to copy and only the address is pushed }
  79. function tcpuparamanager.push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;
  80. begin
  81. result:=push_addr_param_intern(varspez,def,calloption,16);
  82. end;
  83. function tcpuparamanager.ret_in_param(def:tdef;pd:tabstractprocdef):boolean;
  84. begin
  85. if handle_common_ret_in_param(def,pd,result) then
  86. exit;
  87. case def.typ of
  88. { it is a matter of interpretation, if objects should be returned in registers according to the abi as the
  89. abi talks only about structures and unions
  90. at least for the compiler, it is a problem, if an object is returned in registers
  91. consider
  92. tobject1 = object
  93. function f : tobject1;
  94. ...
  95. contructor init;
  96. end;
  97. the constructor changes the size of tobject1, so its return location might change from register to memory, this
  98. is something the compiler could not handle currently, so we do not return objects in registers yet
  99. objectdef:
  100. begin
  101. result:=is_object(def) and (def.size>32);
  102. exit;
  103. end;}
  104. recorddef:
  105. begin
  106. result:=def.size>32;
  107. exit;
  108. end;
  109. else
  110. ;
  111. end;
  112. result:=inherited ret_in_param(def,pd);
  113. end;
  114. procedure tcpuparamanager.create_paraloc1_info_intern(
  115. p : tabstractprocdef; side: tcallercallee;paradef:tdef;var loc : TCGPara;varspez : tvarspez;varoptions : tvaroptions;recsizelimit : aword;
  116. var curintreg: LongInt; var curfloatreg: tsuperregister; var cur_stack_offset : aword);
  117. procedure nextloc(currsize : TCgSize);
  118. begin
  119. if curintreg>high(tparasupregs) then
  120. begin
  121. if (currsize<low(tcgsize2size)) or (currsize>high(tcgsize2size)) then
  122. internalerror(2017080101);
  123. { Parameters are aligned at 8 bytes }
  124. inc(cur_stack_offset,align(tcgsize2size[currsize],sizeof(pint)));
  125. end;
  126. inc(curintreg);
  127. if currsize=OS_F128 then
  128. inc(curfloatreg,4)
  129. else
  130. inc(curfloatreg,2);
  131. end;
  132. var
  133. paraloc : pcgparalocation;
  134. paracgsize : tcgsize;
  135. hparasupregs : pparasupregs;
  136. paralen : longint;
  137. begin
  138. if side=callerside then
  139. hparasupregs:=@paraoutsupregs
  140. else
  141. hparasupregs:=@parainsupregs;
  142. { currently only support C-style array of const,
  143. there should be no location assigned to the vararg array itself }
  144. if (p.proccalloption in cstylearrayofconst) and
  145. is_array_of_const(paradef) then
  146. begin
  147. paraloc:=loc.add_location;
  148. { hack: the paraloc must be valid, but is not actually used }
  149. paraloc^.loc:=LOC_REGISTER;
  150. paraloc^.register:=NR_G0;
  151. paraloc^.size:=OS_ADDR;
  152. paraloc^.def:=voidpointertype;
  153. exit;
  154. end;
  155. if push_addr_param_intern(varspez,paradef,p.proccalloption,recsizelimit) then
  156. begin
  157. paracgsize:=OS_ADDR;
  158. paradef:=cpointerdef.getreusable_no_free(paradef);
  159. end
  160. else
  161. begin
  162. paracgsize:=def_cgsize(paradef);
  163. if paradef.typ=formaldef then
  164. begin
  165. paracgsize:=OS_ADDR;
  166. paradef:=voidpointertype;
  167. end;
  168. end;
  169. loc.reset;
  170. loc.size:=paracgsize;
  171. loc.def:=paradef;
  172. if side=callerside then
  173. loc.Alignment:=std_param_align
  174. else
  175. loc.Alignment:=paradef.alignment;
  176. { sparc64 returns records up to a size of 32 in register, we cannot encode this
  177. in paracgsize, so paracgsize is OS_NO in this case }
  178. if paracgsize=OS_NO then
  179. paralen:=paradef.size
  180. else
  181. paralen:=tcgsize2size[paracgsize];
  182. loc.intsize:=paralen;
  183. while paralen>0 do
  184. begin
  185. paraloc:=loc.add_location;
  186. paraloc^.size:=paracgsize;
  187. paraloc^.def:=paradef;
  188. { ret in param? }
  189. if vo_is_funcret in varoptions then
  190. begin
  191. paraloc^.loc:=LOC_REFERENCE;
  192. paraloc^.reference.offset:=128;
  193. if side=callerside then
  194. paraloc^.reference.index:=NR_STACK_POINTER_REG
  195. else
  196. paraloc^.reference.index:=NR_FRAME_POINTER_REG;
  197. inc(paraloc^.reference.offset,STACK_BIAS);
  198. end
  199. { In case of po_delphi_nested_cc, the parent frame pointer
  200. is always passed on the stack. }
  201. else if (curintreg<=high(tparasupregs)) and
  202. (not(vo_is_parentfp in varoptions) or
  203. not(po_delphi_nested_cc in p.procoptions)) then
  204. begin
  205. if paraloc^.size in [OS_F32,OS_F64,OS_F128] then
  206. begin
  207. paraloc^.loc:=LOC_FPUREGISTER;
  208. case paraloc^.size of
  209. OS_F32:
  210. { singles are put into the uneven register }
  211. paraloc^.register:=newreg(R_FPUREGISTER,curfloatreg+1,R_SUBFS);
  212. OS_F64:
  213. paraloc^.register:=newreg(R_FPUREGISTER,curfloatreg,R_SUBFD);
  214. OS_F128:
  215. paraloc^.register:=newreg(R_FPUREGISTER,curfloatreg,R_SUBFQ);
  216. else
  217. Internalerror(2017072301);
  218. end;
  219. end
  220. else
  221. begin
  222. if paracgsize in [OS_NO,OS_128,OS_S128] then
  223. begin
  224. if paralen>4 then
  225. begin
  226. paraloc^.size:=OS_INT;
  227. paraloc^.def:=u64inttype;
  228. end
  229. else
  230. begin
  231. { for 3-byte records }
  232. paraloc^.size:=OS_32;
  233. paraloc^.def:=u32inttype;
  234. end;
  235. end;
  236. paraloc^.loc:=LOC_REGISTER;
  237. paraloc^.register:=newreg(R_INTREGISTER,hparasupregs^[curintreg],R_SUBWHOLE);
  238. { left align }
  239. if (target_info.endian=endian_big) and
  240. not(paraloc^.size in [OS_64,OS_S64]) and
  241. (paradef.typ in [setdef,recorddef,arraydef,objectdef]) then
  242. begin
  243. paraloc^.shiftval:=-(8-tcgsize2size[paraloc^.size])*8;
  244. paraloc^.Size:=OS_64;
  245. end;
  246. end;
  247. nextloc(paraloc^.Size);
  248. end
  249. else
  250. begin
  251. paraloc^.loc:=LOC_REFERENCE;
  252. paraloc^.reference.offset:=target_info.first_parm_offset+cur_stack_offset;
  253. if side=callerside then
  254. paraloc^.reference.index:=NR_STACK_POINTER_REG
  255. else
  256. paraloc^.reference.index:=NR_FRAME_POINTER_REG;
  257. inc(paraloc^.reference.offset,STACK_BIAS);
  258. if (target_info.endian=endian_big) and
  259. (paralen<tcgsize2size[OS_INT]) and
  260. (paradef.typ<>recorddef) then
  261. inc(paraloc^.reference.offset,4-paralen);
  262. { Parameters are aligned to 8 byte boundaries }
  263. inc(cur_stack_offset,align(paralen,8));
  264. { a stack location covers always the remainder of a parameter }
  265. exit;
  266. end;
  267. dec(paralen,tcgsize2size[paraloc^.size]);
  268. end;
  269. end;
  270. procedure tcpuparamanager.create_paraloc_info_intern(p : tabstractprocdef; side: tcallercallee;paras:tparalist;
  271. var curintreg: LongInt; curfloatreg: tsuperregister; var cur_stack_offset : aword);
  272. var
  273. i : integer;
  274. begin
  275. for i:=0 to paras.count-1 do
  276. create_paraloc1_info_intern(p,side,tparavarsym(paras[i]).vardef,tparavarsym(paras[i]).paraloc[side],tparavarsym(paras[i]).varspez,
  277. tparavarsym(paras[i]).varoptions,16,curintreg,curfloatreg,cur_stack_offset);
  278. end;
  279. function tcpuparamanager.get_funcretloc(p : tabstractprocdef; side: tcallercallee; forcetempdef: tdef): tcgpara;
  280. var
  281. paraloc : pcgparalocation;
  282. retcgsize : tcgsize;
  283. curintreg : LongInt;
  284. curfloatreg : tsuperregister;
  285. cur_stack_offset : aword;
  286. begin
  287. if set_common_funcretloc_info(p,forcetempdef,retcgsize,result) then
  288. exit;
  289. if ret_in_param(result.def,p) then
  290. Internalerror(2017080601);
  291. if is_record(result.def) or is_object(result.def) then
  292. begin
  293. curintreg:=0;
  294. curfloatreg:=RS_F0;
  295. cur_stack_offset:=0;
  296. create_paraloc1_info_intern(p,side,result.def,result,vs_value,
  297. [],32,curintreg,curfloatreg,cur_stack_offset);
  298. { sparc64 calling conventions are difficult, so better check if everything is ok }
  299. if result.location^.loc=LOC_INVALID then
  300. Internalerror(2017080501);
  301. end
  302. else
  303. begin
  304. paraloc:=result.add_location;
  305. { Return in FPU register? }
  306. if result.def.typ=floatdef then
  307. begin
  308. paraloc^.loc:=LOC_FPUREGISTER;
  309. paraloc^.register:=NR_FPU_RESULT_REG;
  310. if retcgsize=OS_F64 then
  311. setsubreg(paraloc^.register,R_SUBFD);
  312. paraloc^.size:=retcgsize;
  313. paraloc^.def:=result.def;
  314. end
  315. else
  316. { Return in register }
  317. begin
  318. paraloc^.loc:=LOC_REGISTER;
  319. paraloc^.size:=retcgsize;
  320. paraloc^.def:=result.def;
  321. if (side=callerside) then
  322. paraloc^.register:=newreg(R_INTREGISTER,RS_FUNCTION_RESULT_REG,cgsize2subreg(R_INTREGISTER,retcgsize))
  323. else
  324. paraloc^.register:=newreg(R_INTREGISTER,RS_FUNCTION_RETURN_REG,cgsize2subreg(R_INTREGISTER,retcgsize));
  325. end;
  326. end;
  327. end;
  328. begin
  329. ParaManager:=tcpuparamanager.create;
  330. end.