mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-07 06:00:16 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cac3767c20 | ||
|
|
e82d2c70c3 | ||
|
|
9716fc73d3 | ||
|
|
aaa8eef9f1 | ||
|
|
c036868938 | ||
|
|
2726f35a10 | ||
|
|
407dc5d4dd | ||
|
|
7dda75646d | ||
|
|
b89f5b8a03 | ||
|
|
16869d8954 | ||
|
|
e353ae8408 | ||
|
|
aa3c963df1 | ||
|
|
0c8cfa7bb2 | ||
|
|
dd837fa693 | ||
|
|
eeff198ff1 | ||
|
|
0cd6371c1b | ||
|
|
5aff16f72b | ||
|
|
2c7896bce4 | ||
|
|
6dd72a09e5 | ||
|
|
4e6a4d9b69 | ||
|
|
44484a7b05 | ||
|
|
6ac7ba388f | ||
|
|
36d0a67070 | ||
|
|
0650dd1992 | ||
|
|
e2d58809ed | ||
|
|
967a74cda9 | ||
|
|
6cc6181261 | ||
|
|
f175b525f4 | ||
|
|
319f1e66cb | ||
|
|
d16969bdce | ||
|
|
c17858bfeb | ||
|
|
ec24fc3d5d | ||
|
|
8819fa88d5 | ||
|
|
5e9c2110db | ||
|
|
5aaa18e7a2 | ||
|
|
8a551b9e64 | ||
|
|
aa548bd19c | ||
|
|
9565f16d84 | ||
|
|
eb76dbdf4d | ||
|
|
967c04e252 | ||
|
|
49de1fac59 | ||
|
|
3c205eb35e | ||
|
|
682b8ef705 | ||
|
|
8c47a40625 | ||
|
|
ee5c6af868 | ||
|
|
1670c89b5e | ||
|
|
e9d34c7ded | ||
|
|
c2f2c58367 | ||
|
|
38acd9e18c | ||
|
|
21604708ca | ||
|
|
5fec6faeb6 | ||
|
|
b659701cef | ||
|
|
e902ad5278 | ||
|
|
0f54369f9b | ||
|
|
f640dcc7a4 | ||
|
|
b59da0e049 | ||
|
|
4ae3aef502 | ||
|
|
926fa3c24c | ||
|
|
8c1740f592 | ||
|
|
611aa0a5b9 | ||
|
|
0b8b5108b9 | ||
|
|
e409a0afec | ||
|
|
24211f8523 | ||
|
|
94462e4dd0 | ||
|
|
aa44668a41 | ||
|
|
33a675624c | ||
|
|
5745b419d9 | ||
|
|
9100235041 | ||
|
|
4d62e75d5a | ||
|
|
b6d3df0e54 | ||
|
|
d5d1230422 | ||
|
|
f35a212f06 | ||
|
|
f49d82deb3 | ||
|
|
77b801b8ae | ||
|
|
163a3fc899 | ||
|
|
9b627e8743 | ||
|
|
9d6865f62d | ||
|
|
d547f2b8f2 | ||
|
|
b5c9eb3463 | ||
|
|
00a3793814 | ||
|
|
4f8d7cb89c | ||
|
|
9310ac59f4 | ||
|
|
9e4de3bfe8 | ||
|
|
598b99fe58 | ||
|
|
9dda76ebe6 | ||
|
|
f5def7ae1c | ||
|
|
58bcf691d6 | ||
|
|
33186b803d | ||
|
|
bb1d7d0750 | ||
|
|
4897c1e80c | ||
|
|
e7605c94dd | ||
|
|
ab5a10374b | ||
|
|
dc2a26582f | ||
|
|
e6512fbe4d | ||
|
|
dbca441573 | ||
|
|
31adc953b7 | ||
|
|
0dd7f5e2f7 | ||
|
|
36556c4705 | ||
|
|
e29fac3f25 | ||
|
|
c984bdb42a | ||
|
|
2645b374a7 | ||
|
|
10192eebe5 | ||
|
|
0e57cbf5b9 | ||
|
|
c7413d96ad | ||
|
|
f3ba8cb33c | ||
|
|
e714933b10 | ||
|
|
9a0f83cbf6 | ||
|
|
c4d30e8437 | ||
|
|
7b30df8a56 | ||
|
|
86aa459dd1 | ||
|
|
6c9a47fc71 | ||
|
|
7a72cf631b | ||
|
|
3d98eef7b8 | ||
|
|
f2011b0b79 | ||
|
|
c0b8a0d9c3 | ||
|
|
5bd0afa307 | ||
|
|
20fb0da7d9 | ||
|
|
35cb1f710c | ||
|
|
7c7b99f9c5 | ||
|
|
09879bd962 | ||
|
|
c8f3fe3788 | ||
|
|
88e3164b55 | ||
|
|
bebd7400c0 | ||
|
|
e190d029dc | ||
|
|
37a70e2ec6 | ||
|
|
6ff073ac11 | ||
|
|
b6e4c47abc | ||
|
|
daf8b409f3 | ||
|
|
0a35029d9a | ||
|
|
e8b51ac0a4 | ||
|
|
f20e626fda | ||
|
|
2672e06aa5 | ||
|
|
4a2b4be59d | ||
|
|
05be9445cd | ||
|
|
4cb4c0e277 | ||
|
|
0135e2d78c | ||
|
|
1266a5a5ce | ||
|
|
7255850e9d | ||
|
|
002a8bebd8 | ||
|
|
452c9fd19b | ||
|
|
0b0e15c9b4 | ||
|
|
ae9db336e7 | ||
|
|
360ea538b7 | ||
|
|
f99900bdc5 | ||
|
|
34f2bce203 | ||
|
|
a05d6ae082 | ||
|
|
6a4eb434f7 | ||
|
|
0c3eb41f57 | ||
|
|
e4bfbda008 | ||
|
|
b4c797c199 | ||
|
|
ebae1bf71d | ||
|
|
fa293e5e8f | ||
|
|
720c89e648 | ||
|
|
abc5f5abd4 | ||
|
|
22ab16b217 | ||
|
|
b80f05dd86 | ||
|
|
1642a0f4d9 | ||
|
|
96e990dade | ||
|
|
3423a12ab6 | ||
|
|
c4a0fb6609 | ||
|
|
8cf23eba9c | ||
|
|
fbe817f1c4 | ||
|
|
3b61394548 | ||
|
|
8ed6b36181 | ||
|
|
9141322666 | ||
|
|
2d91c5441e | ||
|
|
2ff1589c35 | ||
|
|
a308b9edbe | ||
|
|
f6a8e595df | ||
|
|
eaaf62e4b7 | ||
|
|
dc5fc57e19 | ||
|
|
c3c0bbb060 | ||
|
|
98a91d242d | ||
|
|
8da6b1e729 | ||
|
|
d260bce31e | ||
|
|
a94aaca0ec | ||
|
|
2ac3ca3ead | ||
|
|
c559445b62 | ||
|
|
8ea4e4f663 | ||
|
|
2da700fe82 | ||
|
|
0a32ba9b29 | ||
|
|
f51832ca6b | ||
|
|
5f9a30af7a | ||
|
|
453ef0b94c | ||
|
|
4779e64cf5 | ||
|
|
e6025cc087 | ||
|
|
cd4c224dc0 | ||
|
|
07f117b957 | ||
|
|
36e4f9a070 | ||
|
|
67c751d3e5 | ||
|
|
1c59bfeb9f | ||
|
|
2b0041a291 | ||
|
|
39cc1e5116 | ||
|
|
f46856c090 | ||
|
|
b7859052da | ||
|
|
3b30d8103f | ||
|
|
d19fc36f8f | ||
|
|
5fbf76ee9e | ||
|
|
5f79761fe4 | ||
|
|
621af9e7e8 | ||
|
|
f4d107c8f0 | ||
|
|
59643db331 | ||
|
|
46f36dc89d | ||
|
|
384882744c | ||
|
|
b36d1f7e7b | ||
|
|
dd7c66db2e | ||
|
|
47ac9edd7a | ||
|
|
6e6d640fec | ||
|
|
eb88366614 | ||
|
|
50d6ddd591 | ||
|
|
249de7c758 | ||
|
|
c350888d70 | ||
|
|
f23bef1653 | ||
|
|
d17f33a922 | ||
|
|
50a3ca0d6d | ||
|
|
8745455a5b | ||
|
|
e9ab514962 | ||
|
|
4eb0948451 | ||
|
|
8d9f19bd73 | ||
|
|
9fe7bc5818 | ||
|
|
5a590a9b11 | ||
|
|
a4acd64246 | ||
|
|
304b5de1af | ||
|
|
ca2fe0b301 | ||
|
|
bbef4d762c | ||
|
|
f77841d784 | ||
|
|
219a4777c6 | ||
|
|
29f0e9b4d6 | ||
|
|
b75efc42bf | ||
|
|
90dbd4766d | ||
|
|
f7b911ca43 | ||
|
|
6de0333708 | ||
|
|
ab5d3ab22b | ||
|
|
355c3428c0 | ||
|
|
c76b7bfe8d | ||
|
|
64c5362580 | ||
|
|
77469de247 | ||
|
|
48fd827004 | ||
|
|
f09d511ac8 |
No files matched your search
+1
-1
@@ -4,7 +4,7 @@ compile_commands.json
|
||||
vim_rc
|
||||
Config.json
|
||||
|
||||
[Bb]uild*/
|
||||
[Bb]uild*
|
||||
[Bb]in/
|
||||
out/
|
||||
.vscode/
|
||||
|
||||
@@ -5,9 +5,6 @@
|
||||
[submodule "External/cpp-optparse"]
|
||||
path = Source/Common/cpp-optparse
|
||||
url = https://github.com/Sonicadvance1/cpp-optparse
|
||||
[submodule "External/imgui"]
|
||||
path = External/imgui
|
||||
url = https://github.com/Sonicadvance1/imgui.git
|
||||
[submodule "External/xbyak"]
|
||||
shallow = true
|
||||
path = External/xbyak
|
||||
|
||||
+27
-20
@@ -7,7 +7,7 @@ CHECK_INCLUDE_FILES ("gdb/jit-reader.h" HAVE_GDB_JIT_READER_H)
|
||||
option(BUILD_TESTS "Build unit tests to ensure sanity" TRUE)
|
||||
option(BUILD_FEX_LINUX_TESTS "Build FEXLinuxTests, requires x86 compiler" FALSE)
|
||||
option(BUILD_THUNKS "Build thunks" FALSE)
|
||||
set(USE_FEXCONFIG_TOOLKIT "imgui" CACHE STRING "If set, build FEXConfig (qt or imgui)")
|
||||
option(BUILD_FEXCONFIG "Build FEXConfig" TRUE)
|
||||
option(ENABLE_CLANG_THUNKS "Build thunks with clang" FALSE)
|
||||
option(ENABLE_IWYU "Enables include what you use program" FALSE)
|
||||
option(ENABLE_LTO "Enable LTO with compilation" TRUE)
|
||||
@@ -232,7 +232,6 @@ if (ENABLE_JEMALLOC_GLIBC_ALLOC)
|
||||
# The glibc jemalloc subproject which hooks the glibc allocator.
|
||||
# Required for thunks to work.
|
||||
# All host native libraries will use this allocator, while *most* other FEX internal allocations will use the other jemalloc allocator.
|
||||
add_definitions(-DENABLE_JEMALLOC_GLIBC=1)
|
||||
add_subdirectory(External/jemalloc_glibc/)
|
||||
elseif (NOT MINGW_BUILD)
|
||||
message (STATUS
|
||||
@@ -244,9 +243,7 @@ endif()
|
||||
|
||||
if (ENABLE_JEMALLOC)
|
||||
# The jemalloc subproject that all FEXCore fextl objects allocate through.
|
||||
add_definitions(-DENABLE_JEMALLOC=1)
|
||||
add_subdirectory(External/jemalloc/)
|
||||
include_directories(External/jemalloc/pregen/include/)
|
||||
elseif (NOT MINGW_BUILD)
|
||||
message (STATUS
|
||||
" jemalloc disabled!\n"
|
||||
@@ -273,8 +270,10 @@ if (BUILD_TESTS)
|
||||
set(COMPILE_VIXL_DISASSEMBLER TRUE)
|
||||
endif()
|
||||
|
||||
add_subdirectory(External/vixl/)
|
||||
include_directories(SYSTEM External/vixl/src/)
|
||||
if (COMPILE_VIXL_DISASSEMBLER OR ENABLE_VIXL_SIMULATOR)
|
||||
add_subdirectory(External/vixl/)
|
||||
include_directories(SYSTEM External/vixl/src/)
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
# This means we were attempted to get compiled with GCC
|
||||
@@ -284,29 +283,37 @@ endif()
|
||||
find_package(PkgConfig REQUIRED)
|
||||
find_package(Python 3.0 REQUIRED COMPONENTS Interpreter)
|
||||
|
||||
set(XXHASH_BUNDLED_MODE TRUE)
|
||||
set(XXHASH_BUILD_XXHSUM FALSE)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
add_subdirectory(External/xxhash/cmake_unofficial/)
|
||||
|
||||
pkg_search_module(xxhash IMPORTED_TARGET xxhash libxxhash)
|
||||
if (TARGET PkgConfig::xxhash AND NOT CMAKE_CROSSCOMPILING)
|
||||
add_library(xxHash::xxhash ALIAS PkgConfig::xxhash)
|
||||
else()
|
||||
set(XXHASH_BUNDLED_MODE TRUE)
|
||||
set(XXHASH_BUILD_XXHSUM FALSE)
|
||||
add_subdirectory(External/xxhash/cmake_unofficial/)
|
||||
endif()
|
||||
|
||||
add_definitions(-Wno-trigraphs)
|
||||
add_definitions(-DGLOBAL_DATA_DIRECTORY="${DATA_DIRECTORY}/")
|
||||
|
||||
if (BUILD_TESTS)
|
||||
add_subdirectory(External/Catch2/)
|
||||
find_package(Catch2 QUIET)
|
||||
if (NOT Catch2_FOUND)
|
||||
add_subdirectory(External/Catch2/)
|
||||
|
||||
# Pull in catch_discover_tests definition
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/External/Catch2/contrib/")
|
||||
endif()
|
||||
|
||||
# Pull in catch_discover_tests definition
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/External/Catch2/contrib/")
|
||||
include(Catch)
|
||||
endif()
|
||||
|
||||
# Disable fmt install
|
||||
set(FMT_INSTALL OFF)
|
||||
add_subdirectory(External/fmt/)
|
||||
|
||||
if (USE_FEXCONFIG_TOOLKIT STREQUAL "imgui")
|
||||
add_subdirectory(External/imgui/)
|
||||
include_directories(External/imgui/)
|
||||
find_package(fmt QUIET)
|
||||
if (NOT fmt_FOUND)
|
||||
# Disable fmt install
|
||||
set(FMT_INSTALL OFF)
|
||||
add_subdirectory(External/fmt/)
|
||||
endif()
|
||||
|
||||
add_subdirectory(External/tiny-json/)
|
||||
@@ -423,7 +430,7 @@ add_subdirectory(FEXHeaderUtils/)
|
||||
add_subdirectory(CodeEmitter/)
|
||||
add_subdirectory(FEXCore/)
|
||||
|
||||
if (NOT MINGW_BUILD)
|
||||
if (_M_ARM_64 AND NOT MINGW_BUILD)
|
||||
# Binfmt_misc files must be installed prior to Source/ installs
|
||||
add_subdirectory(Data/binfmts/)
|
||||
endif()
|
||||
|
||||
@@ -301,8 +301,9 @@ public:
|
||||
xbfiz_helper(true, s, rd, rn, lsb, width);
|
||||
}
|
||||
void asr(ARMEmitter::Size s, ARMEmitter::Register rd, ARMEmitter::Register rn, uint32_t shift) {
|
||||
LOGMAN_THROW_A_FMT(shift <= RegSizeInBits(s), "Tried to asr a region larger than the register");
|
||||
sbfm(s, rd, rn, shift, RegSizeInBits(s) - 1);
|
||||
const auto RegSize_m1 = RegSizeInBits(s) - 1;
|
||||
shift &= RegSize_m1;
|
||||
sbfm(s, rd, rn, shift, RegSize_m1);
|
||||
}
|
||||
|
||||
void uxtb(ARMEmitter::Size s, ARMEmitter::Register rd, ARMEmitter::Register rn) {
|
||||
@@ -325,14 +326,14 @@ public:
|
||||
}
|
||||
|
||||
void lsl(ARMEmitter::Size s, ARMEmitter::Register rd, ARMEmitter::Register rn, uint32_t shift) {
|
||||
const auto RegSize = RegSizeInBits(s);
|
||||
LOGMAN_THROW_A_FMT(shift < RegSize, "Tried to lsl a region larger than the register");
|
||||
ubfm(s, rd, rn, (RegSize - shift) % RegSize, RegSize - shift - 1);
|
||||
const auto RegSize_m1 = RegSizeInBits(s) - 1;
|
||||
shift &= RegSize_m1;
|
||||
ubfm(s, rd, rn, (RegSizeInBits(s) - shift) & RegSize_m1, RegSize_m1 - shift);
|
||||
}
|
||||
void lsr(ARMEmitter::Size s, ARMEmitter::Register rd, ARMEmitter::Register rn, uint32_t shift) {
|
||||
const auto RegSize = RegSizeInBits(s);
|
||||
LOGMAN_THROW_A_FMT(shift < RegSize, "Tried to lsr a region larger than the register");
|
||||
ubfm(s, rd, rn, shift, RegSize - 1);
|
||||
const auto RegSize_m1 = RegSizeInBits(s) - 1;
|
||||
shift &= RegSize_m1;
|
||||
ubfm(s, rd, rn, shift, RegSize_m1);
|
||||
}
|
||||
void ubfx(ARMEmitter::Size s, ARMEmitter::Register rd, ARMEmitter::Register rn, uint32_t lsb, uint32_t width) {
|
||||
LOGMAN_THROW_A_FMT(width > 0, "ubfx needs width > 0");
|
||||
@@ -368,6 +369,7 @@ public:
|
||||
}
|
||||
|
||||
void ror(ARMEmitter::Size s, ARMEmitter::Register rd, ARMEmitter::Register rn, uint32_t Imm) {
|
||||
Imm &= RegSizeInBits(s) - 1;
|
||||
extr(s, rd, rn, rn, Imm);
|
||||
}
|
||||
|
||||
|
||||
@@ -1070,7 +1070,7 @@ public:
|
||||
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
|
||||
const auto ConvertedSize =
|
||||
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
|
||||
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc(Op, 0, ConvertedSize, 0b10110, rd.D(), rn.D());
|
||||
}
|
||||
@@ -1082,7 +1082,7 @@ public:
|
||||
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
|
||||
const auto ConvertedSize =
|
||||
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
|
||||
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc(Op, 0, ConvertedSize, 0b10110, rd.Q(), rn.Q());
|
||||
}
|
||||
@@ -1095,7 +1095,7 @@ public:
|
||||
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
|
||||
const auto ConvertedSize =
|
||||
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
|
||||
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc(Op, 0, ConvertedSize, 0b10111, rd.D(), rn.D());
|
||||
}
|
||||
@@ -1107,7 +1107,7 @@ public:
|
||||
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
|
||||
const auto ConvertedSize =
|
||||
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
|
||||
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc(Op, 0, ConvertedSize, 0b10111, rd.Q(), rn.Q());
|
||||
}
|
||||
@@ -1123,7 +1123,7 @@ public:
|
||||
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
|
||||
const auto ConvertedSize =
|
||||
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
|
||||
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc<T>(Op, 0, ConvertedSize, 0b11000, rd, rn);
|
||||
}
|
||||
@@ -1138,7 +1138,7 @@ public:
|
||||
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
|
||||
const auto ConvertedSize =
|
||||
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
|
||||
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc<T>(Op, 0, ConvertedSize, 0b11001, rd, rn);
|
||||
}
|
||||
@@ -1154,7 +1154,7 @@ public:
|
||||
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
|
||||
const auto ConvertedSize =
|
||||
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
|
||||
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc<T>(Op, 0, ConvertedSize, 0b11010, rd, rn);
|
||||
}
|
||||
@@ -1169,7 +1169,7 @@ public:
|
||||
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
|
||||
const auto ConvertedSize =
|
||||
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
|
||||
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc<T>(Op, 0, ConvertedSize, 0b11011, rd, rn);
|
||||
}
|
||||
@@ -1184,7 +1184,7 @@ public:
|
||||
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
|
||||
const auto ConvertedSize =
|
||||
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
|
||||
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc<T>(Op, 0, ConvertedSize, 0b11100, rd, rn);
|
||||
}
|
||||
@@ -1199,7 +1199,7 @@ public:
|
||||
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
|
||||
const auto ConvertedSize =
|
||||
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
|
||||
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc<T>(Op, 0, ConvertedSize, 0b11101, rd, rn);
|
||||
}
|
||||
@@ -1214,7 +1214,7 @@ public:
|
||||
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
|
||||
const auto ConvertedSize =
|
||||
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
|
||||
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc<T>(Op, 0, ConvertedSize, 0b11110, rd, rn);
|
||||
}
|
||||
@@ -1229,7 +1229,7 @@ public:
|
||||
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
|
||||
const auto ConvertedSize =
|
||||
size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
|
||||
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc<T>(Op, 0, ConvertedSize, 0b11111, rd, rn);
|
||||
}
|
||||
|
||||
@@ -6,4 +6,3 @@ mask \xff\xff\xff\xff\xff\xfe\xfe\x00\x00\x00\x00\xff\xff\xff\xff\xff\xfe\xff\xf
|
||||
credentials yes
|
||||
fix_binary yes
|
||||
preserve yes
|
||||
expose_interpreter optional
|
||||
@@ -6,4 +6,3 @@ mask \xff\xff\xff\xff\xff\xfe\xfe\x00\x00\x00\x00\xff\xff\xff\xff\xff\xfe\xff\xf
|
||||
credentials yes
|
||||
fix_binary yes
|
||||
preserve yes
|
||||
expose_interpreter optional
|
||||
Vendored
+1
-1
Submodule External/Vulkan-Headers updated: 31aa7f634b...29f979ee5a.
Vendored
+1
-1
Submodule External/drm-headers updated: 34a20394f7...8efb6dc03f.
Vendored
-1
Submodule External/imgui deleted from 4c986ecb8d.
Vendored
+1
-1
Submodule External/jemalloc updated: 7ae889695b...02ca52b5fe.
Vendored
+1
-1
Submodule External/jemalloc_glibc updated: 888181c5f7...404353974e.
@@ -401,7 +401,7 @@ def print_parse_argloader_options(options):
|
||||
conversion_func = "FEXCore::Config::Handler::{0}(".format(op_vals["ArgumentHandler"])
|
||||
if (value_type == "str"):
|
||||
NeedsString = True
|
||||
conversion_func = "("
|
||||
conversion_func = "std::move("
|
||||
if (value_type == "bool"):
|
||||
# boolean values need a decimal specifier. Otherwise fmt prints strings.
|
||||
conversion_func = "fextl::fmt::format(\"{:d}\", "
|
||||
|
||||
@@ -86,7 +86,6 @@ set (SRCS
|
||||
Common/SoftFloat-3e/s_f32UIToCommonNaN.c
|
||||
Interface/Context/Context.cpp
|
||||
Interface/Core/LookupCache.cpp
|
||||
Interface/Core/BlockSamplingData.cpp
|
||||
Interface/Core/Core.cpp
|
||||
Interface/Core/CPUBackend.cpp
|
||||
Interface/Core/CPUID.cpp
|
||||
@@ -119,7 +118,6 @@ set (SRCS
|
||||
Interface/Core/JIT/Arm64/Arm64Relocations.cpp
|
||||
Interface/Core/X86Tables/BaseTables.cpp
|
||||
Interface/Core/X86Tables/DDDTables.cpp
|
||||
Interface/Core/X86Tables/EVEXTables.cpp
|
||||
Interface/Core/X86Tables/H0F38Tables.cpp
|
||||
Interface/Core/X86Tables/H0F3ATables.cpp
|
||||
Interface/Core/X86Tables/PrimaryGroupTables.cpp
|
||||
@@ -129,7 +127,6 @@ set (SRCS
|
||||
Interface/Core/X86Tables/VEXTables.cpp
|
||||
Interface/Core/X86Tables/X87Tables.cpp
|
||||
Interface/Core/X86Tables/XOPTables.cpp
|
||||
Interface/HLE/Thunks/Thunks.cpp
|
||||
Interface/GDBJIT/GDBJIT.cpp
|
||||
Interface/IR/AOTIR.cpp
|
||||
Interface/IR/IRDumper.cpp
|
||||
@@ -140,7 +137,6 @@ set (SRCS
|
||||
Interface/IR/Passes/IRValidation.cpp
|
||||
Interface/IR/Passes/RAValidation.cpp
|
||||
Interface/IR/Passes/RedundantFlagCalculationElimination.cpp
|
||||
Interface/IR/Passes/DeadStoreElimination.cpp
|
||||
Interface/IR/Passes/RegisterAllocationPass.cpp
|
||||
Interface/IR/Passes/x87StackOptimizationPass.cpp
|
||||
Utils/Telemetry.cpp
|
||||
@@ -197,14 +193,6 @@ else()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (ENABLE_JEMALLOC)
|
||||
list (APPEND LIBS FEX_jemalloc)
|
||||
endif()
|
||||
|
||||
if (ENABLE_JEMALLOC_GLIBC_ALLOC)
|
||||
list (APPEND LIBS FEX_jemalloc_glibc)
|
||||
endif()
|
||||
|
||||
# Generate config
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Interface/Config/Config.json.in
|
||||
@@ -379,3 +367,25 @@ if (NOT MINGW_BUILD)
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
COMPONENT Libraries)
|
||||
endif()
|
||||
|
||||
# Meta-library to link jemalloc libraries enabled in the build configuration.
|
||||
# Only needed for targets that run emulation. For others, use JemallocDummy.
|
||||
add_library(JemallocLibs STATIC Utils/AllocatorHooks.cpp)
|
||||
if (ENABLE_JEMALLOC)
|
||||
target_compile_definitions(JemallocLibs PRIVATE ENABLE_JEMALLOC=1 JEMALLOC_NO_RENAME=1)
|
||||
target_link_libraries(JemallocLibs PUBLIC FEX_jemalloc)
|
||||
endif()
|
||||
if (ENABLE_JEMALLOC_GLIBC_ALLOC)
|
||||
set_source_files_properties(Interface/HLE/Thunks/Thunks.cpp PROPERTIES COMPILE_DEFINITIONS ENABLE_JEMALLOC_GLIBC=1)
|
||||
target_link_libraries(JemallocLibs INTERFACE FEX_jemalloc_glibc)
|
||||
endif()
|
||||
|
||||
if (NOT MINGW_BUILD)
|
||||
# Dummy project to use for host tools.
|
||||
# This overrides use of jemalloc in FEXCore with the normal glibc allocator.
|
||||
add_library(JemallocDummy STATIC Utils/AllocatorHooks.cpp)
|
||||
target_include_directories(JemallocDummy PRIVATE "${PROJECT_SOURCE_DIR}/include/")
|
||||
endif()
|
||||
|
||||
# The shared library should always link enabled jemalloc libraries
|
||||
target_link_libraries(${PROJECT_NAME}_shared JemallocLibs)
|
||||
@@ -55,7 +55,7 @@ void JITSymbols::RegisterJITSpace(const void* HostAddr, uint32_t CodeSize) {
|
||||
}
|
||||
|
||||
// Buffered JIT symbols.
|
||||
void JITSymbols::Register(Core::JITSymbolBuffer* Buffer, const void* HostAddr, uint64_t GuestAddr, uint32_t CodeSize) {
|
||||
void JITSymbols::Register(FEXCore::JITSymbolBuffer* Buffer, const void* HostAddr, uint64_t GuestAddr, uint32_t CodeSize) {
|
||||
if (fd == -1) {
|
||||
return;
|
||||
}
|
||||
@@ -79,7 +79,7 @@ void JITSymbols::Register(Core::JITSymbolBuffer* Buffer, const void* HostAddr, u
|
||||
WriteBuffer(Buffer);
|
||||
}
|
||||
|
||||
void JITSymbols::Register(Core::JITSymbolBuffer* Buffer, const void* HostAddr, uint32_t CodeSize, std::string_view Name, uintptr_t Offset) {
|
||||
void JITSymbols::Register(FEXCore::JITSymbolBuffer* Buffer, const void* HostAddr, uint32_t CodeSize, std::string_view Name, uintptr_t Offset) {
|
||||
if (fd == -1) {
|
||||
return;
|
||||
}
|
||||
@@ -104,7 +104,7 @@ void JITSymbols::Register(Core::JITSymbolBuffer* Buffer, const void* HostAddr, u
|
||||
WriteBuffer(Buffer);
|
||||
}
|
||||
|
||||
void JITSymbols::RegisterNamedRegion(Core::JITSymbolBuffer* Buffer, const void* HostAddr, uint32_t CodeSize, std::string_view Name) {
|
||||
void JITSymbols::RegisterNamedRegion(FEXCore::JITSymbolBuffer* Buffer, const void* HostAddr, uint32_t CodeSize, std::string_view Name) {
|
||||
if (fd == -1) {
|
||||
return;
|
||||
}
|
||||
@@ -128,7 +128,7 @@ void JITSymbols::RegisterNamedRegion(Core::JITSymbolBuffer* Buffer, const void*
|
||||
WriteBuffer(Buffer);
|
||||
}
|
||||
|
||||
void JITSymbols::WriteBuffer(Core::JITSymbolBuffer* Buffer, bool ForceWrite) {
|
||||
void JITSymbols::WriteBuffer(FEXCore::JITSymbolBuffer* Buffer, bool ForceWrite) {
|
||||
auto Now = std::chrono::steady_clock::now();
|
||||
if (!ForceWrite) {
|
||||
if (((Buffer->LastWrite - Now) < Buffer->MAXIMUM_THRESHOLD) && Buffer->Offset < Buffer->NEEDS_WRITE_DISTANCE) {
|
||||
|
||||
@@ -11,6 +11,26 @@
|
||||
#include <string_view>
|
||||
|
||||
namespace FEXCore {
|
||||
// Buffered JIT symbol tracking.
|
||||
struct JITSymbolBuffer {
|
||||
// Maximum buffer size to ensure we are a page in size.
|
||||
constexpr static size_t BUFFER_SIZE = 4096 - (8 * 2);
|
||||
// Maximum distance until the end of the buffer to do a write.
|
||||
constexpr static size_t NEEDS_WRITE_DISTANCE = BUFFER_SIZE - 64;
|
||||
// Maximum time threshhold to wait before a buffer write occurs.
|
||||
constexpr static std::chrono::milliseconds MAXIMUM_THRESHOLD {100};
|
||||
|
||||
JITSymbolBuffer()
|
||||
: LastWrite {std::chrono::steady_clock::now()} {}
|
||||
// stead_clock to ensure a monotonic increasing clock.
|
||||
// In highly stressed situations this can still cause >2% CPU time in vdso_clock_gettime.
|
||||
// If we need lower CPU time when JIT symbols are enabled then FEX can read the cycle counter directly.
|
||||
std::chrono::steady_clock::time_point LastWrite {};
|
||||
size_t Offset {};
|
||||
char Buffer[BUFFER_SIZE] {};
|
||||
};
|
||||
static_assert(sizeof(JITSymbolBuffer) == 4096, "Ensure this is one page in size");
|
||||
|
||||
class JITSymbols final {
|
||||
public:
|
||||
JITSymbols();
|
||||
@@ -21,16 +41,16 @@ public:
|
||||
void RegisterJITSpace(const void* HostAddr, uint32_t CodeSize);
|
||||
|
||||
// Allocate JIT buffer.
|
||||
static fextl::unique_ptr<Core::JITSymbolBuffer> AllocateBuffer() {
|
||||
return fextl::make_unique<Core::JITSymbolBuffer>();
|
||||
static fextl::unique_ptr<FEXCore::JITSymbolBuffer> AllocateBuffer() {
|
||||
return fextl::make_unique<FEXCore::JITSymbolBuffer>();
|
||||
}
|
||||
|
||||
void Register(Core::JITSymbolBuffer* Buffer, const void* HostAddr, uint64_t GuestAddr, uint32_t CodeSize);
|
||||
void Register(Core::JITSymbolBuffer* Buffer, const void* HostAddr, uint32_t CodeSize, std::string_view Name, uintptr_t Offset);
|
||||
void RegisterNamedRegion(Core::JITSymbolBuffer* Buffer, const void* HostAddr, uint32_t CodeSize, std::string_view Name);
|
||||
void Register(FEXCore::JITSymbolBuffer* Buffer, const void* HostAddr, uint64_t GuestAddr, uint32_t CodeSize);
|
||||
void Register(FEXCore::JITSymbolBuffer* Buffer, const void* HostAddr, uint32_t CodeSize, std::string_view Name, uintptr_t Offset);
|
||||
void RegisterNamedRegion(FEXCore::JITSymbolBuffer* Buffer, const void* HostAddr, uint32_t CodeSize, std::string_view Name);
|
||||
|
||||
private:
|
||||
int fd {-1};
|
||||
void WriteBuffer(Core::JITSymbolBuffer* Buffer, bool ForceWrite = false);
|
||||
void WriteBuffer(FEXCore::JITSymbolBuffer* Buffer, bool ForceWrite = false);
|
||||
};
|
||||
} // namespace FEXCore
|
||||
@@ -160,7 +160,31 @@ struct FEX_PACKED X80SoftFloat {
|
||||
|
||||
return Result;
|
||||
#else
|
||||
return extF80_rem(state, lhs, rhs);
|
||||
/*
|
||||
* FPREM is not an IEEE-754 remainder. From the spec:
|
||||
*
|
||||
* Computes the remainder obtained from dividing the value in the ST(0)
|
||||
* register (the dividend) by the value in the ST(1) register (the divisor
|
||||
* or modulus), and stores the result in ST(0). The remainder represents the
|
||||
* following value:
|
||||
*
|
||||
* Remainder := ST(0) − (Q * ST(1))
|
||||
*
|
||||
* Here, Q is an integer value that is obtained by truncating the
|
||||
* floating-point number quotient of [ST(0) / ST(1)] toward zero.
|
||||
*
|
||||
* We implement this sequence literally. softfloat_round_minMag means
|
||||
* "truncate towards zero".
|
||||
*/
|
||||
extFloat80_t quotient = extF80_div(state, lhs, rhs);
|
||||
extFloat80_t Q = extF80_roundToInt(state, quotient, softfloat_round_minMag, true);
|
||||
bool Q_zero = Q.signif == 0 && (Q.signExp & ~(1 << 15)) == 0;
|
||||
|
||||
if (Q_zero) {
|
||||
return lhs;
|
||||
} else {
|
||||
return extF80_sub(state, lhs, extF80_mul(state, Q, rhs));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -262,6 +286,10 @@ struct FEX_PACKED X80SoftFloat {
|
||||
|
||||
return Result;
|
||||
#else
|
||||
extFloat80_t Zero {0, 0};
|
||||
if (extF80_eq(state, lhs, Zero)) {
|
||||
return lhs;
|
||||
}
|
||||
X80SoftFloat Int = FRNDINT(state, rhs, softfloat_round_minMag);
|
||||
LIBRARY_PRECISION Src2_d = Int.ToFMax(state);
|
||||
Src2_d = exp2l(Src2_d);
|
||||
|
||||
@@ -43,7 +43,8 @@ namespace DefaultValues {
|
||||
} // namespace DefaultValues
|
||||
|
||||
enum Paths {
|
||||
PATH_DATA_DIR = 0,
|
||||
PATH_DATA_DIR_LOCAL = 0,
|
||||
PATH_DATA_DIR_GLOBAL,
|
||||
PATH_CONFIG_DIR_LOCAL,
|
||||
PATH_CONFIG_DIR_GLOBAL,
|
||||
PATH_CONFIG_FILE_LOCAL,
|
||||
@@ -53,8 +54,8 @@ enum Paths {
|
||||
};
|
||||
static std::array<fextl::string, Paths::PATH_LAST> Paths;
|
||||
|
||||
void SetDataDirectory(const std::string_view Path) {
|
||||
Paths[PATH_DATA_DIR] = Path;
|
||||
void SetDataDirectory(const std::string_view Path, bool Global) {
|
||||
Paths[PATH_DATA_DIR_LOCAL + Global] = Path;
|
||||
}
|
||||
|
||||
void SetConfigDirectory(const std::string_view Path, bool Global) {
|
||||
@@ -73,15 +74,15 @@ const fextl::string& GetTelemetryDirectory() {
|
||||
Path = TelemetryDirectory;
|
||||
Path += "/";
|
||||
} else {
|
||||
Path = Config::GetDataDirectory() + "Telemetry/";
|
||||
Path = Config::GetDataDirectory(false) + "Telemetry/";
|
||||
}
|
||||
}
|
||||
|
||||
return Path;
|
||||
}
|
||||
|
||||
const fextl::string& GetDataDirectory() {
|
||||
return Paths[PATH_DATA_DIR];
|
||||
const fextl::string& GetDataDirectory(bool Global) {
|
||||
return Paths[PATH_DATA_DIR_LOCAL + Global];
|
||||
}
|
||||
|
||||
const fextl::string& GetConfigDirectory(bool Global) {
|
||||
@@ -230,27 +231,26 @@ void Load() {
|
||||
}
|
||||
}
|
||||
|
||||
fextl::string ExpandPath(const fextl::string& ContainerPrefix, fextl::string PathName) {
|
||||
fextl::string ExpandPath(const fextl::string& ContainerPrefix, const fextl::string& PathName) {
|
||||
if (PathName.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
// Expand home if it exists
|
||||
if (FHU::Filesystem::IsRelative(PathName)) {
|
||||
fextl::string Home = getenv("HOME") ?: "";
|
||||
// Home expansion only works if it is the first character
|
||||
// This matches bash behaviour
|
||||
if (PathName.at(0) == '~') {
|
||||
PathName.replace(0, 1, Home);
|
||||
return PathName;
|
||||
if (PathName.starts_with("~/")) {
|
||||
Home.append(PathName.begin() + 1, PathName.end());
|
||||
return Home;
|
||||
}
|
||||
|
||||
// Expand relative path to absolute
|
||||
char ExistsTempPath[PATH_MAX];
|
||||
char* RealPath = FHU::Filesystem::Absolute(PathName.c_str(), ExistsTempPath);
|
||||
if (RealPath) {
|
||||
PathName = RealPath;
|
||||
if (RealPath && FHU::Filesystem::Exists(RealPath)) {
|
||||
return RealPath;
|
||||
}
|
||||
|
||||
// Only return if it exists
|
||||
@@ -318,81 +318,61 @@ fextl::string FindContainerPrefix() {
|
||||
void ReloadMetaLayer() {
|
||||
Meta->Load();
|
||||
|
||||
// Do configuration option fix ups after everything is reloaded
|
||||
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_CORE)) {
|
||||
// Sanitize Core option
|
||||
FEX_CONFIG_OPT(Core, CORE);
|
||||
#if (_M_X86_64)
|
||||
constexpr uint32_t MaxCoreNumber = 1;
|
||||
#else
|
||||
constexpr uint32_t MaxCoreNumber = 0;
|
||||
#endif
|
||||
if (Core > MaxCoreNumber) {
|
||||
// Sanitize the core option by setting the core to the JIT if invalid
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_CORE, fextl::fmt::format("{}", static_cast<uint32_t>(FEXCore::Config::CONFIG_IRJIT)));
|
||||
}
|
||||
}
|
||||
|
||||
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_CACHEOBJECTCODECOMPILATION)) {
|
||||
FEX_CONFIG_OPT(CacheObjectCodeCompilation, CACHEOBJECTCODECOMPILATION);
|
||||
FEX_CONFIG_OPT(Core, CORE);
|
||||
}
|
||||
|
||||
fextl::string ContainerPrefix {FindContainerPrefix()};
|
||||
auto ExpandPathIfExists = [&ContainerPrefix](FEXCore::Config::ConfigOption Config, fextl::string PathName) {
|
||||
auto NewPath = ExpandPath(ContainerPrefix, PathName);
|
||||
const fextl::string ContainerPrefix {FindContainerPrefix()};
|
||||
auto ExpandPathIfExists = [&ContainerPrefix](FEXCore::Config::ConfigOption Config, const fextl::string& PathName) {
|
||||
const auto NewPath = ExpandPath(ContainerPrefix, PathName);
|
||||
if (!NewPath.empty()) {
|
||||
FEXCore::Config::EraseSet(Config, NewPath);
|
||||
}
|
||||
};
|
||||
|
||||
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_ROOTFS)) {
|
||||
FEX_CONFIG_OPT(PathName, ROOTFS);
|
||||
auto ExpandedString = ExpandPath(ContainerPrefix, PathName());
|
||||
const auto PathName = *Meta->Get(FEXCore::Config::CONFIG_ROOTFS);
|
||||
const auto ExpandedString = ExpandPath(ContainerPrefix, *PathName);
|
||||
if (!ExpandedString.empty()) {
|
||||
// Adjust the path if it ended up being relative
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_ROOTFS, ExpandedString);
|
||||
} else if (!PathName().empty()) {
|
||||
} else if (!PathName->empty()) {
|
||||
// If the filesystem doesn't exist then let's see if it exists in the fex-emu folder
|
||||
fextl::string NamedRootFS = GetDataDirectory() + "RootFS/" + PathName();
|
||||
fextl::string NamedRootFS = GetDataDirectory(false) + "RootFS/" + *PathName;
|
||||
if (FHU::Filesystem::Exists(NamedRootFS)) {
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_ROOTFS, NamedRootFS);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_THUNKHOSTLIBS)) {
|
||||
FEX_CONFIG_OPT(PathName, THUNKHOSTLIBS);
|
||||
ExpandPathIfExists(FEXCore::Config::CONFIG_THUNKHOSTLIBS, PathName());
|
||||
const auto PathName = *Meta->Get(FEXCore::Config::CONFIG_THUNKHOSTLIBS);
|
||||
ExpandPathIfExists(FEXCore::Config::CONFIG_THUNKHOSTLIBS, *PathName);
|
||||
}
|
||||
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_THUNKGUESTLIBS)) {
|
||||
FEX_CONFIG_OPT(PathName, THUNKGUESTLIBS);
|
||||
ExpandPathIfExists(FEXCore::Config::CONFIG_THUNKGUESTLIBS, PathName());
|
||||
const auto PathName = *Meta->Get(FEXCore::Config::CONFIG_THUNKGUESTLIBS);
|
||||
ExpandPathIfExists(FEXCore::Config::CONFIG_THUNKGUESTLIBS, *PathName);
|
||||
}
|
||||
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_THUNKCONFIG)) {
|
||||
FEX_CONFIG_OPT(PathName, THUNKCONFIG);
|
||||
auto ExpandedString = ExpandPath(ContainerPrefix, PathName());
|
||||
const auto PathName = *Meta->Get(FEXCore::Config::CONFIG_THUNKCONFIG);
|
||||
const auto ExpandedString = ExpandPath(ContainerPrefix, *PathName);
|
||||
if (!ExpandedString.empty()) {
|
||||
// Adjust the path if it ended up being relative
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_THUNKCONFIG, ExpandedString);
|
||||
} else if (!PathName().empty()) {
|
||||
} else if (!PathName->empty()) {
|
||||
// If the filesystem doesn't exist then let's see if it exists in the fex-emu folder
|
||||
fextl::string NamedConfig = GetDataDirectory() + "ThunkConfigs/" + PathName();
|
||||
fextl::string NamedConfig = GetDataDirectory(false) + "ThunkConfigs/" + *PathName;
|
||||
if (FHU::Filesystem::Exists(NamedConfig)) {
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_THUNKCONFIG, NamedConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_OUTPUTLOG)) {
|
||||
FEX_CONFIG_OPT(PathName, OUTPUTLOG);
|
||||
if (PathName() != "stdout" && PathName() != "stderr" && PathName() != "server") {
|
||||
ExpandPathIfExists(FEXCore::Config::CONFIG_OUTPUTLOG, PathName());
|
||||
const auto PathName = *Meta->Get(FEXCore::Config::CONFIG_OUTPUTLOG);
|
||||
if (*PathName != "stdout" && *PathName != "stderr" && *PathName != "server") {
|
||||
ExpandPathIfExists(FEXCore::Config::CONFIG_OUTPUTLOG, *PathName);
|
||||
}
|
||||
}
|
||||
|
||||
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_DUMPIR) && !FEXCore::Config::Exists(FEXCore::Config::CONFIG_PASSMANAGERDUMPIR)) {
|
||||
// If DumpIR is set but no PassManagerDumpIR configuration is set, then default to `afteropt`
|
||||
FEX_CONFIG_OPT(PathName, DUMPIR);
|
||||
if (PathName() != "no") {
|
||||
const auto PathName = *Meta->Get(FEXCore::Config::CONFIG_DUMPIR);
|
||||
if (*PathName != "no") {
|
||||
EraseSet(FEXCore::Config::ConfigOption::CONFIG_PASSMANAGERDUMPIR,
|
||||
fextl::fmt::format("{}", static_cast<uint64_t>(FEXCore::Config::PassManagerDumpIR::AFTEROPT)));
|
||||
}
|
||||
|
||||
@@ -1,19 +1,6 @@
|
||||
{
|
||||
"Options": {
|
||||
"CPU": {
|
||||
"Core": {
|
||||
"Type": "uint32",
|
||||
"Default": "FEXCore::Config::ConfigCore::CONFIG_IRJIT",
|
||||
"TextDefault": "irjit",
|
||||
"ShortArg": "c",
|
||||
"Choices": [ "irjit", "host" ],
|
||||
"ArgumentHandler": "CoreHandler",
|
||||
"Desc": [
|
||||
"Which CPU core to use",
|
||||
"host only exists on x86_64",
|
||||
"[irjit, host]"
|
||||
]
|
||||
},
|
||||
"Multiblock": {
|
||||
"Type": "bool",
|
||||
"Default": "false",
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <FEXCore/Core/CPUID.h>
|
||||
#include <FEXCore/Core/HostFeatures.h>
|
||||
#include <FEXCore/Core/SignalDelegator.h>
|
||||
#include <FEXCore/Core/Thunks.h>
|
||||
#include "FEXCore/Debug/InternalThreadState.h"
|
||||
|
||||
#include <string.h>
|
||||
@@ -39,10 +40,6 @@ void FEXCore::Context::ContextImpl::CompileRIPCount(FEXCore::Core::InternalThrea
|
||||
CompileBlock(Thread->CurrentFrame, GuestRIP, MaxInst);
|
||||
}
|
||||
|
||||
void FEXCore::Context::ContextImpl::SetCustomCPUBackendFactory(CustomCPUFactoryType Factory) {
|
||||
CustomCPUFactory = std::move(Factory);
|
||||
}
|
||||
|
||||
void FEXCore::Context::ContextImpl::SetSignalDelegator(FEXCore::SignalDelegator* _SignalDelegation) {
|
||||
SignalDelegation = _SignalDelegation;
|
||||
}
|
||||
@@ -52,6 +49,10 @@ void FEXCore::Context::ContextImpl::SetSyscallHandler(FEXCore::HLE::SyscallHandl
|
||||
SourcecodeResolver = Handler->GetSourcecodeResolver();
|
||||
}
|
||||
|
||||
void FEXCore::Context::ContextImpl::SetThunkHandler(FEXCore::ThunkHandler* Handler) {
|
||||
ThunkHandler = Handler;
|
||||
}
|
||||
|
||||
FEXCore::CPUID::FunctionResults FEXCore::Context::ContextImpl::RunCPUIDFunction(uint32_t Function, uint32_t Leaf) {
|
||||
return CPUID.RunFunction(Function, Leaf);
|
||||
}
|
||||
|
||||
@@ -26,14 +26,8 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <istream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <stddef.h>
|
||||
#include <queue>
|
||||
|
||||
namespace FEXCore {
|
||||
class CodeLoader;
|
||||
@@ -65,16 +59,20 @@ namespace Validation {
|
||||
} // namespace FEXCore::IR
|
||||
|
||||
namespace FEXCore::Context {
|
||||
enum CoreRunningMode {
|
||||
MODE_RUN = 0,
|
||||
MODE_SINGLESTEP = 1,
|
||||
};
|
||||
|
||||
struct ExitFunctionLinkData {
|
||||
uint64_t HostBranch;
|
||||
uint64_t GuestRIP;
|
||||
};
|
||||
|
||||
struct CustomIRResult {
|
||||
void* Creator;
|
||||
void* Data;
|
||||
|
||||
CustomIRResult(void* Creator, void* Data)
|
||||
: Creator(Creator)
|
||||
, Data(Data) {}
|
||||
};
|
||||
|
||||
using BlockDelinkerFunc = void (*)(FEXCore::Core::CpuStateFrame* Frame, FEXCore::Context::ExitFunctionLinkData* Record);
|
||||
constexpr uint32_t TSC_SCALE_MAXIMUM = 1'000'000'000; ///< 1Ghz
|
||||
|
||||
@@ -93,8 +91,6 @@ public:
|
||||
void CompileRIP(FEXCore::Core::InternalThreadState* Thread, uint64_t GuestRIP) override;
|
||||
void CompileRIPCount(FEXCore::Core::InternalThreadState* Thread, uint64_t GuestRIP, uint64_t MaxInst) override;
|
||||
|
||||
void SetCustomCPUBackendFactory(CustomCPUFactoryType Factory) override;
|
||||
|
||||
void HandleCallback(FEXCore::Core::InternalThreadState* Thread, uint64_t RIP) override;
|
||||
|
||||
uint64_t RestoreRIPFromHostPC(FEXCore::Core::InternalThreadState* Thread, uint64_t HostPC) override;
|
||||
@@ -133,7 +129,7 @@ public:
|
||||
*/
|
||||
|
||||
FEXCore::Core::InternalThreadState*
|
||||
CreateThread(uint64_t InitialRIP, uint64_t StackPointer, FEXCore::Core::CPUState* NewThreadState, uint64_t ParentTID) override;
|
||||
CreateThread(uint64_t InitialRIP, uint64_t StackPointer, const FEXCore::Core::CPUState* NewThreadState, uint64_t ParentTID) override;
|
||||
|
||||
// Public for threading
|
||||
void ExecutionThread(FEXCore::Core::InternalThreadState* Thread) override;
|
||||
@@ -151,6 +147,7 @@ public:
|
||||
#endif
|
||||
void SetSignalDelegator(FEXCore::SignalDelegator* SignalDelegation) override;
|
||||
void SetSyscallHandler(FEXCore::HLE::SyscallHandler* Handler) override;
|
||||
void SetThunkHandler(FEXCore::ThunkHandler* Handler) override;
|
||||
|
||||
FEXCore::CPUID::FunctionResults RunCPUIDFunction(uint32_t Function, uint32_t Leaf) override;
|
||||
FEXCore::CPUID::XCRResults RunXCRFunction(uint32_t Function) override;
|
||||
@@ -190,9 +187,10 @@ public:
|
||||
bool IsAddressInCodeBuffer(FEXCore::Core::InternalThreadState* Thread, uintptr_t Address) const override;
|
||||
|
||||
// returns false if a handler was already registered
|
||||
CustomIRResult AddCustomIREntrypoint(uintptr_t Entrypoint, CustomIREntrypointHandler Handler, void* Creator = nullptr, void* Data = nullptr);
|
||||
std::optional<CustomIRResult>
|
||||
AddCustomIREntrypoint(uintptr_t Entrypoint, CustomIREntrypointHandler Handler, void* Creator = nullptr, void* Data = nullptr);
|
||||
|
||||
void AppendThunkDefinitions(std::span<const FEXCore::IR::ThunkDefinition> Definitions) override;
|
||||
void AddThunkTrampolineIRHandler(uintptr_t Entrypoint, uintptr_t GuestThunkEntrypoint) override;
|
||||
|
||||
public:
|
||||
friend class FEXCore::HLE::SyscallHandler;
|
||||
@@ -203,7 +201,6 @@ public:
|
||||
friend class FEXCore::IR::Validation::IRValidation;
|
||||
|
||||
struct {
|
||||
CoreRunningMode RunningMode {CoreRunningMode::MODE_RUN};
|
||||
uint64_t VirtualMemSize {1ULL << 36};
|
||||
uint64_t TSCScale = 0;
|
||||
|
||||
@@ -223,12 +220,8 @@ public:
|
||||
FEX_CONFIG_OPT(AOTIRGenerate, AOTIRGENERATE);
|
||||
FEX_CONFIG_OPT(AOTIRLoad, AOTIRLOAD);
|
||||
FEX_CONFIG_OPT(SMCChecks, SMCCHECKS);
|
||||
FEX_CONFIG_OPT(Core, CORE);
|
||||
FEX_CONFIG_OPT(MaxInstPerBlock, MAXINST);
|
||||
FEX_CONFIG_OPT(RootFSPath, ROOTFS);
|
||||
FEX_CONFIG_OPT(ThunkHostLibsPath, THUNKHOSTLIBS);
|
||||
FEX_CONFIG_OPT(ThunkHostLibsPath32, THUNKHOSTLIBS32);
|
||||
FEX_CONFIG_OPT(ThunkConfigFile, THUNKCONFIG);
|
||||
FEX_CONFIG_OPT(GlobalJITNaming, GLOBALJITNAMING);
|
||||
FEX_CONFIG_OPT(LibraryJITNaming, LIBRARYJITNAMING);
|
||||
FEX_CONFIG_OPT(BlockJITNaming, BLOCKJITNAMING);
|
||||
@@ -253,16 +246,11 @@ public:
|
||||
FEXCore::CPUIDEmu CPUID;
|
||||
FEXCore::HLE::SyscallHandler* SyscallHandler {};
|
||||
FEXCore::HLE::SourcecodeResolver* SourcecodeResolver {};
|
||||
fextl::unique_ptr<FEXCore::ThunkHandler> ThunkHandler;
|
||||
FEXCore::ThunkHandler* ThunkHandler {};
|
||||
fextl::unique_ptr<FEXCore::CPU::Dispatcher> Dispatcher;
|
||||
|
||||
CustomCPUFactoryType CustomCPUFactory;
|
||||
FEXCore::Context::ExitHandler CustomExitHandler;
|
||||
|
||||
#ifdef BLOCKSTATS
|
||||
fextl::unique_ptr<FEXCore::BlockSamplingData> BlockData;
|
||||
#endif
|
||||
|
||||
SignalDelegator* SignalDelegation {};
|
||||
X86GeneratedCode X86CodeGen;
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "FEXCore/Utils/AllocatorHooks.h"
|
||||
#include "Interface/Core/Dispatcher/Dispatcher.h"
|
||||
#include "Interface/Context/Context.h"
|
||||
#include "Interface/HLE/Thunks/Thunks.h"
|
||||
|
||||
#include <FEXCore/Core/CoreState.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#include "Interface/Core/BlockSamplingData.h"
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <utility>
|
||||
|
||||
namespace FEXCore {
|
||||
void BlockSamplingData::DumpBlockData() {
|
||||
std::fstream Output;
|
||||
Output.open("output.csv", std::fstream::out | std::fstream::binary);
|
||||
|
||||
if (!Output.is_open()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Output << "Entry, Min, Max, Total, Calls, Average" << std::endl;
|
||||
|
||||
for (auto it : SamplingMap) {
|
||||
if (!it.second->TotalCalls) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Output << "0x" << std::hex << it.first << ", " << std::dec << it.second->Min << ", " << std::dec << it.second->Max << ", " << std::dec
|
||||
<< it.second->TotalTime << ", " << std::dec << it.second->TotalCalls << ", " << std::dec
|
||||
<< ((double)it.second->TotalTime / (double)it.second->TotalCalls) << std::endl;
|
||||
}
|
||||
Output.close();
|
||||
LogMan::Msg::DFmt("Dumped {} blocks of sampling data", SamplingMap.size());
|
||||
}
|
||||
|
||||
BlockSamplingData::BlockData* BlockSamplingData::GetBlockData(uint64_t RIP) {
|
||||
auto it = SamplingMap.find(RIP);
|
||||
if (it != SamplingMap.end()) {
|
||||
return it->second;
|
||||
}
|
||||
BlockData* NewData = new BlockData {};
|
||||
memset(NewData, 0, sizeof(BlockData));
|
||||
NewData->Min = ~0ULL;
|
||||
SamplingMap[RIP] = NewData;
|
||||
return NewData;
|
||||
}
|
||||
|
||||
BlockSamplingData::~BlockSamplingData() {
|
||||
DumpBlockData();
|
||||
for (auto it : SamplingMap) {
|
||||
delete it.second;
|
||||
}
|
||||
SamplingMap.clear();
|
||||
}
|
||||
} // namespace FEXCore
|
||||
@@ -1,25 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace FEXCore {
|
||||
class BlockSamplingData {
|
||||
public:
|
||||
struct BlockData {
|
||||
uint64_t Start, End;
|
||||
uint64_t Min, Max;
|
||||
uint64_t TotalTime;
|
||||
uint64_t TotalCalls;
|
||||
};
|
||||
|
||||
BlockData* GetBlockData(uint64_t RIP);
|
||||
~BlockSamplingData();
|
||||
|
||||
void DumpBlockData();
|
||||
|
||||
private:
|
||||
std::unordered_map<uint64_t, BlockData*> SamplingMap;
|
||||
};
|
||||
} // namespace FEXCore
|
||||
@@ -48,11 +48,6 @@ namespace CPU {
|
||||
CPUBackend(FEXCore::Core::InternalThreadState* ThreadState, size_t InitialCodeSize, size_t MaxCodeSize);
|
||||
|
||||
virtual ~CPUBackend();
|
||||
/**
|
||||
* @return The name of this backend
|
||||
*/
|
||||
[[nodiscard]]
|
||||
virtual fextl::string GetName() = 0;
|
||||
|
||||
struct CompiledCode {
|
||||
// Where this code block begins.
|
||||
@@ -124,9 +119,6 @@ namespace CPU {
|
||||
*
|
||||
* This is a thread specific compilation unit since there is one CPUBackend per guest thread
|
||||
*
|
||||
* If NeedsOpDispatch is returning false then IR and DebugData may be null and the expectation is that the code will still compile
|
||||
* FEXCore::Core::ThreadState* is valid at the time of compilation.
|
||||
*
|
||||
* @param IR - IR that maps to the IR for this RIP
|
||||
* @param DebugData - Debug data that is available for this IR indirectly
|
||||
*
|
||||
@@ -149,26 +141,6 @@ namespace CPU {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Function for mapping memory in to the CPUBackend's visible space. Allows setting up virtual mappings if required
|
||||
*
|
||||
* @return Currently unused
|
||||
*/
|
||||
[[nodiscard]]
|
||||
virtual void* MapRegion(void* HostPtr, uint64_t GuestPtr, uint64_t Size) = 0;
|
||||
|
||||
/**
|
||||
* @brief Lets FEXCore know if this CPUBackend needs IR and DebugData for CompileCode
|
||||
*
|
||||
* This is useful if the FEXCore Frontend hits an x86-64 instruction that isn't understood but can continue regardless
|
||||
*
|
||||
* This is useful for example, a VM based CPUbackend
|
||||
*
|
||||
* @return true if it needs the IR
|
||||
*/
|
||||
[[nodiscard]]
|
||||
virtual bool NeedsOpDispatch() = 0;
|
||||
|
||||
virtual void ClearCache() {}
|
||||
|
||||
/**
|
||||
|
||||
@@ -96,20 +96,39 @@ static uint32_t GetCPUID() {
|
||||
return CPU;
|
||||
}
|
||||
|
||||
struct CPUFamily {
|
||||
uint32_t Stepping : 4;
|
||||
uint32_t Model : 4;
|
||||
uint32_t ExtendedModel : 4;
|
||||
uint32_t FamilyID : 4;
|
||||
uint32_t ExtendedFamilyID : 8;
|
||||
uint32_t ProcessorType : 4;
|
||||
};
|
||||
|
||||
constexpr static uint32_t GenerateFamily(const CPUFamily Family) {
|
||||
return Family.Stepping | (Family.Model << 4) | (Family.FamilyID << 8) | (Family.ProcessorType << 12) | (Family.ExtendedModel << 16) |
|
||||
(Family.ExtendedFamilyID << 20);
|
||||
}
|
||||
|
||||
#ifdef CPUID_AMD
|
||||
constexpr uint32_t FAMILY_IDENTIFIER = 0 | // Stepping
|
||||
(0xA << 4) | // Model
|
||||
(0xF << 8) | // Family ID
|
||||
(0 << 12) | // Processor type
|
||||
(0 << 16) | // Extended model ID
|
||||
(1 << 20); // Extended family ID
|
||||
constexpr uint32_t FAMILY_IDENTIFIER = GenerateFamily(CPUFamily {
|
||||
.Stepping = 0,
|
||||
.Model = 0xA,
|
||||
.ExtendedModel = 0,
|
||||
.FamilyID = 0xF,
|
||||
.ExtendedFamilyID = 1,
|
||||
.ProcessorType = 0,
|
||||
});
|
||||
|
||||
#else
|
||||
constexpr uint32_t FAMILY_IDENTIFIER = 0 | // Stepping
|
||||
(0x7 << 4) | // Model
|
||||
(0x6 << 8) | // Family ID
|
||||
(0 << 12) | // Processor type
|
||||
(1 << 16) | // Extended model ID
|
||||
(0x0 << 20); // Extended family ID
|
||||
constexpr uint32_t FAMILY_IDENTIFIER = GenerateFamily(CPUFamily {
|
||||
.Stepping = 1,
|
||||
.Model = 6,
|
||||
.ExtendedModel = 0xA,
|
||||
.FamilyID = 6,
|
||||
.ExtendedFamilyID = 0,
|
||||
.ProcessorType = 0,
|
||||
});
|
||||
#endif
|
||||
|
||||
#ifdef _M_ARM_64
|
||||
|
||||
@@ -9,7 +9,6 @@ $end_info$
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include "Interface/Context/Context.h"
|
||||
#include "Interface/Core/ArchHelpers//Arm64Emitter.h"
|
||||
#include "Interface/Core/LookupCache.h"
|
||||
#include "Interface/Core/CPUBackend.h"
|
||||
@@ -20,7 +19,6 @@ $end_info$
|
||||
#include "Interface/Core/JIT/JITCore.h"
|
||||
#include "Interface/Core/Dispatcher/Dispatcher.h"
|
||||
#include "Interface/Core/X86Tables/X86Tables.h"
|
||||
#include "Interface/HLE/Thunks/Thunks.h"
|
||||
#include "Interface/IR/IR.h"
|
||||
#include "Interface/IR/IREmitter.h"
|
||||
#include "Interface/IR/Passes/RegisterAllocationPass.h"
|
||||
@@ -35,6 +33,7 @@ $end_info$
|
||||
#include <FEXCore/Core/Context.h>
|
||||
#include <FEXCore/Core/CoreState.h>
|
||||
#include <FEXCore/Core/SignalDelegator.h>
|
||||
#include <FEXCore/Core/Thunks.h>
|
||||
#include <FEXCore/Core/X86Enums.h>
|
||||
#include <FEXCore/Debug/InternalThreadState.h>
|
||||
#include <FEXCore/HLE/SyscallHandler.h>
|
||||
@@ -79,9 +78,6 @@ ContextImpl::ContextImpl(const FEXCore::HostFeatures& Features)
|
||||
: HostFeatures {Features}
|
||||
, CPUID {this}
|
||||
, IRCaptureCache {this} {
|
||||
#ifdef BLOCKSTATS
|
||||
BlockData = std::make_unique<FEXCore::BlockSamplingData>();
|
||||
#endif
|
||||
if (Config.CacheObjectCodeCompilation() != FEXCore::Config::ConfigObjectCodeHandler::CONFIG_NONE) {
|
||||
CodeObjectCacheService = fextl::make_unique<FEXCore::CodeSerialize::CodeObjectSerializeService>(this);
|
||||
}
|
||||
@@ -322,8 +318,6 @@ bool ContextImpl::InitCore() {
|
||||
|
||||
// Set up the SignalDelegator config since core is initialized.
|
||||
FEXCore::SignalDelegator::SignalDelegatorConfig SignalConfig {
|
||||
.SupportsAVX = HostFeatures.SupportsAVX,
|
||||
|
||||
.DispatcherBegin = Dispatcher->Start,
|
||||
.DispatcherEnd = Dispatcher->End,
|
||||
|
||||
@@ -352,7 +346,6 @@ bool ContextImpl::InitCore() {
|
||||
SignalDelegation->SetConfig(SignalConfig);
|
||||
|
||||
#ifndef _WIN32
|
||||
ThunkHandler = FEXCore::ThunkHandler::Create();
|
||||
#elif !defined(_M_ARM64EC)
|
||||
// WOW64 always needs the interrupt fault check to be enabled.
|
||||
Config.NeedsPendingInterruptFaultCheck = true;
|
||||
@@ -392,9 +385,6 @@ void ContextImpl::ExecuteThread(FEXCore::Core::InternalThreadState* Thread) {
|
||||
|
||||
void ContextImpl::InitializeThreadTLSData(FEXCore::Core::InternalThreadState* Thread) {
|
||||
// Let's do some initial bookkeeping here
|
||||
if (ThunkHandler) {
|
||||
ThunkHandler->RegisterTLSState(Thread);
|
||||
}
|
||||
#ifndef _WIN32
|
||||
Alloc::OSAllocator::RegisterTLSData(Thread);
|
||||
#endif
|
||||
@@ -420,20 +410,14 @@ void ContextImpl::InitializeCompiler(FEXCore::Core::InternalThreadState* Thread)
|
||||
Thread->PassManager->RegisterSyscallHandler(SyscallHandler);
|
||||
|
||||
// Create CPU backend
|
||||
switch (Config.Core) {
|
||||
case FEXCore::Config::CONFIG_IRJIT:
|
||||
Thread->PassManager->InsertRegisterAllocationPass();
|
||||
Thread->CPUBackend = FEXCore::CPU::CreateArm64JITCore(this, Thread);
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_CUSTOM: Thread->CPUBackend = CustomCPUFactory(this, Thread); break;
|
||||
default: ERROR_AND_DIE_FMT("Unknown core configuration"); break;
|
||||
}
|
||||
Thread->PassManager->InsertRegisterAllocationPass();
|
||||
Thread->CPUBackend = FEXCore::CPU::CreateArm64JITCore(this, Thread);
|
||||
|
||||
Thread->PassManager->Finalize();
|
||||
}
|
||||
|
||||
FEXCore::Core::InternalThreadState*
|
||||
ContextImpl::CreateThread(uint64_t InitialRIP, uint64_t StackPointer, FEXCore::Core::CPUState* NewThreadState, uint64_t ParentTID) {
|
||||
ContextImpl::CreateThread(uint64_t InitialRIP, uint64_t StackPointer, const FEXCore::Core::CPUState* NewThreadState, uint64_t ParentTID) {
|
||||
FEXCore::Core::InternalThreadState* Thread = new FEXCore::Core::InternalThreadState {};
|
||||
|
||||
Thread->CurrentFrame->State.gregs[X86State::REG_RSP] = StackPointer;
|
||||
@@ -994,7 +978,8 @@ void ContextImpl::ThreadRemoveCodeEntry(FEXCore::Core::InternalThreadState* Thre
|
||||
Thread->LookupCache->Erase(Thread->CurrentFrame, GuestRIP);
|
||||
}
|
||||
|
||||
CustomIRResult ContextImpl::AddCustomIREntrypoint(uintptr_t Entrypoint, CustomIREntrypointHandler Handler, void* Creator, void* Data) {
|
||||
std::optional<CustomIRResult>
|
||||
ContextImpl::AddCustomIREntrypoint(uintptr_t Entrypoint, CustomIREntrypointHandler Handler, void* Creator, void* Data) {
|
||||
LOGMAN_THROW_A_FMT(Config.Is64BitMode || !(Entrypoint >> 32), "64-bit Entrypoint in 32-bit mode {:x}", Entrypoint);
|
||||
|
||||
std::unique_lock lk(CustomIRMutex);
|
||||
@@ -1004,10 +989,50 @@ CustomIRResult ContextImpl::AddCustomIREntrypoint(uintptr_t Entrypoint, CustomIR
|
||||
|
||||
if (!InsertedIterator.second) {
|
||||
const auto& [fn, Creator, Data] = InsertedIterator.first->second;
|
||||
return CustomIRResult(std::move(lk), Creator, Data);
|
||||
} else {
|
||||
lk.unlock();
|
||||
return CustomIRResult(std::move(lk), 0, 0);
|
||||
return CustomIRResult(Creator, Data);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void ContextImpl::AddThunkTrampolineIRHandler(uintptr_t Entrypoint, uintptr_t GuestThunkEntrypoint) {
|
||||
LOGMAN_THROW_AA_FMT(Entrypoint, "Tried to link null pointer address to guest function");
|
||||
LOGMAN_THROW_AA_FMT(GuestThunkEntrypoint, "Tried to link address to null pointer guest function");
|
||||
if (!Config.Is64BitMode) {
|
||||
LOGMAN_THROW_AA_FMT((Entrypoint >> 32) == 0, "Tried to link 64-bit address in 32-bit mode");
|
||||
LOGMAN_THROW_AA_FMT((GuestThunkEntrypoint >> 32) == 0, "Tried to link 64-bit address in 32-bit mode");
|
||||
}
|
||||
|
||||
LogMan::Msg::DFmt("Thunks: Adding guest trampoline from address {:#x} to guest function {:#x}", Entrypoint, GuestThunkEntrypoint);
|
||||
|
||||
auto Result = AddCustomIREntrypoint(
|
||||
Entrypoint,
|
||||
[this, GuestThunkEntrypoint](uintptr_t Entrypoint, FEXCore::IR::IREmitter* emit) {
|
||||
auto IRHeader = emit->_IRHeader(emit->Invalid(), Entrypoint, 0, 0);
|
||||
auto Block = emit->CreateCodeNode();
|
||||
IRHeader.first->Blocks = emit->WrapNode(Block);
|
||||
emit->SetCurrentCodeBlock(Block);
|
||||
|
||||
const uint8_t GPRSize = GetGPRSize();
|
||||
|
||||
if (GPRSize == 8) {
|
||||
emit->_StoreRegister(emit->_Constant(Entrypoint), X86State::REG_R11, IR::GPRClass, GPRSize);
|
||||
} else {
|
||||
emit->_StoreContext(GPRSize, IR::FPRClass, emit->_VCastFromGPR(8, 8, emit->_Constant(Entrypoint)), offsetof(Core::CPUState, mm[0][0]));
|
||||
}
|
||||
emit->_ExitFunction(emit->_Constant(GuestThunkEntrypoint));
|
||||
},
|
||||
ThunkHandler, (void*)GuestThunkEntrypoint);
|
||||
|
||||
if (Result.has_value()) {
|
||||
if (Result->Creator != ThunkHandler) {
|
||||
ERROR_AND_DIE_FMT("Input address for AddThunkTrampoline is already linked by another module");
|
||||
}
|
||||
if (Result->Data != (void*)GuestThunkEntrypoint) {
|
||||
// NOTE: This may happen in Vulkan thunks if the Vulkan driver resolves two different symbols
|
||||
// to the same function (e.g. vkGetPhysicalDeviceFeatures2/vkGetPhysicalDeviceFeatures2KHR)
|
||||
LogMan::Msg::EFmt("Input address for AddThunkTrampoline is already linked elsewhere");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1030,12 +1055,6 @@ void ContextImpl::UnloadAOTIRCacheEntry(IR::AOTIRCacheEntry* Entry) {
|
||||
IRCaptureCache.UnloadAOTIRCacheEntry(Entry);
|
||||
}
|
||||
|
||||
void ContextImpl::AppendThunkDefinitions(std::span<const FEXCore::IR::ThunkDefinition> Definitions) {
|
||||
if (ThunkHandler) {
|
||||
ThunkHandler->AppendThunkDefinitions(Definitions);
|
||||
}
|
||||
}
|
||||
|
||||
void ContextImpl::ConfigureAOTGen(FEXCore::Core::InternalThreadState* Thread, fextl::set<uint64_t>* ExternalBranches, uint64_t SectionMaxAddress) {
|
||||
Thread->FrontendDecoder->SetExternalBranches(ExternalBranches);
|
||||
Thread->FrontendDecoder->SetSectionMaxAddress(SectionMaxAddress);
|
||||
|
||||
@@ -690,12 +690,8 @@ bool Decoder::NormalOpHeader(const FEXCore::X86Tables::X86InstInfo* Info, uint16
|
||||
}
|
||||
} else if (Info->Type == FEXCore::X86Tables::TYPE_GROUP_EVEX) {
|
||||
FEXCORE_TELEMETRY_SET(EVEXOpTelem, 1);
|
||||
|
||||
/* uint8_t P1 = */ ReadByte();
|
||||
/* uint8_t P2 = */ ReadByte();
|
||||
/* uint8_t P3 = */ ReadByte();
|
||||
uint8_t EVEXOp = ReadByte();
|
||||
return NormalOp(&EVEXTableOps[EVEXOp], EVEXOp);
|
||||
// EVEX unsupported
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGMAN_MSG_A_FMT("Invalid instruction decoding type");
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static softfloat_state SoftFloatStateFromFCW(uint16_t FCW) {
|
||||
softfloat_state State;
|
||||
softfloat_state State {};
|
||||
State.detectTininess = softfloat_tininess_afterRounding;
|
||||
State.exceptionFlags = 0;
|
||||
|
||||
@@ -322,8 +322,11 @@ struct OpHandlers<IR::OP_F64FYL2X> {
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F64SCALE> {
|
||||
static double handle(uint16_t FCW, double src1, double src2) {
|
||||
double trunc = (double)(int64_t)(src2); // truncate
|
||||
return src1 * exp2(trunc);
|
||||
if (src1 == 0.0) { // src1 might be +/- zero
|
||||
return src1; // this will return negative or positive zero if when appropriate
|
||||
}
|
||||
double trun = trunc(src2);
|
||||
return src1 * exp2(trun);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ tags: backend|arm64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "CodeEmitter/Emitter.h"
|
||||
#include "FEXCore/IR/IR.h"
|
||||
#include "Interface/Context/Context.h"
|
||||
#include "Interface/Core/JIT/Arm64/JITClass.h"
|
||||
@@ -190,6 +191,30 @@ DEF_OP(TestNZ) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(TestZ) {
|
||||
auto Op = IROp->C<IR::IROp_TestZ>();
|
||||
LOGMAN_THROW_AA_FMT(IROp->Size < 4, "TestNZ used at higher sizes");
|
||||
const auto EmitSize = ARMEmitter::Size::i32Bit;
|
||||
|
||||
uint64_t Const;
|
||||
uint64_t Mask = IROp->Size == 8 ? ~0ULL : ((1ull << (IROp->Size * 8)) - 1);
|
||||
auto Src1 = GetReg(Op->Src1.ID());
|
||||
|
||||
if (IsInlineConstant(Op->Src2, &Const)) {
|
||||
// We can promote 8/16-bit tests to 32-bit since the constant is masked.
|
||||
LOGMAN_THROW_AA_FMT(!(Const & ~Mask), "constant is already masked");
|
||||
tst(EmitSize, Src1, Const);
|
||||
} else {
|
||||
const auto Src2 = GetReg(Op->Src2.ID());
|
||||
if (Src1 == Src2) {
|
||||
tst(EmitSize, Src1 /* Src2 */, Mask);
|
||||
} else {
|
||||
and_(EmitSize, TMP1, Src1, Src2);
|
||||
tst(EmitSize, TMP1, Mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(SubShift) {
|
||||
auto Op = IROp->C<IR::IROp_SubShift>();
|
||||
|
||||
@@ -272,8 +297,43 @@ DEF_OP(SetSmallNZV) {
|
||||
}
|
||||
|
||||
DEF_OP(AXFlag) {
|
||||
LOGMAN_THROW_A_FMT(CTX->HostFeatures.SupportsFlagM2, "Unsupported flagm2 op");
|
||||
axflag();
|
||||
if (CTX->HostFeatures.SupportsFlagM2) {
|
||||
axflag();
|
||||
} else {
|
||||
// AXFLAG is defined in the Arm spec as
|
||||
//
|
||||
// gt: nzCv -> nzCv
|
||||
// lt: Nzcv -> nzcv <==> 1 + 0
|
||||
// eq: nZCv -> nZCv <==> 1 + (~0)
|
||||
// un: nzCV -> nZcv <==> 0 + 0
|
||||
//
|
||||
// For the latter 3 cases, we therefore get the right NZCV by adding V_inv
|
||||
// to (eq ? ~0 : 0). The remaining case is forced with ccmn.
|
||||
auto V_inv = GetReg(IROp->Args[0].ID());
|
||||
csetm(ARMEmitter::Size::i64Bit, TMP1, ARMEmitter::Condition::CC_EQ);
|
||||
ccmn(ARMEmitter::Size::i64Bit, V_inv, TMP1, ARMEmitter::StatusFlags {0x2} /* nzCv */, ARMEmitter::Condition::CC_LE);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Parity) {
|
||||
auto Op = IROp->C<IR::IROp_Parity>();
|
||||
auto Raw = GetReg(Op->Raw.ID());
|
||||
auto Dest = GetReg(Node);
|
||||
|
||||
// Cascade to calculate parity of bottom 8-bits to bottom bit.
|
||||
eor(ARMEmitter::Size::i32Bit, TMP1, Raw, Raw, ARMEmitter::ShiftType::LSR, 4);
|
||||
eor(ARMEmitter::Size::i32Bit, TMP1, TMP1, TMP1, ARMEmitter::ShiftType::LSR, 2);
|
||||
|
||||
if (Op->Invert) {
|
||||
eon(ARMEmitter::Size::i32Bit, Dest, TMP1, TMP1, ARMEmitter::ShiftType::LSR, 1);
|
||||
} else {
|
||||
eor(ARMEmitter::Size::i32Bit, Dest, TMP1, TMP1, ARMEmitter::ShiftType::LSR, 1);
|
||||
}
|
||||
|
||||
// The above sequence leaves garbage in the upper bits.
|
||||
if (Op->Mask) {
|
||||
and_(ARMEmitter::Size::i32Bit, Dest, Dest, 1);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(CondAddNZCV) {
|
||||
|
||||
@@ -7,7 +7,8 @@ $end_info$
|
||||
*/
|
||||
#include "Interface/Context/Context.h"
|
||||
#include "Interface/Core/JIT/Arm64/JITClass.h"
|
||||
#include "Interface/HLE/Thunks/Thunks.h"
|
||||
|
||||
#include <FEXCore/Core/Thunks.h>
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
|
||||
|
||||
@@ -11,11 +11,11 @@ $end_info$
|
||||
|
||||
#include "Interface/Core/JIT/Arm64/JITClass.h"
|
||||
|
||||
#include <FEXCore/Core/Thunks.h>
|
||||
#include <FEXCore/Core/X86Enums.h>
|
||||
#include <FEXCore/Debug/InternalThreadState.h>
|
||||
#include <FEXCore/HLE/SyscallHandler.h>
|
||||
#include <FEXCore/Utils/MathUtils.h>
|
||||
#include <Interface/HLE/Thunks/Thunks.h>
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
#define DEF_OP(x) void Arm64JITCore::Op_##x(IR::IROp_Header const* IROp, IR::NodeID Node)
|
||||
|
||||
@@ -5,7 +5,6 @@ tags: backend|arm64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Context/Context.h"
|
||||
#include "Interface/Core/JIT/Arm64/JITClass.h"
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
|
||||
@@ -37,25 +37,10 @@ public:
|
||||
explicit Arm64JITCore(FEXCore::Context::ContextImpl* ctx, FEXCore::Core::InternalThreadState* Thread);
|
||||
~Arm64JITCore() override;
|
||||
|
||||
[[nodiscard]]
|
||||
fextl::string GetName() override {
|
||||
return "JIT";
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
CPUBackend::CompiledCode CompileCode(uint64_t Entry, const FEXCore::IR::IRListView* IR, FEXCore::Core::DebugData* DebugData,
|
||||
const FEXCore::IR::RegisterAllocationData* RAData) override;
|
||||
|
||||
[[nodiscard]]
|
||||
void* MapRegion(void* HostPtr, uint64_t, uint64_t) override {
|
||||
return HostPtr;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
bool NeedsOpDispatch() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClearCache() override;
|
||||
|
||||
void ClearRelocations() override {
|
||||
|
||||
@@ -133,27 +133,18 @@ DEF_OP(StoreContextPair) {
|
||||
|
||||
DEF_OP(LoadRegister) {
|
||||
const auto Op = IROp->C<IR::IROp_LoadRegister>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
if (Op->Class == IR::GPRClass) {
|
||||
unsigned Reg = Op->Reg == Core::CPUState::PF_AS_GREG ? (StaticRegisters.size() - 2) :
|
||||
Op->Reg == Core::CPUState::AF_AS_GREG ? (StaticRegisters.size() - 1) :
|
||||
Op->Reg;
|
||||
|
||||
LOGMAN_THROW_A_FMT(Reg < StaticRegisters.size(), "out of range reg");
|
||||
const auto reg = StaticRegisters[Reg];
|
||||
LOGMAN_THROW_A_FMT(Op->Reg < StaticRegisters.size(), "out of range reg");
|
||||
const auto reg = StaticRegisters[Op->Reg];
|
||||
|
||||
if (GetReg(Node).Idx() != reg.Idx()) {
|
||||
if (OpSize == 4) {
|
||||
mov(GetReg(Node).W(), reg.W());
|
||||
} else {
|
||||
mov(GetReg(Node).X(), reg.X());
|
||||
}
|
||||
mov(GetReg(Node).X(), reg.X());
|
||||
}
|
||||
} else if (Op->Class == IR::FPRClass) {
|
||||
[[maybe_unused]] const auto regSize = HostSupportsAVX256 ? Core::CPUState::XMM_AVX_REG_SIZE : Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
LOGMAN_THROW_A_FMT(Op->Reg < StaticFPRegisters.size(), "out of range reg");
|
||||
LOGMAN_THROW_A_FMT(OpSize == regSize, "expected sized");
|
||||
LOGMAN_THROW_A_FMT(IROp->Size == regSize, "expected sized");
|
||||
|
||||
const auto guest = StaticFPRegisters[Op->Reg];
|
||||
const auto host = GetVReg(Node);
|
||||
@@ -170,6 +161,22 @@ DEF_OP(LoadRegister) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(LoadPF) {
|
||||
const auto reg = StaticRegisters[StaticRegisters.size() - 2];
|
||||
|
||||
if (GetReg(Node).Idx() != reg.Idx()) {
|
||||
mov(GetReg(Node).X(), reg.X());
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(LoadAF) {
|
||||
const auto reg = StaticRegisters[StaticRegisters.size() - 1];
|
||||
|
||||
if (GetReg(Node).Idx() != reg.Idx()) {
|
||||
mov(GetReg(Node).X(), reg.X());
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(StoreRegister) {
|
||||
const auto Op = IROp->C<IR::IROp_StoreRegister>();
|
||||
|
||||
@@ -206,6 +213,28 @@ DEF_OP(StoreRegister) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(StorePF) {
|
||||
const auto Op = IROp->C<IR::IROp_StorePF>();
|
||||
const auto reg = StaticRegisters[StaticRegisters.size() - 2];
|
||||
const auto Src = GetReg(Op->Value.ID());
|
||||
|
||||
if (Src.Idx() != reg.Idx()) {
|
||||
// Always use 64-bit, it's faster. Upper bits ignored for 32-bit mode.
|
||||
mov(ARMEmitter::Size::i64Bit, reg, Src);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(StoreAF) {
|
||||
const auto Op = IROp->C<IR::IROp_StoreAF>();
|
||||
const auto reg = StaticRegisters[StaticRegisters.size() - 1];
|
||||
const auto Src = GetReg(Op->Value.ID());
|
||||
|
||||
if (Src.Idx() != reg.Idx()) {
|
||||
// Always use 64-bit, it's faster. Upper bits ignored for 32-bit mode.
|
||||
mov(ARMEmitter::Size::i64Bit, reg, Src);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(LoadContextIndexed) {
|
||||
const auto Op = IROp->C<IR::IROp_LoadContextIndexed>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
@@ -443,6 +443,9 @@ DEF_OP(VFMinScalarInsert) {
|
||||
// AFP.AH lets fmin behave like x86 min
|
||||
fmin(SubRegSize.Scalar, Dst, Src1, Src2);
|
||||
} else {
|
||||
// Only take the first operand if it is strictly less. Otherwise take
|
||||
// the second. This emulates all the weird x86 rules for signed zero and
|
||||
// NaNs. No, they're not IEEE-754 semantics.
|
||||
fcmp(SubRegSize.Scalar, Src1, Src2);
|
||||
fcsel(SubRegSize.Scalar, Dst, Src1, Src2, ARMEmitter::Condition::CC_MI);
|
||||
}
|
||||
@@ -468,8 +471,9 @@ DEF_OP(VFMaxScalarInsert) {
|
||||
// AFP.AH lets fmax behave like x86 max
|
||||
fmax(SubRegSize.Scalar, Dst, Src1, Src2);
|
||||
} else {
|
||||
// Only take the first operand if it is strictly greater. See fmin.
|
||||
fcmp(SubRegSize.Scalar, Src1, Src2);
|
||||
fcsel(SubRegSize.Scalar, Dst, Src2, Src1, ARMEmitter::Condition::CC_MI);
|
||||
fcsel(SubRegSize.Scalar, Dst, Src1, Src2, ARMEmitter::Condition::CC_GT);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1381,46 +1385,21 @@ DEF_OP(VFMin) {
|
||||
mov(Dst.Z(), VTMP1.Z());
|
||||
}
|
||||
} else {
|
||||
if (IsScalar) {
|
||||
// FIXME: We should rework this op to avoid the NZCV spill/fill dance.
|
||||
mrs(TMP1, ARMEmitter::SystemRegister::NZCV);
|
||||
LOGMAN_THROW_AA_FMT(!IsScalar, "should use VFMinScalarInsert instead");
|
||||
|
||||
switch (ElementSize) {
|
||||
case 2: {
|
||||
fcmp(Vector1.H(), Vector2.H());
|
||||
fcsel(Dst.H(), Vector1.H(), Vector2.H(), ARMEmitter::Condition::CC_MI);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
fcmp(Vector1.S(), Vector2.S());
|
||||
fcsel(Dst.S(), Vector1.S(), Vector2.S(), ARMEmitter::Condition::CC_MI);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
fcmp(Vector1.D(), Vector2.D());
|
||||
fcsel(Dst.D(), Vector1.D(), Vector2.D(), ARMEmitter::Condition::CC_MI);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
// Restore NZCV
|
||||
msr(ARMEmitter::SystemRegister::NZCV, TMP1);
|
||||
if (Dst == Vector1) {
|
||||
// Destination is already Vector1, need to insert Vector2 on false.
|
||||
fcmgt(SubRegSize, VTMP1.Q(), Vector2.Q(), Vector1.Q());
|
||||
bif(Dst.Q(), Vector2.Q(), VTMP1.Q());
|
||||
} else if (Dst == Vector2) {
|
||||
// Destination is already Vector2, Invert arguments and insert Vector1 on false.
|
||||
fcmgt(SubRegSize, VTMP1.Q(), Vector1.Q(), Vector2.Q());
|
||||
bif(Dst.Q(), Vector1.Q(), VTMP1.Q());
|
||||
} else {
|
||||
if (Dst == Vector1) {
|
||||
// Destination is already Vector1, need to insert Vector2 on false.
|
||||
fcmgt(SubRegSize, VTMP1.Q(), Vector2.Q(), Vector1.Q());
|
||||
bif(Dst.Q(), Vector2.Q(), VTMP1.Q());
|
||||
} else if (Dst == Vector2) {
|
||||
// Destination is already Vector2, Invert arguments and insert Vector1 on false.
|
||||
fcmgt(SubRegSize, VTMP1.Q(), Vector1.Q(), Vector2.Q());
|
||||
bif(Dst.Q(), Vector1.Q(), VTMP1.Q());
|
||||
} else {
|
||||
// Dst is not either source, need a move.
|
||||
fcmgt(SubRegSize, VTMP1.Q(), Vector2.Q(), Vector1.Q());
|
||||
mov(Dst.Q(), Vector1.Q());
|
||||
bif(Dst.Q(), Vector2.Q(), VTMP1.Q());
|
||||
}
|
||||
// Dst is not either source, need a move.
|
||||
fcmgt(SubRegSize, VTMP1.Q(), Vector2.Q(), Vector1.Q());
|
||||
mov(Dst.Q(), Vector1.Q());
|
||||
bif(Dst.Q(), Vector2.Q(), VTMP1.Q());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1458,46 +1437,21 @@ DEF_OP(VFMax) {
|
||||
mov(Dst.Z(), VTMP1.Z());
|
||||
}
|
||||
} else {
|
||||
if (IsScalar) {
|
||||
// FIXME: We should rework this op to avoid the NZCV spill/fill dance.
|
||||
mrs(TMP1, ARMEmitter::SystemRegister::NZCV);
|
||||
LOGMAN_THROW_AA_FMT(!IsScalar, "should use VFMaxScalarInsert instead");
|
||||
|
||||
switch (ElementSize) {
|
||||
case 2: {
|
||||
fcmp(Vector1.H(), Vector2.H());
|
||||
fcsel(Dst.H(), Vector2.H(), Vector1.H(), ARMEmitter::Condition::CC_MI);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
fcmp(Vector1.S(), Vector2.S());
|
||||
fcsel(Dst.S(), Vector2.S(), Vector1.S(), ARMEmitter::Condition::CC_MI);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
fcmp(Vector1.D(), Vector2.D());
|
||||
fcsel(Dst.D(), Vector2.D(), Vector1.D(), ARMEmitter::Condition::CC_MI);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
// Restore NZCV
|
||||
msr(ARMEmitter::SystemRegister::NZCV, TMP1);
|
||||
if (Dst == Vector1) {
|
||||
// Destination is already Vector1, need to insert Vector2 on true.
|
||||
fcmgt(SubRegSize, VTMP1.Q(), Vector2.Q(), Vector1.Q());
|
||||
bit(Dst.Q(), Vector2.Q(), VTMP1.Q());
|
||||
} else if (Dst == Vector2) {
|
||||
// Destination is already Vector2, Invert arguments and insert Vector1 on true.
|
||||
fcmgt(SubRegSize, VTMP1.Q(), Vector1.Q(), Vector2.Q());
|
||||
bit(Dst.Q(), Vector1.Q(), VTMP1.Q());
|
||||
} else {
|
||||
if (Dst == Vector1) {
|
||||
// Destination is already Vector1, need to insert Vector2 on true.
|
||||
fcmgt(SubRegSize, VTMP1.Q(), Vector2.Q(), Vector1.Q());
|
||||
bit(Dst.Q(), Vector2.Q(), VTMP1.Q());
|
||||
} else if (Dst == Vector2) {
|
||||
// Destination is already Vector2, Invert arguments and insert Vector1 on true.
|
||||
fcmgt(SubRegSize, VTMP1.Q(), Vector1.Q(), Vector2.Q());
|
||||
bit(Dst.Q(), Vector1.Q(), VTMP1.Q());
|
||||
} else {
|
||||
// Dst is not either source, need a move.
|
||||
fcmgt(SubRegSize, VTMP1.Q(), Vector2.Q(), Vector1.Q());
|
||||
mov(Dst.Q(), Vector1.Q());
|
||||
bit(Dst.Q(), Vector2.Q(), VTMP1.Q());
|
||||
}
|
||||
// Dst is not either source, need a move.
|
||||
fcmgt(SubRegSize, VTMP1.Q(), Vector2.Q(), Vector1.Q());
|
||||
mov(Dst.Q(), Vector1.Q());
|
||||
bit(Dst.Q(), Vector2.Q(), VTMP1.Q());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ $end_info$
|
||||
|
||||
#include <FEXCore/Utils/Allocator.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
#include <FEXCore/HLE/SyscallHandler.h>
|
||||
|
||||
#include "Interface/Context/Context.h"
|
||||
#include "Interface/Core/LookupCache.h"
|
||||
@@ -34,7 +35,8 @@ LookupCache::LookupCache(FEXCore::Context::ContextImpl* CTX)
|
||||
// Allocate a region of memory that we can use to back our block pointers
|
||||
// We need one pointer per page of virtual memory
|
||||
// At 64GB of virtual memory this will allocate 128MB of virtual memory space
|
||||
PagePointer = reinterpret_cast<uintptr_t>(FEXCore::Allocator::VirtualAlloc(TotalCacheSize));
|
||||
PagePointer = reinterpret_cast<uintptr_t>(FEXCore::Allocator::VirtualAlloc(TotalCacheSize, false, false));
|
||||
CTX->SyscallHandler->MarkOvercommitRange(PagePointer, TotalCacheSize);
|
||||
|
||||
// Allocate our memory backing our pages
|
||||
// We need 32KB per guest page (One pointer per byte)
|
||||
@@ -52,8 +54,8 @@ LookupCache::LookupCache(FEXCore::Context::ContextImpl* CTX)
|
||||
}
|
||||
|
||||
LookupCache::~LookupCache() {
|
||||
const size_t TotalCacheSize = ctx->Config.VirtualMemSize / 4096 * 8 + CODE_SIZE + L1_SIZE;
|
||||
FEXCore::Allocator::VirtualFree(reinterpret_cast<void*>(PagePointer), TotalCacheSize);
|
||||
ctx->SyscallHandler->UnmarkOvercommitRange(PagePointer, TotalCacheSize);
|
||||
|
||||
// No need to free BlockLinks map.
|
||||
// These will get freed when their memory allocators are deallocated.
|
||||
@@ -63,7 +65,7 @@ void LookupCache::ClearL2Cache() {
|
||||
std::lock_guard<std::recursive_mutex> lk(WriteLock);
|
||||
// Clear out the page memory
|
||||
// PagePointer and PageMemory are sequential with each other. Clear both at once.
|
||||
FEXCore::Allocator::VirtualDontNeed(reinterpret_cast<void*>(PagePointer), ctx->Config.VirtualMemSize / 4096 * 8 + CODE_SIZE);
|
||||
FEXCore::Allocator::VirtualDontNeed(reinterpret_cast<void*>(PagePointer), ctx->Config.VirtualMemSize / 4096 * 8 + CODE_SIZE, false);
|
||||
AllocateOffset = 0;
|
||||
}
|
||||
|
||||
@@ -71,7 +73,7 @@ void LookupCache::ClearCache() {
|
||||
std::lock_guard<std::recursive_mutex> lk(WriteLock);
|
||||
|
||||
// Clear L1 and L2 by clearing the full cache.
|
||||
FEXCore::Allocator::VirtualDontNeed(reinterpret_cast<void*>(PagePointer), TotalCacheSize);
|
||||
FEXCore::Allocator::VirtualDontNeed(reinterpret_cast<void*>(PagePointer), TotalCacheSize, false);
|
||||
// Allocate a new pointer from the BlockLinks pma again.
|
||||
BlockLinks = BlockLinks_pma->new_object<BlockLinksMapType>();
|
||||
// All code is gone, clear the block list
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#include "Interface/Context/Context.h"
|
||||
#include "Interface/Core/ObjectCache/ObjectCacheService.h"
|
||||
|
||||
#include <FEXCore/Config/Config.h>
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#include "Interface/Context/Context.h"
|
||||
#include "Interface/Core/ObjectCache/ObjectCacheService.h"
|
||||
|
||||
|
||||
#include <FEXCore/fextl/memory.h>
|
||||
#include <FEXCore/fextl/string.h>
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -151,11 +151,7 @@ public:
|
||||
}
|
||||
IRPair<IROp_CondJump> CondJumpNZCV(CondClassType Cond) {
|
||||
FlushRegisterCache();
|
||||
|
||||
// The jump will ignore the sources, so it doesn't matter what we put here.
|
||||
// Put an inline constant so RA+codegen will ignore altogether.
|
||||
auto Placeholder = _InlineConstant(0);
|
||||
return _CondJump(Placeholder, Placeholder, InvalidNode, InvalidNode, Cond, 0, true);
|
||||
return _CondJump(InvalidNode, InvalidNode, InvalidNode, InvalidNode, Cond, 0, true);
|
||||
}
|
||||
IRPair<IROp_CondJump> CondJumpBit(Ref Src, unsigned Bit, bool Set) {
|
||||
FlushRegisterCache();
|
||||
@@ -301,8 +297,7 @@ public:
|
||||
};
|
||||
|
||||
void UnhandledOp(OpcodeArgs);
|
||||
template<uint32_t SrcIndex>
|
||||
void MOVGPROp(OpcodeArgs);
|
||||
void MOVGPROp(OpcodeArgs, uint32_t SrcIndex);
|
||||
void MOVGPRNTOp(OpcodeArgs);
|
||||
void MOVVectorAlignedOp(OpcodeArgs);
|
||||
void MOVVectorUnalignedOp(OpcodeArgs);
|
||||
@@ -317,20 +312,16 @@ public:
|
||||
void IRETOp(OpcodeArgs);
|
||||
void CallbackReturnOp(OpcodeArgs);
|
||||
void SecondaryALUOp(OpcodeArgs);
|
||||
template<uint32_t SrcIndex>
|
||||
void ADCOp(OpcodeArgs);
|
||||
template<uint32_t SrcIndex>
|
||||
void SBBOp(OpcodeArgs);
|
||||
void ADCOp(OpcodeArgs, uint32_t SrcIndex);
|
||||
void SBBOp(OpcodeArgs, uint32_t SrcIndex);
|
||||
void SALCOp(OpcodeArgs);
|
||||
void PUSHOp(OpcodeArgs);
|
||||
void PUSHREGOp(OpcodeArgs);
|
||||
void PUSHAOp(OpcodeArgs);
|
||||
template<uint32_t SegmentReg>
|
||||
void PUSHSegmentOp(OpcodeArgs);
|
||||
void PUSHSegmentOp(OpcodeArgs, uint32_t SegmentReg);
|
||||
void POPOp(OpcodeArgs);
|
||||
void POPAOp(OpcodeArgs);
|
||||
template<uint32_t SegmentReg>
|
||||
void POPSegmentOp(OpcodeArgs);
|
||||
void POPSegmentOp(OpcodeArgs, uint32_t SegmentReg);
|
||||
void LEAVEOp(OpcodeArgs);
|
||||
void CALLOp(OpcodeArgs);
|
||||
void CALLAbsoluteOp(OpcodeArgs);
|
||||
@@ -339,21 +330,18 @@ public:
|
||||
void LoopOp(OpcodeArgs);
|
||||
void JUMPOp(OpcodeArgs);
|
||||
void JUMPAbsoluteOp(OpcodeArgs);
|
||||
template<uint32_t SrcIndex>
|
||||
void TESTOp(OpcodeArgs);
|
||||
void TESTOp(OpcodeArgs, uint32_t SrcIndex);
|
||||
void MOVSXDOp(OpcodeArgs);
|
||||
void MOVSXOp(OpcodeArgs);
|
||||
void MOVZXOp(OpcodeArgs);
|
||||
template<uint32_t SrcIndex>
|
||||
void CMPOp(OpcodeArgs);
|
||||
void CMPOp(OpcodeArgs, uint32_t SrcIndex);
|
||||
void SETccOp(OpcodeArgs);
|
||||
void CQOOp(OpcodeArgs);
|
||||
void CDQOp(OpcodeArgs);
|
||||
void XCHGOp(OpcodeArgs);
|
||||
void SAHFOp(OpcodeArgs);
|
||||
void LAHFOp(OpcodeArgs);
|
||||
template<bool ToSeg>
|
||||
void MOVSegOp(OpcodeArgs);
|
||||
void MOVSegOp(OpcodeArgs, bool ToSeg);
|
||||
void FLAGControlOp(OpcodeArgs);
|
||||
void MOVOffsetOp(OpcodeArgs);
|
||||
void CMOVOp(OpcodeArgs);
|
||||
@@ -361,19 +349,14 @@ public:
|
||||
void XGetBVOp(OpcodeArgs);
|
||||
uint32_t LoadConstantShift(X86Tables::DecodedOp Op, bool Is1Bit);
|
||||
void SHLOp(OpcodeArgs);
|
||||
template<bool SHL1Bit>
|
||||
void SHLImmediateOp(OpcodeArgs);
|
||||
void SHLImmediateOp(OpcodeArgs, bool SHL1Bit);
|
||||
void SHROp(OpcodeArgs);
|
||||
template<bool SHR1Bit>
|
||||
void SHRImmediateOp(OpcodeArgs);
|
||||
void SHRImmediateOp(OpcodeArgs, bool SHR1Bit);
|
||||
void SHLDOp(OpcodeArgs);
|
||||
void SHLDImmediateOp(OpcodeArgs);
|
||||
void SHRDOp(OpcodeArgs);
|
||||
void SHRDImmediateOp(OpcodeArgs);
|
||||
template<bool IsImmediate, bool Is1Bit>
|
||||
void ASHROp(OpcodeArgs);
|
||||
template<bool Left, bool IsImmediate, bool Is1Bit>
|
||||
void RotateOp(OpcodeArgs);
|
||||
void ASHROp(OpcodeArgs, bool IsImmediate, bool Is1Bit);
|
||||
void RotateOp(OpcodeArgs, bool Left, bool IsImmediate, bool Is1Bit);
|
||||
void RCROp1Bit(OpcodeArgs);
|
||||
void RCROp8x1Bit(OpcodeArgs);
|
||||
@@ -383,8 +366,6 @@ public:
|
||||
void RCLOp(OpcodeArgs);
|
||||
void RCLSmallerOp(OpcodeArgs);
|
||||
|
||||
template<uint32_t SrcIndex, enum BTAction Action>
|
||||
void BTOp(OpcodeArgs);
|
||||
void BTOp(OpcodeArgs, uint32_t SrcIndex, enum BTAction Action);
|
||||
|
||||
void IMUL1SrcOp(OpcodeArgs);
|
||||
@@ -432,10 +413,8 @@ public:
|
||||
FS,
|
||||
GS,
|
||||
};
|
||||
template<Segment Seg>
|
||||
void ReadSegmentReg(OpcodeArgs);
|
||||
template<Segment Seg>
|
||||
void WriteSegmentReg(OpcodeArgs);
|
||||
void ReadSegmentReg(OpcodeArgs, Segment Seg);
|
||||
void WriteSegmentReg(OpcodeArgs, Segment Seg);
|
||||
void EnterOp(OpcodeArgs);
|
||||
|
||||
void SGDTOp(OpcodeArgs);
|
||||
@@ -869,8 +848,7 @@ public:
|
||||
void RDTSCPOp(OpcodeArgs);
|
||||
void RDPIDOp(OpcodeArgs);
|
||||
|
||||
template<bool ForStore, bool Stream, uint8_t Level>
|
||||
void Prefetch(OpcodeArgs);
|
||||
void Prefetch(OpcodeArgs, bool ForStore, bool Stream, uint8_t Level);
|
||||
|
||||
void PSADBW(OpcodeArgs);
|
||||
|
||||
@@ -1236,8 +1214,12 @@ public:
|
||||
uint32_t Index = 63 - std::countl_zero(Bits);
|
||||
Ref Value = RegCache.Value[Index];
|
||||
|
||||
if (Index >= GPR0Index && Index <= AFIndex) {
|
||||
if (Index >= GPR0Index && Index <= GPR15Index) {
|
||||
_StoreRegister(Value, Index - GPR0Index, GPRClass, GPRSize);
|
||||
} else if (Index == PFIndex) {
|
||||
_StorePF(Value, GPRSize);
|
||||
} else if (Index == AFIndex) {
|
||||
_StoreAF(Value, GPRSize);
|
||||
} else if (Index >= FPR0Index && Index <= FPR15Index) {
|
||||
_StoreRegister(Value, Index - FPR0Index, FPRClass, VectorSize);
|
||||
} else if (Index == DFIndex) {
|
||||
@@ -1896,6 +1878,10 @@ private:
|
||||
if (Size == 8) {
|
||||
RegCache.Partial |= Bit;
|
||||
}
|
||||
} else if (Index == PFIndex) {
|
||||
RegCache.Value[Index] = _LoadPF(Size);
|
||||
} else if (Index == AFIndex) {
|
||||
RegCache.Value[Index] = _LoadAF(Size);
|
||||
} else {
|
||||
RegCache.Value[Index] = _LoadRegister(Offset, RegClass, Size);
|
||||
}
|
||||
@@ -2106,22 +2092,9 @@ private:
|
||||
// Convert NZCV from the Arm representation to an eXternal representation
|
||||
// that's totally not a euphemism for x86, nuh-uh. But maps to exactly we
|
||||
// need, what a coincidence!
|
||||
if (CTX->HostFeatures.SupportsFlagM2) {
|
||||
_AXFlag();
|
||||
} else {
|
||||
// AXFLAG is defined in the Arm spec as
|
||||
//
|
||||
// gt: nzCv -> nzCv
|
||||
// lt: Nzcv -> nzcv <==> 1 + 0
|
||||
// eq: nZCv -> nZCv <==> 1 + (~0)
|
||||
// un: nzCV -> nZcv <==> 0 + 0
|
||||
//
|
||||
// For the latter 3 cases, we therefore get the right NZCV by adding V_inv
|
||||
// to (eq ? ~0 : 0). The remaining case is forced with ccmn.
|
||||
Ref Eq = NZCVSelect(OpSize::i64Bit, {COND_EQ}, _Constant(~0ull), _Constant(0));
|
||||
_CondAddNZCV(OpSize::i64Bit, V_inv, Eq, {COND_FLEU}, 0x2 /* nzCv */);
|
||||
}
|
||||
|
||||
//
|
||||
// Our AXFlag emulation on FlagM2-less systems needs V_inv passed.
|
||||
_AXFlag(CTX->HostFeatures.SupportsFlagM2 ? Invalid() : V_inv);
|
||||
PossiblySetNZCVBits = ~0;
|
||||
CFInverted = true;
|
||||
}
|
||||
@@ -2135,7 +2108,7 @@ private:
|
||||
LOGMAN_THROW_A_FMT(!NZCVDirty, "only expected after fcmp");
|
||||
|
||||
// Convert to x86 flags, saves us from or'ing after.
|
||||
_AXFlag();
|
||||
_AXFlag(Invalid());
|
||||
PossiblySetNZCVBits = ~0;
|
||||
CFInverted = true;
|
||||
|
||||
@@ -2321,7 +2294,8 @@ private:
|
||||
/**
|
||||
* @name These functions are used by the deferred flag handling while it is calculating and storing flags in to RFLAGs.
|
||||
* @{ */
|
||||
Ref LoadPFRaw(bool Invert);
|
||||
Ref LoadPFRaw(bool Mask, bool Invert);
|
||||
Ref SelectPF(bool Invert, IR::OpSize ResultSize, Ref TrueValue, Ref FalseValue);
|
||||
Ref LoadAF();
|
||||
void FixupAF();
|
||||
void SetAFAndFixup(Ref AF);
|
||||
@@ -2481,10 +2455,6 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
Ref Prefetch(bool ForStore, bool Stream, uint8_t CacheLevel, Ref ssa0) {
|
||||
return _Prefetch(ForStore, Stream, CacheLevel, ssa0, Invalid(), MEM_OFFSET_SXTX, 1);
|
||||
}
|
||||
|
||||
Ref Pop(uint8_t Size, Ref SP_RMW) {
|
||||
Ref Value = _AllocateGPR(false);
|
||||
_Pop(Size, SP_RMW, Value);
|
||||
@@ -2516,6 +2486,23 @@ private:
|
||||
void CheckLegacySegmentRead(Ref NewNode, uint32_t SegmentReg);
|
||||
};
|
||||
|
||||
constexpr inline void InstallToTable(auto& FinalTable, const auto& LocalTable) {
|
||||
for (const auto& Op : LocalTable) {
|
||||
auto OpNum = std::get<0>(Op);
|
||||
auto Dispatcher = std::get<2>(Op);
|
||||
for (uint8_t i = 0; i < std::get<1>(Op); ++i) {
|
||||
auto& TableOp = FinalTable[OpNum + i];
|
||||
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
|
||||
if (TableOp.OpcodeDispatcher) {
|
||||
ERROR_AND_DIE_FMT("Duplicate Entry {}", TableOp.Name);
|
||||
}
|
||||
#endif
|
||||
|
||||
TableOp.OpcodeDispatcher = Dispatcher;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InstallOpcodeHandlers(Context::OperatingMode Mode);
|
||||
|
||||
} // namespace FEXCore::IR
|
||||
@@ -462,17 +462,6 @@ void OpDispatchBuilder::InstallAVX128Handlers() {
|
||||
};
|
||||
#undef OPD
|
||||
|
||||
auto InstallToTable = [](auto& FinalTable, auto& LocalTable) {
|
||||
for (auto Op : LocalTable) {
|
||||
auto OpNum = std::get<0>(Op);
|
||||
auto Dispatcher = std::get<2>(Op);
|
||||
for (uint8_t i = 0; i < std::get<1>(Op); ++i) {
|
||||
LOGMAN_THROW_A_FMT(FinalTable[OpNum + i].OpcodeDispatcher == nullptr, "Duplicate Entry");
|
||||
FinalTable[OpNum + i].OpcodeDispatcher = Dispatcher;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
InstallToTable(FEXCore::X86Tables::VEXTableOps, AVX128Table);
|
||||
InstallToTable(FEXCore::X86Tables::VEXTableGroupOps, VEX128TableGroupOps);
|
||||
if (CTX->HostFeatures.SupportsPMULL_128Bit) {
|
||||
@@ -868,7 +857,7 @@ void OpDispatchBuilder::AVX128_VMOVLP(OpcodeArgs) {
|
||||
///< VMOVLPS/PD xmm1, xmm2, mem64
|
||||
// Bits[63:0] come from Src2[63:0]
|
||||
// Bits[127:64] come from Src1[127:64]
|
||||
auto Src2 = LoadSource_WithOpSize(GPRClass, Op, Op->Src[1], OpSize::i64Bit, Op->Flags, {.LoadData = false});
|
||||
auto Src2 = MakeSegmentAddress(Op, Op->Src[1]);
|
||||
Ref Result_Low = _VLoadVectorElement(OpSize::i128Bit, OpSize::i64Bit, Src1.Low, 0, Src2);
|
||||
Ref ZeroVector = LoadZeroVector(OpSize::i128Bit);
|
||||
|
||||
@@ -890,11 +879,11 @@ void OpDispatchBuilder::AVX128_VMOVHP(OpcodeArgs) {
|
||||
if (!Op->Dest.IsGPR()) {
|
||||
///< VMOVHPS/PD mem64, xmm1
|
||||
// Need to store Bits[127:64]. Use a vector element store.
|
||||
auto Dest = LoadSource_WithOpSize(GPRClass, Op, Op->Dest, OpSize::i64Bit, Op->Flags, {.LoadData = false});
|
||||
auto Dest = MakeSegmentAddress(Op, Op->Dest);
|
||||
_VStoreVectorElement(OpSize::i128Bit, OpSize::i64Bit, Src1.Low, 1, Dest);
|
||||
} else if (!Op->Src[1].IsGPR()) {
|
||||
///< VMOVHPS/PD xmm2, xmm1, mem64
|
||||
auto Src2 = LoadSource_WithOpSize(GPRClass, Op, Op->Src[1], OpSize::i64Bit, Op->Flags, {.LoadData = false});
|
||||
auto Src2 = MakeSegmentAddress(Op, Op->Src[1]);
|
||||
|
||||
// Bits[63:0] come from Src1[63:0]
|
||||
// Bits[127:64] come from Src2[63:0]
|
||||
@@ -1247,7 +1236,7 @@ void OpDispatchBuilder::AVX128_PExtr(OpcodeArgs) {
|
||||
}
|
||||
|
||||
// If we are storing to memory then we store the size of the element extracted
|
||||
Ref Dest = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.LoadData = false});
|
||||
Ref Dest = MakeSegmentAddress(Op, Op->Dest);
|
||||
_VStoreVectorElement(OpSize::i128Bit, OverridenElementSize, Src.Low, Index, Dest);
|
||||
}
|
||||
|
||||
@@ -1397,7 +1386,7 @@ void OpDispatchBuilder::AVX128_PINSRImpl(OpcodeArgs, size_t ElementSize, const X
|
||||
Result.Low = _VInsGPR(OpSize::i128Bit, ElementSize, Index, Src1.Low, Src2);
|
||||
} else {
|
||||
// If loading from memory then we only load the element size
|
||||
auto Src2 = LoadSource_WithOpSize(GPRClass, Op, Src2Op, ElementSize, Op->Flags, {.LoadData = false});
|
||||
auto Src2 = MakeSegmentAddress(Op, Src2Op);
|
||||
Result.Low = _VLoadVectorElement(OpSize::i128Bit, ElementSize, Src1.Low, Index, Src2);
|
||||
}
|
||||
|
||||
@@ -1440,7 +1429,7 @@ void OpDispatchBuilder::AVX128_ShiftDoubleImm(OpcodeArgs, ShiftDirection Dir) {
|
||||
Result = Src;
|
||||
} else if (Shift >= Core::CPUState::XMM_SSE_REG_SIZE) {
|
||||
Result.Low = LoadZeroVector(OpSize::i128Bit);
|
||||
Result.High = Result.High;
|
||||
Result.High = Result.Low;
|
||||
} else {
|
||||
Ref ZeroVector = LoadZeroVector(OpSize::i128Bit);
|
||||
RefPair Zero {ZeroVector, ZeroVector};
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
#include "Interface/Core/OpcodeDispatcher.h"
|
||||
|
||||
namespace FEXCore::IR {
|
||||
constexpr inline std::tuple<uint8_t, uint8_t, X86Tables::OpDispatchPtr> OpDispatch_BaseOpTable[] = {
|
||||
// Instructions
|
||||
{0x00, 6, &OpDispatchBuilder::Bind<&OpDispatchBuilder::ALUOp, FEXCore::IR::IROps::OP_ADD, FEXCore::IR::IROps::OP_ATOMICFETCHADD, 0>},
|
||||
|
||||
{0x08, 6, &OpDispatchBuilder::Bind<&OpDispatchBuilder::ALUOp, FEXCore::IR::IROps::OP_OR, FEXCore::IR::IROps::OP_ATOMICFETCHOR, 0>},
|
||||
|
||||
{0x10, 6, &OpDispatchBuilder::Bind<&OpDispatchBuilder::ADCOp, 0>},
|
||||
|
||||
{0x18, 6, &OpDispatchBuilder::Bind<&OpDispatchBuilder::SBBOp, 0>},
|
||||
|
||||
{0x20, 6, &OpDispatchBuilder::Bind<&OpDispatchBuilder::ALUOp, FEXCore::IR::IROps::OP_ANDWITHFLAGS, FEXCore::IR::IROps::OP_ATOMICFETCHAND, 0>},
|
||||
|
||||
{0x28, 6, &OpDispatchBuilder::Bind<&OpDispatchBuilder::ALUOp, FEXCore::IR::IROps::OP_SUB, FEXCore::IR::IROps::OP_ATOMICFETCHSUB, 0>},
|
||||
|
||||
{0x30, 6, &OpDispatchBuilder::Bind<&OpDispatchBuilder::ALUOp, FEXCore::IR::IROps::OP_XOR, FEXCore::IR::IROps::OP_ATOMICFETCHXOR, 0>},
|
||||
|
||||
{0x38, 6, &OpDispatchBuilder::Bind<&OpDispatchBuilder::CMPOp, 0>},
|
||||
{0x50, 8, &OpDispatchBuilder::PUSHREGOp},
|
||||
{0x58, 8, &OpDispatchBuilder::POPOp},
|
||||
{0x68, 1, &OpDispatchBuilder::PUSHOp},
|
||||
{0x69, 1, &OpDispatchBuilder::IMUL2SrcOp},
|
||||
{0x6A, 1, &OpDispatchBuilder::PUSHOp},
|
||||
{0x6B, 1, &OpDispatchBuilder::IMUL2SrcOp},
|
||||
{0x6C, 4, &OpDispatchBuilder::PermissionRestrictedOp},
|
||||
|
||||
{0x70, 16, &OpDispatchBuilder::CondJUMPOp},
|
||||
{0x84, 2, &OpDispatchBuilder::Bind<&OpDispatchBuilder::TESTOp, 0>},
|
||||
{0x86, 2, &OpDispatchBuilder::XCHGOp},
|
||||
{0x88, 4, &OpDispatchBuilder::Bind<&OpDispatchBuilder::MOVGPROp, 0>},
|
||||
|
||||
{0x8C, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::MOVSegOp, false>},
|
||||
{0x8D, 1, &OpDispatchBuilder::LEAOp},
|
||||
{0x8E, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::MOVSegOp, true>},
|
||||
{0x8F, 1, &OpDispatchBuilder::POPOp},
|
||||
{0x90, 8, &OpDispatchBuilder::XCHGOp},
|
||||
|
||||
{0x98, 1, &OpDispatchBuilder::CDQOp},
|
||||
{0x99, 1, &OpDispatchBuilder::CQOOp},
|
||||
{0x9B, 1, &OpDispatchBuilder::NOPOp},
|
||||
{0x9C, 1, &OpDispatchBuilder::PUSHFOp},
|
||||
{0x9D, 1, &OpDispatchBuilder::POPFOp},
|
||||
{0x9E, 1, &OpDispatchBuilder::SAHFOp},
|
||||
{0x9F, 1, &OpDispatchBuilder::LAHFOp},
|
||||
{0xA4, 2, &OpDispatchBuilder::MOVSOp},
|
||||
|
||||
{0xA6, 2, &OpDispatchBuilder::CMPSOp},
|
||||
{0xA8, 2, &OpDispatchBuilder::Bind<&OpDispatchBuilder::TESTOp, 0>},
|
||||
{0xAA, 2, &OpDispatchBuilder::STOSOp},
|
||||
{0xAC, 2, &OpDispatchBuilder::LODSOp},
|
||||
{0xAE, 2, &OpDispatchBuilder::SCASOp},
|
||||
{0xB0, 16, &OpDispatchBuilder::Bind<&OpDispatchBuilder::MOVGPROp, 0>},
|
||||
{0xC2, 2, &OpDispatchBuilder::RETOp},
|
||||
{0xC8, 1, &OpDispatchBuilder::EnterOp},
|
||||
{0xC9, 1, &OpDispatchBuilder::LEAVEOp},
|
||||
{0xCC, 2, &OpDispatchBuilder::INTOp},
|
||||
{0xCF, 1, &OpDispatchBuilder::IRETOp},
|
||||
{0xD7, 2, &OpDispatchBuilder::XLATOp},
|
||||
{0xE0, 3, &OpDispatchBuilder::LoopOp},
|
||||
{0xE3, 1, &OpDispatchBuilder::CondJUMPRCXOp},
|
||||
{0xE4, 4, &OpDispatchBuilder::PermissionRestrictedOp},
|
||||
{0xE8, 1, &OpDispatchBuilder::CALLOp},
|
||||
{0xE9, 1, &OpDispatchBuilder::JUMPOp},
|
||||
{0xEB, 1, &OpDispatchBuilder::JUMPOp},
|
||||
{0xEC, 4, &OpDispatchBuilder::PermissionRestrictedOp},
|
||||
{0xF1, 1, &OpDispatchBuilder::INTOp},
|
||||
{0xF4, 1, &OpDispatchBuilder::INTOp},
|
||||
|
||||
{0xF5, 1, &OpDispatchBuilder::FLAGControlOp},
|
||||
{0xF8, 2, &OpDispatchBuilder::FLAGControlOp},
|
||||
{0xFA, 2, &OpDispatchBuilder::PermissionRestrictedOp},
|
||||
{0xFC, 2, &OpDispatchBuilder::FLAGControlOp},
|
||||
};
|
||||
|
||||
constexpr inline std::tuple<uint8_t, uint8_t, X86Tables::OpDispatchPtr> OpDispatch_BaseOpTable_64[] = {
|
||||
{0x63, 1, &OpDispatchBuilder::MOVSXDOp},
|
||||
{0xA0, 4, &OpDispatchBuilder::MOVOffsetOp},
|
||||
};
|
||||
|
||||
constexpr inline std::tuple<uint8_t, uint8_t, X86Tables::OpDispatchPtr> OpDispatch_BaseOpTable_32[] = {
|
||||
{0x06, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUSHSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_ES_PREFIX>},
|
||||
{0x07, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::POPSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_ES_PREFIX>},
|
||||
{0x0E, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUSHSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_CS_PREFIX>},
|
||||
{0x16, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUSHSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_SS_PREFIX>},
|
||||
{0x17, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::POPSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_SS_PREFIX>},
|
||||
{0x1E, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUSHSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_DS_PREFIX>},
|
||||
{0x1F, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::POPSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_DS_PREFIX>},
|
||||
{0x27, 1, &OpDispatchBuilder::DAAOp},
|
||||
{0x2F, 1, &OpDispatchBuilder::DASOp},
|
||||
{0x37, 1, &OpDispatchBuilder::AAAOp},
|
||||
{0x3F, 1, &OpDispatchBuilder::AASOp},
|
||||
{0x40, 8, &OpDispatchBuilder::INCOp},
|
||||
{0x48, 8, &OpDispatchBuilder::DECOp},
|
||||
|
||||
{0x60, 1, &OpDispatchBuilder::PUSHAOp},
|
||||
{0x61, 1, &OpDispatchBuilder::POPAOp},
|
||||
{0xA0, 4, &OpDispatchBuilder::MOVOffsetOp},
|
||||
{0xCE, 1, &OpDispatchBuilder::INTOp},
|
||||
{0xD4, 1, &OpDispatchBuilder::AAMOp},
|
||||
{0xD5, 1, &OpDispatchBuilder::AADOp},
|
||||
{0xD6, 1, &OpDispatchBuilder::SALCOp},
|
||||
};
|
||||
} // namespace FEXCore::IR
|
||||
@@ -0,0 +1,45 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
#include "Interface/Core/OpcodeDispatcher.h"
|
||||
|
||||
namespace FEXCore::IR {
|
||||
constexpr std::tuple<uint8_t, uint8_t, FEXCore::X86Tables::OpDispatchPtr> OpDispatch_DDDTable[] = {
|
||||
{0x0C, 1, &OpDispatchBuilder::PI2FWOp},
|
||||
{0x0D, 1, &OpDispatchBuilder::Vector_CVT_Int_To_Float<4, false>},
|
||||
{0x1C, 1, &OpDispatchBuilder::PF2IWOp},
|
||||
{0x1D, 1, &OpDispatchBuilder::Vector_CVT_Float_To_Int<4, false, false>},
|
||||
|
||||
{0x86, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorUnaryOp, IR::OP_VFRECP, 4>},
|
||||
{0x87, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorUnaryOp, IR::OP_VFRSQRT, 4>},
|
||||
|
||||
{0x8A, 1, &OpDispatchBuilder::PFNACCOp},
|
||||
{0x8E, 1, &OpDispatchBuilder::PFPNACCOp},
|
||||
|
||||
{0x90, 1, &OpDispatchBuilder::VPFCMPOp<1>},
|
||||
{0x94, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VFMIN, 4>},
|
||||
{0x96, 1, &OpDispatchBuilder::VectorUnaryDuplicateOp<IR::OP_VFRECP, 4>},
|
||||
{0x97, 1, &OpDispatchBuilder::VectorUnaryDuplicateOp<IR::OP_VFRSQRT, 4>},
|
||||
|
||||
{0x9A, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VFSUB, 4>},
|
||||
{0x9E, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VFADD, 4>},
|
||||
|
||||
{0xA0, 1, &OpDispatchBuilder::VPFCMPOp<2>},
|
||||
{0xA4, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VFMAX, 4>},
|
||||
// Can be treated as a move
|
||||
{0xA6, 1, &OpDispatchBuilder::MOVVectorUnalignedOp},
|
||||
{0xA7, 1, &OpDispatchBuilder::MOVVectorUnalignedOp},
|
||||
|
||||
{0xAA, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUROp, IR::OP_VFSUB, 4>},
|
||||
{0xAE, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VFADDP, 4>},
|
||||
|
||||
{0xB0, 1, &OpDispatchBuilder::VPFCMPOp<0>},
|
||||
{0xB4, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VFMUL, 4>},
|
||||
// Can be treated as a move
|
||||
{0xB6, 1, &OpDispatchBuilder::MOVVectorUnalignedOp},
|
||||
{0xB7, 1, &OpDispatchBuilder::PMULHRWOp},
|
||||
|
||||
{0xBB, 1, &OpDispatchBuilder::PSWAPDOp},
|
||||
{0xBF, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VURAVG, 1>},
|
||||
};
|
||||
|
||||
} // namespace FEXCore::IR
|
||||
@@ -6,7 +6,6 @@ desc: Handles x86/64 flag generation
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Context/Context.h"
|
||||
#include "Interface/Core/OpcodeDispatcher.h"
|
||||
#include "Interface/Core/X86Tables/X86Tables.h"
|
||||
|
||||
@@ -77,16 +76,13 @@ Ref OpDispatchBuilder::GetPackedRFLAG(uint32_t FlagsMask) {
|
||||
// Calculate flags early.
|
||||
CalculateDeferredFlags();
|
||||
|
||||
Ref Original = _Constant(0);
|
||||
|
||||
// SF/ZF and N/Z are together on both arm64 and x86_64, so we special case that.
|
||||
bool GetNZ = (FlagsMask & (1 << FEXCore::X86State::RFLAG_SF_RAW_LOC)) && (FlagsMask & (1 << FEXCore::X86State::RFLAG_ZF_RAW_LOC));
|
||||
|
||||
// Handle CF first, since it's at bit 0 and hence doesn't need shift or OR.
|
||||
if (FlagsMask & (1 << FEXCore::X86State::RFLAG_CF_RAW_LOC)) {
|
||||
static_assert(FEXCore::X86State::RFLAG_CF_RAW_LOC == 0);
|
||||
Original = GetRFLAG(FEXCore::X86State::RFLAG_CF_RAW_LOC);
|
||||
}
|
||||
LOGMAN_THROW_A_FMT(FlagsMask & (1 << FEXCore::X86State::RFLAG_CF_RAW_LOC), "CF always handled");
|
||||
static_assert(FEXCore::X86State::RFLAG_CF_RAW_LOC == 0);
|
||||
Ref Original = GetRFLAG(FEXCore::X86State::RFLAG_CF_RAW_LOC);
|
||||
|
||||
for (size_t i = 0; i < FlagOffsets.size(); ++i) {
|
||||
const auto FlagOffset = FlagOffsets[i];
|
||||
@@ -117,7 +113,7 @@ Ref OpDispatchBuilder::GetPackedRFLAG(uint32_t FlagsMask) {
|
||||
// instead.
|
||||
if (FlagsMask & (1 << FEXCore::X86State::RFLAG_PF_RAW_LOC)) {
|
||||
// Set every bit except the bottommost.
|
||||
auto OnesInvPF = _Or(OpSize::i64Bit, LoadPFRaw(false), _Constant(~1ull));
|
||||
auto OnesInvPF = _Or(OpSize::i64Bit, LoadPFRaw(false, false), _InlineConstant(~1ull));
|
||||
|
||||
// Rotate the bottom bit to the appropriate location for PF, so we get
|
||||
// something like 111P1111. Then invert that to get 000p0000. Then OR that
|
||||
@@ -130,13 +126,13 @@ Ref OpDispatchBuilder::GetPackedRFLAG(uint32_t FlagsMask) {
|
||||
if (GetNZ) {
|
||||
static_assert(FEXCore::X86State::RFLAG_SF_RAW_LOC == (FEXCore::X86State::RFLAG_ZF_RAW_LOC + 1));
|
||||
auto NZCV = GetNZCV();
|
||||
auto NZ = _And(OpSize::i64Bit, NZCV, _Constant(0b11u << 30));
|
||||
auto NZ = _And(OpSize::i64Bit, NZCV, _InlineConstant(0b11u << 30));
|
||||
Original = _Orlshr(OpSize::i64Bit, Original, NZ, 31 - FEXCore::X86State::RFLAG_SF_RAW_LOC);
|
||||
}
|
||||
|
||||
// The constant is OR'ed in at the end, to avoid a pointless or xzr, #2.
|
||||
if ((1U << X86State::RFLAG_RESERVED_LOC) & FlagsMask) {
|
||||
Original = _Or(OpSize::i64Bit, Original, _Constant(2));
|
||||
Original = _Or(OpSize::i64Bit, Original, _InlineConstant(2));
|
||||
}
|
||||
|
||||
return Original;
|
||||
@@ -178,22 +174,12 @@ void OpDispatchBuilder::CalculateOF(uint8_t SrcSize, Ref Res, Ref Src1, Ref Src2
|
||||
SetRFLAG<FEXCore::X86State::RFLAG_OF_RAW_LOC>(Anded, SrcSize * 8 - 1, true);
|
||||
}
|
||||
|
||||
Ref OpDispatchBuilder::LoadPFRaw(bool Invert) {
|
||||
// Read the stored byte. This is the original result (up to 64-bits), it needs
|
||||
// parity calculated.
|
||||
auto Result = GetRFLAG(FEXCore::X86State::RFLAG_PF_RAW_LOC);
|
||||
Ref OpDispatchBuilder::LoadPFRaw(bool Mask, bool Invert) {
|
||||
// Most blocks do not read parity, so PF optimization is gated on this flag.
|
||||
CurrentHeader->ReadsParity = true;
|
||||
|
||||
// Cascade to calculate parity of bottom 8-bits to bottom bit.
|
||||
Result = _XorShift(OpSize::i32Bit, Result, Result, ShiftType::LSR, 4);
|
||||
Result = _XorShift(OpSize::i32Bit, Result, Result, ShiftType::LSR, 2);
|
||||
|
||||
if (Invert) {
|
||||
Result = _XornShift(OpSize::i32Bit, Result, Result, ShiftType::LSR, 1);
|
||||
} else {
|
||||
Result = _XorShift(OpSize::i32Bit, Result, Result, ShiftType::LSR, 1);
|
||||
}
|
||||
|
||||
return Result;
|
||||
// Evaluate parity on the deferred raw value.
|
||||
return _Parity(GetRFLAG(FEXCore::X86State::RFLAG_PF_RAW_LOC), Mask, Invert);
|
||||
}
|
||||
|
||||
Ref OpDispatchBuilder::LoadAF() {
|
||||
@@ -277,8 +263,8 @@ Ref OpDispatchBuilder::IncrementByCarry(OpSize OpSize, Ref Src) {
|
||||
}
|
||||
|
||||
Ref OpDispatchBuilder::CalculateFlags_ADC(uint8_t SrcSize, Ref Src1, Ref Src2) {
|
||||
auto Zero = _Constant(0);
|
||||
auto One = _Constant(1);
|
||||
auto Zero = _InlineConstant(0);
|
||||
auto One = _InlineConstant(1);
|
||||
auto OpSize = SrcSize == 8 ? OpSize::i64Bit : OpSize::i32Bit;
|
||||
Ref Res;
|
||||
|
||||
@@ -314,8 +300,8 @@ Ref OpDispatchBuilder::CalculateFlags_ADC(uint8_t SrcSize, Ref Src1, Ref Src2) {
|
||||
}
|
||||
|
||||
Ref OpDispatchBuilder::CalculateFlags_SBB(uint8_t SrcSize, Ref Src1, Ref Src2) {
|
||||
auto Zero = _Constant(0);
|
||||
auto One = _Constant(1);
|
||||
auto Zero = _InlineConstant(0);
|
||||
auto One = _InlineConstant(1);
|
||||
auto OpSize = SrcSize == 8 ? OpSize::i64Bit : OpSize::i32Bit;
|
||||
|
||||
CalculateAF(Src1, Src2);
|
||||
@@ -419,7 +405,7 @@ void OpDispatchBuilder::CalculateFlags_MUL(uint8_t SrcSize, Ref Res, Ref High) {
|
||||
|
||||
// If High = SignBit, then sets to nZCv. Else sets to nzcV. Since SF/ZF
|
||||
// undefined, this does what we need after inverting carry.
|
||||
auto Zero = _Constant(0);
|
||||
auto Zero = _InlineConstant(0);
|
||||
_CondSubNZCV(OpSize::i64Bit, Zero, Zero, CondClassType {COND_EQ}, 0x1 /* nzcV */);
|
||||
CFInverted = true;
|
||||
}
|
||||
@@ -428,7 +414,7 @@ void OpDispatchBuilder::CalculateFlags_UMUL(Ref High) {
|
||||
HandleNZCVWrite();
|
||||
InvalidatePF_AF();
|
||||
|
||||
auto Zero = _Constant(0);
|
||||
auto Zero = _InlineConstant(0);
|
||||
OpSize Size = IR::SizeToOpSize(GetOpSize(High));
|
||||
|
||||
// CF and OF are set if the result of the operation can't be fit in to the destination register
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
#include "Interface/Core/OpcodeDispatcher.h"
|
||||
|
||||
namespace FEXCore::IR {
|
||||
#define OPD(prefix, opcode) (((prefix) << 8) | opcode)
|
||||
constexpr uint16_t PF_38_NONE = 0;
|
||||
constexpr uint16_t PF_38_66 = (1U << 0);
|
||||
constexpr uint16_t PF_38_F3 = (1U << 2);
|
||||
|
||||
constexpr std::tuple<uint16_t, uint8_t, FEXCore::X86Tables::OpDispatchPtr> OpDispatch_H0F38Table[] = {
|
||||
{OPD(PF_38_NONE, 0x00), 1, &OpDispatchBuilder::PSHUFBOp},
|
||||
{OPD(PF_38_66, 0x00), 1, &OpDispatchBuilder::PSHUFBOp},
|
||||
{OPD(PF_38_NONE, 0x01), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VADDP, 2>},
|
||||
{OPD(PF_38_66, 0x01), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VADDP, 2>},
|
||||
{OPD(PF_38_NONE, 0x02), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VADDP, 4>},
|
||||
{OPD(PF_38_66, 0x02), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VADDP, 4>},
|
||||
{OPD(PF_38_NONE, 0x03), 1, &OpDispatchBuilder::PHADDS},
|
||||
{OPD(PF_38_66, 0x03), 1, &OpDispatchBuilder::PHADDS},
|
||||
{OPD(PF_38_NONE, 0x04), 1, &OpDispatchBuilder::PMADDUBSW},
|
||||
{OPD(PF_38_66, 0x04), 1, &OpDispatchBuilder::PMADDUBSW},
|
||||
{OPD(PF_38_NONE, 0x05), 1, &OpDispatchBuilder::PHSUB<2>},
|
||||
{OPD(PF_38_66, 0x05), 1, &OpDispatchBuilder::PHSUB<2>},
|
||||
{OPD(PF_38_NONE, 0x06), 1, &OpDispatchBuilder::PHSUB<4>},
|
||||
{OPD(PF_38_66, 0x06), 1, &OpDispatchBuilder::PHSUB<4>},
|
||||
{OPD(PF_38_NONE, 0x07), 1, &OpDispatchBuilder::PHSUBS},
|
||||
{OPD(PF_38_66, 0x07), 1, &OpDispatchBuilder::PHSUBS},
|
||||
{OPD(PF_38_NONE, 0x08), 1, &OpDispatchBuilder::PSIGN<1>},
|
||||
{OPD(PF_38_66, 0x08), 1, &OpDispatchBuilder::PSIGN<1>},
|
||||
{OPD(PF_38_NONE, 0x09), 1, &OpDispatchBuilder::PSIGN<2>},
|
||||
{OPD(PF_38_66, 0x09), 1, &OpDispatchBuilder::PSIGN<2>},
|
||||
{OPD(PF_38_NONE, 0x0A), 1, &OpDispatchBuilder::PSIGN<4>},
|
||||
{OPD(PF_38_66, 0x0A), 1, &OpDispatchBuilder::PSIGN<4>},
|
||||
{OPD(PF_38_NONE, 0x0B), 1, &OpDispatchBuilder::PMULHRSW},
|
||||
{OPD(PF_38_66, 0x0B), 1, &OpDispatchBuilder::PMULHRSW},
|
||||
{OPD(PF_38_66, 0x10), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorVariableBlend, 1>},
|
||||
{OPD(PF_38_66, 0x14), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorVariableBlend, 4>},
|
||||
{OPD(PF_38_66, 0x15), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorVariableBlend, 8>},
|
||||
{OPD(PF_38_66, 0x17), 1, &OpDispatchBuilder::PTestOp},
|
||||
{OPD(PF_38_NONE, 0x1C), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorUnaryOp, IR::OP_VABS, 1>},
|
||||
{OPD(PF_38_66, 0x1C), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorUnaryOp, IR::OP_VABS, 1>},
|
||||
{OPD(PF_38_NONE, 0x1D), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorUnaryOp, IR::OP_VABS, 2>},
|
||||
{OPD(PF_38_66, 0x1D), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorUnaryOp, IR::OP_VABS, 2>},
|
||||
{OPD(PF_38_NONE, 0x1E), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorUnaryOp, IR::OP_VABS, 4>},
|
||||
{OPD(PF_38_66, 0x1E), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorUnaryOp, IR::OP_VABS, 4>},
|
||||
{OPD(PF_38_66, 0x20), 1, &OpDispatchBuilder::ExtendVectorElements<1, 2, true>},
|
||||
{OPD(PF_38_66, 0x21), 1, &OpDispatchBuilder::ExtendVectorElements<1, 4, true>},
|
||||
{OPD(PF_38_66, 0x22), 1, &OpDispatchBuilder::ExtendVectorElements<1, 8, true>},
|
||||
{OPD(PF_38_66, 0x23), 1, &OpDispatchBuilder::ExtendVectorElements<2, 4, true>},
|
||||
{OPD(PF_38_66, 0x24), 1, &OpDispatchBuilder::ExtendVectorElements<2, 8, true>},
|
||||
{OPD(PF_38_66, 0x25), 1, &OpDispatchBuilder::ExtendVectorElements<4, 8, true>},
|
||||
{OPD(PF_38_66, 0x28), 1, &OpDispatchBuilder::PMULLOp<4, true>},
|
||||
{OPD(PF_38_66, 0x29), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VCMPEQ, 8>},
|
||||
{OPD(PF_38_66, 0x2A), 1, &OpDispatchBuilder::MOVVectorNTOp},
|
||||
{OPD(PF_38_66, 0x2B), 1, &OpDispatchBuilder::PACKUSOp<4>},
|
||||
{OPD(PF_38_66, 0x30), 1, &OpDispatchBuilder::ExtendVectorElements<1, 2, false>},
|
||||
{OPD(PF_38_66, 0x31), 1, &OpDispatchBuilder::ExtendVectorElements<1, 4, false>},
|
||||
{OPD(PF_38_66, 0x32), 1, &OpDispatchBuilder::ExtendVectorElements<1, 8, false>},
|
||||
{OPD(PF_38_66, 0x33), 1, &OpDispatchBuilder::ExtendVectorElements<2, 4, false>},
|
||||
{OPD(PF_38_66, 0x34), 1, &OpDispatchBuilder::ExtendVectorElements<2, 8, false>},
|
||||
{OPD(PF_38_66, 0x35), 1, &OpDispatchBuilder::ExtendVectorElements<4, 8, false>},
|
||||
{OPD(PF_38_66, 0x37), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VCMPGT, 8>},
|
||||
{OPD(PF_38_66, 0x38), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VSMIN, 1>},
|
||||
{OPD(PF_38_66, 0x39), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VSMIN, 4>},
|
||||
{OPD(PF_38_66, 0x3A), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VUMIN, 2>},
|
||||
{OPD(PF_38_66, 0x3B), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VUMIN, 4>},
|
||||
{OPD(PF_38_66, 0x3C), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VSMAX, 1>},
|
||||
{OPD(PF_38_66, 0x3D), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VSMAX, 4>},
|
||||
{OPD(PF_38_66, 0x3E), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VUMAX, 2>},
|
||||
{OPD(PF_38_66, 0x3F), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VUMAX, 4>},
|
||||
{OPD(PF_38_66, 0x40), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VMUL, 4>},
|
||||
{OPD(PF_38_66, 0x41), 1, &OpDispatchBuilder::PHMINPOSUWOp},
|
||||
|
||||
{OPD(PF_38_NONE, 0xF0), 2, &OpDispatchBuilder::MOVBEOp},
|
||||
{OPD(PF_38_66, 0xF0), 2, &OpDispatchBuilder::MOVBEOp},
|
||||
|
||||
{OPD(PF_38_66, 0xF6), 1, &OpDispatchBuilder::ADXOp},
|
||||
{OPD(PF_38_F3, 0xF6), 1, &OpDispatchBuilder::ADXOp},
|
||||
};
|
||||
#undef OPD
|
||||
|
||||
} // namespace FEXCore::IR
|
||||
@@ -0,0 +1,51 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
#include "Interface/Core/OpcodeDispatcher.h"
|
||||
|
||||
namespace FEXCore::IR {
|
||||
#define OPD(REX, prefix, opcode) ((REX << 9) | (prefix << 8) | opcode)
|
||||
#define PF_3A_NONE 0
|
||||
#define PF_3A_66 1
|
||||
constexpr std::tuple<uint16_t, uint8_t, FEXCore::X86Tables::OpDispatchPtr> OpDispatch_H0F3ATable[] = {
|
||||
{OPD(0, PF_3A_66, 0x08), 1, &OpDispatchBuilder::VectorRound<4>},
|
||||
{OPD(0, PF_3A_66, 0x09), 1, &OpDispatchBuilder::VectorRound<8>},
|
||||
{OPD(0, PF_3A_66, 0x0A), 1, &OpDispatchBuilder::InsertScalarRound<4>},
|
||||
{OPD(0, PF_3A_66, 0x0B), 1, &OpDispatchBuilder::InsertScalarRound<8>},
|
||||
{OPD(0, PF_3A_66, 0x0C), 1, &OpDispatchBuilder::VectorBlend<4>},
|
||||
{OPD(0, PF_3A_66, 0x0D), 1, &OpDispatchBuilder::VectorBlend<8>},
|
||||
{OPD(0, PF_3A_66, 0x0E), 1, &OpDispatchBuilder::VectorBlend<2>},
|
||||
|
||||
{OPD(0, PF_3A_NONE, 0x0F), 1, &OpDispatchBuilder::PAlignrOp},
|
||||
{OPD(0, PF_3A_66, 0x0F), 1, &OpDispatchBuilder::PAlignrOp},
|
||||
|
||||
{OPD(0, PF_3A_66, 0x14), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PExtrOp, 1>},
|
||||
{OPD(0, PF_3A_66, 0x15), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PExtrOp, 2>},
|
||||
{OPD(0, PF_3A_66, 0x16), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PExtrOp, 4>},
|
||||
{OPD(0, PF_3A_66, 0x17), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PExtrOp, 4>},
|
||||
|
||||
{OPD(0, PF_3A_66, 0x20), 1, &OpDispatchBuilder::PINSROp<1>},
|
||||
{OPD(0, PF_3A_66, 0x21), 1, &OpDispatchBuilder::InsertPSOp},
|
||||
{OPD(0, PF_3A_66, 0x22), 1, &OpDispatchBuilder::PINSROp<4>},
|
||||
{OPD(0, PF_3A_66, 0x40), 1, &OpDispatchBuilder::DPPOp<4>},
|
||||
{OPD(0, PF_3A_66, 0x41), 1, &OpDispatchBuilder::DPPOp<8>},
|
||||
{OPD(0, PF_3A_66, 0x42), 1, &OpDispatchBuilder::MPSADBWOp},
|
||||
|
||||
{OPD(0, PF_3A_66, 0x60), 1, &OpDispatchBuilder::VPCMPESTRMOp},
|
||||
{OPD(0, PF_3A_66, 0x61), 1, &OpDispatchBuilder::VPCMPESTRIOp},
|
||||
{OPD(0, PF_3A_66, 0x62), 1, &OpDispatchBuilder::VPCMPISTRMOp},
|
||||
{OPD(0, PF_3A_66, 0x63), 1, &OpDispatchBuilder::VPCMPISTRIOp},
|
||||
|
||||
{OPD(0, PF_3A_NONE, 0xCC), 1, &OpDispatchBuilder::SHA1RNDS4Op},
|
||||
};
|
||||
|
||||
constexpr std::tuple<uint16_t, uint8_t, FEXCore::X86Tables::OpDispatchPtr> OpDispatch_H0F3ATable_64[] = {
|
||||
{OPD(1, PF_3A_66, 0x0F), 1, &OpDispatchBuilder::PAlignrOp},
|
||||
{OPD(1, PF_3A_66, 0x16), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PExtrOp, 8>},
|
||||
{OPD(1, PF_3A_66, 0x22), 1, &OpDispatchBuilder::PINSROp<8>},
|
||||
};
|
||||
|
||||
#undef PF_3A_NONE
|
||||
#undef PF_3A_66
|
||||
|
||||
#undef OPD
|
||||
} // namespace FEXCore::IR
|
||||
@@ -0,0 +1,129 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
#include "Interface/Core/OpcodeDispatcher.h"
|
||||
|
||||
namespace FEXCore::IR {
|
||||
using X86Tables::OpToIndex;
|
||||
#define OPD(group, prefix, Reg) (((group - FEXCore::X86Tables::TYPE_GROUP_1) << 6) | (prefix) << 3 | (Reg))
|
||||
constexpr std::tuple<uint16_t, uint8_t, FEXCore::X86Tables::OpDispatchPtr> OpDispatch_PrimaryGroupTables[] = {
|
||||
// GROUP 1
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x80), 0), 1, &OpDispatchBuilder::SecondaryALUOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x80), 1), 1, &OpDispatchBuilder::SecondaryALUOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x80), 2), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::ADCOp, 1>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x80), 3), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::SBBOp, 1>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x80), 4), 1, &OpDispatchBuilder::SecondaryALUOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x80), 5), 1, &OpDispatchBuilder::SecondaryALUOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x80), 6), 1, &OpDispatchBuilder::SecondaryALUOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x80), 7), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::CMPOp, 1>}, // CMP
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x81), 0), 1, &OpDispatchBuilder::SecondaryALUOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x81), 1), 1, &OpDispatchBuilder::SecondaryALUOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x81), 2), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::ADCOp, 1>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x81), 3), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::SBBOp, 1>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x81), 4), 1, &OpDispatchBuilder::SecondaryALUOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x81), 5), 1, &OpDispatchBuilder::SecondaryALUOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x81), 6), 1, &OpDispatchBuilder::SecondaryALUOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x81), 7), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::CMPOp, 1>},
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x83), 0), 1, &OpDispatchBuilder::SecondaryALUOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x83), 1), 1, &OpDispatchBuilder::SecondaryALUOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x83), 2), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::ADCOp, 1>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x83), 3), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::SBBOp, 1>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x83), 4), 1, &OpDispatchBuilder::SecondaryALUOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x83), 5), 1, &OpDispatchBuilder::SecondaryALUOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x83), 6), 1, &OpDispatchBuilder::SecondaryALUOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_1, OpToIndex(0x83), 7), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::CMPOp, 1>},
|
||||
|
||||
// GROUP 2
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xC0), 0), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::RotateOp, true, true, false>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xC0), 1), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::RotateOp, false, true, false>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xC0), 2), 1, &OpDispatchBuilder::RCLOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xC0), 3), 1, &OpDispatchBuilder::RCROp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xC0), 4), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::SHLImmediateOp, false>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xC0), 5), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::SHRImmediateOp, false>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xC0), 6), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::SHLImmediateOp, false>}, // SAL
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xC0), 7), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::ASHROp, true, false>}, // SAR
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xC1), 0), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::RotateOp, true, true, false>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xC1), 1), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::RotateOp, false, true, false>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xC1), 2), 1, &OpDispatchBuilder::RCLOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xC1), 3), 1, &OpDispatchBuilder::RCROp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xC1), 4), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::SHLImmediateOp, false>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xC1), 5), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::SHRImmediateOp, false>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xC1), 6), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::SHLImmediateOp, false>}, // SAL
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xC1), 7), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::ASHROp, true, false>}, // SAR
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD0), 0), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::RotateOp, true, true, true>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD0), 1), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::RotateOp, false, true, true>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD0), 2), 1, &OpDispatchBuilder::RCLOp1Bit},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD0), 3), 1, &OpDispatchBuilder::RCROp8x1Bit},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD0), 4), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::SHLImmediateOp, true>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD0), 5), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::SHRImmediateOp, true>}, // 1Bit SHR
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD0), 6), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::SHLImmediateOp, true>}, // SAL
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD0), 7), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::ASHROp, true, true>}, // SAR
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD1), 0), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::RotateOp, true, true, true>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD1), 1), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::RotateOp, false, true, true>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD1), 2), 1, &OpDispatchBuilder::RCLOp1Bit},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD1), 3), 1, &OpDispatchBuilder::RCROp1Bit},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD1), 4), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::SHLImmediateOp, true>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD1), 5), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::SHRImmediateOp, true>}, // 1Bit SHR
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD1), 6), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::SHLImmediateOp, true>}, // SAL
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD1), 7), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::ASHROp, true, true>}, // SAR
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD2), 0), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::RotateOp, true, false, false>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD2), 1), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::RotateOp, false, false, false>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD2), 2), 1, &OpDispatchBuilder::RCLSmallerOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD2), 3), 1, &OpDispatchBuilder::RCRSmallerOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD2), 4), 1, &OpDispatchBuilder::SHLOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD2), 5), 1, &OpDispatchBuilder::SHROp}, // SHR by CL
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD2), 6), 1, &OpDispatchBuilder::SHLOp}, // SAL
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD2), 7), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::ASHROp, false, false>}, // SAR
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD3), 0), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::RotateOp, true, false, false>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD3), 1), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::RotateOp, false, false, false>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD3), 2), 1, &OpDispatchBuilder::RCLOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD3), 3), 1, &OpDispatchBuilder::RCROp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD3), 4), 1, &OpDispatchBuilder::SHLOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD3), 5), 1, &OpDispatchBuilder::SHROp}, // SHR by CL
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD3), 6), 1, &OpDispatchBuilder::SHLOp}, // SAL
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_2, OpToIndex(0xD3), 7), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::ASHROp, false, false>}, // SAR
|
||||
|
||||
// GROUP 3
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_3, OpToIndex(0xF6), 0), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::TESTOp, 1>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_3, OpToIndex(0xF6), 1), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::TESTOp, 1>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_3, OpToIndex(0xF6), 2), 1, &OpDispatchBuilder::NOTOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_3, OpToIndex(0xF6), 3), 1, &OpDispatchBuilder::NEGOp}, // NEG
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_3, OpToIndex(0xF6), 4), 1, &OpDispatchBuilder::MULOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_3, OpToIndex(0xF6), 5), 1, &OpDispatchBuilder::IMULOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_3, OpToIndex(0xF6), 6), 1, &OpDispatchBuilder::DIVOp}, // DIV
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_3, OpToIndex(0xF6), 7), 1, &OpDispatchBuilder::IDIVOp}, // IDIV
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_3, OpToIndex(0xF7), 0), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::TESTOp, 1>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_3, OpToIndex(0xF7), 1), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::TESTOp, 1>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_3, OpToIndex(0xF7), 2), 1, &OpDispatchBuilder::NOTOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_3, OpToIndex(0xF7), 3), 1, &OpDispatchBuilder::NEGOp}, // NEG
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_3, OpToIndex(0xF7), 4), 1, &OpDispatchBuilder::MULOp}, // MUL
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_3, OpToIndex(0xF7), 5), 1, &OpDispatchBuilder::IMULOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_3, OpToIndex(0xF7), 6), 1, &OpDispatchBuilder::DIVOp}, // DIV
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_3, OpToIndex(0xF7), 7), 1, &OpDispatchBuilder::IDIVOp}, // IDIV
|
||||
|
||||
// GROUP 4
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_4, OpToIndex(0xFE), 0), 1, &OpDispatchBuilder::INCOp}, // INC
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_4, OpToIndex(0xFE), 1), 1, &OpDispatchBuilder::DECOp}, // DEC
|
||||
|
||||
// GROUP 5
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_5, OpToIndex(0xFF), 0), 1, &OpDispatchBuilder::INCOp}, // INC
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_5, OpToIndex(0xFF), 1), 1, &OpDispatchBuilder::DECOp}, // DEC
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_5, OpToIndex(0xFF), 2), 1, &OpDispatchBuilder::CALLAbsoluteOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_5, OpToIndex(0xFF), 4), 1, &OpDispatchBuilder::JUMPAbsoluteOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_5, OpToIndex(0xFF), 6), 1, &OpDispatchBuilder::PUSHOp},
|
||||
|
||||
// GROUP 11
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_11, OpToIndex(0xC6), 0), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::MOVGPROp, 1>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_11, OpToIndex(0xC7), 0), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::MOVGPROp, 1>},
|
||||
};
|
||||
#undef OPD
|
||||
|
||||
} // namespace FEXCore::IR
|
||||
@@ -0,0 +1,161 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
#include "Interface/Core/OpcodeDispatcher.h"
|
||||
|
||||
namespace FEXCore::IR {
|
||||
#define OPD(group, prefix, Reg) (((group - FEXCore::X86Tables::TYPE_GROUP_6) << 5) | (prefix) << 3 | (Reg))
|
||||
constexpr uint16_t PF_NONE = 0;
|
||||
constexpr uint16_t PF_F3 = 1;
|
||||
constexpr uint16_t PF_66 = 2;
|
||||
constexpr uint16_t PF_F2 = 3;
|
||||
constexpr std::tuple<uint16_t, uint8_t, FEXCore::X86Tables::OpDispatchPtr> OpDispatch_SecondaryGroupTables[] = {
|
||||
// GROUP 6
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_6, PF_NONE, 3), 1, &OpDispatchBuilder::PermissionRestrictedOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_6, PF_F3, 3), 1, &OpDispatchBuilder::PermissionRestrictedOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_6, PF_66, 3), 1, &OpDispatchBuilder::PermissionRestrictedOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_6, PF_F2, 3), 1, &OpDispatchBuilder::PermissionRestrictedOp},
|
||||
|
||||
// GROUP 7
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_7, PF_NONE, 0), 1, &OpDispatchBuilder::SGDTOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_7, PF_F3, 0), 1, &OpDispatchBuilder::SGDTOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_7, PF_66, 0), 1, &OpDispatchBuilder::SGDTOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_7, PF_F2, 0), 1, &OpDispatchBuilder::SGDTOp},
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_7, PF_NONE, 3), 1, &OpDispatchBuilder::PermissionRestrictedOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_7, PF_F3, 3), 1, &OpDispatchBuilder::PermissionRestrictedOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_7, PF_66, 3), 1, &OpDispatchBuilder::PermissionRestrictedOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_7, PF_F2, 3), 1, &OpDispatchBuilder::PermissionRestrictedOp},
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_7, PF_NONE, 4), 1, &OpDispatchBuilder::SMSWOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_7, PF_F3, 4), 1, &OpDispatchBuilder::SMSWOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_7, PF_66, 4), 1, &OpDispatchBuilder::SMSWOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_7, PF_F2, 4), 1, &OpDispatchBuilder::SMSWOp},
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_7, PF_NONE, 6), 1, &OpDispatchBuilder::PermissionRestrictedOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_7, PF_F3, 6), 1, &OpDispatchBuilder::PermissionRestrictedOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_7, PF_66, 6), 1, &OpDispatchBuilder::PermissionRestrictedOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_7, PF_F2, 6), 1, &OpDispatchBuilder::PermissionRestrictedOp},
|
||||
|
||||
// GROUP 8
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_8, PF_NONE, 4), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::BTOp, 1, BTAction::BTNone>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_8, PF_F3, 4), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::BTOp, 1, BTAction::BTNone>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_8, PF_66, 4), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::BTOp, 1, BTAction::BTNone>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_8, PF_F2, 4), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::BTOp, 1, BTAction::BTNone>},
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_8, PF_NONE, 5), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::BTOp, 1, BTAction::BTSet>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_8, PF_F3, 5), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::BTOp, 1, BTAction::BTSet>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_8, PF_66, 5), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::BTOp, 1, BTAction::BTSet>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_8, PF_F2, 5), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::BTOp, 1, BTAction::BTSet>},
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_8, PF_NONE, 6), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::BTOp, 1, BTAction::BTClear>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_8, PF_F3, 6), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::BTOp, 1, BTAction::BTClear>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_8, PF_66, 6), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::BTOp, 1, BTAction::BTClear>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_8, PF_F2, 6), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::BTOp, 1, BTAction::BTClear>},
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_8, PF_NONE, 7), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::BTOp, 1, BTAction::BTComplement>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_8, PF_F3, 7), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::BTOp, 1, BTAction::BTComplement>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_8, PF_66, 7), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::BTOp, 1, BTAction::BTComplement>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_8, PF_F2, 7), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::BTOp, 1, BTAction::BTComplement>},
|
||||
|
||||
// GROUP 9
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_9, PF_NONE, 1), 1, &OpDispatchBuilder::CMPXCHGPairOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_9, PF_F3, 1), 1, &OpDispatchBuilder::CMPXCHGPairOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_9, PF_66, 1), 1, &OpDispatchBuilder::CMPXCHGPairOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_9, PF_F2, 1), 1, &OpDispatchBuilder::CMPXCHGPairOp},
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_9, PF_F3, 7), 1, &OpDispatchBuilder::RDPIDOp},
|
||||
|
||||
// GROUP 12
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_12, PF_NONE, 2), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSRLI, 2>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_12, PF_NONE, 4), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSRAIOp, 2>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_12, PF_NONE, 6), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSLLI, 2>},
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_12, PF_66, 2), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSRLI, 2>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_12, PF_66, 4), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSRAIOp, 2>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_12, PF_66, 6), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSLLI, 2>},
|
||||
|
||||
// GROUP 13
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_13, PF_NONE, 2), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSRLI, 4>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_13, PF_NONE, 4), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSRAIOp, 4>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_13, PF_NONE, 6), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSLLI, 4>},
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_13, PF_66, 2), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSRLI, 4>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_13, PF_66, 4), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSRAIOp, 4>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_13, PF_66, 6), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSLLI, 4>},
|
||||
|
||||
// GROUP 14
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_14, PF_NONE, 2), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSRLI, 8>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_14, PF_NONE, 6), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSLLI, 8>},
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_14, PF_66, 2), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSRLI, 8>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_14, PF_66, 3), 1, &OpDispatchBuilder::PSRLDQ},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_14, PF_66, 6), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSLLI, 8>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_14, PF_66, 7), 1, &OpDispatchBuilder::PSLLDQ},
|
||||
|
||||
// GROUP 15
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_NONE, 0), 1, &OpDispatchBuilder::FXSaveOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_NONE, 1), 1, &OpDispatchBuilder::FXRStoreOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_NONE, 2), 1, &OpDispatchBuilder::LDMXCSR},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_NONE, 3), 1, &OpDispatchBuilder::STMXCSR},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_NONE, 4), 1, &OpDispatchBuilder::XSaveOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_NONE, 5), 1, &OpDispatchBuilder::LoadFenceOrXRSTOR}, // LFENCE (or XRSTOR)
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_NONE, 6), 1, &OpDispatchBuilder::MemFenceOrXSAVEOPT}, // MFENCE (or XSAVEOPT)
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_NONE, 7), 1, &OpDispatchBuilder::StoreFenceOrCLFlush}, // SFENCE (or CLFLUSH)
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_F3, 5), 1, &OpDispatchBuilder::UnimplementedOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_F3, 6), 1, &OpDispatchBuilder::UnimplementedOp},
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_66, 6), 1, &OpDispatchBuilder::CLWB},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_66, 7), 1, &OpDispatchBuilder::CLFLUSHOPT},
|
||||
|
||||
// GROUP 16
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_NONE, 0), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::Prefetch, false, true, 1>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_NONE, 1), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::Prefetch, false, false, 1>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_NONE, 2), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::Prefetch, false, false, 2>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_NONE, 3), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::Prefetch, false, false, 3>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_NONE, 4), 4, &OpDispatchBuilder::NOPOp},
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_F3, 0), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::Prefetch, false, true, 1>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_F3, 1), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::Prefetch, false, false, 1>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_F3, 2), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::Prefetch, false, false, 2>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_F3, 3), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::Prefetch, false, false, 3>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_F3, 4), 4, &OpDispatchBuilder::NOPOp},
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_66, 0), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::Prefetch, false, true, 1>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_66, 1), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::Prefetch, false, false, 1>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_66, 2), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::Prefetch, false, false, 2>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_66, 3), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::Prefetch, false, false, 3>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_66, 4), 4, &OpDispatchBuilder::NOPOp},
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_F2, 0), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::Prefetch, false, true, 1>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_F2, 1), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::Prefetch, false, false, 1>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_F2, 2), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::Prefetch, false, false, 2>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_F2, 3), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::Prefetch, false, false, 3>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_F2, 4), 4, &OpDispatchBuilder::NOPOp},
|
||||
|
||||
// GROUP P
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_P, PF_NONE, 0), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::Prefetch, false, false, 1>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_P, PF_NONE, 1), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::Prefetch, true, false, 1>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_P, PF_NONE, 2), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::Prefetch, true, false, 1>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_P, PF_NONE, 3), 5, &OpDispatchBuilder::NOPOp},
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_P, PF_F3, 0), 8, &OpDispatchBuilder::NOPOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_P, PF_66, 0), 8, &OpDispatchBuilder::NOPOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_P, PF_F2, 0), 8, &OpDispatchBuilder::NOPOp},
|
||||
};
|
||||
|
||||
constexpr std::tuple<uint16_t, uint8_t, FEXCore::X86Tables::OpDispatchPtr> OpDispatch_SecondaryGroupTables_64[] = {
|
||||
// GROUP 15
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_F3, 0), 1,
|
||||
&OpDispatchBuilder::Bind<&OpDispatchBuilder::ReadSegmentReg, OpDispatchBuilder::Segment::FS>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_F3, 1), 1,
|
||||
&OpDispatchBuilder::Bind<&OpDispatchBuilder::ReadSegmentReg, OpDispatchBuilder::Segment::GS>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_F3, 2), 1,
|
||||
&OpDispatchBuilder::Bind<&OpDispatchBuilder::WriteSegmentReg, OpDispatchBuilder::Segment::FS>},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_F3, 3), 1,
|
||||
&OpDispatchBuilder::Bind<&OpDispatchBuilder::WriteSegmentReg, OpDispatchBuilder::Segment::GS>},
|
||||
};
|
||||
|
||||
#undef OPD
|
||||
|
||||
} // namespace FEXCore::IR
|
||||
@@ -0,0 +1,22 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
#include "Interface/Core/OpcodeDispatcher.h"
|
||||
|
||||
namespace FEXCore::IR {
|
||||
constexpr std::tuple<uint8_t, uint8_t, FEXCore::X86Tables::OpDispatchPtr> OpDispatch_SecondaryModRMTables[] = {
|
||||
// REG /1
|
||||
{((0 << 3) | 0), 1, &OpDispatchBuilder::UnimplementedOp},
|
||||
{((0 << 3) | 1), 1, &OpDispatchBuilder::UnimplementedOp},
|
||||
|
||||
// REG /2
|
||||
{((1 << 3) | 0), 1, &OpDispatchBuilder::XGetBVOp},
|
||||
|
||||
// REG /3
|
||||
{((2 << 3) | 7), 1, &OpDispatchBuilder::PermissionRestrictedOp},
|
||||
|
||||
// REG /7
|
||||
{((3 << 3) | 0), 1, &OpDispatchBuilder::PermissionRestrictedOp},
|
||||
{((3 << 3) | 1), 1, &OpDispatchBuilder::RDTSCPOp},
|
||||
};
|
||||
|
||||
} // namespace FEXCore::IR
|
||||
@@ -0,0 +1,329 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
#include "Interface/Core/OpcodeDispatcher.h"
|
||||
|
||||
namespace FEXCore::IR {
|
||||
constexpr std::tuple<uint8_t, uint8_t, FEXCore::X86Tables::OpDispatchPtr> OpDispatch_TwoByteOpTable[] = {
|
||||
// Instructions
|
||||
{0x06, 1, &OpDispatchBuilder::PermissionRestrictedOp},
|
||||
{0x07, 1, &OpDispatchBuilder::PermissionRestrictedOp},
|
||||
{0x0B, 1, &OpDispatchBuilder::INTOp},
|
||||
{0x0E, 1, &OpDispatchBuilder::X87EMMS},
|
||||
|
||||
{0x19, 7, &OpDispatchBuilder::NOPOp}, // NOP with ModRM
|
||||
|
||||
{0x20, 4, &OpDispatchBuilder::PermissionRestrictedOp},
|
||||
|
||||
{0x30, 1, &OpDispatchBuilder::PermissionRestrictedOp},
|
||||
{0x31, 1, &OpDispatchBuilder::RDTSCOp},
|
||||
{0x32, 2, &OpDispatchBuilder::PermissionRestrictedOp},
|
||||
{0x34, 3, &OpDispatchBuilder::UnimplementedOp},
|
||||
|
||||
{0x3F, 1, &OpDispatchBuilder::ThunkOp},
|
||||
{0x40, 16, &OpDispatchBuilder::CMOVOp},
|
||||
{0x6E, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::MOVBetweenGPR_FPR, OpDispatchBuilder::VectorOpType::MMX>},
|
||||
{0x6F, 1, &OpDispatchBuilder::MOVQMMXOp},
|
||||
{0x7E, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::MOVBetweenGPR_FPR, OpDispatchBuilder::VectorOpType::MMX>},
|
||||
{0x7F, 1, &OpDispatchBuilder::MOVQMMXOp},
|
||||
{0x80, 16, &OpDispatchBuilder::CondJUMPOp},
|
||||
{0x90, 16, &OpDispatchBuilder::SETccOp},
|
||||
{0xA2, 1, &OpDispatchBuilder::CPUIDOp},
|
||||
{0xA3, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::BTOp, 0, BTAction::BTNone>}, // BT
|
||||
{0xA4, 1, &OpDispatchBuilder::SHLDImmediateOp},
|
||||
{0xA5, 1, &OpDispatchBuilder::SHLDOp},
|
||||
{0xAB, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::BTOp, 0, BTAction::BTSet>}, // BTS
|
||||
{0xAC, 1, &OpDispatchBuilder::SHRDImmediateOp},
|
||||
{0xAD, 1, &OpDispatchBuilder::SHRDOp},
|
||||
{0xAF, 1, &OpDispatchBuilder::IMUL1SrcOp},
|
||||
{0xB0, 2, &OpDispatchBuilder::CMPXCHGOp}, // CMPXCHG
|
||||
{0xB3, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::BTOp, 0, BTAction::BTClear>}, // BTR
|
||||
{0xB6, 2, &OpDispatchBuilder::MOVZXOp},
|
||||
{0xBB, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::BTOp, 0, BTAction::BTComplement>}, // BTC
|
||||
{0xBC, 1, &OpDispatchBuilder::BSFOp}, // BSF
|
||||
{0xBD, 1, &OpDispatchBuilder::BSROp}, // BSF
|
||||
{0xBE, 2, &OpDispatchBuilder::MOVSXOp},
|
||||
{0xC0, 2, &OpDispatchBuilder::XADDOp},
|
||||
{0xC3, 1, &OpDispatchBuilder::MOVGPRNTOp},
|
||||
{0xC4, 1, &OpDispatchBuilder::PINSROp<2>},
|
||||
{0xC5, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PExtrOp, 2>},
|
||||
{0xC8, 8, &OpDispatchBuilder::BSWAPOp},
|
||||
|
||||
// SSE
|
||||
{0x10, 2, &OpDispatchBuilder::MOVVectorUnalignedOp},
|
||||
{0x12, 2, &OpDispatchBuilder::MOVLPOp},
|
||||
{0x14, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUNPCKLOp, 4>},
|
||||
{0x15, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUNPCKHOp, 4>},
|
||||
{0x16, 2, &OpDispatchBuilder::MOVHPDOp},
|
||||
{0x28, 2, &OpDispatchBuilder::MOVVectorAlignedOp},
|
||||
{0x2A, 1, &OpDispatchBuilder::InsertMMX_To_XMM_Vector_CVT_Int_To_Float},
|
||||
{0x2B, 1, &OpDispatchBuilder::MOVVectorNTOp},
|
||||
{0x2C, 1, &OpDispatchBuilder::XMM_To_MMX_Vector_CVT_Float_To_Int<4, false, false>},
|
||||
{0x2D, 1, &OpDispatchBuilder::XMM_To_MMX_Vector_CVT_Float_To_Int<4, false, true>},
|
||||
{0x2E, 2, &OpDispatchBuilder::UCOMISxOp<4>},
|
||||
{0x50, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::MOVMSKOp, 4>},
|
||||
{0x51, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorUnaryOp, IR::OP_VFSQRT, 4>},
|
||||
{0x52, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorUnaryOp, IR::OP_VFRSQRT, 4>},
|
||||
{0x53, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorUnaryOp, IR::OP_VFRECP, 4>},
|
||||
{0x54, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VAND, 16>},
|
||||
{0x55, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUROp, IR::OP_VANDN, 8>},
|
||||
{0x56, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VOR, 16>},
|
||||
{0x57, 1, &OpDispatchBuilder::VectorXOROp},
|
||||
{0x58, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VFADD, 4>},
|
||||
{0x59, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VFMUL, 4>},
|
||||
{0x5A, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::Vector_CVT_Float_To_Float, 8, 4, false>},
|
||||
{0x5B, 1, &OpDispatchBuilder::Vector_CVT_Int_To_Float<4, false>},
|
||||
{0x5C, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VFSUB, 4>},
|
||||
{0x5D, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VFMIN, 4>},
|
||||
{0x5E, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VFDIV, 4>},
|
||||
{0x5F, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VFMAX, 4>},
|
||||
{0x60, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUNPCKLOp, 1>},
|
||||
{0x61, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUNPCKLOp, 2>},
|
||||
{0x62, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUNPCKLOp, 4>},
|
||||
{0x63, 1, &OpDispatchBuilder::PACKSSOp<2>},
|
||||
{0x64, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VCMPGT, 1>},
|
||||
{0x65, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VCMPGT, 2>},
|
||||
{0x66, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VCMPGT, 4>},
|
||||
{0x67, 1, &OpDispatchBuilder::PACKUSOp<2>},
|
||||
{0x68, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUNPCKHOp, 1>},
|
||||
{0x69, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUNPCKHOp, 2>},
|
||||
{0x6A, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUNPCKHOp, 4>},
|
||||
{0x6B, 1, &OpDispatchBuilder::PACKSSOp<4>},
|
||||
{0x70, 1, &OpDispatchBuilder::PSHUFW8ByteOp},
|
||||
|
||||
{0x74, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VCMPEQ, 1>},
|
||||
{0x75, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VCMPEQ, 2>},
|
||||
{0x76, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VCMPEQ, 4>},
|
||||
{0x77, 1, &OpDispatchBuilder::X87EMMS},
|
||||
|
||||
{0xC2, 1, &OpDispatchBuilder::VFCMPOp<4>},
|
||||
{0xC6, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::SHUFOp, 4>},
|
||||
|
||||
{0xD1, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSRLDOp, 2>},
|
||||
{0xD2, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSRLDOp, 4>},
|
||||
{0xD3, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSRLDOp, 8>},
|
||||
{0xD4, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VADD, 8>},
|
||||
{0xD5, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VMUL, 2>},
|
||||
{0xD7, 1, &OpDispatchBuilder::MOVMSKOpOne}, // PMOVMSKB
|
||||
{0xD8, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VUQSUB, 1>},
|
||||
{0xD9, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VUQSUB, 2>},
|
||||
{0xDA, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VUMIN, 1>},
|
||||
{0xDB, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VAND, 8>},
|
||||
{0xDC, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VUQADD, 1>},
|
||||
{0xDD, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VUQADD, 2>},
|
||||
{0xDE, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VUMAX, 1>},
|
||||
{0xDF, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUROp, IR::OP_VANDN, 8>},
|
||||
{0xE0, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VURAVG, 1>},
|
||||
{0xE1, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSRAOp, 2>},
|
||||
{0xE2, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSRAOp, 4>},
|
||||
{0xE3, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VURAVG, 2>},
|
||||
{0xE4, 1, &OpDispatchBuilder::PMULHW<false>},
|
||||
{0xE5, 1, &OpDispatchBuilder::PMULHW<true>},
|
||||
{0xE7, 1, &OpDispatchBuilder::MOVVectorNTOp},
|
||||
{0xE8, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VSQSUB, 1>},
|
||||
{0xE9, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VSQSUB, 2>},
|
||||
{0xEA, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VSMIN, 2>},
|
||||
{0xEB, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VOR, 8>},
|
||||
{0xEC, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VSQADD, 1>},
|
||||
{0xED, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VSQADD, 2>},
|
||||
{0xEE, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VSMAX, 2>},
|
||||
{0xEF, 1, &OpDispatchBuilder::VectorXOROp},
|
||||
|
||||
{0xF1, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSLL, 2>},
|
||||
{0xF2, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSLL, 4>},
|
||||
{0xF3, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSLL, 8>},
|
||||
{0xF4, 1, &OpDispatchBuilder::PMULLOp<4, false>},
|
||||
{0xF5, 1, &OpDispatchBuilder::PMADDWD},
|
||||
{0xF6, 1, &OpDispatchBuilder::PSADBW},
|
||||
{0xF7, 1, &OpDispatchBuilder::MASKMOVOp},
|
||||
{0xF8, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VSUB, 1>},
|
||||
{0xF9, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VSUB, 2>},
|
||||
{0xFA, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VSUB, 4>},
|
||||
{0xFB, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VSUB, 8>},
|
||||
{0xFC, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VADD, 1>},
|
||||
{0xFD, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VADD, 2>},
|
||||
{0xFE, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VADD, 4>},
|
||||
|
||||
// FEX reserved instructions
|
||||
{0x37, 1, &OpDispatchBuilder::CallbackReturnOp},
|
||||
};
|
||||
|
||||
constexpr std::tuple<uint8_t, uint8_t, FEXCore::X86Tables::OpDispatchPtr> OpDispatch_SecondaryRepModTables[] = {
|
||||
{0x10, 2, &OpDispatchBuilder::MOVSSOp},
|
||||
{0x12, 1, &OpDispatchBuilder::VMOVSLDUPOp},
|
||||
{0x16, 1, &OpDispatchBuilder::VMOVSHDUPOp},
|
||||
{0x2A, 1, &OpDispatchBuilder::InsertCVTGPR_To_FPR<4>},
|
||||
{0x2B, 1, &OpDispatchBuilder::MOVVectorNTOp},
|
||||
{0x2C, 1, &OpDispatchBuilder::CVTFPR_To_GPR<4, false>},
|
||||
{0x2D, 1, &OpDispatchBuilder::CVTFPR_To_GPR<4, true>},
|
||||
{0x51, 1, &OpDispatchBuilder::VectorScalarUnaryInsertALUOp<IR::OP_VFSQRTSCALARINSERT, 4>},
|
||||
{0x52, 1, &OpDispatchBuilder::VectorScalarUnaryInsertALUOp<IR::OP_VFRSQRTSCALARINSERT, 4>},
|
||||
{0x53, 1, &OpDispatchBuilder::VectorScalarUnaryInsertALUOp<IR::OP_VFRECPSCALARINSERT, 4>},
|
||||
{0x58, 1, &OpDispatchBuilder::VectorScalarInsertALUOp<IR::OP_VFADDSCALARINSERT, 4>},
|
||||
{0x59, 1, &OpDispatchBuilder::VectorScalarInsertALUOp<IR::OP_VFMULSCALARINSERT, 4>},
|
||||
{0x5A, 1, &OpDispatchBuilder::InsertScalar_CVT_Float_To_Float<8, 4>},
|
||||
{0x5B, 1, &OpDispatchBuilder::Vector_CVT_Float_To_Int<4, false, false>},
|
||||
{0x5C, 1, &OpDispatchBuilder::VectorScalarInsertALUOp<IR::OP_VFSUBSCALARINSERT, 4>},
|
||||
{0x5D, 1, &OpDispatchBuilder::VectorScalarInsertALUOp<IR::OP_VFMINSCALARINSERT, 4>},
|
||||
{0x5E, 1, &OpDispatchBuilder::VectorScalarInsertALUOp<IR::OP_VFDIVSCALARINSERT, 4>},
|
||||
{0x5F, 1, &OpDispatchBuilder::VectorScalarInsertALUOp<IR::OP_VFMAXSCALARINSERT, 4>},
|
||||
{0x6F, 1, &OpDispatchBuilder::MOVVectorUnalignedOp},
|
||||
{0x70, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSHUFWOp, false>},
|
||||
{0x7E, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::MOVQOp, OpDispatchBuilder::VectorOpType::SSE>},
|
||||
{0x7F, 1, &OpDispatchBuilder::MOVVectorUnalignedOp},
|
||||
{0xB8, 1, &OpDispatchBuilder::PopcountOp},
|
||||
{0xBC, 1, &OpDispatchBuilder::TZCNT},
|
||||
{0xBD, 1, &OpDispatchBuilder::LZCNT},
|
||||
{0xC2, 1, &OpDispatchBuilder::InsertScalarFCMPOp<4>},
|
||||
{0xD6, 1, &OpDispatchBuilder::MOVQ2DQ<true>},
|
||||
{0xE6, 1, &OpDispatchBuilder::Vector_CVT_Int_To_Float<4, true>},
|
||||
};
|
||||
|
||||
constexpr std::tuple<uint8_t, uint8_t, FEXCore::X86Tables::OpDispatchPtr> OpDispatch_SecondaryRepNEModTables[] = {
|
||||
{0x10, 2, &OpDispatchBuilder::MOVSDOp},
|
||||
{0x12, 1, &OpDispatchBuilder::MOVDDUPOp},
|
||||
{0x2A, 1, &OpDispatchBuilder::InsertCVTGPR_To_FPR<8>},
|
||||
{0x2B, 1, &OpDispatchBuilder::MOVVectorNTOp},
|
||||
{0x2C, 1, &OpDispatchBuilder::CVTFPR_To_GPR<8, false>},
|
||||
{0x2D, 1, &OpDispatchBuilder::CVTFPR_To_GPR<8, true>},
|
||||
{0x51, 1, &OpDispatchBuilder::VectorScalarUnaryInsertALUOp<IR::OP_VFSQRTSCALARINSERT, 8>},
|
||||
// x52 = Invalid
|
||||
{0x58, 1, &OpDispatchBuilder::VectorScalarInsertALUOp<IR::OP_VFADDSCALARINSERT, 8>},
|
||||
{0x59, 1, &OpDispatchBuilder::VectorScalarInsertALUOp<IR::OP_VFMULSCALARINSERT, 8>},
|
||||
{0x5A, 1, &OpDispatchBuilder::InsertScalar_CVT_Float_To_Float<4, 8>},
|
||||
{0x5C, 1, &OpDispatchBuilder::VectorScalarInsertALUOp<IR::OP_VFSUBSCALARINSERT, 8>},
|
||||
{0x5D, 1, &OpDispatchBuilder::VectorScalarInsertALUOp<IR::OP_VFMINSCALARINSERT, 8>},
|
||||
{0x5E, 1, &OpDispatchBuilder::VectorScalarInsertALUOp<IR::OP_VFDIVSCALARINSERT, 8>},
|
||||
{0x5F, 1, &OpDispatchBuilder::VectorScalarInsertALUOp<IR::OP_VFMAXSCALARINSERT, 8>},
|
||||
{0x70, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSHUFWOp, true>},
|
||||
{0x7C, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VFADDP, 4>},
|
||||
{0x7D, 1, &OpDispatchBuilder::HSUBP<4>},
|
||||
{0xD0, 1, &OpDispatchBuilder::ADDSUBPOp<4>},
|
||||
{0xD6, 1, &OpDispatchBuilder::MOVQ2DQ<false>},
|
||||
{0xC2, 1, &OpDispatchBuilder::InsertScalarFCMPOp<8>},
|
||||
{0xE6, 1, &OpDispatchBuilder::Vector_CVT_Float_To_Int<8, true, true>},
|
||||
{0xF0, 1, &OpDispatchBuilder::MOVVectorUnalignedOp},
|
||||
};
|
||||
|
||||
constexpr std::tuple<uint8_t, uint8_t, FEXCore::X86Tables::OpDispatchPtr> OpDispatch_SecondaryOpSizeModTables[] = {
|
||||
{0x10, 2, &OpDispatchBuilder::MOVVectorUnalignedOp},
|
||||
{0x12, 2, &OpDispatchBuilder::MOVLPOp},
|
||||
{0x14, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUNPCKLOp, 8>},
|
||||
{0x15, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUNPCKHOp, 8>},
|
||||
{0x16, 2, &OpDispatchBuilder::MOVHPDOp},
|
||||
{0x28, 2, &OpDispatchBuilder::MOVVectorAlignedOp},
|
||||
{0x2A, 1, &OpDispatchBuilder::MMX_To_XMM_Vector_CVT_Int_To_Float},
|
||||
{0x2B, 1, &OpDispatchBuilder::MOVVectorNTOp},
|
||||
{0x2C, 1, &OpDispatchBuilder::XMM_To_MMX_Vector_CVT_Float_To_Int<8, true, false>},
|
||||
{0x2D, 1, &OpDispatchBuilder::XMM_To_MMX_Vector_CVT_Float_To_Int<8, true, true>},
|
||||
{0x2E, 2, &OpDispatchBuilder::UCOMISxOp<8>},
|
||||
|
||||
{0x50, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::MOVMSKOp, 8>},
|
||||
{0x51, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorUnaryOp, IR::OP_VFSQRT, 8>},
|
||||
{0x54, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VAND, 16>},
|
||||
{0x55, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUROp, IR::OP_VANDN, 8>},
|
||||
{0x56, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VOR, 16>},
|
||||
{0x57, 1, &OpDispatchBuilder::VectorXOROp},
|
||||
{0x58, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VFADD, 8>},
|
||||
{0x59, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VFMUL, 8>},
|
||||
{0x5A, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::Vector_CVT_Float_To_Float, 4, 8, false>},
|
||||
{0x5B, 1, &OpDispatchBuilder::Vector_CVT_Float_To_Int<4, false, true>},
|
||||
{0x5C, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VFSUB, 8>},
|
||||
{0x5D, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VFMIN, 8>},
|
||||
{0x5E, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VFDIV, 8>},
|
||||
{0x5F, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VFMAX, 8>},
|
||||
{0x60, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUNPCKLOp, 1>},
|
||||
{0x61, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUNPCKLOp, 2>},
|
||||
{0x62, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUNPCKLOp, 4>},
|
||||
{0x63, 1, &OpDispatchBuilder::PACKSSOp<2>},
|
||||
{0x64, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VCMPGT, 1>},
|
||||
{0x65, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VCMPGT, 2>},
|
||||
{0x66, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VCMPGT, 4>},
|
||||
{0x67, 1, &OpDispatchBuilder::PACKUSOp<2>},
|
||||
{0x68, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUNPCKHOp, 1>},
|
||||
{0x69, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUNPCKHOp, 2>},
|
||||
{0x6A, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUNPCKHOp, 4>},
|
||||
{0x6B, 1, &OpDispatchBuilder::PACKSSOp<4>},
|
||||
{0x6C, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUNPCKLOp, 8>},
|
||||
{0x6D, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUNPCKHOp, 8>},
|
||||
{0x6E, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::MOVBetweenGPR_FPR, OpDispatchBuilder::VectorOpType::SSE>},
|
||||
{0x6F, 1, &OpDispatchBuilder::MOVVectorAlignedOp},
|
||||
{0x70, 1, &OpDispatchBuilder::PSHUFDOp},
|
||||
|
||||
{0x74, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VCMPEQ, 1>},
|
||||
{0x75, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VCMPEQ, 2>},
|
||||
{0x76, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VCMPEQ, 4>},
|
||||
{0x78, 1, nullptr}, // GROUP 17
|
||||
{0x7C, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VFADDP, 8>},
|
||||
{0x7D, 1, &OpDispatchBuilder::HSUBP<8>},
|
||||
{0x7E, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::MOVBetweenGPR_FPR, OpDispatchBuilder::VectorOpType::SSE>},
|
||||
{0x7F, 1, &OpDispatchBuilder::MOVVectorAlignedOp},
|
||||
{0xC2, 1, &OpDispatchBuilder::VFCMPOp<8>},
|
||||
{0xC4, 1, &OpDispatchBuilder::PINSROp<2>},
|
||||
{0xC5, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PExtrOp, 2>},
|
||||
{0xC6, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::SHUFOp, 8>},
|
||||
|
||||
{0xD0, 1, &OpDispatchBuilder::ADDSUBPOp<8>},
|
||||
{0xD1, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSRLDOp, 2>},
|
||||
{0xD2, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSRLDOp, 4>},
|
||||
{0xD3, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSRLDOp, 8>},
|
||||
{0xD4, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VADD, 8>},
|
||||
{0xD5, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VMUL, 2>},
|
||||
{0xD6, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::MOVQOp, OpDispatchBuilder::VectorOpType::SSE>},
|
||||
{0xD7, 1, &OpDispatchBuilder::MOVMSKOpOne}, // PMOVMSKB
|
||||
{0xD8, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VUQSUB, 1>},
|
||||
{0xD9, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VUQSUB, 2>},
|
||||
{0xDA, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VUMIN, 1>},
|
||||
{0xDB, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VAND, 16>},
|
||||
{0xDC, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VUQADD, 1>},
|
||||
{0xDD, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VUQADD, 2>},
|
||||
{0xDE, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VUMAX, 1>},
|
||||
{0xDF, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUROp, IR::OP_VANDN, 8>},
|
||||
{0xE0, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VURAVG, 1>},
|
||||
{0xE1, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSRAOp, 2>},
|
||||
{0xE2, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSRAOp, 4>},
|
||||
{0xE3, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VURAVG, 2>},
|
||||
{0xE4, 1, &OpDispatchBuilder::PMULHW<false>},
|
||||
{0xE5, 1, &OpDispatchBuilder::PMULHW<true>},
|
||||
{0xE6, 1, &OpDispatchBuilder::Vector_CVT_Float_To_Int<8, true, false>},
|
||||
{0xE7, 1, &OpDispatchBuilder::MOVVectorNTOp},
|
||||
{0xE8, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VSQSUB, 1>},
|
||||
{0xE9, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VSQSUB, 2>},
|
||||
{0xEA, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VSMIN, 2>},
|
||||
{0xEB, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VOR, 16>},
|
||||
{0xEC, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VSQADD, 1>},
|
||||
{0xED, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VSQADD, 2>},
|
||||
{0xEE, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VSMAX, 2>},
|
||||
{0xEF, 1, &OpDispatchBuilder::VectorXOROp},
|
||||
|
||||
{0xF1, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSLL, 2>},
|
||||
{0xF2, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSLL, 4>},
|
||||
{0xF3, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PSLL, 8>},
|
||||
{0xF4, 1, &OpDispatchBuilder::PMULLOp<4, false>},
|
||||
{0xF5, 1, &OpDispatchBuilder::PMADDWD},
|
||||
{0xF6, 1, &OpDispatchBuilder::PSADBW},
|
||||
{0xF7, 1, &OpDispatchBuilder::MASKMOVOp},
|
||||
{0xF8, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VSUB, 1>},
|
||||
{0xF9, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VSUB, 2>},
|
||||
{0xFA, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VSUB, 4>},
|
||||
{0xFB, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VSUB, 8>},
|
||||
{0xFC, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VADD, 1>},
|
||||
{0xFD, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VADD, 2>},
|
||||
{0xFE, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VADD, 4>},
|
||||
};
|
||||
|
||||
constexpr std::tuple<uint8_t, uint8_t, FEXCore::X86Tables::OpDispatchPtr> OpDispatch_TwoByteOpTable_64[] = {
|
||||
{0x05, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::SyscallOp, true>},
|
||||
{0xA0, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUSHSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_FS_PREFIX>},
|
||||
{0xA1, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::POPSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_FS_PREFIX>},
|
||||
{0xA8, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUSHSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_GS_PREFIX>},
|
||||
{0xA9, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::POPSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_GS_PREFIX>},
|
||||
};
|
||||
|
||||
constexpr std::tuple<uint8_t, uint8_t, FEXCore::X86Tables::OpDispatchPtr> OpDispatch_TwoByteOpTable_32[] = {
|
||||
{0x05, 1, &OpDispatchBuilder::NOPOp},
|
||||
{0xA0, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUSHSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_FS_PREFIX>},
|
||||
{0xA1, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::POPSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_FS_PREFIX>},
|
||||
{0xA8, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::PUSHSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_GS_PREFIX>},
|
||||
{0xA9, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::POPSegmentOp, FEXCore::X86Tables::DecodeFlags::FLAG_GS_PREFIX>},
|
||||
};
|
||||
} // namespace FEXCore::IR
|
||||
@@ -0,0 +1,26 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
#include "Interface/Core/OpcodeDispatcher.h"
|
||||
|
||||
namespace FEXCore::IR {
|
||||
#define OPD(map_select, pp, opcode) (((map_select - 1) << 10) | (pp << 8) | (opcode))
|
||||
constexpr std::tuple<uint16_t, uint8_t, FEXCore::X86Tables::OpDispatchPtr> OpDispatch_VEXTable[] = {
|
||||
{OPD(2, 0b00, 0xF2), 1, &OpDispatchBuilder::ANDNBMIOp}, {OPD(2, 0b00, 0xF5), 1, &OpDispatchBuilder::BZHI},
|
||||
{OPD(2, 0b10, 0xF5), 1, &OpDispatchBuilder::PEXT}, {OPD(2, 0b11, 0xF5), 1, &OpDispatchBuilder::PDEP},
|
||||
{OPD(2, 0b11, 0xF6), 1, &OpDispatchBuilder::MULX}, {OPD(2, 0b00, 0xF7), 1, &OpDispatchBuilder::BEXTRBMIOp},
|
||||
{OPD(2, 0b01, 0xF7), 1, &OpDispatchBuilder::BMI2Shift}, {OPD(2, 0b10, 0xF7), 1, &OpDispatchBuilder::BMI2Shift},
|
||||
{OPD(2, 0b11, 0xF7), 1, &OpDispatchBuilder::BMI2Shift},
|
||||
|
||||
{OPD(3, 0b11, 0xF0), 1, &OpDispatchBuilder::RORX},
|
||||
};
|
||||
#undef OPD
|
||||
|
||||
#define OPD(group, pp, opcode) (((group - X86Tables::InstType::TYPE_VEX_GROUP_12) << 4) | (pp << 3) | (opcode))
|
||||
constexpr std::tuple<uint8_t, uint8_t, X86Tables::OpDispatchPtr> OpDispatch_VEXGroupTable[] = {
|
||||
{OPD(X86Tables::InstType::TYPE_VEX_GROUP_17, 0, 0b001), 1, &OpDispatchBuilder::BLSRBMIOp},
|
||||
{OPD(X86Tables::InstType::TYPE_VEX_GROUP_17, 0, 0b010), 1, &OpDispatchBuilder::BLSMSKBMIOp},
|
||||
{OPD(X86Tables::InstType::TYPE_VEX_GROUP_17, 0, 0b011), 1, &OpDispatchBuilder::BLSIBMIOp},
|
||||
};
|
||||
#undef OPD
|
||||
|
||||
} // namespace FEXCore::IR
|
||||
@@ -106,7 +106,7 @@ void OpDispatchBuilder::MOVHPDOp(OpcodeArgs) {
|
||||
} else {
|
||||
// If the destination is a GPR then the source is memory
|
||||
// xmm1[127:64] = src
|
||||
Ref Src = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, {.LoadData = false});
|
||||
Ref Src = MakeSegmentAddress(Op, Op->Src[0]);
|
||||
Ref Dest = LoadSource_WithOpSize(FPRClass, Op, Op->Dest, 16, Op->Flags);
|
||||
auto Result = _VLoadVectorElement(16, 8, Dest, 1, Src);
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
@@ -115,7 +115,7 @@ void OpDispatchBuilder::MOVHPDOp(OpcodeArgs) {
|
||||
// In this case memory is the destination and the high bits of the XMM are source
|
||||
// Mem64 = xmm1[127:64]
|
||||
Ref Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
|
||||
Ref Dest = LoadSource_WithOpSize(GPRClass, Op, Op->Dest, 8, Op->Flags, {.LoadData = false});
|
||||
Ref Dest = MakeSegmentAddress(Op, Op->Dest);
|
||||
_VStoreVectorElement(16, 8, Src, 1, Dest);
|
||||
}
|
||||
}
|
||||
@@ -144,7 +144,7 @@ void OpDispatchBuilder::MOVLPOp(OpcodeArgs) {
|
||||
StoreResult_WithOpSize(FPRClass, Op, Op->Dest, Result, 16, 16);
|
||||
} else {
|
||||
auto DstSize = GetDstSize(Op);
|
||||
Ref Src = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, {.Align = 8, .LoadData = false});
|
||||
Ref Src = MakeSegmentAddress(Op, Op->Src[0]);
|
||||
Ref Dest = LoadSource_WithOpSize(FPRClass, Op, Op->Dest, DstSize, Op->Flags);
|
||||
auto Result = _VLoadVectorElement(16, 8, Dest, 0, Src);
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
@@ -1515,7 +1515,7 @@ Ref OpDispatchBuilder::PINSROpImpl(OpcodeArgs, size_t ElementSize, const X86Tabl
|
||||
}
|
||||
|
||||
// If loading from memory then we only load the element size
|
||||
auto Src2 = LoadSource_WithOpSize(GPRClass, Op, Src2Op, ElementSize, Op->Flags, {.LoadData = false});
|
||||
Ref Src2 = MakeSegmentAddress(Op, Src2Op);
|
||||
return _VLoadVectorElement(Size, ElementSize, Src1, Index, Src2);
|
||||
}
|
||||
|
||||
@@ -1639,7 +1639,7 @@ void OpDispatchBuilder::PExtrOp(OpcodeArgs, size_t ElementSize) {
|
||||
}
|
||||
|
||||
// If we are storing to memory then we store the size of the element extracted
|
||||
Ref Dest = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.LoadData = false});
|
||||
Ref Dest = MakeSegmentAddress(Op, Op->Dest);
|
||||
_VStoreVectorElement(16, OverridenElementSize, Src, Index, Dest);
|
||||
}
|
||||
|
||||
|
||||
@@ -150,8 +150,6 @@ void OpDispatchBuilder::FSTToStack(OpcodeArgs) {
|
||||
// Store integer to memory (possibly with truncation)
|
||||
void OpDispatchBuilder::FIST(OpcodeArgs, bool Truncate) {
|
||||
auto Size = GetSrcSize(Op);
|
||||
// FIXME(pmatos): is there any advantage of using STORESTACKMEMORY here?
|
||||
// Do we need STORESTACKMEMORY at all?
|
||||
Ref Data = _ReadStackValue(0);
|
||||
Data = _F80CVTInt(Size, Data, Truncate);
|
||||
|
||||
@@ -177,16 +175,15 @@ void OpDispatchBuilder::FADD(OpcodeArgs, size_t Width, bool Integer, OpDispatchB
|
||||
return;
|
||||
}
|
||||
|
||||
LOGMAN_THROW_A_FMT(Width != 80, "No 80-bit floats from memory");
|
||||
// We have one memory argument
|
||||
Ref Arg {};
|
||||
if (Width == 16 || Width == 32 || Width == 64) {
|
||||
if (Integer) {
|
||||
Arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
|
||||
Arg = _F80CVTToInt(Arg, Width / 8);
|
||||
} else {
|
||||
Arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
|
||||
Arg = _F80CVTTo(Arg, Width / 8);
|
||||
}
|
||||
if (Integer) {
|
||||
Arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
|
||||
Arg = _F80CVTToInt(Arg, Width / 8);
|
||||
} else {
|
||||
Arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
|
||||
Arg = _F80CVTTo(Arg, Width / 8);
|
||||
}
|
||||
|
||||
// top of stack is at offset zero
|
||||
@@ -208,16 +205,15 @@ void OpDispatchBuilder::FMUL(OpcodeArgs, size_t Width, bool Integer, OpDispatchB
|
||||
return;
|
||||
}
|
||||
|
||||
LOGMAN_THROW_A_FMT(Width != 80, "No 80-bit floats from memory");
|
||||
// We have one memory argument
|
||||
Ref arg {};
|
||||
if (Width == 16 || Width == 32 || Width == 64) {
|
||||
if (Integer) {
|
||||
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
|
||||
arg = _F80CVTToInt(arg, Width / 8);
|
||||
} else {
|
||||
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
|
||||
arg = _F80CVTTo(arg, Width / 8);
|
||||
}
|
||||
if (Integer) {
|
||||
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
|
||||
arg = _F80CVTToInt(arg, Width / 8);
|
||||
} else {
|
||||
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
|
||||
arg = _F80CVTTo(arg, Width / 8);
|
||||
}
|
||||
|
||||
// top of stack is at offset zero
|
||||
@@ -246,16 +242,15 @@ void OpDispatchBuilder::FDIV(OpcodeArgs, size_t Width, bool Integer, bool Revers
|
||||
return;
|
||||
}
|
||||
|
||||
LOGMAN_THROW_A_FMT(Width != 80, "No 80-bit floats from memory");
|
||||
// We have one memory argument
|
||||
Ref arg {};
|
||||
if (Width == 16 || Width == 32 || Width == 64) {
|
||||
if (Integer) {
|
||||
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
|
||||
arg = _F80CVTToInt(arg, Width / 8);
|
||||
} else {
|
||||
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
|
||||
arg = _F80CVTTo(arg, Width / 8);
|
||||
}
|
||||
if (Integer) {
|
||||
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
|
||||
arg = _F80CVTToInt(arg, Width / 8);
|
||||
} else {
|
||||
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
|
||||
arg = _F80CVTTo(arg, Width / 8);
|
||||
}
|
||||
|
||||
// top of stack is at offset zero
|
||||
@@ -288,16 +283,15 @@ void OpDispatchBuilder::FSUB(OpcodeArgs, size_t Width, bool Integer, bool Revers
|
||||
return;
|
||||
}
|
||||
|
||||
LOGMAN_THROW_A_FMT(Width != 80, "No 80-bit floats from memory");
|
||||
// We have one memory argument
|
||||
Ref Arg {};
|
||||
if (Width == 16 || Width == 32 || Width == 64) {
|
||||
if (Integer) {
|
||||
Arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
|
||||
Arg = _F80CVTToInt(Arg, Width / 8);
|
||||
} else {
|
||||
Arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
|
||||
Arg = _F80CVTTo(Arg, Width / 8);
|
||||
}
|
||||
if (Integer) {
|
||||
Arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
|
||||
Arg = _F80CVTToInt(Arg, Width / 8);
|
||||
} else {
|
||||
Arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
|
||||
Arg = _F80CVTTo(Arg, Width / 8);
|
||||
}
|
||||
|
||||
// top of stack is at offset zero
|
||||
|
||||
@@ -22,26 +22,6 @@ class OrderedNode;
|
||||
|
||||
#define OpcodeArgs [[maybe_unused]] FEXCore::X86Tables::DecodedOp Op
|
||||
|
||||
// Functions in X87.cpp (no change required)
|
||||
// GetX87Top
|
||||
// SetX87ValidTag
|
||||
// GetX87ValidTag
|
||||
// GetX87Tag (will need changing once special tag handling is implemented)
|
||||
// SetX87FTW
|
||||
// GetX87FTW (will need changing once special tag handling is implemented)
|
||||
// SetX87Top
|
||||
// X87ModifySTP
|
||||
// EMMS
|
||||
// FFREE
|
||||
// FNSTENV
|
||||
// FSTCW
|
||||
// LDSW
|
||||
// FNSTSW
|
||||
// FXCH
|
||||
// FCMOV
|
||||
// FST(register to register)
|
||||
// FCHS
|
||||
|
||||
void OpDispatchBuilder::FNINITF64(OpcodeArgs) {
|
||||
// Init host rounding mode to zero
|
||||
auto Zero = _Constant(0);
|
||||
|
||||
@@ -14,12 +14,14 @@ namespace FEXCore::X86Tables {
|
||||
|
||||
void InitializeBaseTables(Context::OperatingMode Mode);
|
||||
void InitializeSecondaryTables(Context::OperatingMode Mode);
|
||||
void InitializeSecondaryGroupTables(Context::OperatingMode Mode);
|
||||
void InitializePrimaryGroupTables(Context::OperatingMode Mode);
|
||||
void InitializeH0F3ATables(Context::OperatingMode Mode);
|
||||
|
||||
void InitializeInfoTables(Context::OperatingMode Mode) {
|
||||
InitializeBaseTables(Mode);
|
||||
InitializeSecondaryTables(Mode);
|
||||
InitializeSecondaryGroupTables(Mode);
|
||||
InitializePrimaryGroupTables(Mode);
|
||||
InitializeH0F3ATables(Mode);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ $end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/X86Tables/X86Tables.h"
|
||||
#include "Interface/Core/OpcodeDispatcher/BaseTables.h"
|
||||
|
||||
#include <FEXCore/Core/Context.h>
|
||||
|
||||
@@ -236,6 +237,7 @@ std::array<X86InstInfo, MAX_PRIMARY_TABLE_SIZE> BaseOps = []() consteval {
|
||||
};
|
||||
|
||||
GenerateTable(&Table.at(0), BaseOpTable, std::size(BaseOpTable));
|
||||
IR::InstallToTable(Table, IR::OpDispatch_BaseOpTable);
|
||||
|
||||
return Table;
|
||||
}();
|
||||
@@ -301,9 +303,11 @@ void InitializeBaseTables(Context::OperatingMode Mode) {
|
||||
|
||||
if (Mode == Context::MODE_64BIT) {
|
||||
GenerateTable(&BaseOps.at(0), BaseOpTable_64, std::size(BaseOpTable_64));
|
||||
IR::InstallToTable(BaseOps, IR::OpDispatch_BaseOpTable_64);
|
||||
}
|
||||
else {
|
||||
GenerateTable(&BaseOps.at(0), BaseOpTable_32, std::size(BaseOpTable_32));
|
||||
IR::InstallToTable(BaseOps, IR::OpDispatch_BaseOpTable_32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ $end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/X86Tables/X86Tables.h"
|
||||
#include "Interface/Core/OpcodeDispatcher/DDDTables.h"
|
||||
|
||||
#include <iterator>
|
||||
|
||||
@@ -54,6 +55,8 @@ std::array<X86InstInfo, MAX_3DNOW_TABLE_SIZE> DDDNowOps = []() consteval {
|
||||
};
|
||||
|
||||
GenerateTable(&Table.at(0), DDDNowOpTable, std::size(DDDNowOpTable));
|
||||
|
||||
IR::InstallToTable(Table, IR::OpDispatch_DDDTable);
|
||||
return Table;
|
||||
}();
|
||||
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
$info$
|
||||
tags: frontend|x86-tables
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/X86Tables/X86Tables.h"
|
||||
|
||||
#include <iterator>
|
||||
|
||||
namespace FEXCore::X86Tables {
|
||||
using namespace InstFlags;
|
||||
std::array<X86InstInfo, MAX_EVEX_TABLE_SIZE> EVEXTableOps = []() consteval {
|
||||
std::array<X86InstInfo, MAX_EVEX_TABLE_SIZE> Table{};
|
||||
constexpr U16U8InfoStruct EVEXTable[] = {
|
||||
{0x10, 1, X86InstInfo{"VMOVUPS", TYPE_INST, FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{0x11, 1, X86InstInfo{"VMOVUPS", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{0x18, 1, X86InstInfo{"VBROADCASTSS", TYPE_INST, FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{0x19, 1, X86InstInfo{"VBROADCASTD", TYPE_INST, FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{0x1A, 1, X86InstInfo{"VBROADCASTSD", TYPE_INST, FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{0x1B, 1, X86InstInfo{"VBROADCASTF64X4", TYPE_INST, FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{0x28, 1, X86InstInfo{"VMOVAPS", TYPE_INST, FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{0x29, 1, X86InstInfo{"VMOVAPS", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{0x59, 1, X86InstInfo{"VBROADCASTQ", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{0x6F, 1, X86InstInfo{"VMOVDQU64", TYPE_INST, FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{0x73, 1, X86InstInfo{"VPSLLDQ", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{0x7F, 1, X86InstInfo{"VMOVDQU64", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{0xE7, 1, X86InstInfo{"VMOVNTDQ", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
};
|
||||
|
||||
GenerateTable(&Table.at(0), EVEXTable, std::size(EVEXTable));
|
||||
|
||||
return Table;
|
||||
}();
|
||||
|
||||
}
|
||||
@@ -6,6 +6,7 @@ $end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/X86Tables/X86Tables.h"
|
||||
#include "Interface/Core/OpcodeDispatcher/H0F38Tables.h"
|
||||
|
||||
#include <iterator>
|
||||
#include <stdint.h>
|
||||
@@ -119,6 +120,8 @@ std::array<X86InstInfo, MAX_0F_38_TABLE_SIZE> H0F38TableOps = []() consteval {
|
||||
#undef OPD
|
||||
|
||||
GenerateTable(&Table.at(0), H0F38Table, std::size(H0F38Table));
|
||||
|
||||
IR::InstallToTable(Table, IR::OpDispatch_H0F38Table);
|
||||
return Table;
|
||||
}();
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ $end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/X86Tables/X86Tables.h"
|
||||
#include "Interface/Core/OpcodeDispatcher/H0F3ATables.h"
|
||||
|
||||
#include <FEXCore/Core/Context.h>
|
||||
|
||||
@@ -55,6 +56,8 @@ std::array<X86InstInfo, MAX_0F_3A_TABLE_SIZE> H0F3ATableOps = []() consteval {
|
||||
};
|
||||
|
||||
GenerateTable(&Table.at(0), H0F3ATable, std::size(H0F3ATable));
|
||||
|
||||
IR::InstallToTable(Table, IR::OpDispatch_H0F3ATable);
|
||||
return Table;
|
||||
}();
|
||||
|
||||
@@ -69,6 +72,7 @@ void InitializeH0F3ATables(Context::OperatingMode Mode) {
|
||||
|
||||
if (Mode == Context::MODE_64BIT) {
|
||||
GenerateTable(&H0F3ATableOps.at(0), H0F3ATable_64, std::size(H0F3ATable_64));
|
||||
IR::InstallToTable(H0F3ATableOps, IR::OpDispatch_H0F3ATable_64);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ $end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/X86Tables/X86Tables.h"
|
||||
#include "Interface/Core/OpcodeDispatcher/PrimaryGroupTables.h"
|
||||
|
||||
#include <FEXCore/Core/Context.h>
|
||||
|
||||
@@ -144,6 +145,8 @@ std::array<X86InstInfo, MAX_INST_GROUP_TABLE_SIZE> PrimaryInstGroupOps = []() co
|
||||
};
|
||||
|
||||
GenerateTable(&Table.at(0), PrimaryGroupOpTable, std::size(PrimaryGroupOpTable));
|
||||
|
||||
IR::InstallToTable(Table, IR::OpDispatch_PrimaryGroupTables);
|
||||
return Table;
|
||||
}();
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ $end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/X86Tables/X86Tables.h"
|
||||
#include "Interface/Core/OpcodeDispatcher/SecondaryGroupTables.h"
|
||||
|
||||
#include <iterator>
|
||||
#include <stdint.h>
|
||||
@@ -488,7 +489,15 @@ std::array<X86InstInfo, MAX_INST_SECOND_GROUP_TABLE_SIZE> SecondInstGroupOps = [
|
||||
#undef OPD
|
||||
|
||||
GenerateTable(&Table.at(0), SecondaryExtensionOpTable, std::size(SecondaryExtensionOpTable));
|
||||
|
||||
IR::InstallToTable(Table, IR::OpDispatch_SecondaryGroupTables);
|
||||
return Table;
|
||||
}();
|
||||
|
||||
void InitializeSecondaryGroupTables(Context::OperatingMode Mode) {
|
||||
if (Mode == Context::MODE_64BIT) {
|
||||
IR::InstallToTable(SecondInstGroupOps, IR::OpDispatch_SecondaryGroupTables_64);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,6 +6,7 @@ $end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/X86Tables/X86Tables.h"
|
||||
#include "Interface/Core/OpcodeDispatcher/SecondaryModRMTables.h"
|
||||
|
||||
#include <iterator>
|
||||
|
||||
@@ -56,6 +57,8 @@ std::array<X86InstInfo, MAX_SECOND_MODRM_TABLE_SIZE> SecondModRMTableOps = []()
|
||||
};
|
||||
|
||||
GenerateTable(&Table.at(0), SecondaryModRMExtensionOpTable, std::size(SecondaryModRMExtensionOpTable));
|
||||
|
||||
IR::InstallToTable(Table, IR::OpDispatch_SecondaryModRMTables);
|
||||
return Table;
|
||||
}();
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ $end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/X86Tables/X86Tables.h"
|
||||
#include "Interface/Core/OpcodeDispatcher/SecondaryTables.h"
|
||||
|
||||
#include <FEXCore/Core/Context.h>
|
||||
|
||||
@@ -270,6 +271,8 @@ auto BaseOpsLambda = []() consteval {
|
||||
|
||||
GenerateTable(&Table.at(0), TwoByteOpTable, std::size(TwoByteOpTable));
|
||||
|
||||
IR::InstallToTable(Table, IR::OpDispatch_TwoByteOpTable);
|
||||
|
||||
return Table;
|
||||
};
|
||||
|
||||
@@ -297,7 +300,7 @@ std::array<X86InstInfo, MAX_REP_MOD_TABLE_SIZE> RepModOps = []() consteval {
|
||||
{0x2E, 2, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
|
||||
|
||||
{0x30, 16, X86InstInfo{"", TYPE_COPY_OTHER, FLAGS_NONE, 0, nullptr}},
|
||||
{0x40, 16, X86InstInfo{"", TYPE_COPY_OTHER, FLAGS_NONE, 0, nullptr}},
|
||||
{0x40, 16, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
|
||||
|
||||
{0x50, 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
|
||||
{0x51, 1, X86InstInfo{"SQRTSS", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_32BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
@@ -359,6 +362,7 @@ std::array<X86InstInfo, MAX_REP_MOD_TABLE_SIZE> RepModOps = []() consteval {
|
||||
|
||||
GenerateTableWithCopy(&Table.at(0), RepModOpTable, std::size(RepModOpTable), &BaseOpsLambda().at(0));
|
||||
|
||||
IR::InstallToTable(Table, IR::OpDispatch_SecondaryRepModTables);
|
||||
return Table;
|
||||
}();
|
||||
|
||||
@@ -383,7 +387,7 @@ std::array<X86InstInfo, MAX_REPNE_MOD_TABLE_SIZE> RepNEModOps = []() consteval {
|
||||
{0x2E, 2, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
|
||||
|
||||
{0x30, 16, X86InstInfo{"", TYPE_COPY_OTHER, FLAGS_NONE, 0, nullptr}},
|
||||
{0x40, 16, X86InstInfo{"", TYPE_COPY_OTHER, FLAGS_NONE, 0, nullptr}},
|
||||
{0x40, 16, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
|
||||
|
||||
{0x50, 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
|
||||
{0x51, 1, X86InstInfo{"SQRTSD", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
@@ -440,6 +444,7 @@ std::array<X86InstInfo, MAX_REPNE_MOD_TABLE_SIZE> RepNEModOps = []() consteval {
|
||||
|
||||
GenerateTableWithCopy(&Table.at(0), RepNEModOpTable, std::size(RepNEModOpTable), &BaseOpsLambda().at(0));
|
||||
|
||||
IR::InstallToTable(Table, IR::OpDispatch_SecondaryRepNEModTables);
|
||||
return Table;
|
||||
}();
|
||||
|
||||
@@ -595,6 +600,7 @@ std::array<X86InstInfo, MAX_OPSIZE_MOD_TABLE_SIZE> OpSizeModOps = []() consteval
|
||||
|
||||
GenerateTableWithCopy(&Table.at(0), OpSizeModOpTable, std::size(OpSizeModOpTable), &BaseOpsLambda().at(0));
|
||||
|
||||
IR::InstallToTable(Table, IR::OpDispatch_SecondaryOpSizeModTables);
|
||||
return Table;
|
||||
}();
|
||||
|
||||
@@ -620,12 +626,16 @@ void InitializeSecondaryTables(Context::OperatingMode Mode) {
|
||||
LateInitCopyTable(&RepModOps.at(0), TwoByteOpTable_64, std::size(TwoByteOpTable_64));
|
||||
LateInitCopyTable(&RepNEModOps.at(0), TwoByteOpTable_64, std::size(TwoByteOpTable_64));
|
||||
LateInitCopyTable(&OpSizeModOps.at(0), TwoByteOpTable_64, std::size(TwoByteOpTable_64));
|
||||
|
||||
IR::InstallToTable(SecondBaseOps, IR::OpDispatch_TwoByteOpTable_64);
|
||||
}
|
||||
else {
|
||||
LateInitCopyTable(&SecondBaseOps.at(0), TwoByteOpTable_32, std::size(TwoByteOpTable_32));
|
||||
LateInitCopyTable(&RepModOps.at(0), TwoByteOpTable_32, std::size(TwoByteOpTable_32));
|
||||
LateInitCopyTable(&RepNEModOps.at(0), TwoByteOpTable_32, std::size(TwoByteOpTable_32));
|
||||
LateInitCopyTable(&OpSizeModOps.at(0), TwoByteOpTable_32, std::size(TwoByteOpTable_32));
|
||||
|
||||
IR::InstallToTable(SecondBaseOps, IR::OpDispatch_TwoByteOpTable_32);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ $end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/X86Tables/X86Tables.h"
|
||||
#include "Interface/Core/OpcodeDispatcher/VEXTables.h"
|
||||
|
||||
#include <iterator>
|
||||
|
||||
@@ -489,6 +490,8 @@ std::array<X86InstInfo, MAX_VEX_TABLE_SIZE> VEXTableOps = []() consteval {
|
||||
#undef OPD
|
||||
|
||||
GenerateTable(&Table.at(0), VEXTable, std::size(VEXTable));
|
||||
|
||||
IR::InstallToTable(Table, IR::OpDispatch_VEXTable);
|
||||
return Table;
|
||||
}();
|
||||
|
||||
@@ -521,6 +524,7 @@ std::array<X86InstInfo, MAX_VEX_GROUP_TABLE_SIZE> VEXTableGroupOps = []() conste
|
||||
|
||||
GenerateTable(&Table.at(0), VEXGroupTable, std::size(VEXGroupTable));
|
||||
|
||||
IR::InstallToTable(Table, IR::OpDispatch_VEXGroupTable);
|
||||
return Table;
|
||||
}();
|
||||
}
|
||||
@@ -474,8 +474,6 @@ constexpr size_t MAX_XOP_TABLE_SIZE = (1 << 13);
|
||||
// group select (2 bits for now) | modrm opcode (3 bits)
|
||||
constexpr size_t MAX_XOP_GROUP_TABLE_SIZE = (1 << 6);
|
||||
|
||||
constexpr size_t MAX_EVEX_TABLE_SIZE = 256;
|
||||
|
||||
extern std::array<X86InstInfo, MAX_PRIMARY_TABLE_SIZE> BaseOps;
|
||||
extern std::array<X86InstInfo, MAX_SECOND_TABLE_SIZE> SecondBaseOps;
|
||||
extern std::array<X86InstInfo, MAX_REP_MOD_TABLE_SIZE> RepModOps;
|
||||
@@ -498,9 +496,6 @@ extern std::array<X86InstInfo, MAX_VEX_GROUP_TABLE_SIZE> VEXTableGroupOps;
|
||||
extern std::array<X86InstInfo, MAX_XOP_TABLE_SIZE> XOPTableOps;
|
||||
extern std::array<X86InstInfo, MAX_XOP_GROUP_TABLE_SIZE> XOPTableGroupOps;
|
||||
|
||||
// EVEX
|
||||
extern std::array<X86InstInfo, MAX_EVEX_TABLE_SIZE> EVEXTableOps;
|
||||
|
||||
template <typename OpcodeType>
|
||||
struct X86TablesInfoStruct {
|
||||
OpcodeType first;
|
||||
@@ -518,7 +513,10 @@ constexpr static inline void GenerateTable(X86InstInfo *FinalTable, X86TablesInf
|
||||
X86InstInfo const &Info = Op.Info;
|
||||
for (uint32_t i = 0; i < Op.second; ++i) {
|
||||
if (FinalTable[OpNum + i].Type != TYPE_UNKNOWN) {
|
||||
ERROR_AND_DIE_FMT("Duplicate Entry {}->{}", FinalTable[OpNum + i].Name, Info.Name);
|
||||
LOGMAN_MSG_A_FMT("Duplicate Entry {}->{}", FinalTable[OpNum + i].Name, Info.Name);
|
||||
}
|
||||
if (FinalTable[OpNum + i].OpcodeDispatcher) {
|
||||
LOGMAN_MSG_A_FMT("Already installed an OpcodeDispatcher for 0x{:x}", OpNum + i);
|
||||
}
|
||||
FinalTable[OpNum + i] = Info;
|
||||
}
|
||||
@@ -533,7 +531,7 @@ constexpr static inline void GenerateTableWithCopy(X86InstInfo *FinalTable, X86T
|
||||
X86InstInfo const &Info = Op.Info;
|
||||
for (uint32_t i = 0; i < Op.second; ++i) {
|
||||
if (FinalTable[OpNum + i].Type != TYPE_UNKNOWN) {
|
||||
ERROR_AND_DIE_FMT("Duplicate Entry {}->{}", FinalTable[OpNum + i].Name, Info.Name);
|
||||
LOGMAN_MSG_A_FMT("Duplicate Entry {}->{}", FinalTable[OpNum + i].Name, Info.Name);
|
||||
}
|
||||
if (Info.Type == TYPE_COPY_OTHER) {
|
||||
FinalTable[OpNum + i] = OtherLocal[OpNum + i];
|
||||
@@ -568,7 +566,7 @@ constexpr static inline void GenerateX87Table(X86InstInfo *FinalTable, X86Tables
|
||||
X86InstInfo const &Info = Op.Info;
|
||||
for (uint32_t i = 0; i < Op.second; ++i) {
|
||||
if (FinalTable[OpNum + i].Type != TYPE_UNKNOWN) {
|
||||
ERROR_AND_DIE_FMT("Duplicate Entry {}->{}", FinalTable[OpNum + i].Name, Info.Name);
|
||||
LOGMAN_MSG_A_FMT("Duplicate Entry {}->{}", FinalTable[OpNum + i].Name, Info.Name);
|
||||
}
|
||||
if ((OpNum & 0b11'000'000) == 0b11'000'000) {
|
||||
// If the mod field is 0b11 then it is a regular op
|
||||
|
||||
@@ -17,9 +17,34 @@
|
||||
#include <shared_mutex>
|
||||
#include <FEXCore/HLE/SourcecodeResolver.h>
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
union Relocation;
|
||||
} // namespace FEXCore::CPU
|
||||
|
||||
namespace FEXCore::Core {
|
||||
struct DebugData;
|
||||
}
|
||||
struct DebugDataSubblock {
|
||||
uint32_t HostCodeOffset;
|
||||
uint32_t HostCodeSize;
|
||||
};
|
||||
|
||||
struct DebugDataGuestOpcode {
|
||||
uint64_t GuestEntryOffset;
|
||||
ptrdiff_t HostEntryOffset;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Contains debug data for a block of code for later debugger analysis
|
||||
*
|
||||
* Needs to remain around for as long as the code could be executed at least
|
||||
*/
|
||||
struct DebugData : public FEXCore::Allocator::FEXAllocOperators {
|
||||
uint64_t HostCodeSize; ///< The size of the code generated in the host JIT
|
||||
fextl::vector<DebugDataSubblock> Subblocks;
|
||||
fextl::vector<DebugDataGuestOpcode> GuestOpcodes;
|
||||
fextl::vector<FEXCore::CPU::Relocation>* Relocations;
|
||||
};
|
||||
} // namespace FEXCore::Core
|
||||
|
||||
namespace FEXCore::Context {
|
||||
class ContextImpl;
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@
|
||||
"SwitchGen": false,
|
||||
"JITDispatchOverride": "NoOp"
|
||||
},
|
||||
"IRHeader SSA:$Blocks, u64:$OriginalRIP, u32:$BlockCount, u32:$NumHostInstructions, i1:$HasX87{false}": {
|
||||
"IRHeader SSA:$Blocks, u64:$OriginalRIP, u32:$BlockCount, u32:$NumHostInstructions, i1:$HasX87{false}, i1:$ReadsParity{false}": {
|
||||
"SwitchGen": false,
|
||||
"JITDispatchOverride": "NoOp"
|
||||
},
|
||||
@@ -366,14 +366,36 @@
|
||||
"DestSize": "Size"
|
||||
},
|
||||
|
||||
"GPR = LoadPF u8:#Size": {
|
||||
"Desc": ["Loads raw PF"],
|
||||
"DestSize": "Size"
|
||||
},
|
||||
|
||||
"GPR = LoadAF u8:#Size": {
|
||||
"Desc": ["Loads raw PF"],
|
||||
"DestSize": "Size"
|
||||
},
|
||||
|
||||
"StoreRegister SSA:$Value, u32:$Reg, RegisterClass:$Class, u8:#Size": {
|
||||
"HasSideEffects": true,
|
||||
"HasSideEffects": true,
|
||||
"Desc": ["Stores a value to a given register.",
|
||||
"Size must match the execution mode."],
|
||||
"DestSize": "Size",
|
||||
"EmitValidation": [
|
||||
"WalkFindRegClass($Value) == $Class"
|
||||
]
|
||||
},
|
||||
|
||||
"StorePF GPR:$Value, u8:#Size": {
|
||||
"HasSideEffects": true,
|
||||
"Desc": ["Stores raw PF"],
|
||||
"DestSize": "Size"
|
||||
},
|
||||
|
||||
"StoreAF GPR:$Value, u8:#Size": {
|
||||
"HasSideEffects": true,
|
||||
"Desc": ["Stores raw AF"],
|
||||
"DestSize": "Size"
|
||||
}
|
||||
},
|
||||
"Memory": {
|
||||
@@ -1139,10 +1161,15 @@
|
||||
"Desc": ["Invert carry flag in NZCV"],
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"AXFlag": {
|
||||
"Desc": ["After an FCmp, converts NZCV flags from the Arm format to a mysterious eXternal format"],
|
||||
"AXFlag GPR:$V_inv": {
|
||||
"Desc": ["After an FCmp, converts NZCV flags from the Arm format to a mysterious eXternal format",
|
||||
"On FlagM2-less platforms, takes the inverted 1/0 overflow flag"],
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"GPR = Parity GPR:$Raw, i1:$Mask, i1:$Invert": {
|
||||
"Desc": ["Calculates PF"],
|
||||
"DestSize": "4"
|
||||
},
|
||||
"RmifNZCV GPR:$Src, u8:$Rotate, u8:$Mask": {
|
||||
"Desc": ["Rotate, mask, and insert into NZCV on FlagM platforms"],
|
||||
"HasSideEffects": true
|
||||
@@ -1331,6 +1358,11 @@
|
||||
"DestSize": "Size",
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"TestZ OpSize:#Size, GPR:$Src1, GPR:$Src2": {
|
||||
"Desc": ["Set NZCV for the binary AND of two GPRs, setting Z accordingly and zeroing C and V. N is undefined."],
|
||||
"DestSize": "Size",
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"GPR = Lshl OpSize:#Size, GPR:$Src1, GPR:$Src2": {
|
||||
"Desc": ["Integer logical shift left"
|
||||
],
|
||||
|
||||
@@ -71,7 +71,6 @@ void PassManager::AddDefaultPasses(FEXCore::Context::ContextImpl* ctx) {
|
||||
|
||||
if (!DisablePasses()) {
|
||||
InsertPass(CreateX87StackOptimizationPass());
|
||||
InsertPass(CreateDeadStoreElimination());
|
||||
InsertPass(CreateConstProp(ctx->HostFeatures.SupportsTSOImm9, &ctx->CPUID));
|
||||
InsertPass(CreateDeadFlagCalculationEliminination());
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ class RegisterAllocationData;
|
||||
|
||||
fextl::unique_ptr<FEXCore::IR::Pass> CreateConstProp(bool SupportsTSOImm9, const FEXCore::CPUIDEmu* CPUID);
|
||||
fextl::unique_ptr<FEXCore::IR::Pass> CreateDeadFlagCalculationEliminination();
|
||||
fextl::unique_ptr<FEXCore::IR::Pass> CreateDeadStoreElimination();
|
||||
fextl::unique_ptr<FEXCore::IR::RegisterAllocationPass> CreateRegisterAllocationPass();
|
||||
fextl::unique_ptr<FEXCore::IR::Pass> CreateX87StackOptimizationPass();
|
||||
|
||||
|
||||
@@ -52,17 +52,6 @@ static bool IsImmLogical(uint64_t imm, unsigned width) {
|
||||
return ARMEmitter::Emitter::IsImmLogical(imm, width);
|
||||
}
|
||||
|
||||
static bool IsBfeAlreadyDone(IREmitter* IREmit, OrderedNodeWrapper src, uint64_t Width) {
|
||||
auto IROp = IREmit->GetOpHeader(src);
|
||||
if (IROp->Op == OP_BFE) {
|
||||
auto Op = IROp->C<IR::IROp_Bfe>();
|
||||
if (Width >= Op->Width) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
class ConstProp final : public FEXCore::IR::Pass {
|
||||
public:
|
||||
explicit ConstProp(bool SupportsTSOImm9, const FEXCore::CPUIDEmu* CPUID)
|
||||
@@ -74,13 +63,40 @@ public:
|
||||
private:
|
||||
void HandleConstantPools(IREmitter* IREmit, const IRListView& CurrentIR);
|
||||
void ConstantPropagation(IREmitter* IREmit, const IRListView& CurrentIR, Ref CodeNode, IROp_Header* IROp);
|
||||
void ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR);
|
||||
|
||||
fextl::unordered_map<uint64_t, Ref> ConstPool;
|
||||
|
||||
bool SupportsTSOImm9 {};
|
||||
const FEXCore::CPUIDEmu* CPUID;
|
||||
|
||||
template<class F>
|
||||
bool InlineIf(IREmitter* IREmit, const IRListView& CurrentIR, Ref CodeNode, IROp_Header* IROp, unsigned Index, F Filter) {
|
||||
uint64_t Constant;
|
||||
if (!IREmit->IsValueConstant(IROp->Args[Index], &Constant) || !Filter(Constant)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[Index]));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, Index, IREmit->_InlineConstant(Constant));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Inline(IREmitter* IREmit, const IRListView& CurrentIR, Ref CodeNode, IROp_Header* IROp, unsigned Index) {
|
||||
return InlineIf(IREmit, CurrentIR, CodeNode, IROp, Index, [](uint64_t _) { return true; });
|
||||
}
|
||||
|
||||
bool InlineIfZero(IREmitter* IREmit, const IRListView& CurrentIR, Ref CodeNode, IROp_Header* IROp, unsigned Index) {
|
||||
return InlineIf(IREmit, CurrentIR, CodeNode, IROp, Index, [](uint64_t X) { return X == 0; });
|
||||
}
|
||||
|
||||
bool InlineIfLargeAddSub(IREmitter* IREmit, const IRListView& CurrentIR, Ref CodeNode, IROp_Header* IROp, unsigned Index) {
|
||||
// We don't allow 8/16-bit operations to have constants, since no
|
||||
// constant would be in bounds after the JIT's 24/16 shift.
|
||||
auto Filter = [&IROp](uint64_t X) {
|
||||
return ARMEmitter::IsImmAddSub(X) && IROp->Size >= 4;
|
||||
};
|
||||
|
||||
return InlineIf(IREmit, CurrentIR, CodeNode, IROp, Index, Filter);
|
||||
}
|
||||
|
||||
void InlineMemImmediate(IREmitter* IREmit, const IRListView& IR, Ref CodeNode, IROp_Header* IROp, OrderedNodeWrapper Offset,
|
||||
MemOffsetType OffsetType, const size_t Offset_Index, uint8_t& OffsetScale, bool TSO) {
|
||||
uint64_t Imm {};
|
||||
@@ -109,21 +125,66 @@ private:
|
||||
|
||||
// Constants are pooled per block.
|
||||
void ConstProp::HandleConstantPools(IREmitter* IREmit, const IRListView& CurrentIR) {
|
||||
const uint32_t SSACount = CurrentIR.GetSSACount();
|
||||
|
||||
// Allocation/initialization deferred until first use, since many multiblocks
|
||||
// don't have constants leftover after all inlining.
|
||||
fextl::vector<Ref> Remap {};
|
||||
|
||||
struct Entry {
|
||||
int64_t Value;
|
||||
Ref R;
|
||||
};
|
||||
|
||||
|
||||
fextl::vector<Entry> Pool {};
|
||||
|
||||
for (auto [BlockNode, BlockIROp] : CurrentIR.GetBlocks()) {
|
||||
Pool.clear();
|
||||
|
||||
for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) {
|
||||
if (IROp->Op == OP_CONSTANT) {
|
||||
auto Op = IROp->C<IR::IROp_Constant>();
|
||||
auto it = ConstPool.find(Op->Constant);
|
||||
bool Found = false;
|
||||
|
||||
if (it != ConstPool.end()) {
|
||||
auto CodeIter = CurrentIR.at(CodeNode);
|
||||
IREmit->ReplaceUsesWithAfter(CodeNode, it->second, CodeIter);
|
||||
} else {
|
||||
ConstPool[Op->Constant] = CodeNode;
|
||||
// Search for the constant. This is O(n^2) but n is small since it's
|
||||
// local and most constants are inlined. In practice, it ends up much
|
||||
// faster than a hash table.
|
||||
for (auto K : Pool) {
|
||||
if (K.Value == Op->Constant) {
|
||||
uint32_t Value = CurrentIR.GetID(CodeNode).Value;
|
||||
LOGMAN_THROW_A_FMT(Value < SSACount, "def not yet remapped");
|
||||
|
||||
if (Remap.empty()) {
|
||||
Remap.resize(SSACount, nullptr);
|
||||
}
|
||||
|
||||
Remap[Value] = K.R;
|
||||
Found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Found) {
|
||||
Pool.push_back({.Value = Op->Constant, .R = CodeNode});
|
||||
}
|
||||
} else if (!Remap.empty()) {
|
||||
const uint8_t NumArgs = IR::GetArgs(IROp->Op);
|
||||
for (uint8_t i = 0; i < NumArgs; ++i) {
|
||||
if (IROp->Args[i].IsInvalid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t Value = IROp->Args[i].ID().Value;
|
||||
LOGMAN_THROW_A_FMT(Value < SSACount, "src not yet remapped");
|
||||
|
||||
Ref New = Remap[Value];
|
||||
if (New) {
|
||||
IREmit->ReplaceNodeArgument(CodeNode, i, New);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ConstPool.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,10 +212,14 @@ void ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
|
||||
if (IsConstant1 && IsConstant2 && IROp->Op == OP_ADD) {
|
||||
uint64_t NewConstant = (Constant1 + Constant2) & getMask(IROp);
|
||||
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
|
||||
break;
|
||||
} else if (IsConstant1 && IsConstant2 && IROp->Op == OP_SUB) {
|
||||
uint64_t NewConstant = (Constant1 - Constant2) & getMask(IROp);
|
||||
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
|
||||
} else if (IsConstant2 && !ARMEmitter::IsImmAddSub(Constant2) && ARMEmitter::IsImmAddSub(-Constant2)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (IsConstant2 && !ARMEmitter::IsImmAddSub(Constant2) && ARMEmitter::IsImmAddSub(-Constant2)) {
|
||||
// If the second argument is constant, the immediate is not ImmAddSub, but when negated is.
|
||||
// So, negate the operation to negate (and inline) the constant.
|
||||
if (IROp->Op == OP_ADD) {
|
||||
@@ -175,6 +240,23 @@ void ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
|
||||
// Replace the second source with the negated constant.
|
||||
IREmit->ReplaceNodeArgument(CodeNode, Op->Src2_Index, NegConstant);
|
||||
}
|
||||
|
||||
if (!InlineIfLargeAddSub(IREmit, CurrentIR, CodeNode, IROp, 1) && (IROp->Op == OP_SUB || IROp->Op == OP_SUBWITHFLAGS)) {
|
||||
// TODO: Generalize this
|
||||
InlineIfZero(IREmit, CurrentIR, CodeNode, IROp, 0);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case OP_ADDNZCV: {
|
||||
InlineIfLargeAddSub(IREmit, CurrentIR, CodeNode, IROp, 1);
|
||||
break;
|
||||
}
|
||||
case OP_SUBNZCV: {
|
||||
if (!InlineIfLargeAddSub(IREmit, CurrentIR, CodeNode, IROp, 1)) {
|
||||
// TODO: Generalize this
|
||||
InlineIfZero(IREmit, CurrentIR, CodeNode, IROp, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_SUBSHIFT: {
|
||||
@@ -194,51 +276,38 @@ void ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
|
||||
uint64_t Constant1 {};
|
||||
uint64_t Constant2 {};
|
||||
|
||||
bool Replaced = false;
|
||||
|
||||
// Order matter for short circuit evaluation, subsequent ifs read constant2.
|
||||
if (IREmit->IsValueConstant(IROp->Args[1], &Constant2) && IREmit->IsValueConstant(IROp->Args[0], &Constant1)) {
|
||||
uint64_t NewConstant = (Constant1 & Constant2) & getMask(IROp);
|
||||
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
|
||||
} else if (Constant2 == 1) {
|
||||
// happens from flag calcs
|
||||
auto val = IREmit->GetOpHeader(IROp->Args[0]);
|
||||
|
||||
uint64_t Constant3;
|
||||
if (val->Op == OP_SELECT && IREmit->IsValueConstant(val->Args[2], &Constant2) && IREmit->IsValueConstant(val->Args[3], &Constant3) &&
|
||||
Constant2 == 1 && Constant3 == 0) {
|
||||
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(IROp->Args[0]));
|
||||
}
|
||||
Replaced = true;
|
||||
} else if (IROp->Args[0].ID() == IROp->Args[1].ID() || (Constant2 & getMask(IROp)) == getMask(IROp)) {
|
||||
// AND with same value results in original value
|
||||
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(IROp->Args[0]));
|
||||
Replaced = true;
|
||||
}
|
||||
|
||||
if (!Replaced) {
|
||||
InlineIf(IREmit, CurrentIR, CodeNode, IROp, 1, [&IROp](uint64_t X) { return IsImmLogical(X, IROp->Size * 8); });
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_OR: {
|
||||
uint64_t Constant1 {};
|
||||
uint64_t Constant2 {};
|
||||
|
||||
if (IREmit->IsValueConstant(IROp->Args[0], &Constant1) && IREmit->IsValueConstant(IROp->Args[1], &Constant2)) {
|
||||
uint64_t NewConstant = Constant1 | Constant2;
|
||||
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
|
||||
} else if (IROp->Args[0].ID() == IROp->Args[1].ID()) {
|
||||
// OR with same value results in original value
|
||||
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(IROp->Args[0]));
|
||||
}
|
||||
InlineIf(IREmit, CurrentIR, CodeNode, IROp, 1, [&IROp](uint64_t X) { return IsImmLogical(X, IROp->Size * 8); });
|
||||
break;
|
||||
}
|
||||
case OP_XOR: {
|
||||
uint64_t Constant1 {};
|
||||
uint64_t Constant2 {};
|
||||
|
||||
if (IREmit->IsValueConstant(IROp->Args[0], &Constant1) && IREmit->IsValueConstant(IROp->Args[1], &Constant2)) {
|
||||
uint64_t NewConstant = Constant1 ^ Constant2;
|
||||
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
|
||||
} else if (IROp->Args[0].ID() == IROp->Args[1].ID()) {
|
||||
if (IROp->Args[0].ID() == IROp->Args[1].ID()) {
|
||||
// XOR with same value results to zero
|
||||
IREmit->SetWriteCursor(CodeNode);
|
||||
IREmit->ReplaceAllUsesWith(CodeNode, IREmit->_Constant(0));
|
||||
} else {
|
||||
// XOR with zero results in the nonzero source
|
||||
bool Replaced = false;
|
||||
for (unsigned i = 0; i < 2; ++i) {
|
||||
if (!IREmit->IsValueConstant(IROp->Args[i], &Constant1)) {
|
||||
continue;
|
||||
@@ -251,11 +320,22 @@ void ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
|
||||
IREmit->SetWriteCursor(CodeNode);
|
||||
Ref Arg = CurrentIR.GetNode(IROp->Args[1 - i]);
|
||||
IREmit->ReplaceAllUsesWith(CodeNode, Arg);
|
||||
Replaced = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!Replaced) {
|
||||
InlineIf(IREmit, CurrentIR, CodeNode, IROp, 1, [&IROp](uint64_t X) { return IsImmLogical(X, IROp->Size * 8); });
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_ANDWITHFLAGS:
|
||||
case OP_ANDN:
|
||||
case OP_TESTNZ: {
|
||||
InlineIf(IREmit, CurrentIR, CodeNode, IROp, 1, [&IROp](uint64_t X) { return IsImmLogical(X, IROp->Size * 8); });
|
||||
break;
|
||||
}
|
||||
case OP_NEG: {
|
||||
uint64_t Constant {};
|
||||
|
||||
@@ -265,6 +345,11 @@ void ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_ASHR:
|
||||
case OP_ROR: {
|
||||
Inline(IREmit, CurrentIR, CodeNode, IROp, 1);
|
||||
break;
|
||||
}
|
||||
case OP_LSHL: {
|
||||
uint64_t Constant1 {};
|
||||
uint64_t Constant2 {};
|
||||
@@ -278,26 +363,20 @@ void ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
|
||||
IREmit->SetWriteCursor(CodeNode);
|
||||
Ref Arg = CurrentIR.GetNode(IROp->Args[0]);
|
||||
IREmit->ReplaceAllUsesWith(CodeNode, Arg);
|
||||
} else {
|
||||
Inline(IREmit, CurrentIR, CodeNode, IROp, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_LSHR: {
|
||||
uint64_t Constant1 {};
|
||||
uint64_t Constant2 {};
|
||||
|
||||
if (IREmit->IsValueConstant(IROp->Args[0], &Constant1) && IREmit->IsValueConstant(IROp->Args[1], &Constant2)) {
|
||||
// Shifts mask the shift amount by 63 or 31 depending on operating size;
|
||||
// The source is masked, which will produce a correctly masked
|
||||
// destination. Masking the destination without the source instead will
|
||||
// right-shift garbage into the upper bits instead of zeroes.
|
||||
Constant1 &= getMask(IROp);
|
||||
Constant2 &= (IROp->Size == 8 ? 63 : 31);
|
||||
uint64_t NewConstant = (Constant1 >> Constant2);
|
||||
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
|
||||
} else if (IREmit->IsValueConstant(IROp->Args[1], &Constant2) && Constant2 == 0) {
|
||||
if (IREmit->IsValueConstant(IROp->Args[1], &Constant2) && Constant2 == 0) {
|
||||
IREmit->SetWriteCursor(CodeNode);
|
||||
Ref Arg = CurrentIR.GetNode(IROp->Args[0]);
|
||||
IREmit->ReplaceAllUsesWith(CodeNode, Arg);
|
||||
} else {
|
||||
Inline(IREmit, CurrentIR, CodeNode, IROp, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -305,46 +384,12 @@ void ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
|
||||
auto Op = IROp->C<IR::IROp_Bfe>();
|
||||
uint64_t Constant;
|
||||
|
||||
// Is this value already BFE'd?
|
||||
if (IsBfeAlreadyDone(IREmit, Op->Src, Op->Width)) {
|
||||
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(Op->Src));
|
||||
break;
|
||||
}
|
||||
|
||||
// Is this value already ZEXT'd?
|
||||
if (Op->lsb == 0) {
|
||||
// LoadMem, LoadMemTSO & LoadContext ZExt
|
||||
auto source = Op->Src;
|
||||
auto sourceHeader = IREmit->GetOpHeader(source);
|
||||
|
||||
if (Op->Width >= (sourceHeader->Size * 8) &&
|
||||
(sourceHeader->Op == OP_LOADMEM || sourceHeader->Op == OP_LOADMEMTSO || sourceHeader->Op == OP_LOADCONTEXT)) {
|
||||
// Load mem / load ctx zexts, no need to vmem
|
||||
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(source));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (IROp->Size <= 8 && IREmit->IsValueConstant(Op->Src, &Constant)) {
|
||||
uint64_t SourceMask = Op->Width == 64 ? ~0ULL : ((1ULL << Op->Width) - 1);
|
||||
SourceMask <<= Op->lsb;
|
||||
|
||||
uint64_t NewConstant = (Constant & SourceMask) >> Op->lsb;
|
||||
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
|
||||
} else if (IROp->Size == CurrentIR.GetOp<IROp_Header>(IROp->Args[0])->Size && Op->Width == (IROp->Size * 8) && Op->lsb == 0) {
|
||||
// A BFE that extracts all bits results in original value
|
||||
// XXX - This is broken for now - see https://github.com/FEX-Emu/FEX/issues/351
|
||||
// IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(IROp->Args[0]));
|
||||
} else if (Op->Width == 1 && Op->lsb == 0) {
|
||||
// common from flag codegen
|
||||
auto val = IREmit->GetOpHeader(IROp->Args[0]);
|
||||
|
||||
uint64_t Constant2 {};
|
||||
uint64_t Constant3 {};
|
||||
if (val->Op == OP_SELECT && IREmit->IsValueConstant(val->Args[2], &Constant2) && IREmit->IsValueConstant(val->Args[3], &Constant3) &&
|
||||
Constant2 == 1 && Constant3 == 0) {
|
||||
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(IROp->Args[0]));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -369,18 +414,10 @@ void ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
|
||||
}
|
||||
case OP_BFI: {
|
||||
auto Op = IROp->C<IR::IROp_Bfi>();
|
||||
uint64_t ConstantDest {};
|
||||
uint64_t ConstantSrc {};
|
||||
bool DestIsConstant = IREmit->IsValueConstant(IROp->Args[0], &ConstantDest);
|
||||
bool SrcIsConstant = IREmit->IsValueConstant(IROp->Args[1], &ConstantSrc);
|
||||
|
||||
if (DestIsConstant && SrcIsConstant) {
|
||||
uint64_t SourceMask = Op->Width == 64 ? ~0ULL : ((1ULL << Op->Width) - 1);
|
||||
uint64_t NewConstant = ConstantDest & ~(SourceMask << Op->lsb);
|
||||
NewConstant |= (ConstantSrc & SourceMask) << Op->lsb;
|
||||
|
||||
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
|
||||
} else if (SrcIsConstant && HasConsecutiveBits(ConstantSrc, Op->Width)) {
|
||||
if (SrcIsConstant && HasConsecutiveBits(ConstantSrc, Op->Width)) {
|
||||
// We are trying to insert constant, if it is a bitfield of only set bits then we can orr or and it.
|
||||
IREmit->SetWriteCursor(CodeNode);
|
||||
uint64_t SourceMask = Op->Width == 64 ? ~0ULL : ((1ULL << Op->Width) - 1);
|
||||
@@ -397,24 +434,6 @@ void ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_MUL: {
|
||||
uint64_t Constant1 {};
|
||||
uint64_t Constant2 {};
|
||||
|
||||
if (IREmit->IsValueConstant(IROp->Args[0], &Constant1) && IREmit->IsValueConstant(IROp->Args[1], &Constant2)) {
|
||||
uint64_t NewConstant = (Constant1 * Constant2) & getMask(IROp);
|
||||
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
|
||||
} else if (IREmit->IsValueConstant(IROp->Args[1], &Constant2) && std::popcount(Constant2) == 1) {
|
||||
if (IROp->Size == 4 || IROp->Size == 8) {
|
||||
uint64_t amt = std::countr_zero(Constant2);
|
||||
IREmit->SetWriteCursor(CodeNode);
|
||||
auto shift = IREmit->_Lshl(IR::SizeToOpSize(IROp->Size), CurrentIR.GetNode(IROp->Args[0]), IREmit->_Constant(amt));
|
||||
IREmit->ReplaceAllUsesWith(CodeNode, shift);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_VMOV: {
|
||||
// elim from load mem
|
||||
auto source = IROp->Args[0];
|
||||
@@ -561,244 +580,101 @@ void ConstProp::ConstantPropagation(IREmitter* IREmit, const IRListView& Current
|
||||
break;
|
||||
}
|
||||
|
||||
default: break;
|
||||
case OP_ADC:
|
||||
case OP_ADCWITHFLAGS:
|
||||
case OP_STORECONTEXT:
|
||||
case OP_RMIFNZCV: {
|
||||
InlineIfZero(IREmit, CurrentIR, CodeNode, IROp, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
case OP_CONDADDNZCV:
|
||||
case OP_CONDSUBNZCV: {
|
||||
InlineIfZero(IREmit, CurrentIR, CodeNode, IROp, 0);
|
||||
InlineIf(IREmit, CurrentIR, CodeNode, IROp, 1, ARMEmitter::IsImmAddSub);
|
||||
break;
|
||||
}
|
||||
case OP_SELECT: {
|
||||
InlineIf(IREmit, CurrentIR, CodeNode, IROp, 1, ARMEmitter::IsImmAddSub);
|
||||
|
||||
void ConstProp::ConstantInlining(IREmitter* IREmit, const IRListView& CurrentIR) {
|
||||
for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) {
|
||||
switch (IROp->Op) {
|
||||
case OP_LSHR:
|
||||
case OP_ASHR:
|
||||
case OP_ROR:
|
||||
case OP_LSHL: {
|
||||
uint64_t Constant2 {};
|
||||
if (IREmit->IsValueConstant(IROp->Args[1], &Constant2)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[1]));
|
||||
uint64_t AllOnes = IROp->Size == 8 ? 0xffff'ffff'ffff'ffffull : 0xffff'ffffull;
|
||||
|
||||
// this shouldn't be here, but rather on the emitter themselves or the constprop transformation?
|
||||
if (IROp->Size <= 4) {
|
||||
Constant2 &= 31;
|
||||
} else {
|
||||
Constant2 &= 63;
|
||||
}
|
||||
uint64_t Constant2 {};
|
||||
uint64_t Constant3 {};
|
||||
if (IREmit->IsValueConstant(IROp->Args[2], &Constant2) && IREmit->IsValueConstant(IROp->Args[3], &Constant3) &&
|
||||
(Constant2 == 1 || Constant2 == AllOnes) && Constant3 == 0) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[2]));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
|
||||
}
|
||||
break;
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 2, IREmit->_InlineConstant(Constant2));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 3, IREmit->_InlineConstant(Constant3));
|
||||
}
|
||||
case OP_ADD:
|
||||
case OP_SUB:
|
||||
case OP_ADDNZCV:
|
||||
case OP_SUBNZCV:
|
||||
case OP_ADDWITHFLAGS:
|
||||
case OP_SUBWITHFLAGS: {
|
||||
uint64_t Constant2 {};
|
||||
if (IREmit->IsValueConstant(IROp->Args[1], &Constant2)) {
|
||||
// We don't allow 8/16-bit operations to have constants, since no
|
||||
// constant would be in bounds after the JIT's 24/16 shift.
|
||||
if (ARMEmitter::IsImmAddSub(Constant2) && IROp->Size >= 4) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[1]));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
|
||||
}
|
||||
} else if (IROp->Op == OP_SUBNZCV || IROp->Op == OP_SUBWITHFLAGS || IROp->Op == OP_SUB) {
|
||||
// TODO: Generalize this
|
||||
uint64_t Constant1 {};
|
||||
if (IREmit->IsValueConstant(IROp->Args[0], &Constant1)) {
|
||||
if (Constant1 == 0) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[0]));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->_InlineConstant(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case OP_ADC:
|
||||
case OP_ADCWITHFLAGS:
|
||||
case OP_STORECONTEXT: {
|
||||
uint64_t Constant1 {};
|
||||
if (IREmit->IsValueConstant(IROp->Args[0], &Constant1)) {
|
||||
if (Constant1 == 0) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[0]));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->_InlineConstant(0));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case OP_RMIFNZCV: {
|
||||
uint64_t Constant1 {};
|
||||
if (IREmit->IsValueConstant(IROp->Args[0], &Constant1)) {
|
||||
if (Constant1 == 0) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[0]));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->_InlineConstant(0));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case OP_CONDADDNZCV:
|
||||
case OP_CONDSUBNZCV: {
|
||||
uint64_t Constant2 {};
|
||||
if (IREmit->IsValueConstant(IROp->Args[1], &Constant2)) {
|
||||
if (ARMEmitter::IsImmAddSub(Constant2)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[1]));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t Constant1 {};
|
||||
if (IREmit->IsValueConstant(IROp->Args[0], &Constant1)) {
|
||||
if (Constant1 == 0) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[0]));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->_InlineConstant(0));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_TESTNZ: {
|
||||
uint64_t Constant1 {};
|
||||
if (IREmit->IsValueConstant(IROp->Args[1], &Constant1)) {
|
||||
if (IsImmLogical(Constant1, IROp->Size * 8)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[1]));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant1));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_SELECT: {
|
||||
uint64_t Constant1 {};
|
||||
if (IREmit->IsValueConstant(IROp->Args[1], &Constant1)) {
|
||||
if (ARMEmitter::IsImmAddSub(Constant1)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[1]));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant1));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case OP_NZCVSELECT: {
|
||||
// We always allow source 1 to be zero, but source 0 can only be a
|
||||
// special 1/~0 constant if source 1 is 0.
|
||||
if (InlineIfZero(IREmit, CurrentIR, CodeNode, IROp, 1)) {
|
||||
uint64_t AllOnes = IROp->Size == 8 ? 0xffff'ffff'ffff'ffffull : 0xffff'ffffull;
|
||||
|
||||
uint64_t Constant2 {};
|
||||
uint64_t Constant3 {};
|
||||
if (IREmit->IsValueConstant(IROp->Args[2], &Constant2) && IREmit->IsValueConstant(IROp->Args[3], &Constant3) &&
|
||||
(Constant2 == 1 || Constant2 == AllOnes) && Constant3 == 0) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[2]));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 2, IREmit->_InlineConstant(Constant2));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 3, IREmit->_InlineConstant(Constant3));
|
||||
}
|
||||
|
||||
break;
|
||||
InlineIf(IREmit, CurrentIR, CodeNode, IROp, 0, [&AllOnes](uint64_t X) { return X == 1 || X == AllOnes; });
|
||||
}
|
||||
case OP_NZCVSELECT: {
|
||||
uint64_t AllOnes = IROp->Size == 8 ? 0xffff'ffff'ffff'ffffull : 0xffff'ffffull;
|
||||
break;
|
||||
}
|
||||
case OP_CONDJUMP: {
|
||||
InlineIf(IREmit, CurrentIR, CodeNode, IROp, 1, ARMEmitter::IsImmAddSub);
|
||||
break;
|
||||
}
|
||||
case OP_EXITFUNCTION: {
|
||||
auto Op = IROp->C<IR::IROp_ExitFunction>();
|
||||
|
||||
// We always allow source 1 to be zero, but source 0 can only be a
|
||||
// special 1/~0 constant if source 1 is 0.
|
||||
uint64_t Constant0 {};
|
||||
uint64_t Constant1 {};
|
||||
if (IREmit->IsValueConstant(IROp->Args[1], &Constant1) && Constant1 == 0) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[1]));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant1));
|
||||
|
||||
if (IREmit->IsValueConstant(IROp->Args[0], &Constant0) && (Constant0 == 1 || Constant0 == AllOnes)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[0]));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->_InlineConstant(Constant0));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case OP_CONDJUMP: {
|
||||
uint64_t Constant2 {};
|
||||
if (IREmit->IsValueConstant(IROp->Args[1], &Constant2)) {
|
||||
if (ARMEmitter::IsImmAddSub(Constant2)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[1]));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_EXITFUNCTION: {
|
||||
auto Op = IROp->C<IR::IROp_ExitFunction>();
|
||||
|
||||
uint64_t Constant {};
|
||||
if (IREmit->IsValueConstant(Op->NewRIP, &Constant)) {
|
||||
if (!Inline(IREmit, CurrentIR, CodeNode, IROp, Op->NewRIP_Index)) {
|
||||
auto NewRIP = IREmit->GetOpHeader(Op->NewRIP);
|
||||
if (NewRIP->Op == OP_ENTRYPOINTOFFSET) {
|
||||
auto EO = NewRIP->C<IR::IROp_EntrypointOffset>();
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->NewRIP));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->_InlineConstant(Constant));
|
||||
} else {
|
||||
auto NewRIP = IREmit->GetOpHeader(Op->NewRIP);
|
||||
if (NewRIP->Op == OP_ENTRYPOINTOFFSET) {
|
||||
auto EO = NewRIP->C<IR::IROp_EntrypointOffset>();
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->NewRIP));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->_InlineEntrypointOffset(IR::SizeToOpSize(EO->Header.Size), EO->Offset));
|
||||
}
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->_InlineEntrypointOffset(IR::SizeToOpSize(EO->Header.Size), EO->Offset));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_OR:
|
||||
case OP_XOR:
|
||||
case OP_AND:
|
||||
case OP_ANDWITHFLAGS:
|
||||
case OP_ANDN: {
|
||||
uint64_t Constant2 {};
|
||||
if (IREmit->IsValueConstant(IROp->Args[1], &Constant2)) {
|
||||
if (IsImmLogical(Constant2, IROp->Size * 8)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(IROp->Args[1]));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_LOADMEM: {
|
||||
auto Op = IROp->CW<IR::IROp_LoadMem>();
|
||||
InlineMemImmediate(IREmit, CurrentIR, CodeNode, IROp, Op->Offset, Op->OffsetType, Op->Offset_Index, Op->OffsetScale, false);
|
||||
break;
|
||||
}
|
||||
case OP_STOREMEM: {
|
||||
auto Op = IROp->CW<IR::IROp_StoreMem>();
|
||||
InlineMemImmediate(IREmit, CurrentIR, CodeNode, IROp, Op->Offset, Op->OffsetType, Op->Offset_Index, Op->OffsetScale, false);
|
||||
break;
|
||||
}
|
||||
case OP_PREFETCH: {
|
||||
auto Op = IROp->CW<IR::IROp_Prefetch>();
|
||||
InlineMemImmediate(IREmit, CurrentIR, CodeNode, IROp, Op->Offset, Op->OffsetType, Op->Offset_Index, Op->OffsetScale, false);
|
||||
break;
|
||||
}
|
||||
case OP_LOADMEMTSO: {
|
||||
auto Op = IROp->CW<IR::IROp_LoadMemTSO>();
|
||||
InlineMemImmediate(IREmit, CurrentIR, CodeNode, IROp, Op->Offset, Op->OffsetType, Op->Offset_Index, Op->OffsetScale, true);
|
||||
break;
|
||||
}
|
||||
case OP_STOREMEMTSO: {
|
||||
auto Op = IROp->CW<IR::IROp_StoreMemTSO>();
|
||||
InlineMemImmediate(IREmit, CurrentIR, CodeNode, IROp, Op->Offset, Op->OffsetType, Op->Offset_Index, Op->OffsetScale, true);
|
||||
break;
|
||||
}
|
||||
case OP_MEMCPY: {
|
||||
auto Op = IROp->CW<IR::IROp_MemCpy>();
|
||||
break;
|
||||
}
|
||||
|
||||
uint64_t Constant {};
|
||||
if (IREmit->IsValueConstant(Op->Direction, &Constant)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Direction));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, Op->Direction_Index, IREmit->_InlineConstant(Constant));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_MEMSET: {
|
||||
auto Op = IROp->CW<IR::IROp_MemSet>();
|
||||
case OP_LOADMEM: {
|
||||
auto Op = IROp->CW<IR::IROp_LoadMem>();
|
||||
InlineMemImmediate(IREmit, CurrentIR, CodeNode, IROp, Op->Offset, Op->OffsetType, Op->Offset_Index, Op->OffsetScale, false);
|
||||
break;
|
||||
}
|
||||
case OP_STOREMEM: {
|
||||
auto Op = IROp->CW<IR::IROp_StoreMem>();
|
||||
InlineMemImmediate(IREmit, CurrentIR, CodeNode, IROp, Op->Offset, Op->OffsetType, Op->Offset_Index, Op->OffsetScale, false);
|
||||
break;
|
||||
}
|
||||
case OP_PREFETCH: {
|
||||
auto Op = IROp->CW<IR::IROp_Prefetch>();
|
||||
InlineMemImmediate(IREmit, CurrentIR, CodeNode, IROp, Op->Offset, Op->OffsetType, Op->Offset_Index, Op->OffsetScale, false);
|
||||
break;
|
||||
}
|
||||
case OP_LOADMEMTSO: {
|
||||
auto Op = IROp->CW<IR::IROp_LoadMemTSO>();
|
||||
InlineMemImmediate(IREmit, CurrentIR, CodeNode, IROp, Op->Offset, Op->OffsetType, Op->Offset_Index, Op->OffsetScale, true);
|
||||
break;
|
||||
}
|
||||
case OP_STOREMEMTSO: {
|
||||
auto Op = IROp->CW<IR::IROp_StoreMemTSO>();
|
||||
InlineMemImmediate(IREmit, CurrentIR, CodeNode, IROp, Op->Offset, Op->OffsetType, Op->Offset_Index, Op->OffsetScale, true);
|
||||
break;
|
||||
}
|
||||
case OP_MEMCPY: {
|
||||
auto Op = IROp->CW<IR::IROp_MemCpy>();
|
||||
Inline(IREmit, CurrentIR, CodeNode, IROp, Op->Direction_Index);
|
||||
break;
|
||||
}
|
||||
case OP_MEMSET: {
|
||||
auto Op = IROp->CW<IR::IROp_MemSet>();
|
||||
Inline(IREmit, CurrentIR, CodeNode, IROp, Op->Direction_Index);
|
||||
break;
|
||||
}
|
||||
|
||||
uint64_t Constant {};
|
||||
if (IREmit->IsValueConstant(Op->Direction, &Constant)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Direction));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, Op->Direction_Index, IREmit->_InlineConstant(Constant));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -807,13 +683,11 @@ void ConstProp::Run(IREmitter* IREmit) {
|
||||
|
||||
auto CurrentIR = IREmit->ViewIR();
|
||||
|
||||
HandleConstantPools(IREmit, CurrentIR);
|
||||
|
||||
for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) {
|
||||
ConstantPropagation(IREmit, CurrentIR, CodeNode, IROp);
|
||||
}
|
||||
|
||||
ConstantInlining(IREmit, CurrentIR);
|
||||
HandleConstantPools(IREmit, IREmit->ViewIR());
|
||||
}
|
||||
|
||||
fextl::unique_ptr<FEXCore::IR::Pass> CreateConstProp(bool SupportsTSOImm9, const FEXCore::CPUIDEmu* CPUID) {
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
$info$
|
||||
tags: ir|opts
|
||||
desc: Cross block store-after-store elimination
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/IR/IREmitter.h"
|
||||
#include "Interface/IR/PassManager.h"
|
||||
|
||||
#include <FEXCore/Core/CoreState.h>
|
||||
#include <FEXCore/Core/X86Enums.h>
|
||||
#include <FEXCore/IR/IR.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
#include <FEXCore/Utils/Profiler.h>
|
||||
|
||||
#include <memory>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace FEXCore::IR {
|
||||
|
||||
constexpr int PropagationRounds = 5;
|
||||
|
||||
// Return a bit representing a single GPR or FPR.
|
||||
static inline uint64_t RegBit(RegisterClassType Class, uint32_t Reg) {
|
||||
uint32_t AdjustedReg = (Class == FPRClass) ? (32 + Reg) : Reg;
|
||||
|
||||
return 1ULL << AdjustedReg;
|
||||
}
|
||||
|
||||
class DeadStoreElimination final : public FEXCore::IR::Pass {
|
||||
public:
|
||||
void Run(IREmitter* IREmit) override;
|
||||
};
|
||||
|
||||
struct ReadWriteKill {
|
||||
uint64_t reads {0};
|
||||
uint64_t writes {0};
|
||||
uint64_t kill {0};
|
||||
};
|
||||
|
||||
struct Info {
|
||||
ReadWriteKill reg;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This is a temporary pass to detect simple multiblock dead reg stores
|
||||
*
|
||||
* First pass computes which regs are read and written per block
|
||||
*
|
||||
* Second pass computes which regs are stored, but overwritten by the next block(s).
|
||||
* It also propagates this information a few times to catch dead regs across multiple blocks.
|
||||
*
|
||||
* Third pass removes the dead stores.
|
||||
*
|
||||
*/
|
||||
void DeadStoreElimination::Run(IREmitter* IREmit) {
|
||||
FEXCORE_PROFILE_SCOPED("PassManager::DSE");
|
||||
|
||||
auto CurrentIR = IREmit->ViewIR();
|
||||
fextl::vector<Info> InfoMap(CurrentIR.GetSSACount());
|
||||
|
||||
// Pass 1
|
||||
// Compute regs read/writes per block
|
||||
// This is conservative and doesn't try to be smart about loads after writes
|
||||
{
|
||||
for (auto [BlockNode, BlockIROp] : CurrentIR.GetBlocks()) {
|
||||
auto& BlockInfo = InfoMap[CurrentIR.GetID(BlockNode).Value];
|
||||
|
||||
for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) {
|
||||
if (IROp->Op == OP_STOREREGISTER) {
|
||||
auto Op = IROp->C<IR::IROp_StoreRegister>();
|
||||
BlockInfo.reg.writes |= RegBit(Op->Class, Op->Reg);
|
||||
} else if (IROp->Op == OP_LOADREGISTER) {
|
||||
auto Op = IROp->C<IR::IROp_LoadRegister>();
|
||||
BlockInfo.reg.reads |= RegBit(Op->Class, Op->Reg);
|
||||
} else if (IROp->Op == OP_INVALIDATEFLAGS) {
|
||||
auto Op = IROp->C<IR::IROp_InvalidateFlags>();
|
||||
|
||||
if (Op->Flags & (1u << X86State::RFLAG_PF_RAW_LOC)) {
|
||||
BlockInfo.reg.writes |= RegBit(GPRClass, Core::CPUState::PF_AS_GREG);
|
||||
}
|
||||
|
||||
if (Op->Flags & (1u << X86State::RFLAG_AF_RAW_LOC)) {
|
||||
BlockInfo.reg.writes |= RegBit(GPRClass, Core::CPUState::AF_AS_GREG);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 2
|
||||
// Compute flags/registers that are stored, but always ovewritten in the next blocks
|
||||
// Propagate the information a few times to eliminate more
|
||||
for (int i = 0; i < PropagationRounds; i++) {
|
||||
for (auto [BlockNode, BlockIROp] : CurrentIR.GetBlocks()) {
|
||||
auto CodeBlock = BlockIROp->C<IROp_CodeBlock>();
|
||||
|
||||
auto IROp = CurrentIR.GetNode(CurrentIR.GetNode(CodeBlock->Last)->Header.Previous)->Op(CurrentIR.GetData());
|
||||
|
||||
if (IROp->Op == OP_JUMP) {
|
||||
auto Op = IROp->C<IR::IROp_Jump>();
|
||||
auto& BlockInfo = InfoMap[CurrentIR.GetID(BlockNode).Value];
|
||||
auto& TargetInfo = InfoMap[Op->Header.Args[0].ID().Value];
|
||||
|
||||
// stores to remove are written by the next block but not read
|
||||
BlockInfo.reg.kill = TargetInfo.reg.writes & ~(TargetInfo.reg.reads) & ~BlockInfo.reg.reads;
|
||||
|
||||
// If written by the next block can be considered as written by this block, if not read
|
||||
BlockInfo.reg.writes |= BlockInfo.reg.kill & ~BlockInfo.reg.reads;
|
||||
} else if (IROp->Op == OP_CONDJUMP) {
|
||||
auto Op = IROp->C<IR::IROp_CondJump>();
|
||||
|
||||
auto& BlockInfo = InfoMap[CurrentIR.GetID(BlockNode).Value];
|
||||
auto& TrueTargetInfo = InfoMap[Op->TrueBlock.ID().Value];
|
||||
auto& FalseTargetInfo = InfoMap[Op->FalseBlock.ID().Value];
|
||||
|
||||
// stores to remove are written by the next blocks but not read
|
||||
BlockInfo.reg.kill = TrueTargetInfo.reg.writes & ~(TrueTargetInfo.reg.reads) & ~BlockInfo.reg.reads;
|
||||
BlockInfo.reg.kill &= FalseTargetInfo.reg.writes & ~(FalseTargetInfo.reg.reads) & ~BlockInfo.reg.reads;
|
||||
|
||||
// if written by the next blocks can be considered as written by this block, if not read
|
||||
BlockInfo.reg.writes |= BlockInfo.reg.kill & ~BlockInfo.reg.reads;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 3
|
||||
// Remove the dead stores
|
||||
{
|
||||
for (auto [BlockNode, BlockIROp] : CurrentIR.GetBlocks()) {
|
||||
auto& BlockInfo = InfoMap[CurrentIR.GetID(BlockNode).Value];
|
||||
|
||||
for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) {
|
||||
if (IROp->Op == OP_STOREREGISTER) {
|
||||
auto Op = IROp->C<IR::IROp_StoreRegister>();
|
||||
|
||||
// If this OP_STOREREGISTER is never read, remove it
|
||||
if (BlockInfo.reg.kill & RegBit(Op->Class, Op->Reg)) {
|
||||
IREmit->Remove(CodeNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fextl::unique_ptr<FEXCore::IR::Pass> CreateDeadStoreElimination() {
|
||||
return fextl::make_unique<DeadStoreElimination>();
|
||||
}
|
||||
|
||||
} // namespace FEXCore::IR
|
||||
@@ -7,6 +7,9 @@ $end_info$
|
||||
*/
|
||||
|
||||
#include "FEXCore/Core/X86Enums.h"
|
||||
#include "FEXCore/Utils/CompilerDefs.h"
|
||||
#include "FEXCore/Utils/MathUtils.h"
|
||||
#include "FEXCore/fextl/deque.h"
|
||||
#include "Interface/IR/IR.h"
|
||||
#include "Interface/IR/IREmitter.h"
|
||||
|
||||
@@ -15,16 +18,13 @@ $end_info$
|
||||
|
||||
#include "Interface/IR/PassManager.h"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
// Flag bit flags
|
||||
#define FLAG_V (1U << 0)
|
||||
#define FLAG_C (1U << 1)
|
||||
#define FLAG_Z (1U << 2)
|
||||
#define FLAG_N (1U << 3)
|
||||
#define FLAG_A (1U << 4)
|
||||
#define FLAG_P (1U << 5)
|
||||
#define FLAG_P (1U << 4)
|
||||
#define FLAG_A (1U << 5)
|
||||
|
||||
#define FLAG_ZCV (FLAG_Z | FLAG_C | FLAG_V)
|
||||
#define FLAG_NZCV (FLAG_N | FLAG_ZCV)
|
||||
@@ -32,10 +32,7 @@ $end_info$
|
||||
|
||||
namespace FEXCore::IR {
|
||||
|
||||
struct FlagInfo {
|
||||
// If set, all following fields are zero, used for a quick exit.
|
||||
bool Trivial;
|
||||
|
||||
struct FlagInfoUnpacked {
|
||||
// Set of flags read by the instruction.
|
||||
unsigned Read;
|
||||
|
||||
@@ -46,15 +43,106 @@ struct FlagInfo {
|
||||
// eliminated.
|
||||
bool CanEliminate;
|
||||
|
||||
// If true, the opcode can be replaced with Replacement if its flag writes can
|
||||
// all be eliminated.
|
||||
bool CanReplace;
|
||||
// If set, the opcode can be replaced with Replacement if its flag writes can
|
||||
// all be eliminated, or ReplacementNoWrite if its register write can be
|
||||
// eliminated.
|
||||
IROps Replacement;
|
||||
|
||||
// If true, the opcode can be replaced with ReplacementNoWrite if its register
|
||||
// write is unused but its flags are still needed.
|
||||
bool CanReplaceWrite;
|
||||
IROps ReplacementNoWrite;
|
||||
|
||||
// Needs speical handling
|
||||
bool Special;
|
||||
};
|
||||
|
||||
struct FlagInfo {
|
||||
uint64_t Raw;
|
||||
|
||||
static constexpr struct FlagInfo Pack(struct FlagInfoUnpacked F) {
|
||||
uint64_t R = F.Read | (F.Write << 8) | (F.CanEliminate << 16) | (((uint64_t)F.Replacement) << 32) |
|
||||
((uint64_t)F.ReplacementNoWrite << 48) | (F.Special ? (1ull << 63) : 0);
|
||||
return {.Raw = R};
|
||||
}
|
||||
|
||||
bool Trivial() {
|
||||
return Raw == 0;
|
||||
}
|
||||
|
||||
unsigned Read() {
|
||||
return Bits(0, 8);
|
||||
}
|
||||
|
||||
unsigned Write() {
|
||||
return Bits(8, 8);
|
||||
}
|
||||
|
||||
bool CanEliminate() {
|
||||
return Bits(16, 1);
|
||||
}
|
||||
|
||||
bool Special() {
|
||||
return Bits(63, 1);
|
||||
}
|
||||
|
||||
IROps Replacement() {
|
||||
return (IROps)Bits(32, 16);
|
||||
}
|
||||
|
||||
IROps ReplacementNoWrite() {
|
||||
return (IROps)Bits(48, 16);
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned Bits(unsigned Start, unsigned Count) {
|
||||
return (Raw >> Start) & ((1u << Count) - 1);
|
||||
}
|
||||
};
|
||||
|
||||
struct BlockInfo {
|
||||
fextl::vector<Ref> Predecessors;
|
||||
uint8_t Flags;
|
||||
bool InWorklist;
|
||||
};
|
||||
|
||||
struct ControlFlowGraph {
|
||||
fextl::unordered_map<uint32_t, BlockInfo> BlockMap;
|
||||
IRListView& IR;
|
||||
|
||||
void AddBlock(fextl::deque<Ref>& Worklist, Ref Block) {
|
||||
uint32_t ID = IR.GetID(Block).Value;
|
||||
|
||||
// Add the block with conservative flags and already in the worklist.
|
||||
auto Info = &BlockMap.emplace(ID, BlockInfo {{}, FLAG_ALL, true}).first->second;
|
||||
|
||||
// Add some initial capacity
|
||||
Info->Predecessors.reserve(2);
|
||||
|
||||
// Add to worklist
|
||||
Worklist.push_back(Block);
|
||||
}
|
||||
|
||||
BlockInfo* Get(uint32_t Block) {
|
||||
return &BlockMap.try_emplace(Block).first->second;
|
||||
}
|
||||
|
||||
BlockInfo* Get(Ref Block) {
|
||||
return Get(IR.GetID(Block).Value);
|
||||
}
|
||||
|
||||
BlockInfo* Get(OrderedNodeWrapper Block) {
|
||||
return Get(Block.ID().Value);
|
||||
}
|
||||
|
||||
void RecordEdge(Ref From, Ref To) {
|
||||
auto Info = Get(To);
|
||||
Info->Predecessors.push_back(From);
|
||||
}
|
||||
|
||||
void AddWorklist(fextl::deque<Ref>& Worklist, Ref Block) {
|
||||
auto Info = Get(Block);
|
||||
if (!Info->InWorklist) {
|
||||
Info->InWorklist = true;
|
||||
Worklist.push_front(Block);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class DeadFlagCalculationEliminination final : public FEXCore::IR::Pass {
|
||||
@@ -66,10 +154,10 @@ private:
|
||||
unsigned FlagForReg(unsigned Reg);
|
||||
unsigned FlagsForCondClassType(CondClassType Cond);
|
||||
bool EliminateDeadCode(IREmitter* IREmit, Ref CodeNode, IROp_Header* IROp);
|
||||
};
|
||||
|
||||
unsigned DeadFlagCalculationEliminination::FlagForReg(unsigned Reg) {
|
||||
return Reg == Core::CPUState::PF_AS_GREG ? FLAG_P : Reg == Core::CPUState::AF_AS_GREG ? FLAG_A : 0;
|
||||
void FoldBranch(IREmitter* IREmit, IRListView& CurrentIR, IROp_CondJump* Op, Ref CodeNode);
|
||||
CondClassType X86ToArmFloatCond(CondClassType X86);
|
||||
bool ProcessBlock(IREmitter* IREmit, IRListView& CurrentIR, Ref Block, ControlFlowGraph& CFG);
|
||||
void OptimizeParity(IREmitter* IREmit, IRListView& CurrentIR, ControlFlowGraph& CFG);
|
||||
};
|
||||
|
||||
unsigned DeadFlagCalculationEliminination::FlagsForCondClassType(CondClassType Cond) {
|
||||
@@ -107,160 +195,186 @@ unsigned DeadFlagCalculationEliminination::FlagsForCondClassType(CondClassType C
|
||||
}
|
||||
}
|
||||
|
||||
FlagInfo DeadFlagCalculationEliminination::Classify(IROp_Header* IROp) {
|
||||
switch (IROp->Op) {
|
||||
constexpr FlagInfo ClassifyConst(IROps Op) {
|
||||
switch (Op) {
|
||||
case OP_ANDWITHFLAGS:
|
||||
return {
|
||||
return FlagInfo::Pack({
|
||||
.Write = FLAG_NZCV,
|
||||
.CanReplace = true,
|
||||
.Replacement = OP_AND,
|
||||
};
|
||||
.ReplacementNoWrite = OP_TESTNZ,
|
||||
});
|
||||
|
||||
case OP_ADDWITHFLAGS:
|
||||
return {
|
||||
return FlagInfo::Pack({
|
||||
.Write = FLAG_NZCV,
|
||||
.CanReplace = true,
|
||||
.Replacement = OP_ADD,
|
||||
.CanReplaceWrite = true,
|
||||
.ReplacementNoWrite = OP_ADDNZCV,
|
||||
};
|
||||
});
|
||||
|
||||
case OP_SUBWITHFLAGS:
|
||||
return {
|
||||
return FlagInfo::Pack({
|
||||
.Write = FLAG_NZCV,
|
||||
.CanReplace = true,
|
||||
.Replacement = OP_SUB,
|
||||
.CanReplaceWrite = true,
|
||||
.ReplacementNoWrite = OP_SUBNZCV,
|
||||
};
|
||||
});
|
||||
|
||||
case OP_ADCWITHFLAGS:
|
||||
return {
|
||||
return FlagInfo::Pack({
|
||||
.Read = FLAG_C,
|
||||
.Write = FLAG_NZCV,
|
||||
.CanReplace = true,
|
||||
.Replacement = OP_ADC,
|
||||
.CanReplaceWrite = true,
|
||||
.ReplacementNoWrite = OP_ADCNZCV,
|
||||
};
|
||||
});
|
||||
|
||||
case OP_ADCZEROWITHFLAGS:
|
||||
return {
|
||||
return FlagInfo::Pack({
|
||||
.Read = FLAG_C,
|
||||
.Write = FLAG_NZCV,
|
||||
.CanReplace = true,
|
||||
.Replacement = OP_ADCZERO,
|
||||
};
|
||||
});
|
||||
|
||||
case OP_SBBWITHFLAGS:
|
||||
return {
|
||||
return FlagInfo::Pack({
|
||||
.Read = FLAG_C,
|
||||
.Write = FLAG_NZCV,
|
||||
.CanReplace = true,
|
||||
.Replacement = OP_SBB,
|
||||
.CanReplaceWrite = true,
|
||||
.ReplacementNoWrite = OP_SBBNZCV,
|
||||
};
|
||||
});
|
||||
|
||||
case OP_SHIFTFLAGS:
|
||||
// _ShiftFlags conditionally sets NZCV+PF, which we model here as a
|
||||
// read-modify-write. Logically, it also conditionally makes AF undefined,
|
||||
// which we model by omitting AF from both Read and Write sets (since
|
||||
// "cond ? AF : undef" may be optimized to "AF").
|
||||
return {
|
||||
return FlagInfo::Pack({
|
||||
.Read = FLAG_NZCV | FLAG_P,
|
||||
.Write = FLAG_NZCV | FLAG_P,
|
||||
.CanEliminate = true,
|
||||
};
|
||||
});
|
||||
|
||||
case OP_ROTATEFLAGS:
|
||||
// _RotateFlags conditionally sets CV, again modeled as RMW.
|
||||
return {
|
||||
return FlagInfo::Pack({
|
||||
.Read = FLAG_C | FLAG_V,
|
||||
.Write = FLAG_C | FLAG_V,
|
||||
.CanEliminate = true,
|
||||
};
|
||||
});
|
||||
|
||||
case OP_RDRAND: return {.Write = FLAG_NZCV};
|
||||
case OP_RDRAND: return FlagInfo::Pack({.Write = FLAG_NZCV});
|
||||
|
||||
case OP_ADDNZCV:
|
||||
case OP_SUBNZCV:
|
||||
case OP_TESTNZ:
|
||||
case OP_FCMP:
|
||||
case OP_STORENZCV:
|
||||
return {
|
||||
return FlagInfo::Pack({
|
||||
.Write = FLAG_NZCV,
|
||||
.CanEliminate = true,
|
||||
};
|
||||
});
|
||||
|
||||
case OP_AXFLAG:
|
||||
// Per the Arm spec, axflag reads Z/V/C but not N. It writes all flags.
|
||||
return {
|
||||
return FlagInfo::Pack({
|
||||
.Read = FLAG_ZCV,
|
||||
.Write = FLAG_NZCV,
|
||||
.CanEliminate = true,
|
||||
};
|
||||
});
|
||||
|
||||
case OP_CMPPAIRZ:
|
||||
return {
|
||||
return FlagInfo::Pack({
|
||||
.Write = FLAG_Z,
|
||||
.CanEliminate = true,
|
||||
};
|
||||
});
|
||||
|
||||
case OP_CARRYINVERT:
|
||||
return {
|
||||
return FlagInfo::Pack({
|
||||
.Read = FLAG_C,
|
||||
.Write = FLAG_C,
|
||||
.CanEliminate = true,
|
||||
};
|
||||
});
|
||||
|
||||
case OP_SETSMALLNZV:
|
||||
return {
|
||||
return FlagInfo::Pack({
|
||||
.Write = FLAG_N | FLAG_Z | FLAG_V,
|
||||
.CanEliminate = true,
|
||||
};
|
||||
});
|
||||
|
||||
case OP_LOADNZCV: return {.Read = FLAG_NZCV};
|
||||
case OP_LOADNZCV: return FlagInfo::Pack({.Read = FLAG_NZCV});
|
||||
|
||||
case OP_ADC:
|
||||
case OP_SBB: return {.Read = FLAG_C};
|
||||
case OP_ADCZERO:
|
||||
case OP_SBB: return FlagInfo::Pack({.Read = FLAG_C});
|
||||
|
||||
case OP_ADCNZCV:
|
||||
case OP_SBBNZCV:
|
||||
return {
|
||||
return FlagInfo::Pack({
|
||||
.Read = FLAG_C,
|
||||
.Write = FLAG_NZCV,
|
||||
.CanEliminate = true,
|
||||
};
|
||||
});
|
||||
|
||||
case OP_LOADPF: return FlagInfo::Pack({.Read = FLAG_P});
|
||||
case OP_LOADAF: return FlagInfo::Pack({.Read = FLAG_A});
|
||||
case OP_STOREPF: return FlagInfo::Pack({.Write = FLAG_P, .CanEliminate = true});
|
||||
case OP_STOREAF: return FlagInfo::Pack({.Write = FLAG_A, .CanEliminate = true});
|
||||
|
||||
case OP_NZCVSELECT:
|
||||
case OP_NZCVSELECTINCREMENT:
|
||||
case OP_NEG:
|
||||
case OP_CONDJUMP:
|
||||
case OP_CONDSUBNZCV:
|
||||
case OP_CONDADDNZCV:
|
||||
case OP_RMIFNZCV:
|
||||
case OP_INVALIDATEFLAGS: return FlagInfo::Pack({.Special = true});
|
||||
default: return FlagInfo::Pack({});
|
||||
}
|
||||
}
|
||||
|
||||
constexpr auto FlagInfos = std::invoke([] {
|
||||
std::array<FlagInfo, OP_LAST> ret = {};
|
||||
|
||||
for (unsigned i = 0; i < OP_LAST; ++i) {
|
||||
ret[i] = ClassifyConst((IROps)i);
|
||||
}
|
||||
|
||||
return ret;
|
||||
});
|
||||
|
||||
FlagInfo DeadFlagCalculationEliminination::Classify(IROp_Header* IROp) {
|
||||
FlagInfo Info = FlagInfos[IROp->Op];
|
||||
if (!Info.Special()) {
|
||||
return Info;
|
||||
}
|
||||
|
||||
switch (IROp->Op) {
|
||||
case OP_NZCVSELECT:
|
||||
case OP_NZCVSELECTINCREMENT: {
|
||||
auto Op = IROp->CW<IR::IROp_NZCVSelect>();
|
||||
return {.Read = FlagsForCondClassType(Op->Cond)};
|
||||
return FlagInfo::Pack({.Read = FlagsForCondClassType(Op->Cond)});
|
||||
}
|
||||
|
||||
case OP_NEG: {
|
||||
auto Op = IROp->CW<IR::IROp_Neg>();
|
||||
return {.Read = FlagsForCondClassType(Op->Cond)};
|
||||
return FlagInfo::Pack({.Read = FlagsForCondClassType(Op->Cond)});
|
||||
}
|
||||
|
||||
case OP_CONDJUMP: {
|
||||
auto Op = IROp->CW<IR::IROp_CondJump>();
|
||||
if (!Op->FromNZCV) {
|
||||
break;
|
||||
return FlagInfo::Pack({});
|
||||
}
|
||||
|
||||
return {.Read = FlagsForCondClassType(Op->Cond)};
|
||||
return FlagInfo::Pack({.Read = FlagsForCondClassType(Op->Cond)});
|
||||
}
|
||||
|
||||
case OP_CONDSUBNZCV:
|
||||
case OP_CONDADDNZCV: {
|
||||
auto Op = IROp->CW<IR::IROp_CondAddNZCV>();
|
||||
return {
|
||||
return FlagInfo::Pack({
|
||||
.Read = FlagsForCondClassType(Op->Cond),
|
||||
.Write = FLAG_NZCV,
|
||||
.CanEliminate = true,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
case OP_RMIFNZCV: {
|
||||
@@ -271,10 +385,10 @@ FlagInfo DeadFlagCalculationEliminination::Classify(IROp_Header* IROp) {
|
||||
static_assert(FLAG_C == (1 << 1), "rmif mask lines up with our bits");
|
||||
static_assert(FLAG_V == (1 << 0), "rmif mask lines up with our bits");
|
||||
|
||||
return {
|
||||
return FlagInfo::Pack({
|
||||
.Write = Op->Mask,
|
||||
.CanEliminate = true,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
case OP_INVALIDATEFLAGS: {
|
||||
@@ -309,39 +423,16 @@ FlagInfo DeadFlagCalculationEliminination::Classify(IROp_Header* IROp) {
|
||||
// The mental model of InvalidateFlags is writing undefined values to all
|
||||
// of the selected flags, allowing the write-after-write optimizations to
|
||||
// optimize invalidate-after-write for free.
|
||||
return {
|
||||
return FlagInfo::Pack({
|
||||
.Write = Flags,
|
||||
.CanEliminate = true,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
case OP_LOADREGISTER: {
|
||||
auto Op = IROp->CW<IR::IROp_LoadRegister>();
|
||||
if (Op->Class != GPRClass) {
|
||||
break;
|
||||
}
|
||||
|
||||
return {.Read = FlagForReg(Op->Reg)};
|
||||
default: LOGMAN_THROW_AA_FMT(false, "invalid special op"); FEX_UNREACHABLE;
|
||||
}
|
||||
|
||||
case OP_STOREREGISTER: {
|
||||
auto Op = IROp->CW<IR::IROp_StoreRegister>();
|
||||
if (Op->Class != GPRClass) {
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned Flag = FlagForReg(Op->Reg);
|
||||
|
||||
return {
|
||||
.Write = Flag,
|
||||
.CanEliminate = Flag != 0,
|
||||
};
|
||||
}
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
return {.Trivial = true};
|
||||
FEX_UNREACHABLE;
|
||||
}
|
||||
|
||||
// General purpose dead code elimination. Returns whether flag handling should
|
||||
@@ -385,83 +476,295 @@ bool DeadFlagCalculationEliminination::EliminateDeadCode(IREmitter* IREmit, Ref
|
||||
return true;
|
||||
}
|
||||
|
||||
CondClassType DeadFlagCalculationEliminination::X86ToArmFloatCond(CondClassType X86) {
|
||||
// Table of x86 condition codes that map to arm64 condition codes, in the
|
||||
// sense that fcmp+axflag+branch(x86) is equivalent to fcmp+branch(arm).
|
||||
//
|
||||
// E would be "equal or unordered", no condition code.
|
||||
// G would be "greater than or less than", no condition code.
|
||||
//
|
||||
// SF/OF conditions are trivial and therefore shouldn't actually be generated
|
||||
switch (X86) {
|
||||
case COND_UGE /* A */: return {COND_FGE} /* GE */;
|
||||
case COND_UGT /* AE */: return {COND_FGT} /* GT */;
|
||||
case COND_ULT /* B */: return {COND_SLT} /* LT */;
|
||||
case COND_ULE /* BE */: return {COND_SLE} /* LE */;
|
||||
case COND_SLE /* LE */: return {COND_SLE} /* LE */;
|
||||
default: return {COND_AL};
|
||||
}
|
||||
}
|
||||
|
||||
void DeadFlagCalculationEliminination::FoldBranch(IREmitter* IREmit, IRListView& CurrentIR, IROp_CondJump* Op, Ref CodeNode) {
|
||||
// Skip past StoreRegisters at the end -- they don't touch flags.
|
||||
auto PrevWrap = CodeNode->Header.Previous;
|
||||
while (CurrentIR.GetOp<IR::IROp_Header>(PrevWrap)->Op == OP_STOREREGISTER ||
|
||||
CurrentIR.GetOp<IR::IROp_Header>(PrevWrap)->Op == OP_STOREPF || CurrentIR.GetOp<IR::IROp_Header>(PrevWrap)->Op == OP_STOREAF) {
|
||||
PrevWrap = CurrentIR.GetNode(PrevWrap)->Header.Previous;
|
||||
}
|
||||
|
||||
auto Prev = CurrentIR.GetOp<IR::IROp_Header>(PrevWrap);
|
||||
if (Prev->Op == OP_AXFLAG) {
|
||||
// Pattern match a branch fed by AXFLAG.
|
||||
CondClassType ArmCond = X86ToArmFloatCond(Op->Cond);
|
||||
if (ArmCond == COND_AL) {
|
||||
return;
|
||||
}
|
||||
|
||||
Op->Cond = ArmCond;
|
||||
} else if (Prev->Op == OP_SUBNZCV) {
|
||||
// Pattern match a branch fed by a compare. We could also handle bit tests
|
||||
// here, but tbz/tbnz has a limited offset range which we don't have a way to
|
||||
// deal with yet. Let's hope that's not a big deal.
|
||||
if (!(Op->Cond == COND_NEQ || Op->Cond == COND_EQ) || (Prev->Size < 4)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto SecondArg = CurrentIR.GetOp<IR::IROp_Header>(Prev->Args[1]);
|
||||
if (SecondArg->Op != OP_INLINECONSTANT || SecondArg->C<IR::IROp_InlineConstant>()->Constant != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We've matched. Fold the compare into branch.
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 0, CurrentIR.GetNode(Prev->Args[0]));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, CurrentIR.GetNode(Prev->Args[1]));
|
||||
Op->FromNZCV = false;
|
||||
Op->CompareSize = Prev->Size;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// The compare/test/axflag sets flags but does not write registers. Flags are
|
||||
// dead after the jump. The jump does not read flags anymore. There is no
|
||||
// intervening instruction. Therefore the compare is dead.
|
||||
IREmit->Remove(CurrentIR.GetNode(PrevWrap));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This pass removes dead code locally.
|
||||
*/
|
||||
bool DeadFlagCalculationEliminination::ProcessBlock(IREmitter* IREmit, IRListView& CurrentIR, Ref Block, ControlFlowGraph& CFG) {
|
||||
uint32_t FlagsRead = FLAG_ALL;
|
||||
|
||||
// Reverse iteration is not yet working with the iterators
|
||||
auto BlockIROp = CurrentIR.GetOp<IR::IROp_CodeBlock>(Block);
|
||||
|
||||
// We grab these nodes this way so we can iterate easily
|
||||
auto CodeBegin = CurrentIR.at(BlockIROp->Begin);
|
||||
auto CodeLast = CurrentIR.at(BlockIROp->Last);
|
||||
|
||||
// Advance past EndBlock to get at the exit.
|
||||
--CodeLast;
|
||||
|
||||
// Initialize the FlagsRead mask according to the exit instruction.
|
||||
auto [ExitNode, ExitOp] = CodeLast();
|
||||
if (ExitOp->Op == IR::OP_CONDJUMP) {
|
||||
auto Op = ExitOp->CW<IR::IROp_CondJump>();
|
||||
FlagsRead = CFG.Get(Op->TrueBlock)->Flags | CFG.Get(Op->FalseBlock)->Flags;
|
||||
} else if (ExitOp->Op == IR::OP_JUMP) {
|
||||
FlagsRead = CFG.Get(ExitOp->Args[0])->Flags;
|
||||
}
|
||||
|
||||
// Iterate the block in reverse
|
||||
while (true) {
|
||||
auto [CodeNode, IROp] = CodeLast();
|
||||
|
||||
// Optimizing flags can cause earlier flag reads to become dead but dead
|
||||
// flag reads should not impede optimiation of earlier dead flag writes.
|
||||
// We must DCE as we go to ensure we converge in a single iteration.
|
||||
if (!EliminateDeadCode(IREmit, CodeNode, IROp)) {
|
||||
// Optimiation algorithm: For each flag written...
|
||||
//
|
||||
// If the flag has a later read (per FlagsRead), remove the flag from
|
||||
// FlagsRead, since the reader is covered by this write.
|
||||
//
|
||||
// Else, there is no later read, so remove the flag write (if we can).
|
||||
// This is the active part of the optimization.
|
||||
//
|
||||
// Then, add each flag read to FlagsRead.
|
||||
//
|
||||
// This order is important: instructions that read-modify-write flags
|
||||
// (like adcs) first read flags, then write flags. Since we're iterating
|
||||
// the block backwards, that means we handle the write first.
|
||||
struct FlagInfo Info = Classify(IROp);
|
||||
|
||||
if (!Info.Trivial()) {
|
||||
bool Eliminated = false;
|
||||
|
||||
if ((FlagsRead & Info.Write()) == 0) {
|
||||
if ((Info.CanEliminate() || Info.Replacement()) && CodeNode->GetUses() == 0) {
|
||||
IREmit->Remove(CodeNode);
|
||||
Eliminated = true;
|
||||
} else if (Info.Replacement()) {
|
||||
IROp->Op = Info.Replacement();
|
||||
}
|
||||
} else if (Info.ReplacementNoWrite() && CodeNode->GetUses() == 0) {
|
||||
IROp->Op = Info.ReplacementNoWrite();
|
||||
}
|
||||
|
||||
// If we don't care about the sign or carry, we can optimize testnz.
|
||||
// Carry is inverted between testz and testnz so we check that too. Note
|
||||
// this flag is outside of the if, since the TestNZ might result from
|
||||
// optimizing AndWithFlags, and we need to converge locally in a single
|
||||
// iteration.
|
||||
if (IROp->Op == OP_TESTNZ && IROp->Size < 4 && !(FlagsRead & (FLAG_N | FLAG_C))) {
|
||||
IROp->Op = OP_TESTZ;
|
||||
}
|
||||
|
||||
FlagsRead &= ~Info.Write();
|
||||
|
||||
// If we eliminated the instruction, we eliminate its read too. This
|
||||
// check is required to ensure the pass converges locally in a single
|
||||
// iteration.
|
||||
if (!Eliminated) {
|
||||
FlagsRead |= Info.Read();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate in reverse
|
||||
if (CodeLast == CodeBegin) {
|
||||
break;
|
||||
}
|
||||
--CodeLast;
|
||||
}
|
||||
|
||||
// For the purposes of global propagation, the content of our progress doesn't
|
||||
// matter -- only the difference in our final FlagsRead contributes to changes
|
||||
// in the predecessors.
|
||||
uint32_t OldFlagsRead = CFG.Get(Block)->Flags;
|
||||
CFG.Get(Block)->Flags = FlagsRead;
|
||||
return (OldFlagsRead != FlagsRead);
|
||||
}
|
||||
|
||||
void DeadFlagCalculationEliminination::OptimizeParity(IREmitter* IREmit, IRListView& CurrentIR, ControlFlowGraph& CFG) {
|
||||
// Mapping for flags inside this pass.
|
||||
const uint8_t PARTIAL = 0;
|
||||
const uint8_t FULL = 1;
|
||||
|
||||
// Initialize conservatively: all blocks need full parity. This initialization
|
||||
// matters for proper handling of backedges.
|
||||
for (auto [Block, BlockHeader] : CurrentIR.GetBlocks()) {
|
||||
CFG.Get(Block)->Flags = FULL;
|
||||
}
|
||||
|
||||
for (auto [Block, BlockHeader] : CurrentIR.GetBlocks()) {
|
||||
bool Full = false;
|
||||
auto Predecessors = CFG.Get(Block)->Predecessors;
|
||||
|
||||
if (Predecessors.empty()) {
|
||||
// Conservatively assume there was full parity before the start block
|
||||
Full = true;
|
||||
} else {
|
||||
// If any predecessor needs full parity at the end, we need full parity.
|
||||
for (auto Pred : Predecessors) {
|
||||
Full |= (CFG.Get(Pred)->Flags == FULL);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto [CodeNode, IROp] : CurrentIR.GetCode(Block)) {
|
||||
if (IROp->Op == OP_STOREPF) {
|
||||
auto Op = IROp->CW<IR::IROp_StorePF>();
|
||||
auto Generator = CurrentIR.GetOp<IR::IROp_Header>(Op->Value);
|
||||
|
||||
// Determine if we only write 0/1 to the parity flag.
|
||||
Full = true;
|
||||
if (Generator->Op == OP_NZCVSELECT) {
|
||||
auto C0 = CurrentIR.GetOp<IR::IROp_Header>(Generator->Args[0]);
|
||||
auto C1 = CurrentIR.GetOp<IR::IROp_Header>(Generator->Args[1]);
|
||||
if (C0->Op == C1->Op && C0->Op == OP_INLINECONSTANT) {
|
||||
auto IC0 = CurrentIR.GetOp<IR::IROp_InlineConstant>(Generator->Args[0]);
|
||||
auto IC1 = CurrentIR.GetOp<IR::IROp_InlineConstant>(Generator->Args[1]);
|
||||
|
||||
// We need the full 8 if the constant has upper bits set.
|
||||
Full = (IC0->Constant | IC1->Constant) & ~1;
|
||||
}
|
||||
}
|
||||
} else if (IROp->Op == OP_PARITY && !Full) {
|
||||
// Eliminate parity calculations if it's only 1-bit.
|
||||
auto Parity = IROp->C<IROp_Parity>();
|
||||
Ref Value = CurrentIR.GetNode(Parity->Raw);
|
||||
|
||||
if (Parity->Invert) {
|
||||
IREmit->SetWriteCursor(CodeNode);
|
||||
Value = IREmit->_Xor(OpSize::i32Bit, Value, IREmit->_InlineConstant(1));
|
||||
}
|
||||
|
||||
IREmit->ReplaceUsesWithAfter(CodeNode, Value, CurrentIR.at(CodeNode));
|
||||
IREmit->Remove(CodeNode);
|
||||
}
|
||||
}
|
||||
|
||||
// Record our final state for our successors to read.
|
||||
CFG.Get(Block)->Flags = Full ? FULL : PARTIAL;
|
||||
}
|
||||
}
|
||||
|
||||
void DeadFlagCalculationEliminination::Run(IREmitter* IREmit) {
|
||||
FEXCORE_PROFILE_SCOPED("PassManager::DFE");
|
||||
|
||||
auto CurrentIR = IREmit->ViewIR();
|
||||
fextl::deque<Ref> Worklist;
|
||||
|
||||
ControlFlowGraph CFG {.IR = CurrentIR};
|
||||
|
||||
// Gather blocks
|
||||
for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) {
|
||||
// We model all flags as read at the end of the block, since this pass is
|
||||
// presently purely local. Optimizing this requires global anslysis.
|
||||
uint32_t FlagsRead = FLAG_ALL;
|
||||
CFG.AddBlock(Worklist, BlockNode);
|
||||
}
|
||||
|
||||
// Reverse iteration is not yet working with the iterators
|
||||
auto BlockIROp = BlockHeader->CW<FEXCore::IR::IROp_CodeBlock>();
|
||||
// Gather CFG
|
||||
for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) {
|
||||
auto CodeLast = CurrentIR.at(BlockHeader->C<IROp_CodeBlock>()->Last);
|
||||
--CodeLast;
|
||||
auto [ExitNode, ExitOp] = CodeLast();
|
||||
if (ExitOp->Op == IR::OP_CONDJUMP) {
|
||||
auto Op = ExitOp->CW<IR::IROp_CondJump>();
|
||||
|
||||
// We grab these nodes this way so we can iterate easily
|
||||
auto CodeBegin = CurrentIR.at(BlockIROp->Begin);
|
||||
auto CodeLast = CurrentIR.at(BlockIROp->Last);
|
||||
|
||||
// Iterate the block in reverse
|
||||
while (1) {
|
||||
auto [CodeNode, IROp] = CodeLast();
|
||||
|
||||
// Optimizing flags can cause earlier flag reads to become dead but dead
|
||||
// flag reads should not impede optimiation of earlier dead flag writes.
|
||||
// We must DCE as we go to ensure we converge in a single iteration.
|
||||
if (!EliminateDeadCode(IREmit, CodeNode, IROp)) {
|
||||
// Optimiation algorithm: For each flag written...
|
||||
//
|
||||
// If the flag has a later read (per FlagsRead), remove the flag from
|
||||
// FlagsRead, since the reader is covered by this write.
|
||||
//
|
||||
// Else, there is no later read, so remove the flag write (if we can).
|
||||
// This is the active part of the optimization.
|
||||
//
|
||||
// Then, add each flag read to FlagsRead.
|
||||
//
|
||||
// This order is important: instructions that read-modify-write flags
|
||||
// (like adcs) first read flags, then write flags. Since we're iterating
|
||||
// the block backwards, that means we handle the write first.
|
||||
struct FlagInfo Info = Classify(IROp);
|
||||
|
||||
if (!Info.Trivial) {
|
||||
bool Eliminated = false;
|
||||
|
||||
if ((FlagsRead & Info.Write) == 0) {
|
||||
if ((Info.CanEliminate || Info.CanReplace) && CodeNode->GetUses() == 0) {
|
||||
IREmit->Remove(CodeNode);
|
||||
Eliminated = true;
|
||||
} else if (Info.CanReplace) {
|
||||
IROp->Op = Info.Replacement;
|
||||
}
|
||||
} else {
|
||||
FlagsRead &= ~Info.Write;
|
||||
|
||||
if (Info.CanReplaceWrite && CodeNode->GetUses() == 0) {
|
||||
IROp->Op = Info.ReplacementNoWrite;
|
||||
}
|
||||
}
|
||||
|
||||
// If we eliminated the instruction, we eliminate its read too. This
|
||||
// check is required to ensure the pass converges locally in a single
|
||||
// iteration.
|
||||
if (!Eliminated) {
|
||||
FlagsRead |= Info.Read;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate in reverse
|
||||
if (CodeLast == CodeBegin) {
|
||||
break;
|
||||
}
|
||||
--CodeLast;
|
||||
CFG.RecordEdge(BlockNode, CurrentIR.GetNode(Op->TrueBlock));
|
||||
CFG.RecordEdge(BlockNode, CurrentIR.GetNode(Op->FalseBlock));
|
||||
} else if (ExitOp->Op == IR::OP_JUMP) {
|
||||
CFG.RecordEdge(BlockNode, CurrentIR.GetNode(ExitOp->Args[0]));
|
||||
}
|
||||
}
|
||||
|
||||
// After processing a block, if we made progress, we must process its
|
||||
// predecessors to propagate globally. A block will be reprocessed only if
|
||||
// there is a loop backedge.
|
||||
for (; !Worklist.empty(); Worklist.pop_back()) {
|
||||
auto Block = Worklist.back();
|
||||
auto Info = CFG.Get(Block);
|
||||
Info->InWorklist = false;
|
||||
|
||||
if (ProcessBlock(IREmit, CurrentIR, Block, CFG)) {
|
||||
for (auto Pred : Info->Predecessors) {
|
||||
CFG.AddWorklist(Worklist, Pred);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fold compares into branches now that we're otherwise optimized. This needs
|
||||
// to run after eliminating carries etc and it needs the global flag metadata.
|
||||
// But it only needs to run once, we don't do it in the loop.
|
||||
for (auto [Block, _] : CurrentIR.GetBlocks()) {
|
||||
// Grab the jump
|
||||
auto BlockIROp = CurrentIR.GetOp<IR::IROp_CodeBlock>(Block);
|
||||
auto CodeLast = CurrentIR.at(BlockIROp->Last);
|
||||
--CodeLast;
|
||||
|
||||
auto [ExitNode, ExitOp] = CodeLast();
|
||||
if (ExitOp->Op == IR::OP_CONDJUMP) {
|
||||
auto Op = ExitOp->CW<IR::IROp_CondJump>();
|
||||
uint32_t FlagsOut = CFG.Get(Op->TrueBlock)->Flags | CFG.Get(Op->FalseBlock)->Flags;
|
||||
|
||||
if ((FlagsOut & FLAG_NZCV) == 0 && Op->FromNZCV) {
|
||||
FoldBranch(IREmit, CurrentIR, Op, ExitNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (CurrentIR.GetHeader()->ReadsParity) {
|
||||
OptimizeParity(IREmit, CurrentIR, CFG);
|
||||
}
|
||||
}
|
||||
|
||||
fextl::unique_ptr<FEXCore::IR::Pass> CreateDeadFlagCalculationEliminination() {
|
||||
|
||||
@@ -28,13 +28,10 @@ namespace {
|
||||
uint32_t Available;
|
||||
uint32_t Count;
|
||||
|
||||
// If bit R of Allocated is 1, then RegToSSA[R] is the Old node
|
||||
// If bit R of Available is 0, then RegToSSA[R] is the Old node
|
||||
// currently allocated to R. Else, RegToSSA[R] is UNDEFINED, no need to
|
||||
// clear this when freeing registers.
|
||||
Ref RegToSSA[32];
|
||||
|
||||
// Allocated base registers. Similar to ~Available except for pairs.
|
||||
uint32_t Allocated;
|
||||
};
|
||||
|
||||
IR::RegisterClassType GetRegClassFromNode(IR::IRListView* IR, IR::IROp_Header* IROp) {
|
||||
@@ -90,7 +87,7 @@ private:
|
||||
//
|
||||
// SSAToNewSSA tracks the current remapping. nullptr indicates no remapping.
|
||||
//
|
||||
// Since its indexed by Old nodes, SSAToNewSSA does not grow.
|
||||
// Since its indexed by Old nodes, SSAToNewSSA does not grow after allocation.
|
||||
fextl::vector<Ref> SSAToNewSSA;
|
||||
|
||||
// Inverse of SSAToNewSSA. Since it's indexed by new nodes, it grows.
|
||||
@@ -100,19 +97,27 @@ private:
|
||||
fextl::vector<PhysicalRegister> SSAToReg;
|
||||
|
||||
bool IsOld(Ref Node) {
|
||||
return IR->GetID(Node).Value < SSAToNewSSA.size();
|
||||
return IR->GetID(Node).Value < PreferredReg.size();
|
||||
};
|
||||
|
||||
// Return the New node (if it exists) for an Old node, else the Old node.
|
||||
Ref Map(Ref Old) {
|
||||
LOGMAN_THROW_A_FMT(IsOld(Old), "Pre-condition");
|
||||
|
||||
return SSAToNewSSA[IR->GetID(Old).Value] ?: Old;
|
||||
if (SSAToNewSSA.empty()) {
|
||||
return Old;
|
||||
} else {
|
||||
return SSAToNewSSA[IR->GetID(Old).Value] ?: Old;
|
||||
}
|
||||
};
|
||||
|
||||
// Return the Old node for a possibly-remapped node.
|
||||
Ref Unmap(Ref Node) {
|
||||
return NewSSAToSSA[IR->GetID(Node).Value] ?: Node;
|
||||
if (NewSSAToSSA.empty()) {
|
||||
return Node;
|
||||
} else {
|
||||
return NewSSAToSSA[IR->GetID(Node).Value] ?: Node;
|
||||
}
|
||||
};
|
||||
|
||||
// Record a remapping of Old to New.
|
||||
@@ -125,6 +130,10 @@ private:
|
||||
LOGMAN_THROW_A_FMT(NewID >= NewSSAToSSA.size(), "Brand new SSA def");
|
||||
NewSSAToSSA.resize(NewID + 1, 0);
|
||||
|
||||
if (SSAToNewSSA.empty()) {
|
||||
SSAToNewSSA.resize(PreferredReg.size(), nullptr);
|
||||
}
|
||||
|
||||
SSAToNewSSA[OldID] = New;
|
||||
NewSSAToSSA[NewID] = Old;
|
||||
|
||||
@@ -198,7 +207,7 @@ private:
|
||||
PhysicalRegister Reg = SSAToReg[IR->GetID(Map(Old)).Value];
|
||||
RegisterClass* Class = GetClass(Reg);
|
||||
|
||||
return (Class->Allocated & GetRegBits(Reg)) && Class->RegToSSA[Reg.Reg] == Old;
|
||||
return (Class->Available & GetRegBits(Reg)) == 0 && Class->RegToSSA[Reg.Reg] == Old;
|
||||
};
|
||||
|
||||
void FreeReg(PhysicalRegister Reg) {
|
||||
@@ -206,10 +215,8 @@ private:
|
||||
uint32_t RegBits = GetRegBits(Reg);
|
||||
|
||||
LOGMAN_THROW_AA_FMT(!(Class->Available & RegBits), "Register double-free");
|
||||
LOGMAN_THROW_AA_FMT((Class->Allocated & RegBits), "Register double-free");
|
||||
|
||||
Class->Available |= RegBits;
|
||||
Class->Allocated &= ~RegBits;
|
||||
};
|
||||
|
||||
bool HasSource(IROp_Header* I, Ref Old) {
|
||||
@@ -228,11 +235,14 @@ private:
|
||||
};
|
||||
|
||||
Ref DecodeSRANode(const IROp_Header* IROp, Ref Node) {
|
||||
if (IROp->Op == OP_LOADREGISTER) {
|
||||
if (IROp->Op == OP_LOADREGISTER || IROp->Op == OP_LOADPF || IROp->Op == OP_LOADAF) {
|
||||
return Node;
|
||||
} else if (IROp->Op == OP_STOREREGISTER) {
|
||||
const IROp_StoreRegister* Op = IROp->C<IR::IROp_StoreRegister>();
|
||||
return IR->GetNode(Op->Value);
|
||||
} else if (IROp->Op == OP_STOREPF || IROp->Op == OP_STOREAF) {
|
||||
const IROp_StorePF* Op = IROp->C<IR::IROp_StorePF>();
|
||||
return IR->GetNode(Op->Value);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@@ -242,6 +252,8 @@ private:
|
||||
RegisterClassType Class;
|
||||
uint8_t Reg;
|
||||
|
||||
uint8_t FlagOffset = Classes[GPRFixedClass.Val].Count - 2;
|
||||
|
||||
if (IROp->Op == OP_LOADREGISTER) {
|
||||
const IROp_LoadRegister* Op = IROp->C<IR::IROp_LoadRegister>();
|
||||
|
||||
@@ -253,17 +265,15 @@ private:
|
||||
|
||||
Class = Op->Class;
|
||||
Reg = Op->Reg;
|
||||
} else if (IROp->Op == OP_LOADPF || IROp->Op == OP_STOREPF) {
|
||||
return PhysicalRegister {GPRFixedClass, FlagOffset};
|
||||
} else if (IROp->Op == OP_LOADAF || IROp->Op == OP_STOREAF) {
|
||||
return PhysicalRegister {GPRFixedClass, (uint8_t)(FlagOffset + 1)};
|
||||
}
|
||||
|
||||
LOGMAN_THROW_A_FMT(Class == GPRClass || Class == FPRClass, "SRA classes");
|
||||
uint8_t FlagOffset = Classes[GPRFixedClass.Val].Count - 2;
|
||||
|
||||
if (Class == FPRClass) {
|
||||
return PhysicalRegister {FPRFixedClass, Reg};
|
||||
} else if (Reg == Core::CPUState::PF_AS_GREG) {
|
||||
return PhysicalRegister {GPRFixedClass, FlagOffset};
|
||||
} else if (Reg == Core::CPUState::AF_AS_GREG) {
|
||||
return PhysicalRegister {GPRFixedClass, (uint8_t)(FlagOffset + 1)};
|
||||
} else {
|
||||
return PhysicalRegister {GPRFixedClass, Reg};
|
||||
}
|
||||
@@ -280,8 +290,9 @@ private:
|
||||
Ref Candidate = nullptr;
|
||||
uint32_t BestDistance = UINT32_MAX;
|
||||
uint8_t BestReg = ~0;
|
||||
uint32_t Allocated = ((1u << Class->Count) - 1) & ~Class->Available;
|
||||
|
||||
foreach_bit(i, Class->Allocated) {
|
||||
foreach_bit(i, Allocated) {
|
||||
Ref Old = Class->RegToSSA[i];
|
||||
|
||||
LOGMAN_THROW_AA_FMT(Old != nullptr, "Invariant3");
|
||||
@@ -347,10 +358,8 @@ private:
|
||||
uint32_t RegBits = GetRegBits(Reg);
|
||||
|
||||
LOGMAN_THROW_AA_FMT((Class->Available & RegBits) == RegBits, "Precondition");
|
||||
LOGMAN_THROW_AA_FMT(!(Class->Allocated & RegBits), "Precondition");
|
||||
|
||||
Class->Available &= ~RegBits;
|
||||
Class->Allocated |= (1u << Reg.Reg);
|
||||
Class->RegToSSA[Reg.Reg] = Unmap(Node);
|
||||
|
||||
if (Index >= SSAToReg.size()) {
|
||||
@@ -419,8 +428,7 @@ private:
|
||||
RegisterClassType ClassType = GetRegClassFromNode(IR, IROp);
|
||||
RegisterClass* Class = &Classes[ClassType];
|
||||
|
||||
// Spill to make room in the register file. Free registers need not be
|
||||
// contiguous, we'll shuffle later.
|
||||
// Spill to make room in the register file.
|
||||
if (!Class->Available) {
|
||||
IREmit->SetWriteCursorBefore(CodeNode);
|
||||
SpillReg(Class, Pivot);
|
||||
@@ -458,9 +466,8 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) {
|
||||
auto IR_ = IREmit->ViewIR();
|
||||
IR = &IR_;
|
||||
|
||||
// SSAToNewSSA, NewSSAToSSA allocated on first-use
|
||||
PreferredReg.resize(IR->GetSSACount(), PhysicalRegister::Invalid());
|
||||
SSAToNewSSA.resize(IR->GetSSACount(), nullptr);
|
||||
NewSSAToSSA.resize(IR->GetSSACount(), nullptr);
|
||||
SSAToReg.resize(IR->GetSSACount(), PhysicalRegister::Invalid());
|
||||
NextUses.resize(IR->GetSSACount(), 0);
|
||||
SpillSlotCount = 0;
|
||||
@@ -473,7 +480,6 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) {
|
||||
// At the start of each block, all registers are available.
|
||||
for (auto& Class : Classes) {
|
||||
Class.Available = (1u << Class.Count) - 1;
|
||||
Class.Allocated = 0;
|
||||
}
|
||||
|
||||
SourcesNextUses.clear();
|
||||
@@ -500,9 +506,9 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) {
|
||||
// of SourcesNextUses is consistent. The forward pass can then iterate
|
||||
// forwards and just flip the order.
|
||||
const uint8_t NumArgs = IR::GetRAArgs(IROp->Op);
|
||||
for (int8_t i = NumArgs - 1; i >= 0; --i) {
|
||||
for (int i = NumArgs - 1; i >= 0; --i) {
|
||||
const auto& Arg = IROp->Args[i];
|
||||
if (IsValidArg(Arg)) {
|
||||
if (!Arg.IsInvalid()) {
|
||||
const uint32_t Index = Arg.ID().Value;
|
||||
|
||||
SourcesNextUses.push_back(NextUses[Index]);
|
||||
@@ -511,7 +517,7 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) {
|
||||
}
|
||||
|
||||
// Record preferred registers for SRA. We also record the Node accessing
|
||||
// each register, used below. Since we initialized Class->Allocated = 0,
|
||||
// each register, used below. Since we initialized Class->Available,
|
||||
// RegToSSA is otherwise undefined so we can stash our temps there.
|
||||
if (auto Node = DecodeSRANode(IROp, CodeNode); Node != nullptr) {
|
||||
auto Reg = DecodeSRAReg(IROp, Node);
|
||||
@@ -564,7 +570,7 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) {
|
||||
auto Reg = DecodeSRAReg(IROp, Node);
|
||||
RegisterClass* Class = &Classes[Reg.Class];
|
||||
|
||||
if (Class->Allocated & (1u << Reg.Reg)) {
|
||||
if (!(Class->Available & (1u << Reg.Reg))) {
|
||||
Ref Old = Class->RegToSSA[Reg.Reg];
|
||||
|
||||
LOGMAN_THROW_A_FMT(IsOld(Old), "RegToSSA invariant");
|
||||
@@ -612,21 +618,24 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) {
|
||||
}
|
||||
|
||||
for (auto s = 0; s < IR::GetRAArgs(IROp->Op); ++s) {
|
||||
if (!IsValidArg(IROp->Args[s])) {
|
||||
if (IROp->Args[s].IsInvalid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SourceIndex--;
|
||||
LOGMAN_THROW_AA_FMT(SourceIndex >= 0, "Consistent source count");
|
||||
|
||||
Ref Old = IR->GetNode(IROp->Args[s]);
|
||||
LOGMAN_THROW_A_FMT(IsInRegisterFile(Old), "sources in file");
|
||||
|
||||
if (!SourcesNextUses[SourceIndex]) {
|
||||
FreeReg(SSAToReg[IR->GetID(Map(Old)).Value]);
|
||||
Ref Old = IR->GetNode(IROp->Args[s]);
|
||||
auto Reg = SSAToReg[IR->GetID(Map(Old)).Value];
|
||||
|
||||
if (!Reg.IsInvalid()) {
|
||||
LOGMAN_THROW_A_FMT(IsInRegisterFile(Old), "sources in file");
|
||||
FreeReg(Reg);
|
||||
}
|
||||
}
|
||||
|
||||
NextUses[IR->GetID(Old).Value] = SourcesNextUses[SourceIndex];
|
||||
NextUses[IROp->Args[s].ID().Value] = SourcesNextUses[SourceIndex];
|
||||
}
|
||||
|
||||
// Assign destinations.
|
||||
@@ -635,11 +644,13 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) {
|
||||
}
|
||||
|
||||
// Remap sources last, since AssignReg can shuffle.
|
||||
for (auto s = 0; s < IR::GetRAArgs(IROp->Op); ++s) {
|
||||
Ref Remapped = SSAToNewSSA[IR->GetID(IR->GetNode(IROp->Args[s])).Value];
|
||||
if (!SSAToNewSSA.empty()) {
|
||||
for (auto s = 0; s < IR::GetRAArgs(IROp->Op); ++s) {
|
||||
Ref Remapped = SSAToNewSSA[IROp->Args[s].ID().Value];
|
||||
|
||||
if (Remapped != nullptr) {
|
||||
IREmit->ReplaceNodeArgument(CodeNode, s, Remapped);
|
||||
if (Remapped != nullptr) {
|
||||
IREmit->ReplaceNodeArgument(CodeNode, s, Remapped);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -644,7 +644,6 @@ void X87StackOptimization::Run(IREmitter* Emit) {
|
||||
|
||||
case OP_F80SINSTACK: {
|
||||
HandleUnop(OP_F64SIN, false, OP_F80SIN);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,24 +19,11 @@
|
||||
#include <sys/user.h>
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_JEMALLOC
|
||||
#include <jemalloc/jemalloc.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <memory>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
extern "C" {
|
||||
typedef void* (*mmap_hook_type)(void* addr, size_t length, int prot, int flags, int fd, off_t offset);
|
||||
typedef int (*munmap_hook_type)(void* addr, size_t length);
|
||||
|
||||
#ifdef ENABLE_JEMALLOC
|
||||
extern mmap_hook_type je___mmap_hook;
|
||||
extern munmap_hook_type je___munmap_hook;
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace fextl::pmr {
|
||||
static fextl::pmr::default_resource FEXDefaultResource;
|
||||
std::pmr::memory_resource* get_default_resource() {
|
||||
@@ -127,19 +114,15 @@ void ReenableSBRKAllocations(void* Ptr) {
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
void SetupHooks() {
|
||||
Alloc64 = Alloc::OSAllocator::Create64BitAllocator();
|
||||
#ifdef ENABLE_JEMALLOC
|
||||
je___mmap_hook = FEX_mmap;
|
||||
je___munmap_hook = FEX_munmap;
|
||||
#endif
|
||||
SetJemallocMmapHook(FEX_mmap);
|
||||
SetJemallocMunmapHook(FEX_munmap);
|
||||
FEXCore::Allocator::mmap = FEX_mmap;
|
||||
FEXCore::Allocator::munmap = FEX_munmap;
|
||||
}
|
||||
|
||||
void ClearHooks() {
|
||||
#ifdef ENABLE_JEMALLOC
|
||||
je___mmap_hook = ::mmap;
|
||||
je___munmap_hook = ::munmap;
|
||||
#endif
|
||||
SetJemallocMmapHook(::mmap);
|
||||
SetJemallocMunmapHook(::munmap);
|
||||
FEXCore::Allocator::mmap = ::mmap;
|
||||
FEXCore::Allocator::munmap = ::munmap;
|
||||
|
||||
@@ -239,7 +222,7 @@ fextl::vector<MemoryRegion> CollectMemoryGaps(uintptr_t Begin, uintptr_t End, in
|
||||
|
||||
// Parse mapped region in the format "fffff7cc3000-fffff7cc4000 r--p ..."
|
||||
{
|
||||
uintptr_t RegionBegin;
|
||||
uintptr_t RegionBegin {};
|
||||
auto result = std::from_chars(Cursor, line_end, RegionBegin, 16);
|
||||
LogMan::Throw::AFmt(result.ec == std::errc {} && *result.ptr == '-', "Unexpected line format");
|
||||
Cursor = result.ptr + 1;
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#ifdef ENABLE_JEMALLOC
|
||||
#include <jemalloc/jemalloc.h>
|
||||
#endif
|
||||
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace FEXCore::Allocator {
|
||||
|
||||
#ifndef _WIN32
|
||||
using mmap_hook_type = void* (*)(void* addr, size_t length, int prot, int flags, int fd, off_t offset);
|
||||
using munmap_hook_type = int (*)(void* addr, size_t length);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_JEMALLOC
|
||||
void* malloc(size_t size) {
|
||||
return ::je_malloc(size);
|
||||
}
|
||||
void* calloc(size_t n, size_t size) {
|
||||
return ::je_calloc(n, size);
|
||||
}
|
||||
void* memalign(size_t align, size_t s) {
|
||||
return ::je_memalign(align, s);
|
||||
}
|
||||
void* valloc(size_t size) {
|
||||
return ::je_valloc(size);
|
||||
}
|
||||
int posix_memalign(void** r, size_t a, size_t s) {
|
||||
return ::je_posix_memalign(r, a, s);
|
||||
}
|
||||
void* realloc(void* ptr, size_t size) {
|
||||
return ::je_realloc(ptr, size);
|
||||
}
|
||||
void free(void* ptr) {
|
||||
return ::je_free(ptr);
|
||||
}
|
||||
size_t malloc_usable_size(void* ptr) {
|
||||
return ::je_malloc_usable_size(ptr);
|
||||
}
|
||||
void* aligned_alloc(size_t a, size_t s) {
|
||||
return ::je_aligned_alloc(a, s);
|
||||
}
|
||||
void aligned_free(void* ptr) {
|
||||
return ::je_free(ptr);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
extern "C" mmap_hook_type je___mmap_hook;
|
||||
extern "C" munmap_hook_type je___munmap_hook;
|
||||
|
||||
void SetJemallocMmapHook(mmap_hook_type Hook) {
|
||||
je___mmap_hook = Hook;
|
||||
}
|
||||
void SetJemallocMunmapHook(munmap_hook_type Hook) {
|
||||
je___munmap_hook = Hook;
|
||||
}
|
||||
#endif
|
||||
|
||||
#elif defined(_WIN32)
|
||||
#error "Tried building _WIN32 without jemalloc"
|
||||
|
||||
#else
|
||||
void* malloc(size_t size) {
|
||||
return ::malloc(size);
|
||||
}
|
||||
void* calloc(size_t n, size_t size) {
|
||||
return ::calloc(n, size);
|
||||
}
|
||||
void* memalign(size_t align, size_t s) {
|
||||
return ::memalign(align, s);
|
||||
}
|
||||
void* valloc(size_t size) {
|
||||
return ::valloc(size);
|
||||
}
|
||||
int posix_memalign(void** r, size_t a, size_t s) {
|
||||
return ::posix_memalign(r, a, s);
|
||||
}
|
||||
void* realloc(void* ptr, size_t size) {
|
||||
return ::realloc(ptr, size);
|
||||
}
|
||||
void free(void* ptr) {
|
||||
return ::free(ptr);
|
||||
}
|
||||
size_t malloc_usable_size(void* ptr) {
|
||||
return ::malloc_usable_size(ptr);
|
||||
}
|
||||
void* aligned_alloc(size_t a, size_t s) {
|
||||
return ::aligned_alloc(a, s);
|
||||
}
|
||||
void aligned_free(void* ptr) {
|
||||
return ::free(ptr);
|
||||
}
|
||||
|
||||
void SetJemallocMmapHook(mmap_hook_type) {}
|
||||
void SetJemallocMunmapHook(munmap_hook_type) {}
|
||||
|
||||
#endif
|
||||
} // namespace FEXCore::Allocator
|
||||
@@ -11,7 +11,4 @@ To use this factory, one must override the provided `FEXCore::CPU::CPUBackend` c
|
||||
|
||||
`FEXCore::CPU::CPUBackend::GetName` - Returns an `std::string` for the name of this core
|
||||
`FEXCore::CPU::CPUBackend::CompileCode` - Provides the CPUBackend with potentially an IR and DebugData for compiling code. Returns a pointer that needs to be long lasting to a piece of code that will be executed for the particular RIP.
|
||||
Both IR and DebugData can be null if `NeedsOpDispatch` returns false
|
||||
`FEXCore::CPU::CPUBackend::MapRegion` - This function needs to be implemented if the CPUBackend needs to map host facing memory in to the backend. Allows setting up virtual memory mapping if required
|
||||
`FEXCore::CPU::CPUBackend::Initialize` - Called after the guest memory is initialized and all state is ready for the code to start initializing. Gets called just before the CPUBackend starts executing code for the first time.
|
||||
`FEXCore::CPU::CPUBackend::NeedsOpDispatch` - Tells FEXCore if the backend needs the FEXCore IR and DebugData provided to it. This can be useful if FEXCore hits something it doesn't understand but it doesn't matter since the CPUBackend can still understand it from raw x86-64 (ex VM based CPU backend).
|
||||
@@ -18,18 +18,6 @@
|
||||
|
||||
namespace FEXCore::Config {
|
||||
namespace Handler {
|
||||
static inline std::optional<fextl::string> CoreHandler(std::string_view Value) {
|
||||
if (Value == "irjit") {
|
||||
return "0";
|
||||
}
|
||||
#ifdef _M_X86_64
|
||||
else if (Value == "host") {
|
||||
return "1";
|
||||
}
|
||||
#endif
|
||||
return "0";
|
||||
}
|
||||
|
||||
static inline std::optional<fextl::string> SMCCheckHandler(std::string_view Value) {
|
||||
if (Value == "none") {
|
||||
return "0";
|
||||
@@ -60,11 +48,6 @@ enum ConfigOption {
|
||||
#define ENUMDEFINES
|
||||
#include <FEXCore/Config/ConfigOptions.inl>
|
||||
|
||||
enum ConfigCore {
|
||||
CONFIG_IRJIT,
|
||||
CONFIG_CUSTOM,
|
||||
};
|
||||
|
||||
enum ConfigSMCChecks {
|
||||
CONFIG_SMC_NONE,
|
||||
CONFIG_SMC_MTRACK,
|
||||
@@ -145,11 +128,11 @@ namespace DefaultValues {
|
||||
#undef P
|
||||
} // namespace DefaultValues
|
||||
|
||||
FEX_DEFAULT_VISIBILITY void SetDataDirectory(std::string_view Path);
|
||||
FEX_DEFAULT_VISIBILITY void SetDataDirectory(std::string_view Path, bool Global);
|
||||
FEX_DEFAULT_VISIBILITY void SetConfigDirectory(const std::string_view Path, bool Global);
|
||||
FEX_DEFAULT_VISIBILITY void SetConfigFileLocation(std::string_view Path, bool Global);
|
||||
|
||||
FEX_DEFAULT_VISIBILITY const fextl::string& GetDataDirectory();
|
||||
FEX_DEFAULT_VISIBILITY const fextl::string& GetDataDirectory(bool Global = false);
|
||||
FEX_DEFAULT_VISIBILITY const fextl::string& GetConfigDirectory(bool Global);
|
||||
FEX_DEFAULT_VISIBILITY const fextl::string& GetConfigFileLocation(bool Global = false);
|
||||
FEX_DEFAULT_VISIBILITY fextl::string GetApplicationConfig(const std::string_view Program, bool Global);
|
||||
|
||||
@@ -14,14 +14,13 @@
|
||||
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <span>
|
||||
|
||||
namespace FEXCore {
|
||||
class CodeLoader;
|
||||
struct HostFeatures;
|
||||
class ForkableSharedMutex;
|
||||
class ThunkHandler;
|
||||
} // namespace FEXCore
|
||||
|
||||
namespace FEXCore::Core {
|
||||
@@ -59,23 +58,6 @@ enum OperatingMode {
|
||||
MODE_64BIT,
|
||||
};
|
||||
|
||||
struct CustomIRResult {
|
||||
void* Creator;
|
||||
void* Data;
|
||||
|
||||
explicit operator bool() const noexcept {
|
||||
return !lock;
|
||||
}
|
||||
|
||||
CustomIRResult(std::unique_lock<std::shared_mutex>&& lock, void* Creator, void* Data)
|
||||
: Creator(Creator)
|
||||
, Data(Data)
|
||||
, lock(std::move(lock)) {}
|
||||
|
||||
private:
|
||||
std::unique_lock<std::shared_mutex> lock;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief IR Serialization handler class.
|
||||
*/
|
||||
@@ -87,19 +69,8 @@ public:
|
||||
virtual void Close() = 0;
|
||||
};
|
||||
|
||||
struct VDSOSigReturn {
|
||||
void* VDSO_kernel_sigreturn;
|
||||
void* VDSO_kernel_rt_sigreturn;
|
||||
};
|
||||
|
||||
struct ThreadsState {
|
||||
FEXCore::Core::InternalThreadState* ParentThread;
|
||||
fextl::vector<FEXCore::Core::InternalThreadState*>* Threads;
|
||||
};
|
||||
|
||||
using CodeRangeInvalidationFn = std::function<void(uint64_t start, uint64_t Length)>;
|
||||
|
||||
using CustomCPUFactoryType = std::function<fextl::unique_ptr<CPU::CPUBackend>(Context*, Core::InternalThreadState* Thread)>;
|
||||
using CustomIREntrypointHandler = std::function<void(uintptr_t Entrypoint, IR::IREmitter*)>;
|
||||
|
||||
using ExitHandler = std::function<void(Core::InternalThreadState* Thread, ExitReason)>;
|
||||
@@ -154,16 +125,6 @@ public:
|
||||
FEX_DEFAULT_VISIBILITY virtual void CompileRIP(FEXCore::Core::InternalThreadState* Thread, uint64_t GuestRIP) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual void CompileRIPCount(FEXCore::Core::InternalThreadState* Thread, uint64_t GuestRIP, uint64_t MaxInst) = 0;
|
||||
|
||||
/**
|
||||
* @brief Allows the frontend to pass in a custom CPUBackend creation factory
|
||||
*
|
||||
* This allows the frontend to have its own frontend. Typically for debugging
|
||||
*
|
||||
* @param CTX The context that we created
|
||||
* @param Factory The factory that the context will call if the DefaultCore config ise set to CUSTOM
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY virtual void SetCustomCPUBackendFactory(CustomCPUFactoryType Factory) = 0;
|
||||
|
||||
FEX_DEFAULT_VISIBILITY virtual void HandleCallback(FEXCore::Core::InternalThreadState* Thread, uint64_t RIP) = 0;
|
||||
|
||||
///< State reconstruction helpers
|
||||
@@ -204,8 +165,8 @@ public:
|
||||
* @return A new InternalThreadState object for using with a new guest thread.
|
||||
*/
|
||||
|
||||
FEX_DEFAULT_VISIBILITY virtual FEXCore::Core::InternalThreadState*
|
||||
CreateThread(uint64_t InitialRIP, uint64_t StackPointer, FEXCore::Core::CPUState* NewThreadState = nullptr, uint64_t ParentTID = 0) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual FEXCore::Core::InternalThreadState* CreateThread(
|
||||
uint64_t InitialRIP, uint64_t StackPointer, const FEXCore::Core::CPUState* NewThreadState = nullptr, uint64_t ParentTID = 0) = 0;
|
||||
|
||||
FEX_DEFAULT_VISIBILITY virtual void ExecutionThread(FEXCore::Core::InternalThreadState* Thread) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual void DestroyThread(FEXCore::Core::InternalThreadState* Thread, bool NeedsTLSUninstall = false) = 0;
|
||||
@@ -215,6 +176,7 @@ public:
|
||||
#endif
|
||||
FEX_DEFAULT_VISIBILITY virtual void SetSignalDelegator(FEXCore::SignalDelegator* SignalDelegation) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual void SetSyscallHandler(FEXCore::HLE::SyscallHandler* Handler) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual void SetThunkHandler(FEXCore::ThunkHandler* Handler) = 0;
|
||||
|
||||
FEX_DEFAULT_VISIBILITY virtual FEXCore::CPUID::FunctionResults RunCPUIDFunction(uint32_t Function, uint32_t Leaf) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual FEXCore::CPUID::XCRResults RunXCRFunction(uint32_t Function) = 0;
|
||||
@@ -251,14 +213,6 @@ public:
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY virtual bool IsAddressInCodeBuffer(FEXCore::Core::InternalThreadState* Thread, uintptr_t Address) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Allows the frontend to register its own thunk handlers independent of what is controlled in the backend.
|
||||
*
|
||||
* @param CTX A valid non-null context instance.
|
||||
* @param Definitions A vector of thunk definitions that the frontend controls
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY virtual void AppendThunkDefinitions(std::span<const FEXCore::IR::ThunkDefinition> Definitions) = 0;
|
||||
|
||||
/**
|
||||
* @brief Informs the context if hardware TSO is supported.
|
||||
* Once hardware TSO is enabled, then TSO emulation through atomics is disabled and relies on the hardware.
|
||||
@@ -275,6 +229,14 @@ public:
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY virtual void EnableExitOnHLT() = 0;
|
||||
|
||||
/**
|
||||
* @brief Adds a new Thunk trampoline handler
|
||||
*
|
||||
* @param Entrypoint The guest PC that the custom thunk trampoline IR handler will be installed at.
|
||||
* @param GuestThunkEntrypoint The thunk entrypoint that the IR handler will redirect to.
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY virtual void AddThunkTrampolineIRHandler(uintptr_t Entrypoint, uintptr_t GuestThunkEntrypoint) = 0;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
|
||||
@@ -40,8 +40,6 @@ public:
|
||||
virtual ~SignalDelegator() = default;
|
||||
|
||||
struct SignalDelegatorConfig {
|
||||
bool SupportsAVX {};
|
||||
|
||||
// Dispatcher information
|
||||
uint64_t DispatcherBegin;
|
||||
uint64_t DispatcherEnd;
|
||||
|
||||
+2
-8
@@ -7,13 +7,12 @@ $end_info$
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Interface/IR/IR.h"
|
||||
|
||||
#include <FEXCore/fextl/memory.h>
|
||||
#include <FEXCore/fextl/vector.h>
|
||||
#include <FEXCore/IR/IR.h>
|
||||
|
||||
namespace FEXCore::Context {
|
||||
class ContextImpl;
|
||||
class Context;
|
||||
}
|
||||
|
||||
namespace FEXCore::Core {
|
||||
@@ -30,11 +29,6 @@ typedef void ThunkedFunction(void* ArgsRv);
|
||||
class ThunkHandler {
|
||||
public:
|
||||
virtual ThunkedFunction* LookupThunk(const IR::SHA256Sum& sha256) = 0;
|
||||
virtual void RegisterTLSState(FEXCore::Core::InternalThreadState* Thread) = 0;
|
||||
virtual ~ThunkHandler() {}
|
||||
|
||||
static fextl::unique_ptr<ThunkHandler> Create();
|
||||
|
||||
virtual void AppendThunkDefinitions(std::span<const FEXCore::IR::ThunkDefinition> Definitions) = 0;
|
||||
};
|
||||
}; // namespace FEXCore
|
||||
@@ -10,13 +10,13 @@
|
||||
#include <FEXCore/fextl/memory.h>
|
||||
#include <FEXCore/fextl/vector.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <shared_mutex>
|
||||
#include <type_traits>
|
||||
|
||||
namespace FEXCore {
|
||||
class LookupCache;
|
||||
class CompileService;
|
||||
struct JITSymbolBuffer;
|
||||
} // namespace FEXCore
|
||||
|
||||
namespace FEXCore::Context {
|
||||
@@ -25,7 +25,6 @@ class Context;
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
class CPUBackend;
|
||||
union Relocation;
|
||||
} // namespace FEXCore::CPU
|
||||
|
||||
namespace FEXCore::Frontend {
|
||||
@@ -38,47 +37,6 @@ class PassManager;
|
||||
} // namespace FEXCore::IR
|
||||
|
||||
namespace FEXCore::Core {
|
||||
struct DebugDataSubblock {
|
||||
uint32_t HostCodeOffset;
|
||||
uint32_t HostCodeSize;
|
||||
};
|
||||
|
||||
struct DebugDataGuestOpcode {
|
||||
uint64_t GuestEntryOffset;
|
||||
ptrdiff_t HostEntryOffset;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Contains debug data for a block of code for later debugger analysis
|
||||
*
|
||||
* Needs to remain around for as long as the code could be executed at least
|
||||
*/
|
||||
struct DebugData : public FEXCore::Allocator::FEXAllocOperators {
|
||||
uint64_t HostCodeSize; ///< The size of the code generated in the host JIT
|
||||
fextl::vector<DebugDataSubblock> Subblocks;
|
||||
fextl::vector<DebugDataGuestOpcode> GuestOpcodes;
|
||||
fextl::vector<FEXCore::CPU::Relocation>* Relocations;
|
||||
};
|
||||
|
||||
// Buffered JIT symbol tracking.
|
||||
struct JITSymbolBuffer {
|
||||
// Maximum buffer size to ensure we are a page in size.
|
||||
constexpr static size_t BUFFER_SIZE = 4096 - (8 * 2);
|
||||
// Maximum distance until the end of the buffer to do a write.
|
||||
constexpr static size_t NEEDS_WRITE_DISTANCE = BUFFER_SIZE - 64;
|
||||
// Maximum time threshhold to wait before a buffer write occurs.
|
||||
constexpr static std::chrono::milliseconds MAXIMUM_THRESHOLD {100};
|
||||
|
||||
JITSymbolBuffer()
|
||||
: LastWrite {std::chrono::steady_clock::now()} {}
|
||||
// stead_clock to ensure a monotonic increasing clock.
|
||||
// In highly stressed situations this can still cause >2% CPU time in vdso_clock_gettime.
|
||||
// If we need lower CPU time when JIT symbols are enabled then FEX can read the cycle counter directly.
|
||||
std::chrono::steady_clock::time_point LastWrite {};
|
||||
size_t Offset {};
|
||||
char Buffer[BUFFER_SIZE] {};
|
||||
};
|
||||
static_assert(sizeof(JITSymbolBuffer) == 4096, "Ensure this is one page in size");
|
||||
|
||||
// Special-purpose replacement for std::unique_ptr to allow InternalThreadState to be standard layout.
|
||||
// Since a NonMovableUniquePtr is neither copyable nor movable, its only function is to own and release the contained object.
|
||||
|
||||
@@ -72,6 +72,8 @@ public:
|
||||
return OSABI;
|
||||
}
|
||||
virtual void MarkGuestExecutableRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Start, uint64_t Length) {}
|
||||
virtual void MarkOvercommitRange(uint64_t Start, uint64_t Length) {}
|
||||
virtual void UnmarkOvercommitRange(uint64_t Start, uint64_t Length) {}
|
||||
virtual AOTIRCacheEntryLookupResult LookupAOTIRCacheEntry(FEXCore::Core::InternalThreadState* Thread, uint64_t GuestAddr) = 0;
|
||||
|
||||
virtual SourcecodeResolver* GetSourcecodeResolver() {
|
||||
|
||||
@@ -4,16 +4,13 @@
|
||||
#include <FEXCore/Utils/EnumOperators.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
|
||||
#ifndef ENABLE_JEMALLOC
|
||||
#ifndef _WIN32
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <sys/mman.h>
|
||||
#else
|
||||
#define NTDDI_VERSION 0x0A000005
|
||||
#include <memoryapi.h>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include <new>
|
||||
@@ -21,24 +18,6 @@
|
||||
#include <cstdint>
|
||||
#include <sys/types.h>
|
||||
|
||||
extern "C" {
|
||||
// jemalloc defines nothrow on its internal C function signatures.
|
||||
#ifdef ENABLE_JEMALLOC
|
||||
#define JEMALLOC_NOTHROW __attribute__((nothrow))
|
||||
// Forward declare jemalloc functions so we don't need to pull in the jemalloc header in to the public API.
|
||||
FEX_DEFAULT_VISIBILITY JEMALLOC_NOTHROW extern void* je_malloc(size_t size);
|
||||
FEX_DEFAULT_VISIBILITY JEMALLOC_NOTHROW extern void* je_calloc(size_t n, size_t size);
|
||||
FEX_DEFAULT_VISIBILITY JEMALLOC_NOTHROW extern void* je_memalign(size_t align, size_t s);
|
||||
FEX_DEFAULT_VISIBILITY JEMALLOC_NOTHROW extern void* je_valloc(size_t size);
|
||||
FEX_DEFAULT_VISIBILITY JEMALLOC_NOTHROW extern int je_posix_memalign(void** r, size_t a, size_t s);
|
||||
FEX_DEFAULT_VISIBILITY JEMALLOC_NOTHROW extern void* je_realloc(void* ptr, size_t size);
|
||||
FEX_DEFAULT_VISIBILITY JEMALLOC_NOTHROW extern void je_free(void* ptr);
|
||||
FEX_DEFAULT_VISIBILITY JEMALLOC_NOTHROW extern size_t je_malloc_usable_size(void* ptr);
|
||||
FEX_DEFAULT_VISIBILITY JEMALLOC_NOTHROW extern void* je_aligned_alloc(size_t a, size_t s);
|
||||
#undef JEMALLOC_NOTHROW
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace FEXCore::Allocator {
|
||||
enum class ProtectOptions : uint32_t {
|
||||
None = 0,
|
||||
@@ -49,35 +28,38 @@ enum class ProtectOptions : uint32_t {
|
||||
FEX_DEF_NUM_OPS(ProtectOptions)
|
||||
|
||||
#ifdef _WIN32
|
||||
inline void* VirtualAlloc(void* Base, size_t Size, bool Execute = false) {
|
||||
inline void* VirtualAlloc(void* Base, size_t Size, bool Execute = false, bool Commit = true) {
|
||||
// Allocate top-down to avoid polluting the lower VA space, as even on 64-bit some programs (i.e. LuaJIT) require allocations below 4GB.
|
||||
DWORD Flags = (Commit ? MEM_COMMIT : 0) | MEM_RESERVE | MEM_TOP_DOWN;
|
||||
#ifdef _M_ARM_64EC
|
||||
MEM_EXTENDED_PARAMETER Parameter {};
|
||||
if (Execute) {
|
||||
Parameter.Type = MemExtendedParameterAttributeFlags;
|
||||
Parameter.ULong64 = MEM_EXTENDED_PARAMETER_EC_CODE;
|
||||
};
|
||||
return ::VirtualAlloc2(nullptr, Base, Size, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, Execute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE,
|
||||
Execute ? &Parameter : nullptr, Execute ? 1 : 0);
|
||||
return ::VirtualAlloc2(nullptr, Base, Size, Flags, Execute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, Execute ? &Parameter : nullptr,
|
||||
Execute ? 1 : 0);
|
||||
#else
|
||||
return ::VirtualAlloc(Base, Size, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, Execute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
|
||||
return ::VirtualAlloc(Base, Size, Flags, Execute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void* VirtualAlloc(size_t Size, bool Execute = false) {
|
||||
return VirtualAlloc(nullptr, Size, Execute);
|
||||
inline void* VirtualAlloc(size_t Size, bool Execute = false, bool Commit = true) {
|
||||
return VirtualAlloc(nullptr, Size, Execute, Commit);
|
||||
}
|
||||
|
||||
inline void VirtualFree(void* Ptr, size_t Size) {
|
||||
::VirtualFree(Ptr, 0, MEM_RELEASE);
|
||||
}
|
||||
|
||||
inline void VirtualDontNeed(void* Ptr, size_t Size) {
|
||||
inline void VirtualDontNeed(void* Ptr, size_t Size, bool Recommit = true) {
|
||||
// Zero the page-aligned region, preserving permissions.
|
||||
MEMORY_BASIC_INFORMATION Info;
|
||||
::VirtualQuery(Ptr, &Info, sizeof(Info));
|
||||
::VirtualFree(Ptr, Size, MEM_DECOMMIT);
|
||||
::VirtualAlloc(Ptr, Size, MEM_COMMIT, Info.AllocationProtect);
|
||||
if (Recommit) {
|
||||
::VirtualAlloc(Ptr, Size, MEM_COMMIT, Info.AllocationProtect);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool VirtualProtect(void* Ptr, size_t Size, ProtectOptions options) {
|
||||
@@ -107,18 +89,20 @@ using MUNMAP_Hook = int (*)(void*, size_t);
|
||||
FEX_DEFAULT_VISIBILITY extern MMAP_Hook mmap;
|
||||
FEX_DEFAULT_VISIBILITY extern MUNMAP_Hook munmap;
|
||||
|
||||
inline void* VirtualAlloc(size_t Size, bool Execute = false) {
|
||||
// All commit parameters are ignored here, they are unnecessary as Linux supports overcommit
|
||||
|
||||
inline void* VirtualAlloc(size_t Size, bool Execute = false, bool Commit = true) {
|
||||
return FEXCore::Allocator::mmap(nullptr, Size, PROT_READ | PROT_WRITE | (Execute ? PROT_EXEC : 0), MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
}
|
||||
|
||||
inline void* VirtualAlloc(void* Base, size_t Size, bool Execute = false) {
|
||||
inline void* VirtualAlloc(void* Base, size_t Size, bool Execute = false, bool Commit = true) {
|
||||
return FEXCore::Allocator::mmap(Base, Size, PROT_READ | PROT_WRITE | (Execute ? PROT_EXEC : 0), MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
}
|
||||
|
||||
inline void VirtualFree(void* Ptr, size_t Size) {
|
||||
FEXCore::Allocator::munmap(Ptr, Size);
|
||||
}
|
||||
inline void VirtualDontNeed(void* Ptr, size_t Size) {
|
||||
inline void VirtualDontNeed(void* Ptr, size_t Size, bool Recommit = true) {
|
||||
::madvise(reinterpret_cast<void*>(Ptr), Size, MADV_DONTNEED);
|
||||
}
|
||||
inline bool VirtualProtect(void* Ptr, size_t Size, ProtectOptions options) {
|
||||
@@ -138,108 +122,23 @@ inline bool VirtualProtect(void* Ptr, size_t Size, ProtectOptions options) {
|
||||
|
||||
#endif
|
||||
|
||||
// Memory allocation routines aliased to jemalloc functions.
|
||||
#ifdef ENABLE_JEMALLOC
|
||||
inline void* malloc(size_t size) {
|
||||
return ::je_malloc(size);
|
||||
}
|
||||
inline void* calloc(size_t n, size_t size) {
|
||||
return ::je_calloc(n, size);
|
||||
}
|
||||
inline void* memalign(size_t align, size_t s) {
|
||||
return ::je_memalign(align, s);
|
||||
}
|
||||
inline void* valloc(size_t size) {
|
||||
return ::je_valloc(size);
|
||||
}
|
||||
inline int posix_memalign(void** r, size_t a, size_t s) {
|
||||
return ::je_posix_memalign(r, a, s);
|
||||
}
|
||||
inline void* realloc(void* ptr, size_t size) {
|
||||
return ::je_realloc(ptr, size);
|
||||
}
|
||||
inline void free(void* ptr) {
|
||||
return ::je_free(ptr);
|
||||
}
|
||||
inline size_t malloc_usable_size(void* ptr) {
|
||||
return ::je_malloc_usable_size(ptr);
|
||||
}
|
||||
inline void* aligned_alloc(size_t a, size_t s) {
|
||||
return ::je_aligned_alloc(a, s);
|
||||
}
|
||||
inline void aligned_free(void* ptr) {
|
||||
return ::je_free(ptr);
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
inline void* malloc(size_t size) {
|
||||
return ::malloc(size);
|
||||
}
|
||||
inline void* calloc(size_t n, size_t size) {
|
||||
return ::calloc(n, size);
|
||||
}
|
||||
inline void* memalign(size_t align, size_t s) {
|
||||
return ::_aligned_malloc(s, align);
|
||||
}
|
||||
inline void* valloc(size_t size) {
|
||||
return ::_aligned_malloc(size, 4096);
|
||||
}
|
||||
inline int posix_memalign(void** r, size_t a, size_t s) {
|
||||
void* ptr = _aligned_malloc(s, a);
|
||||
if (ptr) {
|
||||
*r = ptr;
|
||||
}
|
||||
return errno;
|
||||
}
|
||||
inline void* realloc(void* ptr, size_t size) {
|
||||
return ::realloc(ptr, size);
|
||||
}
|
||||
inline void free(void* ptr) {
|
||||
return ::free(ptr);
|
||||
}
|
||||
inline size_t malloc_usable_size(void* ptr) {
|
||||
return ::_msize(ptr);
|
||||
}
|
||||
inline void* aligned_alloc(size_t a, size_t s) {
|
||||
return ::_aligned_malloc(s, a);
|
||||
}
|
||||
inline void aligned_free(void* ptr) {
|
||||
return ::_aligned_free(ptr);
|
||||
}
|
||||
#else
|
||||
inline void* malloc(size_t size) {
|
||||
return ::malloc(size);
|
||||
}
|
||||
inline void* calloc(size_t n, size_t size) {
|
||||
return ::calloc(n, size);
|
||||
}
|
||||
inline void* memalign(size_t align, size_t s) {
|
||||
return ::memalign(align, s);
|
||||
}
|
||||
inline void* valloc(size_t size) {
|
||||
#ifdef __ANDROID__
|
||||
return ::aligned_alloc(4096, size);
|
||||
#else
|
||||
return ::valloc(size);
|
||||
#endif
|
||||
}
|
||||
inline int posix_memalign(void** r, size_t a, size_t s) {
|
||||
return ::posix_memalign(r, a, s);
|
||||
}
|
||||
inline void* realloc(void* ptr, size_t size) {
|
||||
return ::realloc(ptr, size);
|
||||
}
|
||||
inline void free(void* ptr) {
|
||||
return ::free(ptr);
|
||||
}
|
||||
inline size_t malloc_usable_size(void* ptr) {
|
||||
return ::malloc_usable_size(ptr);
|
||||
}
|
||||
inline void* aligned_alloc(size_t a, size_t s) {
|
||||
return ::aligned_alloc(a, s);
|
||||
}
|
||||
inline void aligned_free(void* ptr) {
|
||||
return ::free(ptr);
|
||||
}
|
||||
// Memory allocation routines to be defined externally.
|
||||
// This allows to use jemalloc for emulation while using the normal allocator
|
||||
// for host tools without building FEXCore twice.
|
||||
void* malloc(size_t size);
|
||||
void* calloc(size_t n, size_t size);
|
||||
void* memalign(size_t align, size_t s);
|
||||
void* valloc(size_t size);
|
||||
int posix_memalign(void** r, size_t a, size_t s);
|
||||
void* realloc(void* ptr, size_t size);
|
||||
void free(void* ptr);
|
||||
size_t malloc_usable_size(void* ptr);
|
||||
void* aligned_alloc(size_t a, size_t s);
|
||||
void aligned_free(void* ptr);
|
||||
|
||||
#ifndef _WIN32
|
||||
void SetJemallocMmapHook(void* (*)(void* addr, size_t length, int prot, int flags, int fd, off_t offset));
|
||||
void SetJemallocMunmapHook(int (*)(void* addr, size_t length));
|
||||
#endif
|
||||
|
||||
struct FEXAllocOperators {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
file(GLOB_RECURSE TESTS CONFIGURE_DEPENDS *.cpp)
|
||||
|
||||
set (LIBS fmt::fmt vixl Catch2::Catch2WithMain FEXCore_Base)
|
||||
set (LIBS fmt::fmt vixl Catch2::Catch2WithMain FEXCore_Base JemallocLibs)
|
||||
foreach(TEST ${TESTS})
|
||||
get_filename_component(TEST_NAME ${TEST} NAME_WLE)
|
||||
add_executable(FEXCore_Tests_${TEST_NAME} ${TEST})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
if (COMPILE_VIXL_DISASSEMBLER)
|
||||
file(GLOB_RECURSE TESTS CONFIGURE_DEPENDS *.cpp)
|
||||
|
||||
set (LIBS fmt::fmt vixl Catch2::Catch2WithMain FEXCore_Base)
|
||||
set (LIBS fmt::fmt vixl Catch2::Catch2WithMain FEXCore_Base JemallocLibs)
|
||||
foreach(TEST ${TESTS})
|
||||
get_filename_component(TEST_NAME ${TEST} NAME_WLE)
|
||||
add_executable(Emitter_${TEST_NAME} ${TEST})
|
||||
|
||||
@@ -23,8 +23,11 @@ CursorKind.REQUIRESEXPR = CursorKind(154),
|
||||
# C++2a std::bit_cast expression.
|
||||
CursorKind.BUILTINBITCASTEXPR = CursorKind(280)
|
||||
|
||||
# a concept declaration.
|
||||
CursorKind.CONCEPTDECL = CursorKind(604),
|
||||
try:
|
||||
# a concept declaration.
|
||||
CursorKind.CONCEPTDECL = CursorKind(604),
|
||||
except:
|
||||
pass
|
||||
|
||||
@dataclass
|
||||
class TypeDefinition:
|
||||
|
||||
+16
-11
@@ -243,7 +243,7 @@ void EnvLoader::Load() {
|
||||
// Walk all the environment options and corresponding config option.
|
||||
#define OPT_BASE(type, group, enum, json, default) \
|
||||
Value = GetVar(EnvMap, "FEX_" #enum); \
|
||||
if (Value.has_value()) Set(FEXCore::Config::ConfigOption::CONFIG_##enum, fextl::string(*Value));
|
||||
if (Value.has_value()) Set(FEXCore::Config::ConfigOption::CONFIG_##enum, *Value);
|
||||
#include <FEXCore/Config/ConfigValues.inl>
|
||||
}
|
||||
|
||||
@@ -476,22 +476,26 @@ const char* GetHomeDirectory() {
|
||||
}
|
||||
#endif
|
||||
|
||||
fextl::string GetDataDirectory(const PortableInformation& PortableInfo) {
|
||||
fextl::string GetDataDirectory(bool Global, const PortableInformation& PortableInfo) {
|
||||
const char* DataOverride = getenv("FEX_APP_DATA_LOCATION");
|
||||
|
||||
if (PortableInfo.IsPortable && !DataOverride) {
|
||||
if (PortableInfo.IsPortable && (Global || !DataOverride)) {
|
||||
return fextl::fmt::format("{}fex-emu/", PortableInfo.InterpreterPath);
|
||||
}
|
||||
|
||||
fextl::string DataDir {};
|
||||
const char* HomeDir = GetHomeDirectory();
|
||||
const char* DataXDG = getenv("XDG_DATA_HOME");
|
||||
if (DataOverride) {
|
||||
// Data override will override the complete directory
|
||||
DataDir = DataOverride;
|
||||
if (Global) {
|
||||
DataDir = GLOBAL_DATA_DIRECTORY;
|
||||
} else {
|
||||
DataDir = DataXDG ?: HomeDir;
|
||||
DataDir += "/.fex-emu/";
|
||||
const char* HomeDir = GetHomeDirectory();
|
||||
const char* DataXDG = getenv("XDG_DATA_HOME");
|
||||
if (DataOverride) {
|
||||
// Data override will override the complete directory
|
||||
DataDir = DataOverride;
|
||||
} else {
|
||||
DataDir = DataXDG ?: HomeDir;
|
||||
DataDir += "/.fex-emu/";
|
||||
}
|
||||
}
|
||||
return DataDir;
|
||||
}
|
||||
@@ -531,7 +535,8 @@ fextl::string GetConfigFileLocation(bool Global, const PortableInformation& Port
|
||||
}
|
||||
|
||||
void InitializeConfigs(const PortableInformation& PortableInfo) {
|
||||
FEXCore::Config::SetDataDirectory(GetDataDirectory(PortableInfo));
|
||||
FEXCore::Config::SetDataDirectory(GetDataDirectory(false, PortableInfo), false);
|
||||
FEXCore::Config::SetDataDirectory(GetDataDirectory(true, PortableInfo), true);
|
||||
FEXCore::Config::SetConfigDirectory(GetConfigDirectory(false, PortableInfo), false);
|
||||
FEXCore::Config::SetConfigDirectory(GetConfigDirectory(true, PortableInfo), true);
|
||||
FEXCore::Config::SetConfigFileLocation(GetConfigFileLocation(false, PortableInfo), false);
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
add_subdirectory(CommonTools)
|
||||
|
||||
if (NOT MINGW_BUILD)
|
||||
if (USE_FEXCONFIG_TOOLKIT STREQUAL "imgui")
|
||||
add_subdirectory(FEXConfig/)
|
||||
elseif (USE_FEXCONFIG_TOOLKIT STREQUAL "qt")
|
||||
if (BUILD_FEXCONFIG)
|
||||
find_package(Qt6 COMPONENTS Qml Quick Widgets QUIET)
|
||||
if (NOT Qt6_FOUND)
|
||||
find_package(Qt5 COMPONENTS Qml Quick Widgets REQUIRED)
|
||||
endif()
|
||||
|
||||
add_subdirectory(FEXQonfig/)
|
||||
add_subdirectory(FEXConfig/)
|
||||
endif()
|
||||
|
||||
if (ENABLE_GDB_SYMBOLS)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
list(APPEND LIBS FEXCore Common CommonTools)
|
||||
list(APPEND LIBS FEXCore Common CommonTools JemallocLibs)
|
||||
|
||||
set (SRCS Main.cpp)
|
||||
add_executable(CodeSizeValidation ${SRCS})
|
||||
|
||||
@@ -480,8 +480,6 @@ int main(int argc, char** argv, char** const envp) {
|
||||
// Setup configurations that this tool needs
|
||||
// Maximum one instruction.
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_MAXINST, "1");
|
||||
// IRJIT. Only works on JITs.
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_CORE, fextl::fmt::format("{}", static_cast<uint64_t>(FEXCore::Config::CONFIG_IRJIT)));
|
||||
// Enable block disassembly.
|
||||
FEXCore::Config::EraseSet(
|
||||
FEXCore::Config::CONFIG_DISASSEMBLE,
|
||||
|
||||
@@ -9,6 +9,7 @@ target_link_libraries(FEXBash
|
||||
PRIVATE
|
||||
FEXCore
|
||||
Common
|
||||
JemallocLibs
|
||||
LinuxEmulation
|
||||
${PTHREAD_LIB}
|
||||
)
|
||||
|
||||
@@ -1,31 +1,20 @@
|
||||
set(NAME FEXConfig)
|
||||
set(SRCS Main.cpp
|
||||
${CMAKE_SOURCE_DIR}/External/imgui/examples/imgui_impl_sdl.cpp
|
||||
${CMAKE_SOURCE_DIR}/External/imgui/examples/imgui_impl_opengl3.cpp)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
|
||||
find_library(EPOXY epoxy REQUIRED)
|
||||
find_package(SDL2 REQUIRED)
|
||||
|
||||
add_definitions(-DIMGUI_IMPL_OPENGL_LOADER_CUSTOM=<epoxy/gl.h>)
|
||||
add_executable(${NAME} ${SRCS})
|
||||
|
||||
target_include_directories(${NAME} PRIVATE ${CMAKE_SOURCE_DIR}/Source/)
|
||||
target_include_directories(${NAME} PRIVATE ${CMAKE_SOURCE_DIR}/External/imgui/examples/)
|
||||
|
||||
# Fix for SDL2 includes under Alpine Linux.
|
||||
target_include_directories(${NAME} PRIVATE /usr/include/directfb/)
|
||||
|
||||
if (TARGET SDL2::SDL2)
|
||||
target_link_libraries(${NAME} PRIVATE SDL2::SDL2)
|
||||
add_executable(FEXConfig)
|
||||
target_sources(FEXConfig PRIVATE Main.cpp Main.h)
|
||||
target_include_directories(FEXConfig PRIVATE ${CMAKE_SOURCE_DIR}/Source/)
|
||||
target_link_libraries(FEXConfig PRIVATE Common JemallocDummy)
|
||||
if (Qt6_FOUND)
|
||||
qt_add_resources(QT_RESOURCES qml6.qrc)
|
||||
target_link_libraries(FEXConfig PRIVATE Qt6::Qml Qt6::Quick Qt6::Widgets)
|
||||
else()
|
||||
target_include_directories(${NAME} PRIVATE ${SDL2_INCLUDE_DIRS})
|
||||
target_link_libraries(${NAME} PRIVATE ${SDL2_LIBRARIES})
|
||||
qt_add_resources(QT_RESOURCES qml5.qrc)
|
||||
target_link_libraries(FEXConfig PRIVATE Qt5::Qml Qt5::Quick Qt5::Widgets)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${NAME} PRIVATE Common pthread epoxy X11 EGL imgui)
|
||||
target_sources(FEXConfig PRIVATE ${QT_RESOURCES})
|
||||
|
||||
if (CMAKE_BUILD_TYPE MATCHES "RELEASE")
|
||||
target_link_options(${NAME}
|
||||
target_link_options(FEXConfig
|
||||
PRIVATE
|
||||
"LINKER:--gc-sections"
|
||||
"LINKER:--strip-all"
|
||||
@@ -33,7 +22,7 @@ if (CMAKE_BUILD_TYPE MATCHES "RELEASE")
|
||||
)
|
||||
endif()
|
||||
|
||||
install(TARGETS ${NAME}
|
||||
install(TARGETS FEXConfig
|
||||
RUNTIME
|
||||
DESTINATION bin
|
||||
COMPONENT runtime)
|
||||
+325
-951
File diff suppressed because it is too large.
Load diff
@@ -41,7 +41,6 @@ class RootFSModel : public QStandardItemModel {
|
||||
std::latch ExitRequest {1};
|
||||
|
||||
int INotifyFD;
|
||||
int FolderFD;
|
||||
|
||||
void INotifyThreadFunc();
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loaded 100 of 218 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user