cpupi.pas 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045
  1. {
  2. Copyright (c) 2002-2010 by Florian Klaempfl and Jonas Maebe
  3. This unit contains the CPU specific part of tprocinfo
  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 cpupi;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. cutils,globtype,aasmdata,aasmcpu,aasmtai,
  22. procinfo,cpubase,cpuinfo, symtype,aasmbase,cgbase,
  23. psub, cclasses;
  24. type
  25. { tcpuprocinfo }
  26. tcpuprocinfo=class(tcgprocinfo)
  27. private
  28. FFuncType: TWasmFuncType;
  29. FLocals: array of TWasmBasicType;
  30. FParametersCount: Integer;
  31. FFirstFreeLocal: Integer;
  32. FAllocatedLocals: array of TWasmBasicType;
  33. FGotoTargets: TFPHashObjectList;
  34. function ConvertBranchTargetNumbersToLabels(ai: tai; blockstack: twasmstruc_stack): TAsmMapFuncResult;
  35. function ConvertIfToBrIf(ai: tai; blockstack: twasmstruc_stack): TAsmMapFuncResult;
  36. function ConvertLoopToBr(ai: tai; blockstack: twasmstruc_stack): TAsmMapFuncResult;
  37. function StripBlockInstructions(ai: tai; blockstack: twasmstruc_stack): TAsmMapFuncResult;
  38. { used for allocating locals during the postprocess_code stage (i.e. after register allocation) }
  39. function AllocWasmLocal(wbt: TWasmBasicType): Integer;
  40. function GetLocalType(localidx: Integer): TWasmBasicType;
  41. public
  42. { label to the nearest local exception handler }
  43. CurrRaiseLabel : tasmlabel;
  44. constructor create(aparent: tprocinfo); override;
  45. destructor destroy; override;
  46. function calc_stackframe_size : longint;override;
  47. procedure setup_eh; override;
  48. procedure generate_exit_label(list: tasmlist); override;
  49. procedure postprocess_code; override;
  50. procedure set_first_temp_offset;override;
  51. procedure add_goto_target(l : tasmlabel);
  52. function is_goto_target(l : tasmsymbol): Boolean;
  53. end;
  54. implementation
  55. uses
  56. systems,verbose,globals,tgcpu,cgexcept,
  57. tgobj,paramgr,symconst,symdef,symtable,symcpu,cgutils,pass_2,parabase,
  58. fmodule,hlcgobj,hlcgcpu,defutil,itcpugas;
  59. {*****************************************************************************
  60. twasmexceptionstatehandler_noexceptions
  61. *****************************************************************************}
  62. type
  63. { twasmexceptionstatehandler_noexceptions }
  64. twasmexceptionstatehandler_noexceptions = class(tcgexceptionstatehandler)
  65. class procedure get_exception_temps(list:TAsmList;var t:texceptiontemps); override;
  66. class procedure unget_exception_temps(list:TAsmList;const t:texceptiontemps); override;
  67. class procedure new_exception(list:TAsmList;const t:texceptiontemps; const exceptframekind: texceptframekind; out exceptstate: texceptionstate); override;
  68. class procedure free_exception(list: TAsmList; const t: texceptiontemps; const s: texceptionstate; a: aint; endexceptlabel: tasmlabel; onlyfree:boolean); override;
  69. class procedure handle_nested_exception(list:TAsmList;var t:texceptiontemps;var entrystate: texceptionstate); override;
  70. end;
  71. class procedure twasmexceptionstatehandler_noexceptions.get_exception_temps(list:TAsmList;var t:texceptiontemps);
  72. begin
  73. if not assigned(exceptionreasontype) then
  74. exceptionreasontype:=search_system_proc('fpc_setjmp').returndef;
  75. reference_reset(t.envbuf,0,[]);
  76. reference_reset(t.jmpbuf,0,[]);
  77. tg.gethltemp(list,exceptionreasontype,exceptionreasontype.size,tt_persistent,t.reasonbuf);
  78. end;
  79. class procedure twasmexceptionstatehandler_noexceptions.unget_exception_temps(list:TAsmList;const t:texceptiontemps);
  80. begin
  81. tg.ungettemp(list,t.reasonbuf);
  82. end;
  83. class procedure twasmexceptionstatehandler_noexceptions.new_exception(list:TAsmList;const t:texceptiontemps; const exceptframekind: texceptframekind; out exceptstate: texceptionstate);
  84. begin
  85. exceptstate.exceptionlabel:=nil;
  86. exceptstate.oldflowcontrol:=flowcontrol;
  87. exceptstate.finallycodelabel:=nil;
  88. flowcontrol:=[fc_inflowcontrol,fc_catching_exceptions];
  89. end;
  90. class procedure twasmexceptionstatehandler_noexceptions.free_exception(list: TAsmList; const t: texceptiontemps; const s: texceptionstate; a: aint; endexceptlabel: tasmlabel; onlyfree:boolean);
  91. begin
  92. end;
  93. class procedure twasmexceptionstatehandler_noexceptions.handle_nested_exception(list:TAsmList;var t:texceptiontemps;var entrystate: texceptionstate);
  94. begin
  95. list.Concat(tai_comment.Create(strpnew('TODO: handle_nested_exception')));
  96. end;
  97. {*****************************************************************************
  98. twasmexceptionstatehandler_jsexceptions
  99. *****************************************************************************}
  100. type
  101. twasmexceptionstatehandler_jsexceptions = class(tcgexceptionstatehandler)
  102. class procedure get_exception_temps(list:TAsmList;var t:texceptiontemps); override;
  103. class procedure unget_exception_temps(list:TAsmList;const t:texceptiontemps); override;
  104. class procedure new_exception(list:TAsmList;const t:texceptiontemps; const exceptframekind: texceptframekind; out exceptstate: texceptionstate); override;
  105. class procedure free_exception(list: TAsmList; const t: texceptiontemps; const s: texceptionstate; a: aint; endexceptlabel: tasmlabel; onlyfree:boolean); override;
  106. class procedure handle_nested_exception(list:TAsmList;var t:texceptiontemps;var entrystate: texceptionstate); override;
  107. end;
  108. class procedure twasmexceptionstatehandler_jsexceptions.get_exception_temps(list:TAsmList;var t:texceptiontemps);
  109. begin
  110. if not assigned(exceptionreasontype) then
  111. exceptionreasontype:=search_system_proc('fpc_setjmp').returndef;
  112. reference_reset(t.envbuf,0,[]);
  113. reference_reset(t.jmpbuf,0,[]);
  114. tg.gethltemp(list,exceptionreasontype,exceptionreasontype.size,tt_persistent,t.reasonbuf);
  115. end;
  116. class procedure twasmexceptionstatehandler_jsexceptions.unget_exception_temps(list:TAsmList;const t:texceptiontemps);
  117. begin
  118. tg.ungettemp(list,t.reasonbuf);
  119. end;
  120. class procedure twasmexceptionstatehandler_jsexceptions.new_exception(list:TAsmList;const t:texceptiontemps; const exceptframekind: texceptframekind; out exceptstate: texceptionstate);
  121. begin
  122. exceptstate.exceptionlabel:=nil;
  123. exceptstate.oldflowcontrol:=flowcontrol;
  124. exceptstate.finallycodelabel:=nil;
  125. flowcontrol:=[fc_inflowcontrol,fc_catching_exceptions];
  126. end;
  127. class procedure twasmexceptionstatehandler_jsexceptions.free_exception(list: TAsmList; const t: texceptiontemps; const s: texceptionstate; a: aint; endexceptlabel: tasmlabel; onlyfree:boolean);
  128. begin
  129. end;
  130. class procedure twasmexceptionstatehandler_jsexceptions.handle_nested_exception(list:TAsmList;var t:texceptiontemps;var entrystate: texceptionstate);
  131. begin
  132. list.Concat(tai_comment.Create(strpnew('TODO: handle_nested_exception')));
  133. end;
  134. {*****************************************************************************
  135. twasmexceptionstatehandler_nativeexceptions
  136. *****************************************************************************}
  137. type
  138. { twasmexceptionstatehandler_nativeexceptions }
  139. twasmexceptionstatehandler_nativeexceptions = class(tcgexceptionstatehandler)
  140. class procedure new_exception(list:TAsmList;const t:texceptiontemps; const exceptframekind: texceptframekind; out exceptstate: texceptionstate); override;
  141. class procedure free_exception(list: TAsmList; const t: texceptiontemps; const s: texceptionstate; a: aint; endexceptlabel: tasmlabel; onlyfree:boolean); override;
  142. class procedure handle_nested_exception(list:TAsmList;var t:texceptiontemps;var entrystate: texceptionstate); override;
  143. { start of an "on" (catch) block }
  144. class procedure begin_catch(list: TAsmList; excepttype: tobjectdef; nextonlabel: tasmlabel; out exceptlocdef: tdef; out exceptlocreg: tregister); override;
  145. { end of an "on" (catch) block }
  146. class procedure end_catch(list: TAsmList); override;
  147. end;
  148. class procedure twasmexceptionstatehandler_nativeexceptions.new_exception(list:TAsmList;const t:texceptiontemps; const exceptframekind: texceptframekind; out exceptstate: texceptionstate);
  149. begin
  150. exceptstate.exceptionlabel:=nil;
  151. exceptstate.oldflowcontrol:=flowcontrol;
  152. exceptstate.finallycodelabel:=nil;
  153. flowcontrol:=[fc_inflowcontrol,fc_catching_exceptions];
  154. end;
  155. class procedure twasmexceptionstatehandler_nativeexceptions.free_exception(list: TAsmList; const t: texceptiontemps; const s: texceptionstate; a: aint; endexceptlabel: tasmlabel; onlyfree:boolean);
  156. begin
  157. end;
  158. class procedure twasmexceptionstatehandler_nativeexceptions.handle_nested_exception(list:TAsmList;var t:texceptiontemps;var entrystate: texceptionstate);
  159. begin
  160. Message1(parser_f_unsupported_feature,'nested exception');
  161. end;
  162. class procedure twasmexceptionstatehandler_nativeexceptions.begin_catch(list: TAsmList; excepttype: tobjectdef; nextonlabel: tasmlabel; out exceptlocdef: tdef; out exceptlocreg: tregister);
  163. var
  164. pd: tprocdef;
  165. href2: treference;
  166. fpc_catches_res,
  167. paraloc1: tcgpara;
  168. exceptloc: tlocation;
  169. indirect: boolean;
  170. otherunit: boolean;
  171. begin
  172. paraloc1.init;
  173. otherunit:=findunitsymtable(excepttype.owner).moduleid<>findunitsymtable(current_procinfo.procdef.owner).moduleid;
  174. indirect:=(tf_supports_packages in target_info.flags) and
  175. (target_info.system in systems_indirect_var_imports) and
  176. (cs_imported_data in current_settings.localswitches) and
  177. otherunit;
  178. { send the vmt parameter }
  179. pd:=search_system_proc('fpc_catches');
  180. reference_reset_symbol(href2, current_asmdata.RefAsmSymbol(excepttype.vmt_mangledname, AT_DATA, indirect), 0, sizeof(pint), []);
  181. if otherunit then
  182. current_module.add_extern_asmsym(excepttype.vmt_mangledname, AB_EXTERNAL, AT_DATA);
  183. paramanager.getcgtempparaloc(list, pd, 1, paraloc1);
  184. hlcg.a_loadaddr_ref_cgpara(list, excepttype.vmt_def, href2, paraloc1);
  185. paramanager.freecgpara(list, paraloc1);
  186. fpc_catches_res:=hlcg.g_call_system_proc(list, pd, [@paraloc1], nil);
  187. location_reset(exceptloc, LOC_REGISTER, def_cgsize(fpc_catches_res.def));
  188. exceptloc.register:=hlcg.getaddressregister(list, fpc_catches_res.def);
  189. hlcg.gen_load_cgpara_loc(list, fpc_catches_res.def, fpc_catches_res, exceptloc, true);
  190. { is it this catch? }
  191. thlcgwasm(hlcg).a_cmp_const_reg_stack(list, fpc_catches_res.def, OC_NE, 0, exceptloc.register);
  192. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_if));
  193. thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  194. paraloc1.done;
  195. exceptlocdef:=fpc_catches_res.def;
  196. exceptlocreg:=exceptloc.register;
  197. end;
  198. class procedure twasmexceptionstatehandler_nativeexceptions.end_catch(list: TAsmList);
  199. begin
  200. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_if));
  201. end;
  202. {*****************************************************************************
  203. twasmexceptionstatehandler_bfexceptions
  204. *****************************************************************************}
  205. type
  206. { twasmexceptionstatehandler_bfexceptions }
  207. twasmexceptionstatehandler_bfexceptions = class(tcgexceptionstatehandler)
  208. class procedure new_exception(list:TAsmList;const t:texceptiontemps; const exceptframekind: texceptframekind; out exceptstate: texceptionstate); override;
  209. class procedure free_exception(list: TAsmList; const t: texceptiontemps; const s: texceptionstate; a: aint; endexceptlabel: tasmlabel; onlyfree:boolean); override;
  210. class procedure handle_nested_exception(list:TAsmList;var t:texceptiontemps;var entrystate: texceptionstate); override;
  211. { start of an "on" (catch) block }
  212. class procedure begin_catch(list: TAsmList; excepttype: tobjectdef; nextonlabel: tasmlabel; out exceptlocdef: tdef; out exceptlocreg: tregister); override;
  213. { end of an "on" (catch) block }
  214. class procedure end_catch(list: TAsmList); override;
  215. end;
  216. class procedure twasmexceptionstatehandler_bfexceptions.new_exception(list:TAsmList;const t:texceptiontemps; const exceptframekind: texceptframekind; out exceptstate: texceptionstate);
  217. begin
  218. exceptstate.exceptionlabel:=nil;
  219. exceptstate.oldflowcontrol:=flowcontrol;
  220. exceptstate.finallycodelabel:=nil;
  221. flowcontrol:=[fc_inflowcontrol,fc_catching_exceptions];
  222. end;
  223. class procedure twasmexceptionstatehandler_bfexceptions.free_exception(list: TAsmList; const t: texceptiontemps; const s: texceptionstate; a: aint; endexceptlabel: tasmlabel; onlyfree:boolean);
  224. begin
  225. end;
  226. class procedure twasmexceptionstatehandler_bfexceptions.handle_nested_exception(list:TAsmList;var t:texceptiontemps;var entrystate: texceptionstate);
  227. begin
  228. Message1(parser_f_unsupported_feature,'nested exception');
  229. end;
  230. class procedure twasmexceptionstatehandler_bfexceptions.begin_catch(list: TAsmList; excepttype: tobjectdef; nextonlabel: tasmlabel; out exceptlocdef: tdef; out exceptlocreg: tregister);
  231. var
  232. pd: tprocdef;
  233. href2: treference;
  234. fpc_catches_res,
  235. paraloc1: tcgpara;
  236. exceptloc: tlocation;
  237. indirect: boolean;
  238. otherunit: boolean;
  239. begin
  240. paraloc1.init;
  241. otherunit:=findunitsymtable(excepttype.owner).moduleid<>findunitsymtable(current_procinfo.procdef.owner).moduleid;
  242. indirect:=(tf_supports_packages in target_info.flags) and
  243. (target_info.system in systems_indirect_var_imports) and
  244. (cs_imported_data in current_settings.localswitches) and
  245. otherunit;
  246. { send the vmt parameter }
  247. pd:=search_system_proc('fpc_catches');
  248. reference_reset_symbol(href2, current_asmdata.RefAsmSymbol(excepttype.vmt_mangledname, AT_DATA, indirect), 0, sizeof(pint), []);
  249. if otherunit then
  250. current_module.add_extern_asmsym(excepttype.vmt_mangledname, AB_EXTERNAL, AT_DATA);
  251. paramanager.getcgtempparaloc(list, pd, 1, paraloc1);
  252. hlcg.a_loadaddr_ref_cgpara(list, excepttype.vmt_def, href2, paraloc1);
  253. paramanager.freecgpara(list, paraloc1);
  254. fpc_catches_res:=hlcg.g_call_system_proc(list, pd, [@paraloc1], nil);
  255. location_reset(exceptloc, LOC_REGISTER, def_cgsize(fpc_catches_res.def));
  256. exceptloc.register:=hlcg.getaddressregister(list, fpc_catches_res.def);
  257. hlcg.gen_load_cgpara_loc(list, fpc_catches_res.def, fpc_catches_res, exceptloc, true);
  258. { is it this catch? }
  259. thlcgwasm(hlcg).a_cmp_const_reg_stack(list, fpc_catches_res.def, OC_NE, 0, exceptloc.register);
  260. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_if));
  261. thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  262. paraloc1.done;
  263. exceptlocdef:=fpc_catches_res.def;
  264. exceptlocreg:=exceptloc.register;
  265. end;
  266. class procedure twasmexceptionstatehandler_bfexceptions.end_catch(list: TAsmList);
  267. begin
  268. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_end_if));
  269. end;
  270. {*****************************************************************************
  271. twasmblockitem
  272. *****************************************************************************}
  273. type
  274. { twasmblockitem }
  275. twasmblockitem = class(TLinkedListItem)
  276. blockstart: taicpu;
  277. elseinstr: taicpu;
  278. constructor Create(ablockstart: taicpu);
  279. end;
  280. constructor twasmblockitem.Create(ablockstart: taicpu);
  281. begin
  282. blockstart:=ablockstart;
  283. end;
  284. {*****************************************************************************
  285. twasmblockstack
  286. *****************************************************************************}
  287. type
  288. { twasmblockstack }
  289. twasmblockstack = class(tlinkedlist)
  290. end;
  291. {*****************************************************************************
  292. tcpuprocinfo
  293. *****************************************************************************}
  294. function tcpuprocinfo.ConvertBranchTargetNumbersToLabels(ai: tai; blockstack: twasmstruc_stack): TAsmMapFuncResult;
  295. var
  296. instr: taicpu;
  297. bl: taicpu_wasm_structured_instruction;
  298. l: TAsmLabel;
  299. begin
  300. result.typ:=amfrtNoChange;
  301. if ai.typ<>ait_instruction then
  302. exit;
  303. instr:=taicpu(ai);
  304. if not (instr.opcode in [a_br,a_br_if]) then
  305. exit;
  306. if instr.ops<>1 then
  307. internalerror(2023101601);
  308. if instr.oper[0]^.typ<>top_const then
  309. exit;
  310. bl:=blockstack[instr.oper[0]^.val];
  311. l:=bl.getlabel;
  312. instr.loadsymbol(0,l,0);
  313. end;
  314. function tcpuprocinfo.ConvertIfToBrIf(ai: tai; blockstack: twasmstruc_stack): TAsmMapFuncResult;
  315. begin
  316. result.typ:=amfrtNoChange;
  317. if (ai.typ=ait_wasm_structured_instruction) and (taicpu_wasm_structured_instruction(ai).wstyp=aitws_if) then
  318. begin
  319. result.typ:=amfrtNewList;
  320. result.newlist:=TAsmList.Create;
  321. tai_wasmstruc_if(ai).ConvertToBrIf(result.newlist,@AllocWasmLocal);
  322. end;
  323. end;
  324. function tcpuprocinfo.ConvertLoopToBr(ai: tai; blockstack: twasmstruc_stack): TAsmMapFuncResult;
  325. begin
  326. result.typ:=amfrtNoChange;
  327. if (ai.typ=ait_wasm_structured_instruction) and (taicpu_wasm_structured_instruction(ai).wstyp=aitws_loop) then
  328. begin
  329. result.typ:=amfrtNewList;
  330. result.newlist:=TAsmList.Create;
  331. tai_wasmstruc_loop(ai).ConvertToBr(result.newlist);
  332. end;
  333. end;
  334. function tcpuprocinfo.StripBlockInstructions(ai: tai; blockstack: twasmstruc_stack): TAsmMapFuncResult;
  335. var
  336. instr: taicpu;
  337. begin
  338. result.typ:=amfrtNoChange;
  339. if ai.typ<>ait_instruction then
  340. exit;
  341. instr:=taicpu(ai);
  342. if instr.opcode in [a_block,a_end_block] then
  343. result.typ:=amfrtDeleteAi;
  344. end;
  345. function tcpuprocinfo.AllocWasmLocal(wbt: TWasmBasicType): Integer;
  346. begin
  347. SetLength(FAllocatedLocals,Length(FAllocatedLocals)+1);
  348. FAllocatedLocals[High(FAllocatedLocals)]:=wbt;
  349. result:=High(FAllocatedLocals)+FFirstFreeLocal;
  350. SetLength(FLocals,Length(FLocals)+1);
  351. FLocals[High(FLocals)]:=wbt;
  352. end;
  353. function tcpuprocinfo.GetLocalType(localidx: Integer): TWasmBasicType;
  354. begin
  355. if (localidx<Low(FLocals)) or (localidx>High(FLocals)) then
  356. internalerror(2024022601);
  357. result:=FLocals[localidx];
  358. end;
  359. constructor tcpuprocinfo.create(aparent: tprocinfo);
  360. begin
  361. inherited create(aparent);
  362. FGotoTargets:=TFPHashObjectList.Create(false);
  363. if ts_wasm_bf_exceptions in current_settings.targetswitches then
  364. current_asmdata.getjumplabel(CurrRaiseLabel);
  365. end;
  366. destructor tcpuprocinfo.destroy;
  367. begin
  368. FGotoTargets.Free;
  369. inherited destroy;
  370. end;
  371. function tcpuprocinfo.calc_stackframe_size: longint;
  372. begin
  373. { the stack frame in WebAssembly should always have a 16-byte alignment }
  374. Result:=Align(inherited calc_stackframe_size,16);
  375. end;
  376. procedure tcpuprocinfo.setup_eh;
  377. begin
  378. if ts_wasm_native_exceptions in current_settings.targetswitches then
  379. cexceptionstatehandler:=twasmexceptionstatehandler_nativeexceptions
  380. else if ts_wasm_js_exceptions in current_settings.targetswitches then
  381. cexceptionstatehandler:=twasmexceptionstatehandler_jsexceptions
  382. else if ts_wasm_no_exceptions in current_settings.targetswitches then
  383. cexceptionstatehandler:=twasmexceptionstatehandler_noexceptions
  384. else if ts_wasm_bf_exceptions in current_settings.targetswitches then
  385. cexceptionstatehandler:=twasmexceptionstatehandler_bfexceptions
  386. else
  387. internalerror(2021091701);
  388. end;
  389. procedure tcpuprocinfo.generate_exit_label(list: tasmlist);
  390. begin
  391. list.concat(taicpu.op_none(a_end_block));
  392. inherited generate_exit_label(list);
  393. end;
  394. procedure tcpuprocinfo.postprocess_code;
  395. function findfirst_tai_functype(asmlist: TAsmList): tai_functype;
  396. var
  397. hp: tai;
  398. begin
  399. result:=nil;
  400. if not assigned(asmlist) then
  401. exit;
  402. hp:=tai(asmlist.first);
  403. while assigned(hp) do
  404. begin
  405. if hp.typ=ait_functype then
  406. begin
  407. result:=tai_functype(hp);
  408. exit;
  409. end;
  410. hp:=tai(hp.Next);
  411. end;
  412. end;
  413. procedure replace_local_frame_pointer(asmlist: TAsmList);
  414. var
  415. hp: tai;
  416. instr: taicpu;
  417. l: Integer;
  418. begin
  419. if not assigned(asmlist) then
  420. exit;
  421. hp:=tai(asmlist.first);
  422. while assigned(hp) do
  423. begin
  424. if hp.typ=ait_instruction then
  425. begin
  426. instr:=taicpu(hp);
  427. for l:=0 to instr.ops-1 do
  428. if (instr.oper[l]^.typ=top_reg) and (instr.oper[l]^.reg=NR_LOCAL_FRAME_POINTER_REG) then
  429. instr.loadref(l,tcpuprocdef(current_procinfo.procdef).frame_pointer_ref);
  430. end;
  431. hp:=tai(hp.Next);
  432. end;
  433. end;
  434. function FindNextInstruction(hp: tai): taicpu;
  435. begin
  436. result:=nil;
  437. if not assigned(hp) then
  438. exit;
  439. repeat
  440. hp:=tai(hp.next);
  441. until not assigned(hp) or (hp.typ=ait_instruction);
  442. if assigned(hp) then
  443. result:=taicpu(hp);
  444. end;
  445. procedure resolve_labels_pass1(asmlist: TAsmList);
  446. var
  447. hp: tai;
  448. lastinstr, nextinstr: taicpu;
  449. cur_nesting_depth: longint;
  450. lbl: tai_label;
  451. blockstack: twasmblockstack;
  452. cblock: twasmblockitem;
  453. begin
  454. blockstack:=twasmblockstack.create;
  455. cur_nesting_depth:=0;
  456. lastinstr:=nil;
  457. hp:=tai(asmlist.first);
  458. while assigned(hp) do
  459. begin
  460. case hp.typ of
  461. ait_instruction:
  462. begin
  463. lastinstr:=taicpu(hp);
  464. case lastinstr.opcode of
  465. a_block,
  466. a_loop,
  467. a_if,
  468. a_try:
  469. begin
  470. blockstack.Concat(twasmblockitem.create(lastinstr));
  471. inc(cur_nesting_depth);
  472. end;
  473. a_else:
  474. begin
  475. cblock:=twasmblockitem(blockstack.Last);
  476. if (cblock=nil) or
  477. (cblock.blockstart.opcode<>a_if) or
  478. assigned(cblock.elseinstr) then
  479. Message1(parser_f_unsupported_feature,'misplaced a_else');
  480. cblock.elseinstr:=lastinstr;
  481. end;
  482. a_end_block,
  483. a_end_loop,
  484. a_end_if,
  485. a_end_try:
  486. begin
  487. dec(cur_nesting_depth);
  488. if cur_nesting_depth<0 then
  489. Message1(parser_f_unsupported_feature,'negative nesting level');
  490. cblock:=twasmblockitem(blockstack.GetLast);
  491. if (cblock=nil) or
  492. ((cblock.blockstart.opcode=a_block) and (lastinstr.opcode<>a_end_block)) or
  493. ((cblock.blockstart.opcode=a_loop) and (lastinstr.opcode<>a_end_loop)) or
  494. ((cblock.blockstart.opcode=a_if) and (lastinstr.opcode<>a_end_if)) or
  495. ((cblock.blockstart.opcode=a_try) and (lastinstr.opcode<>a_end_try)) then
  496. Message1(parser_f_unsupported_feature,'incompatible nesting level');
  497. cblock.free;
  498. end;
  499. else
  500. ;
  501. end;
  502. end;
  503. ait_label:
  504. begin
  505. lbl:=tai_label(hp);
  506. lbl.labsym.nestingdepth:=-1;
  507. nextinstr:=FindNextInstruction(hp);
  508. if assigned(nextinstr) and (nextinstr.opcode in [a_end_block,a_end_try,a_end_if]) then
  509. lbl.labsym.nestingdepth:=cur_nesting_depth
  510. else if assigned(lastinstr) and (lastinstr.opcode=a_loop) then
  511. lbl.labsym.nestingdepth:=cur_nesting_depth
  512. else if assigned(lastinstr) and (lastinstr.opcode in [a_end_block,a_end_try,a_end_if]) then
  513. lbl.labsym.nestingdepth:=cur_nesting_depth+1
  514. else if assigned(nextinstr) and (nextinstr.opcode=a_loop) then
  515. lbl.labsym.nestingdepth:=cur_nesting_depth+1;
  516. end;
  517. else
  518. ;
  519. end;
  520. hp:=tai(hp.Next);
  521. end;
  522. if cur_nesting_depth<>0 then
  523. Message1(parser_f_unsupported_feature,'unbalanced nesting level');
  524. blockstack.free;
  525. end;
  526. function resolve_labels_pass2(asmlist: TAsmList): Boolean;
  527. var
  528. hp: tai;
  529. instr: taicpu;
  530. hlabel: tasmsymbol;
  531. cur_nesting_depth: longint;
  532. begin
  533. Result:=true;
  534. cur_nesting_depth:=0;
  535. hp:=tai(asmlist.first);
  536. while assigned(hp) do
  537. begin
  538. if hp.typ=ait_instruction then
  539. begin
  540. instr:=taicpu(hp);
  541. case instr.opcode of
  542. a_block,
  543. a_loop,
  544. a_if,
  545. a_try:
  546. inc(cur_nesting_depth);
  547. a_end_block,
  548. a_end_loop,
  549. a_end_if,
  550. a_end_try:
  551. begin
  552. dec(cur_nesting_depth);
  553. if cur_nesting_depth<0 then
  554. Message1(parser_f_unsupported_feature,'negative nesting level');
  555. end;
  556. a_br,
  557. a_br_if:
  558. begin
  559. if instr.ops<>1 then
  560. Message1(parser_f_unsupported_feature,'a_br or a_br_if with wrong operand count');
  561. if instr.oper[0]^.typ=top_ref then
  562. begin
  563. if not assigned(instr.oper[0]^.ref^.symbol) then
  564. Message1(parser_f_unsupported_feature,'a_br or a_br_if with wrong ref operand');
  565. if (instr.oper[0]^.ref^.base<>NR_NO) or
  566. (instr.oper[0]^.ref^.index<>NR_NO) or
  567. (instr.oper[0]^.ref^.offset<>0) then
  568. Message1(parser_f_unsupported_feature,'a_br or a_br_if with wrong ref type');
  569. if (instr.oper[0]^.ref^.symbol.nestingdepth<>-1) and
  570. (cur_nesting_depth>=instr.oper[0]^.ref^.symbol.nestingdepth) then
  571. instr.loadconst(0,cur_nesting_depth-instr.oper[0]^.ref^.symbol.nestingdepth)
  572. else
  573. begin
  574. result:=false;
  575. hlabel:=tasmsymbol(instr.oper[0]^.ref^.symbol);
  576. asmlist.insertafter(tai_comment.create(strpnew('Unable to find destination of label '+hlabel.name)),hp);
  577. end;
  578. end;
  579. end;
  580. else
  581. ;
  582. end;
  583. end;
  584. hp:=tai(hp.Next);
  585. end;
  586. if cur_nesting_depth<>0 then
  587. Message1(parser_f_unsupported_feature,'unbalanced nesting level');
  588. end;
  589. function resolve_labels_simple(asmlist: TAsmList): Boolean;
  590. begin
  591. if not assigned(asmlist) then
  592. exit(true);
  593. resolve_labels_pass1(asmlist);
  594. result:=resolve_labels_pass2(asmlist);
  595. end;
  596. procedure resolve_labels_via_state_machine(asmlist: TAsmList);
  597. var
  598. blocks: TFPHashObjectList;
  599. curr_block, tmplist: TAsmList;
  600. hp, hpnext: tai;
  601. block_nr, machine_state, target_block_index: Integer;
  602. state_machine_loop_start_label, state_machine_exit: TAsmLabel;
  603. begin
  604. blocks:=TFPHashObjectList.Create;
  605. curr_block:=TAsmList.Create;
  606. blocks.Add('.start',curr_block);
  607. repeat
  608. hp:=tai(asmlist.First);
  609. if assigned(hp) then
  610. begin
  611. asmlist.Remove(hp);
  612. if hp.typ=ait_label then
  613. begin
  614. curr_block:=TAsmList.Create;
  615. blocks.Add(tai_label(hp).labsym.Name,curr_block);
  616. end;
  617. curr_block.Concat(hp);
  618. end;
  619. until not assigned(hp);
  620. { asmlist is now empty }
  621. asmlist.Concat(tai_comment.Create(strpnew('labels resolved via state machine')));
  622. machine_state:=AllocWasmLocal(wbt_i32);
  623. asmlist.Concat(tai_comment.Create(strpnew('machine state is in local '+tostr(machine_state))));
  624. asmlist.Concat(taicpu.op_const(a_i32_const,0));
  625. asmlist.Concat(taicpu.op_const(a_local_set,machine_state));
  626. asmlist.Concat(taicpu.op_none(a_block));
  627. asmlist.Concat(taicpu.op_none(a_loop));
  628. current_asmdata.getjumplabel(state_machine_loop_start_label);
  629. asmlist.concat(tai_label.create(state_machine_loop_start_label));
  630. current_asmdata.getjumplabel(state_machine_exit);
  631. for block_nr:=0 to blocks.Count-1 do
  632. asmlist.Concat(taicpu.op_none(a_block));
  633. for block_nr:=0 to blocks.Count-1 do
  634. begin
  635. { TODO: this sequence can be replaced with a single br_table instruction }
  636. asmlist.Concat(taicpu.op_const(a_local_get,machine_state));
  637. asmlist.Concat(taicpu.op_const(a_i32_const,block_nr));
  638. asmlist.Concat(taicpu.op_none(a_i32_eq));
  639. asmlist.Concat(taicpu.op_const(a_br_if,block_nr));
  640. end;
  641. asmlist.Concat(taicpu.op_none(a_unreachable));
  642. tmplist:=TAsmList.Create;
  643. for block_nr:=0 to blocks.Count-1 do
  644. begin
  645. asmlist.Concat(taicpu.op_none(a_end_block));
  646. asmlist.Concat(tai_comment.Create(strpnew('block '+tostr(block_nr)+' for label '+blocks.NameOfIndex(block_nr))));
  647. curr_block:=TAsmList(blocks[block_nr]);
  648. hp:=tai(curr_block.First);
  649. while assigned(hp) do
  650. begin
  651. hpnext:=tai(hp.next);
  652. if (hp.typ=ait_instruction) and (taicpu(hp).opcode in [a_br,a_br_if]) and
  653. (taicpu(hp).ops=1) and
  654. (taicpu(hp).oper[0]^.typ=top_ref) and
  655. assigned(taicpu(hp).oper[0]^.ref^.symbol) then
  656. begin
  657. target_block_index:=blocks.FindIndexOf(taicpu(hp).oper[0]^.ref^.symbol.Name);
  658. curr_block.InsertBefore(tai_comment.Create(strpnew(
  659. 'branch '+gas_op2str[taicpu(hp).opcode]+
  660. ' '+taicpu(hp).oper[0]^.ref^.symbol.Name+
  661. ' target_block_index='+tostr(target_block_index))),hp);
  662. if target_block_index<>-1 then
  663. begin
  664. tmplist.Clear;
  665. if taicpu(hp).opcode=a_br_if then
  666. tmplist.Concat(taicpu.op_none(a_if));
  667. tmplist.Concat(taicpu.op_const(a_i32_const,target_block_index));
  668. tmplist.Concat(taicpu.op_const(a_local_set,machine_state));
  669. tmplist.Concat(taicpu.op_sym(a_br,state_machine_loop_start_label));
  670. if taicpu(hp).opcode=a_br_if then
  671. tmplist.Concat(taicpu.op_none(a_end_if));
  672. curr_block.insertListAfter(hp,tmplist);
  673. curr_block.Remove(hp);
  674. end;
  675. end;
  676. hp:=hpnext;
  677. end;
  678. if block_nr<(blocks.Count-1) then
  679. begin
  680. curr_block.Concat(taicpu.op_const(a_i32_const,block_nr+1));
  681. curr_block.Concat(taicpu.op_const(a_local_set,machine_state));
  682. curr_block.Concat(taicpu.op_sym(a_br,state_machine_loop_start_label));
  683. end
  684. else
  685. curr_block.Concat(taicpu.op_sym(a_br,state_machine_exit));
  686. asmlist.concatList(curr_block);
  687. end;
  688. tmplist.Free;
  689. asmlist.Concat(taicpu.op_none(a_end_loop));
  690. asmlist.Concat(taicpu.op_none(a_end_block));
  691. asmlist.concat(tai_label.create(state_machine_exit));
  692. end;
  693. procedure filter_start_exit_code(asmlist: TAsmList; out entry_code, proc_body, exit_code: TAsmList);
  694. var
  695. hp, hpnext, hpprev: tai;
  696. begin
  697. entry_code:=TAsmList.Create;
  698. proc_body:=TAsmList.Create;
  699. exit_code:=TAsmList.Create;
  700. repeat
  701. hp:=tai(asmlist.First);
  702. if assigned(hp) then
  703. begin
  704. hpnext:=tai(hp.next);
  705. if (hp.typ=ait_instruction) and (taicpu(hp).opcode=a_block) then
  706. break;
  707. asmlist.Remove(hp);
  708. entry_code.Concat(hp);
  709. hp:=hpnext;
  710. end;
  711. until not assigned(hp);
  712. repeat
  713. hp:=tai(asmlist.Last);
  714. if assigned(hp) then
  715. begin
  716. hpprev:=tai(hp.Previous);
  717. if (hp.typ=ait_instruction) and (taicpu(hp).opcode=a_end_block) then
  718. break;
  719. asmlist.Remove(hp);
  720. exit_code.Insert(hp);
  721. hp:=hpprev;
  722. end;
  723. until not assigned(hp);
  724. proc_body.insertList(asmlist);
  725. end;
  726. procedure resolve_labels_of_asmlist_with_try_blocks_recursive(asmlist: TAsmList);
  727. var
  728. hp: tai;
  729. i: Integer;
  730. begin
  731. if not assigned(asmlist) then
  732. exit;
  733. hp:=tai(asmlist.First);
  734. while assigned(hp) do
  735. begin
  736. if hp.typ=ait_wasm_structured_instruction then
  737. begin
  738. if not (taicpu_wasm_structured_instruction(hp).wstyp in [aitws_try_catch,aitws_try_delegate]) then
  739. internalerror(2023102201);
  740. resolve_labels_of_asmlist_with_try_blocks_recursive(tai_wasmstruc_try(hp).try_asmlist);
  741. if taicpu_wasm_structured_instruction(hp).wstyp=aitws_try_catch then
  742. with tai_wasmstruc_try_catch(hp) do
  743. begin
  744. for i:=low(catch_list) to high(catch_list) do
  745. resolve_labels_of_asmlist_with_try_blocks_recursive(catch_list[i].asmlist);
  746. resolve_labels_of_asmlist_with_try_blocks_recursive(catch_all_asmlist);
  747. end
  748. else if taicpu_wasm_structured_instruction(hp).wstyp=aitws_try_delegate then
  749. {nothing}
  750. else
  751. internalerror(2023102202);
  752. end;
  753. hp:=tai(hp.next);
  754. end;
  755. resolve_labels_via_state_machine(asmlist);
  756. end;
  757. procedure resolve_labels_complex(var asmlist: TAsmList);
  758. var
  759. entry_code, proc_body, exit_code: TAsmList;
  760. begin
  761. filter_start_exit_code(asmlist,entry_code,proc_body,exit_code);
  762. asmlist.Free;
  763. asmlist:=proc_body;
  764. proc_body:=nil;
  765. wasm_convert_to_structured_asmlist(asmlist);
  766. map_structured_asmlist(asmlist,@ConvertBranchTargetNumbersToLabels);
  767. map_structured_asmlist(asmlist,@ConvertIfToBrIf);
  768. map_structured_asmlist(asmlist,@ConvertLoopToBr);
  769. wasm_convert_to_flat_asmlist(asmlist);
  770. map_structured_asmlist(asmlist,@StripBlockInstructions);
  771. wasm_convert_to_structured_asmlist(asmlist);
  772. resolve_labels_of_asmlist_with_try_blocks_recursive(asmlist);
  773. wasm_convert_to_flat_asmlist(asmlist);
  774. asmlist.insertList(entry_code);
  775. entry_code.free;
  776. asmlist.concatList(exit_code);
  777. exit_code.free;
  778. if not resolve_labels_simple(asmlist) then
  779. internalerror(2023102101);
  780. end;
  781. function prepare_locals: TAsmList;
  782. var
  783. local: tai_local;
  784. first: Boolean;
  785. l : TWasmLocal;
  786. begin
  787. result:=TAsmList.create;
  788. local:=nil;
  789. first:=true;
  790. l:=ttgwasm(tg).localvars.first;
  791. FFuncType:=findfirst_tai_functype(aktproccode).functype;
  792. FLocals:=Copy(FFuncType.params);
  793. FParametersCount:=Length(FLocals);
  794. FFirstFreeLocal:=FParametersCount;
  795. while Assigned(l) do
  796. begin
  797. SetLength(FLocals,Length(FLocals)+1);
  798. FLocals[High(FLocals)]:=l.typ;
  799. local:=tai_local.create(l.typ);
  800. local.first:=first;
  801. first:=false;
  802. result.Concat(local);
  803. l:=l.nextseq;
  804. Inc(FFirstFreeLocal);
  805. end;
  806. end;
  807. procedure add_extra_allocated_locals(localslist: TAsmList);
  808. var
  809. t: TWasmBasicType;
  810. begin
  811. for t in FAllocatedLocals do
  812. localslist.Concat(tai_local.create(t));
  813. end;
  814. procedure insert_localslist(destlist,localslist: TAsmList);
  815. begin
  816. if assigned(localslist) then
  817. begin
  818. tai_local(localslist.Last).last:=true;
  819. destlist.insertListAfter(findfirst_tai_functype(destlist),localslist);
  820. end;
  821. end;
  822. procedure check_goto_br_instructions(list: TAsmList; out HasGotoBrInstructions: boolean);
  823. var
  824. hp: tai;
  825. begin
  826. HasGotoBrInstructions:=False;
  827. hp:=tai(list.first);
  828. while assigned(hp) do
  829. begin
  830. if (hp.typ=ait_instruction) and (taicpu(hp).is_br_generated_by_goto) then
  831. begin
  832. HasGotoBrInstructions:=True;
  833. if (taicpu(hp).opcode<>a_br) or
  834. (taicpu(hp).ops<>1) or
  835. (taicpu(hp).oper[0]^.typ<>top_ref) or
  836. (taicpu(hp).oper[0]^.ref^.offset<>0) or
  837. (taicpu(hp).oper[0]^.ref^.base<>NR_NO) or
  838. (taicpu(hp).oper[0]^.ref^.index<>NR_NO) or
  839. (taicpu(hp).oper[0]^.ref^.symbol=nil) then
  840. internalerror(2023102203);
  841. if not is_goto_target(taicpu(hp).oper[0]^.ref^.symbol) then
  842. internalerror(2023102204);
  843. end;
  844. hp:=tai(hp.next);
  845. end;
  846. end;
  847. procedure validate_code;
  848. var
  849. vs: TWasmValidationStacks;
  850. hp: tai;
  851. begin
  852. vs:=TWasmValidationStacks.Create(@GetLocalType,FFuncType);
  853. hp:=tai(aktproccode.first);
  854. while assigned(hp) do
  855. begin
  856. if hp.typ=ait_instruction then
  857. vs.Validate(taicpu(hp));
  858. hp:=tai(hp.next);
  859. end;
  860. vs.Free;
  861. end;
  862. var
  863. localslist: TAsmList;
  864. labels_resolved, has_goto: Boolean;
  865. begin
  866. check_goto_br_instructions(aktproccode,has_goto);
  867. localslist:=prepare_locals;
  868. replace_local_frame_pointer(aktproccode);
  869. labels_resolved:=false;
  870. if not has_goto then
  871. { TODO: make resolve_labels_simple handle goto labels correctly }
  872. labels_resolved:=resolve_labels_simple(aktproccode);
  873. {$ifndef DEBUG_WASM_GOTO}
  874. if not labels_resolved then
  875. {$endif DEBUG_WASM_GOTO}
  876. resolve_labels_complex(aktproccode);
  877. add_extra_allocated_locals(localslist);
  878. insert_localslist(aktproccode,localslist);
  879. localslist.Free;
  880. {$ifdef DEBUG_WASM_VALIDATION}
  881. validate_code;
  882. {$endif DEBUG_WASM_VALIDATION}
  883. inherited postprocess_code;
  884. end;
  885. procedure tcpuprocinfo.set_first_temp_offset;
  886. var
  887. sz : integer;
  888. i : integer;
  889. sym: tsym;
  890. begin
  891. {
  892. Stackframe layout:
  893. sp:
  894. <incoming parameters>
  895. sp+first_temp_offset:
  896. <locals>
  897. <temp>
  898. }
  899. procdef.init_paraloc_info(calleeside);
  900. sz := procdef.calleeargareasize;
  901. tg.setfirsttemp(sz);
  902. end;
  903. procedure tcpuprocinfo.add_goto_target(l: tasmlabel);
  904. begin
  905. FGotoTargets.Add(l.Name,l);
  906. end;
  907. function tcpuprocinfo.is_goto_target(l: tasmsymbol): Boolean;
  908. begin
  909. result:=FGotoTargets.FindIndexOf(l.Name)<>-1;
  910. end;
  911. initialization
  912. cprocinfo:=tcpuprocinfo;
  913. end.