asmjit 0.2.0 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (204) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/Rakefile +5 -3
  4. data/asmjit.gemspec +1 -3
  5. data/ext/asmjit/asmjit/.editorconfig +10 -0
  6. data/ext/asmjit/asmjit/.github/FUNDING.yml +1 -0
  7. data/ext/asmjit/asmjit/.github/workflows/build-config.json +47 -0
  8. data/ext/asmjit/asmjit/.github/workflows/build.yml +156 -0
  9. data/ext/asmjit/asmjit/.gitignore +6 -0
  10. data/ext/asmjit/asmjit/CMakeLists.txt +611 -0
  11. data/ext/asmjit/asmjit/LICENSE.md +17 -0
  12. data/ext/asmjit/asmjit/README.md +69 -0
  13. data/ext/asmjit/asmjit/src/asmjit/a64.h +62 -0
  14. data/ext/asmjit/asmjit/src/asmjit/arm/a64archtraits_p.h +81 -0
  15. data/ext/asmjit/asmjit/src/asmjit/arm/a64assembler.cpp +5115 -0
  16. data/ext/asmjit/asmjit/src/asmjit/arm/a64assembler.h +72 -0
  17. data/ext/asmjit/asmjit/src/asmjit/arm/a64builder.cpp +51 -0
  18. data/ext/asmjit/asmjit/src/asmjit/arm/a64builder.h +57 -0
  19. data/ext/asmjit/asmjit/src/asmjit/arm/a64compiler.cpp +60 -0
  20. data/ext/asmjit/asmjit/src/asmjit/arm/a64compiler.h +247 -0
  21. data/ext/asmjit/asmjit/src/asmjit/arm/a64emithelper.cpp +464 -0
  22. data/ext/asmjit/asmjit/src/asmjit/arm/a64emithelper_p.h +50 -0
  23. data/ext/asmjit/asmjit/src/asmjit/arm/a64emitter.h +1228 -0
  24. data/ext/asmjit/asmjit/src/asmjit/arm/a64formatter.cpp +298 -0
  25. data/ext/asmjit/asmjit/src/asmjit/arm/a64formatter_p.h +59 -0
  26. data/ext/asmjit/asmjit/src/asmjit/arm/a64func.cpp +189 -0
  27. data/ext/asmjit/asmjit/src/asmjit/arm/a64func_p.h +33 -0
  28. data/ext/asmjit/asmjit/src/asmjit/arm/a64globals.h +1894 -0
  29. data/ext/asmjit/asmjit/src/asmjit/arm/a64instapi.cpp +278 -0
  30. data/ext/asmjit/asmjit/src/asmjit/arm/a64instapi_p.h +41 -0
  31. data/ext/asmjit/asmjit/src/asmjit/arm/a64instdb.cpp +1957 -0
  32. data/ext/asmjit/asmjit/src/asmjit/arm/a64instdb.h +74 -0
  33. data/ext/asmjit/asmjit/src/asmjit/arm/a64instdb_p.h +876 -0
  34. data/ext/asmjit/asmjit/src/asmjit/arm/a64operand.cpp +85 -0
  35. data/ext/asmjit/asmjit/src/asmjit/arm/a64operand.h +312 -0
  36. data/ext/asmjit/asmjit/src/asmjit/arm/a64rapass.cpp +852 -0
  37. data/ext/asmjit/asmjit/src/asmjit/arm/a64rapass_p.h +105 -0
  38. data/ext/asmjit/asmjit/src/asmjit/arm/a64utils.h +179 -0
  39. data/ext/asmjit/asmjit/src/asmjit/arm/armformatter.cpp +143 -0
  40. data/ext/asmjit/asmjit/src/asmjit/arm/armformatter_p.h +44 -0
  41. data/ext/asmjit/asmjit/src/asmjit/arm/armglobals.h +21 -0
  42. data/ext/asmjit/asmjit/src/asmjit/arm/armoperand.h +621 -0
  43. data/ext/asmjit/asmjit/src/asmjit/arm.h +62 -0
  44. data/ext/asmjit/asmjit/src/asmjit/asmjit-scope-begin.h +17 -0
  45. data/ext/asmjit/asmjit/src/asmjit/asmjit-scope-end.h +9 -0
  46. data/ext/asmjit/asmjit/src/asmjit/asmjit.h +33 -0
  47. data/ext/asmjit/asmjit/src/asmjit/core/api-build_p.h +55 -0
  48. data/ext/asmjit/asmjit/src/asmjit/core/api-config.h +613 -0
  49. data/ext/asmjit/asmjit/src/asmjit/core/archcommons.h +229 -0
  50. data/ext/asmjit/asmjit/src/asmjit/core/archtraits.cpp +160 -0
  51. data/ext/asmjit/asmjit/src/asmjit/core/archtraits.h +290 -0
  52. data/ext/asmjit/asmjit/src/asmjit/core/assembler.cpp +406 -0
  53. data/ext/asmjit/asmjit/src/asmjit/core/assembler.h +129 -0
  54. data/ext/asmjit/asmjit/src/asmjit/core/builder.cpp +889 -0
  55. data/ext/asmjit/asmjit/src/asmjit/core/builder.h +1391 -0
  56. data/ext/asmjit/asmjit/src/asmjit/core/codebuffer.h +113 -0
  57. data/ext/asmjit/asmjit/src/asmjit/core/codeholder.cpp +1149 -0
  58. data/ext/asmjit/asmjit/src/asmjit/core/codeholder.h +1035 -0
  59. data/ext/asmjit/asmjit/src/asmjit/core/codewriter.cpp +175 -0
  60. data/ext/asmjit/asmjit/src/asmjit/core/codewriter_p.h +179 -0
  61. data/ext/asmjit/asmjit/src/asmjit/core/compiler.cpp +582 -0
  62. data/ext/asmjit/asmjit/src/asmjit/core/compiler.h +737 -0
  63. data/ext/asmjit/asmjit/src/asmjit/core/compilerdefs.h +173 -0
  64. data/ext/asmjit/asmjit/src/asmjit/core/constpool.cpp +363 -0
  65. data/ext/asmjit/asmjit/src/asmjit/core/constpool.h +250 -0
  66. data/ext/asmjit/asmjit/src/asmjit/core/cpuinfo.cpp +1162 -0
  67. data/ext/asmjit/asmjit/src/asmjit/core/cpuinfo.h +813 -0
  68. data/ext/asmjit/asmjit/src/asmjit/core/emithelper.cpp +323 -0
  69. data/ext/asmjit/asmjit/src/asmjit/core/emithelper_p.h +58 -0
  70. data/ext/asmjit/asmjit/src/asmjit/core/emitter.cpp +333 -0
  71. data/ext/asmjit/asmjit/src/asmjit/core/emitter.h +741 -0
  72. data/ext/asmjit/asmjit/src/asmjit/core/emitterutils.cpp +129 -0
  73. data/ext/asmjit/asmjit/src/asmjit/core/emitterutils_p.h +89 -0
  74. data/ext/asmjit/asmjit/src/asmjit/core/environment.cpp +46 -0
  75. data/ext/asmjit/asmjit/src/asmjit/core/environment.h +508 -0
  76. data/ext/asmjit/asmjit/src/asmjit/core/errorhandler.cpp +14 -0
  77. data/ext/asmjit/asmjit/src/asmjit/core/errorhandler.h +228 -0
  78. data/ext/asmjit/asmjit/src/asmjit/core/formatter.cpp +584 -0
  79. data/ext/asmjit/asmjit/src/asmjit/core/formatter.h +247 -0
  80. data/ext/asmjit/asmjit/src/asmjit/core/formatter_p.h +34 -0
  81. data/ext/asmjit/asmjit/src/asmjit/core/func.cpp +286 -0
  82. data/ext/asmjit/asmjit/src/asmjit/core/func.h +1445 -0
  83. data/ext/asmjit/asmjit/src/asmjit/core/funcargscontext.cpp +293 -0
  84. data/ext/asmjit/asmjit/src/asmjit/core/funcargscontext_p.h +199 -0
  85. data/ext/asmjit/asmjit/src/asmjit/core/globals.cpp +133 -0
  86. data/ext/asmjit/asmjit/src/asmjit/core/globals.h +393 -0
  87. data/ext/asmjit/asmjit/src/asmjit/core/inst.cpp +113 -0
  88. data/ext/asmjit/asmjit/src/asmjit/core/inst.h +772 -0
  89. data/ext/asmjit/asmjit/src/asmjit/core/jitallocator.cpp +1242 -0
  90. data/ext/asmjit/asmjit/src/asmjit/core/jitallocator.h +261 -0
  91. data/ext/asmjit/asmjit/src/asmjit/core/jitruntime.cpp +80 -0
  92. data/ext/asmjit/asmjit/src/asmjit/core/jitruntime.h +89 -0
  93. data/ext/asmjit/asmjit/src/asmjit/core/logger.cpp +69 -0
  94. data/ext/asmjit/asmjit/src/asmjit/core/logger.h +198 -0
  95. data/ext/asmjit/asmjit/src/asmjit/core/misc_p.h +33 -0
  96. data/ext/asmjit/asmjit/src/asmjit/core/operand.cpp +132 -0
  97. data/ext/asmjit/asmjit/src/asmjit/core/operand.h +1611 -0
  98. data/ext/asmjit/asmjit/src/asmjit/core/osutils.cpp +84 -0
  99. data/ext/asmjit/asmjit/src/asmjit/core/osutils.h +61 -0
  100. data/ext/asmjit/asmjit/src/asmjit/core/osutils_p.h +68 -0
  101. data/ext/asmjit/asmjit/src/asmjit/core/raassignment_p.h +418 -0
  102. data/ext/asmjit/asmjit/src/asmjit/core/rabuilders_p.h +612 -0
  103. data/ext/asmjit/asmjit/src/asmjit/core/radefs_p.h +1204 -0
  104. data/ext/asmjit/asmjit/src/asmjit/core/ralocal.cpp +1166 -0
  105. data/ext/asmjit/asmjit/src/asmjit/core/ralocal_p.h +254 -0
  106. data/ext/asmjit/asmjit/src/asmjit/core/rapass.cpp +1969 -0
  107. data/ext/asmjit/asmjit/src/asmjit/core/rapass_p.h +1183 -0
  108. data/ext/asmjit/asmjit/src/asmjit/core/rastack.cpp +184 -0
  109. data/ext/asmjit/asmjit/src/asmjit/core/rastack_p.h +171 -0
  110. data/ext/asmjit/asmjit/src/asmjit/core/string.cpp +559 -0
  111. data/ext/asmjit/asmjit/src/asmjit/core/string.h +372 -0
  112. data/ext/asmjit/asmjit/src/asmjit/core/support.cpp +494 -0
  113. data/ext/asmjit/asmjit/src/asmjit/core/support.h +1773 -0
  114. data/ext/asmjit/asmjit/src/asmjit/core/target.cpp +14 -0
  115. data/ext/asmjit/asmjit/src/asmjit/core/target.h +53 -0
  116. data/ext/asmjit/asmjit/src/asmjit/core/type.cpp +74 -0
  117. data/ext/asmjit/asmjit/src/asmjit/core/type.h +419 -0
  118. data/ext/asmjit/asmjit/src/asmjit/core/virtmem.cpp +722 -0
  119. data/ext/asmjit/asmjit/src/asmjit/core/virtmem.h +242 -0
  120. data/ext/asmjit/asmjit/src/asmjit/core/zone.cpp +353 -0
  121. data/ext/asmjit/asmjit/src/asmjit/core/zone.h +615 -0
  122. data/ext/asmjit/asmjit/src/asmjit/core/zonehash.cpp +309 -0
  123. data/ext/asmjit/asmjit/src/asmjit/core/zonehash.h +186 -0
  124. data/ext/asmjit/asmjit/src/asmjit/core/zonelist.cpp +163 -0
  125. data/ext/asmjit/asmjit/src/asmjit/core/zonelist.h +209 -0
  126. data/ext/asmjit/asmjit/src/asmjit/core/zonestack.cpp +176 -0
  127. data/ext/asmjit/asmjit/src/asmjit/core/zonestack.h +239 -0
  128. data/ext/asmjit/asmjit/src/asmjit/core/zonestring.h +120 -0
  129. data/ext/asmjit/asmjit/src/asmjit/core/zonetree.cpp +99 -0
  130. data/ext/asmjit/asmjit/src/asmjit/core/zonetree.h +380 -0
  131. data/ext/asmjit/asmjit/src/asmjit/core/zonevector.cpp +356 -0
  132. data/ext/asmjit/asmjit/src/asmjit/core/zonevector.h +690 -0
  133. data/ext/asmjit/asmjit/src/asmjit/core.h +1861 -0
  134. data/ext/asmjit/asmjit/src/asmjit/x86/x86archtraits_p.h +148 -0
  135. data/ext/asmjit/asmjit/src/asmjit/x86/x86assembler.cpp +5110 -0
  136. data/ext/asmjit/asmjit/src/asmjit/x86/x86assembler.h +685 -0
  137. data/ext/asmjit/asmjit/src/asmjit/x86/x86builder.cpp +52 -0
  138. data/ext/asmjit/asmjit/src/asmjit/x86/x86builder.h +351 -0
  139. data/ext/asmjit/asmjit/src/asmjit/x86/x86compiler.cpp +61 -0
  140. data/ext/asmjit/asmjit/src/asmjit/x86/x86compiler.h +721 -0
  141. data/ext/asmjit/asmjit/src/asmjit/x86/x86emithelper.cpp +619 -0
  142. data/ext/asmjit/asmjit/src/asmjit/x86/x86emithelper_p.h +60 -0
  143. data/ext/asmjit/asmjit/src/asmjit/x86/x86emitter.h +4315 -0
  144. data/ext/asmjit/asmjit/src/asmjit/x86/x86formatter.cpp +944 -0
  145. data/ext/asmjit/asmjit/src/asmjit/x86/x86formatter_p.h +58 -0
  146. data/ext/asmjit/asmjit/src/asmjit/x86/x86func.cpp +503 -0
  147. data/ext/asmjit/asmjit/src/asmjit/x86/x86func_p.h +33 -0
  148. data/ext/asmjit/asmjit/src/asmjit/x86/x86globals.h +2169 -0
  149. data/ext/asmjit/asmjit/src/asmjit/x86/x86instapi.cpp +1732 -0
  150. data/ext/asmjit/asmjit/src/asmjit/x86/x86instapi_p.h +41 -0
  151. data/ext/asmjit/asmjit/src/asmjit/x86/x86instdb.cpp +4427 -0
  152. data/ext/asmjit/asmjit/src/asmjit/x86/x86instdb.h +563 -0
  153. data/ext/asmjit/asmjit/src/asmjit/x86/x86instdb_p.h +311 -0
  154. data/ext/asmjit/asmjit/src/asmjit/x86/x86opcode_p.h +436 -0
  155. data/ext/asmjit/asmjit/src/asmjit/x86/x86operand.cpp +231 -0
  156. data/ext/asmjit/asmjit/src/asmjit/x86/x86operand.h +1085 -0
  157. data/ext/asmjit/asmjit/src/asmjit/x86/x86rapass.cpp +1509 -0
  158. data/ext/asmjit/asmjit/src/asmjit/x86/x86rapass_p.h +94 -0
  159. data/ext/asmjit/asmjit/src/asmjit/x86.h +93 -0
  160. data/ext/asmjit/asmjit/src/asmjit.natvis +245 -0
  161. data/ext/asmjit/asmjit/test/asmjit_test_assembler.cpp +84 -0
  162. data/ext/asmjit/asmjit/test/asmjit_test_assembler.h +85 -0
  163. data/ext/asmjit/asmjit/test/asmjit_test_assembler_a64.cpp +4006 -0
  164. data/ext/asmjit/asmjit/test/asmjit_test_assembler_x64.cpp +17833 -0
  165. data/ext/asmjit/asmjit/test/asmjit_test_assembler_x86.cpp +8300 -0
  166. data/ext/asmjit/asmjit/test/asmjit_test_compiler.cpp +253 -0
  167. data/ext/asmjit/asmjit/test/asmjit_test_compiler.h +73 -0
  168. data/ext/asmjit/asmjit/test/asmjit_test_compiler_a64.cpp +690 -0
  169. data/ext/asmjit/asmjit/test/asmjit_test_compiler_x86.cpp +4317 -0
  170. data/ext/asmjit/asmjit/test/asmjit_test_emitters.cpp +197 -0
  171. data/ext/asmjit/asmjit/test/asmjit_test_instinfo.cpp +181 -0
  172. data/ext/asmjit/asmjit/test/asmjit_test_misc.h +257 -0
  173. data/ext/asmjit/asmjit/test/asmjit_test_perf.cpp +62 -0
  174. data/ext/asmjit/asmjit/test/asmjit_test_perf.h +61 -0
  175. data/ext/asmjit/asmjit/test/asmjit_test_perf_a64.cpp +699 -0
  176. data/ext/asmjit/asmjit/test/asmjit_test_perf_x86.cpp +5032 -0
  177. data/ext/asmjit/asmjit/test/asmjit_test_unit.cpp +172 -0
  178. data/ext/asmjit/asmjit/test/asmjit_test_x86_sections.cpp +172 -0
  179. data/ext/asmjit/asmjit/test/asmjitutils.h +38 -0
  180. data/ext/asmjit/asmjit/test/broken.cpp +312 -0
  181. data/ext/asmjit/asmjit/test/broken.h +148 -0
  182. data/ext/asmjit/asmjit/test/cmdline.h +61 -0
  183. data/ext/asmjit/asmjit/test/performancetimer.h +41 -0
  184. data/ext/asmjit/asmjit/tools/configure-makefiles.sh +13 -0
  185. data/ext/asmjit/asmjit/tools/configure-ninja.sh +13 -0
  186. data/ext/asmjit/asmjit/tools/configure-sanitizers.sh +13 -0
  187. data/ext/asmjit/asmjit/tools/configure-vs2019-x64.bat +2 -0
  188. data/ext/asmjit/asmjit/tools/configure-vs2019-x86.bat +2 -0
  189. data/ext/asmjit/asmjit/tools/configure-vs2022-x64.bat +2 -0
  190. data/ext/asmjit/asmjit/tools/configure-vs2022-x86.bat +2 -0
  191. data/ext/asmjit/asmjit/tools/configure-xcode.sh +8 -0
  192. data/ext/asmjit/asmjit/tools/enumgen.js +417 -0
  193. data/ext/asmjit/asmjit/tools/enumgen.sh +3 -0
  194. data/ext/asmjit/asmjit/tools/tablegen-arm.js +365 -0
  195. data/ext/asmjit/asmjit/tools/tablegen-arm.sh +3 -0
  196. data/ext/asmjit/asmjit/tools/tablegen-x86.js +2638 -0
  197. data/ext/asmjit/asmjit/tools/tablegen-x86.sh +3 -0
  198. data/ext/asmjit/asmjit/tools/tablegen.js +947 -0
  199. data/ext/asmjit/asmjit/tools/tablegen.sh +4 -0
  200. data/ext/asmjit/asmjit.cc +167 -30
  201. data/ext/asmjit/extconf.rb +9 -9
  202. data/lib/asmjit/version.rb +1 -1
  203. data/lib/asmjit.rb +14 -4
  204. metadata +198 -17
