cpupara.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. {
  2. Copyright (c) 2019 by Dmtiry Boyarintsev
  3. Calling conventions for the WebAssembly
  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. symconst,symbase,symsym,symtype,symdef,paramgr,parabase,cgbase,cgutils;
  25. type
  26. { tcpuparamanager }
  27. tcpuparamanager=class(TParaManager)
  28. function get_saved_registers_int(calloption: tproccalloption): tcpuregisterarray;override;
  29. function push_high_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;override;
  30. function keep_para_array_range(varspez: tvarspez; def: tdef; calloption: tproccalloption): boolean; override;
  31. function push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;override;
  32. function push_size(varspez: tvarspez; def: tdef; calloption: tproccalloption): longint;override;
  33. function create_paraloc_info(p : TAbstractProcDef; side: tcallercallee):longint;override;
  34. function create_varargs_paraloc_info(p : tabstractprocdef; side: tcallercallee; varargspara:tvarargsparalist):longint;override;
  35. function get_funcretloc(p : tabstractprocdef; side: tcallercallee; forcetempdef: tdef): tcgpara;override;
  36. { true if the location in paraloc can be reused as localloc }
  37. function param_use_paraloc(const cgpara: tcgpara): boolean; override;
  38. { Returns true if the return value is actually a parameter pointer }
  39. function ret_in_param(def:tdef;pd:tabstractprocdef):boolean;override;
  40. function is_stack_paraloc(paraloc: pcgparalocation): boolean;override;
  41. private
  42. procedure create_paraloc_info_intern(p : tabstractprocdef; side: tcallercallee; paras: tparalist;
  43. var parasize:longint);
  44. end;
  45. implementation
  46. uses
  47. cutils,verbose,systems,
  48. defutil,wasmdef,
  49. aasmcpu,
  50. hlcgobj;
  51. function tcpuparamanager.get_saved_registers_int(calloption: tproccalloption): tcpuregisterarray;
  52. const
  53. { dummy, not used for WebAssembly }
  54. saved_regs: {$ifndef VER3_0}tcpuregisterarray{$else}array [0..0] of tsuperregister{$endif} = (RS_NO);
  55. begin
  56. result:=saved_regs;
  57. end;
  58. function tcpuparamanager.push_high_param(varspez: tvarspez; def: tdef; calloption: tproccalloption): boolean;
  59. begin
  60. { we don't need a separate high parameter, since all arrays in Java
  61. have an implicit associated length }
  62. if not is_open_array(def) and
  63. not is_array_of_const(def) then
  64. result:=inherited
  65. else
  66. result:=false;
  67. end;
  68. function tcpuparamanager.keep_para_array_range(varspez: tvarspez; def: tdef; calloption: tproccalloption): boolean;
  69. begin
  70. { even though these don't need a high parameter (see push_high_param),
  71. we do have to keep the original parameter's array length because it's
  72. used by the compiler (to determine the size of the array to construct
  73. to pass to an array of const parameter) }
  74. if not is_array_of_const(def) then
  75. result:=inherited
  76. else
  77. result:=true;
  78. end;
  79. { true if a parameter is too large to copy and only the address is pushed }
  80. function tcpuparamanager.push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;
  81. begin
  82. result:=false;
  83. { var,out,constref always require address }
  84. if varspez in [vs_var,vs_out,vs_constref] then
  85. begin
  86. result:=true;
  87. exit;
  88. end;
  89. { Only vs_const, vs_value here }
  90. case def.typ of
  91. variantdef,
  92. formaldef :
  93. result:=true;
  94. recorddef :
  95. begin
  96. { Delphi stdcall passes records on the stack for call by value }
  97. result:=(varspez=vs_const) or (def.size=0);
  98. end;
  99. arraydef :
  100. begin
  101. result:=(tarraydef(def).highrange>=tarraydef(def).lowrange) or
  102. is_open_array(def) or
  103. is_array_of_const(def) or
  104. is_array_constructor(def);
  105. end;
  106. objectdef :
  107. result:=is_object(def);
  108. stringdef :
  109. result:= (tstringdef(def).stringtype in [st_shortstring,st_longstring]);
  110. procvardef :
  111. result:=not(calloption in cdecl_pocalls) and not tprocvardef(def).is_addressonly;
  112. setdef :
  113. result:=not is_smallset(def);
  114. else
  115. ;
  116. end;
  117. end;
  118. function tcpuparamanager.push_size(varspez: tvarspez; def: tdef; calloption: tproccalloption): longint;
  119. begin
  120. { all aggregate types are emulated using indirect pointer types }
  121. result:=inherited;
  122. end;
  123. function tcpuparamanager.get_funcretloc(p : tabstractprocdef; side: tcallercallee; forcetempdef: tdef): tcgpara;
  124. var
  125. paraloc : pcgparalocation;
  126. retcgsize : tcgsize;
  127. begin
  128. result.init;
  129. result.alignment:=get_para_align(p.proccalloption);
  130. if not assigned(forcetempdef) then
  131. result.def:=p.returndef
  132. else
  133. begin
  134. result.def:=forcetempdef;
  135. result.temporary:=true;
  136. end;
  137. result.def:=get_para_push_size(result.def);
  138. { void has no location }
  139. if is_void(result.def) then
  140. begin
  141. paraloc:=result.add_location;
  142. result.size:=OS_NO;
  143. result.intsize:=0;
  144. paraloc^.size:=OS_NO;
  145. paraloc^.def:=voidtype;
  146. paraloc^.loc:=LOC_VOID;
  147. exit;
  148. end;
  149. { Constructors return self instead of a boolean }
  150. if (p.proctypeoption=potype_constructor) then
  151. begin
  152. retcgsize:=OS_INT;
  153. result.intsize:=sizeof(pint);
  154. end
  155. //todo: wasm should have the similar
  156. {else if jvmimplicitpointertype(result.def) then
  157. begin
  158. retcgsize:=OS_ADDR;
  159. result.def:=cpointerdef.getreusable_no_free(result.def);
  160. end}
  161. else
  162. begin
  163. retcgsize:=def_cgsize(result.def);
  164. result.intsize:=result.def.size;
  165. end;
  166. result.size:=retcgsize;
  167. paraloc:=result.add_location;
  168. { all values are returned on the evaluation stack }
  169. paraloc^.loc:=LOC_REFERENCE;
  170. paraloc^.reference.index:=NR_EVAL_STACK_BASE;
  171. paraloc^.reference.offset:=0;
  172. paraloc^.size:=result.size;
  173. paraloc^.def:=result.def;
  174. end;
  175. function tcpuparamanager.param_use_paraloc(const cgpara: tcgpara): boolean;
  176. begin
  177. { all parameters are copied by the VM to local variable locations }
  178. result:=true;
  179. end;
  180. function tcpuparamanager.ret_in_param(def:tdef;pd:tabstractprocdef):boolean;
  181. begin
  182. { not as efficient as returning in param for jvmimplicitpointertypes,
  183. but in the latter case the routines are harder to use from Java
  184. (especially for arrays), because the caller then manually has to
  185. allocate the instance/array of the right size }
  186. Result:=false;
  187. end;
  188. function tcpuparamanager.is_stack_paraloc(paraloc: pcgparalocation): boolean;
  189. begin
  190. { all parameters are passed on the evaluation stack }
  191. result:=true;
  192. end;
  193. function tcpuparamanager.create_varargs_paraloc_info(p : tabstractprocdef; side: tcallercallee; varargspara:tvarargsparalist):longint;
  194. var
  195. parasize : longint;
  196. begin
  197. parasize:=0;
  198. { calculate the registers for the normal parameters }
  199. create_paraloc_info_intern(p,side,p.paras,parasize);
  200. { append the varargs }
  201. if assigned(varargspara) then
  202. begin
  203. if side=callerside then
  204. create_paraloc_info_intern(p,side,varargspara,parasize)
  205. else
  206. internalerror(2019021924);
  207. end;
  208. create_funcretloc_info(p,side);
  209. result:=parasize;
  210. end;
  211. procedure tcpuparamanager.create_paraloc_info_intern(p : tabstractprocdef; side: tcallercallee;paras:tparalist;
  212. var parasize:longint);
  213. var
  214. paraloc : pcgparalocation;
  215. i : integer;
  216. hp : tparavarsym;
  217. paracgsize : tcgsize;
  218. paraofs : longint;
  219. paradef : tdef;
  220. begin
  221. paraofs:=0;
  222. for i:=0 to paras.count-1 do
  223. begin
  224. hp:=tparavarsym(paras[i]);
  225. if push_copyout_param(hp.varspez,hp.vardef,p.proccalloption) then
  226. begin
  227. { passed via array reference (instead of creating a new array
  228. type for every single parameter, use java_jlobject) }
  229. paracgsize:=OS_ADDR;
  230. paradef:=ptruinttype;
  231. end
  232. else if push_addr_param(hp.varspez, hp.vardef,p.proccalloption) then
  233. begin
  234. paracgsize:=OS_ADDR;
  235. paradef:=cpointerdef.getreusable_no_free(hp.vardef);
  236. end
  237. else
  238. begin
  239. paracgsize:=def_cgsize(hp.vardef);
  240. if paracgsize=OS_NO then
  241. paracgsize:=OS_ADDR;
  242. paradef:=hp.vardef;
  243. end;
  244. paradef:=get_para_push_size(paradef);
  245. hp.paraloc[side].reset;
  246. hp.paraloc[side].size:=paracgsize;
  247. hp.paraloc[side].def:=paradef;
  248. hp.paraloc[side].alignment:=std_param_align;
  249. hp.paraloc[side].intsize:=tcgsize2size[paracgsize];
  250. paraloc:=hp.paraloc[side].add_location;
  251. { All parameters are passed on the evaluation stack, pushed from
  252. left to right (including self, if applicable). At the callee side,
  253. they're available as local variables 0..n-1 }
  254. paraloc^.loc:=LOC_REFERENCE;
  255. paraloc^.reference.offset:=paraofs;
  256. paraloc^.size:=paracgsize;
  257. paraloc^.def:=paradef;
  258. case side of
  259. callerside:
  260. begin
  261. paraloc^.loc:=LOC_REFERENCE;
  262. { we use a fake loc_reference to indicate the stack location;
  263. the offset (set above) will be used by ncal to order the
  264. parameters so they will be pushed in the right order }
  265. paraloc^.reference.index:=NR_EVAL_STACK_BASE;
  266. end;
  267. calleeside:
  268. begin
  269. paraloc^.loc:=LOC_REFERENCE;
  270. paraloc^.reference.index:=NR_STACK_POINTER_REG;
  271. end;
  272. else
  273. ;
  274. end;
  275. { 2 slots for 64 bit integers and floats, 1 slot for the rest }
  276. inc(paraofs);
  277. end;
  278. parasize:=paraofs;
  279. end;
  280. function tcpuparamanager.create_paraloc_info(p : tabstractprocdef; side: tcallercallee):longint;
  281. var
  282. parasize : longint;
  283. begin
  284. parasize:=0;
  285. create_paraloc_info_intern(p,side,p.paras,parasize);
  286. { Create Function result paraloc }
  287. create_funcretloc_info(p,side);
  288. { We need to return the size allocated on the stack }
  289. result:=parasize;
  290. end;
  291. initialization
  292. ParaManager:=tcpuparamanager.create;
  293. end.