asmjit 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/asmjit.gemspec +1 -1
  4. data/ext/asmjit/asmjit/.editorconfig +10 -0
  5. data/ext/asmjit/asmjit/.github/FUNDING.yml +1 -0
  6. data/ext/asmjit/asmjit/.github/workflows/build-config.json +47 -0
  7. data/ext/asmjit/asmjit/.github/workflows/build.yml +156 -0
  8. data/ext/asmjit/asmjit/.gitignore +6 -0
  9. data/ext/asmjit/asmjit/CMakeLists.txt +611 -0
  10. data/ext/asmjit/asmjit/LICENSE.md +17 -0
  11. data/ext/asmjit/asmjit/README.md +69 -0
  12. data/ext/asmjit/asmjit/src/asmjit/a64.h +62 -0
  13. data/ext/asmjit/asmjit/src/asmjit/arm/a64archtraits_p.h +81 -0
  14. data/ext/asmjit/asmjit/src/asmjit/arm/a64assembler.cpp +5115 -0
  15. data/ext/asmjit/asmjit/src/asmjit/arm/a64assembler.h +72 -0
  16. data/ext/asmjit/asmjit/src/asmjit/arm/a64builder.cpp +51 -0
  17. data/ext/asmjit/asmjit/src/asmjit/arm/a64builder.h +57 -0
  18. data/ext/asmjit/asmjit/src/asmjit/arm/a64compiler.cpp +60 -0
  19. data/ext/asmjit/asmjit/src/asmjit/arm/a64compiler.h +247 -0
  20. data/ext/asmjit/asmjit/src/asmjit/arm/a64emithelper.cpp +464 -0
  21. data/ext/asmjit/asmjit/src/asmjit/arm/a64emithelper_p.h +50 -0
  22. data/ext/asmjit/asmjit/src/asmjit/arm/a64emitter.h +1228 -0
  23. data/ext/asmjit/asmjit/src/asmjit/arm/a64formatter.cpp +298 -0
  24. data/ext/asmjit/asmjit/src/asmjit/arm/a64formatter_p.h +59 -0
  25. data/ext/asmjit/asmjit/src/asmjit/arm/a64func.cpp +189 -0
  26. data/ext/asmjit/asmjit/src/asmjit/arm/a64func_p.h +33 -0
  27. data/ext/asmjit/asmjit/src/asmjit/arm/a64globals.h +1894 -0
  28. data/ext/asmjit/asmjit/src/asmjit/arm/a64instapi.cpp +278 -0
  29. data/ext/asmjit/asmjit/src/asmjit/arm/a64instapi_p.h +41 -0
  30. data/ext/asmjit/asmjit/src/asmjit/arm/a64instdb.cpp +1957 -0
  31. data/ext/asmjit/asmjit/src/asmjit/arm/a64instdb.h +74 -0
  32. data/ext/asmjit/asmjit/src/asmjit/arm/a64instdb_p.h +876 -0
  33. data/ext/asmjit/asmjit/src/asmjit/arm/a64operand.cpp +85 -0
  34. data/ext/asmjit/asmjit/src/asmjit/arm/a64operand.h +312 -0
  35. data/ext/asmjit/asmjit/src/asmjit/arm/a64rapass.cpp +852 -0
  36. data/ext/asmjit/asmjit/src/asmjit/arm/a64rapass_p.h +105 -0
  37. data/ext/asmjit/asmjit/src/asmjit/arm/a64utils.h +179 -0
  38. data/ext/asmjit/asmjit/src/asmjit/arm/armformatter.cpp +143 -0
  39. data/ext/asmjit/asmjit/src/asmjit/arm/armformatter_p.h +44 -0
  40. data/ext/asmjit/asmjit/src/asmjit/arm/armglobals.h +21 -0
  41. data/ext/asmjit/asmjit/src/asmjit/arm/armoperand.h +621 -0
  42. data/ext/asmjit/asmjit/src/asmjit/arm.h +62 -0
  43. data/ext/asmjit/asmjit/src/asmjit/asmjit-scope-begin.h +17 -0
  44. data/ext/asmjit/asmjit/src/asmjit/asmjit-scope-end.h +9 -0
  45. data/ext/asmjit/asmjit/src/asmjit/asmjit.h +33 -0
  46. data/ext/asmjit/asmjit/src/asmjit/core/api-build_p.h +55 -0
  47. data/ext/asmjit/asmjit/src/asmjit/core/api-config.h +613 -0
  48. data/ext/asmjit/asmjit/src/asmjit/core/archcommons.h +229 -0
  49. data/ext/asmjit/asmjit/src/asmjit/core/archtraits.cpp +160 -0
  50. data/ext/asmjit/asmjit/src/asmjit/core/archtraits.h +290 -0
  51. data/ext/asmjit/asmjit/src/asmjit/core/assembler.cpp +406 -0
  52. data/ext/asmjit/asmjit/src/asmjit/core/assembler.h +129 -0
  53. data/ext/asmjit/asmjit/src/asmjit/core/builder.cpp +889 -0
  54. data/ext/asmjit/asmjit/src/asmjit/core/builder.h +1391 -0
  55. data/ext/asmjit/asmjit/src/asmjit/core/codebuffer.h +113 -0
  56. data/ext/asmjit/asmjit/src/asmjit/core/codeholder.cpp +1149 -0
  57. data/ext/asmjit/asmjit/src/asmjit/core/codeholder.h +1035 -0
  58. data/ext/asmjit/asmjit/src/asmjit/core/codewriter.cpp +175 -0
  59. data/ext/asmjit/asmjit/src/asmjit/core/codewriter_p.h +179 -0
  60. data/ext/asmjit/asmjit/src/asmjit/core/compiler.cpp +582 -0
  61. data/ext/asmjit/asmjit/src/asmjit/core/compiler.h +737 -0
  62. data/ext/asmjit/asmjit/src/asmjit/core/compilerdefs.h +173 -0
  63. data/ext/asmjit/asmjit/src/asmjit/core/constpool.cpp +363 -0
  64. data/ext/asmjit/asmjit/src/asmjit/core/constpool.h +250 -0
  65. data/ext/asmjit/asmjit/src/asmjit/core/cpuinfo.cpp +1162 -0
  66. data/ext/asmjit/asmjit/src/asmjit/core/cpuinfo.h +813 -0
  67. data/ext/asmjit/asmjit/src/asmjit/core/emithelper.cpp +323 -0
  68. data/ext/asmjit/asmjit/src/asmjit/core/emithelper_p.h +58 -0
  69. data/ext/asmjit/asmjit/src/asmjit/core/emitter.cpp +333 -0
  70. data/ext/asmjit/asmjit/src/asmjit/core/emitter.h +741 -0
  71. data/ext/asmjit/asmjit/src/asmjit/core/emitterutils.cpp +129 -0
  72. data/ext/asmjit/asmjit/src/asmjit/core/emitterutils_p.h +89 -0
  73. data/ext/asmjit/asmjit/src/asmjit/core/environment.cpp +46 -0
  74. data/ext/asmjit/asmjit/src/asmjit/core/environment.h +508 -0
  75. data/ext/asmjit/asmjit/src/asmjit/core/errorhandler.cpp +14 -0
  76. data/ext/asmjit/asmjit/src/asmjit/core/errorhandler.h +228 -0
  77. data/ext/asmjit/asmjit/src/asmjit/core/formatter.cpp +584 -0
  78. data/ext/asmjit/asmjit/src/asmjit/core/formatter.h +247 -0
  79. data/ext/asmjit/asmjit/src/asmjit/core/formatter_p.h +34 -0
  80. data/ext/asmjit/asmjit/src/asmjit/core/func.cpp +286 -0
  81. data/ext/asmjit/asmjit/src/asmjit/core/func.h +1445 -0
  82. data/ext/asmjit/asmjit/src/asmjit/core/funcargscontext.cpp +293 -0
  83. data/ext/asmjit/asmjit/src/asmjit/core/funcargscontext_p.h +199 -0
  84. data/ext/asmjit/asmjit/src/asmjit/core/globals.cpp +133 -0
  85. data/ext/asmjit/asmjit/src/asmjit/core/globals.h +393 -0
  86. data/ext/asmjit/asmjit/src/asmjit/core/inst.cpp +113 -0
  87. data/ext/asmjit/asmjit/src/asmjit/core/inst.h +772 -0
  88. data/ext/asmjit/asmjit/src/asmjit/core/jitallocator.cpp +1242 -0
  89. data/ext/asmjit/asmjit/src/asmjit/core/jitallocator.h +261 -0
  90. data/ext/asmjit/asmjit/src/asmjit/core/jitruntime.cpp +80 -0
  91. data/ext/asmjit/asmjit/src/asmjit/core/jitruntime.h +89 -0
  92. data/ext/asmjit/asmjit/src/asmjit/core/logger.cpp +69 -0
  93. data/ext/asmjit/asmjit/src/asmjit/core/logger.h +198 -0
  94. data/ext/asmjit/asmjit/src/asmjit/core/misc_p.h +33 -0
  95. data/ext/asmjit/asmjit/src/asmjit/core/operand.cpp +132 -0
  96. data/ext/asmjit/asmjit/src/asmjit/core/operand.h +1611 -0
  97. data/ext/asmjit/asmjit/src/asmjit/core/osutils.cpp +84 -0
  98. data/ext/asmjit/asmjit/src/asmjit/core/osutils.h +61 -0
  99. data/ext/asmjit/asmjit/src/asmjit/core/osutils_p.h +68 -0
  100. data/ext/asmjit/asmjit/src/asmjit/core/raassignment_p.h +418 -0
  101. data/ext/asmjit/asmjit/src/asmjit/core/rabuilders_p.h +612 -0
  102. data/ext/asmjit/asmjit/src/asmjit/core/radefs_p.h +1204 -0
  103. data/ext/asmjit/asmjit/src/asmjit/core/ralocal.cpp +1166 -0
  104. data/ext/asmjit/asmjit/src/asmjit/core/ralocal_p.h +254 -0
  105. data/ext/asmjit/asmjit/src/asmjit/core/rapass.cpp +1969 -0
  106. data/ext/asmjit/asmjit/src/asmjit/core/rapass_p.h +1183 -0
  107. data/ext/asmjit/asmjit/src/asmjit/core/rastack.cpp +184 -0
  108. data/ext/asmjit/asmjit/src/asmjit/core/rastack_p.h +171 -0
  109. data/ext/asmjit/asmjit/src/asmjit/core/string.cpp +559 -0
  110. data/ext/asmjit/asmjit/src/asmjit/core/string.h +372 -0
  111. data/ext/asmjit/asmjit/src/asmjit/core/support.cpp +494 -0
  112. data/ext/asmjit/asmjit/src/asmjit/core/support.h +1773 -0
  113. data/ext/asmjit/asmjit/src/asmjit/core/target.cpp +14 -0
  114. data/ext/asmjit/asmjit/src/asmjit/core/target.h +53 -0
  115. data/ext/asmjit/asmjit/src/asmjit/core/type.cpp +74 -0
  116. data/ext/asmjit/asmjit/src/asmjit/core/type.h +419 -0
  117. data/ext/asmjit/asmjit/src/asmjit/core/virtmem.cpp +722 -0
  118. data/ext/asmjit/asmjit/src/asmjit/core/virtmem.h +242 -0
  119. data/ext/asmjit/asmjit/src/asmjit/core/zone.cpp +353 -0
  120. data/ext/asmjit/asmjit/src/asmjit/core/zone.h +615 -0
  121. data/ext/asmjit/asmjit/src/asmjit/core/zonehash.cpp +309 -0
  122. data/ext/asmjit/asmjit/src/asmjit/core/zonehash.h +186 -0
  123. data/ext/asmjit/asmjit/src/asmjit/core/zonelist.cpp +163 -0
  124. data/ext/asmjit/asmjit/src/asmjit/core/zonelist.h +209 -0
  125. data/ext/asmjit/asmjit/src/asmjit/core/zonestack.cpp +176 -0
  126. data/ext/asmjit/asmjit/src/asmjit/core/zonestack.h +239 -0
  127. data/ext/asmjit/asmjit/src/asmjit/core/zonestring.h +120 -0
  128. data/ext/asmjit/asmjit/src/asmjit/core/zonetree.cpp +99 -0
  129. data/ext/asmjit/asmjit/src/asmjit/core/zonetree.h +380 -0
  130. data/ext/asmjit/asmjit/src/asmjit/core/zonevector.cpp +356 -0
  131. data/ext/asmjit/asmjit/src/asmjit/core/zonevector.h +690 -0
  132. data/ext/asmjit/asmjit/src/asmjit/core.h +1861 -0
  133. data/ext/asmjit/asmjit/src/asmjit/x86/x86archtraits_p.h +148 -0
  134. data/ext/asmjit/asmjit/src/asmjit/x86/x86assembler.cpp +5110 -0
  135. data/ext/asmjit/asmjit/src/asmjit/x86/x86assembler.h +685 -0
  136. data/ext/asmjit/asmjit/src/asmjit/x86/x86builder.cpp +52 -0
  137. data/ext/asmjit/asmjit/src/asmjit/x86/x86builder.h +351 -0
  138. data/ext/asmjit/asmjit/src/asmjit/x86/x86compiler.cpp +61 -0
  139. data/ext/asmjit/asmjit/src/asmjit/x86/x86compiler.h +721 -0
  140. data/ext/asmjit/asmjit/src/asmjit/x86/x86emithelper.cpp +619 -0
  141. data/ext/asmjit/asmjit/src/asmjit/x86/x86emithelper_p.h +60 -0
  142. data/ext/asmjit/asmjit/src/asmjit/x86/x86emitter.h +4315 -0
  143. data/ext/asmjit/asmjit/src/asmjit/x86/x86formatter.cpp +944 -0
  144. data/ext/asmjit/asmjit/src/asmjit/x86/x86formatter_p.h +58 -0
  145. data/ext/asmjit/asmjit/src/asmjit/x86/x86func.cpp +503 -0
  146. data/ext/asmjit/asmjit/src/asmjit/x86/x86func_p.h +33 -0
  147. data/ext/asmjit/asmjit/src/asmjit/x86/x86globals.h +2169 -0
  148. data/ext/asmjit/asmjit/src/asmjit/x86/x86instapi.cpp +1732 -0
  149. data/ext/asmjit/asmjit/src/asmjit/x86/x86instapi_p.h +41 -0
  150. data/ext/asmjit/asmjit/src/asmjit/x86/x86instdb.cpp +4427 -0
  151. data/ext/asmjit/asmjit/src/asmjit/x86/x86instdb.h +563 -0
  152. data/ext/asmjit/asmjit/src/asmjit/x86/x86instdb_p.h +311 -0
  153. data/ext/asmjit/asmjit/src/asmjit/x86/x86opcode_p.h +436 -0
  154. data/ext/asmjit/asmjit/src/asmjit/x86/x86operand.cpp +231 -0
  155. data/ext/asmjit/asmjit/src/asmjit/x86/x86operand.h +1085 -0
  156. data/ext/asmjit/asmjit/src/asmjit/x86/x86rapass.cpp +1509 -0
  157. data/ext/asmjit/asmjit/src/asmjit/x86/x86rapass_p.h +94 -0
  158. data/ext/asmjit/asmjit/src/asmjit/x86.h +93 -0
  159. data/ext/asmjit/asmjit/src/asmjit.natvis +245 -0
  160. data/ext/asmjit/asmjit/test/asmjit_test_assembler.cpp +84 -0
  161. data/ext/asmjit/asmjit/test/asmjit_test_assembler.h +85 -0
  162. data/ext/asmjit/asmjit/test/asmjit_test_assembler_a64.cpp +4006 -0
  163. data/ext/asmjit/asmjit/test/asmjit_test_assembler_x64.cpp +17833 -0
  164. data/ext/asmjit/asmjit/test/asmjit_test_assembler_x86.cpp +8300 -0
  165. data/ext/asmjit/asmjit/test/asmjit_test_compiler.cpp +253 -0
  166. data/ext/asmjit/asmjit/test/asmjit_test_compiler.h +73 -0
  167. data/ext/asmjit/asmjit/test/asmjit_test_compiler_a64.cpp +690 -0
  168. data/ext/asmjit/asmjit/test/asmjit_test_compiler_x86.cpp +4317 -0
  169. data/ext/asmjit/asmjit/test/asmjit_test_emitters.cpp +197 -0
  170. data/ext/asmjit/asmjit/test/asmjit_test_instinfo.cpp +181 -0
  171. data/ext/asmjit/asmjit/test/asmjit_test_misc.h +257 -0
  172. data/ext/asmjit/asmjit/test/asmjit_test_perf.cpp +62 -0
  173. data/ext/asmjit/asmjit/test/asmjit_test_perf.h +61 -0
  174. data/ext/asmjit/asmjit/test/asmjit_test_perf_a64.cpp +699 -0
  175. data/ext/asmjit/asmjit/test/asmjit_test_perf_x86.cpp +5032 -0
  176. data/ext/asmjit/asmjit/test/asmjit_test_unit.cpp +172 -0
  177. data/ext/asmjit/asmjit/test/asmjit_test_x86_sections.cpp +172 -0
  178. data/ext/asmjit/asmjit/test/asmjitutils.h +38 -0
  179. data/ext/asmjit/asmjit/test/broken.cpp +312 -0
  180. data/ext/asmjit/asmjit/test/broken.h +148 -0
  181. data/ext/asmjit/asmjit/test/cmdline.h +61 -0
  182. data/ext/asmjit/asmjit/test/performancetimer.h +41 -0
  183. data/ext/asmjit/asmjit/tools/configure-makefiles.sh +13 -0
  184. data/ext/asmjit/asmjit/tools/configure-ninja.sh +13 -0
  185. data/ext/asmjit/asmjit/tools/configure-sanitizers.sh +13 -0
  186. data/ext/asmjit/asmjit/tools/configure-vs2019-x64.bat +2 -0
  187. data/ext/asmjit/asmjit/tools/configure-vs2019-x86.bat +2 -0
  188. data/ext/asmjit/asmjit/tools/configure-vs2022-x64.bat +2 -0
  189. data/ext/asmjit/asmjit/tools/configure-vs2022-x86.bat +2 -0
  190. data/ext/asmjit/asmjit/tools/configure-xcode.sh +8 -0
  191. data/ext/asmjit/asmjit/tools/enumgen.js +417 -0
  192. data/ext/asmjit/asmjit/tools/enumgen.sh +3 -0
  193. data/ext/asmjit/asmjit/tools/tablegen-arm.js +365 -0
  194. data/ext/asmjit/asmjit/tools/tablegen-arm.sh +3 -0
  195. data/ext/asmjit/asmjit/tools/tablegen-x86.js +2638 -0
  196. data/ext/asmjit/asmjit/tools/tablegen-x86.sh +3 -0
  197. data/ext/asmjit/asmjit/tools/tablegen.js +947 -0
  198. data/ext/asmjit/asmjit/tools/tablegen.sh +4 -0
  199. data/ext/asmjit/asmjit.cc +18 -0
  200. data/lib/asmjit/version.rb +1 -1
  201. metadata +197 -2