@@ -0,0 +1,1445 @@
1
+ // This file is part of AsmJit project <https://asmjit.com>
2
+ //
3
+ // See asmjit.h or LICENSE.md for license and copyright information
4
+ // SPDX-License-Identifier: Zlib
5
+
6
+ #ifndef ASMJIT_CORE_FUNC_H_INCLUDED
7
+ #define ASMJIT_CORE_FUNC_H_INCLUDED
8
+
9
+ #include "../core/archtraits.h"
10
+ #include "../core/environment.h"
11
+ #include "../core/operand.h"
12
+ #include "../core/type.h"
13
+ #include "../core/support.h"
14
+
15
+ ASMJIT_BEGIN_NAMESPACE
16
+
17
+ //! \addtogroup asmjit_function
18
+ //! \{
19
+
20
+ //! Calling convention id.
21
+ //!
22
+ //! Calling conventions can be divided into the following groups:
23
+ //!
24
+ //! - Universal - calling conventions are applicable to any target. They will be converted to a target dependent
25
+ //! calling convention at runtime by \ref CallConv::init() with some help from \ref Environment. The purpose of
26
+ //! these calling conventions is to make using functions less target dependent and closer to C and C++.
27
+ //!
28
+ //! - Target specific - calling conventions that are used by a particular architecture and ABI. For example
29
+ //! Windows 64-bit calling convention and AMD64 SystemV calling convention.
30
+ enum class CallConvId : uint8_t {
31
+ //! None or invalid (can't be used).
32
+ kNone = 0,
33
+
34
+ // Universal Calling Conventions
35
+ // -----------------------------
36
+
37
+ //! Standard function call or explicit `__cdecl` where it can be specified.
38
+ //!
39
+ //! This is a universal calling convention, which is used to initialize specific calling connventions based on
40
+ //! architecture, platform, and its ABI.
41
+ kCDecl = 1,
42
+
43
+ //! `__stdcall` on targets that support this calling convention (X86).
44
+ //!
45
+ //! \note This calling convention is only supported on 32-bit X86. If used on environment that doesn't support
46
+ //! this calling convention it will be replaced by \ref CallConvId::kCDecl.
47
+ kStdCall = 2,
48
+
49
+ //! `__fastcall` on targets that support this calling convention (X86).
50
+ //!
51
+ //! \note This calling convention is only supported on 32-bit X86. If used on environment that doesn't support
52
+ //! this calling convention it will be replaced by \ref CallConvId::kCDecl.
53
+ kFastCall = 3,
54
+
55
+ //! `__vectorcall` on targets that support this calling convention (X86/X64).
56
+ //!
57
+ //! \note This calling convention is only supported on 32-bit and 64-bit X86 architecture on Windows platform.
58
+ //! If used on environment that doesn't support this calling it will be replaced by \ref CallConvId::kCDecl.
59
+ kVectorCall = 4,
60
+
61
+ //! `__thiscall` on targets that support this calling convention (X86).
62
+ //!
63
+ //! \note This calling convention is only supported on 32-bit X86 Windows platform. If used on environment that
64
+ //! doesn't support this calling convention it will be replaced by \ref CallConvId::kCDecl.
65
+ kThisCall = 5,
66
+
67
+ //! `__attribute__((regparm(1)))` convention (GCC and Clang).
68
+ kRegParm1 = 6,
69
+ //! `__attribute__((regparm(2)))` convention (GCC and Clang).
70
+ kRegParm2 = 7,
71
+ //! `__attribute__((regparm(3)))` convention (GCC and Clang).
72
+ kRegParm3 = 8,
73
+
74
+ //! Soft-float calling convention (ARM).
75
+ //!
76
+ //! Floating point arguments are passed via general purpose registers.
77
+ kSoftFloat = 9,
78
+
79
+ //! Hard-float calling convention (ARM).
80
+ //!
81
+ //! Floating point arguments are passed via SIMD registers.
82
+ kHardFloat = 10,
83
+
84
+ //! AsmJit specific calling convention designed for calling functions inside a multimedia code that don't use many
85
+ //! registers internally, but are long enough to be called and not inlined. These functions are usually used to
86
+ //! calculate trigonometric functions, logarithms, etc...
87
+ kLightCall2 = 16,
88
+ kLightCall3 = 17,
89
+ kLightCall4 = 18,
90
+
91
+ // ABI-Specific Calling Conventions
92
+ // --------------------------------
93
+
94
+ //! X64 System-V calling convention.
95
+ kX64SystemV = 32,
96
+ //! X64 Windows calling convention.
97
+ kX64Windows = 33,
98
+
99
+ //! Maximum value of `CallConvId`.
100
+ kMaxValue = kX64Windows,
101
+
102
+ // Host Calling Conventions
103
+ // ------------------------
104
+
105
+ //! Host calling convention detected at compile-time.
106
+ kHost =
107
+ #if defined(_DOXYGEN)
108
+ DETECTED_AT_COMPILE_TIME
109
+ #elif ASMJIT_ARCH_ARM == 32 && defined(__SOFTFP__)
110
+ kSoftFloat
111
+ #elif ASMJIT_ARCH_ARM == 32 && !defined(__SOFTFP__)
112
+ kHardFloat
113
+ #else
114
+ kCDecl
115
+ #endif
116
+ };
117
+
118
+ //! Strategy used by calling conventions to assign registers to function arguments.
119
+ //!
120
+ //! Calling convention strategy describes how AsmJit should convert function arguments used by \ref FuncSignature
121
+ //! into register identifiers and stack offsets. The \ref CallConvStrategy::kDefault strategy assigns registers
122
+ //! and then stack whereas \ref CallConvStrategy::kX64Windows strategy does register shadowing as defined by WIN64
123
+ //! calling convention, which is only used by 64-bit Windows.
124
+ enum class CallConvStrategy : uint8_t {
125
+ //! Default register assignment strategy.
126
+ kDefault = 0,
127
+ //! Windows 64-bit ABI register assignment strategy.
128
+ kX64Windows = 1,
129
+ //! Windows 64-bit __vectorcall register assignment strategy.
130
+ kX64VectorCall = 2,
131
+
132
+ //! Maximum value of `CallConvStrategy`.
133
+ kMaxValue = kX64VectorCall
134
+ };
135
+
136
+ //! Calling convention flags.
137
+ enum class CallConvFlags : uint32_t {
138
+ //! No flags.
139
+ kNone = 0,
140
+ //! Callee is responsible for cleaning up the stack.
141
+ kCalleePopsStack = 0x0001u,
142
+ //! Pass vector arguments indirectly (as a pointer).
143
+ kIndirectVecArgs = 0x0002u,
144
+ //! Pass F32 and F64 arguments via VEC128 register.
145
+ kPassFloatsByVec = 0x0004u,
146
+ //! Pass MMX and vector arguments via stack if the function has variable arguments.
147
+ kPassVecByStackIfVA = 0x0008u,
148
+ //! MMX registers are passed and returned via GP registers.
149
+ kPassMmxByGp = 0x0010u,
150
+ //! MMX registers are passed and returned via XMM registers.
151
+ kPassMmxByXmm = 0x0020u,
152
+ //! Calling convention can be used with variable arguments.
153
+ kVarArgCompatible = 0x0080u
154
+ };
155
+ ASMJIT_DEFINE_ENUM_FLAGS(CallConvFlags)
156
+
157
+ //! Function calling convention.
158
+ //!
159
+ //! Function calling convention is a scheme that defines how function parameters are passed and how function
160
+ //! returns its result. AsmJit defines a variety of architecture and OS specific calling conventions and also
161
+ //! provides a compile time detection to make the code-generation easier.
162
+ struct CallConv {
163
+ //! \name Constants
164
+ //! \{
165
+
166
+ enum : uint32_t {
167
+ //! Maximum number of register arguments per register group.
168
+ //!
169
+ //! \note This is not really AsmJit's limitatation, it's just the number that makes sense considering all common
170
+ //! calling conventions. Usually even conventions that use registers to pass function arguments are limited to 8
171
+ //! and less arguments passed via registers per group.
172
+ kMaxRegArgsPerGroup = 16
173
+ };
174
+
175
+ //! \}
176
+
177
+ //! \name Members
178
+ //! \{
179
+
180
+ //! Target architecture.
181
+ Arch _arch;
182
+ //! Calling convention id.
183
+ CallConvId _id;
184
+ //! Register assignment strategy.
185
+ CallConvStrategy _strategy;
186
+
187
+ //! Red zone size (AMD64 == 128 bytes).
188
+ uint8_t _redZoneSize;
189
+ //! Spill zone size (WIN-X64 == 32 bytes).
190
+ uint8_t _spillZoneSize;
191
+ //! Natural stack alignment as defined by OS/ABI.
192
+ uint8_t _naturalStackAlignment;
193
+
194
+ //! Calling convention flags.
195
+ CallConvFlags _flags;
196
+
197
+ //! Size to save/restore per register group.
198
+ Support::Array<uint8_t, Globals::kNumVirtGroups> _saveRestoreRegSize;
199
+ //! Alignment of save/restore groups.
200
+ Support::Array<uint8_t, Globals::kNumVirtGroups> _saveRestoreAlignment;
201
+
202
+ //! Mask of all passed registers, per group.
203
+ Support::Array<RegMask, Globals::kNumVirtGroups> _passedRegs;
204
+ //! Mask of all preserved registers, per group.
205
+ Support::Array<RegMask, Globals::kNumVirtGroups> _preservedRegs;
206
+
207
+ //! Passed registers' order.
208
+ union RegOrder {
209
+ //! Passed registers, ordered.
210
+ uint8_t id[kMaxRegArgsPerGroup];
211
+ //! Packed IDs in `uint32_t` array.
212
+ uint32_t packed[(kMaxRegArgsPerGroup + 3) / 4];
213
+ };
214
+
215
+ //! Passed registers' order, per register group.
216
+ Support::Array<RegOrder, Globals::kNumVirtGroups> _passedOrder;
217
+
218
+ //! \}
219
+
220
+ //! \name Construction & Destruction
221
+ //! \{
222
+
223
+ //! Initializes this calling convention to the given `ccId` based on the `environment`.
224
+ //!
225
+ //! See \ref CallConvId and \ref Environment for more details.
226
+ ASMJIT_API Error init(CallConvId ccId, const Environment& environment) noexcept;
227
+
228
+ //! Resets this CallConv struct into a defined state.
229
+ //!
230
+ //! It's recommended to reset the \ref CallConv struct in case you would like create a custom calling convention
231
+ //! as it prevents from using an uninitialized data (CallConv doesn't have a constructor that would initialize it,
232
+ //! it's just a struct).
233
+ inline void reset() noexcept {
234
+ memset(this, 0, sizeof(*this));
235
+ memset(_passedOrder.data(), 0xFF, sizeof(_passedOrder));
236
+ }
237
+
238
+ //! \}
239
+
240
+ //! \name Accessors
241
+ //! \{
242
+
243
+ //! Returns the target architecture of this calling convention.
244
+ inline Arch arch() const noexcept { return _arch; }
245
+ //! Sets the target architecture of this calling convention.
246
+ inline void setArch(Arch arch) noexcept { _arch = arch; }
247
+
248
+ //! Returns the calling convention id.
249
+ inline CallConvId id() const noexcept { return _id; }
250
+ //! Sets the calling convention id.
251
+ inline void setId(CallConvId ccId) noexcept { _id = ccId; }
252
+
253
+ //! Returns the strategy used to assign registers to arguments.
254
+ inline CallConvStrategy strategy() const noexcept { return _strategy; }
255
+ //! Sets the strategy used to assign registers to arguments.
256
+ inline void setStrategy(CallConvStrategy ccStrategy) noexcept { _strategy = ccStrategy; }
257
+
258
+ //! Tests whether the calling convention has the given `flag` set.
259
+ inline bool hasFlag(CallConvFlags flag) const noexcept { return Support::test(_flags, flag); }
260
+ //! Returns the calling convention flags, see `Flags`.
261
+ inline CallConvFlags flags() const noexcept { return _flags; }
262
+ //! Adds the calling convention flags, see `Flags`.
263
+ inline void setFlags(CallConvFlags flag) noexcept { _flags = flag; };
264
+ //! Adds the calling convention flags, see `Flags`.
265
+ inline void addFlags(CallConvFlags flags) noexcept { _flags |= flags; };
266
+
267
+ //! Tests whether this calling convention specifies 'RedZone'.
268
+ inline bool hasRedZone() const noexcept { return _redZoneSize != 0; }
269
+ //! Tests whether this calling convention specifies 'SpillZone'.
270
+ inline bool hasSpillZone() const noexcept { return _spillZoneSize != 0; }
271
+
272
+ //! Returns size of 'RedZone'.
273
+ inline uint32_t redZoneSize() const noexcept { return _redZoneSize; }
274
+ //! Returns size of 'SpillZone'.
275
+ inline uint32_t spillZoneSize() const noexcept { return _spillZoneSize; }
276
+
277
+ //! Sets size of 'RedZone'.
278
+ inline void setRedZoneSize(uint32_t size) noexcept { _redZoneSize = uint8_t(size); }
279
+ //! Sets size of 'SpillZone'.
280
+ inline void setSpillZoneSize(uint32_t size) noexcept { _spillZoneSize = uint8_t(size); }
281
+
282
+ //! Returns a natural stack alignment.
283
+ inline uint32_t naturalStackAlignment() const noexcept { return _naturalStackAlignment; }
284
+ //! Sets a natural stack alignment.
285
+ //!
286
+ //! This function can be used to override the default stack alignment in case that you know that it's alignment is
287
+ //! different. For example it allows to implement custom calling conventions that guarantee higher stack alignment.
288
+ inline void setNaturalStackAlignment(uint32_t value) noexcept { _naturalStackAlignment = uint8_t(value); }
289
+
290
+ //! Returns the size of a register (or its part) to be saved and restored of the given `group`.
291
+ inline uint32_t saveRestoreRegSize(RegGroup group) const noexcept { return _saveRestoreRegSize[group]; }
292
+ //! Sets the size of a vector register (or its part) to be saved and restored.
293
+ inline void setSaveRestoreRegSize(RegGroup group, uint32_t size) noexcept { _saveRestoreRegSize[group] = uint8_t(size); }
294
+
295
+ //! Returns the alignment of a save-restore area of the given `group`.
296
+ inline uint32_t saveRestoreAlignment(RegGroup group) const noexcept { return _saveRestoreAlignment[group]; }
297
+ //! Sets the alignment of a save-restore area of the given `group`.
298
+ inline void setSaveRestoreAlignment(RegGroup group, uint32_t alignment) noexcept { _saveRestoreAlignment[group] = uint8_t(alignment); }
299
+
300
+ //! Returns the order of passed registers of the given `group`.
301
+ inline const uint8_t* passedOrder(RegGroup group) const noexcept {
302
+ ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
303
+ return _passedOrder[size_t(group)].id;
304
+ }
305
+
306
+ //! Returns the mask of passed registers of the given `group`.
307
+ inline RegMask passedRegs(RegGroup group) const noexcept {
308
+ ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
309
+ return _passedRegs[size_t(group)];
310
+ }
311
+
312
+ inline void _setPassedPacked(RegGroup group, uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3) noexcept {
313
+ ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
314
+
315
+ _passedOrder[group].packed[0] = p0;
316
+ _passedOrder[group].packed[1] = p1;
317
+ _passedOrder[group].packed[2] = p2;
318
+ _passedOrder[group].packed[3] = p3;
319
+ }
320
+
321
+ //! Resets the order and mask of passed registers.
322
+ inline void setPassedToNone(RegGroup group) noexcept {
323
+ ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
324
+
325
+ _setPassedPacked(group, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu);
326
+ _passedRegs[size_t(group)] = 0u;
327
+ }
328
+
329
+ //! Sets the order and mask of passed registers.
330
+ inline void setPassedOrder(RegGroup group, uint32_t a0, uint32_t a1 = 0xFF, uint32_t a2 = 0xFF, uint32_t a3 = 0xFF, uint32_t a4 = 0xFF, uint32_t a5 = 0xFF, uint32_t a6 = 0xFF, uint32_t a7 = 0xFF) noexcept {
331
+ ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
332
+
333
+ // NOTE: This should always be called with all arguments known at compile time, so even if it looks scary it
334
+ // should be translated into few instructions.
335
+ _setPassedPacked(group, Support::bytepack32_4x8(a0, a1, a2, a3),
336
+ Support::bytepack32_4x8(a4, a5, a6, a7),
337
+ 0xFFFFFFFFu,
338
+ 0xFFFFFFFFu);
339
+
340
+ _passedRegs[group] = (a0 != 0xFF ? 1u << a0 : 0u) |
341
+ (a1 != 0xFF ? 1u << a1 : 0u) |
342
+ (a2 != 0xFF ? 1u << a2 : 0u) |
343
+ (a3 != 0xFF ? 1u << a3 : 0u) |
344
+ (a4 != 0xFF ? 1u << a4 : 0u) |
345
+ (a5 != 0xFF ? 1u << a5 : 0u) |
346
+ (a6 != 0xFF ? 1u << a6 : 0u) |
347
+ (a7 != 0xFF ? 1u << a7 : 0u) ;
348
+ }
349
+
350
+ //! Returns preserved register mask of the given `group`.
351
+ inline RegMask preservedRegs(RegGroup group) const noexcept {
352
+ ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
353
+ return _preservedRegs[group];
354
+ }
355
+
356
+ //! Sets preserved register mask of the given `group`.
357
+ inline void setPreservedRegs(RegGroup group, RegMask regs) noexcept {
358
+ ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
359
+ _preservedRegs[group] = regs;
360
+ }
361
+
362
+ //! \}
363
+ };
364
+
365
+ //! Function signature.
366
+ //!
367
+ //! Contains information about function return type, count of arguments and their TypeIds. Function signature is
368
+ //! a low level structure which doesn't contain platform specific or calling convention specific information.
369
+ struct FuncSignature {
370
+ //! \name Constants
371
+ //! \{
372
+
373
+ enum : uint8_t {
374
+ //! Doesn't have variable number of arguments (`...`).
375
+ kNoVarArgs = 0xFFu
376
+ };
377
+
378
+ //! \}
379
+
380
+ //! \name Members
381
+ //! \{
382
+
383
+ //! Calling convention id.
384
+ CallConvId _ccId;
385
+ //! Count of arguments.
386
+ uint8_t _argCount;
387
+ //! Index of a first VA or `kNoVarArgs`.
388
+ uint8_t _vaIndex;
389
+ //! Return value TypeId.
390
+ TypeId _ret;
391
+ //! Function arguments TypeIds.
392
+ const TypeId* _args;
393
+
394
+ //! \}
395
+
396
+ //! \name Initializtion & Reset
397
+ //! \{
398
+
399
+ //! Initializes the function signature.
400
+ inline void init(CallConvId ccId, uint32_t vaIndex, TypeId ret, const TypeId* args, uint32_t argCount) noexcept {
401
+ ASMJIT_ASSERT(argCount <= 0xFF);
402
+
403
+ _ccId = ccId;
404
+ _argCount = uint8_t(argCount);
405
+ _vaIndex = uint8_t(vaIndex);
406
+ _ret = ret;
407
+ _args = args;
408
+ }
409
+
410
+ inline void reset() noexcept { memset(this, 0, sizeof(*this)); }
411
+
412
+ //! \}
413
+
414
+ //! \name Accessors
415
+ //! \{
416
+
417
+ //! Returns the calling convention.
418
+ inline CallConvId callConvId() const noexcept { return _ccId; }
419
+ //! Sets the calling convention to `ccId`;
420
+ inline void setCallConvId(CallConvId ccId) noexcept { _ccId = ccId; }
421
+
422
+ //! Tests whether the function has variable number of arguments (...).
423
+ inline bool hasVarArgs() const noexcept { return _vaIndex != kNoVarArgs; }
424
+ //! Returns the variable arguments (...) index, `kNoVarArgs` if none.
425
+ inline uint32_t vaIndex() const noexcept { return _vaIndex; }
426
+ //! Sets the variable arguments (...) index to `index`.
427
+ inline void setVaIndex(uint32_t index) noexcept { _vaIndex = uint8_t(index); }
428
+ //! Resets the variable arguments index (making it a non-va function).
429
+ inline void resetVaIndex() noexcept { _vaIndex = kNoVarArgs; }
430
+
431
+ //! Returns the number of function arguments.
432
+ inline uint32_t argCount() const noexcept { return _argCount; }
433
+
434
+ inline bool hasRet() const noexcept { return _ret != TypeId::kVoid; }
435
+ //! Returns the return value type.
436
+ inline TypeId ret() const noexcept { return _ret; }
437
+
438
+ //! Returns the type of the argument at index `i`.
439
+ inline TypeId arg(uint32_t i) const noexcept {
440
+ ASMJIT_ASSERT(i < _argCount);
441
+ return _args[i];
442
+ }
443
+ //! Returns the array of function arguments' types.
444
+ inline const TypeId* args() const noexcept { return _args; }
445
+
446
+ //! \}
447
+ };
448
+
449
+ template<typename... RET_ARGS>
450
+ class FuncSignatureT : public FuncSignature {
451
+ public:
452
+ inline FuncSignatureT(CallConvId ccId = CallConvId::kHost, uint32_t vaIndex = kNoVarArgs) noexcept {
453
+ static constexpr TypeId ret_args[] = { (TypeId(TypeUtils::TypeIdOfT<RET_ARGS>::kTypeId))... };
454
+ init(ccId, vaIndex, ret_args[0], ret_args + 1, uint32_t(ASMJIT_ARRAY_SIZE(ret_args) - 1));
455
+ }
456
+ };
457
+
458
+ //! Function signature builder.
459
+ class FuncSignatureBuilder : public FuncSignature {
460
+ public:
461
+ TypeId _builderArgList[Globals::kMaxFuncArgs];
462
+
463
+ //! \name Initializtion & Reset
464
+ //! \{
465
+
466
+ inline FuncSignatureBuilder(CallConvId ccId = CallConvId::kHost, uint32_t vaIndex = kNoVarArgs) noexcept {
467
+ init(ccId, vaIndex, TypeId::kVoid, _builderArgList, 0);
468
+ }
469
+
470
+ //! \}
471
+
472
+ //! \name Accessors
473
+ //! \{
474
+
475
+ //! Sets the return type to `retType`.
476
+ inline void setRet(TypeId retType) noexcept { _ret = retType; }
477
+ //! Sets the return type based on `T`.
478
+ template<typename T>
479
+ inline void setRetT() noexcept { setRet(TypeId(TypeUtils::TypeIdOfT<T>::kTypeId)); }
480
+
481
+ //! Sets the argument at index `index` to `argType`.
482
+ inline void setArg(uint32_t index, TypeId argType) noexcept {
483
+ ASMJIT_ASSERT(index < _argCount);
484
+ _builderArgList[index] = argType;
485
+ }
486
+ //! Sets the argument at index `i` to the type based on `T`.
487
+ template<typename T>
488
+ inline void setArgT(uint32_t index) noexcept { setArg(index, TypeId(TypeUtils::TypeIdOfT<T>::kTypeId)); }
489
+
490
+ //! Appends an argument of `type` to the function prototype.
491
+ inline void addArg(TypeId type) noexcept {
492
+ ASMJIT_ASSERT(_argCount < Globals::kMaxFuncArgs);
493
+ _builderArgList[_argCount++] = type;
494
+ }
495
+ //! Appends an argument of type based on `T` to the function prototype.
496
+ template<typename T>
497
+ inline void addArgT() noexcept { addArg(TypeId(TypeUtils::TypeIdOfT<T>::kTypeId)); }
498
+
499
+ //! \}
500
+ };
501
+
502
+ //! Argument or return value (or its part) as defined by `FuncSignature`, but with register or stack address
503
+ //! (and other metadata) assigned.
504
+ struct FuncValue {
505
+ //! \name Constants
506
+ //! \{
507
+
508
+ enum Bits : uint32_t {
509
+ kTypeIdShift = 0, //!< TypeId shift.
510
+ kTypeIdMask = 0x000000FFu, //!< TypeId mask.
511
+
512
+ kFlagIsReg = 0x00000100u, //!< Passed by register.
513
+ kFlagIsStack = 0x00000200u, //!< Passed by stack.
514
+ kFlagIsIndirect = 0x00000400u, //!< Passed indirectly by reference (internally a pointer).
515
+ kFlagIsDone = 0x00000800u, //!< Used internally by arguments allocator.
516
+
517
+ kStackOffsetShift = 12, //!< Stack offset shift.
518
+ kStackOffsetMask = 0xFFFFF000u, //!< Stack offset mask (must occupy MSB bits).
519
+
520
+ kRegIdShift = 16, //!< RegId shift.
521
+ kRegIdMask = 0x00FF0000u, //!< RegId mask.
522
+
523
+ kRegTypeShift = 24, //!< RegType shift.
524
+ kRegTypeMask = 0xFF000000u //!< RegType mask.
525
+ };
526
+
527
+ //! \}
528
+
529
+ //! \name Members
530
+ //! \{
531
+
532
+ uint32_t _data;
533
+
534
+ //! \}
535
+
536
+ //! \name Initializtion & Reset
537
+ //!
538
+ //! These initialize the whole `FuncValue` to either register or stack. Useful when you know all of these
539
+ //! properties and wanna just set it up.
540
+ //!
541
+ //! \{
542
+
543
+ //! Initializes the `typeId` of this `FuncValue`.
544
+ inline void initTypeId(TypeId typeId) noexcept {
545
+ _data = uint32_t(typeId) << kTypeIdShift;
546
+ }
547
+
548
+ inline void initReg(RegType regType, uint32_t regId, TypeId typeId, uint32_t flags = 0) noexcept {
549
+ _data = (uint32_t(regType) << kRegTypeShift) | (regId << kRegIdShift) | (uint32_t(typeId) << kTypeIdShift) | kFlagIsReg | flags;
550
+ }
551
+
552
+ inline void initStack(int32_t offset, TypeId typeId) noexcept {
553
+ _data = (uint32_t(offset) << kStackOffsetShift) | (uint32_t(typeId) << kTypeIdShift) | kFlagIsStack;
554
+ }
555
+
556
+ //! Resets the value to its unassigned state.
557
+ inline void reset() noexcept { _data = 0; }
558
+
559
+ //! \}
560
+
561
+ //! \name Assign
562
+ //!
563
+ //! These initialize only part of `FuncValue`, useful when building `FuncValue` incrementally. The caller
564
+ //! should first init the type-id by caliing `initTypeId` and then continue building either register or stack.
565
+ //!
566
+ //! \{
567
+
568
+ inline void assignRegData(RegType regType, uint32_t regId) noexcept {
569
+ ASMJIT_ASSERT((_data & (kRegTypeMask | kRegIdMask)) == 0);
570
+ _data |= (uint32_t(regType) << kRegTypeShift) | (regId << kRegIdShift) | kFlagIsReg;
571
+ }
572
+
573
+ inline void assignStackOffset(int32_t offset) noexcept {
574
+ ASMJIT_ASSERT((_data & kStackOffsetMask) == 0);
575
+ _data |= (uint32_t(offset) << kStackOffsetShift) | kFlagIsStack;
576
+ }
577
+
578
+ //! \}
579
+
580
+ //! \name Accessors
581
+ //! \{
582
+
583
+ //! Returns true if the value is initialized (explicit bool cast).
584
+ inline explicit operator bool() const noexcept { return _data != 0; }
585
+
586
+ inline void _replaceValue(uint32_t mask, uint32_t value) noexcept { _data = (_data & ~mask) | value; }
587
+
588
+ //! Tests whether the `FuncValue` has a flag `flag` set.
589
+ inline bool hasFlag(uint32_t flag) const noexcept { return Support::test(_data, flag); }
590
+ //! Adds `flags` to `FuncValue`.
591
+ inline void addFlags(uint32_t flags) noexcept { _data |= flags; }
592
+ //! Clears `flags` of `FuncValue`.
593
+ inline void clearFlags(uint32_t flags) noexcept { _data &= ~flags; }
594
+
595
+ //! Tests whether the value is initialized (i.e. contains a valid data).
596
+ inline bool isInitialized() const noexcept { return _data != 0; }
597
+ //! Tests whether the argument is passed by register.
598
+ inline bool isReg() const noexcept { return hasFlag(kFlagIsReg); }
599
+ //! Tests whether the argument is passed by stack.
600
+ inline bool isStack() const noexcept { return hasFlag(kFlagIsStack); }
601
+ //! Tests whether the argument is passed by register.
602
+ inline bool isAssigned() const noexcept { return hasFlag(kFlagIsReg | kFlagIsStack); }
603
+ //! Tests whether the argument is passed through a pointer (used by WIN64 to pass XMM|YMM|ZMM).
604
+ inline bool isIndirect() const noexcept { return hasFlag(kFlagIsIndirect); }
605
+
606
+ //! Tests whether the argument was already processed (used internally).
607
+ inline bool isDone() const noexcept { return hasFlag(kFlagIsDone); }
608
+
609
+ //! Returns a register type of the register used to pass function argument or return value.
610
+ inline RegType regType() const noexcept { return RegType((_data & kRegTypeMask) >> kRegTypeShift); }
611
+ //! Sets a register type of the register used to pass function argument or return value.
612
+ inline void setRegType(RegType regType) noexcept { _replaceValue(kRegTypeMask, uint32_t(regType) << kRegTypeShift); }
613
+
614
+ //! Returns a physical id of the register used to pass function argument or return value.
615
+ inline uint32_t regId() const noexcept { return (_data & kRegIdMask) >> kRegIdShift; }
616
+ //! Sets a physical id of the register used to pass function argument or return value.
617
+ inline void setRegId(uint32_t regId) noexcept { _replaceValue(kRegIdMask, regId << kRegIdShift); }
618
+
619
+ //! Returns a stack offset of this argument.
620
+ inline int32_t stackOffset() const noexcept { return int32_t(_data & kStackOffsetMask) >> kStackOffsetShift; }
621
+ //! Sets a stack offset of this argument.
622
+ inline void setStackOffset(int32_t offset) noexcept { _replaceValue(kStackOffsetMask, uint32_t(offset) << kStackOffsetShift); }
623
+
624
+ //! Tests whether the argument or return value has associated `TypeId`.
625
+ inline bool hasTypeId() const noexcept { return Support::test(_data, kTypeIdMask); }
626
+ //! Returns a TypeId of this argument or return value.
627
+ inline TypeId typeId() const noexcept { return TypeId((_data & kTypeIdMask) >> kTypeIdShift); }
628
+ //! Sets a TypeId of this argument or return value.
629
+ inline void setTypeId(TypeId typeId) noexcept { _replaceValue(kTypeIdMask, uint32_t(typeId) << kTypeIdShift); }
630
+
631
+ //! \}
632
+ };
633
+
634
+ //! Contains multiple `FuncValue` instances in an array so functions that use multiple registers for arguments or
635
+ //! return values can represent all inputs and outputs.
636
+ struct FuncValuePack {
637
+ public:
638
+ //! \name Members
639
+ //! \{
640
+
641
+ //! Values of the pack.
642
+ FuncValue _values[Globals::kMaxValuePack];
643
+
644
+ //! \}
645
+
646
+ //! \name Initialization & Reset
647
+ //! \{
648
+
649
+ //! Resets all values in the pack.
650
+ inline void reset() noexcept {
651
+ for (size_t i = 0; i < Globals::kMaxValuePack; i++)
652
+ _values[i].reset();
653
+ }
654
+
655
+ //! \}
656
+
657
+ //! \name Accessors
658
+ //! \{
659
+
660
+ //! Calculates how many values are in the pack, checking for non-values from the end.
661
+ inline uint32_t count() const noexcept {
662
+ uint32_t n = Globals::kMaxValuePack;
663
+ while (n && !_values[n - 1])
664
+ n--;
665
+ return n;
666
+ }
667
+
668
+ inline FuncValue* values() noexcept { return _values; }
669
+ inline const FuncValue* values() const noexcept { return _values; }
670
+
671
+ inline void resetValue(size_t index) noexcept {
672
+ ASMJIT_ASSERT(index < Globals::kMaxValuePack);
673
+ _values[index].reset();
674
+ }
675
+
676
+ inline bool hasValue(size_t index) noexcept {
677
+ ASMJIT_ASSERT(index < Globals::kMaxValuePack);
678
+ return _values[index].isInitialized();
679
+ }
680
+
681
+ inline void assignReg(size_t index, const BaseReg& reg, TypeId typeId = TypeId::kVoid) noexcept {
682
+ ASMJIT_ASSERT(index < Globals::kMaxValuePack);
683
+ ASMJIT_ASSERT(reg.isPhysReg());
684
+ _values[index].initReg(reg.type(), reg.id(), typeId);
685
+ }
686
+
687
+ inline void assignReg(size_t index, RegType regType, uint32_t regId, TypeId typeId = TypeId::kVoid) noexcept {
688
+ ASMJIT_ASSERT(index < Globals::kMaxValuePack);
689
+ _values[index].initReg(regType, regId, typeId);
690
+ }
691
+
692
+ inline void assignStack(size_t index, int32_t offset, TypeId typeId = TypeId::kVoid) noexcept {
693
+ ASMJIT_ASSERT(index < Globals::kMaxValuePack);
694
+ _values[index].initStack(offset, typeId);
695
+ }
696
+
697
+ inline FuncValue& operator[](size_t index) {
698
+ ASMJIT_ASSERT(index < Globals::kMaxValuePack);
699
+ return _values[index];
700
+ }
701
+
702
+ inline const FuncValue& operator[](size_t index) const {
703
+ ASMJIT_ASSERT(index < Globals::kMaxValuePack);
704
+ return _values[index];
705
+ }
706
+
707
+ //! \}
708
+ };
709
+
710
+ //! Attributes are designed in a way that all are initially false, and user or \ref FuncFrame finalizer adds
711
+ //! them when necessary.
712
+ enum class FuncAttributes : uint32_t {
713
+ //! No attributes.
714
+ kNoAttributes = 0,
715
+
716
+ //! Function has variable number of arguments.
717
+ kHasVarArgs = 0x00000001u,
718
+ //! Preserve frame pointer (don't omit FP).
719
+ kHasPreservedFP = 0x00000010u,
720
+ //! Function calls other functions (is not leaf).
721
+ kHasFuncCalls = 0x00000020u,
722
+ //! Function has aligned save/restore of vector registers.
723
+ kAlignedVecSR = 0x00000040u,
724
+ //! FuncFrame is finalized and can be used by prolog/epilog inserter (PEI).
725
+ kIsFinalized = 0x00000800u,
726
+
727
+ // X86 Specific Attributes
728
+ // -----------------------
729
+
730
+ //! Enables the use of AVX within the function's body, prolog, and epilog (X86).
731
+ //!
732
+ //! This flag instructs prolog and epilog emitter to use AVX instead of SSE for manipulating XMM registers.
733
+ kX86_AVXEnabled = 0x00010000u,
734
+
735
+ //! Enables the use of AVX-512 within the function's body, prolog, and epilog (X86).
736
+ //!
737
+ //! This flag instructs Compiler register allocator to use additional 16 registers introduced by AVX-512.
738
+ //! Additionally, if the functions saves full width of ZMM registers (custom calling conventions only) then
739
+ //! the prolog/epilog inserter would use AVX-512 move instructions to emit the save and restore sequence.
740
+ kX86_AVX512Enabled = 0x00020000u,
741
+
742
+ //! This flag instructs the epilog writer to emit EMMS instruction before RET (X86).
743
+ kX86_MMXCleanup = 0x00040000u,
744
+
745
+ //! This flag instructs the epilog writer to emit VZEROUPPER instruction before RET (X86).
746
+ kX86_AVXCleanup = 0x00080000u
747
+ };
748
+ ASMJIT_DEFINE_ENUM_FLAGS(FuncAttributes)
749
+
750
+ //! Function detail - \ref CallConv and expanded \ref FuncSignature.
751
+ //!
752
+ //! Function detail is architecture and OS dependent representation of a function. It contains a materialized
753
+ //! calling convention and expanded function signature so all arguments have assigned either register type/id
754
+ //! or stack address.
755
+ class FuncDetail {
756
+ public:
757
+ //! \name Constants
758
+ //! \{
759
+
760
+ enum : uint8_t {
761
+ //! Doesn't have variable number of arguments (`...`).
762
+ kNoVarArgs = 0xFFu
763
+ };
764
+
765
+ //! \}
766
+
767
+ //! \name Members
768
+ //! \{
769
+
770
+ //! Calling convention.
771
+ CallConv _callConv;
772
+ //! Number of function arguments.
773
+ uint8_t _argCount;
774
+ //! Variable arguments index of `kNoVarArgs`.
775
+ uint8_t _vaIndex;
776
+ //! Reserved for future use.
777
+ uint16_t _reserved;
778
+ //! Registers that contain arguments.
779
+ Support::Array<RegMask, Globals::kNumVirtGroups> _usedRegs;
780
+ //! Size of arguments passed by stack.
781
+ uint32_t _argStackSize;
782
+ //! Function return value(s).
783
+ FuncValuePack _rets;
784
+ //! Function arguments.
785
+ FuncValuePack _args[Globals::kMaxFuncArgs];
786
+
787
+ //! \}
788
+
789
+ //! \name Construction & Destruction
790
+ //! \{
791
+
792
+ inline FuncDetail() noexcept { reset(); }
793
+ inline FuncDetail(const FuncDetail& other) noexcept = default;
794
+
795
+ //! Initializes this `FuncDetail` to the given signature.
796
+ ASMJIT_API Error init(const FuncSignature& signature, const Environment& environment) noexcept;
797
+ inline void reset() noexcept { memset(this, 0, sizeof(*this)); }
798
+
799
+ //! \}
800
+
801
+ //! \name Accessors
802
+ //! \{
803
+
804
+ //! Returns the function's calling convention, see `CallConv`.
805
+ inline const CallConv& callConv() const noexcept { return _callConv; }
806
+
807
+ //! Returns the associated calling convention flags, see `CallConv::Flags`.
808
+ inline CallConvFlags flags() const noexcept { return _callConv.flags(); }
809
+ //! Checks whether a CallConv `flag` is set, see `CallConv::Flags`.
810
+ inline bool hasFlag(CallConvFlags ccFlag) const noexcept { return _callConv.hasFlag(ccFlag); }
811
+
812
+ //! Tests whether the function has a return value.
813
+ inline bool hasRet() const noexcept { return bool(_rets[0]); }
814
+ //! Returns the number of function arguments.
815
+ inline uint32_t argCount() const noexcept { return _argCount; }
816
+
817
+ //! Returns function return values.
818
+ inline FuncValuePack& retPack() noexcept { return _rets; }
819
+ //! Returns function return values.
820
+ inline const FuncValuePack& retPack() const noexcept { return _rets; }
821
+
822
+ //! Returns a function return value associated with the given `valueIndex`.
823
+ inline FuncValue& ret(size_t valueIndex = 0) noexcept { return _rets[valueIndex]; }
824
+ //! Returns a function return value associated with the given `valueIndex` (const).
825
+ inline const FuncValue& ret(size_t valueIndex = 0) const noexcept { return _rets[valueIndex]; }
826
+
827
+ //! Returns function argument packs array.
828
+ inline FuncValuePack* argPacks() noexcept { return _args; }
829
+ //! Returns function argument packs array (const).
830
+ inline const FuncValuePack* argPacks() const noexcept { return _args; }
831
+
832
+ //! Returns function argument pack at the given `argIndex`.
833
+ inline FuncValuePack& argPack(size_t argIndex) noexcept {
834
+ ASMJIT_ASSERT(argIndex < Globals::kMaxFuncArgs);
835
+ return _args[argIndex];
836
+ }
837
+
838
+ //! Returns function argument pack at the given `argIndex` (const).
839
+ inline const FuncValuePack& argPack(size_t argIndex) const noexcept {
840
+ ASMJIT_ASSERT(argIndex < Globals::kMaxFuncArgs);
841
+ return _args[argIndex];
842
+ }
843
+
844
+ //! Returns an argument at `valueIndex` from the argument pack at the given `argIndex`.
845
+ inline FuncValue& arg(size_t argIndex, size_t valueIndex = 0) noexcept {
846
+ ASMJIT_ASSERT(argIndex < Globals::kMaxFuncArgs);
847
+ return _args[argIndex][valueIndex];
848
+ }
849
+
850
+ //! Returns an argument at `valueIndex` from the argument pack at the given `argIndex` (const).
851
+ inline const FuncValue& arg(size_t argIndex, size_t valueIndex = 0) const noexcept {
852
+ ASMJIT_ASSERT(argIndex < Globals::kMaxFuncArgs);
853
+ return _args[argIndex][valueIndex];
854
+ }
855
+
856
+ //! Resets an argument at the given `argIndex`.
857
+ //!
858
+ //! If the argument is a parameter pack (has multiple values) all values are reset.
859
+ inline void resetArg(size_t argIndex) noexcept {
860
+ ASMJIT_ASSERT(argIndex < Globals::kMaxFuncArgs);
861
+ _args[argIndex].reset();
862
+ }
863
+
864
+ //! Tests whether the function has variable arguments.
865
+ inline bool hasVarArgs() const noexcept { return _vaIndex != kNoVarArgs; }
866
+ //! Returns an index of a first variable argument.
867
+ inline uint32_t vaIndex() const noexcept { return _vaIndex; }
868
+
869
+ //! Tests whether the function passes one or more argument by stack.
870
+ inline bool hasStackArgs() const noexcept { return _argStackSize != 0; }
871
+ //! Returns stack size needed for function arguments passed on the stack.
872
+ inline uint32_t argStackSize() const noexcept { return _argStackSize; }
873
+
874
+ //! Returns red zone size.
875
+ inline uint32_t redZoneSize() const noexcept { return _callConv.redZoneSize(); }
876
+ //! Returns spill zone size.
877
+ inline uint32_t spillZoneSize() const noexcept { return _callConv.spillZoneSize(); }
878
+ //! Returns natural stack alignment.
879
+ inline uint32_t naturalStackAlignment() const noexcept { return _callConv.naturalStackAlignment(); }
880
+
881
+ //! Returns a mask of all passed registers of the given register `group`.
882
+ inline RegMask passedRegs(RegGroup group) const noexcept { return _callConv.passedRegs(group); }
883
+ //! Returns a mask of all preserved registers of the given register `group`.
884
+ inline RegMask preservedRegs(RegGroup group) const noexcept { return _callConv.preservedRegs(group); }
885
+
886
+ //! Returns a mask of all used registers of the given register `group`.
887
+ inline RegMask usedRegs(RegGroup group) const noexcept {
888
+ ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
889
+ return _usedRegs[size_t(group)];
890
+ }
891
+
892
+ //! Adds `regs` to the mask of used registers of the given register `group`.
893
+ inline void addUsedRegs(RegGroup group, RegMask regs) noexcept {
894
+ ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
895
+ _usedRegs[size_t(group)] |= regs;
896
+ }
897
+
898
+ //! \}
899
+ };
900
+
901
+ //! Function frame.
902
+ //!
903
+ //! Function frame is used directly by prolog and epilog insertion (PEI) utils. It provides information necessary to
904
+ //! insert a proper and ABI comforming prolog and epilog. Function frame calculation is based on `CallConv` and
905
+ //! other function attributes.
906
+ //!
907
+ //! SSE vs AVX vs AVX-512
908
+ //! ---------------------
909
+ //!
910
+ //! Function frame provides a way to tell prolog/epilog inserter to use AVX instructions instead of SSE. Use
911
+ //! `setAvxEnabled()` and `setAvx512Enabled()` to enable AVX and/or AVX-512, respectively. Enabling AVX-512
912
+ //! is mostly for Compiler as it would use 32 SIMD registers instead of 16 when enabled.
913
+ //!
914
+ //! \note If your code uses AVX instructions and AVX is not enabled there would be a performance hit in case that
915
+ //! some registers had to be saved/restored in function's prolog/epilog, respectively. Thus, it's recommended to
916
+ //! always let the function frame know about the use of AVX.
917
+ //!
918
+ //! Function Frame Structure
919
+ //! ------------------------
920
+ //!
921
+ //! Various properties can contribute to the size and structure of the function frame. The function frame in most
922
+ //! cases won't use all of the properties illustrated (for example Spill Zone and Red Zone are never used together).
923
+ //!
924
+ //! ```
925
+ //! +-----------------------------+
926
+ //! | Arguments Passed by Stack |
927
+ //! +-----------------------------+
928
+ //! | Spill Zone |
929
+ //! +-----------------------------+ <- Stack offset (args) starts from here.
930
+ //! | Return Address, if Pushed |
931
+ //! +-----------------------------+ <- Stack pointer (SP) upon entry.
932
+ //! | Save/Restore Stack. |
933
+ //! +-----------------------------+-----------------------------+
934
+ //! | Local Stack | |
935
+ //! +-----------------------------+ Final Stack |
936
+ //! | Call Stack | |
937
+ //! +-----------------------------+-----------------------------+ <- SP after prolog.
938
+ //! | Red Zone |
939
+ //! +-----------------------------+
940
+ //! ```
941
+ class FuncFrame {
942
+ public:
943
+ //! \name Constants
944
+ //! \{
945
+
946
+ enum : uint32_t {
947
+ //! Tag used to inform that some offset is invalid.
948
+ kTagInvalidOffset = 0xFFFFFFFFu
949
+ };
950
+
951
+ //! \}
952
+
953
+ //! \name Members
954
+ //! \{
955
+
956
+ //! Function attributes.
957
+ FuncAttributes _attributes;
958
+
959
+ //! Target architecture.
960
+ Arch _arch;
961
+ //! SP register ID (to access call stack and local stack).
962
+ uint8_t _spRegId;
963
+ //! SA register ID (to access stack arguments).
964
+ uint8_t _saRegId;
965
+
966
+ //! Red zone size (copied from CallConv).
967
+ uint8_t _redZoneSize;
968
+ //! Spill zone size (copied from CallConv).
969
+ uint8_t _spillZoneSize;
970
+ //! Natural stack alignment (copied from CallConv).
971
+ uint8_t _naturalStackAlignment;
972
+ //! Minimum stack alignment to turn on dynamic alignment.
973
+ uint8_t _minDynamicAlignment;
974
+
975
+ //! Call stack alignment.
976
+ uint8_t _callStackAlignment;
977
+ //! Local stack alignment.
978
+ uint8_t _localStackAlignment;
979
+ //! Final stack alignment.
980
+ uint8_t _finalStackAlignment;
981
+
982
+ //! Adjustment of the stack before returning (X86-STDCALL).
983
+ uint16_t _calleeStackCleanup;
984
+
985
+ //! Call stack size.
986
+ uint32_t _callStackSize;
987
+ //! Local stack size.
988
+ uint32_t _localStackSize;
989
+ //! Final stack size (sum of call stack and local stack).
990
+ uint32_t _finalStackSize;
991
+
992
+ //! Local stack offset (non-zero only if call stack is used).
993
+ uint32_t _localStackOffset;
994
+ //! Offset relative to SP that contains previous SP (before alignment).
995
+ uint32_t _daOffset;
996
+ //! Offset of the first stack argument relative to SP.
997
+ uint32_t _saOffsetFromSP;
998
+ //! Offset of the first stack argument relative to SA (_saRegId or FP).
999
+ uint32_t _saOffsetFromSA;
1000
+
1001
+ //! Local stack adjustment in prolog/epilog.
1002
+ uint32_t _stackAdjustment;
1003
+
1004
+ //! Registers that are dirty.
1005
+ Support::Array<RegMask, Globals::kNumVirtGroups> _dirtyRegs;
1006
+ //! Registers that must be preserved (copied from CallConv).
1007
+ Support::Array<RegMask, Globals::kNumVirtGroups> _preservedRegs;
1008
+ //! Size to save/restore per register group.
1009
+ Support::Array<uint8_t, Globals::kNumVirtGroups> _saveRestoreRegSize;
1010
+ //! Alignment of save/restore area per register group.
1011
+ Support::Array<uint8_t, Globals::kNumVirtGroups> _saveRestoreAlignment;
1012
+
1013
+ //! Stack size required to save registers with push/pop.
1014
+ uint16_t _pushPopSaveSize;
1015
+ //! Stack size required to save extra registers that cannot use push/pop.
1016
+ uint16_t _extraRegSaveSize;
1017
+ //! Offset where registers saved/restored via push/pop are stored
1018
+ uint32_t _pushPopSaveOffset;
1019
+ //! Offset where extra ragisters that cannot use push/pop are stored.
1020
+ uint32_t _extraRegSaveOffset;
1021
+
1022
+ //! \}
1023
+
1024
+ //! \name Construction & Destruction
1025
+ //! \{
1026
+
1027
+ inline FuncFrame() noexcept { reset(); }
1028
+ inline FuncFrame(const FuncFrame& other) noexcept = default;
1029
+
1030
+ ASMJIT_API Error init(const FuncDetail& func) noexcept;
1031
+
1032
+ inline void reset() noexcept {
1033
+ memset(this, 0, sizeof(FuncFrame));
1034
+ _spRegId = BaseReg::kIdBad;
1035
+ _saRegId = BaseReg::kIdBad;
1036
+ _daOffset = kTagInvalidOffset;
1037
+ }
1038
+
1039
+ //! \}
1040
+
1041
+ //! \name Accessors
1042
+ //! \{
1043
+
1044
+ //! Returns the target architecture of the function frame.
1045
+ inline Arch arch() const noexcept { return _arch; }
1046
+
1047
+ //! Returns function frame attributes, see `Attributes`.
1048
+ inline FuncAttributes attributes() const noexcept { return _attributes; }
1049
+ //! Checks whether the FuncFame contains an attribute `attr`.
1050
+ inline bool hasAttribute(FuncAttributes attr) const noexcept { return Support::test(_attributes, attr); }
1051
+ //! Adds attributes `attrs` to the FuncFrame.
1052
+ inline void addAttributes(FuncAttributes attrs) noexcept { _attributes |= attrs; }
1053
+ //! Clears attributes `attrs` from the FrameFrame.
1054
+ inline void clearAttributes(FuncAttributes attrs) noexcept { _attributes &= ~attrs; }
1055
+
1056
+ //! Tests whether the function has variable number of arguments.
1057
+ inline bool hasVarArgs() const noexcept { return hasAttribute(FuncAttributes::kHasVarArgs); }
1058
+ //! Sets the variable arguments flag.
1059
+ inline void setVarArgs() noexcept { addAttributes(FuncAttributes::kHasVarArgs); }
1060
+ //! Resets variable arguments flag.
1061
+ inline void resetVarArgs() noexcept { clearAttributes(FuncAttributes::kHasVarArgs); }
1062
+
1063
+ //! Tests whether the function preserves frame pointer (EBP|ESP on X86).
1064
+ inline bool hasPreservedFP() const noexcept { return hasAttribute(FuncAttributes::kHasPreservedFP); }
1065
+ //! Enables preserved frame pointer.
1066
+ inline void setPreservedFP() noexcept { addAttributes(FuncAttributes::kHasPreservedFP); }
1067
+ //! Disables preserved frame pointer.
1068
+ inline void resetPreservedFP() noexcept { clearAttributes(FuncAttributes::kHasPreservedFP); }
1069
+
1070
+ //! Tests whether the function calls other functions.
1071
+ inline bool hasFuncCalls() const noexcept { return hasAttribute(FuncAttributes::kHasFuncCalls); }
1072
+ //! Sets `kFlagHasCalls` to true.
1073
+ inline void setFuncCalls() noexcept { addAttributes(FuncAttributes::kHasFuncCalls); }
1074
+ //! Sets `kFlagHasCalls` to false.
1075
+ inline void resetFuncCalls() noexcept { clearAttributes(FuncAttributes::kHasFuncCalls); }
1076
+
1077
+ //! Tests whether the function has AVX enabled.
1078
+ inline bool isAvxEnabled() const noexcept { return hasAttribute(FuncAttributes::kX86_AVXEnabled); }
1079
+ //! Enables AVX use.
1080
+ inline void setAvxEnabled() noexcept { addAttributes(FuncAttributes::kX86_AVXEnabled); }
1081
+ //! Disables AVX use.
1082
+ inline void resetAvxEnabled() noexcept { clearAttributes(FuncAttributes::kX86_AVXEnabled); }
1083
+
1084
+ //! Tests whether the function has AVX-512 enabled.
1085
+ inline bool isAvx512Enabled() const noexcept { return hasAttribute(FuncAttributes::kX86_AVX512Enabled); }
1086
+ //! Enables AVX-512 use.
1087
+ inline void setAvx512Enabled() noexcept { addAttributes(FuncAttributes::kX86_AVX512Enabled); }
1088
+ //! Disables AVX-512 use.
1089
+ inline void resetAvx512Enabled() noexcept { clearAttributes(FuncAttributes::kX86_AVX512Enabled); }
1090
+
1091
+ //! Tests whether the function has MMX cleanup - 'emms' instruction in epilog.
1092
+ inline bool hasMmxCleanup() const noexcept { return hasAttribute(FuncAttributes::kX86_MMXCleanup); }
1093
+ //! Enables MMX cleanup.
1094
+ inline void setMmxCleanup() noexcept { addAttributes(FuncAttributes::kX86_MMXCleanup); }
1095
+ //! Disables MMX cleanup.
1096
+ inline void resetMmxCleanup() noexcept { clearAttributes(FuncAttributes::kX86_MMXCleanup); }
1097
+
1098
+ //! Tests whether the function has AVX cleanup - 'vzeroupper' instruction in epilog.
1099
+ inline bool hasAvxCleanup() const noexcept { return hasAttribute(FuncAttributes::kX86_AVXCleanup); }
1100
+ //! Enables AVX cleanup.
1101
+ inline void setAvxCleanup() noexcept { addAttributes(FuncAttributes::kX86_AVXCleanup); }
1102
+ //! Disables AVX cleanup.
1103
+ inline void resetAvxCleanup() noexcept { clearAttributes(FuncAttributes::kX86_AVXCleanup); }
1104
+
1105
+ //! Tests whether the function uses call stack.
1106
+ inline bool hasCallStack() const noexcept { return _callStackSize != 0; }
1107
+ //! Tests whether the function uses local stack.
1108
+ inline bool hasLocalStack() const noexcept { return _localStackSize != 0; }
1109
+ //! Tests whether vector registers can be saved and restored by using aligned reads and writes.
1110
+ inline bool hasAlignedVecSR() const noexcept { return hasAttribute(FuncAttributes::kAlignedVecSR); }
1111
+ //! Tests whether the function has to align stack dynamically.
1112
+ inline bool hasDynamicAlignment() const noexcept { return _finalStackAlignment >= _minDynamicAlignment; }
1113
+
1114
+ //! Tests whether the calling convention specifies 'RedZone'.
1115
+ inline bool hasRedZone() const noexcept { return _redZoneSize != 0; }
1116
+ //! Tests whether the calling convention specifies 'SpillZone'.
1117
+ inline bool hasSpillZone() const noexcept { return _spillZoneSize != 0; }
1118
+
1119
+ //! Returns the size of 'RedZone'.
1120
+ inline uint32_t redZoneSize() const noexcept { return _redZoneSize; }
1121
+ //! Returns the size of 'SpillZone'.
1122
+ inline uint32_t spillZoneSize() const noexcept { return _spillZoneSize; }
1123
+ //! Returns natural stack alignment (guaranteed stack alignment upon entry).
1124
+ inline uint32_t naturalStackAlignment() const noexcept { return _naturalStackAlignment; }
1125
+ //! Returns natural stack alignment (guaranteed stack alignment upon entry).
1126
+ inline uint32_t minDynamicAlignment() const noexcept { return _minDynamicAlignment; }
1127
+
1128
+ //! Tests whether the callee must adjust SP before returning (X86-STDCALL only)
1129
+ inline bool hasCalleeStackCleanup() const noexcept { return _calleeStackCleanup != 0; }
1130
+ //! Returns home many bytes of the stack the callee must adjust before returning (X86-STDCALL only)
1131
+ inline uint32_t calleeStackCleanup() const noexcept { return _calleeStackCleanup; }
1132
+
1133
+ //! Returns call stack alignment.
1134
+ inline uint32_t callStackAlignment() const noexcept { return _callStackAlignment; }
1135
+ //! Returns local stack alignment.
1136
+ inline uint32_t localStackAlignment() const noexcept { return _localStackAlignment; }
1137
+ //! Returns final stack alignment (the maximum value of call, local, and natural stack alignments).
1138
+ inline uint32_t finalStackAlignment() const noexcept { return _finalStackAlignment; }
1139
+
1140
+ //! Sets call stack alignment.
1141
+ //!
1142
+ //! \note This also updates the final stack alignment.
1143
+ inline void setCallStackAlignment(uint32_t alignment) noexcept {
1144
+ _callStackAlignment = uint8_t(alignment);
1145
+ _finalStackAlignment = Support::max(_naturalStackAlignment, _callStackAlignment, _localStackAlignment);
1146
+ }
1147
+
1148
+ //! Sets local stack alignment.
1149
+ //!
1150
+ //! \note This also updates the final stack alignment.
1151
+ inline void setLocalStackAlignment(uint32_t value) noexcept {
1152
+ _localStackAlignment = uint8_t(value);
1153
+ _finalStackAlignment = Support::max(_naturalStackAlignment, _callStackAlignment, _localStackAlignment);
1154
+ }
1155
+
1156
+ //! Combines call stack alignment with `alignment`, updating it to the greater value.
1157
+ //!
1158
+ //! \note This also updates the final stack alignment.
1159
+ inline void updateCallStackAlignment(uint32_t alignment) noexcept {
1160
+ _callStackAlignment = uint8_t(Support::max<uint32_t>(_callStackAlignment, alignment));
1161
+ _finalStackAlignment = Support::max(_finalStackAlignment, _callStackAlignment);
1162
+ }
1163
+
1164
+ //! Combines local stack alignment with `alignment`, updating it to the greater value.
1165
+ //!
1166
+ //! \note This also updates the final stack alignment.
1167
+ inline void updateLocalStackAlignment(uint32_t alignment) noexcept {
1168
+ _localStackAlignment = uint8_t(Support::max<uint32_t>(_localStackAlignment, alignment));
1169
+ _finalStackAlignment = Support::max(_finalStackAlignment, _localStackAlignment);
1170
+ }
1171
+
1172
+ //! Returns call stack size.
1173
+ inline uint32_t callStackSize() const noexcept { return _callStackSize; }
1174
+ //! Returns local stack size.
1175
+ inline uint32_t localStackSize() const noexcept { return _localStackSize; }
1176
+
1177
+ //! Sets call stack size.
1178
+ inline void setCallStackSize(uint32_t size) noexcept { _callStackSize = size; }
1179
+ //! Sets local stack size.
1180
+ inline void setLocalStackSize(uint32_t size) noexcept { _localStackSize = size; }
1181
+
1182
+ //! Combines call stack size with `size`, updating it to the greater value.
1183
+ inline void updateCallStackSize(uint32_t size) noexcept { _callStackSize = Support::max(_callStackSize, size); }
1184
+ //! Combines local stack size with `size`, updating it to the greater value.
1185
+ inline void updateLocalStackSize(uint32_t size) noexcept { _localStackSize = Support::max(_localStackSize, size); }
1186
+
1187
+ //! Returns final stack size (only valid after the FuncFrame is finalized).
1188
+ inline uint32_t finalStackSize() const noexcept { return _finalStackSize; }
1189
+
1190
+ //! Returns an offset to access the local stack (non-zero only if call stack is used).
1191
+ inline uint32_t localStackOffset() const noexcept { return _localStackOffset; }
1192
+
1193
+ //! Tests whether the function prolog/epilog requires a memory slot for storing unaligned SP.
1194
+ inline bool hasDAOffset() const noexcept { return _daOffset != kTagInvalidOffset; }
1195
+ //! Returns a memory offset used to store DA (dynamic alignment) slot (relative to SP).
1196
+ inline uint32_t daOffset() const noexcept { return _daOffset; }
1197
+
1198
+ inline uint32_t saOffset(uint32_t regId) const noexcept {
1199
+ return regId == _spRegId ? saOffsetFromSP()
1200
+ : saOffsetFromSA();
1201
+ }
1202
+
1203
+ inline uint32_t saOffsetFromSP() const noexcept { return _saOffsetFromSP; }
1204
+ inline uint32_t saOffsetFromSA() const noexcept { return _saOffsetFromSA; }
1205
+
1206
+ //! Returns mask of registers of the given register `group` that are modified by the function. The engine would
1207
+ //! then calculate which registers must be saved & restored by the function by using the data provided by the
1208
+ //! calling convention.
1209
+ inline RegMask dirtyRegs(RegGroup group) const noexcept {
1210
+ ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
1211
+ return _dirtyRegs[group];
1212
+ }
1213
+
1214
+ //! Sets which registers (as a mask) are modified by the function.
1215
+ //!
1216
+ //! \remarks Please note that this will completely overwrite the existing register mask, use `addDirtyRegs()`
1217
+ //! to modify the existing register mask.
1218
+ inline void setDirtyRegs(RegGroup group, RegMask regs) noexcept {
1219
+ ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
1220
+ _dirtyRegs[group] = regs;
1221
+ }
1222
+
1223
+ //! Adds which registers (as a mask) are modified by the function.
1224
+ inline void addDirtyRegs(RegGroup group, RegMask regs) noexcept {
1225
+ ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
1226
+ _dirtyRegs[group] |= regs;
1227
+ }
1228
+
1229
+ //! \overload
1230
+ inline void addDirtyRegs(const BaseReg& reg) noexcept {
1231
+ ASMJIT_ASSERT(reg.id() < Globals::kMaxPhysRegs);
1232
+ addDirtyRegs(reg.group(), Support::bitMask(reg.id()));
1233
+ }
1234
+
1235
+ //! \overload
1236
+ template<typename... Args>
1237
+ inline void addDirtyRegs(const BaseReg& reg, Args&&... args) noexcept {
1238
+ addDirtyRegs(reg);
1239
+ addDirtyRegs(std::forward<Args>(args)...);
1240
+ }
1241
+
1242
+ inline void setAllDirty() noexcept {
1243
+ for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_dirtyRegs); i++)
1244
+ _dirtyRegs[i] = 0xFFFFFFFFu;
1245
+ }
1246
+
1247
+ inline void setAllDirty(RegGroup group) noexcept {
1248
+ ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
1249
+ _dirtyRegs[group] = 0xFFFFFFFFu;
1250
+ }
1251
+
1252
+ //! Returns a calculated mask of registers of the given `group` that will be saved and restored in the function's
1253
+ //! prolog and epilog, respectively. The register mask is calculated from both `dirtyRegs` (provided by user) and
1254
+ //! `preservedMask` (provided by the calling convention).
1255
+ inline RegMask savedRegs(RegGroup group) const noexcept {
1256
+ ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
1257
+ return _dirtyRegs[group] & _preservedRegs[group];
1258
+ }
1259
+
1260
+ //! Returns the mask of preserved registers of the given register `group`.
1261
+ //!
1262
+ //! Preserved registers are those that must survive the function call unmodified. The function can only modify
1263
+ //! preserved registers it they are saved and restored in funciton's prolog and epilog, respectively.
1264
+ inline RegMask preservedRegs(RegGroup group) const noexcept {
1265
+ ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
1266
+ return _preservedRegs[group];
1267
+ }
1268
+
1269
+ inline uint32_t saveRestoreRegSize(RegGroup group) const noexcept {
1270
+ ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
1271
+ return _saveRestoreRegSize[group];
1272
+ }
1273
+
1274
+ inline uint32_t saveRestoreAlignment(RegGroup group) const noexcept {
1275
+ ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
1276
+ return _saveRestoreAlignment[group];
1277
+ }
1278
+
1279
+ inline bool hasSARegId() const noexcept { return _saRegId != BaseReg::kIdBad; }
1280
+ inline uint32_t saRegId() const noexcept { return _saRegId; }
1281
+ inline void setSARegId(uint32_t regId) { _saRegId = uint8_t(regId); }
1282
+ inline void resetSARegId() { setSARegId(BaseReg::kIdBad); }
1283
+
1284
+ //! Returns stack size required to save/restore registers via push/pop.
1285
+ inline uint32_t pushPopSaveSize() const noexcept { return _pushPopSaveSize; }
1286
+ //! Returns an offset to the stack where registers are saved via push/pop.
1287
+ inline uint32_t pushPopSaveOffset() const noexcept { return _pushPopSaveOffset; }
1288
+
1289
+ //! Returns stack size required to save/restore extra registers that don't use push/pop/
1290
+ //!
1291
+ //! \note On X86 this covers all registers except GP registers, on other architectures it can be always
1292
+ //! zero (for example AArch64 saves all registers via push/pop like instructions, so this would be zero).
1293
+ inline uint32_t extraRegSaveSize() const noexcept { return _extraRegSaveSize; }
1294
+ //! Returns an offset to the stack where extra registers are saved.
1295
+ inline uint32_t extraRegSaveOffset() const noexcept { return _extraRegSaveOffset; }
1296
+
1297
+ //! Tests whether the functions contains stack adjustment.
1298
+ inline bool hasStackAdjustment() const noexcept { return _stackAdjustment != 0; }
1299
+ //! Returns function's stack adjustment used in function's prolog and epilog.
1300
+ //!
1301
+ //! If the returned value is zero it means that the stack is not adjusted. This can mean both that the stack
1302
+ //! is not used and/or the stack is only adjusted by instructions that pust/pop registers into/from stack.
1303
+ inline uint32_t stackAdjustment() const noexcept { return _stackAdjustment; }
1304
+
1305
+ //! \}
1306
+
1307
+ //! \name Finaliztion
1308
+ //! \{
1309
+
1310
+ ASMJIT_API Error finalize() noexcept;
1311
+
1312
+ //! \}
1313
+ };
1314
+
1315
+ //! A helper class that can be used to assign a physical register for each function argument. Use with
1316
+ //! `BaseEmitter::emitArgsAssignment()`.
1317
+ class FuncArgsAssignment {
1318
+ public:
1319
+ //! \name Members
1320
+ //! \{
1321
+
1322
+ //! Function detail.
1323
+ const FuncDetail* _funcDetail;
1324
+ //! Register that can be used to access arguments passed by stack.
1325
+ uint8_t _saRegId;
1326
+ //! Reserved for future use.
1327
+ uint8_t _reserved[3];
1328
+ //! Mapping of each function argument.
1329
+ FuncValuePack _argPacks[Globals::kMaxFuncArgs];
1330
+
1331
+ //! \}
1332
+
1333
+ //! \name Construction & Destruction
1334
+ //! \{
1335
+
1336
+ inline explicit FuncArgsAssignment(const FuncDetail* fd = nullptr) noexcept { reset(fd); }
1337
+
1338
+ inline FuncArgsAssignment(const FuncArgsAssignment& other) noexcept {
1339
+ memcpy(this, &other, sizeof(*this));
1340
+ }
1341
+
1342
+ inline void reset(const FuncDetail* fd = nullptr) noexcept {
1343
+ _funcDetail = fd;
1344
+ _saRegId = uint8_t(BaseReg::kIdBad);
1345
+ memset(_reserved, 0, sizeof(_reserved));
1346
+ memset(_argPacks, 0, sizeof(_argPacks));
1347
+ }
1348
+
1349
+ //! \}
1350
+
1351
+ //! \name Accessors
1352
+ //! \{
1353
+
1354
+ inline const FuncDetail* funcDetail() const noexcept { return _funcDetail; }
1355
+ inline void setFuncDetail(const FuncDetail* fd) noexcept { _funcDetail = fd; }
1356
+
1357
+ inline bool hasSARegId() const noexcept { return _saRegId != BaseReg::kIdBad; }
1358
+ inline uint32_t saRegId() const noexcept { return _saRegId; }
1359
+ inline void setSARegId(uint32_t regId) { _saRegId = uint8_t(regId); }
1360
+ inline void resetSARegId() { _saRegId = uint8_t(BaseReg::kIdBad); }
1361
+
1362
+ inline FuncValue& arg(size_t argIndex, size_t valueIndex) noexcept {
1363
+ ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
1364
+ return _argPacks[argIndex][valueIndex];
1365
+ }
1366
+ inline const FuncValue& arg(size_t argIndex, size_t valueIndex) const noexcept {
1367
+ ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
1368
+ return _argPacks[argIndex][valueIndex];
1369
+ }
1370
+
1371
+ inline bool isAssigned(size_t argIndex, size_t valueIndex) const noexcept {
1372
+ ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
1373
+ return _argPacks[argIndex][valueIndex].isAssigned();
1374
+ }
1375
+
1376
+ inline void assignReg(size_t argIndex, const BaseReg& reg, TypeId typeId = TypeId::kVoid) noexcept {
1377
+ ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
1378
+ ASMJIT_ASSERT(reg.isPhysReg());
1379
+ _argPacks[argIndex][0].initReg(reg.type(), reg.id(), typeId);
1380
+ }
1381
+
1382
+ inline void assignReg(size_t argIndex, RegType regType, uint32_t regId, TypeId typeId = TypeId::kVoid) noexcept {
1383
+ ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
1384
+ _argPacks[argIndex][0].initReg(regType, regId, typeId);
1385
+ }
1386
+
1387
+ inline void assignStack(size_t argIndex, int32_t offset, TypeId typeId = TypeId::kVoid) noexcept {
1388
+ ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
1389
+ _argPacks[argIndex][0].initStack(offset, typeId);
1390
+ }
1391
+
1392
+ inline void assignRegInPack(size_t argIndex, size_t valueIndex, const BaseReg& reg, TypeId typeId = TypeId::kVoid) noexcept {
1393
+ ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
1394
+ ASMJIT_ASSERT(reg.isPhysReg());
1395
+ _argPacks[argIndex][valueIndex].initReg(reg.type(), reg.id(), typeId);
1396
+ }
1397
+
1398
+ inline void assignRegInPack(size_t argIndex, size_t valueIndex, RegType regType, uint32_t regId, TypeId typeId = TypeId::kVoid) noexcept {
1399
+ ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
1400
+ _argPacks[argIndex][valueIndex].initReg(regType, regId, typeId);
1401
+ }
1402
+
1403
+ inline void assignStackInPack(size_t argIndex, size_t valueIndex, int32_t offset, TypeId typeId = TypeId::kVoid) noexcept {
1404
+ ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
1405
+ _argPacks[argIndex][valueIndex].initStack(offset, typeId);
1406
+ }
1407
+
1408
+ // NOTE: All `assignAll()` methods are shortcuts to assign all arguments at once, however, since registers are
1409
+ // passed all at once these initializers don't provide any way to pass TypeId and/or to keep any argument between
1410
+ // the arguments passed unassigned.
1411
+ inline void _assignAllInternal(size_t argIndex, const BaseReg& reg) noexcept {
1412
+ assignReg(argIndex, reg);
1413
+ }
1414
+
1415
+ template<typename... Args>
1416
+ inline void _assignAllInternal(size_t argIndex, const BaseReg& reg, Args&&... args) noexcept {
1417
+ assignReg(argIndex, reg);
1418
+ _assignAllInternal(argIndex + 1, std::forward<Args>(args)...);
1419
+ }
1420
+
1421
+ template<typename... Args>
1422
+ inline void assignAll(Args&&... args) noexcept {
1423
+ _assignAllInternal(0, std::forward<Args>(args)...);
1424
+ }
1425
+
1426
+ //! \}
1427
+
1428
+ //! \name Utilities
1429
+ //! \{
1430
+
1431
+ //! Update `FuncFrame` based on function's arguments assignment.
1432
+ //!
1433
+ //! \note You MUST call this in orher to use `BaseEmitter::emitArgsAssignment()`, otherwise the FuncFrame would
1434
+ //! not contain the information necessary to assign all arguments into the registers and/or stack specified.
1435
+ ASMJIT_API Error updateFuncFrame(FuncFrame& frame) const noexcept;
1436
+
1437
+ //! \}
1438
+ };
1439
+
1440
+ //! \}
1441
+
1442
+ ASMJIT_END_NAMESPACE
1443
+
1444
+ #endif // ASMJIT_CORE_FUNC_H_INCLUDED
1445
+