nwasmcal.pas 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. {
  2. Copyright (c) 2019 by Dmitry Boyarintsev
  3. WebAssembly-specific code for call nodes
  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 nwasmcal;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. cgbase,
  22. symtype,symdef,cgutils,parabase,
  23. node,ncal,ncgcal,hlcgobj,aasmcpu,cpubase, wasmdef;
  24. type
  25. { twasmcallparanode }
  26. twasmcallparanode = class(tcgcallparanode)
  27. private
  28. procedure secondpass_all;
  29. procedure push_all;
  30. public
  31. procedure secondcallparan;override;
  32. end;
  33. { twasmcallnode }
  34. twasmcallnode = class(tcgcallnode)
  35. protected
  36. function pass_typecheck:tnode;override;
  37. procedure extra_post_call_code; override;
  38. procedure do_release_unused_return_value; override;
  39. procedure set_result_location(realresdef: tstoreddef); override;
  40. end;
  41. implementation
  42. uses
  43. globals, globtype, verbose, aasmdata, defutil, tgobj, hlcgcpu, symconst, symsym, symcpu;
  44. { twasmcallparanode }
  45. procedure twasmcallparanode.secondpass_all;
  46. begin
  47. { Skip nothingn nodes which are used after disabling
  48. a parameter }
  49. if (left.nodetype<>nothingn) then
  50. secondcallparan_do_secondpass;
  51. { next parameter }
  52. if assigned(right) then
  53. twasmcallparanode(right).secondpass_all;
  54. end;
  55. procedure twasmcallparanode.push_all;
  56. begin
  57. { Skip nothingn nodes which are used after disabling
  58. a parameter }
  59. if (left.nodetype<>nothingn) then
  60. secondcallparan_after_secondpass;
  61. { next parameter }
  62. if assigned(right) then
  63. twasmcallparanode(right).push_all;
  64. end;
  65. procedure twasmcallparanode.secondcallparan;
  66. begin
  67. if not(assigned(parasym)) then
  68. internalerror(200304242);
  69. { On WebAssembly we generate code for evaluating all the parameters
  70. first, and then we push them only after we've evaluated them all.
  71. This is because the evaluation phase can generate labels, which
  72. wreaks havoc in our 'goto' label resolution algorithm, when there
  73. are labels at different stack heights. }
  74. secondpass_all;
  75. push_all;
  76. end;
  77. { twasmcallnode }
  78. function twasmcallnode.pass_typecheck:tnode;
  79. var
  80. p: tcallparanode;
  81. pvs: tparavarsym;
  82. begin
  83. result:=inherited;
  84. if codegenerror then
  85. exit;
  86. if assigned(procdefinition) then
  87. begin
  88. p:=tcallparanode(left);
  89. while assigned(p) do
  90. begin
  91. pvs:=p.parasym;
  92. if assigned(p.left) and is_wasm_reference_type(p.left.resultdef) and
  93. assigned(pvs) and
  94. ((pvs.varspez in [vs_var,vs_constref,vs_out]) or
  95. ((pvs.varspez=vs_const) and (pvs.vardef.typ=formaldef))) then
  96. CGMessage(parser_e_wasm_ref_types_can_only_be_passed_by_value);
  97. p:=tcallparanode(tcallparanode(p).right);
  98. end;
  99. end;
  100. end;
  101. procedure twasmcallnode.extra_post_call_code;
  102. begin
  103. thlcgwasm(hlcg).g_adjust_stack_after_call(current_asmdata.CurrAsmList,procdefinition);
  104. hlcg.g_maybe_checkforexceptions(current_asmdata.CurrAsmList);
  105. end;
  106. procedure twasmcallnode.do_release_unused_return_value;
  107. var
  108. ft: TWasmFuncType;
  109. i: Integer;
  110. begin
  111. if procdefinition.typ=procvardef then
  112. ft:=tcpuprocvardef(procdefinition).create_functype
  113. else
  114. ft:=tcpuprocdef(procdefinition).create_functype;
  115. for i:=1 to Length(ft.results) do
  116. begin
  117. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_drop));
  118. thlcgwasm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  119. end;
  120. ft.free;
  121. end;
  122. procedure twasmcallnode.set_result_location(realresdef: tstoreddef);
  123. begin
  124. // default implementation is placing the return value on LOC_REGISTER.
  125. // WebAssembly always returns the value on stack.
  126. location_reset_ref(location,LOC_REFERENCE,def_cgsize(realresdef),1,[]);
  127. tg.gethltemp(current_asmdata.CurrAsmList,realresdef,retloc.intsize,tt_normal,location.reference);
  128. end;
  129. begin
  130. ccallnode:=twasmcallnode;
  131. ccallparanode:=twasmcallparanode;
  132. end.