@@ -0,0 +1,1732 @@
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
+ // ----------------------------------------------------------------------------
7
+ // IMPORTANT: AsmJit now uses an external instruction database to populate
8
+ // static tables within this file. Perform the following steps to regenerate
9
+ // all tables enclosed by ${...}:
10
+ //
11
+ // 1. Install node.js environment <https://nodejs.org>
12
+ // 2. Go to asmjit/tools directory
13
+ // 3. Get the latest asmdb from <https://github.com/asmjit/asmdb> and
14
+ // copy/link the `asmdb` directory to `asmjit/tools/asmdb`.
15
+ // 4. Execute `node tablegen-x86.js`
16
+ //
17
+ // Instruction encoding and opcodes were added to the `x86inst.cpp` database
18
+ // manually in the past and they are not updated by the script as it became
19
+ // tricky. However, everything else is updated including instruction operands
20
+ // and tables required to validate them, instruction read/write information
21
+ // (including registers and flags), and all indexes to all tables.
22
+ // ----------------------------------------------------------------------------
23
+
24
+ #include "../core/api-build_p.h"
25
+ #if !defined(ASMJIT_NO_X86)
26
+
27
+ #include "../core/cpuinfo.h"
28
+ #include "../core/misc_p.h"
29
+ #include "../core/support.h"
30
+ #include "../x86/x86instapi_p.h"
31
+ #include "../x86/x86instdb_p.h"
32
+ #include "../x86/x86opcode_p.h"
33
+ #include "../x86/x86operand.h"
34
+
35
+ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
36
+
37
+ // x86::InstInternal - Text
38
+ // ========================
39
+
40
+ #ifndef ASMJIT_NO_TEXT
41
+ Error InstInternal::instIdToString(Arch arch, InstId instId, String& output) noexcept {
42
+ DebugUtils::unused(arch);
43
+
44
+ if (ASMJIT_UNLIKELY(!Inst::isDefinedId(instId)))
45
+ return DebugUtils::errored(kErrorInvalidInstruction);
46
+
47
+ const InstDB::InstInfo& info = InstDB::infoById(instId);
48
+ return output.append(InstDB::_nameData + info._nameDataIndex);
49
+ }
50
+
51
+ InstId InstInternal::stringToInstId(Arch arch, const char* s, size_t len) noexcept {
52
+ DebugUtils::unused(arch);
53
+
54
+ if (ASMJIT_UNLIKELY(!s))
55
+ return Inst::kIdNone;
56
+
57
+ if (len == SIZE_MAX)
58
+ len = strlen(s);
59
+
60
+ if (ASMJIT_UNLIKELY(len == 0 || len > InstDB::kMaxNameSize))
61
+ return Inst::kIdNone;
62
+
63
+ uint32_t prefix = uint32_t(s[0]) - 'a';
64
+ if (ASMJIT_UNLIKELY(prefix > 'z' - 'a'))
65
+ return Inst::kIdNone;
66
+
67
+ uint32_t index = InstDB::instNameIndex[prefix].start;
68
+ if (ASMJIT_UNLIKELY(!index))
69
+ return Inst::kIdNone;
70
+
71
+ const char* nameData = InstDB::_nameData;
72
+ const InstDB::InstInfo* table = InstDB::_instInfoTable;
73
+
74
+ const InstDB::InstInfo* base = table + index;
75
+ const InstDB::InstInfo* end = table + InstDB::instNameIndex[prefix].end;
76
+
77
+ for (size_t lim = (size_t)(end - base); lim != 0; lim >>= 1) {
78
+ const InstDB::InstInfo* cur = base + (lim >> 1);
79
+ int result = Support::cmpInstName(nameData + cur[0]._nameDataIndex, s, len);
80
+
81
+ if (result < 0) {
82
+ base = cur + 1;
83
+ lim--;
84
+ continue;
85
+ }
86
+
87
+ if (result > 0)
88
+ continue;
89
+
90
+ return InstId((size_t)(cur - table));
91
+ }
92
+
93
+ return Inst::kIdNone;
94
+ }
95
+ #endif // !ASMJIT_NO_TEXT
96
+
97
+ // x86::InstInternal - Validate
98
+ // ============================
99
+
100
+ #ifndef ASMJIT_NO_VALIDATION
101
+ struct X86ValidationData {
102
+ //! Allowed registers by \ref RegType.
103
+ RegMask allowedRegMask[uint32_t(RegType::kMaxValue) + 1];
104
+ uint32_t allowedMemBaseRegs;
105
+ uint32_t allowedMemIndexRegs;
106
+ };
107
+
108
+ #define VALUE(x) \
109
+ (x == uint32_t(RegType::kX86_GpbLo)) ? InstDB::OpFlags::kRegGpbLo : \
110
+ (x == uint32_t(RegType::kX86_GpbHi)) ? InstDB::OpFlags::kRegGpbHi : \
111
+ (x == uint32_t(RegType::kX86_Gpw )) ? InstDB::OpFlags::kRegGpw : \
112
+ (x == uint32_t(RegType::kX86_Gpd )) ? InstDB::OpFlags::kRegGpd : \
113
+ (x == uint32_t(RegType::kX86_Gpq )) ? InstDB::OpFlags::kRegGpq : \
114
+ (x == uint32_t(RegType::kX86_Xmm )) ? InstDB::OpFlags::kRegXmm : \
115
+ (x == uint32_t(RegType::kX86_Ymm )) ? InstDB::OpFlags::kRegYmm : \
116
+ (x == uint32_t(RegType::kX86_Zmm )) ? InstDB::OpFlags::kRegZmm : \
117
+ (x == uint32_t(RegType::kX86_Mm )) ? InstDB::OpFlags::kRegMm : \
118
+ (x == uint32_t(RegType::kX86_KReg )) ? InstDB::OpFlags::kRegKReg : \
119
+ (x == uint32_t(RegType::kX86_SReg )) ? InstDB::OpFlags::kRegSReg : \
120
+ (x == uint32_t(RegType::kX86_CReg )) ? InstDB::OpFlags::kRegCReg : \
121
+ (x == uint32_t(RegType::kX86_DReg )) ? InstDB::OpFlags::kRegDReg : \
122
+ (x == uint32_t(RegType::kX86_St )) ? InstDB::OpFlags::kRegSt : \
123
+ (x == uint32_t(RegType::kX86_Bnd )) ? InstDB::OpFlags::kRegBnd : \
124
+ (x == uint32_t(RegType::kX86_Tmm )) ? InstDB::OpFlags::kRegTmm : \
125
+ (x == uint32_t(RegType::kX86_Rip )) ? InstDB::OpFlags::kNone : InstDB::OpFlags::kNone
126
+ static const InstDB::OpFlags _x86OpFlagFromRegType[uint32_t(RegType::kMaxValue) + 1] = { ASMJIT_LOOKUP_TABLE_32(VALUE, 0) };
127
+ #undef VALUE
128
+
129
+ #define REG_MASK_FROM_REG_TYPE_X86(x) \
130
+ (x == uint32_t(RegType::kX86_GpbLo)) ? 0x0000000Fu : \
131
+ (x == uint32_t(RegType::kX86_GpbHi)) ? 0x0000000Fu : \
132
+ (x == uint32_t(RegType::kX86_Gpw )) ? 0x000000FFu : \
133
+ (x == uint32_t(RegType::kX86_Gpd )) ? 0x000000FFu : \
134
+ (x == uint32_t(RegType::kX86_Gpq )) ? 0x000000FFu : \
135
+ (x == uint32_t(RegType::kX86_Xmm )) ? 0x000000FFu : \
136
+ (x == uint32_t(RegType::kX86_Ymm )) ? 0x000000FFu : \
137
+ (x == uint32_t(RegType::kX86_Zmm )) ? 0x000000FFu : \
138
+ (x == uint32_t(RegType::kX86_Mm )) ? 0x000000FFu : \
139
+ (x == uint32_t(RegType::kX86_KReg )) ? 0x000000FFu : \
140
+ (x == uint32_t(RegType::kX86_SReg )) ? 0x0000007Eu : \
141
+ (x == uint32_t(RegType::kX86_CReg )) ? 0x0000FFFFu : \
142
+ (x == uint32_t(RegType::kX86_DReg )) ? 0x000000FFu : \
143
+ (x == uint32_t(RegType::kX86_St )) ? 0x000000FFu : \
144
+ (x == uint32_t(RegType::kX86_Bnd )) ? 0x0000000Fu : \
145
+ (x == uint32_t(RegType::kX86_Tmm )) ? 0x000000FFu : \
146
+ (x == uint32_t(RegType::kX86_Rip )) ? 0x00000001u : 0u
147
+
148
+ #define REG_MASK_FROM_REG_TYPE_X64(x) \
149
+ (x == uint32_t(RegType::kX86_GpbLo)) ? 0x0000FFFFu : \
150
+ (x == uint32_t(RegType::kX86_GpbHi)) ? 0x0000000Fu : \
151
+ (x == uint32_t(RegType::kX86_Gpw )) ? 0x0000FFFFu : \
152
+ (x == uint32_t(RegType::kX86_Gpd )) ? 0x0000FFFFu : \
153
+ (x == uint32_t(RegType::kX86_Gpq )) ? 0x0000FFFFu : \
154
+ (x == uint32_t(RegType::kX86_Xmm )) ? 0xFFFFFFFFu : \
155
+ (x == uint32_t(RegType::kX86_Ymm )) ? 0xFFFFFFFFu : \
156
+ (x == uint32_t(RegType::kX86_Zmm )) ? 0xFFFFFFFFu : \
157
+ (x == uint32_t(RegType::kX86_Mm )) ? 0x000000FFu : \
158
+ (x == uint32_t(RegType::kX86_KReg )) ? 0x000000FFu : \
159
+ (x == uint32_t(RegType::kX86_SReg )) ? 0x0000007Eu : \
160
+ (x == uint32_t(RegType::kX86_CReg )) ? 0x0000FFFFu : \
161
+ (x == uint32_t(RegType::kX86_DReg )) ? 0x0000FFFFu : \
162
+ (x == uint32_t(RegType::kX86_St )) ? 0x000000FFu : \
163
+ (x == uint32_t(RegType::kX86_Bnd )) ? 0x0000000Fu : \
164
+ (x == uint32_t(RegType::kX86_Tmm )) ? 0x000000FFu : \
165
+ (x == uint32_t(RegType::kX86_Rip )) ? 0x00000001u : 0u
166
+
167
+ #define B(RegType) (uint32_t(1) << uint32_t(RegType))
168
+
169
+ static const X86ValidationData _x86ValidationData = {
170
+ { ASMJIT_LOOKUP_TABLE_32(REG_MASK_FROM_REG_TYPE_X86, 0) },
171
+ B(RegType::kX86_Gpw) | B(RegType::kX86_Gpd) | B(RegType::kX86_Rip) | B(RegType::kLabelTag),
172
+ B(RegType::kX86_Gpw) | B(RegType::kX86_Gpd) | B(RegType::kX86_Xmm) | B(RegType::kX86_Ymm) | B(RegType::kX86_Zmm)
173
+ };
174
+
175
+ static const X86ValidationData _x64ValidationData = {
176
+ { ASMJIT_LOOKUP_TABLE_32(REG_MASK_FROM_REG_TYPE_X64, 0) },
177
+ B(RegType::kX86_Gpd) | B(RegType::kX86_Gpq) | B(RegType::kX86_Rip) | B(RegType::kLabelTag),
178
+ B(RegType::kX86_Gpd) | B(RegType::kX86_Gpq) | B(RegType::kX86_Xmm) | B(RegType::kX86_Ymm) | B(RegType::kX86_Zmm)
179
+ };
180
+
181
+ #undef B
182
+
183
+ #undef REG_MASK_FROM_REG_TYPE_X64
184
+ #undef REG_MASK_FROM_REG_TYPE_X86
185
+
186
+ static ASMJIT_FORCE_INLINE bool x86IsZmmOrM512(const Operand_& op) noexcept {
187
+ return Reg::isZmm(op) || (op.isMem() && op.size() == 64);
188
+ }
189
+
190
+ static ASMJIT_FORCE_INLINE bool x86CheckOSig(const InstDB::OpSignature& op, const InstDB::OpSignature& ref, bool& immOutOfRange) noexcept {
191
+ // Fail if operand types are incompatible.
192
+ InstDB::OpFlags commonFlags = op.flags() & ref.flags();
193
+
194
+ if (!Support::test(commonFlags, InstDB::OpFlags::kOpMask)) {
195
+ // Mark temporarily `immOutOfRange` so we can return a more descriptive error later.
196
+ if (op.hasImm() && ref.hasImm()) {
197
+ immOutOfRange = true;
198
+ return true;
199
+ }
200
+
201
+ return false;
202
+ }
203
+
204
+ // Fail if some memory specific flags do not match.
205
+ if (Support::test(commonFlags, InstDB::OpFlags::kMemMask)) {
206
+ if (ref.hasFlag(InstDB::OpFlags::kFlagMemBase) && !op.hasFlag(InstDB::OpFlags::kFlagMemBase))
207
+ return false;
208
+ }
209
+
210
+ // Fail if register indexes do not match.
211
+ if (Support::test(commonFlags, InstDB::OpFlags::kRegMask)) {
212
+ if (ref.regMask() && !Support::test(op.regMask(), ref.regMask()))
213
+ return false;
214
+ }
215
+
216
+ return true;
217
+ }
218
+
219
+ ASMJIT_FAVOR_SIZE Error InstInternal::validate(Arch arch, const BaseInst& inst, const Operand_* operands, size_t opCount, ValidationFlags validationFlags) noexcept {
220
+ // Only called when `arch` matches X86 family.
221
+ ASMJIT_ASSERT(Environment::isFamilyX86(arch));
222
+
223
+ const X86ValidationData* vd;
224
+ if (arch == Arch::kX86)
225
+ vd = &_x86ValidationData;
226
+ else
227
+ vd = &_x64ValidationData;
228
+
229
+ uint32_t i;
230
+ InstDB::Mode mode = InstDB::modeFromArch(arch);
231
+
232
+ // Get the instruction data.
233
+ InstId instId = inst.id();
234
+ InstOptions options = inst.options();
235
+
236
+ if (ASMJIT_UNLIKELY(!Inst::isDefinedId(instId)))
237
+ return DebugUtils::errored(kErrorInvalidInstruction);
238
+
239
+ const InstDB::InstInfo& instInfo = InstDB::infoById(instId);
240
+ const InstDB::CommonInfo& commonInfo = instInfo.commonInfo();
241
+
242
+ InstDB::InstFlags iFlags = instInfo.flags();
243
+
244
+ constexpr InstOptions kRepAny = InstOptions::kX86_Rep | InstOptions::kX86_Repne;
245
+ constexpr InstOptions kXAcqXRel = InstOptions::kX86_XAcquire | InstOptions::kX86_XRelease;
246
+ constexpr InstOptions kAvx512Options = InstOptions::kX86_ZMask | InstOptions::kX86_ER | InstOptions::kX86_SAE;
247
+
248
+ // Validate LOCK|XACQUIRE|XRELEASE Prefixes
249
+ // ----------------------------------------
250
+
251
+ if (Support::test(options, InstOptions::kX86_Lock | kXAcqXRel)) {
252
+ if (Support::test(options, InstOptions::kX86_Lock)) {
253
+ if (ASMJIT_UNLIKELY(!Support::test(iFlags, InstDB::InstFlags::kLock) && !Support::test(options, kXAcqXRel)))
254
+ return DebugUtils::errored(kErrorInvalidLockPrefix);
255
+
256
+ if (ASMJIT_UNLIKELY(opCount < 1 || !operands[0].isMem()))
257
+ return DebugUtils::errored(kErrorInvalidLockPrefix);
258
+ }
259
+
260
+ if (Support::test(options, kXAcqXRel)) {
261
+ if (ASMJIT_UNLIKELY(!Support::test(options, InstOptions::kX86_Lock) || (options & kXAcqXRel) == kXAcqXRel))
262
+ return DebugUtils::errored(kErrorInvalidPrefixCombination);
263
+
264
+ if (ASMJIT_UNLIKELY(Support::test(options, InstOptions::kX86_XAcquire) && !Support::test(iFlags, InstDB::InstFlags::kXAcquire)))
265
+ return DebugUtils::errored(kErrorInvalidXAcquirePrefix);
266
+
267
+ if (ASMJIT_UNLIKELY(Support::test(options, InstOptions::kX86_XRelease) && !Support::test(iFlags, InstDB::InstFlags::kXRelease)))
268
+ return DebugUtils::errored(kErrorInvalidXReleasePrefix);
269
+ }
270
+ }
271
+
272
+ // Validate REP and REPNE Prefixes
273
+ // -------------------------------
274
+
275
+ if (Support::test(options, kRepAny)) {
276
+ if (ASMJIT_UNLIKELY((options & kRepAny) == kRepAny))
277
+ return DebugUtils::errored(kErrorInvalidPrefixCombination);
278
+
279
+ if (ASMJIT_UNLIKELY(!Support::test(iFlags, InstDB::InstFlags::kRep)))
280
+ return DebugUtils::errored(kErrorInvalidRepPrefix);
281
+ }
282
+
283
+ // Translate Each Operand to the Corresponding OpSignature
284
+ // -------------------------------------------------------
285
+
286
+ InstDB::OpSignature oSigTranslated[Globals::kMaxOpCount];
287
+ InstDB::OpFlags combinedOpFlags = InstDB::OpFlags::kNone;
288
+ uint32_t combinedRegMask = 0;
289
+ const Mem* memOp = nullptr;
290
+
291
+ for (i = 0; i < opCount; i++) {
292
+ const Operand_& op = operands[i];
293
+ if (op.opType() == OperandType::kNone)
294
+ break;
295
+
296
+ InstDB::OpFlags opFlags = InstDB::OpFlags::kNone;
297
+ RegMask regMask = 0;
298
+
299
+ switch (op.opType()) {
300
+ case OperandType::kReg: {
301
+ RegType regType = op.as<BaseReg>().type();
302
+ opFlags = _x86OpFlagFromRegType[size_t(regType)];
303
+
304
+ if (ASMJIT_UNLIKELY(opFlags == InstDB::OpFlags::kNone))
305
+ return DebugUtils::errored(kErrorInvalidRegType);
306
+
307
+ // If `regId` is equal or greater than Operand::kVirtIdMin it means that the register is virtual and its
308
+ // index will be assigned later by the register allocator. We must pass unless asked to disallow virtual
309
+ // registers.
310
+ uint32_t regId = op.id();
311
+ if (regId < Operand::kVirtIdMin) {
312
+ if (ASMJIT_UNLIKELY(regId >= 32))
313
+ return DebugUtils::errored(kErrorInvalidPhysId);
314
+
315
+ if (ASMJIT_UNLIKELY(Support::bitTest(vd->allowedRegMask[size_t(regType)], regId) == 0))
316
+ return DebugUtils::errored(kErrorInvalidPhysId);
317
+
318
+ regMask = Support::bitMask(regId);
319
+ combinedRegMask |= regMask;
320
+ }
321
+ else {
322
+ if (uint32_t(validationFlags & ValidationFlags::kEnableVirtRegs) == 0)
323
+ return DebugUtils::errored(kErrorIllegalVirtReg);
324
+ regMask = 0xFFFFFFFFu;
325
+ }
326
+ break;
327
+ }
328
+
329
+ // TODO: Validate base and index and combine these with `combinedRegMask`.
330
+ case OperandType::kMem: {
331
+ const Mem& m = op.as<Mem>();
332
+ memOp = &m;
333
+
334
+ uint32_t memSize = m.size();
335
+ RegType baseType = m.baseType();
336
+ RegType indexType = m.indexType();
337
+
338
+ if (m.segmentId() > 6)
339
+ return DebugUtils::errored(kErrorInvalidSegment);
340
+
341
+ // Validate AVX-512 broadcast {1tox}.
342
+ if (m.hasBroadcast()) {
343
+ if (memSize != 0) {
344
+ // If the size is specified it has to match the broadcast size.
345
+ if (ASMJIT_UNLIKELY(commonInfo.hasAvx512B32() && memSize != 4))
346
+ return DebugUtils::errored(kErrorInvalidBroadcast);
347
+
348
+ if (ASMJIT_UNLIKELY(commonInfo.hasAvx512B64() && memSize != 8))
349
+ return DebugUtils::errored(kErrorInvalidBroadcast);
350
+ }
351
+ else {
352
+ // If there is no size we implicitly calculate it so we can validate N in {1toN} properly.
353
+ memSize = commonInfo.hasAvx512B64() ? 8 :
354
+ commonInfo.hasAvx512B32() ? 4 : 2;
355
+ }
356
+
357
+ memSize <<= uint32_t(m.getBroadcast());
358
+ }
359
+
360
+ if (baseType != RegType::kNone && baseType > RegType::kLabelTag) {
361
+ uint32_t baseId = m.baseId();
362
+
363
+ if (m.isRegHome()) {
364
+ // Home address of a virtual register. In such case we don't want to validate the type of the
365
+ // base register as it will always be patched to ESP|RSP.
366
+ }
367
+ else {
368
+ if (ASMJIT_UNLIKELY(!Support::bitTest(vd->allowedMemBaseRegs, baseType)))
369
+ return DebugUtils::errored(kErrorInvalidAddress);
370
+ }
371
+
372
+ // Create information that will be validated only if this is an implicit memory operand. Basically
373
+ // only usable for string instructions and other instructions where memory operand is implicit and
374
+ // has 'seg:[reg]' form.
375
+ if (baseId < Operand::kVirtIdMin) {
376
+ if (ASMJIT_UNLIKELY(baseId >= 32))
377
+ return DebugUtils::errored(kErrorInvalidPhysId);
378
+
379
+ // Physical base id.
380
+ regMask = Support::bitMask(baseId);
381
+ combinedRegMask |= regMask;
382
+ }
383
+ else {
384
+ // Virtual base id - fill the whole mask for implicit mem validation. The register is not assigned
385
+ // yet, so we cannot predict the phys id.
386
+ if (uint32_t(validationFlags & ValidationFlags::kEnableVirtRegs) == 0)
387
+ return DebugUtils::errored(kErrorIllegalVirtReg);
388
+ regMask = 0xFFFFFFFFu;
389
+ }
390
+
391
+ if (indexType == RegType::kNone && !m.offsetLo32())
392
+ opFlags |= InstDB::OpFlags::kFlagMemBase;
393
+ }
394
+ else if (baseType == RegType::kLabelTag) {
395
+ // [Label] - there is no need to validate the base as it's label.
396
+ }
397
+ else {
398
+ // Base is a 64-bit address.
399
+ int64_t offset = m.offset();
400
+ if (!Support::isInt32(offset)) {
401
+ if (mode == InstDB::Mode::kX86) {
402
+ // 32-bit mode: Make sure that the address is either `int32_t` or `uint32_t`.
403
+ if (!Support::isUInt32(offset))
404
+ return DebugUtils::errored(kErrorInvalidAddress64Bit);
405
+ }
406
+ else {
407
+ // 64-bit mode: Zero extension is allowed if the address has 32-bit index register or the address
408
+ // has no index register (it's still encodable).
409
+ if (indexType != RegType::kNone) {
410
+ if (!Support::isUInt32(offset))
411
+ return DebugUtils::errored(kErrorInvalidAddress64Bit);
412
+
413
+ if (indexType != RegType::kX86_Gpd)
414
+ return DebugUtils::errored(kErrorInvalidAddress64BitZeroExtension);
415
+ }
416
+ else {
417
+ // We don't validate absolute 64-bit addresses without an index register as this also depends
418
+ // on the target's base address. We don't have the information to do it at this moment.
419
+ }
420
+ }
421
+ }
422
+ }
423
+
424
+ if (indexType != RegType::kNone) {
425
+ if (ASMJIT_UNLIKELY(!Support::bitTest(vd->allowedMemIndexRegs, indexType)))
426
+ return DebugUtils::errored(kErrorInvalidAddress);
427
+
428
+ if (indexType == RegType::kX86_Xmm) {
429
+ opFlags |= InstDB::OpFlags::kVm32x | InstDB::OpFlags::kVm64x;
430
+ }
431
+ else if (indexType == RegType::kX86_Ymm) {
432
+ opFlags |= InstDB::OpFlags::kVm32y | InstDB::OpFlags::kVm64y;
433
+ }
434
+ else if (indexType == RegType::kX86_Zmm) {
435
+ opFlags |= InstDB::OpFlags::kVm32z | InstDB::OpFlags::kVm64z;
436
+ }
437
+ else {
438
+ if (baseType != RegType::kNone)
439
+ opFlags |= InstDB::OpFlags::kFlagMib;
440
+ }
441
+
442
+ // [RIP + {XMM|YMM|ZMM}] is not allowed.
443
+ if (baseType == RegType::kX86_Rip && Support::test(opFlags, InstDB::OpFlags::kVmMask))
444
+ return DebugUtils::errored(kErrorInvalidAddress);
445
+
446
+ uint32_t indexId = m.indexId();
447
+ if (indexId < Operand::kVirtIdMin) {
448
+ if (ASMJIT_UNLIKELY(indexId >= 32))
449
+ return DebugUtils::errored(kErrorInvalidPhysId);
450
+
451
+ combinedRegMask |= Support::bitMask(indexId);
452
+ }
453
+ else {
454
+ if (uint32_t(validationFlags & ValidationFlags::kEnableVirtRegs) == 0)
455
+ return DebugUtils::errored(kErrorIllegalVirtReg);
456
+ }
457
+
458
+ // Only used for implicit memory operands having 'seg:[reg]' form, so clear it.
459
+ regMask = 0;
460
+ }
461
+
462
+ switch (memSize) {
463
+ case 0: opFlags |= InstDB::OpFlags::kMemUnspecified; break;
464
+ case 1: opFlags |= InstDB::OpFlags::kMem8; break;
465
+ case 2: opFlags |= InstDB::OpFlags::kMem16; break;
466
+ case 4: opFlags |= InstDB::OpFlags::kMem32; break;
467
+ case 6: opFlags |= InstDB::OpFlags::kMem48; break;
468
+ case 8: opFlags |= InstDB::OpFlags::kMem64; break;
469
+ case 10: opFlags |= InstDB::OpFlags::kMem80; break;
470
+ case 16: opFlags |= InstDB::OpFlags::kMem128; break;
471
+ case 32: opFlags |= InstDB::OpFlags::kMem256; break;
472
+ case 64: opFlags |= InstDB::OpFlags::kMem512; break;
473
+
474
+ default:
475
+ return DebugUtils::errored(kErrorInvalidOperandSize);
476
+ }
477
+
478
+ break;
479
+ }
480
+
481
+ case OperandType::kImm: {
482
+ uint64_t immValue = op.as<Imm>().valueAs<uint64_t>();
483
+
484
+ if (int64_t(immValue) >= 0) {
485
+ if (immValue <= 0x7u)
486
+ opFlags = InstDB::OpFlags::kImmI64 | InstDB::OpFlags::kImmU64 | InstDB::OpFlags::kImmI32 | InstDB::OpFlags::kImmU32 |
487
+ InstDB::OpFlags::kImmI16 | InstDB::OpFlags::kImmU16 | InstDB::OpFlags::kImmI8 | InstDB::OpFlags::kImmU8 |
488
+ InstDB::OpFlags::kImmI4 | InstDB::OpFlags::kImmU4 ;
489
+ else if (immValue <= 0xFu)
490
+ opFlags = InstDB::OpFlags::kImmI64 | InstDB::OpFlags::kImmU64 | InstDB::OpFlags::kImmI32 | InstDB::OpFlags::kImmU32 |
491
+ InstDB::OpFlags::kImmI16 | InstDB::OpFlags::kImmU16 | InstDB::OpFlags::kImmI8 | InstDB::OpFlags::kImmU8 |
492
+ InstDB::OpFlags::kImmU4 ;
493
+ else if (immValue <= 0x7Fu)
494
+ opFlags = InstDB::OpFlags::kImmI64 | InstDB::OpFlags::kImmU64 | InstDB::OpFlags::kImmI32 | InstDB::OpFlags::kImmU32 |
495
+ InstDB::OpFlags::kImmI16 | InstDB::OpFlags::kImmU16 | InstDB::OpFlags::kImmI8 | InstDB::OpFlags::kImmU8 ;
496
+ else if (immValue <= 0xFFu)
497
+ opFlags = InstDB::OpFlags::kImmI64 | InstDB::OpFlags::kImmU64 | InstDB::OpFlags::kImmI32 | InstDB::OpFlags::kImmU32 |
498
+ InstDB::OpFlags::kImmI16 | InstDB::OpFlags::kImmU16 | InstDB::OpFlags::kImmU8 ;
499
+ else if (immValue <= 0x7FFFu)
500
+ opFlags = InstDB::OpFlags::kImmI64 | InstDB::OpFlags::kImmU64 | InstDB::OpFlags::kImmI32 | InstDB::OpFlags::kImmU32 |
501
+ InstDB::OpFlags::kImmI16 | InstDB::OpFlags::kImmU16 ;
502
+ else if (immValue <= 0xFFFFu)
503
+ opFlags = InstDB::OpFlags::kImmI64 | InstDB::OpFlags::kImmU64 | InstDB::OpFlags::kImmI32 | InstDB::OpFlags::kImmU32 |
504
+ InstDB::OpFlags::kImmU16 ;
505
+ else if (immValue <= 0x7FFFFFFFu)
506
+ opFlags = InstDB::OpFlags::kImmI64 | InstDB::OpFlags::kImmU64 | InstDB::OpFlags::kImmI32 | InstDB::OpFlags::kImmU32;
507
+ else if (immValue <= 0xFFFFFFFFu)
508
+ opFlags = InstDB::OpFlags::kImmI64 | InstDB::OpFlags::kImmU64 | InstDB::OpFlags::kImmU32;
509
+ else if (immValue <= 0x7FFFFFFFFFFFFFFFu)
510
+ opFlags = InstDB::OpFlags::kImmI64 | InstDB::OpFlags::kImmU64;
511
+ else
512
+ opFlags = InstDB::OpFlags::kImmU64;
513
+ }
514
+ else {
515
+ immValue = Support::neg(immValue);
516
+ if (immValue <= 0x8u)
517
+ opFlags = InstDB::OpFlags::kImmI64 | InstDB::OpFlags::kImmI32 | InstDB::OpFlags::kImmI16 | InstDB::OpFlags::kImmI8 | InstDB::OpFlags::kImmI4;
518
+ else if (immValue <= 0x80u)
519
+ opFlags = InstDB::OpFlags::kImmI64 | InstDB::OpFlags::kImmI32 | InstDB::OpFlags::kImmI16 | InstDB::OpFlags::kImmI8;
520
+ else if (immValue <= 0x8000u)
521
+ opFlags = InstDB::OpFlags::kImmI64 | InstDB::OpFlags::kImmI32 | InstDB::OpFlags::kImmI16;
522
+ else if (immValue <= 0x80000000u)
523
+ opFlags = InstDB::OpFlags::kImmI64 | InstDB::OpFlags::kImmI32;
524
+ else
525
+ opFlags = InstDB::OpFlags::kImmI64;
526
+ }
527
+ break;
528
+ }
529
+
530
+ case OperandType::kLabel: {
531
+ opFlags |= InstDB::OpFlags::kRel8 | InstDB::OpFlags::kRel32;
532
+ break;
533
+ }
534
+
535
+ default:
536
+ return DebugUtils::errored(kErrorInvalidState);
537
+ }
538
+
539
+ InstDB::OpSignature& oSigDst = oSigTranslated[i];
540
+ oSigDst._flags = uint64_t(opFlags) & 0x00FFFFFFFFFFFFFFu;
541
+ oSigDst._regMask = uint8_t(regMask & 0xFFu);
542
+ combinedOpFlags |= opFlags;
543
+ }
544
+
545
+ // Decrease the number of operands of those that are none. This is important as Assembler and Compiler may just pass
546
+ // more operands padded with none (which means that no operand is given at that index). However, validate that there
547
+ // are no gaps (like [reg, none, reg] or [none, reg]).
548
+ if (i < opCount) {
549
+ while (--opCount > i)
550
+ if (ASMJIT_UNLIKELY(!operands[opCount].isNone()))
551
+ return DebugUtils::errored(kErrorInvalidInstruction);
552
+ }
553
+
554
+ // Validate X86 and X64 specific cases.
555
+ if (mode == InstDB::Mode::kX86) {
556
+ // Illegal use of 64-bit register in 32-bit mode.
557
+ if (ASMJIT_UNLIKELY(Support::test(combinedOpFlags, InstDB::OpFlags::kRegGpq)))
558
+ return DebugUtils::errored(kErrorInvalidUseOfGpq);
559
+ }
560
+ else {
561
+ // Illegal use of a high 8-bit register with REX prefix.
562
+ bool hasREX = inst.hasOption(InstOptions::kX86_Rex) || (combinedRegMask & 0xFFFFFF00u) != 0;
563
+ if (ASMJIT_UNLIKELY(hasREX && Support::test(combinedOpFlags, InstDB::OpFlags::kRegGpbHi)))
564
+ return DebugUtils::errored(kErrorInvalidUseOfGpbHi);
565
+ }
566
+
567
+ // Validate Instruction Signature by Comparing Against All `iSig` Rows
568
+ // -------------------------------------------------------------------
569
+
570
+ const InstDB::InstSignature* iSig = InstDB::_instSignatureTable + commonInfo._iSignatureIndex;
571
+ const InstDB::InstSignature* iEnd = iSig + commonInfo._iSignatureCount;
572
+
573
+ if (iSig != iEnd) {
574
+ const InstDB::OpSignature* opSignatureTable = InstDB::_opSignatureTable;
575
+
576
+ // If set it means that we matched a signature where only immediate value
577
+ // was out of bounds. We can return a more descriptive error if we know this.
578
+ bool globalImmOutOfRange = false;
579
+
580
+ do {
581
+ // Check if the architecture is compatible.
582
+ if (!iSig->supportsMode(mode))
583
+ continue;
584
+
585
+ // Compare the operands table with reference operands.
586
+ uint32_t j = 0;
587
+ uint32_t iSigCount = iSig->opCount();
588
+ bool localImmOutOfRange = false;
589
+
590
+ if (iSigCount == opCount) {
591
+ for (j = 0; j < opCount; j++)
592
+ if (!x86CheckOSig(oSigTranslated[j], iSig->opSignature(j), localImmOutOfRange))
593
+ break;
594
+ }
595
+ else if (iSigCount - iSig->implicitOpCount() == opCount) {
596
+ uint32_t r = 0;
597
+ for (j = 0; j < opCount && r < iSigCount; j++, r++) {
598
+ const InstDB::OpSignature* oChk = oSigTranslated + j;
599
+ const InstDB::OpSignature* oRef;
600
+ Next:
601
+ oRef = opSignatureTable + iSig->opSignatureIndex(r);
602
+ // Skip implicit operands.
603
+ if (oRef->isImplicit()) {
604
+ if (++r >= iSigCount)
605
+ break;
606
+ else
607
+ goto Next;
608
+ }
609
+
610
+ if (!x86CheckOSig(*oChk, *oRef, localImmOutOfRange))
611
+ break;
612
+ }
613
+ }
614
+
615
+ if (j == opCount) {
616
+ if (!localImmOutOfRange) {
617
+ // Match, must clear possible `globalImmOutOfRange`.
618
+ globalImmOutOfRange = false;
619
+ break;
620
+ }
621
+ globalImmOutOfRange = localImmOutOfRange;
622
+ }
623
+ } while (++iSig != iEnd);
624
+
625
+ if (iSig == iEnd) {
626
+ if (globalImmOutOfRange)
627
+ return DebugUtils::errored(kErrorInvalidImmediate);
628
+ else
629
+ return DebugUtils::errored(kErrorInvalidInstruction);
630
+ }
631
+ }
632
+
633
+ // Validate AVX512 Options
634
+ // -----------------------
635
+
636
+ const RegOnly& extraReg = inst.extraReg();
637
+
638
+ if (Support::test(options, kAvx512Options)) {
639
+ if (commonInfo.hasFlag(InstDB::InstFlags::kEvex)) {
640
+ // Validate AVX-512 {z}.
641
+ if (Support::test(options, InstOptions::kX86_ZMask)) {
642
+ if (ASMJIT_UNLIKELY(Support::test(options, InstOptions::kX86_ZMask) && !commonInfo.hasAvx512Z()))
643
+ return DebugUtils::errored(kErrorInvalidKZeroUse);
644
+ }
645
+
646
+ // Validate AVX-512 {sae} and {er}.
647
+ if (Support::test(options, InstOptions::kX86_SAE | InstOptions::kX86_ER)) {
648
+ // Rounding control is impossible if the instruction is not reg-to-reg.
649
+ if (ASMJIT_UNLIKELY(memOp))
650
+ return DebugUtils::errored(kErrorInvalidEROrSAE);
651
+
652
+ // Check if {sae} or {er} is supported by the instruction.
653
+ if (Support::test(options, InstOptions::kX86_ER)) {
654
+ // NOTE: if both {sae} and {er} are set, we don't care, as {sae} is implied.
655
+ if (ASMJIT_UNLIKELY(!commonInfo.hasAvx512ER()))
656
+ return DebugUtils::errored(kErrorInvalidEROrSAE);
657
+ }
658
+ else {
659
+ if (ASMJIT_UNLIKELY(!commonInfo.hasAvx512SAE()))
660
+ return DebugUtils::errored(kErrorInvalidEROrSAE);
661
+ }
662
+
663
+ // {sae} and {er} are defined for either scalar ops or vector ops that require LL to be 10 (512-bit vector
664
+ // operations). We don't need any more bits in the instruction database to be able to validate this, as
665
+ // each AVX512 instruction that has broadcast is vector instruction (in this case we require zmm registers),
666
+ // otherwise it's a scalar instruction, which is valid.
667
+ if (commonInfo.hasAvx512B()) {
668
+ // Supports broadcast, thus we require LL to be '10', which means there have to be ZMM registers used. We
669
+ // don't calculate LL here, but we know that it would be '10' if there is at least one ZMM register used.
670
+
671
+ // There is no {er}/{sae}-enabled instruction with less than two operands.
672
+ ASMJIT_ASSERT(opCount >= 2);
673
+ if (ASMJIT_UNLIKELY(!x86IsZmmOrM512(operands[0]) && !x86IsZmmOrM512(operands[1])))
674
+ return DebugUtils::errored(kErrorInvalidEROrSAE);
675
+ }
676
+ }
677
+ }
678
+ else {
679
+ // Not an AVX512 instruction - maybe OpExtra is xCX register used by REP/REPNE prefix.
680
+ if (Support::test(options, kAvx512Options) || !Support::test(options, kRepAny))
681
+ return DebugUtils::errored(kErrorInvalidInstruction);
682
+ }
683
+ }
684
+
685
+ // Validate {Extra} Register
686
+ // -------------------------
687
+
688
+ if (extraReg.isReg()) {
689
+ if (Support::test(options, kRepAny)) {
690
+ // Validate REP|REPNE {cx|ecx|rcx}.
691
+ if (ASMJIT_UNLIKELY(Support::test(iFlags, InstDB::InstFlags::kRepIgnored)))
692
+ return DebugUtils::errored(kErrorInvalidExtraReg);
693
+
694
+ if (extraReg.isPhysReg()) {
695
+ if (ASMJIT_UNLIKELY(extraReg.id() != Gp::kIdCx))
696
+ return DebugUtils::errored(kErrorInvalidExtraReg);
697
+ }
698
+
699
+ // The type of the {...} register must match the type of the base register
700
+ // of memory operand. So if the memory operand uses 32-bit register the
701
+ // count register must also be 32-bit, etc...
702
+ if (ASMJIT_UNLIKELY(!memOp || extraReg.type() != memOp->baseType()))
703
+ return DebugUtils::errored(kErrorInvalidExtraReg);
704
+ }
705
+ else if (commonInfo.hasFlag(InstDB::InstFlags::kEvex)) {
706
+ // Validate AVX-512 {k}.
707
+ if (ASMJIT_UNLIKELY(extraReg.type() != RegType::kX86_KReg))
708
+ return DebugUtils::errored(kErrorInvalidExtraReg);
709
+
710
+ if (ASMJIT_UNLIKELY(extraReg.id() == 0 || !commonInfo.hasAvx512K()))
711
+ return DebugUtils::errored(kErrorInvalidKMaskUse);
712
+ }
713
+ else {
714
+ return DebugUtils::errored(kErrorInvalidExtraReg);
715
+ }
716
+ }
717
+
718
+ return kErrorOk;
719
+ }
720
+ #endif // !ASMJIT_NO_VALIDATION
721
+
722
+ // x86::InstInternal - QueryRWInfo
723
+ // ===============================
724
+
725
+ #ifndef ASMJIT_NO_INTROSPECTION
726
+ static const Support::Array<uint64_t, uint32_t(RegGroup::kMaxValue) + 1> rwRegGroupByteMask = {{
727
+ 0x00000000000000FFu, // GP.
728
+ 0xFFFFFFFFFFFFFFFFu, // XMM|YMM|ZMM.
729
+ 0x00000000000000FFu, // MM.
730
+ 0x00000000000000FFu, // KReg.
731
+ 0x0000000000000003u, // SReg.
732
+ 0x00000000000000FFu, // CReg.
733
+ 0x00000000000000FFu, // DReg.
734
+ 0x00000000000003FFu, // St().
735
+ 0x000000000000FFFFu, // BND.
736
+ 0x00000000000000FFu // RIP.
737
+ }};
738
+
739
+ static ASMJIT_FORCE_INLINE void rwZeroExtendGp(OpRWInfo& opRwInfo, const Gp& reg, uint32_t nativeGpSize) noexcept {
740
+ ASMJIT_ASSERT(BaseReg::isGp(reg.as<Operand>()));
741
+ if (reg.size() + 4 == nativeGpSize) {
742
+ opRwInfo.addOpFlags(OpRWFlags::kZExt);
743
+ opRwInfo.setExtendByteMask(~opRwInfo.writeByteMask() & 0xFFu);
744
+ }
745
+ }
746
+
747
+ static ASMJIT_FORCE_INLINE void rwZeroExtendAvxVec(OpRWInfo& opRwInfo, const Vec& reg) noexcept {
748
+ DebugUtils::unused(reg);
749
+
750
+ uint64_t msk = ~Support::fillTrailingBits(opRwInfo.writeByteMask());
751
+ if (msk) {
752
+ opRwInfo.addOpFlags(OpRWFlags::kZExt);
753
+ opRwInfo.setExtendByteMask(msk);
754
+ }
755
+ }
756
+
757
+ static ASMJIT_FORCE_INLINE void rwZeroExtendNonVec(OpRWInfo& opRwInfo, const Reg& reg) noexcept {
758
+ uint64_t msk = ~Support::fillTrailingBits(opRwInfo.writeByteMask()) & rwRegGroupByteMask[reg.group()];
759
+ if (msk) {
760
+ opRwInfo.addOpFlags(OpRWFlags::kZExt);
761
+ opRwInfo.setExtendByteMask(msk);
762
+ }
763
+ }
764
+
765
+ static ASMJIT_FORCE_INLINE Error rwHandleAVX512(const BaseInst& inst, const InstDB::CommonInfo& commonInfo, InstRWInfo* out) noexcept {
766
+ if (inst.hasExtraReg() && inst.extraReg().type() == RegType::kX86_KReg && out->opCount() > 0) {
767
+ // AVX-512 instruction that uses a destination with {k} register (zeroing vs masking).
768
+ out->_extraReg.addOpFlags(OpRWFlags::kRead);
769
+ out->_extraReg.setReadByteMask(0xFF);
770
+ if (!inst.hasOption(InstOptions::kX86_ZMask) && !commonInfo.hasAvx512Flag(InstDB::Avx512Flags::kImplicitZ)) {
771
+ out->_operands[0].addOpFlags(OpRWFlags::kRead);
772
+ out->_operands[0]._readByteMask |= out->_operands[0]._writeByteMask;
773
+ }
774
+ }
775
+
776
+ return kErrorOk;
777
+ }
778
+
779
+ static ASMJIT_FORCE_INLINE bool hasSameRegType(const BaseReg* regs, size_t opCount) noexcept {
780
+ ASMJIT_ASSERT(opCount > 0);
781
+ RegType regType = regs[0].type();
782
+ for (size_t i = 1; i < opCount; i++)
783
+ if (regs[i].type() != regType)
784
+ return false;
785
+ return true;
786
+ }
787
+
788
+ Error InstInternal::queryRWInfo(Arch arch, const BaseInst& inst, const Operand_* operands, size_t opCount, InstRWInfo* out) noexcept {
789
+ // Only called when `arch` matches X86 family.
790
+ ASMJIT_ASSERT(Environment::isFamilyX86(arch));
791
+
792
+ // Get the instruction data.
793
+ InstId instId = inst.id();
794
+ if (ASMJIT_UNLIKELY(!Inst::isDefinedId(instId)))
795
+ return DebugUtils::errored(kErrorInvalidInstruction);
796
+
797
+ // Read/Write flags.
798
+ const InstDB::InstInfo& instInfo = InstDB::_instInfoTable[instId];
799
+ const InstDB::CommonInfo& commonInfo = InstDB::_commonInfoTable[instInfo._commonInfoIndex];
800
+ const InstDB::AdditionalInfo& additionalInfo = InstDB::_additionalInfoTable[instInfo._additionalInfoIndex];
801
+ const InstDB::RWFlagsInfoTable& rwFlags = InstDB::_rwFlagsInfoTable[additionalInfo._rwFlagsIndex];
802
+
803
+ // There are two data tables, one for `opCount == 2` and the second for
804
+ // `opCount != 2`. There are two reasons for that:
805
+ // - There are instructions that share the same name that have both 2 or 3 operands, which have different
806
+ // RW information / semantics.
807
+ // - There must be 2 tables otherwise the lookup index won't fit into 8 bits (there is more than 256 records
808
+ // of combined rwInfo A and B).
809
+ const InstDB::RWInfo& instRwInfo = opCount == 2 ? InstDB::rwInfoA[InstDB::rwInfoIndexA[instId]]
810
+ : InstDB::rwInfoB[InstDB::rwInfoIndexB[instId]];
811
+ const InstDB::RWInfoRm& instRmInfo = InstDB::rwInfoRm[instRwInfo.rmInfo];
812
+
813
+ out->_instFlags = InstDB::_instFlagsTable[additionalInfo._instFlagsIndex];
814
+ out->_opCount = uint8_t(opCount);
815
+ out->_rmFeature = instRmInfo.rmFeature;
816
+ out->_extraReg.reset();
817
+ out->_readFlags = CpuRWFlags(rwFlags.readFlags);
818
+ out->_writeFlags = CpuRWFlags(rwFlags.writeFlags);
819
+
820
+ uint32_t opTypeMask = 0u;
821
+ uint32_t nativeGpSize = Environment::registerSizeFromArch(arch);
822
+
823
+ constexpr OpRWFlags R = OpRWFlags::kRead;
824
+ constexpr OpRWFlags W = OpRWFlags::kWrite;
825
+ constexpr OpRWFlags X = OpRWFlags::kRW;
826
+ constexpr OpRWFlags RegM = OpRWFlags::kRegMem;
827
+ constexpr OpRWFlags RegPhys = OpRWFlags::kRegPhysId;
828
+ constexpr OpRWFlags MibRead = OpRWFlags::kMemBaseRead | OpRWFlags::kMemIndexRead;
829
+
830
+ if (instRwInfo.category == InstDB::RWInfo::kCategoryGeneric) {
831
+ uint32_t i;
832
+ uint32_t rmOpsMask = 0;
833
+ uint32_t rmMaxSize = 0;
834
+
835
+ for (i = 0; i < opCount; i++) {
836
+ OpRWInfo& op = out->_operands[i];
837
+ const Operand_& srcOp = operands[i];
838
+ const InstDB::RWInfoOp& rwOpData = InstDB::rwInfoOp[instRwInfo.opInfoIndex[i]];
839
+
840
+ opTypeMask |= Support::bitMask(srcOp.opType());
841
+
842
+ if (!srcOp.isRegOrMem()) {
843
+ op.reset();
844
+ continue;
845
+ }
846
+
847
+ op._opFlags = rwOpData.flags & ~OpRWFlags::kZExt;
848
+ op._physId = rwOpData.physId;
849
+ op._rmSize = 0;
850
+ op._resetReserved();
851
+
852
+ uint64_t rByteMask = rwOpData.rByteMask;
853
+ uint64_t wByteMask = rwOpData.wByteMask;
854
+
855
+ if (op.isRead() && !rByteMask) rByteMask = Support::lsbMask<uint64_t>(srcOp.size());
856
+ if (op.isWrite() && !wByteMask) wByteMask = Support::lsbMask<uint64_t>(srcOp.size());
857
+
858
+ op._readByteMask = rByteMask;
859
+ op._writeByteMask = wByteMask;
860
+ op._extendByteMask = 0;
861
+ op._consecutiveLeadCount = rwOpData.consecutiveLeadCount;
862
+
863
+ if (srcOp.isReg()) {
864
+ // Zero extension.
865
+ if (op.isWrite()) {
866
+ if (srcOp.as<Reg>().isGp()) {
867
+ // GP registers on X64 are special:
868
+ // - 8-bit and 16-bit writes aren't zero extended.
869
+ // - 32-bit writes ARE zero extended.
870
+ rwZeroExtendGp(op, srcOp.as<Gp>(), nativeGpSize);
871
+ }
872
+ else if (Support::test(rwOpData.flags, OpRWFlags::kZExt)) {
873
+ // Otherwise follow ZExt.
874
+ rwZeroExtendNonVec(op, srcOp.as<Gp>());
875
+ }
876
+ }
877
+
878
+ // Aggregate values required to calculate valid Reg/M info.
879
+ rmMaxSize = Support::max(rmMaxSize, srcOp.size());
880
+ rmOpsMask |= Support::bitMask<uint32_t>(i);
881
+ }
882
+ else {
883
+ const x86::Mem& memOp = srcOp.as<x86::Mem>();
884
+ // The RW flags of BASE+INDEX are either provided by the data, which means
885
+ // that the instruction is border-case, or they are deduced from the operand.
886
+ if (memOp.hasBaseReg() && !op.hasOpFlag(OpRWFlags::kMemBaseRW))
887
+ op.addOpFlags(OpRWFlags::kMemBaseRead);
888
+ if (memOp.hasIndexReg() && !op.hasOpFlag(OpRWFlags::kMemIndexRW))
889
+ op.addOpFlags(OpRWFlags::kMemIndexRead);
890
+ }
891
+ }
892
+
893
+ // Only keep kMovOp if the instruction is actually register to register move of the same kind.
894
+ if (out->hasInstFlag(InstRWFlags::kMovOp)) {
895
+ if (!(opCount >= 2 && opTypeMask == Support::bitMask(OperandType::kReg) && hasSameRegType(reinterpret_cast<const BaseReg*>(operands), opCount)))
896
+ out->_instFlags &= ~InstRWFlags::kMovOp;
897
+ }
898
+
899
+ // Special cases require more logic.
900
+ if (instRmInfo.flags & (InstDB::RWInfoRm::kFlagMovssMovsd | InstDB::RWInfoRm::kFlagPextrw | InstDB::RWInfoRm::kFlagFeatureIfRMI)) {
901
+ if (instRmInfo.flags & InstDB::RWInfoRm::kFlagMovssMovsd) {
902
+ if (opCount == 2) {
903
+ if (operands[0].isReg() && operands[1].isReg()) {
904
+ // Doesn't zero extend the destination.
905
+ out->_operands[0]._extendByteMask = 0;
906
+ }
907
+ }
908
+ }
909
+ else if (instRmInfo.flags & InstDB::RWInfoRm::kFlagPextrw) {
910
+ if (opCount == 3 && Reg::isMm(operands[1])) {
911
+ out->_rmFeature = 0;
912
+ rmOpsMask = 0;
913
+ }
914
+ }
915
+ else if (instRmInfo.flags & InstDB::RWInfoRm::kFlagFeatureIfRMI) {
916
+ if (opCount != 3 || !operands[2].isImm()) {
917
+ out->_rmFeature = 0;
918
+ }
919
+ }
920
+ }
921
+
922
+ rmOpsMask &= instRmInfo.rmOpsMask;
923
+ if (rmOpsMask) {
924
+ Support::BitWordIterator<uint32_t> it(rmOpsMask);
925
+ do {
926
+ i = it.next();
927
+
928
+ OpRWInfo& op = out->_operands[i];
929
+ op.addOpFlags(RegM);
930
+
931
+ switch (instRmInfo.category) {
932
+ case InstDB::RWInfoRm::kCategoryFixed:
933
+ op.setRmSize(instRmInfo.fixedSize);
934
+ break;
935
+ case InstDB::RWInfoRm::kCategoryConsistent:
936
+ op.setRmSize(operands[i].size());
937
+ break;
938
+ case InstDB::RWInfoRm::kCategoryHalf:
939
+ op.setRmSize(rmMaxSize / 2u);
940
+ break;
941
+ case InstDB::RWInfoRm::kCategoryQuarter:
942
+ op.setRmSize(rmMaxSize / 4u);
943
+ break;
944
+ case InstDB::RWInfoRm::kCategoryEighth:
945
+ op.setRmSize(rmMaxSize / 8u);
946
+ break;
947
+ }
948
+ } while (it.hasNext());
949
+ }
950
+
951
+ return rwHandleAVX512(inst, commonInfo, out);
952
+ }
953
+
954
+ switch (instRwInfo.category) {
955
+ case InstDB::RWInfo::kCategoryMov: {
956
+ // Special case for 'mov' instruction. Here there are some variants that we have to handle as 'mov' can be
957
+ // used to move between GP, segment, control and debug registers. Moving between GP registers also allow to
958
+ // use memory operand.
959
+
960
+ // We will again set the flag if it's actually a move from GP to GP register, otherwise this flag cannot be set.
961
+ out->_instFlags &= ~InstRWFlags::kMovOp;
962
+
963
+ if (opCount == 2) {
964
+ if (operands[0].isReg() && operands[1].isReg()) {
965
+ const Reg& o0 = operands[0].as<Reg>();
966
+ const Reg& o1 = operands[1].as<Reg>();
967
+
968
+ if (o0.isGp() && o1.isGp()) {
969
+ out->_operands[0].reset(W | RegM, operands[0].size());
970
+ out->_operands[1].reset(R | RegM, operands[1].size());
971
+
972
+ rwZeroExtendGp(out->_operands[0], operands[0].as<Gp>(), nativeGpSize);
973
+ out->_instFlags |= InstRWFlags::kMovOp;
974
+ return kErrorOk;
975
+ }
976
+
977
+ if (o0.isGp() && o1.isSReg()) {
978
+ out->_operands[0].reset(W | RegM, nativeGpSize);
979
+ out->_operands[0].setRmSize(2);
980
+ out->_operands[1].reset(R, 2);
981
+ return kErrorOk;
982
+ }
983
+
984
+ if (o0.isSReg() && o1.isGp()) {
985
+ out->_operands[0].reset(W, 2);
986
+ out->_operands[1].reset(R | RegM, 2);
987
+ out->_operands[1].setRmSize(2);
988
+ return kErrorOk;
989
+ }
990
+
991
+ if (o0.isGp() && (o1.isCReg() || o1.isDReg())) {
992
+ out->_operands[0].reset(W, nativeGpSize);
993
+ out->_operands[1].reset(R, nativeGpSize);
994
+ out->_writeFlags = CpuRWFlags::kX86_OF |
995
+ CpuRWFlags::kX86_SF |
996
+ CpuRWFlags::kX86_ZF |
997
+ CpuRWFlags::kX86_AF |
998
+ CpuRWFlags::kX86_PF |
999
+ CpuRWFlags::kX86_CF;
1000
+ return kErrorOk;
1001
+ }
1002
+
1003
+ if ((o0.isCReg() || o0.isDReg()) && o1.isGp()) {
1004
+ out->_operands[0].reset(W, nativeGpSize);
1005
+ out->_operands[1].reset(R, nativeGpSize);
1006
+ out->_writeFlags = CpuRWFlags::kX86_OF |
1007
+ CpuRWFlags::kX86_SF |
1008
+ CpuRWFlags::kX86_ZF |
1009
+ CpuRWFlags::kX86_AF |
1010
+ CpuRWFlags::kX86_PF |
1011
+ CpuRWFlags::kX86_CF;
1012
+ return kErrorOk;
1013
+ }
1014
+ }
1015
+
1016
+ if (operands[0].isReg() && operands[1].isMem()) {
1017
+ const Reg& o0 = operands[0].as<Reg>();
1018
+ const Mem& o1 = operands[1].as<Mem>();
1019
+
1020
+ if (o0.isGp()) {
1021
+ if (!o1.isOffset64Bit())
1022
+ out->_operands[0].reset(W, o0.size());
1023
+ else
1024
+ out->_operands[0].reset(W | RegPhys, o0.size(), Gp::kIdAx);
1025
+
1026
+ out->_operands[1].reset(R | MibRead, o0.size());
1027
+ rwZeroExtendGp(out->_operands[0], operands[0].as<Gp>(), nativeGpSize);
1028
+ return kErrorOk;
1029
+ }
1030
+
1031
+ if (o0.isSReg()) {
1032
+ out->_operands[0].reset(W, 2);
1033
+ out->_operands[1].reset(R, 2);
1034
+ return kErrorOk;
1035
+ }
1036
+ }
1037
+
1038
+ if (operands[0].isMem() && operands[1].isReg()) {
1039
+ const Mem& o0 = operands[0].as<Mem>();
1040
+ const Reg& o1 = operands[1].as<Reg>();
1041
+
1042
+ if (o1.isGp()) {
1043
+ out->_operands[0].reset(W | MibRead, o1.size());
1044
+ if (!o0.isOffset64Bit())
1045
+ out->_operands[1].reset(R, o1.size());
1046
+ else
1047
+ out->_operands[1].reset(R | RegPhys, o1.size(), Gp::kIdAx);
1048
+ return kErrorOk;
1049
+ }
1050
+
1051
+ if (o1.isSReg()) {
1052
+ out->_operands[0].reset(W | MibRead, 2);
1053
+ out->_operands[1].reset(R, 2);
1054
+ return kErrorOk;
1055
+ }
1056
+ }
1057
+
1058
+ if (Reg::isGp(operands[0]) && operands[1].isImm()) {
1059
+ const Reg& o0 = operands[0].as<Reg>();
1060
+ out->_operands[0].reset(W | RegM, o0.size());
1061
+ out->_operands[1].reset();
1062
+
1063
+ rwZeroExtendGp(out->_operands[0], operands[0].as<Gp>(), nativeGpSize);
1064
+ return kErrorOk;
1065
+ }
1066
+
1067
+ if (operands[0].isMem() && operands[1].isImm()) {
1068
+ const Reg& o0 = operands[0].as<Reg>();
1069
+ out->_operands[0].reset(W | MibRead, o0.size());
1070
+ out->_operands[1].reset();
1071
+ return kErrorOk;
1072
+ }
1073
+ }
1074
+ break;
1075
+ }
1076
+
1077
+ case InstDB::RWInfo::kCategoryMovabs: {
1078
+ if (opCount == 2) {
1079
+ if (Reg::isGp(operands[0]) && operands[1].isMem()) {
1080
+ const Reg& o0 = operands[0].as<Reg>();
1081
+ out->_operands[0].reset(W | RegPhys, o0.size(), Gp::kIdAx);
1082
+ out->_operands[1].reset(R | MibRead, o0.size());
1083
+ rwZeroExtendGp(out->_operands[0], operands[0].as<Gp>(), nativeGpSize);
1084
+ return kErrorOk;
1085
+ }
1086
+
1087
+ if (operands[0].isMem() && Reg::isGp(operands[1])) {
1088
+ const Reg& o1 = operands[1].as<Reg>();
1089
+ out->_operands[0].reset(W | MibRead, o1.size());
1090
+ out->_operands[1].reset(R | RegPhys, o1.size(), Gp::kIdAx);
1091
+ return kErrorOk;
1092
+ }
1093
+
1094
+ if (Reg::isGp(operands[0]) && operands[1].isImm()) {
1095
+ const Reg& o0 = operands[0].as<Reg>();
1096
+ out->_operands[0].reset(W, o0.size());
1097
+ out->_operands[1].reset();
1098
+
1099
+ rwZeroExtendGp(out->_operands[0], operands[0].as<Gp>(), nativeGpSize);
1100
+ return kErrorOk;
1101
+ }
1102
+ }
1103
+ break;
1104
+ }
1105
+
1106
+ case InstDB::RWInfo::kCategoryImul: {
1107
+ // Special case for 'imul' instruction.
1108
+ //
1109
+ // There are 3 variants in general:
1110
+ //
1111
+ // 1. Standard multiplication: 'A = A * B'.
1112
+ // 2. Multiplication with imm: 'A = B * C'.
1113
+ // 3. Extended multiplication: 'A:B = B * C'.
1114
+
1115
+ if (opCount == 2) {
1116
+ if (operands[0].isReg() && operands[1].isImm()) {
1117
+ out->_operands[0].reset(X, operands[0].size());
1118
+ out->_operands[1].reset();
1119
+
1120
+ rwZeroExtendGp(out->_operands[0], operands[0].as<Gp>(), nativeGpSize);
1121
+ return kErrorOk;
1122
+ }
1123
+
1124
+ if (Reg::isGpw(operands[0]) && operands[1].size() == 1) {
1125
+ // imul ax, r8/m8 <- AX = AL * r8/m8
1126
+ out->_operands[0].reset(X | RegPhys, 2, Gp::kIdAx);
1127
+ out->_operands[0].setReadByteMask(Support::lsbMask<uint64_t>(1));
1128
+ out->_operands[1].reset(R | RegM, 1);
1129
+ }
1130
+ else {
1131
+ // imul r?, r?/m?
1132
+ out->_operands[0].reset(X, operands[0].size());
1133
+ out->_operands[1].reset(R | RegM, operands[0].size());
1134
+ rwZeroExtendGp(out->_operands[0], operands[0].as<Gp>(), nativeGpSize);
1135
+ }
1136
+
1137
+ if (operands[1].isMem())
1138
+ out->_operands[1].addOpFlags(MibRead);
1139
+ return kErrorOk;
1140
+ }
1141
+
1142
+ if (opCount == 3) {
1143
+ if (operands[2].isImm()) {
1144
+ out->_operands[0].reset(W, operands[0].size());
1145
+ out->_operands[1].reset(R | RegM, operands[1].size());
1146
+ out->_operands[2].reset();
1147
+
1148
+ rwZeroExtendGp(out->_operands[0], operands[0].as<Gp>(), nativeGpSize);
1149
+ if (operands[1].isMem())
1150
+ out->_operands[1].addOpFlags(MibRead);
1151
+ return kErrorOk;
1152
+ }
1153
+ else {
1154
+ out->_operands[0].reset(W | RegPhys, operands[0].size(), Gp::kIdDx);
1155
+ out->_operands[1].reset(X | RegPhys, operands[1].size(), Gp::kIdAx);
1156
+ out->_operands[2].reset(R | RegM, operands[2].size());
1157
+
1158
+ rwZeroExtendGp(out->_operands[0], operands[0].as<Gp>(), nativeGpSize);
1159
+ rwZeroExtendGp(out->_operands[1], operands[1].as<Gp>(), nativeGpSize);
1160
+ if (operands[2].isMem())
1161
+ out->_operands[2].addOpFlags(MibRead);
1162
+ return kErrorOk;
1163
+ }
1164
+ }
1165
+ break;
1166
+ }
1167
+
1168
+ case InstDB::RWInfo::kCategoryMovh64: {
1169
+ // Special case for 'movhpd|movhps' instructions. Note that this is only required for legacy (non-AVX)
1170
+ // variants as AVX instructions use either 2 or 3 operands that are in `kCategoryGeneric` category.
1171
+ if (opCount == 2) {
1172
+ if (BaseReg::isVec(operands[0]) && operands[1].isMem()) {
1173
+ out->_operands[0].reset(W, 8);
1174
+ out->_operands[0].setWriteByteMask(Support::lsbMask<uint64_t>(8) << 8);
1175
+ out->_operands[1].reset(R | MibRead, 8);
1176
+ return kErrorOk;
1177
+ }
1178
+
1179
+ if (operands[0].isMem() && BaseReg::isVec(operands[1])) {
1180
+ out->_operands[0].reset(W | MibRead, 8);
1181
+ out->_operands[1].reset(R, 8);
1182
+ out->_operands[1].setReadByteMask(Support::lsbMask<uint64_t>(8) << 8);
1183
+ return kErrorOk;
1184
+ }
1185
+ }
1186
+ break;
1187
+ }
1188
+
1189
+ case InstDB::RWInfo::kCategoryPunpcklxx: {
1190
+ // Special case for 'punpcklbw|punpckldq|punpcklwd' instructions.
1191
+ if (opCount == 2) {
1192
+ if (Reg::isXmm(operands[0])) {
1193
+ out->_operands[0].reset(X, 16);
1194
+ out->_operands[0].setReadByteMask(0x0F0Fu);
1195
+ out->_operands[0].setWriteByteMask(0xFFFFu);
1196
+ out->_operands[1].reset(R, 16);
1197
+ out->_operands[1].setWriteByteMask(0x0F0Fu);
1198
+
1199
+ if (Reg::isXmm(operands[1])) {
1200
+ return kErrorOk;
1201
+ }
1202
+
1203
+ if (operands[1].isMem()) {
1204
+ out->_operands[1].addOpFlags(MibRead);
1205
+ return kErrorOk;
1206
+ }
1207
+ }
1208
+
1209
+ if (Reg::isMm(operands[0])) {
1210
+ out->_operands[0].reset(X, 8);
1211
+ out->_operands[0].setReadByteMask(0x0Fu);
1212
+ out->_operands[0].setWriteByteMask(0xFFu);
1213
+ out->_operands[1].reset(R, 4);
1214
+ out->_operands[1].setReadByteMask(0x0Fu);
1215
+
1216
+ if (Reg::isMm(operands[1])) {
1217
+ return kErrorOk;
1218
+ }
1219
+
1220
+ if (operands[1].isMem()) {
1221
+ out->_operands[1].addOpFlags(MibRead);
1222
+ return kErrorOk;
1223
+ }
1224
+ }
1225
+ }
1226
+ break;
1227
+ }
1228
+
1229
+ case InstDB::RWInfo::kCategoryVmaskmov: {
1230
+ // Special case for 'vmaskmovpd|vmaskmovps|vpmaskmovd|vpmaskmovq' instructions.
1231
+ if (opCount == 3) {
1232
+ if (BaseReg::isVec(operands[0]) && BaseReg::isVec(operands[1]) && operands[2].isMem()) {
1233
+ out->_operands[0].reset(W, operands[0].size());
1234
+ out->_operands[1].reset(R, operands[1].size());
1235
+ out->_operands[2].reset(R | MibRead, operands[1].size());
1236
+
1237
+ rwZeroExtendAvxVec(out->_operands[0], operands[0].as<Vec>());
1238
+ return kErrorOk;
1239
+ }
1240
+
1241
+ if (operands[0].isMem() && BaseReg::isVec(operands[1]) && BaseReg::isVec(operands[2])) {
1242
+ out->_operands[0].reset(X | MibRead, operands[1].size());
1243
+ out->_operands[1].reset(R, operands[1].size());
1244
+ out->_operands[2].reset(R, operands[2].size());
1245
+ return kErrorOk;
1246
+ }
1247
+ }
1248
+ break;
1249
+ }
1250
+
1251
+ case InstDB::RWInfo::kCategoryVmovddup: {
1252
+ // Special case for 'vmovddup' instruction. This instruction has an interesting semantic as 128-bit XMM
1253
+ // version only uses 64-bit memory operand (m64), however, 256/512-bit versions use 256/512-bit memory
1254
+ // operand, respectively.
1255
+ if (opCount == 2) {
1256
+ if (BaseReg::isVec(operands[0]) && BaseReg::isVec(operands[1])) {
1257
+ uint32_t o0Size = operands[0].size();
1258
+ uint32_t o1Size = o0Size == 16 ? 8 : o0Size;
1259
+
1260
+ out->_operands[0].reset(W, o0Size);
1261
+ out->_operands[1].reset(R | RegM, o1Size);
1262
+ out->_operands[1]._readByteMask &= 0x00FF00FF00FF00FFu;
1263
+
1264
+ rwZeroExtendAvxVec(out->_operands[0], operands[0].as<Vec>());
1265
+ return rwHandleAVX512(inst, commonInfo, out);
1266
+ }
1267
+
1268
+ if (BaseReg::isVec(operands[0]) && operands[1].isMem()) {
1269
+ uint32_t o0Size = operands[0].size();
1270
+ uint32_t o1Size = o0Size == 16 ? 8 : o0Size;
1271
+
1272
+ out->_operands[0].reset(W, o0Size);
1273
+ out->_operands[1].reset(R | MibRead, o1Size);
1274
+
1275
+ rwZeroExtendAvxVec(out->_operands[0], operands[0].as<Vec>());
1276
+ return rwHandleAVX512(inst, commonInfo, out);
1277
+ }
1278
+ }
1279
+ break;
1280
+ }
1281
+
1282
+ case InstDB::RWInfo::kCategoryVmovmskpd:
1283
+ case InstDB::RWInfo::kCategoryVmovmskps: {
1284
+ // Special case for 'vmovmskpd|vmovmskps' instructions.
1285
+ if (opCount == 2) {
1286
+ if (BaseReg::isGp(operands[0]) && BaseReg::isVec(operands[1])) {
1287
+ out->_operands[0].reset(W, 1);
1288
+ out->_operands[0].setExtendByteMask(Support::lsbMask<uint32_t>(nativeGpSize - 1) << 1);
1289
+ out->_operands[1].reset(R, operands[1].size());
1290
+ return kErrorOk;
1291
+ }
1292
+ }
1293
+ break;
1294
+ }
1295
+
1296
+ case InstDB::RWInfo::kCategoryVmov1_2:
1297
+ case InstDB::RWInfo::kCategoryVmov1_4:
1298
+ case InstDB::RWInfo::kCategoryVmov1_8: {
1299
+ // Special case for instructions where the destination is 1:N (narrowing).
1300
+ //
1301
+ // Vmov1_2:
1302
+ // vcvtpd2dq|vcvttpd2dq
1303
+ // vcvtpd2udq|vcvttpd2udq
1304
+ // vcvtpd2ps|vcvtps2ph
1305
+ // vcvtqq2ps|vcvtuqq2ps
1306
+ // vpmovwb|vpmovswb|vpmovuswb
1307
+ // vpmovdw|vpmovsdw|vpmovusdw
1308
+ // vpmovqd|vpmovsqd|vpmovusqd
1309
+ //
1310
+ // Vmov1_4:
1311
+ // vpmovdb|vpmovsdb|vpmovusdb
1312
+ // vpmovqw|vpmovsqw|vpmovusqw
1313
+ //
1314
+ // Vmov1_8:
1315
+ // pmovmskb|vpmovmskb
1316
+ // vpmovqb|vpmovsqb|vpmovusqb
1317
+ uint32_t shift = instRwInfo.category - InstDB::RWInfo::kCategoryVmov1_2 + 1;
1318
+
1319
+ if (opCount >= 2) {
1320
+ if (opCount >= 3) {
1321
+ if (opCount > 3)
1322
+ return DebugUtils::errored(kErrorInvalidInstruction);
1323
+ out->_operands[2].reset();
1324
+ }
1325
+
1326
+ if (operands[0].isReg() && operands[1].isReg()) {
1327
+ uint32_t size1 = operands[1].size();
1328
+ uint32_t size0 = size1 >> shift;
1329
+
1330
+ out->_operands[0].reset(W, size0);
1331
+ out->_operands[1].reset(R, size1);
1332
+
1333
+ if (instRmInfo.rmOpsMask & 0x1) {
1334
+ out->_operands[0].addOpFlags(RegM);
1335
+ out->_operands[0].setRmSize(size0);
1336
+ }
1337
+
1338
+ if (instRmInfo.rmOpsMask & 0x2) {
1339
+ out->_operands[1].addOpFlags(RegM);
1340
+ out->_operands[1].setRmSize(size1);
1341
+ }
1342
+
1343
+ // Handle 'pmovmskb|vpmovmskb'.
1344
+ if (BaseReg::isGp(operands[0]))
1345
+ rwZeroExtendGp(out->_operands[0], operands[0].as<Gp>(), nativeGpSize);
1346
+
1347
+ if (BaseReg::isVec(operands[0]))
1348
+ rwZeroExtendAvxVec(out->_operands[0], operands[0].as<Vec>());
1349
+
1350
+ return rwHandleAVX512(inst, commonInfo, out);
1351
+ }
1352
+
1353
+ if (operands[0].isReg() && operands[1].isMem()) {
1354
+ uint32_t size1 = operands[1].size() ? operands[1].size() : uint32_t(16);
1355
+ uint32_t size0 = size1 >> shift;
1356
+
1357
+ out->_operands[0].reset(W, size0);
1358
+ out->_operands[1].reset(R | MibRead, size1);
1359
+ return kErrorOk;
1360
+ }
1361
+
1362
+ if (operands[0].isMem() && operands[1].isReg()) {
1363
+ uint32_t size1 = operands[1].size();
1364
+ uint32_t size0 = size1 >> shift;
1365
+
1366
+ out->_operands[0].reset(W | MibRead, size0);
1367
+ out->_operands[1].reset(R, size1);
1368
+
1369
+ return rwHandleAVX512(inst, commonInfo, out);
1370
+ }
1371
+ }
1372
+ break;
1373
+ }
1374
+
1375
+ case InstDB::RWInfo::kCategoryVmov2_1:
1376
+ case InstDB::RWInfo::kCategoryVmov4_1:
1377
+ case InstDB::RWInfo::kCategoryVmov8_1: {
1378
+ // Special case for instructions where the destination is N:1 (widening).
1379
+ //
1380
+ // Vmov2_1:
1381
+ // vcvtdq2pd|vcvtudq2pd
1382
+ // vcvtps2pd|vcvtph2ps
1383
+ // vcvtps2qq|vcvtps2uqq
1384
+ // vcvttps2qq|vcvttps2uqq
1385
+ // vpmovsxbw|vpmovzxbw
1386
+ // vpmovsxwd|vpmovzxwd
1387
+ // vpmovsxdq|vpmovzxdq
1388
+ //
1389
+ // Vmov4_1:
1390
+ // vpmovsxbd|vpmovzxbd
1391
+ // vpmovsxwq|vpmovzxwq
1392
+ //
1393
+ // Vmov8_1:
1394
+ // vpmovsxbq|vpmovzxbq
1395
+ uint32_t shift = instRwInfo.category - InstDB::RWInfo::kCategoryVmov2_1 + 1;
1396
+
1397
+ if (opCount >= 2) {
1398
+ if (opCount >= 3) {
1399
+ if (opCount > 3)
1400
+ return DebugUtils::errored(kErrorInvalidInstruction);
1401
+ out->_operands[2].reset();
1402
+ }
1403
+
1404
+ uint32_t size0 = operands[0].size();
1405
+ uint32_t size1 = size0 >> shift;
1406
+
1407
+ out->_operands[0].reset(W, size0);
1408
+ out->_operands[1].reset(R, size1);
1409
+
1410
+ if (operands[0].isReg() && operands[1].isReg()) {
1411
+ if (instRmInfo.rmOpsMask & 0x1) {
1412
+ out->_operands[0].addOpFlags(RegM);
1413
+ out->_operands[0].setRmSize(size0);
1414
+ }
1415
+
1416
+ if (instRmInfo.rmOpsMask & 0x2) {
1417
+ out->_operands[1].addOpFlags(RegM);
1418
+ out->_operands[1].setRmSize(size1);
1419
+ }
1420
+
1421
+ return rwHandleAVX512(inst, commonInfo, out);
1422
+ }
1423
+
1424
+ if (operands[0].isReg() && operands[1].isMem()) {
1425
+ out->_operands[1].addOpFlags(MibRead);
1426
+
1427
+ return rwHandleAVX512(inst, commonInfo, out);
1428
+ }
1429
+ }
1430
+ break;
1431
+ }
1432
+ }
1433
+
1434
+ return DebugUtils::errored(kErrorInvalidInstruction);
1435
+ }
1436
+ #endif // !ASMJIT_NO_INTROSPECTION
1437
+
1438
+ // x86::InstInternal - QueryFeatures
1439
+ // =================================
1440
+
1441
+ #ifndef ASMJIT_NO_INTROSPECTION
1442
+ struct RegAnalysis {
1443
+ uint32_t regTypeMask;
1444
+ uint32_t highVecUsed;
1445
+
1446
+ inline bool hasRegType(RegType regType) const noexcept {
1447
+ return Support::bitTest(regTypeMask, regType);
1448
+ }
1449
+ };
1450
+
1451
+ static RegAnalysis InstInternal_regAnalysis(const Operand_* operands, size_t opCount) noexcept {
1452
+ uint32_t mask = 0;
1453
+ uint32_t highVecUsed = 0;
1454
+
1455
+ for (uint32_t i = 0; i < opCount; i++) {
1456
+ const Operand_& op = operands[i];
1457
+ if (op.isReg()) {
1458
+ const BaseReg& reg = op.as<BaseReg>();
1459
+ mask |= Support::bitMask(reg.type());
1460
+ if (reg.isVec())
1461
+ highVecUsed |= uint32_t(reg.id() >= 16 && reg.id() < 32);
1462
+ }
1463
+ else if (op.isMem()) {
1464
+ const BaseMem& mem = op.as<BaseMem>();
1465
+ if (mem.hasBaseReg()) mask |= Support::bitMask(mem.baseType());
1466
+ if (mem.hasIndexReg()) {
1467
+ mask |= Support::bitMask(mem.indexType());
1468
+ highVecUsed |= uint32_t(mem.indexId() >= 16 && mem.indexId() < 32);
1469
+ }
1470
+ }
1471
+ }
1472
+
1473
+ return RegAnalysis { mask, highVecUsed };
1474
+ }
1475
+
1476
+ static inline uint32_t InstInternal_usesAvx512(InstOptions instOptions, const RegOnly& extraReg, const RegAnalysis& regAnalysis) noexcept {
1477
+ uint32_t hasEvex = uint32_t(instOptions & (InstOptions::kX86_Evex | InstOptions::kX86_AVX512Mask));
1478
+ uint32_t hasKMask = extraReg.type() == RegType::kX86_KReg;
1479
+ uint32_t hasKOrZmm = regAnalysis.regTypeMask & Support::bitMask(RegType::kX86_Zmm, RegType::kX86_KReg);
1480
+
1481
+ return hasEvex | hasKMask | hasKOrZmm;
1482
+ }
1483
+
1484
+ Error InstInternal::queryFeatures(Arch arch, const BaseInst& inst, const Operand_* operands, size_t opCount, CpuFeatures* out) noexcept {
1485
+ // Only called when `arch` matches X86 family.
1486
+ DebugUtils::unused(arch);
1487
+ ASMJIT_ASSERT(Environment::isFamilyX86(arch));
1488
+
1489
+ // Get the instruction data.
1490
+ InstId instId = inst.id();
1491
+ InstOptions options = inst.options();
1492
+
1493
+ if (ASMJIT_UNLIKELY(!Inst::isDefinedId(instId)))
1494
+ return DebugUtils::errored(kErrorInvalidInstruction);
1495
+
1496
+ const InstDB::InstInfo& instInfo = InstDB::infoById(instId);
1497
+ const InstDB::AdditionalInfo& additionalInfo = InstDB::_additionalInfoTable[instInfo._additionalInfoIndex];
1498
+
1499
+ const uint8_t* fData = additionalInfo.featuresBegin();
1500
+ const uint8_t* fEnd = additionalInfo.featuresEnd();
1501
+
1502
+ // Copy all features to `out`.
1503
+ out->reset();
1504
+ do {
1505
+ uint32_t feature = fData[0];
1506
+ if (!feature)
1507
+ break;
1508
+ out->add(feature);
1509
+ } while (++fData != fEnd);
1510
+
1511
+ // Since AsmJit aggregates instructions that share the same name we have to
1512
+ // deal with some special cases and also with MMX/SSE and AVX/AVX2 overlaps.
1513
+ if (fData != additionalInfo.featuresBegin()) {
1514
+ RegAnalysis regAnalysis = InstInternal_regAnalysis(operands, opCount);
1515
+
1516
+ // Handle MMX vs SSE overlap.
1517
+ if (out->has(CpuFeatures::X86::kMMX) || out->has(CpuFeatures::X86::kMMX2)) {
1518
+ // Only instructions defined by SSE and SSE2 overlap. Instructions introduced by newer instruction sets like
1519
+ // SSE3+ don't state MMX as they require SSE3+.
1520
+ if (out->has(CpuFeatures::X86::kSSE) || out->has(CpuFeatures::X86::kSSE2)) {
1521
+ if (!regAnalysis.hasRegType(RegType::kX86_Xmm)) {
1522
+ // The instruction doesn't use XMM register(s), thus it's MMX/MMX2 only.
1523
+ out->remove(CpuFeatures::X86::kSSE);
1524
+ out->remove(CpuFeatures::X86::kSSE2);
1525
+ out->remove(CpuFeatures::X86::kSSE4_1);
1526
+ }
1527
+ else {
1528
+ out->remove(CpuFeatures::X86::kMMX);
1529
+ out->remove(CpuFeatures::X86::kMMX2);
1530
+ }
1531
+
1532
+ // Special case: PEXTRW instruction is MMX/SSE2 instruction. However, MMX/SSE version cannot access memory
1533
+ // (only register to register extract) so when SSE4.1 introduced the whole family of PEXTR/PINSR instructions
1534
+ // they also introduced PEXTRW with a new opcode 0x15 that can extract directly to memory. This instruction
1535
+ // is, of course, not compatible with MMX/SSE2 and would #UD if SSE4.1 is not supported.
1536
+ if (instId == Inst::kIdPextrw) {
1537
+ if (opCount >= 1 && operands[0].isMem())
1538
+ out->remove(CpuFeatures::X86::kSSE2);
1539
+ else
1540
+ out->remove(CpuFeatures::X86::kSSE4_1);
1541
+ }
1542
+ }
1543
+ }
1544
+
1545
+ // Handle PCLMULQDQ vs VPCLMULQDQ.
1546
+ if (out->has(CpuFeatures::X86::kVPCLMULQDQ)) {
1547
+ if (regAnalysis.hasRegType(RegType::kX86_Zmm) || Support::test(options, InstOptions::kX86_Evex)) {
1548
+ // AVX512_F & VPCLMULQDQ.
1549
+ out->remove(CpuFeatures::X86::kAVX, CpuFeatures::X86::kPCLMULQDQ);
1550
+ }
1551
+ else if (regAnalysis.hasRegType(RegType::kX86_Ymm)) {
1552
+ out->remove(CpuFeatures::X86::kAVX512_F, CpuFeatures::X86::kAVX512_VL);
1553
+ }
1554
+ else {
1555
+ // AVX & PCLMULQDQ.
1556
+ out->remove(CpuFeatures::X86::kAVX512_F, CpuFeatures::X86::kAVX512_VL, CpuFeatures::X86::kVPCLMULQDQ);
1557
+ }
1558
+ }
1559
+
1560
+ // Handle AVX vs AVX2 overlap.
1561
+ if (out->has(CpuFeatures::X86::kAVX) && out->has(CpuFeatures::X86::kAVX2)) {
1562
+ bool isAVX2 = true;
1563
+ // Special case: VBROADCASTSS and VBROADCASTSD were introduced in AVX, but only version that uses memory as a
1564
+ // source operand. AVX2 then added support for register source operand.
1565
+ if (instId == Inst::kIdVbroadcastss || instId == Inst::kIdVbroadcastsd) {
1566
+ if (opCount > 1 && operands[1].isMem())
1567
+ isAVX2 = false;
1568
+ }
1569
+ else {
1570
+ // AVX instruction set doesn't support integer operations on YMM registers as these were later introcuced by
1571
+ // AVX2. In our case we have to check if YMM register(s) are in use and if that is the case this is an AVX2
1572
+ // instruction.
1573
+ if (!(regAnalysis.regTypeMask & Support::bitMask(RegType::kX86_Ymm, RegType::kX86_Zmm)))
1574
+ isAVX2 = false;
1575
+ }
1576
+
1577
+ if (isAVX2)
1578
+ out->remove(CpuFeatures::X86::kAVX);
1579
+ else
1580
+ out->remove(CpuFeatures::X86::kAVX2);
1581
+ }
1582
+
1583
+ // Handle AVX|AVX2|FMA|F16C vs AVX512 overlap.
1584
+ if (out->has(CpuFeatures::X86::kAVX) || out->has(CpuFeatures::X86::kAVX2) || out->has(CpuFeatures::X86::kFMA) || out->has(CpuFeatures::X86::kF16C)) {
1585
+ // Only AVX512-F|BW|DQ allow to encode AVX/AVX2/FMA/F16C instructions
1586
+ if (out->has(CpuFeatures::X86::kAVX512_F) || out->has(CpuFeatures::X86::kAVX512_BW) || out->has(CpuFeatures::X86::kAVX512_DQ)) {
1587
+ uint32_t usesAvx512 = InstInternal_usesAvx512(options, inst.extraReg(), regAnalysis);
1588
+ uint32_t mustUseEvex = 0;
1589
+
1590
+ switch (instId) {
1591
+ // Special case: VPSLLDQ and VPSRLDQ instructions only allow `reg, reg. imm` combination in AVX|AVX2 mode,
1592
+ // then AVX-512 introduced `reg, reg/mem, imm` combination that uses EVEX prefix. This means that if the
1593
+ // second operand is memory then this is AVX-512_BW instruction and not AVX/AVX2 instruction.
1594
+ case Inst::kIdVpslldq:
1595
+ case Inst::kIdVpsrldq:
1596
+ mustUseEvex = opCount >= 2 && operands[1].isMem();
1597
+ break;
1598
+
1599
+ // Special case: VPBROADCAST[B|D|Q|W] only supports r32/r64 with EVEX prefix.
1600
+ case Inst::kIdVpbroadcastb:
1601
+ case Inst::kIdVpbroadcastd:
1602
+ case Inst::kIdVpbroadcastq:
1603
+ case Inst::kIdVpbroadcastw:
1604
+ mustUseEvex = opCount >= 2 && x86::Reg::isGp(operands[1]);
1605
+ break;
1606
+
1607
+ // Special case: VPERMPD - AVX2 vs AVX512-F case.
1608
+ case Inst::kIdVpermpd:
1609
+ mustUseEvex = opCount >= 3 && !operands[2].isImm();
1610
+ break;
1611
+
1612
+ // Special case: VPERMQ - AVX2 vs AVX512-F case.
1613
+ case Inst::kIdVpermq:
1614
+ mustUseEvex = opCount >= 3 && (operands[1].isMem() || !operands[2].isImm());
1615
+ break;
1616
+ }
1617
+
1618
+ if (!(usesAvx512 | mustUseEvex | regAnalysis.highVecUsed))
1619
+ out->remove(CpuFeatures::X86::kAVX512_F, CpuFeatures::X86::kAVX512_BW, CpuFeatures::X86::kAVX512_DQ, CpuFeatures::X86::kAVX512_VL);
1620
+ else
1621
+ out->remove(CpuFeatures::X86::kAVX, CpuFeatures::X86::kAVX2, CpuFeatures::X86::kFMA, CpuFeatures::X86::kF16C);
1622
+ }
1623
+ }
1624
+
1625
+ // Handle AVX_VNNI vs AVX512_VNNI overlap.
1626
+ if (out->has(CpuFeatures::X86::kAVX512_VNNI)) {
1627
+ // By default the AVX512_VNNI instruction should be used, because it was introduced first. However, VEX|VEX3
1628
+ // prefix can be used to force AVX_VNNI instead.
1629
+ uint32_t usesAvx512 = InstInternal_usesAvx512(options, inst.extraReg(), regAnalysis);
1630
+
1631
+ if (!usesAvx512 && Support::test(options, InstOptions::kX86_Vex | InstOptions::kX86_Vex3))
1632
+ out->remove(CpuFeatures::X86::kAVX512_VNNI, CpuFeatures::X86::kAVX512_VL);
1633
+ else
1634
+ out->remove(CpuFeatures::X86::kAVX_VNNI);
1635
+ }
1636
+
1637
+ // Clear AVX512_VL if ZMM register is used.
1638
+ if (regAnalysis.hasRegType(RegType::kX86_Zmm))
1639
+ out->remove(CpuFeatures::X86::kAVX512_VL);
1640
+ }
1641
+
1642
+ return kErrorOk;
1643
+ }
1644
+ #endif // !ASMJIT_NO_INTROSPECTION
1645
+
1646
+ // x86::InstInternal - Tests
1647
+ // =========================
1648
+
1649
+ #if defined(ASMJIT_TEST)
1650
+ UNIT(x86_inst_api_text) {
1651
+ // All known instructions should be matched.
1652
+ INFO("Matching all X86 instructions");
1653
+ for (uint32_t a = 1; a < Inst::_kIdCount; a++) {
1654
+ StringTmp<128> aName;
1655
+ EXPECT(InstInternal::instIdToString(Arch::kX86, a, aName) == kErrorOk,
1656
+ "Failed to get the name of instruction #%u", a);
1657
+
1658
+ uint32_t b = InstInternal::stringToInstId(Arch::kX86, aName.data(), aName.size());
1659
+ StringTmp<128> bName;
1660
+ InstInternal::instIdToString(Arch::kX86, b, bName);
1661
+
1662
+ EXPECT(a == b,
1663
+ "Instructions do not match \"%s\" (#%u) != \"%s\" (#%u)", aName.data(), a, bName.data(), b);
1664
+ }
1665
+ }
1666
+
1667
+ template<typename... Args>
1668
+ static Error queryRWInfoSimple(InstRWInfo* out, Arch arch, InstId instId, InstOptions options, Args&&... args) {
1669
+ BaseInst inst(instId);
1670
+ inst.addOptions(options);
1671
+ Operand_ opArray[] = { std::forward<Args>(args)... };
1672
+ return InstInternal::queryRWInfo(arch, inst, opArray, sizeof...(args), out);
1673
+ }
1674
+
1675
+ UNIT(x86_inst_api_rm_feature) {
1676
+ INFO("Verifying whether RM/feature is reported correctly for PEXTRW instruction");
1677
+ {
1678
+ InstRWInfo rwi;
1679
+
1680
+ queryRWInfoSimple(&rwi, Arch::kX64, Inst::kIdPextrw, InstOptions::kNone, eax, mm1, imm(1));
1681
+ EXPECT(rwi.rmFeature() == 0);
1682
+
1683
+ queryRWInfoSimple(&rwi, Arch::kX64, Inst::kIdPextrw, InstOptions::kNone, eax, xmm1, imm(1));
1684
+ EXPECT(rwi.rmFeature() == CpuFeatures::X86::kSSE4_1);
1685
+ }
1686
+
1687
+ INFO("Verifying whether RM/feature is reported correctly for AVX512 shift instructions");
1688
+ {
1689
+ InstRWInfo rwi;
1690
+
1691
+ queryRWInfoSimple(&rwi, Arch::kX64, Inst::kIdVpslld, InstOptions::kNone, xmm1, xmm2, imm(8));
1692
+ EXPECT(rwi.rmFeature() == CpuFeatures::X86::kAVX512_F);
1693
+
1694
+ queryRWInfoSimple(&rwi, Arch::kX64, Inst::kIdVpsllq, InstOptions::kNone, ymm1, ymm2, imm(8));
1695
+ EXPECT(rwi.rmFeature() == CpuFeatures::X86::kAVX512_F);
1696
+
1697
+ queryRWInfoSimple(&rwi, Arch::kX64, Inst::kIdVpsrad, InstOptions::kNone, xmm1, xmm2, imm(8));
1698
+ EXPECT(rwi.rmFeature() == CpuFeatures::X86::kAVX512_F);
1699
+
1700
+ queryRWInfoSimple(&rwi, Arch::kX64, Inst::kIdVpsrld, InstOptions::kNone, ymm1, ymm2, imm(8));
1701
+ EXPECT(rwi.rmFeature() == CpuFeatures::X86::kAVX512_F);
1702
+
1703
+ queryRWInfoSimple(&rwi, Arch::kX64, Inst::kIdVpsrlq, InstOptions::kNone, xmm1, xmm2, imm(8));
1704
+ EXPECT(rwi.rmFeature() == CpuFeatures::X86::kAVX512_F);
1705
+
1706
+ queryRWInfoSimple(&rwi, Arch::kX64, Inst::kIdVpslldq, InstOptions::kNone, xmm1, xmm2, imm(8));
1707
+ EXPECT(rwi.rmFeature() == CpuFeatures::X86::kAVX512_BW);
1708
+
1709
+ queryRWInfoSimple(&rwi, Arch::kX64, Inst::kIdVpsllw, InstOptions::kNone, ymm1, ymm2, imm(8));
1710
+ EXPECT(rwi.rmFeature() == CpuFeatures::X86::kAVX512_BW);
1711
+
1712
+ queryRWInfoSimple(&rwi, Arch::kX64, Inst::kIdVpsraw, InstOptions::kNone, xmm1, xmm2, imm(8));
1713
+ EXPECT(rwi.rmFeature() == CpuFeatures::X86::kAVX512_BW);
1714
+
1715
+ queryRWInfoSimple(&rwi, Arch::kX64, Inst::kIdVpsrldq, InstOptions::kNone, ymm1, ymm2, imm(8));
1716
+ EXPECT(rwi.rmFeature() == CpuFeatures::X86::kAVX512_BW);
1717
+
1718
+ queryRWInfoSimple(&rwi, Arch::kX64, Inst::kIdVpsrlw, InstOptions::kNone, xmm1, xmm2, imm(8));
1719
+ EXPECT(rwi.rmFeature() == CpuFeatures::X86::kAVX512_BW);
1720
+
1721
+ queryRWInfoSimple(&rwi, Arch::kX64, Inst::kIdVpslld, InstOptions::kNone, xmm1, xmm2, xmm3);
1722
+ EXPECT(rwi.rmFeature() == 0);
1723
+
1724
+ queryRWInfoSimple(&rwi, Arch::kX64, Inst::kIdVpsllw, InstOptions::kNone, xmm1, xmm2, xmm3);
1725
+ EXPECT(rwi.rmFeature() == 0);
1726
+ }
1727
+ }
1728
+ #endif
1729
+
1730
+ ASMJIT_END_SUB_NAMESPACE
1731
+
1732
+ #endif // !ASMJIT_NO_X86