tgcpu.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. {
  2. Copyright (C) 2019 Dmitry Boyarintsev
  3. This unit handles the temporary variables 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. }
  17. unit tgcpu;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. globtype,
  22. aasmdata,
  23. cgutils, cpubase,
  24. symtype,tgobj;
  25. type
  26. { TWasmLocal }
  27. TWasmLocal = class
  28. inuse : Boolean;
  29. index : integer;
  30. typ : TWasmBasicType;
  31. next : TWasmLocal; // next in the same basic type
  32. nextseq : TWasmLocal; // from 0 to max
  33. constructor create(atype: TWasmBasicType; aindex: integer);
  34. end;
  35. { TWasmLocalVars }
  36. TWasmLocalVars = class
  37. private
  38. last: TWasmLocal; // need public?
  39. public
  40. locv: array[TWasmBasicType] of TWasmLocal;
  41. ordered: array of integer;
  42. first: TWasmLocal; // first in sequence
  43. varindex: integer;
  44. constructor Create(astartindex: Integer = 0);
  45. destructor Destroy; override;
  46. function alloc(bt: TWasmBasicType): integer;
  47. procedure dealloc(bt: TWasmBasicType; index: integer);
  48. procedure dealloc(index: integer);
  49. end;
  50. { ttgwasm }
  51. ttgwasm = class(ttgobj)
  52. private
  53. procedure updateFirstTemp;
  54. procedure allocLocalVarToRef(wbt: TWasmBasicType; out ref: treference);
  55. procedure LocalVarToRef(idx: integer; size: Integer; out ref: treference);
  56. protected
  57. // procedure getimplicitobjtemp(list: TAsmList; def: tdef; temptype: ttemptype; out ref: treference);
  58. // function getifspecialtemp(list: TAsmList; def: tdef; forcesize: asizeint; temptype: ttemptype; out ref: treference): boolean;
  59. procedure alloctemp(list: TAsmList; size: asizeint; alignment: shortint; temptype: ttemptype; def: tdef; fini: boolean; out ref: treference); override;
  60. public
  61. localvars: TWasmLocalVars;
  62. constructor create; override;
  63. destructor destroy; override;
  64. procedure setfirsttemp(l : asizeint); override;
  65. procedure getlocal(list: TAsmList; size: asizeint; alignment: shortint; def: tdef; var ref: treference); override;
  66. procedure gethltemp(list: TAsmList; def: tdef; forcesize: asizeint; temptype: ttemptype; out ref: treference); override;
  67. procedure gethltempmanaged(list: TAsmList; def: tdef; temptype: ttemptype; out ref: treference); override;
  68. procedure ungettemp(list: TAsmList; const ref : treference); override;
  69. end;
  70. function defToWasmBasic(def: tdef; var wbt: TWasmBasicType): Boolean;
  71. implementation
  72. uses
  73. verbose,
  74. cgbase,
  75. symconst,symtable,symdef,symsym,symcpu,defutil,
  76. aasmbase,aasmcpu,
  77. hlcgobj,hlcgcpu, procinfo;
  78. function defToWasmBasic(def: tdef; var wbt: TWasmBasicType): Boolean;
  79. begin
  80. Result := assigned(def);
  81. if not Result then Exit;
  82. if is_pointer(def) then
  83. wbt := wbt_i32 // wasm32
  84. else if is_ordinal(def) then begin
  85. if is_64bit(def) then wbt := wbt_i64
  86. else wbt := wbt_i32;
  87. end else if is_real(def) then begin
  88. if is_single(def) then wbt := wbt_f32
  89. else wbt := wbt_f64; // real/double/extended
  90. end else
  91. Result := false;
  92. end;
  93. { TWasmLocal }
  94. constructor TWasmLocal.create(atype: TWasmBasicType;
  95. aindex: integer);
  96. begin
  97. typ:=atype;
  98. index:=aindex;
  99. end;
  100. { TWasmLocalVars }
  101. constructor TWasmLocalVars.Create(astartindex: Integer = 0);
  102. begin
  103. inherited Create;
  104. varindex := astartindex;
  105. end;
  106. destructor TWasmLocalVars.Destroy;
  107. var
  108. t : TWasmLocal;
  109. n : TWasmLocal;
  110. begin
  111. t := first;
  112. while Assigned(t) do
  113. begin
  114. n:=t;
  115. t:=t.nextseq;
  116. n.Free;
  117. end;
  118. inherited Destroy;
  119. end;
  120. { ttgwasm }
  121. //procedure ttgwasm.getimplicitobjtemp(list: TAsmList; def: tdef; temptype: ttemptype; out ref: treference);
  122. // var
  123. // sym: tsym;
  124. // pd: tprocdef;
  125. // begin
  126. // gettemp(list,java_jlobject.size,java_jlobject.alignment,temptype,ref);
  127. // list.concat(taicpu.op_sym(a_new,current_asmdata.RefAsmSymbol(tabstractrecorddef(def).jvm_full_typename(true),AT_METADATA)));
  128. // { the constructor doesn't return anything, so put a duplicate of the
  129. // self pointer on the evaluation stack for use as function result
  130. // after the constructor has run }
  131. // list.concat(taicpu.op_none(a_dup));
  132. // thlcgjvm(hlcg).incstack(list,2);
  133. // { call the constructor }
  134. // sym:=tsym(tabstractrecorddef(def).symtable.find('CREATE'));
  135. // if assigned(sym) and
  136. // (sym.typ=procsym) then
  137. // begin
  138. // pd:=tprocsym(sym).find_bytype_parameterless(potype_constructor);
  139. // if not assigned(pd) then
  140. // internalerror(2011032701);
  141. // end
  142. // else
  143. // internalerror(2011060301);
  144. // hlcg.a_call_name(list,pd,pd.mangledname,[],nil,false);
  145. // thlcgjvm(hlcg).decstack(list,1);
  146. // { store reference to instance }
  147. // thlcgjvm(hlcg).a_load_stack_ref(list,java_jlobject,ref,0);
  148. // end;
  149. //function ttgwasm.getifspecialtemp(list: TAsmList; def: tdef; forcesize: asizeint; temptype: ttemptype; out ref: treference): boolean;
  150. // var
  151. // eledef: tdef;
  152. // ndim: longint;
  153. // sym: tsym;
  154. // pd: tprocdef;
  155. // begin
  156. // result:=false;
  157. // case def.typ of
  158. // arraydef:
  159. // begin
  160. // if not is_dynamic_array(def) then
  161. // begin
  162. // { allocate an array of the right size }
  163. // gettemp(list,java_jlobject.size,java_jlobject.alignment,temptype,ref);
  164. // ndim:=0;
  165. // eledef:=def;
  166. // repeat
  167. // if forcesize<>-1 then
  168. // thlcgjvm(hlcg).a_load_const_stack(list,s32inttype,forcesize div tarraydef(eledef).elesize,R_INTREGISTER)
  169. // else
  170. // thlcgjvm(hlcg).a_load_const_stack(list,s32inttype,tarraydef(eledef).elecount,R_INTREGISTER);
  171. // eledef:=tarraydef(eledef).elementdef;
  172. // inc(ndim);
  173. // forcesize:=-1;
  174. // until (eledef.typ<>arraydef) or
  175. // is_dynamic_array(eledef);
  176. // eledef:=tarraydef(def).elementdef;
  177. // thlcgjvm(hlcg).g_newarray(list,def,ndim);
  178. // thlcgjvm(hlcg).a_load_stack_ref(list,java_jlobject,ref,0);
  179. // result:=true;
  180. // end;
  181. // end;
  182. // recorddef:
  183. // begin
  184. // getimplicitobjtemp(list,def,temptype,ref);
  185. // result:=true;
  186. // end;
  187. // setdef:
  188. // begin
  189. // if tsetdef(def).elementdef.typ=enumdef then
  190. // begin
  191. // { load enum class type }
  192. // list.concat(taicpu.op_sym(a_ldc,current_asmdata.RefAsmSymbol(tcpuenumdef(tenumdef(tsetdef(def).elementdef).getbasedef).classdef.jvm_full_typename(true),AT_METADATA)));
  193. // thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,1);
  194. // { call tenumset.noneOf() class method }
  195. // sym:=tsym(tobjectdef(java_juenumset).symtable.find('NONEOF'));
  196. // if assigned(sym) and
  197. // (sym.typ=procsym) then
  198. // begin
  199. // if tprocsym(sym).procdeflist.Count<>1 then
  200. // internalerror(2011062801);
  201. // pd:=tprocdef(tprocsym(sym).procdeflist[0]);
  202. // hlcg.a_call_name(list,pd,pd.mangledname,[],nil,false);
  203. // end;
  204. // { static calls method replaces parameter with set instance
  205. // -> no change in stack height }
  206. // end
  207. // else
  208. // begin
  209. // list.concat(taicpu.op_sym(a_new,current_asmdata.RefAsmSymbol(java_jubitset.jvm_full_typename(true),AT_METADATA)));
  210. // { the constructor doesn't return anything, so put a duplicate of the
  211. // self pointer on the evaluation stack for use as function result
  212. // after the constructor has run }
  213. // list.concat(taicpu.op_none(a_dup));
  214. // thlcgjvm(hlcg).incstack(list,2);
  215. // { call the constructor }
  216. // sym:=tsym(java_jubitset.symtable.find('CREATE'));
  217. // if assigned(sym) and
  218. // (sym.typ=procsym) then
  219. // begin
  220. // pd:=tprocsym(sym).find_bytype_parameterless(potype_constructor);
  221. // if not assigned(pd) then
  222. // internalerror(2011062802);
  223. // end
  224. // else
  225. // internalerror(2011062803);
  226. // hlcg.a_call_name(list,pd,pd.mangledname,[],nil,false);
  227. // { duplicate self pointer is removed }
  228. // thlcgjvm(hlcg).decstack(list,1);
  229. // end;
  230. // { store reference to instance }
  231. // gettemp(list,java_jlobject.size,java_jlobject.alignment,temptype,ref);
  232. // thlcgjvm(hlcg).a_load_stack_ref(list,java_jlobject,ref,0);
  233. // result:=true;
  234. // end;
  235. // procvardef:
  236. // begin
  237. // if not tprocvardef(def).is_addressonly then
  238. // begin
  239. // getimplicitobjtemp(list,tcpuprocvardef(def).classdef,temptype,ref);
  240. // result:=true;
  241. // end;
  242. // end;
  243. // stringdef:
  244. // begin
  245. // if is_shortstring(def) then
  246. // begin
  247. // gettemp(list,java_jlobject.size,java_jlobject.alignment,temptype,ref);
  248. // { add the maxlen parameter (s8inttype because parameters must
  249. // be sign extended) }
  250. // thlcgjvm(hlcg).a_load_const_stack(list,s8inttype,shortint(tstringdef(def).len),R_INTREGISTER);
  251. // { call the constructor }
  252. // sym:=tsym(tobjectdef(java_shortstring).symtable.find('CREATEEMPTY'));
  253. // if assigned(sym) and
  254. // (sym.typ=procsym) then
  255. // begin
  256. // if tprocsym(sym).procdeflist.Count<>1 then
  257. // internalerror(2011052404);
  258. // pd:=tprocdef(tprocsym(sym).procdeflist[0]);
  259. // hlcg.a_call_name(list,pd,pd.mangledname,[],nil,false);
  260. // end;
  261. // { static calls method replaces parameter with string instance
  262. // -> no change in stack height }
  263. // { store reference to instance }
  264. // thlcgjvm(hlcg).a_load_stack_ref(list,java_jlobject,ref,0);
  265. // result:=true;
  266. // end;
  267. // end;
  268. // filedef:
  269. // begin
  270. // case tfiledef(def).filetyp of
  271. // ft_text:
  272. // result:=getifspecialtemp(list,search_system_type('TEXTREC').typedef,forcesize,temptype,ref);
  273. // ft_typed,
  274. // ft_untyped:
  275. // result:=getifspecialtemp(list,search_system_type('FILEREC').typedef,forcesize,temptype,ref);
  276. // end;
  277. // end;
  278. // else
  279. // ;
  280. // end;
  281. // end;
  282. procedure ttgwasm.alloctemp(list: TAsmList; size: asizeint; alignment: shortint; temptype: ttemptype; def: tdef; fini: boolean; out ref: treference);
  283. begin
  284. inherited;
  285. //Internalerror(2019091802);
  286. { the WebAssembly only supports 1 slot (= 4 bytes in FPC) and 2 slot (= 8 bytes in
  287. FPC) temps on the stack. double and int64 are 2 slots, the rest is one slot.
  288. There are no problems with reusing the same slot for a value of a different
  289. type. There are no alignment requirements either. }
  290. {if size<4 then
  291. size:=4;
  292. if not(size in [4,8]) then
  293. internalerror(2010121401);
  294. inherited alloctemp(list, size shr 2, 1, temptype, def, false, ref);}
  295. end;
  296. procedure ttgwasm.updateFirstTemp;
  297. begin
  298. firsttemp := localvars.varindex;
  299. if lasttemp<firsttemp then lasttemp := firsttemp;
  300. end;
  301. constructor ttgwasm.create;
  302. begin
  303. inherited create;
  304. direction := 1; // temp variables are allocated as "locals", and it starts with 0 and goes beyond!
  305. localvars:=TWasmLocalVars.Create;
  306. end;
  307. destructor ttgwasm.destroy;
  308. begin
  309. localvars.Free;
  310. inherited destroy;
  311. end;
  312. procedure ttgwasm.setfirsttemp(l: asizeint);
  313. begin
  314. firsttemp:=l;
  315. lasttemp:=l;
  316. localvars.varindex := l; //?
  317. end;
  318. procedure ttgwasm.getlocal(list: TAsmList; size: asizeint; alignment: shortint; def: tdef; var ref: treference);
  319. var
  320. wbt : TWasmBasicType;
  321. idx : integer;
  322. begin
  323. if defToWasmBasic(def, wbt) then
  324. alloclocalVarToRef(wbt, ref)
  325. else begin
  326. //Internalerror(2019091801); // no support of structural type
  327. inherited;
  328. end;
  329. end;
  330. procedure ttgwasm.gethltemp(list: TAsmList; def: tdef; forcesize: asizeint; temptype: ttemptype; out ref: treference);
  331. var
  332. wbt: TWasmBasicType;
  333. begin
  334. if Assigned(def) and defToWasmBasic(def, wbt) then begin
  335. allocLocalVarToRef(wbt, ref);
  336. end else
  337. inherited;
  338. end;
  339. procedure ttgwasm.gethltempmanaged(list: TAsmList; def: tdef; temptype: ttemptype; out ref: treference);
  340. begin
  341. inherited;
  342. end;
  343. procedure ttgwasm.ungettemp(list: TAsmList; const ref: treference);
  344. begin
  345. if ref.base=NR_LOCAL_STACK_POINTER_REG then
  346. localvars.dealloc(ref.offset)
  347. else
  348. inherited;
  349. end;
  350. procedure ttgwasm.allocLocalVarToRef(wbt: TWasmBasicType; out ref: treference);
  351. var
  352. idx : integer;
  353. begin
  354. idx := localvars.alloc(wbt);
  355. localVarToRef(idx, 1, ref);
  356. end;
  357. procedure ttgwasm.localVarToRef(idx: integer; size: integer; out ref: treference);
  358. begin
  359. reference_reset_base(ref,NR_LOCAL_STACK_POINTER_REG,idx,ctempposinvalid,size,[]);
  360. updateFirstTemp;
  361. end;
  362. function TWasmLocalVars.alloc(bt: TWasmBasicType): integer;
  363. var
  364. i : integer;
  365. lc : TWasmLocal;
  366. t : TWasmLocal;
  367. begin
  368. lc := locv[bt];
  369. t := nil;
  370. while Assigned(lc) and (lc.inuse) do begin
  371. t := lc;
  372. lc := lc.next;
  373. end;
  374. if Assigned(lc) then begin
  375. lc.inuse := true;
  376. end else begin
  377. lc := TWasmLocal.Create(bt, varindex);
  378. if Assigned(t)
  379. then t.next := lc
  380. else locv[bt]:=lc;
  381. lc.inuse:=true;
  382. inc(varindex);
  383. if Assigned(last) then last.nextseq := lc;
  384. if not Assigned(first) then first := lc;
  385. last := lc;
  386. end;
  387. alloc := lc.index;
  388. end;
  389. procedure TWasmLocalVars.dealloc(bt: TWasmBasicType; index: integer);
  390. var
  391. lc : TWasmLocal;
  392. begin
  393. lc := locv[bt];
  394. while Assigned(lc) and (lc.index <> index) do
  395. lc := lc.next;
  396. if Assigned(lc) then lc.inuse := false;
  397. end;
  398. procedure TWasmLocalVars.dealloc(index: integer);
  399. var
  400. lc : TWasmLocal;
  401. begin
  402. lc := first;
  403. while Assigned(lc) and (lc.index <> index) do
  404. lc := lc.nextseq;
  405. if Assigned(lc) then lc.inuse := false;
  406. end;
  407. initialization
  408. tgobjclass:=ttgwasm;
  409. end.