cpupara.pas 11 KB

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