cpupi.pas 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052
  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. if not (po_assembler in current_procinfo.procdef.procoptions) then
  392. list.concat(taicpu.op_none(a_end_block));
  393. inherited generate_exit_label(list);
  394. end;
  395. procedure tcpuprocinfo.postprocess_code;
  396. function findfirst_tai_functype(asmlist: TAsmList): tai_functype;
  397. var
  398. hp: tai;
  399. begin
  400. result:=nil;
  401. if not assigned(asmlist) then
  402. exit;
  403. hp:=tai(asmlist.first);
  404. while assigned(hp) do
  405. begin
  406. if hp.typ=ait_functype then
  407. begin
  408. result:=tai_functype(hp);
  409. exit;
  410. end;
  411. hp:=tai(hp.Next);
  412. end;
  413. end;
  414. procedure replace_local_frame_pointer(asmlist: TAsmList);
  415. var
  416. hp: tai;
  417. instr: taicpu;
  418. l: Integer;
  419. begin
  420. if not assigned(asmlist) then
  421. exit;
  422. hp:=tai(asmlist.first);
  423. while assigned(hp) do
  424. begin
  425. if hp.typ=ait_instruction then
  426. begin
  427. instr:=taicpu(hp);
  428. for l:=0 to instr.ops-1 do
  429. if (instr.oper[l]^.typ=top_reg) and (instr.oper[l]^.reg=NR_LOCAL_FRAME_POINTER_REG) then
  430. instr.loadref(l,tcpuprocdef(current_procinfo.procdef).frame_pointer_ref);
  431. end;
  432. hp:=tai(hp.Next);
  433. end;
  434. end;
  435. function FindNextInstruction(hp: tai): taicpu;
  436. begin
  437. result:=nil;
  438. if not assigned(hp) then
  439. exit;
  440. repeat
  441. hp:=tai(hp.next);
  442. until not assigned(hp) or (hp.typ=ait_instruction);
  443. if assigned(hp) then
  444. result:=taicpu(hp);
  445. end;
  446. procedure resolve_labels_pass1(asmlist: TAsmList);
  447. var
  448. hp: tai;
  449. lastinstr, nextinstr: taicpu;
  450. cur_nesting_depth: longint;
  451. lbl: tai_label;
  452. blockstack: twasmblockstack;
  453. cblock: twasmblockitem;
  454. begin
  455. blockstack:=twasmblockstack.create;
  456. cur_nesting_depth:=0;
  457. lastinstr:=nil;
  458. hp:=tai(asmlist.first);
  459. while assigned(hp) do
  460. begin
  461. case hp.typ of
  462. ait_instruction:
  463. begin
  464. lastinstr:=taicpu(hp);
  465. case lastinstr.opcode of
  466. a_block,
  467. a_loop,
  468. a_if,
  469. a_try:
  470. begin
  471. blockstack.Concat(twasmblockitem.create(lastinstr));
  472. inc(cur_nesting_depth);
  473. end;
  474. a_else:
  475. begin
  476. cblock:=twasmblockitem(blockstack.Last);
  477. if (cblock=nil) or
  478. (cblock.blockstart.opcode<>a_if) or
  479. assigned(cblock.elseinstr) then
  480. Message1(parser_f_unsupported_feature,'misplaced a_else');
  481. cblock.elseinstr:=lastinstr;
  482. end;
  483. a_end_block,
  484. a_end_loop,
  485. a_end_if,
  486. a_end_try:
  487. begin
  488. dec(cur_nesting_depth);
  489. if cur_nesting_depth<0 then
  490. Message1(parser_f_unsupported_feature,'negative nesting level');
  491. cblock:=twasmblockitem(blockstack.GetLast);
  492. if (cblock=nil) or
  493. ((cblock.blockstart.opcode=a_block) and (lastinstr.opcode<>a_end_block)) or
  494. ((cblock.blockstart.opcode=a_loop) and (lastinstr.opcode<>a_end_loop)) or
  495. ((cblock.blockstart.opcode=a_if) and (lastinstr.opcode<>a_end_if)) or
  496. ((cblock.blockstart.opcode=a_try) and (lastinstr.opcode<>a_end_try)) then
  497. Message1(parser_f_unsupported_feature,'incompatible nesting level');
  498. cblock.free;
  499. end;
  500. else
  501. ;
  502. end;
  503. end;
  504. ait_label:
  505. begin
  506. lbl:=tai_label(hp);
  507. lbl.labsym.nestingdepth:=-1;
  508. nextinstr:=FindNextInstruction(hp);
  509. if assigned(nextinstr) and (nextinstr.opcode in [a_end_block,a_end_try,a_end_if]) then
  510. lbl.labsym.nestingdepth:=cur_nesting_depth
  511. else if assigned(lastinstr) and (lastinstr.opcode=a_loop) then
  512. lbl.labsym.nestingdepth:=cur_nesting_depth
  513. else if assigned(lastinstr) and (lastinstr.opcode in [a_end_block,a_end_try,a_end_if]) then
  514. lbl.labsym.nestingdepth:=cur_nesting_depth+1
  515. else if assigned(nextinstr) and (nextinstr.opcode=a_loop) then
  516. lbl.labsym.nestingdepth:=cur_nesting_depth+1;
  517. end;
  518. else
  519. ;
  520. end;
  521. hp:=tai(hp.Next);
  522. end;
  523. if cur_nesting_depth<>0 then
  524. Message1(parser_f_unsupported_feature,'unbalanced nesting level');
  525. blockstack.free;
  526. end;
  527. function resolve_labels_pass2(asmlist: TAsmList): Boolean;
  528. var
  529. hp: tai;
  530. instr: taicpu;
  531. hlabel: tasmsymbol;
  532. cur_nesting_depth: longint;
  533. begin
  534. Result:=true;
  535. cur_nesting_depth:=0;
  536. hp:=tai(asmlist.first);
  537. while assigned(hp) do
  538. begin
  539. if hp.typ=ait_instruction then
  540. begin
  541. instr:=taicpu(hp);
  542. case instr.opcode of
  543. a_block,
  544. a_loop,
  545. a_if,
  546. a_try:
  547. inc(cur_nesting_depth);
  548. a_end_block,
  549. a_end_loop,
  550. a_end_if,
  551. a_end_try:
  552. begin
  553. dec(cur_nesting_depth);
  554. if cur_nesting_depth<0 then
  555. Message1(parser_f_unsupported_feature,'negative nesting level');
  556. end;
  557. a_br,
  558. a_br_if:
  559. begin
  560. if instr.ops<>1 then
  561. Message1(parser_f_unsupported_feature,'a_br or a_br_if with wrong operand count');
  562. if instr.oper[0]^.typ=top_ref then
  563. begin
  564. if not assigned(instr.oper[0]^.ref^.symbol) then
  565. Message1(parser_f_unsupported_feature,'a_br or a_br_if with wrong ref operand');
  566. if (instr.oper[0]^.ref^.base<>NR_NO) or
  567. (instr.oper[0]^.ref^.index<>NR_NO) or
  568. (instr.oper[0]^.ref^.offset<>0) then
  569. Message1(parser_f_unsupported_feature,'a_br or a_br_if with wrong ref type');
  570. if (instr.oper[0]^.ref^.symbol.nestingdepth<>-1) and
  571. (cur_nesting_depth>=instr.oper[0]^.ref^.symbol.nestingdepth) then
  572. instr.loadconst(0,cur_nesting_depth-instr.oper[0]^.ref^.symbol.nestingdepth)
  573. else
  574. begin
  575. result:=false;
  576. hlabel:=tasmsymbol(instr.oper[0]^.ref^.symbol);
  577. asmlist.insertafter(tai_comment.create(strpnew('Unable to find destination of label '+hlabel.name)),hp);
  578. end;
  579. end;
  580. end;
  581. else
  582. ;
  583. end;
  584. end;
  585. hp:=tai(hp.Next);
  586. end;
  587. if cur_nesting_depth<>0 then
  588. Message1(parser_f_unsupported_feature,'unbalanced nesting level');
  589. end;
  590. function resolve_labels_simple(asmlist: TAsmList): Boolean;
  591. begin
  592. if not assigned(asmlist) then
  593. exit(true);
  594. resolve_labels_pass1(asmlist);
  595. result:=resolve_labels_pass2(asmlist);
  596. end;
  597. procedure resolve_labels_via_state_machine(asmlist: TAsmList);
  598. var
  599. blocks: TFPHashObjectList;
  600. curr_block, tmplist: TAsmList;
  601. hp, hpnext: tai;
  602. block_nr, machine_state, target_block_index: Integer;
  603. state_machine_loop_start_label, state_machine_exit: TAsmLabel;
  604. begin
  605. blocks:=TFPHashObjectList.Create;
  606. curr_block:=TAsmList.Create;
  607. blocks.Add('.start',curr_block);
  608. repeat
  609. hp:=tai(asmlist.First);
  610. if assigned(hp) then
  611. begin
  612. asmlist.Remove(hp);
  613. if hp.typ=ait_label then
  614. begin
  615. if (tai_label(hp).labsym.is_used) then
  616. begin
  617. curr_block:=TAsmList.Create;
  618. blocks.Add(tai_label(hp).labsym.Name,curr_block);
  619. end;
  620. end
  621. else
  622. curr_block.Concat(hp);
  623. end;
  624. until not assigned(hp);
  625. { asmlist is now empty }
  626. asmlist.Concat(tai_comment.Create(strpnew('labels resolved via state machine')));
  627. machine_state:=AllocWasmLocal(wbt_i32);
  628. asmlist.Concat(tai_comment.Create(strpnew('machine state is in local '+tostr(machine_state))));
  629. asmlist.Concat(taicpu.op_const(a_i32_const,0));
  630. asmlist.Concat(taicpu.op_const(a_local_set,machine_state));
  631. asmlist.Concat(taicpu.op_none(a_block));
  632. asmlist.Concat(taicpu.op_none(a_loop));
  633. current_asmdata.getjumplabel(state_machine_loop_start_label);
  634. asmlist.concat(tai_label.create(state_machine_loop_start_label));
  635. current_asmdata.getjumplabel(state_machine_exit);
  636. for block_nr:=0 to blocks.Count-1 do
  637. asmlist.Concat(taicpu.op_none(a_block));
  638. for block_nr:=0 to blocks.Count-1 do
  639. begin
  640. { TODO: this sequence can be replaced with a single br_table instruction }
  641. asmlist.Concat(taicpu.op_const(a_local_get,machine_state));
  642. asmlist.Concat(taicpu.op_const(a_i32_const,block_nr));
  643. asmlist.Concat(taicpu.op_none(a_i32_eq));
  644. asmlist.Concat(taicpu.op_const(a_br_if,block_nr));
  645. end;
  646. asmlist.Concat(taicpu.op_none(a_unreachable));
  647. tmplist:=TAsmList.Create;
  648. for block_nr:=0 to blocks.Count-1 do
  649. begin
  650. asmlist.Concat(taicpu.op_none(a_end_block));
  651. asmlist.Concat(tai_comment.Create(strpnew('block '+tostr(block_nr)+' for label '+blocks.NameOfIndex(block_nr))));
  652. curr_block:=TAsmList(blocks[block_nr]);
  653. hp:=tai(curr_block.First);
  654. while assigned(hp) do
  655. begin
  656. hpnext:=tai(hp.next);
  657. if (hp.typ=ait_instruction) and (taicpu(hp).opcode in [a_br,a_br_if]) and
  658. (taicpu(hp).ops=1) and
  659. (taicpu(hp).oper[0]^.typ=top_ref) and
  660. assigned(taicpu(hp).oper[0]^.ref^.symbol) then
  661. begin
  662. target_block_index:=blocks.FindIndexOf(taicpu(hp).oper[0]^.ref^.symbol.Name);
  663. curr_block.InsertBefore(tai_comment.Create(strpnew(
  664. 'branch '+gas_op2str[taicpu(hp).opcode]+
  665. ' '+taicpu(hp).oper[0]^.ref^.symbol.Name+
  666. ' target_block_index='+tostr(target_block_index))),hp);
  667. if target_block_index<>-1 then
  668. begin
  669. tmplist.Clear;
  670. if taicpu(hp).opcode=a_br_if then
  671. tmplist.Concat(taicpu.op_none(a_if));
  672. tmplist.Concat(taicpu.op_const(a_i32_const,target_block_index));
  673. tmplist.Concat(taicpu.op_const(a_local_set,machine_state));
  674. tmplist.Concat(taicpu.op_sym(a_br,state_machine_loop_start_label));
  675. if taicpu(hp).opcode=a_br_if then
  676. tmplist.Concat(taicpu.op_none(a_end_if));
  677. curr_block.insertListAfter(hp,tmplist);
  678. curr_block.Remove(hp);
  679. end;
  680. end;
  681. hp:=hpnext;
  682. end;
  683. if block_nr<(blocks.Count-1) then
  684. begin
  685. curr_block.Concat(taicpu.op_const(a_i32_const,block_nr+1));
  686. curr_block.Concat(taicpu.op_const(a_local_set,machine_state));
  687. curr_block.Concat(taicpu.op_sym(a_br,state_machine_loop_start_label));
  688. end
  689. else
  690. curr_block.Concat(taicpu.op_sym(a_br,state_machine_exit));
  691. asmlist.concatList(curr_block);
  692. end;
  693. tmplist.Free;
  694. asmlist.Concat(taicpu.op_none(a_end_loop));
  695. asmlist.Concat(taicpu.op_none(a_end_block));
  696. asmlist.concat(tai_label.create(state_machine_exit));
  697. end;
  698. procedure filter_start_exit_code(asmlist: TAsmList; out entry_code, proc_body, exit_code: TAsmList);
  699. var
  700. hp, hpnext, hpprev: tai;
  701. begin
  702. entry_code:=TAsmList.Create;
  703. proc_body:=TAsmList.Create;
  704. exit_code:=TAsmList.Create;
  705. repeat
  706. hp:=tai(asmlist.First);
  707. if assigned(hp) then
  708. begin
  709. hpnext:=tai(hp.next);
  710. if (hp.typ=ait_instruction) and (taicpu(hp).opcode=a_block) then
  711. break;
  712. asmlist.Remove(hp);
  713. entry_code.Concat(hp);
  714. hp:=hpnext;
  715. end;
  716. until not assigned(hp);
  717. repeat
  718. hp:=tai(asmlist.Last);
  719. if assigned(hp) then
  720. begin
  721. hpprev:=tai(hp.Previous);
  722. if (hp.typ=ait_instruction) and (taicpu(hp).opcode=a_end_block) then
  723. break;
  724. asmlist.Remove(hp);
  725. exit_code.Insert(hp);
  726. hp:=hpprev;
  727. end;
  728. until not assigned(hp);
  729. proc_body.insertList(asmlist);
  730. end;
  731. procedure resolve_labels_of_asmlist_with_try_blocks_recursive(asmlist: TAsmList);
  732. var
  733. hp: tai;
  734. i: Integer;
  735. begin
  736. if not assigned(asmlist) then
  737. exit;
  738. hp:=tai(asmlist.First);
  739. while assigned(hp) do
  740. begin
  741. if hp.typ=ait_wasm_structured_instruction then
  742. begin
  743. if not (taicpu_wasm_structured_instruction(hp).wstyp in [aitws_try_catch,aitws_try_delegate]) then
  744. internalerror(2023102201);
  745. resolve_labels_of_asmlist_with_try_blocks_recursive(tai_wasmstruc_try(hp).try_asmlist);
  746. if taicpu_wasm_structured_instruction(hp).wstyp=aitws_try_catch then
  747. with tai_wasmstruc_try_catch(hp) do
  748. begin
  749. for i:=low(catch_list) to high(catch_list) do
  750. resolve_labels_of_asmlist_with_try_blocks_recursive(catch_list[i].asmlist);
  751. resolve_labels_of_asmlist_with_try_blocks_recursive(catch_all_asmlist);
  752. end
  753. else if taicpu_wasm_structured_instruction(hp).wstyp=aitws_try_delegate then
  754. {nothing}
  755. else
  756. internalerror(2023102202);
  757. end;
  758. hp:=tai(hp.next);
  759. end;
  760. resolve_labels_via_state_machine(asmlist);
  761. end;
  762. procedure resolve_labels_complex(var asmlist: TAsmList);
  763. var
  764. entry_code, proc_body, exit_code: TAsmList;
  765. begin
  766. filter_start_exit_code(asmlist,entry_code,proc_body,exit_code);
  767. asmlist.Free;
  768. asmlist:=proc_body;
  769. proc_body:=nil;
  770. wasm_convert_to_structured_asmlist(asmlist);
  771. map_structured_asmlist(asmlist,@ConvertBranchTargetNumbersToLabels);
  772. map_structured_asmlist(asmlist,@ConvertIfToBrIf);
  773. map_structured_asmlist(asmlist,@ConvertLoopToBr);
  774. wasm_convert_to_flat_asmlist(asmlist);
  775. map_structured_asmlist(asmlist,@StripBlockInstructions);
  776. wasm_convert_to_structured_asmlist(asmlist);
  777. resolve_labels_of_asmlist_with_try_blocks_recursive(asmlist);
  778. wasm_convert_to_flat_asmlist(asmlist);
  779. asmlist.insertList(entry_code);
  780. entry_code.free;
  781. asmlist.concatList(exit_code);
  782. exit_code.free;
  783. if not resolve_labels_simple(asmlist) then
  784. internalerror(2023102101);
  785. end;
  786. function prepare_locals: TAsmList;
  787. var
  788. local: tai_local;
  789. l : TWasmLocal;
  790. begin
  791. result:=TAsmList.create;
  792. local:=tai_local.create([]);
  793. result.Concat(local);
  794. l:=ttgwasm(tg).localvars.first;
  795. FFuncType:=findfirst_tai_functype(aktproccode).functype;
  796. FLocals:=Copy(FFuncType.params);
  797. FParametersCount:=Length(FLocals);
  798. FFirstFreeLocal:=FParametersCount;
  799. while Assigned(l) do
  800. begin
  801. SetLength(FLocals,Length(FLocals)+1);
  802. FLocals[High(FLocals)]:=l.typ;
  803. local.AddLocal(l.typ);
  804. l:=l.nextseq;
  805. Inc(FFirstFreeLocal);
  806. end;
  807. end;
  808. procedure add_extra_allocated_locals(localslist: TAsmList);
  809. begin
  810. if tai(localslist.First).typ<>ait_local then
  811. internalerror(2024081501);
  812. tai_local(localslist.First).AddLocals(FAllocatedLocals);
  813. end;
  814. procedure insert_localslist(destlist,localslist: TAsmList);
  815. begin
  816. if assigned(localslist) then
  817. destlist.insertListAfter(findfirst_tai_functype(destlist),localslist);
  818. end;
  819. procedure check_goto_br_instructions(list: TAsmList; out HasGotoBrInstructions: boolean);
  820. var
  821. hp: tai;
  822. begin
  823. HasGotoBrInstructions:=False;
  824. hp:=tai(list.first);
  825. while assigned(hp) do
  826. begin
  827. if (hp.typ=ait_instruction) and (taicpu(hp).is_br_generated_by_goto) then
  828. begin
  829. HasGotoBrInstructions:=True;
  830. if (taicpu(hp).opcode<>a_br) or
  831. (taicpu(hp).ops<>1) or
  832. (taicpu(hp).oper[0]^.typ<>top_ref) or
  833. (taicpu(hp).oper[0]^.ref^.offset<>0) or
  834. (taicpu(hp).oper[0]^.ref^.base<>NR_NO) or
  835. (taicpu(hp).oper[0]^.ref^.index<>NR_NO) or
  836. (taicpu(hp).oper[0]^.ref^.symbol=nil) then
  837. internalerror(2023102203);
  838. if not is_goto_target(taicpu(hp).oper[0]^.ref^.symbol) then
  839. internalerror(2023102204);
  840. end;
  841. hp:=tai(hp.next);
  842. end;
  843. end;
  844. procedure validate_code;
  845. var
  846. vs: TWasmValidationStacks;
  847. hp: tai;
  848. begin
  849. vs:=TWasmValidationStacks.Create(@GetLocalType,FFuncType);
  850. hp:=tai(aktproccode.first);
  851. while assigned(hp) do
  852. begin
  853. if hp.typ=ait_instruction then
  854. vs.Validate(taicpu(hp));
  855. hp:=tai(hp.next);
  856. end;
  857. vs.Free;
  858. end;
  859. procedure postprocess_code_assembler;
  860. begin
  861. aktproccode.InsertAfter(tai_local.create([]),findfirst_tai_functype(aktproccode));
  862. end;
  863. var
  864. localslist: TAsmList;
  865. labels_resolved, has_goto: Boolean;
  866. begin
  867. if po_assembler in procdef.procoptions then
  868. begin
  869. postprocess_code_assembler;
  870. exit;
  871. end;
  872. check_goto_br_instructions(aktproccode,has_goto);
  873. localslist:=prepare_locals;
  874. replace_local_frame_pointer(aktproccode);
  875. labels_resolved:=false;
  876. if not has_goto then
  877. { TODO: make resolve_labels_simple handle goto labels correctly }
  878. labels_resolved:=resolve_labels_simple(aktproccode);
  879. {$ifndef DEBUG_WASM_GOTO}
  880. if not labels_resolved then
  881. {$endif DEBUG_WASM_GOTO}
  882. resolve_labels_complex(aktproccode);
  883. add_extra_allocated_locals(localslist);
  884. insert_localslist(aktproccode,localslist);
  885. localslist.Free;
  886. {$ifdef DEBUG_WASM_VALIDATION}
  887. validate_code;
  888. {$endif DEBUG_WASM_VALIDATION}
  889. inherited postprocess_code;
  890. end;
  891. procedure tcpuprocinfo.set_first_temp_offset;
  892. var
  893. sz : integer;
  894. i : integer;
  895. sym: tsym;
  896. begin
  897. {
  898. Stackframe layout:
  899. sp:
  900. <incoming parameters>
  901. sp+first_temp_offset:
  902. <locals>
  903. <temp>
  904. }
  905. procdef.init_paraloc_info(calleeside);
  906. sz := procdef.calleeargareasize;
  907. tg.setfirsttemp(sz);
  908. end;
  909. procedure tcpuprocinfo.add_goto_target(l: tasmlabel);
  910. begin
  911. FGotoTargets.Add(l.Name,l);
  912. end;
  913. function tcpuprocinfo.is_goto_target(l: tasmsymbol): Boolean;
  914. begin
  915. result:=FGotoTargets.FindIndexOf(l.Name)<>-1;
  916. end;
  917. initialization
  918. cprocinfo:=tcpuprocinfo;
  919. end.