mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 22:00:16 +02:00
Compare commits
100
Commits
FEX-2506
...
FEX-2507.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea20429351 | ||
|
|
91828efa7a | ||
|
|
cce605d5e0 | ||
|
|
3ba84ad06a | ||
|
|
c6aae9e05a | ||
|
|
95b4618833 | ||
|
|
1fe17d55d9 | ||
|
|
ead73371d9 | ||
|
|
6f089a4323 | ||
|
|
640f024551 | ||
|
|
99920f89dd | ||
|
|
8ea276267f | ||
|
|
6ebbd91245 | ||
|
|
fa0a54deb9 | ||
|
|
046043090f | ||
|
|
61150a18cc | ||
|
|
95ca20cfee | ||
|
|
5a536d47fd | ||
|
|
afbc7da027 | ||
|
|
c093c08c40 | ||
|
|
360d8c629e | ||
|
|
abb41d39e4 | ||
|
|
d4eb4ef594 | ||
|
|
02f45854e8 | ||
|
|
62de1004df | ||
|
|
7f216ca02f | ||
|
|
492b0fdda8 | ||
|
|
bb072c0112 | ||
|
|
afabe7cb47 | ||
|
|
38e0fc2434 | ||
|
|
16a70eafc6 | ||
|
|
de4becc26e | ||
|
|
af23f4325f | ||
|
|
94af96df8f | ||
|
|
e685ab818e | ||
|
|
5f2a72b65b | ||
|
|
c1842a6167 | ||
|
|
3f3907b5d1 | ||
|
|
1899465390 | ||
|
|
18360d4ccb | ||
|
|
a691c3cd99 | ||
|
|
70bc561bbf | ||
|
|
b9222d8431 | ||
|
|
a5fad89e57 | ||
|
|
a14360b89d | ||
|
|
c9aaedd217 | ||
|
|
cda15ce9ea | ||
|
|
3f3b6ad337 | ||
|
|
62410c4381 | ||
|
|
cf82b56dd8 | ||
|
|
4a74bea7ab | ||
|
|
c6d8e60ef8 | ||
|
|
1212cd526a | ||
|
|
79a8ed53b6 | ||
|
|
a6ce115d9c | ||
|
|
646a5a7f9e | ||
|
|
7f71b6f1b2 | ||
|
|
16d5ca447f | ||
|
|
3d0c20a263 | ||
|
|
1c38b8b046 | ||
|
|
e1124480be | ||
|
|
5243f50ed1 | ||
|
|
cde805147f | ||
|
|
7e39eb3df2 | ||
|
|
af366d4480 | ||
|
|
33ef98aae7 | ||
|
|
c16db2db4a | ||
|
|
059d980c33 | ||
|
|
581381fd86 | ||
|
|
0072b289bb | ||
|
|
e2bd79087e | ||
|
|
6cd78fc90d | ||
|
|
c346aca241 | ||
|
|
bf83569f0b | ||
|
|
1502f04a8a | ||
|
|
7bd9d0ae23 | ||
|
|
cf57afdf26 | ||
|
|
43e6aebc7a | ||
|
|
22780993e1 | ||
|
|
578dcee9af | ||
|
|
61d77e3f9b | ||
|
|
4ce0acba80 | ||
|
|
d137212222 | ||
|
|
9ad4e3a6a0 | ||
|
|
57627d4fcf | ||
|
|
23b69271eb | ||
|
|
9d2f557666 | ||
|
|
4f9e352ff0 | ||
|
|
3e85e60a30 | ||
|
|
febce21b21 | ||
|
|
755364e2df | ||
|
|
df63979773 | ||
|
|
992d86bbc1 | ||
|
|
534b338161 | ||
|
|
ef250f936c | ||
|
|
e57130e364 | ||
|
|
eedcb35270 | ||
|
|
1eb470083c | ||
|
|
1f15a4e35b | ||
|
|
7ed9bea16b |
No files matched your search
@@ -78,7 +78,7 @@ jobs:
|
||||
# Note the current convention is to use the -S and -B options here to specify source
|
||||
# and build directories, but this is only available with CMake 3.13 and higher.
|
||||
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$GITHUB_WORKSPACE/toolchain_mingw.cmake -DMINGW_TRIPLE=$MINGW_TRIPLE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DBUILD_TESTS=False -DCMAKE_INSTALL_PREFIX=${{runner.workspace}}/build/install
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$GITHUB_WORKSPACE/Data/CMake/toolchain_mingw.cmake -DMINGW_TRIPLE=$MINGW_TRIPLE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DBUILD_TESTS=False -DCMAKE_INSTALL_PREFIX=${{runner.workspace}}/build/install
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
+5
-9
@@ -35,8 +35,8 @@ set (FEXCORE_PROFILER_BACKEND "gpuvis" CACHE STRING "Set which backend to use fo
|
||||
option(ENABLE_GLIBC_ALLOCATOR_HOOK_FAULT "Enables glibc memory allocation hooking with fault for CI testing")
|
||||
option(USE_PDB_DEBUGINFO "Builds debug info in PDB format" FALSE)
|
||||
|
||||
set (X86_32_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/toolchain_x86_32.cmake" CACHE FILEPATH "Toolchain file for the (cross-)compiler targeting i686")
|
||||
set (X86_64_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/toolchain_x86_64.cmake" CACHE FILEPATH "Toolchain file for the (cross-)compiler targeting x86_64")
|
||||
set (X86_32_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/Data/CMake/toolchain_x86_32.cmake" CACHE FILEPATH "Toolchain file for the (cross-)compiler targeting i686")
|
||||
set (X86_64_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/Data/CMake/toolchain_x86_64.cmake" CACHE FILEPATH "Toolchain file for the (cross-)compiler targeting x86_64")
|
||||
set (X86_DEV_ROOTFS "/" CACHE FILEPATH "Path to the sysroot used for cross-compiling for i686 and x86_64")
|
||||
set (DATA_DIRECTORY "" CACHE PATH "Global data directory (override)")
|
||||
if (NOT DATA_DIRECTORY)
|
||||
@@ -97,7 +97,7 @@ endif()
|
||||
# uninstall target
|
||||
if(NOT TARGET uninstall)
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/CMakeFiles/cmake_uninstall.cmake.in"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Data/CMake/cmake_uninstall.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/cmake_uninstall.cmake"
|
||||
IMMEDIATE @ONLY)
|
||||
|
||||
@@ -436,10 +436,6 @@ endif()
|
||||
|
||||
add_compile_options(-Wall)
|
||||
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/Config.h.in
|
||||
${CMAKE_BINARY_DIR}/generated/ConfigDefines.h)
|
||||
|
||||
include(CTest)
|
||||
if (BUILD_TESTS)
|
||||
message(STATUS "Unit tests are enabled")
|
||||
@@ -622,12 +618,12 @@ set (CPACK_PACKAGE_CONTACT "FEX-Emu Maintainers <team@fex-emu.com>")
|
||||
set (CPACK_PACKAGE_VERSION_MAJOR "${FEX_VERSION_MAJOR}")
|
||||
set (CPACK_PACKAGE_VERSION_MINOR "${FEX_VERSION_MINOR}")
|
||||
set (CPACK_PACKAGE_VERSION_PATCH "${FEX_VERSION_PATCH}")
|
||||
set (CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/CPack/Description.txt")
|
||||
set (CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/Data/CMake/CPack/Description.txt")
|
||||
|
||||
# Debian defines
|
||||
set (CPACK_DEBIAN_PACKAGE_DEPENDS "libc6, libstdc++6, libepoxy0, libsdl2-2.0-0, libegl1, libx11-6, squashfuse")
|
||||
set (CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/CPack/postinst;${CMAKE_CURRENT_SOURCE_DIR}/CPack/prerm;${CMAKE_CURRENT_SOURCE_DIR}/CPack/triggers")
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Data/CMake/CPack/postinst;${CMAKE_CURRENT_SOURCE_DIR}/Data/CMake/CPack/prerm;${CMAKE_CURRENT_SOURCE_DIR}/Data/CMake/CPack/triggers")
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
|
||||
# binfmt_misc conflicts with qemu-user-static
|
||||
# We also only install binfmt_misc on aarch64 hosts
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
x86 and x86-64 Linux emulator
|
||||
|
||||
FEX is very much work in progress, so expect things to change.
|
||||
@@ -820,7 +820,6 @@ public:
|
||||
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
|
||||
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i32Bit :
|
||||
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
|
||||
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit :
|
||||
ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc<T>(Op, 0, ConvertedSize, 0b00010, rd, rn);
|
||||
@@ -856,7 +855,6 @@ public:
|
||||
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
|
||||
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i32Bit :
|
||||
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
|
||||
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit :
|
||||
ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc<T>(Op, 0, ConvertedSize, 0b00110, rd, rn);
|
||||
@@ -1195,7 +1193,6 @@ public:
|
||||
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
|
||||
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i32Bit :
|
||||
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
|
||||
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit :
|
||||
ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b00010, rd, rn);
|
||||
@@ -1225,7 +1222,6 @@ public:
|
||||
constexpr uint32_t Op = 0b0000'1110'0010'0000'0000'10 << 10;
|
||||
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i32Bit :
|
||||
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
|
||||
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit :
|
||||
ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b00110, rd, rn);
|
||||
@@ -1322,9 +1318,7 @@ public:
|
||||
void fcvtxn(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
|
||||
LOGMAN_THROW_A_FMT(size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit subregsize supported");
|
||||
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;
|
||||
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc(Op, 1, ConvertedSize, 0b10110, rd.D(), rn.D());
|
||||
}
|
||||
@@ -1333,9 +1327,7 @@ public:
|
||||
void fcvtxn2(ARMEmitter::SubRegSize size, ARMEmitter::VRegister rd, ARMEmitter::VRegister rn) {
|
||||
LOGMAN_THROW_A_FMT(size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit subregsize supported");
|
||||
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;
|
||||
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc(Op, 1, ConvertedSize, 0b10110, rd.Q(), rn.Q());
|
||||
}
|
||||
@@ -1344,9 +1336,7 @@ public:
|
||||
LOGMAN_THROW_A_FMT(size == ARMEmitter::SubRegSize::i64Bit || size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize "
|
||||
"supported");
|
||||
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;
|
||||
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b11000, rd, rn);
|
||||
}
|
||||
@@ -1355,9 +1345,7 @@ public:
|
||||
LOGMAN_THROW_A_FMT(size == ARMEmitter::SubRegSize::i64Bit || size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize "
|
||||
"supported");
|
||||
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;
|
||||
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b11001, rd, rn);
|
||||
}
|
||||
@@ -1367,9 +1355,7 @@ public:
|
||||
LOGMAN_THROW_A_FMT(size == ARMEmitter::SubRegSize::i64Bit || size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize "
|
||||
"supported");
|
||||
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;
|
||||
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b11010, rd, rn);
|
||||
}
|
||||
@@ -1378,9 +1364,7 @@ public:
|
||||
LOGMAN_THROW_A_FMT(size == ARMEmitter::SubRegSize::i64Bit || size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize "
|
||||
"supported");
|
||||
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;
|
||||
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b11011, rd, rn);
|
||||
}
|
||||
@@ -1389,9 +1373,7 @@ public:
|
||||
LOGMAN_THROW_A_FMT(size == ARMEmitter::SubRegSize::i64Bit || size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize "
|
||||
"supported");
|
||||
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;
|
||||
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b11100, rd, rn);
|
||||
}
|
||||
@@ -1400,9 +1382,7 @@ public:
|
||||
LOGMAN_THROW_A_FMT(size == ARMEmitter::SubRegSize::i64Bit || size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize "
|
||||
"supported");
|
||||
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;
|
||||
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b11101, rd, rn);
|
||||
}
|
||||
@@ -1411,9 +1391,7 @@ public:
|
||||
LOGMAN_THROW_A_FMT(size == ARMEmitter::SubRegSize::i64Bit || size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize "
|
||||
"supported");
|
||||
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;
|
||||
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b11110, rd, rn);
|
||||
}
|
||||
@@ -1422,9 +1400,7 @@ public:
|
||||
LOGMAN_THROW_A_FMT(size == ARMEmitter::SubRegSize::i64Bit || size == ARMEmitter::SubRegSize::i32Bit, "Only 32-bit & 64-bit subregsize "
|
||||
"supported");
|
||||
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;
|
||||
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMD2RegMisc<T>(Op, 1, ConvertedSize, 0b11111, rd, rn);
|
||||
}
|
||||
@@ -1553,7 +1529,6 @@ public:
|
||||
constexpr uint32_t Op = 0b0000'1110'0011'0000'0000'10 << 10;
|
||||
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i32Bit :
|
||||
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
|
||||
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit :
|
||||
ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMDAcrossLanes<T>(Op, 0, ConvertedSize, 0b00011, rd, rn);
|
||||
@@ -1597,7 +1572,6 @@ public:
|
||||
constexpr uint32_t Op = 0b0000'1110'0011'0000'0000'10 << 10;
|
||||
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i32Bit :
|
||||
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i16Bit :
|
||||
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit :
|
||||
ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
ASIMDAcrossLanes<T>(Op, 1, ConvertedSize, 0b00011, rd, rn);
|
||||
@@ -1630,10 +1604,7 @@ public:
|
||||
LOGMAN_THROW_A_FMT(size != ARMEmitter::SubRegSize::i8Bit && size != ARMEmitter::SubRegSize::i64Bit, "Destination 8/64-bit subregsize "
|
||||
"unsupported");
|
||||
constexpr uint32_t Op = 0b0000'1110'0011'0000'0000'10 << 10;
|
||||
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
|
||||
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit :
|
||||
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit :
|
||||
ARMEmitter::SubRegSize::i8Bit;
|
||||
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
const auto U = size == ARMEmitter::SubRegSize::i16Bit ? 0 : 1;
|
||||
|
||||
@@ -1647,10 +1618,7 @@ public:
|
||||
LOGMAN_THROW_A_FMT(size != ARMEmitter::SubRegSize::i8Bit && size != ARMEmitter::SubRegSize::i64Bit, "Destination 8/64-bit subregsize "
|
||||
"unsupported");
|
||||
constexpr uint32_t Op = 0b0000'1110'0011'0000'0000'10 << 10;
|
||||
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit :
|
||||
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i8Bit :
|
||||
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i8Bit :
|
||||
ARMEmitter::SubRegSize::i8Bit;
|
||||
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i16Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
const auto U = size == ARMEmitter::SubRegSize::i16Bit ? 0 : 1;
|
||||
|
||||
@@ -1664,10 +1632,7 @@ public:
|
||||
LOGMAN_THROW_A_FMT(size != ARMEmitter::SubRegSize::i8Bit && size != ARMEmitter::SubRegSize::i64Bit, "Destination 8/64-bit subregsize "
|
||||
"unsupported");
|
||||
constexpr uint32_t Op = 0b0000'1110'0011'0000'0000'10 << 10;
|
||||
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i64Bit :
|
||||
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i32Bit :
|
||||
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ARMEmitter::SubRegSize::i32Bit;
|
||||
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i32Bit;
|
||||
|
||||
const auto U = size == ARMEmitter::SubRegSize::i16Bit ? 0 : 1;
|
||||
|
||||
@@ -1681,10 +1646,7 @@ public:
|
||||
LOGMAN_THROW_A_FMT(size != ARMEmitter::SubRegSize::i8Bit && size != ARMEmitter::SubRegSize::i64Bit, "Destination 8/64-bit subregsize "
|
||||
"unsupported");
|
||||
constexpr uint32_t Op = 0b0000'1110'0011'0000'0000'10 << 10;
|
||||
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i64Bit :
|
||||
size == ARMEmitter::SubRegSize::i32Bit ? ARMEmitter::SubRegSize::i32Bit :
|
||||
size == ARMEmitter::SubRegSize::i16Bit ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ARMEmitter::SubRegSize::i32Bit;
|
||||
const auto ConvertedSize = size == ARMEmitter::SubRegSize::i64Bit ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i32Bit;
|
||||
|
||||
const auto U = size == ARMEmitter::SubRegSize::i16Bit ? 0 : 1;
|
||||
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,3 @@
|
||||
x86 and x86-64 Linux emulator
|
||||
|
||||
FEX allows you to run x86 applications on ARM64 Linux devices. It offers broad compatibility with both 32-bit and 64-bit binaries, and it can be used alongside Wine/Proton to play Windows games.
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,6 +1,6 @@
|
||||
# This is a reference AArch64 cross compile script
|
||||
# Pass in to cmake when building:
|
||||
# eg: cmake -DCMAKE_TOOLCHAIN_FILE=../CMakeToolchains/AArch64.cmake ..
|
||||
# eg: cmake --toolchain ../Data/CMake/toolchain_aarch64.cmake ..
|
||||
if (NOT DEFINED ENV{SYSROOT})
|
||||
message(FATAL_ERROR "Need to have SYSROOT environment variable set")
|
||||
endif()
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,35 @@
|
||||
{ pkgs ? import <nixpkgs> { } }:
|
||||
|
||||
let
|
||||
pkgsCross32 = pkgs.pkgsCross.gnu32;
|
||||
pkgsCross64 = pkgs.pkgsCross.gnu64;
|
||||
|
||||
gcc32 = pkgs.writeText "toolchain_nix_gcc_x86_32.txt" ''
|
||||
set(CMAKE_SYSTEM_PROCESSOR i686)
|
||||
set(CMAKE_C_COMPILER ${pkgsCross32.buildPackages.gcc}/bin/i686-unknown-linux-gnu-gcc)
|
||||
set(CMAKE_CXX_COMPILER ${pkgsCross32.buildPackages.gcc}/bin/i686-unknown-linux-gnu-g++)
|
||||
'';
|
||||
|
||||
gcc64 = pkgs.writeText "toolchain_nix_gcc_x86_64.txt" ''
|
||||
set(CMAKE_SYSTEM_PROCESSOR x86_64)
|
||||
set(CMAKE_C_COMPILER ${pkgsCross64.buildPackages.gcc}/bin/x86_64-unknown-linux-gnu-gcc)
|
||||
set(CMAKE_CXX_COMPILER ${pkgsCross64.buildPackages.gcc}/bin/x86_64-unknown-linux-gnu-g++)
|
||||
'';
|
||||
in
|
||||
pkgs.mkShell {
|
||||
buildInputs = [
|
||||
pkgsCross64.buildPackages.clang
|
||||
pkgsCross32.buildPackages.clang
|
||||
];
|
||||
|
||||
shellHook = ''
|
||||
if [[ $- == *i* ]]; then
|
||||
echo "toolchain32: ${gcc32}"
|
||||
echo "toolchain64: ${gcc64}"
|
||||
echo ""
|
||||
echo "Use \$FEX_CMAKE_TOOLCHAINS to configure CMake."
|
||||
fi
|
||||
'';
|
||||
|
||||
FEX_CMAKE_TOOLCHAINS = "-DX86_32_TOOLCHAIN_FILE=${gcc32} -DX86_64_TOOLCHAIN_FILE=${gcc64}";
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
{ pkgs ? import <nixpkgs> { } }:
|
||||
|
||||
let
|
||||
pkgsCross32 = pkgs.pkgsCross.gnu32;
|
||||
pkgsCross64 = pkgs.pkgsCross.gnu64;
|
||||
|
||||
devRootFS = pkgs.buildEnv {
|
||||
name = "fex-dev-rootfs";
|
||||
paths = [
|
||||
pkgsCross64.stdenv.cc.libc_dev
|
||||
pkgsCross32.stdenv.cc.libc_dev
|
||||
pkgsCross64.stdenv.cc.cc
|
||||
pkgsCross32.stdenv.cc.cc
|
||||
|
||||
pkgs.alsa-lib.dev
|
||||
pkgs.libdrm.dev
|
||||
pkgs.libGL.dev
|
||||
pkgs.wayland.dev
|
||||
pkgs.xorg.libX11.dev
|
||||
pkgs.xorg.libxcb.dev
|
||||
pkgs.xorg.libXrandr.dev
|
||||
pkgs.xorg.libXrender.dev
|
||||
pkgs.xorg.xorgproto
|
||||
];
|
||||
ignoreCollisions = true;
|
||||
pathsToLink = [
|
||||
"/include"
|
||||
"/lib"
|
||||
];
|
||||
|
||||
postBuild = ''
|
||||
mkdir -p $out/usr
|
||||
ln -s $out/include $out/usr/
|
||||
'';
|
||||
};
|
||||
|
||||
toolchain32 = pkgs.writeText "toolchain_nix_x86_32.txt" ''
|
||||
set(CMAKE_EXE_LINKER_FLAGS_INIT "-fuse-ld=lld")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS_INIT "-fuse-ld=lld")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_INIT "-fuse-ld=lld")
|
||||
set(CMAKE_SYSTEM_PROCESSOR i686)
|
||||
set(CMAKE_C_COMPILER clang)
|
||||
set(CMAKE_CXX_COMPILER clang++)
|
||||
set(CMAKE_C_COMPILER ${pkgsCross32.buildPackages.clang}/bin/i686-unknown-linux-gnu-clang)
|
||||
set(CMAKE_CXX_COMPILER ${pkgsCross32.buildPackages.clang}/bin/i686-unknown-linux-gnu-clang++)
|
||||
set(CLANG_FLAGS "-nodefaultlibs -nostartfiles -lstdc++ -target i686-linux-gnu -msse2 -mfpmath=sse --sysroot=${devRootFS} -iwithsysroot/include")
|
||||
set(CMAKE_C_FLAGS "''${CMAKE_C_FLAGS} ''${CLANG_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "''${CMAKE_CXX_FLAGS} ''${CLANG_FLAGS}")
|
||||
'';
|
||||
|
||||
toolchain64 = pkgs.writeText "toolchain_nix_x86_64.txt" ''
|
||||
set(CMAKE_EXE_LINKER_FLAGS_INIT "-fuse-ld=lld")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS_INIT "-fuse-ld=lld")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_INIT "-fuse-ld=lld")
|
||||
set(CMAKE_SYSTEM_PROCESSOR x86_64)
|
||||
set(CMAKE_C_COMPILER clang)
|
||||
set(CMAKE_CXX_COMPILER clang++)
|
||||
set(CMAKE_C_COMPILER ${pkgsCross64.buildPackages.clang}/bin/x86_64-unknown-linux-gnu-clang)
|
||||
set(CMAKE_CXX_COMPILER ${pkgsCross64.buildPackages.clang}/bin/x86_64-unknown-linux-gnu-clang++)
|
||||
set(CLANG_FLAGS "-nodefaultlibs -nostartfiles -lstdc++ -target x86_64-linux-gnu --sysroot=${devRootFS} -iwithsysroot/usr/include")
|
||||
set(CMAKE_C_FLAGS "''${CMAKE_C_FLAGS} ''${CLANG_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "''${CMAKE_CXX_FLAGS} ''${CLANG_FLAGS}")
|
||||
'';
|
||||
in
|
||||
pkgs.mkShell {
|
||||
buildInputs = [
|
||||
pkgsCross64.buildPackages.clang
|
||||
pkgsCross32.buildPackages.clang
|
||||
];
|
||||
|
||||
shellHook = ''
|
||||
if [[ $- == *i* ]]; then
|
||||
echo "Set up dev RootFS at ${devRootFS}"
|
||||
echo "toolchain32: ${toolchain32}"
|
||||
echo "toolchain64: ${toolchain64}"
|
||||
echo ""
|
||||
echo "Use \$FEX_CMAKE_TOOLCHAINS to configure CMake."
|
||||
fi
|
||||
'';
|
||||
|
||||
FEX_CMAKE_TOOLCHAINS = "-DX86_32_TOOLCHAIN_FILE=${toolchain32} -DX86_64_TOOLCHAIN_FILE=${toolchain64} -DX86_DEV_ROOTFS=${devRootFS}";
|
||||
ROOTFS = "${devRootFS}";
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
{ pkgs ? import <nixpkgs> { } }:
|
||||
|
||||
let
|
||||
toolchain = pkgs.fetchzip {
|
||||
url = "https://github.com/bylaws/llvm-mingw/releases/download/20250305/llvm-mingw-20250305-ucrt-ubuntu-20.04-aarch64.tar.xz";
|
||||
sha256 = "sha256-cA03/ab9O61eO9+S2JzIXD4V0HzTXK5/AYyxW2d73Po=";
|
||||
};
|
||||
|
||||
cmakeToolchainFile = pkgs.substitute {
|
||||
# Use absolute paths that are discoverable outside of the nix shell
|
||||
src = ../../CMake/toolchain_mingw.cmake;
|
||||
substitutions = ["--replace-fail" "\${MINGW_TRIPLE}-" "${toolchain}/bin/\${MINGW_TRIPLE}-"];
|
||||
};
|
||||
|
||||
mesonCrossFile = pkgs.writeText "crossfile_llvm_mingw.txt" ''
|
||||
[binaries]
|
||||
ar = '${toolchain}/bin/arm64ec-w64-mingw32-ar'
|
||||
c = '${toolchain}/bin/arm64ec-w64-mingw32-gcc'
|
||||
cpp = '${toolchain}/bin/arm64ec-w64-mingw32-g++'
|
||||
ld = '${toolchain}/bin/arm64ec-w64-mingw32-ld'
|
||||
windres = '${toolchain}/bin/arm64ec-w64-mingw32-windres'
|
||||
strip = '${toolchain}/bin/strip'
|
||||
widl = '${toolchain}/bin/arm64ec-w64-mingw32-widl'
|
||||
pkgconfig = 'aarch64-linux-gnu-pkg-config'
|
||||
[host_machine]
|
||||
system = 'windows'
|
||||
cpu_family = 'aarch64'
|
||||
cpu = 'aarch64'
|
||||
endian = 'little'
|
||||
'';
|
||||
in
|
||||
pkgs.mkShell {
|
||||
buildInputs = [
|
||||
toolchain
|
||||
];
|
||||
|
||||
shellHook = ''
|
||||
if [[ $- == *i* ]]; then
|
||||
echo "llvm-mingw set up at ${toolchain}."
|
||||
echo ""
|
||||
echo "To configure DXVK/vkd3d-proton: meson setup \$FEX_MESON_CROSSFILE"
|
||||
echo ""
|
||||
echo "To configure 32-bit FEX build: cmake \$FEX_CMAKE_TOOLCHAIN_WOW64"
|
||||
echo "To configure 64-bit FEX build: cmake \$FEX_CMAKE_TOOLCHAIN_ARM64EC"
|
||||
fi
|
||||
'';
|
||||
|
||||
# E.g. cmake $FEX_CMAKE_TOOLCHAIN_ARM64EC -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DENABLE_LTO=False -DBUILD_TESTS=False
|
||||
FEX_CMAKE_TOOLCHAIN_ARM64EC = "--toolchain ${cmakeToolchainFile} -DMINGW_TRIPLE=arm64ec-w64-mingw32 -DCMAKE_INSTALL_LIBDIR=/usr/lib/wine/aarch64-windows";
|
||||
FEX_CMAKE_TOOLCHAIN_WOW64 = "--toolchain ${cmakeToolchainFile} -DMINGW_TRIPLE=aarch64-w64-mingw32 -DCMAKE_INSTALL_LIBDIR=/usr/lib/wine/aarch64-windows";
|
||||
FEX_MESON_CROSSFILE = "--cross-file ${mesonCrossFile}";
|
||||
}
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#! /usr/bin/env nix-shell
|
||||
#! nix-shell -i bash WineOnArm/shell.nix
|
||||
|
||||
# Helper script to configure CMake for building FEX as library for emulation
|
||||
# of 32-bit applications in Wine/Proton.
|
||||
# The required cross-toolchains will be set up and managed by nix.
|
||||
|
||||
if [ $# -eq 0 ]
|
||||
then
|
||||
echo "Expected CMake argument list"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f CMakeCache.txt ]
|
||||
then
|
||||
echo "Expected empty build folder"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -o xtrace
|
||||
cmake $FEX_CMAKE_TOOLCHAIN_WOW64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DENABLE_LTO=False -DBUILD_TESTS=False $@
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#! /usr/bin/env nix-shell
|
||||
#! nix-shell -i bash WineOnArm/shell.nix
|
||||
|
||||
# Helper script to configure CMake for building FEX as library for emulation
|
||||
# of 64-bit applications in Wine/Proton
|
||||
# Nix is used to install and manage the required cross-toolchains.
|
||||
|
||||
if [ $# -eq 0 ]
|
||||
then
|
||||
echo "Expected CMake argument list"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f CMakeCache.txt ]
|
||||
then
|
||||
echo "Expected empty build folder"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -o xtrace
|
||||
cmake $FEX_CMAKE_TOOLCHAIN_ARM64EC -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DENABLE_LTO=False -DBUILD_TESTS=False $@
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#! /usr/bin/env nix-shell
|
||||
#! nix-shell -i bash FEXLinuxTests/shell.nix
|
||||
|
||||
# Helper script to configure CMake for building FEXLinuxTests.
|
||||
# Nix is used to install and manage the required cross-toolchains.
|
||||
|
||||
if [ ! -f CMakeCache.txt ]
|
||||
then
|
||||
echo "Must be run from a pre-configured CMake build folder"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Remove previous build to ensure the new toolchain is applied
|
||||
rm -rf unittests/FEXLinuxTests
|
||||
|
||||
set -o xtrace
|
||||
cmake . $FEX_CMAKE_TOOLCHAINS -DBUILD_TESTS=ON -DBUILD_FEX_LINUX_TESTS=ON
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
# Helper script to configure CMake for library forwarding in FEX.
|
||||
# Nix is used to install and manage the required cross-toolchains.
|
||||
|
||||
if [ ! -f CMakeCache.txt ]
|
||||
then
|
||||
echo "Must be run from a pre-configured CMake build folder"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Remove previous build to ensure the new toolchain is applied
|
||||
rm -rf guest-libs guest-libs-32 Guest Guest_32
|
||||
|
||||
# Set clang executable path manually since the one from the nix store
|
||||
# will be picked up otherwise
|
||||
CLANG_EXEC_PATH=""
|
||||
if ! grep -q CLANG_EXEC_PATH CMakeCache.txt
|
||||
then
|
||||
CLANG_EXEC_PATH="-DCLANG_EXEC_PATH=`which clang`"
|
||||
fi
|
||||
|
||||
nix-shell `dirname -- "$0"`/LibraryForwarding/shell.nix \
|
||||
--run "set -o xtrace; cmake . \$FEX_CMAKE_TOOLCHAINS -DBUILD_THUNKS=ON $CLANG_EXEC_PATH; set +o xtrace"
|
||||
Vendored
+1
-1
Submodule External/tracy updated: 5d542dc09f...650c98ece7.
@@ -3,14 +3,32 @@
|
||||
|
||||
#ifdef _M_X86_64
|
||||
#include <xmmintrin.h>
|
||||
#include <immintrin.h>
|
||||
#endif
|
||||
|
||||
namespace FEXCore {
|
||||
struct VectorScalarF64Pair {
|
||||
double val[2];
|
||||
};
|
||||
|
||||
#ifdef _M_ARM_64
|
||||
// Can't use uint8x16_t directly from arm_neon.h here.
|
||||
// Overrides softfloat-3e's defines which causes problems.
|
||||
using VectorRegType = __attribute__((neon_vector_type(16))) uint8_t;
|
||||
struct VectorRegPairType {
|
||||
VectorRegType val[2];
|
||||
};
|
||||
|
||||
static inline VectorRegPairType MakeVectorRegPair(VectorRegType low, VectorRegType high) {
|
||||
return VectorRegPairType {low, high};
|
||||
}
|
||||
|
||||
#elif defined(_M_X86_64)
|
||||
using VectorRegType = __m128i;
|
||||
using VectorRegPairType = __m256i;
|
||||
|
||||
static inline VectorRegPairType MakeVectorRegPair(VectorRegType low, VectorRegType high) {
|
||||
return _mm256_set_m128i(high, low);
|
||||
}
|
||||
#endif
|
||||
} // namespace FEXCore
|
||||
@@ -118,7 +118,7 @@
|
||||
},
|
||||
"ThunkHostLibs": {
|
||||
"Type": "str",
|
||||
"Default": "@CMAKE_INSTALL_FULL_LIBDIR@/fex-emu/HostThunks/",
|
||||
"Default": "@CMAKE_INSTALL_FULL_LIBDIR@/fex-emu/HostThunks",
|
||||
"ShortArg": "t",
|
||||
"Desc": [
|
||||
"Folder to find the host-side thunking libraries."
|
||||
@@ -126,26 +126,12 @@
|
||||
},
|
||||
"ThunkGuestLibs": {
|
||||
"Type": "str",
|
||||
"Default": "@CMAKE_INSTALL_PREFIX@/share/fex-emu/GuestThunks/",
|
||||
"Default": "@CMAKE_INSTALL_PREFIX@/share/fex-emu/GuestThunks",
|
||||
"ShortArg": "j",
|
||||
"Desc": [
|
||||
"Folder to find the guest-side thunking libraries."
|
||||
]
|
||||
},
|
||||
"ThunkHostLibs32": {
|
||||
"Type": "str",
|
||||
"Default": "@CMAKE_INSTALL_FULL_LIBDIR@/fex-emu/HostThunks_32/",
|
||||
"Desc": [
|
||||
"Folder to find the 32-bit host-side thunking libraries."
|
||||
]
|
||||
},
|
||||
"ThunkGuestLibs32": {
|
||||
"Type": "str",
|
||||
"Default": "@CMAKE_INSTALL_PREFIX@/share/fex-emu/GuestThunks_32/",
|
||||
"Desc": [
|
||||
"Folder to find the 32-bit guest-side thunking libraries."
|
||||
]
|
||||
},
|
||||
"ThunkConfig": {
|
||||
"Type": "str",
|
||||
"Default": "",
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace CPU {
|
||||
fextl::shared_ptr<CodeBuffer> StartLargerCodeBuffer();
|
||||
|
||||
// Write offset into the latest CodeBuffer
|
||||
std::size_t LatestOffset;
|
||||
std::size_t LatestOffset {};
|
||||
|
||||
// Protects writes to the latest CodeBuffer and changes to LatestOffset
|
||||
FEXCore::ForkableUniqueMutex CodeBufferWriteMutex;
|
||||
@@ -198,7 +198,6 @@ namespace CPU {
|
||||
|
||||
FEXCore::Core::InternalThreadState* ThreadState;
|
||||
|
||||
size_t MaxCodeSize;
|
||||
[[nodiscard]]
|
||||
CodeBuffer* GetEmptyCodeBuffer();
|
||||
|
||||
|
||||
@@ -426,7 +426,7 @@ void ContextImpl::InitializeCompiler(FEXCore::Core::InternalThreadState* Thread)
|
||||
Thread->PassManager->RegisterSyscallHandler(SyscallHandler);
|
||||
|
||||
// Create CPU backend
|
||||
Thread->PassManager->InsertRegisterAllocationPass();
|
||||
Thread->PassManager->InsertRegisterAllocationPass(this);
|
||||
Thread->CPUBackend = FEXCore::CPU::CreateArm64JITCore(this, Thread);
|
||||
|
||||
Thread->PassManager->Finalize();
|
||||
@@ -771,14 +771,8 @@ ContextImpl::CompileCodeResult ContextImpl::CompileCode(FEXCore::Core::InternalT
|
||||
if (!IRView) {
|
||||
return {nullptr, nullptr, 0, 0};
|
||||
}
|
||||
auto DebugData = fextl::make_unique<FEXCore::Core::DebugData>();
|
||||
|
||||
// If the trap flag is set we generate single instruction blocks that each check to generate a single step exception.
|
||||
bool TFSet = Thread->CurrentFrame->State.flags[X86State::RFLAG_TF_RAW_LOC];
|
||||
|
||||
// Attempt to get the CPU backend to compile this code
|
||||
|
||||
|
||||
// Re-check if another thread raced us in compiling this block.
|
||||
// We could lock CodeBufferWriteMutex earlier to prevent this from happening,
|
||||
// but this would increase lock contention. Redundant frontend runs aren't
|
||||
@@ -790,6 +784,11 @@ ContextImpl::CompileCodeResult ContextImpl::CompileCode(FEXCore::Core::InternalT
|
||||
}
|
||||
}
|
||||
|
||||
auto DebugData = fextl::make_unique<FEXCore::Core::DebugData>();
|
||||
|
||||
// If the trap flag is set we generate single instruction blocks that each check to generate a single step exception.
|
||||
bool TFSet = Thread->CurrentFrame->State.flags[X86State::RFLAG_TF_RAW_LOC];
|
||||
|
||||
auto CompiledCode = Thread->CPUBackend->CompileCode(GuestRIP, Length, TotalInstructions == 1, &*IRView, DebugData.get(), TFSet);
|
||||
|
||||
// Release the IR
|
||||
@@ -988,7 +987,8 @@ void ContextImpl::AddThunkTrampolineIRHandler(uintptr_t Entrypoint, uintptr_t Gu
|
||||
const auto GPRSize = GetGPROpSize();
|
||||
|
||||
if (GPRSize == IR::OpSize::i64Bit) {
|
||||
emit->_StoreRegister(emit->_Constant(Entrypoint), X86State::REG_R11, IR::GPRClass, GPRSize);
|
||||
IR::Ref R = emit->_StoreRegister(emit->_Constant(Entrypoint), GPRSize);
|
||||
R->Reg = IR::PhysicalRegister(IR::GPRFixedClass, X86State::REG_R11).Raw;
|
||||
} else {
|
||||
emit->_StoreContext(GPRSize, IR::FPRClass, emit->_VCastFromGPR(IR::OpSize::i64Bit, IR::OpSize::i64Bit, emit->_Constant(Entrypoint)),
|
||||
offsetof(Core::CPUState, mm[0][0]));
|
||||
|
||||
@@ -517,14 +517,15 @@ void Dispatcher::EmitDispatcher() {
|
||||
|
||||
ldr(ARMEmitter::XReg::x3, R, Offset);
|
||||
if (!CTX->Config.DisableVixlIndirectCalls) [[unlikely]] {
|
||||
GenerateIndirectRuntimeCall<uint64_t, uint64_t, uint64_t, uint64_t>(ARMEmitter::Reg::r3);
|
||||
GenerateIndirectRuntimeCall<__uint128_t, uint64_t, uint64_t, uint64_t>(ARMEmitter::Reg::r3);
|
||||
} else {
|
||||
blr(ARMEmitter::Reg::r3);
|
||||
}
|
||||
// Result is now in x0
|
||||
|
||||
// Result is now in x0, x1
|
||||
if (!TMP_ABIARGS) {
|
||||
mov(TMP1, ARMEmitter::XReg::x0);
|
||||
mov(TMP2, ARMEmitter::XReg::x1);
|
||||
}
|
||||
|
||||
FillStaticRegs();
|
||||
@@ -539,8 +540,6 @@ void Dispatcher::EmitDispatcher() {
|
||||
|
||||
LUDIVHandlerAddress = EmitLongALUOpHandler(STATE_PTR(CpuStateFrame, Pointers.AArch64.LUDIV));
|
||||
LDIVHandlerAddress = EmitLongALUOpHandler(STATE_PTR(CpuStateFrame, Pointers.AArch64.LDIV));
|
||||
LUREMHandlerAddress = EmitLongALUOpHandler(STATE_PTR(CpuStateFrame, Pointers.AArch64.LUREM));
|
||||
LREMHandlerAddress = EmitLongALUOpHandler(STATE_PTR(CpuStateFrame, Pointers.AArch64.LREM));
|
||||
|
||||
// Interpreter fallbacks
|
||||
{
|
||||
@@ -559,6 +558,8 @@ void Dispatcher::EmitDispatcher() {
|
||||
FABI_I64_I16_F80_F80_PTR,
|
||||
FABI_F80_I16_F80_PTR,
|
||||
FABI_F80_I16_F80_F80_PTR,
|
||||
FABI_F80x2_I16_F80_PTR,
|
||||
FABI_F64x2_I16_F64_PTR,
|
||||
FABI_I32_I64_I64_V128_V128_I16,
|
||||
FABI_I32_V128_V128_I16,
|
||||
}};
|
||||
@@ -627,6 +628,22 @@ uint64_t Dispatcher::GenerateABICall(FallbackABI ABI) {
|
||||
constexpr static auto VABI1 = ARMEmitter::VReg::v0;
|
||||
constexpr static auto VABI2 = ARMEmitter::VReg::v1;
|
||||
|
||||
auto FillF80x2Result = [&]() {
|
||||
if (!TMP_ABIARGS) {
|
||||
mov(VTMP1.Q(), VABI1.Q());
|
||||
mov(VTMP2.Q(), VABI2.Q());
|
||||
}
|
||||
FillForABICall(CTX->HostFeatures.SupportsPreserveAllABI, true);
|
||||
};
|
||||
|
||||
auto FillF64x2Result = [&]() {
|
||||
if (!TMP_ABIARGS) {
|
||||
fmov(VTMP1.D(), VABI1.D());
|
||||
fmov(VTMP2.D(), VABI2.D());
|
||||
}
|
||||
FillForABICall(CTX->HostFeatures.SupportsPreserveAllABI, true);
|
||||
};
|
||||
|
||||
auto FillF80Result = [&]() {
|
||||
if (VTMP1 != VABI1) {
|
||||
mov(VTMP1.Q(), VABI1.Q());
|
||||
@@ -947,6 +964,52 @@ uint64_t Dispatcher::GenerateABICall(FallbackABI ABI) {
|
||||
|
||||
FillF80Result();
|
||||
} break;
|
||||
case FABI_F80x2_I16_F80_PTR: {
|
||||
// Linux Reg/Win32 Reg:
|
||||
// tmp4 (x4/x13): FallbackHandler
|
||||
// x30: return
|
||||
// vtmp1 (v0/v16): vector source 1
|
||||
// vtmp2 (v1/v16): vector source 2
|
||||
|
||||
SpillForABICall(CTX->HostFeatures.SupportsPreserveAllABI, TMP3, true);
|
||||
|
||||
ldrh(ARMEmitter::WReg::w0, STATE, offsetof(FEXCore::Core::CPUState, FCW));
|
||||
mov(ARMEmitter::XReg::x1, STATE);
|
||||
if (!TMP_ABIARGS) {
|
||||
mov(VABI1.Q(), VTMP1.Q());
|
||||
}
|
||||
|
||||
if (!CTX->Config.DisableVixlIndirectCalls) [[unlikely]] {
|
||||
// GenerateIndirectRuntimeCall<FEXCore::VectorRegPairType, uint16_t, FEXCore::VectorRegType, uint64_t>(FallbackPointerReg);
|
||||
} else {
|
||||
blr(FallbackPointerReg);
|
||||
}
|
||||
|
||||
FillF80x2Result();
|
||||
} break;
|
||||
case FABI_F64x2_I16_F64_PTR: {
|
||||
// Linux Reg/Win32 Reg:
|
||||
// tmp4 (x4/x13): FallbackHandler
|
||||
// x30: return
|
||||
// vtmp1 (v0/v16): vector source 1
|
||||
// vtmp2 (v1/v16): vector source 2
|
||||
|
||||
SpillForABICall(CTX->HostFeatures.SupportsPreserveAllABI, TMP3, true);
|
||||
|
||||
ldrh(ARMEmitter::WReg::w0, STATE, offsetof(FEXCore::Core::CPUState, FCW));
|
||||
mov(ARMEmitter::XReg::x1, STATE);
|
||||
if (!TMP_ABIARGS) {
|
||||
fmov(VABI1.D(), VTMP1.D());
|
||||
}
|
||||
|
||||
if (!CTX->Config.DisableVixlIndirectCalls) [[unlikely]] {
|
||||
// GenerateIndirectRuntimeCall<FEXCore::VectorScalarF64Pair, uint16_t, FEXCore::VectorRegType, uint64_t>(FallbackPointerReg);
|
||||
} else {
|
||||
blr(FallbackPointerReg);
|
||||
}
|
||||
|
||||
FillF64x2Result();
|
||||
} break;
|
||||
case FABI_I32_I64_I64_V128_V128_I16: {
|
||||
// Linux Reg/Win32 Reg:
|
||||
// stack: FallbackHandler
|
||||
@@ -1036,8 +1099,6 @@ void Dispatcher::InitThreadPointers(FEXCore::Core::InternalThreadState* Thread)
|
||||
auto& AArch64 = Thread->CurrentFrame->Pointers.AArch64;
|
||||
AArch64.LUDIVHandler = LUDIVHandlerAddress;
|
||||
AArch64.LDIVHandler = LDIVHandlerAddress;
|
||||
AArch64.LUREMHandler = LUREMHandlerAddress;
|
||||
AArch64.LREMHandler = LREMHandlerAddress;
|
||||
|
||||
// Fill in the fallback handlers
|
||||
InterpreterOps::FillFallbackIndexPointers(Common.FallbackHandlerPointers, &ABIPointers[0]);
|
||||
|
||||
@@ -118,8 +118,6 @@ private:
|
||||
// Long division helpers
|
||||
uint64_t LUDIVHandlerAddress {};
|
||||
uint64_t LDIVHandlerAddress {};
|
||||
uint64_t LUREMHandlerAddress {};
|
||||
uint64_t LREMHandlerAddress {};
|
||||
|
||||
void EmitDispatcher();
|
||||
uint64_t GenerateABICall(FallbackABI ABI);
|
||||
|
||||
@@ -69,10 +69,6 @@ Decoder::Decoder(FEXCore::Context::ContextImpl* ctx)
|
||||
, OSABI {ctx->SyscallHandler ? ctx->SyscallHandler->GetOSABI() : FEXCore::HLE::SyscallOSABI::OS_UNKNOWN}
|
||||
, PoolObject {ctx->FrontendAllocator, sizeof(FEXCore::X86Tables::DecodedInst) * DefaultDecodedBufferSize} {}
|
||||
|
||||
Decoder::~Decoder() {
|
||||
PoolObject.UnclaimBuffer();
|
||||
}
|
||||
|
||||
uint8_t Decoder::ReadByte() {
|
||||
uint8_t Byte = InstStream[InstructionSize];
|
||||
LOGMAN_THROW_A_FMT(InstructionSize < MAX_INST_SIZE, "Max instruction size exceeded!");
|
||||
|
||||
@@ -34,7 +34,6 @@ public:
|
||||
};
|
||||
|
||||
Decoder(FEXCore::Context::ContextImpl* ctx);
|
||||
~Decoder();
|
||||
void DecodeInstructionsAtEntry(const uint8_t* InstStream, uint64_t PC, uint64_t MaxInst,
|
||||
std::function<void(uint64_t BlockEntry, uint64_t Start, uint64_t Length)> AddContainedCodePage);
|
||||
|
||||
|
||||
@@ -202,6 +202,15 @@ struct OpHandlers<IR::OP_F80COS> {
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80SINCOS> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static VectorRegPairType handle(uint16_t FCW, VectorRegType Src1, FEXCore::Core::CpuStateFrame* Frame) {
|
||||
FEXCORE_PROFILE_INSTANT_INCREMENT(Frame->Thread, AccumulatedFloatFallbackCount, 1);
|
||||
softfloat_state State = SoftFloatStateFromFCW(FCW, true);
|
||||
return FEXCore::MakeVectorRegPair(X80SoftFloat::FSIN(&State, Src1), X80SoftFloat::FCOS(&State, Src1));
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F80XTRACT_EXP> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static VectorRegType handle(uint16_t FCW, VectorRegType Src1, FEXCore::Core::CpuStateFrame* Frame) {
|
||||
@@ -315,6 +324,21 @@ struct OpHandlers<IR::OP_F64COS> {
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F64SINCOS> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static VectorScalarF64Pair handle(uint16_t FCW, double src, FEXCore::Core::CpuStateFrame* Frame) {
|
||||
FEXCORE_PROFILE_INSTANT_INCREMENT(Frame->Thread, AccumulatedFloatFallbackCount, 1);
|
||||
double sin, cos;
|
||||
#ifdef _WIN32
|
||||
sin = ::sin(src);
|
||||
cos = ::cos(src);
|
||||
#else
|
||||
sincos(src, &sin, &cos);
|
||||
#endif
|
||||
return VectorScalarF64Pair {sin, cos};
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct OpHandlers<IR::OP_F64TAN> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static double handle(uint16_t FCW, double src, FEXCore::Core::CpuStateFrame* Frame) {
|
||||
|
||||
@@ -50,6 +50,8 @@ void InterpreterOps::FillFallbackIndexPointers(Core::FallbackABIInfo* Info, uint
|
||||
Info[Core::OPINDEX_F80SQRT] = {ABIHandlers[FABI_F80_I16_F80_PTR], reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F80SQRT>::handle)};
|
||||
Info[Core::OPINDEX_F80SIN] = {ABIHandlers[FABI_F80_I16_F80_PTR], reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F80SIN>::handle)};
|
||||
Info[Core::OPINDEX_F80COS] = {ABIHandlers[FABI_F80_I16_F80_PTR], reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F80COS>::handle)};
|
||||
Info[Core::OPINDEX_F80SINCOS] = {ABIHandlers[FABI_F80x2_I16_F80_PTR],
|
||||
reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F80SINCOS>::handle)};
|
||||
Info[Core::OPINDEX_F80XTRACT_EXP] = {ABIHandlers[FABI_F80_I16_F80_PTR],
|
||||
reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F80XTRACT_EXP>::handle)};
|
||||
Info[Core::OPINDEX_F80XTRACT_SIG] = {ABIHandlers[FABI_F80_I16_F80_PTR],
|
||||
@@ -82,6 +84,8 @@ void InterpreterOps::FillFallbackIndexPointers(Core::FallbackABIInfo* Info, uint
|
||||
// Double Precision Unary
|
||||
Info[Core::OPINDEX_F64SIN] = {ABIHandlers[FABI_F64_I16_F64_PTR], reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F64SIN>::handle)};
|
||||
Info[Core::OPINDEX_F64COS] = {ABIHandlers[FABI_F64_I16_F64_PTR], reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F64COS>::handle)};
|
||||
Info[Core::OPINDEX_F64SINCOS] = {ABIHandlers[FABI_F64x2_I16_F64_PTR],
|
||||
reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F64SINCOS>::handle)};
|
||||
Info[Core::OPINDEX_F64TAN] = {ABIHandlers[FABI_F64_I16_F64_PTR], reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F64TAN>::handle)};
|
||||
Info[Core::OPINDEX_F64F2XM1] = {ABIHandlers[FABI_F64_I16_F64_PTR],
|
||||
reinterpret_cast<uint64_t>(&FEXCore::CPU::OpHandlers<IR::OP_F64F2XM1>::handle)};
|
||||
@@ -198,6 +202,12 @@ bool InterpreterOps::GetFallbackHandler(const IR::IROp_Header* IROp, FallbackInf
|
||||
return true; \
|
||||
}
|
||||
|
||||
#define COMMON_UNARYPAIR_X87_OP(OP) \
|
||||
case IR::OP_F80##OP: { \
|
||||
*Info = {FABI_F80x2_I16_F80_PTR, Core::OPINDEX_F80##OP}; \
|
||||
return true; \
|
||||
}
|
||||
|
||||
#define COMMON_BINARY_X87_OP(OP) \
|
||||
case IR::OP_F80##OP: { \
|
||||
*Info = {FABI_F80_I16_F80_F80_PTR, Core::OPINDEX_F80##OP}; \
|
||||
@@ -215,6 +225,12 @@ bool InterpreterOps::GetFallbackHandler(const IR::IROp_Header* IROp, FallbackInf
|
||||
*Info = {FABI_F64_I16_F64_PTR, Core::OPINDEX_F64##OP}; \
|
||||
return true; \
|
||||
}
|
||||
#define COMMON_UNARYPAIR_F64_OP(OP) \
|
||||
case IR::OP_F64##OP: { \
|
||||
*Info = {FABI_F64x2_I16_F64_PTR, Core::OPINDEX_F64##OP}; \
|
||||
return true; \
|
||||
}
|
||||
|
||||
#define COMMON_BINARY_F64_OP(OP) \
|
||||
case IR::OP_F64##OP: { \
|
||||
*Info = {FABI_F64_I16_F64_F64_PTR, Core::OPINDEX_F64##OP}; \
|
||||
@@ -228,6 +244,7 @@ bool InterpreterOps::GetFallbackHandler(const IR::IROp_Header* IROp, FallbackInf
|
||||
COMMON_UNARY_X87_OP(SQRT)
|
||||
COMMON_UNARY_X87_OP(SIN)
|
||||
COMMON_UNARY_X87_OP(COS)
|
||||
COMMON_UNARYPAIR_X87_OP(SINCOS)
|
||||
COMMON_UNARY_X87_OP(XTRACT_EXP)
|
||||
COMMON_UNARY_X87_OP(XTRACT_SIG)
|
||||
COMMON_UNARY_X87_OP(BCDSTORE)
|
||||
@@ -249,6 +266,7 @@ bool InterpreterOps::GetFallbackHandler(const IR::IROp_Header* IROp, FallbackInf
|
||||
COMMON_UNARY_F64_OP(TAN)
|
||||
COMMON_UNARY_F64_OP(SIN)
|
||||
COMMON_UNARY_F64_OP(COS)
|
||||
COMMON_UNARYPAIR_F64_OP(SINCOS)
|
||||
|
||||
// Double Precision Binary
|
||||
COMMON_BINARY_F64_OP(FYL2X)
|
||||
|
||||
@@ -27,6 +27,8 @@ enum FallbackABI {
|
||||
FABI_I64_I16_F80_F80_PTR,
|
||||
FABI_F80_I16_F80_PTR,
|
||||
FABI_F80_I16_F80_F80_PTR,
|
||||
FABI_F80x2_I16_F80_PTR,
|
||||
FABI_F64x2_I16_F64_PTR,
|
||||
FABI_I32_I64_I64_V128_V128_I16,
|
||||
FABI_I32_V128_V128_I16,
|
||||
FABI_UNKNOWN,
|
||||
|
||||
@@ -401,122 +401,6 @@ DEF_OP(SMull) {
|
||||
smull(GetReg(Node).X(), GetReg(Op->Src1).W(), GetReg(Op->Src2).W());
|
||||
}
|
||||
|
||||
DEF_OP(Div) {
|
||||
auto Op = IROp->C<IR::IROp_Div>();
|
||||
|
||||
// Each source is OpSize in size
|
||||
// So you can have up to a 128bit divide from x86-64
|
||||
const auto OpSize = IROp->Size;
|
||||
const auto EmitSize = ConvertSize(IROp);
|
||||
|
||||
const auto Dst = GetReg(Node);
|
||||
auto Src1 = GetReg(Op->Src1);
|
||||
auto Src2 = GetReg(Op->Src2);
|
||||
|
||||
if (OpSize == IR::OpSize::i8Bit) {
|
||||
sxtb(EmitSize, TMP1, Src1);
|
||||
sxtb(EmitSize, TMP2, Src2);
|
||||
|
||||
Src1 = TMP1;
|
||||
Src2 = TMP2;
|
||||
} else if (OpSize == IR::OpSize::i16Bit) {
|
||||
sxth(EmitSize, TMP1, Src1);
|
||||
sxth(EmitSize, TMP2, Src2);
|
||||
|
||||
Src1 = TMP1;
|
||||
Src2 = TMP2;
|
||||
}
|
||||
|
||||
sdiv(EmitSize, Dst, Src1, Src2);
|
||||
}
|
||||
|
||||
DEF_OP(UDiv) {
|
||||
auto Op = IROp->C<IR::IROp_UDiv>();
|
||||
|
||||
// Each source is OpSize in size
|
||||
// So you can have up to a 128bit divide from x86-64
|
||||
const auto OpSize = IROp->Size;
|
||||
const auto EmitSize = ConvertSize(IROp);
|
||||
|
||||
const auto Dst = GetReg(Node);
|
||||
auto Src1 = GetReg(Op->Src1);
|
||||
auto Src2 = GetReg(Op->Src2);
|
||||
|
||||
if (OpSize == IR::OpSize::i8Bit) {
|
||||
uxtb(EmitSize, TMP1, Src1);
|
||||
uxtb(EmitSize, TMP2, Src2);
|
||||
|
||||
Src1 = TMP1;
|
||||
Src2 = TMP2;
|
||||
} else if (OpSize == IR::OpSize::i16Bit) {
|
||||
uxth(EmitSize, TMP1, Src1);
|
||||
uxth(EmitSize, TMP2, Src2);
|
||||
|
||||
Src1 = TMP1;
|
||||
Src2 = TMP2;
|
||||
}
|
||||
|
||||
udiv(EmitSize, Dst, Src1, Src2);
|
||||
}
|
||||
|
||||
DEF_OP(Rem) {
|
||||
auto Op = IROp->C<IR::IROp_Rem>();
|
||||
// Each source is OpSize in size
|
||||
// So you can have up to a 128bit divide from x86-64
|
||||
const auto OpSize = IROp->Size;
|
||||
const auto EmitSize = ConvertSize(IROp);
|
||||
|
||||
const auto Dst = GetReg(Node);
|
||||
auto Src1 = GetReg(Op->Src1);
|
||||
auto Src2 = GetReg(Op->Src2);
|
||||
|
||||
if (OpSize == IR::OpSize::i8Bit) {
|
||||
sxtb(EmitSize, TMP1, Src1);
|
||||
sxtb(EmitSize, TMP2, Src2);
|
||||
|
||||
Src1 = TMP1;
|
||||
Src2 = TMP2;
|
||||
} else if (OpSize == IR::OpSize::i16Bit) {
|
||||
sxth(EmitSize, TMP1, Src1);
|
||||
sxth(EmitSize, TMP2, Src2);
|
||||
|
||||
Src1 = TMP1;
|
||||
Src2 = TMP2;
|
||||
}
|
||||
|
||||
sdiv(EmitSize, TMP1, Src1, Src2);
|
||||
msub(EmitSize, Dst, TMP1, Src2, Src1);
|
||||
}
|
||||
|
||||
DEF_OP(URem) {
|
||||
auto Op = IROp->C<IR::IROp_URem>();
|
||||
// Each source is OpSize in size
|
||||
// So you can have up to a 128bit divide from x86-64
|
||||
const auto OpSize = IROp->Size;
|
||||
const auto EmitSize = ConvertSize(IROp);
|
||||
|
||||
const auto Dst = GetReg(Node);
|
||||
auto Src1 = GetReg(Op->Src1);
|
||||
auto Src2 = GetReg(Op->Src2);
|
||||
|
||||
if (OpSize == IR::OpSize::i8Bit) {
|
||||
uxtb(EmitSize, TMP1, Src1);
|
||||
uxtb(EmitSize, TMP2, Src2);
|
||||
|
||||
Src1 = TMP1;
|
||||
Src2 = TMP2;
|
||||
} else if (OpSize == IR::OpSize::i16Bit) {
|
||||
uxth(EmitSize, TMP1, Src1);
|
||||
uxth(EmitSize, TMP2, Src2);
|
||||
|
||||
Src1 = TMP1;
|
||||
Src2 = TMP2;
|
||||
}
|
||||
|
||||
udiv(EmitSize, TMP3, Src1, Src2);
|
||||
msub(EmitSize, Dst, TMP3, Src2, Src1);
|
||||
}
|
||||
|
||||
DEF_OP(MulH) {
|
||||
auto Op = IROp->C<IR::IROp_MulH>();
|
||||
const auto OpSize = IROp->Size;
|
||||
@@ -955,15 +839,39 @@ DEF_OP(PExt) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(LDiv) {
|
||||
auto Op = IROp->C<IR::IROp_LDiv>();
|
||||
DEF_OP(Div) {
|
||||
auto Op = IROp->C<IR::IROp_Div>();
|
||||
const auto OpSize = IROp->Size;
|
||||
const auto EmitSize = OpSize >= IR::OpSize::i32Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit;
|
||||
|
||||
const auto Dst = GetReg(Node);
|
||||
const auto Quotient = GetReg(Op->OutQuotient);
|
||||
const auto Remainder = GetReg(Op->OutRemainder);
|
||||
auto Lower = GetReg(Op->Lower);
|
||||
auto Divisor = GetReg(Op->Divisor);
|
||||
|
||||
if (Op->Upper.IsInvalid()) {
|
||||
const auto EmitSize = ConvertSize(IROp);
|
||||
|
||||
if (OpSize == IR::OpSize::i8Bit) {
|
||||
sxtb(EmitSize, TMP1, Lower);
|
||||
sxtb(EmitSize, TMP2, Divisor);
|
||||
|
||||
Lower = TMP1;
|
||||
Divisor = TMP2;
|
||||
} else if (OpSize == IR::OpSize::i16Bit) {
|
||||
sxth(EmitSize, TMP1, Lower);
|
||||
sxth(EmitSize, TMP2, Divisor);
|
||||
|
||||
Lower = TMP1;
|
||||
Divisor = TMP2;
|
||||
}
|
||||
|
||||
sdiv(EmitSize, Quotient, Lower, Divisor);
|
||||
msub(EmitSize, Remainder, Quotient, Divisor, Lower);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto EmitSize = OpSize >= IR::OpSize::i32Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit;
|
||||
const auto Upper = GetReg(Op->Upper);
|
||||
const auto Lower = GetReg(Op->Lower);
|
||||
const auto Divisor = GetReg(Op->Divisor);
|
||||
|
||||
// Each source is OpSize in size
|
||||
// So you can have up to a 128bit divide from x86-64
|
||||
@@ -972,7 +880,8 @@ DEF_OP(LDiv) {
|
||||
uxth(EmitSize, TMP1, Lower);
|
||||
bfi(EmitSize, TMP1, Upper, 16, 16);
|
||||
sxth(EmitSize, TMP2, Divisor);
|
||||
sdiv(EmitSize, Dst, TMP1, TMP2);
|
||||
sdiv(EmitSize, Quotient, TMP1, TMP2);
|
||||
msub(EmitSize, Remainder, Quotient, TMP2, TMP1);
|
||||
break;
|
||||
}
|
||||
case IR::OpSize::i32Bit: {
|
||||
@@ -980,7 +889,8 @@ DEF_OP(LDiv) {
|
||||
mov(EmitSize, TMP1, Lower);
|
||||
bfi(EmitSize, TMP1, Upper, 32, 32);
|
||||
sxtw(TMP2, Divisor.W());
|
||||
sdiv(EmitSize, Dst, TMP1, TMP2);
|
||||
sdiv(EmitSize, Quotient, TMP1, TMP2);
|
||||
msub(EmitSize, Remainder, Quotient, TMP2, TMP1);
|
||||
break;
|
||||
}
|
||||
case IR::OpSize::i64Bit: {
|
||||
@@ -1007,8 +917,9 @@ DEF_OP(LDiv) {
|
||||
blr(TMP4);
|
||||
ldr<ARMEmitter::IndexType::POST>(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16);
|
||||
|
||||
// Move result to its destination register
|
||||
mov(EmitSize, Dst, TMP1);
|
||||
// Move results to the destination registers
|
||||
mov(EmitSize, Quotient, TMP1);
|
||||
mov(EmitSize, Remainder, TMP2);
|
||||
|
||||
// Skip 64-bit path
|
||||
b(&LongDIVRet);
|
||||
@@ -1016,39 +927,57 @@ DEF_OP(LDiv) {
|
||||
|
||||
Bind(&Only64Bit);
|
||||
// 64-Bit only
|
||||
{ sdiv(EmitSize, Dst, Lower, Divisor); }
|
||||
{
|
||||
sdiv(EmitSize, Quotient, Lower, Divisor);
|
||||
msub(EmitSize, Remainder, Quotient, Divisor, Lower);
|
||||
}
|
||||
|
||||
Bind(&LongDIVRet);
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unknown LDIV Size: {}", OpSize); break;
|
||||
default: LOGMAN_MSG_A_FMT("Unknown DIV Size: {}", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(LUDiv) {
|
||||
auto Op = IROp->C<IR::IROp_LUDiv>();
|
||||
DEF_OP(UDiv) {
|
||||
auto Op = IROp->C<IR::IROp_UDiv>();
|
||||
const auto OpSize = IROp->Size;
|
||||
const auto EmitSize = OpSize >= IR::OpSize::i32Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit;
|
||||
|
||||
const auto Dst = GetReg(Node);
|
||||
const auto Upper = GetReg(Op->Upper);
|
||||
const auto Quotient = GetReg(Op->OutQuotient);
|
||||
const auto Remainder = GetReg(Op->OutRemainder);
|
||||
const auto Lower = GetReg(Op->Lower);
|
||||
const auto Divisor = GetReg(Op->Divisor);
|
||||
|
||||
// Each source is OpSize in size
|
||||
// So you can have up to a 128bit divide from x86-64=
|
||||
if (Op->Upper.IsInvalid()) {
|
||||
const auto EmitSize = ConvertSize(IROp);
|
||||
udiv(EmitSize, Quotient, Lower, Divisor);
|
||||
msub(EmitSize, Remainder, Quotient, Divisor, Lower);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto EmitSize = OpSize >= IR::OpSize::i32Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit;
|
||||
const auto Upper = GetReg(Op->Upper);
|
||||
|
||||
switch (OpSize) {
|
||||
case IR::OpSize::i16Bit: {
|
||||
uxth(EmitSize, TMP1, Lower);
|
||||
bfi(EmitSize, TMP1, Upper, 16, 16);
|
||||
udiv(EmitSize, Dst, TMP1, Divisor);
|
||||
udiv(EmitSize, Quotient, TMP1, Divisor);
|
||||
msub(EmitSize, Remainder, Quotient, Divisor, TMP1);
|
||||
break;
|
||||
}
|
||||
case IR::OpSize::i32Bit: {
|
||||
// We need to mask divisor if we have Upper bits, since the frontend does
|
||||
// not on the hope that we can optimize to use the path above.
|
||||
mov(ARMEmitter::Size::i32Bit, TMP2, Divisor);
|
||||
|
||||
// TODO: 32-bit operation should be guaranteed not to leave garbage in the upper bits.
|
||||
mov(EmitSize, TMP1, Lower);
|
||||
bfi(EmitSize, TMP1, Upper, 32, 32);
|
||||
udiv(EmitSize, Dst, TMP1, Divisor);
|
||||
udiv(EmitSize, Quotient, TMP1, TMP2);
|
||||
msub(EmitSize, Remainder, Quotient, TMP2, TMP1);
|
||||
break;
|
||||
}
|
||||
case IR::OpSize::i64Bit: {
|
||||
@@ -1071,8 +1000,9 @@ DEF_OP(LUDiv) {
|
||||
blr(TMP4);
|
||||
ldr<ARMEmitter::IndexType::POST>(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16);
|
||||
|
||||
// Move result to its destination register
|
||||
mov(EmitSize, Dst, TMP1);
|
||||
// Move results to the destination registers
|
||||
mov(EmitSize, Quotient, TMP1);
|
||||
mov(EmitSize, Remainder, TMP2);
|
||||
|
||||
// Skip 64-bit path
|
||||
b(&LongDIVRet);
|
||||
@@ -1080,7 +1010,10 @@ DEF_OP(LUDiv) {
|
||||
|
||||
Bind(&Only64Bit);
|
||||
// 64-Bit only
|
||||
{ udiv(EmitSize, Dst, Lower, Divisor); }
|
||||
{
|
||||
udiv(EmitSize, Quotient, Lower, Divisor);
|
||||
msub(EmitSize, Remainder, Quotient, Divisor, Lower);
|
||||
}
|
||||
|
||||
Bind(&LongDIVRet);
|
||||
break;
|
||||
@@ -1089,149 +1022,6 @@ DEF_OP(LUDiv) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(LRem) {
|
||||
auto Op = IROp->C<IR::IROp_LRem>();
|
||||
const auto OpSize = IROp->Size;
|
||||
const auto EmitSize = OpSize >= IR::OpSize::i32Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit;
|
||||
|
||||
const auto Dst = GetReg(Node);
|
||||
const auto Upper = GetReg(Op->Upper);
|
||||
const auto Lower = GetReg(Op->Lower);
|
||||
const auto Divisor = GetReg(Op->Divisor);
|
||||
|
||||
// Each source is OpSize in size
|
||||
// So you can have up to a 128bit divide from x86-64
|
||||
switch (OpSize) {
|
||||
case IR::OpSize::i16Bit: {
|
||||
uxth(EmitSize, TMP1, Lower);
|
||||
bfi(EmitSize, TMP1, Upper, 16, 16);
|
||||
sxth(EmitSize, TMP2, Divisor);
|
||||
sdiv(EmitSize, TMP3, TMP1, TMP2);
|
||||
msub(EmitSize, Dst, TMP3, TMP2, TMP1);
|
||||
break;
|
||||
}
|
||||
case IR::OpSize::i32Bit: {
|
||||
// TODO: 32-bit operation should be guaranteed not to leave garbage in the upper bits.
|
||||
mov(EmitSize, TMP1, Lower);
|
||||
bfi(EmitSize, TMP1, Upper, 32, 32);
|
||||
sxtw(TMP3, Divisor.W());
|
||||
sdiv(EmitSize, TMP2, TMP1, TMP3);
|
||||
msub(EmitSize, Dst, TMP2, TMP3, TMP1);
|
||||
break;
|
||||
}
|
||||
case IR::OpSize::i64Bit: {
|
||||
ARMEmitter::ForwardLabel Only64Bit {};
|
||||
ARMEmitter::ForwardLabel LongDIVRet {};
|
||||
|
||||
// Check if the upper bits match the top bit of the lower 64-bits
|
||||
// Sign extend the top bit of lower bits
|
||||
sbfx(EmitSize, TMP1, Lower, 63, 1);
|
||||
eor(EmitSize, TMP1, TMP1, Upper);
|
||||
|
||||
// If the sign bit matches then the result is zero
|
||||
cbz(EmitSize, TMP1, &Only64Bit);
|
||||
|
||||
// Long divide
|
||||
{
|
||||
mov(EmitSize, TMP1, Upper);
|
||||
mov(EmitSize, TMP2, Lower);
|
||||
mov(EmitSize, TMP3, Divisor);
|
||||
|
||||
ldr(TMP4, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.AArch64.LREMHandler));
|
||||
|
||||
str<ARMEmitter::IndexType::PRE>(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, -16);
|
||||
blr(TMP4);
|
||||
ldr<ARMEmitter::IndexType::POST>(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16);
|
||||
|
||||
// Move result to its destination register
|
||||
mov(EmitSize, Dst, TMP1);
|
||||
|
||||
// Skip 64-bit path
|
||||
b(&LongDIVRet);
|
||||
}
|
||||
|
||||
Bind(&Only64Bit);
|
||||
// 64-Bit only
|
||||
{
|
||||
sdiv(EmitSize, TMP1, Lower, Divisor);
|
||||
msub(EmitSize, Dst, TMP1, Divisor, Lower);
|
||||
}
|
||||
Bind(&LongDIVRet);
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unknown LREM Size: {}", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(LURem) {
|
||||
auto Op = IROp->C<IR::IROp_LURem>();
|
||||
const auto OpSize = IROp->Size;
|
||||
const auto EmitSize = OpSize >= IR::OpSize::i32Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit;
|
||||
|
||||
const auto Dst = GetReg(Node);
|
||||
const auto Upper = GetReg(Op->Upper);
|
||||
const auto Lower = GetReg(Op->Lower);
|
||||
const auto Divisor = GetReg(Op->Divisor);
|
||||
|
||||
// Each source is OpSize in size
|
||||
// So you can have up to a 128bit divide from x86-64
|
||||
switch (OpSize) {
|
||||
case IR::OpSize::i16Bit: {
|
||||
uxth(EmitSize, TMP1, Lower);
|
||||
bfi(EmitSize, TMP1, Upper, 16, 16);
|
||||
udiv(EmitSize, TMP2, TMP1, Divisor);
|
||||
msub(EmitSize, Dst, TMP2, Divisor, TMP1);
|
||||
break;
|
||||
}
|
||||
case IR::OpSize::i32Bit: {
|
||||
// TODO: 32-bit operation should be guaranteed not to leave garbage in the upper bits.
|
||||
mov(EmitSize, TMP1, Lower);
|
||||
bfi(EmitSize, TMP1, Upper, 32, 32);
|
||||
udiv(EmitSize, TMP2, TMP1, Divisor);
|
||||
msub(EmitSize, Dst, TMP2, Divisor, TMP1);
|
||||
break;
|
||||
}
|
||||
case IR::OpSize::i64Bit: {
|
||||
ARMEmitter::ForwardLabel Only64Bit {};
|
||||
ARMEmitter::ForwardLabel LongDIVRet {};
|
||||
|
||||
// Check the upper bits for zero
|
||||
// If the upper bits are zero then we can do a 64-bit divide
|
||||
cbz(EmitSize, Upper, &Only64Bit);
|
||||
|
||||
// Long divide
|
||||
{
|
||||
mov(EmitSize, TMP1, Upper);
|
||||
mov(EmitSize, TMP2, Lower);
|
||||
mov(EmitSize, TMP3, Divisor);
|
||||
|
||||
ldr(TMP4, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.AArch64.LUREMHandler));
|
||||
|
||||
str<ARMEmitter::IndexType::PRE>(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, -16);
|
||||
blr(TMP4);
|
||||
ldr<ARMEmitter::IndexType::POST>(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16);
|
||||
|
||||
// Move result to its destination register
|
||||
mov(EmitSize, Dst, TMP1);
|
||||
|
||||
// Skip 64-bit path
|
||||
b(&LongDIVRet);
|
||||
}
|
||||
|
||||
Bind(&Only64Bit);
|
||||
// 64-Bit only
|
||||
{
|
||||
udiv(EmitSize, TMP1, Lower, Divisor);
|
||||
msub(EmitSize, Dst, TMP1, Divisor, Lower);
|
||||
}
|
||||
|
||||
Bind(&LongDIVRet);
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unknown LUREM Size: {}", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Not) {
|
||||
auto Op = IROp->C<IR::IROp_Not>();
|
||||
|
||||
|
||||
@@ -411,6 +411,7 @@ DEF_OP(ThreadRemoveCodeEntry) {
|
||||
DEF_OP(CPUID) {
|
||||
auto Op = IROp->C<IR::IROp_CPUID>();
|
||||
|
||||
isb();
|
||||
mov(ARMEmitter::Size::i64Bit, TMP2, GetReg(Op->Function));
|
||||
mov(ARMEmitter::Size::i64Bit, TMP3, GetReg(Op->Leaf));
|
||||
|
||||
|
||||
@@ -41,31 +41,32 @@ $end_info$
|
||||
#include <limits>
|
||||
|
||||
namespace {
|
||||
static uint64_t LUDIV(uint64_t SrcHigh, uint64_t SrcLow, uint64_t Divisor) {
|
||||
struct DivRem {
|
||||
uint64_t Quotient;
|
||||
uint64_t Remainder;
|
||||
};
|
||||
|
||||
static struct DivRem LUDIV(uint64_t SrcHigh, uint64_t SrcLow, uint64_t Divisor) {
|
||||
__uint128_t Source = (static_cast<__uint128_t>(SrcHigh) << 64) | SrcLow;
|
||||
__uint128_t Res = Source / Divisor;
|
||||
return Res;
|
||||
|
||||
return {
|
||||
.Quotient = (uint64_t)(Source / Divisor),
|
||||
.Remainder = (uint64_t)(Source % Divisor),
|
||||
};
|
||||
}
|
||||
|
||||
static int64_t LDIV(uint64_t SrcHigh, uint64_t SrcLow, int64_t Divisor) {
|
||||
static struct DivRem
|
||||
LDIV(uint64_t SrcHigh, uint64_t SrcLow, int64_t Divisor) {
|
||||
__int128_t Source = (static_cast<__uint128_t>(SrcHigh) << 64) | SrcLow;
|
||||
__int128_t Res = Source / Divisor;
|
||||
return Res;
|
||||
|
||||
return {
|
||||
.Quotient = (uint64_t)(Source / Divisor),
|
||||
.Remainder = (uint64_t)(Source % Divisor),
|
||||
};
|
||||
}
|
||||
|
||||
static uint64_t LUREM(uint64_t SrcHigh, uint64_t SrcLow, uint64_t Divisor) {
|
||||
__uint128_t Source = (static_cast<__uint128_t>(SrcHigh) << 64) | SrcLow;
|
||||
__uint128_t Res = Source % Divisor;
|
||||
return Res;
|
||||
}
|
||||
|
||||
static int64_t LREM(uint64_t SrcHigh, uint64_t SrcLow, int64_t Divisor) {
|
||||
__int128_t Source = (static_cast<__uint128_t>(SrcHigh) << 64) | SrcLow;
|
||||
__int128_t Res = Source % Divisor;
|
||||
return Res;
|
||||
}
|
||||
|
||||
static void PrintValue(uint64_t Value) {
|
||||
static void
|
||||
PrintValue(uint64_t Value) {
|
||||
LogMan::Msg::DFmt("Value: 0x{:x}", Value);
|
||||
}
|
||||
|
||||
@@ -83,6 +84,16 @@ void Arm64JITCore::Op_Unhandled(const IR::IROp_Header* IROp, IR::Ref Node) {
|
||||
LOGMAN_MSG_A_FMT("Unhandled IR Op: {}", FEXCore::IR::GetName(IROp->Op));
|
||||
#endif
|
||||
} else {
|
||||
auto FillF80x2Result = [&](auto DstLo, auto DstHi) {
|
||||
mov(DstLo.Q(), VTMP1.Q());
|
||||
mov(DstHi.Q(), VTMP2.Q());
|
||||
};
|
||||
|
||||
auto FillF64x2Result = [&](auto DstLo, auto DstHi) {
|
||||
fmov(DstLo.D(), VTMP1.D());
|
||||
fmov(DstHi.D(), VTMP2.D());
|
||||
};
|
||||
|
||||
auto FillF80Result = [&]() {
|
||||
const auto Dst = GetVReg(Node);
|
||||
mov(Dst.Q(), VTMP1.Q());
|
||||
@@ -228,6 +239,30 @@ void Arm64JITCore::Op_Unhandled(const IR::IROp_Header* IROp, IR::Ref Node) {
|
||||
ldr<ARMEmitter::IndexType::POST>(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16);
|
||||
FillF64Result();
|
||||
} break;
|
||||
case FABI_F64x2_I16_F64_PTR: {
|
||||
// Linux Reg/Win32 Reg:
|
||||
// tmp4 (x4/x13): FallbackHandler
|
||||
// x30: return
|
||||
// vtmp1 (v0/v16): vector source
|
||||
// vtmp2 (v1/v16): vector source
|
||||
#ifdef VIXL_SIMULATOR
|
||||
LOGMAN_THROW_A_FMT(CTX->Config.DisableVixlIndirectCalls, "Vector register pairs unsupported by simulator currently");
|
||||
#endif
|
||||
str<ARMEmitter::IndexType::PRE>(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, -16);
|
||||
|
||||
const auto Src1 = GetVReg(IROp->Args[0]);
|
||||
const auto DstLo = GetVReg(IROp->Args[1]);
|
||||
const auto DstHi = GetVReg(IROp->Args[2]);
|
||||
|
||||
fmov(VTMP1.D(), Src1.D());
|
||||
|
||||
ldr(TMP1, STATE_PTR(CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex].ABIHandler));
|
||||
ldr(TMP4, STATE_PTR(CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex].Func));
|
||||
blr(TMP1);
|
||||
|
||||
ldr<ARMEmitter::IndexType::POST>(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16);
|
||||
FillF64x2Result(DstLo, DstHi);
|
||||
} break;
|
||||
|
||||
case FABI_F64_I16_F64_F64_PTR: {
|
||||
// Linux Reg/Win32 Reg:
|
||||
@@ -344,6 +379,31 @@ void Arm64JITCore::Op_Unhandled(const IR::IROp_Header* IROp, IR::Ref Node) {
|
||||
FillF80Result();
|
||||
} break;
|
||||
|
||||
case FABI_F80x2_I16_F80_PTR: {
|
||||
// Linux Reg/Win32 Reg:
|
||||
// tmp4 (x4/x13): FallbackHandler
|
||||
// x30: return
|
||||
// vtmp1 (v0/v16): vector source 1
|
||||
// vtmp2 (v1/v16): vector source 2
|
||||
#ifdef VIXL_SIMULATOR
|
||||
LOGMAN_THROW_A_FMT(CTX->Config.DisableVixlIndirectCalls, "Vector register pairs unsupported by simulator currently");
|
||||
#endif
|
||||
str<ARMEmitter::IndexType::PRE>(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, -16);
|
||||
|
||||
const auto Src1 = GetVReg(IROp->Args[0]);
|
||||
const auto DstLo = GetVReg(IROp->Args[1]);
|
||||
const auto DstHi = GetVReg(IROp->Args[2]);
|
||||
|
||||
mov(VTMP1.Q(), Src1.Q());
|
||||
|
||||
ldr(TMP1, STATE_PTR(CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex].ABIHandler));
|
||||
ldr(TMP4, STATE_PTR(CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex].Func));
|
||||
blr(TMP1);
|
||||
|
||||
ldr<ARMEmitter::IndexType::POST>(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16);
|
||||
FillF80x2Result(DstLo, DstHi);
|
||||
} break;
|
||||
|
||||
case FABI_F80_I16_F80_F80_PTR: {
|
||||
// Linux Reg/Win32 Reg:
|
||||
// tmp4 (x4/x13): FallbackHandler
|
||||
@@ -545,8 +605,6 @@ Arm64JITCore::Arm64JITCore(FEXCore::Context::ContextImpl* ctx, FEXCore::Core::In
|
||||
|
||||
AArch64.LUDIV = reinterpret_cast<uint64_t>(LUDIV);
|
||||
AArch64.LDIV = reinterpret_cast<uint64_t>(LDIV);
|
||||
AArch64.LUREM = reinterpret_cast<uint64_t>(LUREM);
|
||||
AArch64.LREM = reinterpret_cast<uint64_t>(LREM);
|
||||
}
|
||||
|
||||
CurrentCodeBuffer = CodeBuffers.GetLatest();
|
||||
@@ -794,10 +852,8 @@ CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, uint64_t Size
|
||||
}
|
||||
}
|
||||
|
||||
if (DebugData) {
|
||||
DebugData->Subblocks.push_back({static_cast<uint32_t>(BlockStartHostCode - CodeData.BlockEntry),
|
||||
static_cast<uint32_t>(GetCursorAddress<uint8_t*>() - BlockStartHostCode)});
|
||||
}
|
||||
DebugData->Subblocks.push_back({static_cast<uint32_t>(BlockStartHostCode - CodeData.BlockEntry),
|
||||
static_cast<uint32_t>(GetCursorAddress<uint8_t*>() - BlockStartHostCode)});
|
||||
}
|
||||
|
||||
// Make sure last branch is generated. It certainly can't be eliminated here.
|
||||
@@ -940,10 +996,8 @@ CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, uint64_t Size
|
||||
}
|
||||
#endif
|
||||
|
||||
if (DebugData) {
|
||||
DebugData->HostCodeSize = CodeData.Size;
|
||||
DebugData->Relocations = &Relocations;
|
||||
}
|
||||
DebugData->HostCodeSize = CodeData.Size;
|
||||
DebugData->Relocations = &Relocations;
|
||||
|
||||
this->IR = nullptr;
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ $end_info$
|
||||
#include "Interface/Core/ArchHelpers/Arm64Emitter.h"
|
||||
#include "Interface/Core/CPUID.h"
|
||||
#include "Interface/Core/JIT/JITClass.h"
|
||||
#include "Interface/IR/RegisterAllocationData.h"
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
#include <FEXCore/Utils/MathUtils.h>
|
||||
|
||||
@@ -174,23 +175,16 @@ DEF_OP(LoadAF) {
|
||||
|
||||
DEF_OP(StoreRegister) {
|
||||
const auto Op = IROp->C<IR::IROp_StoreRegister>();
|
||||
auto Reg = IR::PhysicalRegister(Node);
|
||||
|
||||
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];
|
||||
|
||||
if (Reg.Class == IR::GPRFixedClass) {
|
||||
// Always use 64-bit, it's faster. Upper bits ignored for 32-bit mode.
|
||||
mov(ARMEmitter::Size::i64Bit, reg, GetReg(Op->Value));
|
||||
} else if (Op->Class == IR::FPRClass) {
|
||||
mov(ARMEmitter::Size::i64Bit, GetReg(Reg), GetReg(Op->Value));
|
||||
} else if (Reg.Class == IR::FPRFixedClass) {
|
||||
[[maybe_unused]] const auto regSize = HostSupportsAVX256 ? IR::OpSize::i256Bit : IR::OpSize::i128Bit;
|
||||
LOGMAN_THROW_A_FMT(Op->Reg < StaticFPRegisters.size(), "reg out of range");
|
||||
LOGMAN_THROW_A_FMT(IROp->Size == regSize, "expected sized");
|
||||
|
||||
const auto guest = StaticFPRegisters[Op->Reg];
|
||||
const auto guest = GetVReg(Reg);
|
||||
const auto host = GetVReg(Op->Value);
|
||||
|
||||
if (HostSupportsAVX256) {
|
||||
@@ -199,7 +193,7 @@ DEF_OP(StoreRegister) {
|
||||
mov(guest.Q(), host.Q());
|
||||
}
|
||||
} else {
|
||||
LOGMAN_THROW_A_FMT(false, "Unhandled Op->Class {}", Op->Class);
|
||||
LOGMAN_THROW_A_FMT(false, "Unhandled Op->Class {}", Reg.Class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1311,7 +1311,6 @@ void OpDispatchBuilder::CPUIDOp(OpcodeArgs) {
|
||||
Ref RCX = _AllocateGPR(false);
|
||||
Ref RDX = _AllocateGPR(false);
|
||||
|
||||
_Fence({FEXCore::IR::Fence_Inst});
|
||||
_CPUID(Src, Leaf, RAX, RBX, RCX, RDX);
|
||||
|
||||
StoreGPRRegister(X86State::REG_RAX, RAX);
|
||||
@@ -2852,9 +2851,10 @@ void OpDispatchBuilder::AASOp(OpcodeArgs) {
|
||||
void OpDispatchBuilder::AAMOp(OpcodeArgs) {
|
||||
auto AL = LoadGPRRegister(X86State::REG_RAX, OpSize::i8Bit);
|
||||
auto Imm8 = _Constant(Op->Src[0].Data.Literal.Value & 0xFF);
|
||||
auto UDivOp = _UDiv(OpSize::i64Bit, AL, Imm8);
|
||||
auto URemOp = _URem(OpSize::i64Bit, AL, Imm8);
|
||||
auto Res = _AddShift(OpSize::i64Bit, URemOp, UDivOp, ShiftType::LSL, 8);
|
||||
Ref Quotient = _AllocateGPR(true);
|
||||
Ref Remainder = _AllocateGPR(true);
|
||||
_UDiv(OpSize::i64Bit, AL, Invalid(), Imm8, Quotient, Remainder);
|
||||
auto Res = _AddShift(OpSize::i64Bit, Remainder, Quotient, ShiftType::LSL, 8);
|
||||
StoreGPRRegister(X86State::REG_RAX, Res, OpSize::i16Bit);
|
||||
|
||||
SetNZ_ZeroCV(OpSize::i8Bit, Res);
|
||||
@@ -3581,52 +3581,43 @@ void OpDispatchBuilder::NEGOp(OpcodeArgs) {
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::DIVOp(OpcodeArgs) {
|
||||
// This loads the divisor
|
||||
Ref Divisor = LoadSource(GPRClass, Op, Op->Dest, Op->Flags);
|
||||
|
||||
const auto GPRSize = CTX->GetGPROpSize();
|
||||
const auto Size = OpSizeFromSrc(Op);
|
||||
auto Size = OpSizeFromSrc(Op);
|
||||
|
||||
// This loads the divisor. 32-bit/64-bit paths mask inside the JIT, 8/16 do not.
|
||||
Ref Divisor = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.AllowUpperGarbage = Size >= OpSize::i32Bit});
|
||||
|
||||
if (Size == OpSize::i64Bit && !CTX->Config.Is64BitMode) {
|
||||
LogMan::Msg::EFmt("Doesn't exist in 32bit mode");
|
||||
DecodeFailure = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Ref Quotient = _AllocateGPR(true);
|
||||
Ref Remainder = _AllocateGPR(true);
|
||||
|
||||
if (Size == OpSize::i8Bit) {
|
||||
Ref Src1 = LoadGPRRegister(X86State::REG_RAX, OpSize::i16Bit);
|
||||
|
||||
auto UDivOp = _UDiv(OpSize::i16Bit, Src1, Divisor);
|
||||
auto URemOp = _URem(OpSize::i16Bit, Src1, Divisor);
|
||||
_UDiv(OpSize::i16Bit, Src1, Invalid(), Divisor, Quotient, Remainder);
|
||||
|
||||
// AX[15:0] = concat<URem[7:0]:UDiv[7:0]>
|
||||
auto ResultAX = _Bfi(GPRSize, 8, 8, UDivOp, URemOp);
|
||||
auto ResultAX = _Bfi(GPRSize, 8, 8, Quotient, Remainder);
|
||||
StoreGPRRegister(X86State::REG_RAX, ResultAX, OpSize::i16Bit);
|
||||
} else if (Size == OpSize::i16Bit) {
|
||||
Ref Src1 = LoadGPRRegister(X86State::REG_RAX);
|
||||
Ref Src2 = LoadGPRRegister(X86State::REG_RDX);
|
||||
auto UDivOp = _LUDiv(OpSize::i16Bit, Src1, Src2, Divisor);
|
||||
auto URemOp = _LURem(OpSize::i16Bit, Src1, Src2, Divisor);
|
||||
|
||||
StoreGPRRegister(X86State::REG_RAX, UDivOp, Size);
|
||||
StoreGPRRegister(X86State::REG_RDX, URemOp, Size);
|
||||
} else if (Size == OpSize::i32Bit) {
|
||||
} else {
|
||||
Ref Src1 = LoadGPRRegister(X86State::REG_RAX);
|
||||
Ref Src2 = LoadGPRRegister(X86State::REG_RDX);
|
||||
|
||||
Ref UDivOp = _Bfe(OpSize::i32Bit, IR::OpSizeAsBits(Size), 0, _LUDiv(OpSize::i32Bit, Src1, Src2, Divisor));
|
||||
Ref URemOp = _Bfe(OpSize::i32Bit, IR::OpSizeAsBits(Size), 0, _LURem(OpSize::i32Bit, Src1, Src2, Divisor));
|
||||
_UDiv(Size, Src1, Src2, Divisor, Quotient, Remainder);
|
||||
|
||||
StoreGPRRegister(X86State::REG_RAX, UDivOp);
|
||||
StoreGPRRegister(X86State::REG_RDX, URemOp);
|
||||
} else if (Size == OpSize::i64Bit) {
|
||||
if (!CTX->Config.Is64BitMode) {
|
||||
LogMan::Msg::EFmt("Doesn't exist in 32bit mode");
|
||||
DecodeFailure = true;
|
||||
return;
|
||||
if (Size == OpSize::i32Bit) {
|
||||
Quotient = _Bfe(OpSize::i32Bit, IR::OpSizeAsBits(Size), 0, Quotient);
|
||||
Remainder = _Bfe(OpSize::i32Bit, IR::OpSizeAsBits(Size), 0, Remainder);
|
||||
Size = OpSize::iInvalid;
|
||||
}
|
||||
Ref Src1 = LoadGPRRegister(X86State::REG_RAX);
|
||||
Ref Src2 = LoadGPRRegister(X86State::REG_RDX);
|
||||
|
||||
auto UDivOp = _LUDiv(OpSize::i64Bit, Src1, Src2, Divisor);
|
||||
auto URemOp = _LURem(OpSize::i64Bit, Src1, Src2, Divisor);
|
||||
|
||||
StoreGPRRegister(X86State::REG_RAX, UDivOp);
|
||||
StoreGPRRegister(X86State::REG_RDX, URemOp);
|
||||
StoreGPRRegister(X86State::REG_RAX, Quotient, Size);
|
||||
StoreGPRRegister(X86State::REG_RDX, Remainder, Size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3635,50 +3626,41 @@ void OpDispatchBuilder::IDIVOp(OpcodeArgs) {
|
||||
Ref Divisor = LoadSource(GPRClass, Op, Op->Dest, Op->Flags);
|
||||
|
||||
const auto GPRSize = CTX->GetGPROpSize();
|
||||
const auto Size = OpSizeFromSrc(Op);
|
||||
auto Size = OpSizeFromSrc(Op);
|
||||
|
||||
if (Size == OpSize::i64Bit && !CTX->Config.Is64BitMode) {
|
||||
LogMan::Msg::EFmt("Doesn't exist in 32bit mode");
|
||||
DecodeFailure = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Ref Quotient = _AllocateGPR(true);
|
||||
Ref Remainder = _AllocateGPR(true);
|
||||
|
||||
if (Size == OpSize::i8Bit) {
|
||||
Ref Src1 = LoadGPRRegister(X86State::REG_RAX);
|
||||
Src1 = _Sbfe(OpSize::i64Bit, 16, 0, Src1);
|
||||
Divisor = _Sbfe(OpSize::i64Bit, 8, 0, Divisor);
|
||||
|
||||
auto UDivOp = _Div(OpSize::i64Bit, Src1, Divisor);
|
||||
auto URemOp = _Rem(OpSize::i64Bit, Src1, Divisor);
|
||||
_Div(OpSize::i64Bit, Src1, Invalid(), Divisor, Quotient, Remainder);
|
||||
|
||||
// AX[15:0] = concat<URem[7:0]:UDiv[7:0]>
|
||||
auto ResultAX = _Bfi(GPRSize, 8, 8, UDivOp, URemOp);
|
||||
auto ResultAX = _Bfi(GPRSize, 8, 8, Quotient, Remainder);
|
||||
StoreGPRRegister(X86State::REG_RAX, ResultAX, OpSize::i16Bit);
|
||||
} else if (Size == OpSize::i16Bit) {
|
||||
Ref Src1 = LoadGPRRegister(X86State::REG_RAX);
|
||||
Ref Src2 = LoadGPRRegister(X86State::REG_RDX);
|
||||
auto UDivOp = _LDiv(OpSize::i16Bit, Src1, Src2, Divisor);
|
||||
auto URemOp = _LRem(OpSize::i16Bit, Src1, Src2, Divisor);
|
||||
|
||||
StoreGPRRegister(X86State::REG_RAX, UDivOp, Size);
|
||||
StoreGPRRegister(X86State::REG_RDX, URemOp, Size);
|
||||
} else if (Size == OpSize::i32Bit) {
|
||||
} else {
|
||||
Ref Src1 = LoadGPRRegister(X86State::REG_RAX);
|
||||
Ref Src2 = LoadGPRRegister(X86State::REG_RDX);
|
||||
|
||||
Ref UDivOp = _Bfe(OpSize::i32Bit, IR::OpSizeAsBits(Size), 0, _LDiv(OpSize::i32Bit, Src1, Src2, Divisor));
|
||||
Ref URemOp = _Bfe(OpSize::i32Bit, IR::OpSizeAsBits(Size), 0, _LRem(OpSize::i32Bit, Src1, Src2, Divisor));
|
||||
_Div(Size, Src1, Src2, Divisor, Quotient, Remainder);
|
||||
|
||||
StoreGPRRegister(X86State::REG_RAX, UDivOp);
|
||||
StoreGPRRegister(X86State::REG_RDX, URemOp);
|
||||
} else if (Size == OpSize::i64Bit) {
|
||||
if (!CTX->Config.Is64BitMode) {
|
||||
LogMan::Msg::EFmt("Doesn't exist in 32bit mode");
|
||||
DecodeFailure = true;
|
||||
return;
|
||||
if (Size == OpSize::i32Bit) {
|
||||
Quotient = _Bfe(OpSize::i32Bit, IR::OpSizeAsBits(Size), 0, Quotient);
|
||||
Remainder = _Bfe(OpSize::i32Bit, IR::OpSizeAsBits(Size), 0, Remainder);
|
||||
Size = OpSize::iInvalid;
|
||||
}
|
||||
Ref Src1 = LoadGPRRegister(X86State::REG_RAX);
|
||||
Ref Src2 = LoadGPRRegister(X86State::REG_RDX);
|
||||
|
||||
auto UDivOp = _LDiv(OpSize::i64Bit, Src1, Src2, Divisor);
|
||||
auto URemOp = _LRem(OpSize::i64Bit, Src1, Src2, Divisor);
|
||||
|
||||
StoreGPRRegister(X86State::REG_RAX, UDivOp);
|
||||
StoreGPRRegister(X86State::REG_RDX, URemOp);
|
||||
StoreGPRRegister(X86State::REG_RAX, Quotient, Size);
|
||||
StoreGPRRegister(X86State::REG_RDX, Remainder, Size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4412,9 +4394,6 @@ void OpDispatchBuilder::ALUOp(OpcodeArgs, FEXCore::IR::IROps ALUIROp, FEXCore::I
|
||||
if (!DestIsLockedMem(Op) && ALUIROp == FEXCore::IR::IROps::OP_XOR && Op->Dest.IsGPR() && Op->Src[SrcIdx].IsGPR() &&
|
||||
Op->Dest.Data.GPR == Op->Src[SrcIdx].Data.GPR) {
|
||||
|
||||
// Move 0 into the register
|
||||
StoreResult(GPRClass, Op, _Constant(0), OpSize::iInvalid);
|
||||
|
||||
// Set flags for zero result with inverted carry. We subtract an arbitrary
|
||||
// register from itself to get the zero, since `subs wzr, #0` is not
|
||||
// encodable. This is optimal and works regardless of the opsize.
|
||||
@@ -4423,6 +4402,10 @@ void OpDispatchBuilder::ALUOp(OpcodeArgs, FEXCore::IR::IROps ALUIROp, FEXCore::I
|
||||
InvalidateAF();
|
||||
CalculatePF(_SubWithFlags(OpSize::i32Bit, Zero, Zero));
|
||||
CFInverted = true;
|
||||
FlushRegisterCache();
|
||||
|
||||
// Move 0 into the register
|
||||
StoreResult(GPRClass, Op, _Constant(0), OpSize::iInvalid);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "Interface/Context/Context.h"
|
||||
#include "Interface/IR/IR.h"
|
||||
#include "Interface/IR/IREmitter.h"
|
||||
#include "Interface/IR/RegisterAllocationData.h"
|
||||
|
||||
#include <FEXCore/Config/Config.h>
|
||||
#include <FEXCore/Core/Context.h>
|
||||
@@ -1208,13 +1209,15 @@ public:
|
||||
Ref Value = RegCache.Value[Index];
|
||||
|
||||
if (Index >= GPR0Index && Index <= GPR15Index) {
|
||||
_StoreRegister(Value, Index - GPR0Index, GPRClass, GPRSize);
|
||||
Ref R = _StoreRegister(Value, GPRSize);
|
||||
R->Reg = PhysicalRegister(GPRFixedClass, Index - GPR0Index).Raw;
|
||||
} 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);
|
||||
Ref R = _StoreRegister(Value, VectorSize);
|
||||
R->Reg = PhysicalRegister(FPRFixedClass, Index - FPR0Index).Raw;
|
||||
} else if (Index == DFIndex) {
|
||||
_StoreContext(OpSize::i8Bit, GPRClass, Value, offsetof(Core::CPUState, flags[X86State::RFLAG_DF_RAW_LOC]));
|
||||
} else {
|
||||
@@ -2489,10 +2492,10 @@ private:
|
||||
}
|
||||
|
||||
struct ArithRef {
|
||||
IREmitter* E;
|
||||
bool IsConstant;
|
||||
IREmitter* E {};
|
||||
bool IsConstant {};
|
||||
union {
|
||||
Ref R;
|
||||
Ref R {};
|
||||
uint64_t C;
|
||||
};
|
||||
|
||||
|
||||
@@ -165,6 +165,11 @@ struct FEX_PACKED NodeWrapperBase final {
|
||||
LOGMAN_THROW_A_FMT(IsPointer(), "Offsets are within 2GiB range");
|
||||
}
|
||||
|
||||
void SetInvalid() {
|
||||
NodeOffset = 0;
|
||||
LOGMAN_THROW_A_FMT(IsInvalid(), "Zero state");
|
||||
}
|
||||
|
||||
void SetImmediate(uint32_t Immediate) {
|
||||
LOGMAN_THROW_A_FMT(Immediate < (1u << 31), "Bounded");
|
||||
NodeOffset = Immediate | (1u << 31);
|
||||
|
||||
@@ -79,15 +79,13 @@
|
||||
|
||||
"constexpr uint8_t COND_AL = 32 /* always */",
|
||||
|
||||
"constexpr FEXCore::IR::RegisterClassType GPRClass {0}",
|
||||
"constexpr FEXCore::IR::RegisterClassType GPRFixedClass {1}",
|
||||
"constexpr FEXCore::IR::RegisterClassType FPRClass {2}",
|
||||
"constexpr FEXCore::IR::RegisterClassType FPRFixedClass {3}",
|
||||
"constexpr FEXCore::IR::RegisterClassType InvalidClass {0}",
|
||||
"constexpr FEXCore::IR::RegisterClassType GPRClass {1}",
|
||||
"constexpr FEXCore::IR::RegisterClassType GPRFixedClass {2}",
|
||||
"constexpr FEXCore::IR::RegisterClassType FPRClass {3}",
|
||||
"constexpr FEXCore::IR::RegisterClassType FPRFixedClass {4}",
|
||||
"constexpr FEXCore::IR::RegisterClassType ComplexClass {5}",
|
||||
"constexpr FEXCore::IR::RegisterClassType InvalidClass {7}",
|
||||
"",
|
||||
"// Only up to 30 registers per register class",
|
||||
"constexpr uint8_t InvalidReg {31}",
|
||||
"constexpr uint8_t NumClasses {6}",
|
||||
"",
|
||||
"constexpr FEXCore::IR::TypeDefinition i8 {TypeDefinition::Create(1, 0)}",
|
||||
"constexpr FEXCore::IR::TypeDefinition i16 {TypeDefinition::Create(2, 0)}",
|
||||
@@ -380,14 +378,11 @@
|
||||
"DestSize": "Size"
|
||||
},
|
||||
|
||||
"StoreRegister SSA:$Value, u32:$Reg, RegisterClass:$Class, OpSize:#Size": {
|
||||
"SSA = StoreRegister SSA:$Value, OpSize:#Size": {
|
||||
"HasSideEffects": true,
|
||||
"Desc": ["Stores a value to a given register.",
|
||||
"Size must match the execution mode."],
|
||||
"DestSize": "Size",
|
||||
"EmitValidation": [
|
||||
"WalkFindRegClass($Value) == $Class"
|
||||
]
|
||||
"DestSize": "Size"
|
||||
},
|
||||
|
||||
"StorePF GPR:$Value, OpSize:#Size": {
|
||||
@@ -1459,38 +1454,6 @@
|
||||
],
|
||||
"DestSize": "FEXCore::IR::OpSize::i64Bit"
|
||||
},
|
||||
"GPR = Div OpSize:#Size, GPR:$Src1, GPR:$Src2": {
|
||||
"Desc": ["Integer signed division"
|
||||
],
|
||||
"DestSize": "Size",
|
||||
"EmitValidation": [
|
||||
"Size == FEXCore::IR::OpSize::i8Bit || Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
|
||||
]
|
||||
},
|
||||
"GPR = UDiv OpSize:#Size, GPR:$Src1, GPR:$Src2": {
|
||||
"Desc": ["Integer unsigned division"
|
||||
],
|
||||
"DestSize": "Size",
|
||||
"EmitValidation": [
|
||||
"Size == FEXCore::IR::OpSize::i8Bit || Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
|
||||
]
|
||||
},
|
||||
"GPR = Rem OpSize:#Size, GPR:$Src1, GPR:$Src2": {
|
||||
"Desc": ["Integer signed remainder"
|
||||
],
|
||||
"DestSize": "Size",
|
||||
"EmitValidation": [
|
||||
"Size == FEXCore::IR::OpSize::i8Bit || Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
|
||||
]
|
||||
},
|
||||
"GPR = URem OpSize:#Size, GPR:$Src1, GPR:$Src2": {
|
||||
"Desc": ["Integer unsigned remainder"
|
||||
],
|
||||
"DestSize": "Size",
|
||||
"EmitValidation": [
|
||||
"Size == FEXCore::IR::OpSize::i8Bit || Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
|
||||
]
|
||||
},
|
||||
"GPR = MulH OpSize:#Size, GPR:$Src1, GPR:$Src2": {
|
||||
"Desc": ["Integer signed multiply returning high results",
|
||||
"op:",
|
||||
@@ -1635,45 +1598,23 @@
|
||||
]
|
||||
},
|
||||
|
||||
"GPR = LDiv OpSize:#Size, GPR:$Lower, GPR:$Upper, GPR:$Divisor": {
|
||||
"GPR:$Quotient, GPR:$Remainder = Div OpSize:#Size, GPR:$Lower, GPR:$Upper, GPR:$Divisor": {
|
||||
"Desc": ["Integer long signed division returning lower bits",
|
||||
"The Lower and Upper registers will be concated together to generate a dividend twice the size",
|
||||
"Then the divisor divides the temporary dividend and returns the results in the original sized register",
|
||||
"If Upper is invalid, this is a non-long division."
|
||||
],
|
||||
"DestSize": "Size",
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"GPR:$Quotient, GPR:$Remainder = UDiv OpSize:#Size, GPR:$Lower, GPR:$Upper, GPR:$Divisor": {
|
||||
"Desc": ["Integer long unsigned division returning lower bits",
|
||||
"The Lower and Upper registers will be concated together to generate a dividend twice the size",
|
||||
"Then the divisor divides the temporary dividend and returns the results in the original sized register"
|
||||
"Then the divisor divides the temporary dividend and returns the results in the original sized register",
|
||||
"If Upper is invalid, this is a non-long division."
|
||||
],
|
||||
"DestSize": "Size",
|
||||
"EmitValidation": [
|
||||
"Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
|
||||
]
|
||||
},
|
||||
"GPR = LUDiv OpSize:#Size, GPR:$Lower, GPR:$Upper, GPR:$Divisor": {
|
||||
"Desc": ["Integer long unsigned division returning lower bits",
|
||||
"The Lower and Upper registers will be concated together to generate a dividend twice the size",
|
||||
"Then the divisor divides the temporary dividend and returns the results in the original sized register"
|
||||
],
|
||||
"DestSize": "Size",
|
||||
"EmitValidation": [
|
||||
"Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
|
||||
]
|
||||
},
|
||||
"GPR = LRem OpSize:#Size, GPR:$Lower, GPR:$Upper, GPR:$Divisor": {
|
||||
"Desc": ["Integer long signed remainder returning lower bits",
|
||||
"The Lower and Upper registers will be concated together to generate a dividend twice the size",
|
||||
"Then the divisor divides the temporary dividend and returns the remainder results in the original sized register"
|
||||
],
|
||||
"DestSize": "Size",
|
||||
"EmitValidation": [
|
||||
"Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
|
||||
]
|
||||
},
|
||||
"GPR = LURem OpSize:#Size, GPR:$Lower, GPR:$Upper, GPR:$Divisor": {
|
||||
"Desc": ["Integer long unsigned remainder returning lower bits",
|
||||
"The Lower and Upper registers will be concated together to generate a dividend twice the size",
|
||||
"Then the divisor divides the temporary dividend and returns the remainder results in the original sized register"
|
||||
],
|
||||
"DestSize": "Size",
|
||||
"EmitValidation": [
|
||||
"Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit"
|
||||
]
|
||||
"HasSideEffects": true
|
||||
},
|
||||
|
||||
"Float to GPR": {"Ignore": 1},
|
||||
@@ -2847,6 +2788,11 @@
|
||||
"FPR = F64COS FPR:$Src": {
|
||||
"DestSize": "OpSize::i64Bit",
|
||||
"JITDispatch": false
|
||||
},
|
||||
"FPR:$Sin, FPR:$Cos = F64SINCOS FPR:$Src": {
|
||||
"DestSize": "OpSize::i64Bit",
|
||||
"HasSideEffects": true,
|
||||
"JITDispatch": false
|
||||
}
|
||||
},
|
||||
"F80": {
|
||||
@@ -3186,6 +3132,11 @@
|
||||
"DestSize": "OpSize::i128Bit",
|
||||
"JITDispatch": false
|
||||
},
|
||||
"FPR:$Sin, FPR:$Cos = F80SINCOS FPR:$X80Src": {
|
||||
"DestSize": "OpSize::i128Bit",
|
||||
"HasSideEffects": true,
|
||||
"JITDispatch": false
|
||||
},
|
||||
"F80SINCOSStack": {
|
||||
"X87": true,
|
||||
"HasSideEffects": true
|
||||
|
||||
@@ -127,10 +127,6 @@ public:
|
||||
PoolObject.ReownOrClaimBuffer();
|
||||
}
|
||||
|
||||
~DualIntrusiveAllocatorThreadPool() {
|
||||
PoolObject.UnclaimBuffer();
|
||||
}
|
||||
|
||||
void ReownOrClaimBuffer() {
|
||||
Data = PoolObject.ReownOrClaimBuffer();
|
||||
List = Data + MemorySize;
|
||||
|
||||
@@ -82,8 +82,8 @@ void PassManager::AddDefaultValidationPasses() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void PassManager::InsertRegisterAllocationPass() {
|
||||
InsertPass(IR::CreateRegisterAllocationPass(), "RA");
|
||||
void PassManager::InsertRegisterAllocationPass(FEXCore::Context::ContextImpl* ctx) {
|
||||
InsertPass(IR::CreateRegisterAllocationPass(&ctx->CPUID), "RA");
|
||||
}
|
||||
|
||||
void PassManager::Run(IREmitter* IREmit) {
|
||||
|
||||
@@ -56,7 +56,7 @@ public:
|
||||
return PassPtr;
|
||||
}
|
||||
|
||||
void InsertRegisterAllocationPass();
|
||||
void InsertRegisterAllocationPass(FEXCore::Context::ContextImpl* ctx);
|
||||
|
||||
void Run(IREmitter* IREmit);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <FEXCore/fextl/memory.h>
|
||||
|
||||
namespace FEXCore {
|
||||
class CPUIDEmu;
|
||||
struct HostFeatures;
|
||||
} // namespace FEXCore
|
||||
|
||||
@@ -17,7 +18,7 @@ class RegisterAllocationPass;
|
||||
|
||||
fextl::unique_ptr<FEXCore::IR::Pass> CreateConstProp(bool SupportsTSOImm9);
|
||||
fextl::unique_ptr<FEXCore::IR::Pass> CreateDeadFlagCalculationEliminination();
|
||||
fextl::unique_ptr<FEXCore::IR::RegisterAllocationPass> CreateRegisterAllocationPass();
|
||||
fextl::unique_ptr<FEXCore::IR::RegisterAllocationPass> CreateRegisterAllocationPass(const FEXCore::CPUIDEmu* CPUID);
|
||||
fextl::unique_ptr<FEXCore::IR::Pass> CreateX87StackOptimizationPass(const FEXCore::HostFeatures&, OpSize GPROpSize);
|
||||
|
||||
namespace Validation {
|
||||
|
||||
@@ -103,7 +103,7 @@ void IRValidation::Run(IREmitter* IREmit) {
|
||||
}
|
||||
|
||||
// If no physical register was assigned
|
||||
if (PhyReg.Reg == IR::InvalidReg) {
|
||||
if (PhyReg.IsInvalid()) {
|
||||
HadError |= true;
|
||||
Errors << "%" << ID << ": Had destination but with no register assigned" << std::endl;
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ struct ControlFlowGraph {
|
||||
// Add some initial capacity
|
||||
Info.Predecessors.reserve(2);
|
||||
|
||||
BlockMap[ID] = Info;
|
||||
BlockMap[ID] = std::move(Info);
|
||||
Worklist.push_back(ID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ $end_info$
|
||||
#include "Interface/IR/IREmitter.h"
|
||||
#include "Interface/IR/RegisterAllocationData.h"
|
||||
#include "Interface/IR/Passes.h"
|
||||
#include "Interface/Core/CPUID.h"
|
||||
#include <FEXCore/IR/IR.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
#include <FEXCore/Utils/Profiler.h>
|
||||
@@ -21,9 +22,6 @@ using namespace FEXCore;
|
||||
|
||||
namespace FEXCore::IR {
|
||||
namespace {
|
||||
[[maybe_unused]] constexpr uint32_t INVALID_REG = IR::InvalidReg;
|
||||
constexpr uint32_t INVALID_CLASS = IR::InvalidClass.Val;
|
||||
|
||||
struct RegisterClass {
|
||||
uint32_t Available;
|
||||
uint32_t Count;
|
||||
@@ -55,15 +53,18 @@ namespace {
|
||||
|
||||
class ConstrainedRAPass final : public RegisterAllocationPass {
|
||||
public:
|
||||
explicit ConstrainedRAPass(const FEXCore::CPUIDEmu* CPUID)
|
||||
: CPUID {CPUID} {}
|
||||
void Run(IREmitter* IREmit) override;
|
||||
void AddRegisters(IR::RegisterClassType Class, uint32_t RegisterCount) override;
|
||||
bool TryPostRAMerge(Ref LastNode, Ref CodeNode, IROp_Header* IROp);
|
||||
|
||||
private:
|
||||
RegisterClass Classes[INVALID_CLASS];
|
||||
RegisterClass Classes[IR::NumClasses];
|
||||
|
||||
IREmitter* IREmit;
|
||||
IRListView* IR;
|
||||
const FEXCore::CPUIDEmu* CPUID;
|
||||
|
||||
// Map of nodes to their preferred register, to coalesce load/store reg.
|
||||
fextl::vector<PhysicalRegister> PreferredReg;
|
||||
@@ -106,15 +107,8 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (IR->GetOp<IROp_Header>(Arg)->Op) {
|
||||
case OP_INLINECONSTANT:
|
||||
case OP_INLINEENTRYPOINTOFFSET:
|
||||
case OP_IRHEADER: return false;
|
||||
|
||||
case OP_SPILLREGISTER: LOGMAN_MSG_A_FMT("should not be seen"); return false;
|
||||
|
||||
default: return true;
|
||||
}
|
||||
auto Op = IR->GetOp<IROp_Header>(Arg)->Op;
|
||||
return Op != OP_INLINECONSTANT && Op != OP_INLINEENTRYPOINTOFFSET;
|
||||
};
|
||||
|
||||
RegisterClass* GetClass(PhysicalRegister Reg) {
|
||||
@@ -168,33 +162,24 @@ private:
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
PhysicalRegister DecodeSRAReg(const IROp_Header* IROp) {
|
||||
RegisterClassType Class {};
|
||||
uint8_t Reg {};
|
||||
|
||||
PhysicalRegister DecodeSRAReg(const IROp_Header* IROp, Ref Node) {
|
||||
uint8_t FlagOffset = Classes[GPRFixedClass.Val].Count - 2;
|
||||
|
||||
if (IROp->Op == OP_LOADREGISTER) {
|
||||
const IROp_LoadRegister* Op = IROp->C<IR::IROp_LoadRegister>();
|
||||
|
||||
Class = Op->Class;
|
||||
Reg = Op->Reg;
|
||||
} else if (IROp->Op == OP_STOREREGISTER) {
|
||||
const IROp_StoreRegister* Op = IROp->C<IR::IROp_StoreRegister>();
|
||||
|
||||
Class = Op->Class;
|
||||
Reg = Op->Reg;
|
||||
if (IROp->Op == OP_STOREREGISTER) {
|
||||
return PhysicalRegister(Node);
|
||||
} 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");
|
||||
if (Class == FPRClass) {
|
||||
return PhysicalRegister {FPRFixedClass, Reg};
|
||||
} else {
|
||||
return PhysicalRegister {GPRFixedClass, Reg};
|
||||
const IROp_LoadRegister* Op = IROp->C<IR::IROp_LoadRegister>();
|
||||
|
||||
LOGMAN_THROW_A_FMT(Op->Class == GPRClass || Op->Class == FPRClass, "SRA classes");
|
||||
if (Op->Class == FPRClass) {
|
||||
return PhysicalRegister {FPRFixedClass, (uint8_t)Op->Reg};
|
||||
} else {
|
||||
return PhysicalRegister {GPRFixedClass, (uint8_t)Op->Reg};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -204,8 +189,8 @@ private:
|
||||
case OP_ALLOCATEGPRAFTER: return true;
|
||||
case OP_ALLOCATEFPR: return true;
|
||||
case OP_RMWHANDLE: return PhysicalRegister(Node) == PhysicalRegister(Header->Args[0]);
|
||||
case OP_LOADREGISTER: return PhysicalRegister(Node) == DecodeSRAReg(Header);
|
||||
case OP_STOREREGISTER: return PhysicalRegister(Header->Args[0]) == DecodeSRAReg(Header);
|
||||
case OP_LOADREGISTER: return PhysicalRegister(Node) == DecodeSRAReg(Header, Node);
|
||||
case OP_STOREREGISTER: return PhysicalRegister(Header->Args[0]) == DecodeSRAReg(Header, Node);
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
@@ -378,37 +363,128 @@ private:
|
||||
};
|
||||
|
||||
void ConstrainedRAPass::AddRegisters(IR::RegisterClassType Class, uint32_t RegisterCount) {
|
||||
LOGMAN_THROW_A_FMT(RegisterCount <= INVALID_REG, "Up to {} regs supported", INVALID_REG);
|
||||
LOGMAN_THROW_A_FMT(RegisterCount <= 31, "Up to 31 regs supported");
|
||||
|
||||
Classes[Class].Count = RegisterCount;
|
||||
}
|
||||
|
||||
inline bool KillMove(IROp_Header* LastOp, IROp_Header* IROp, Ref LastNode, Ref CodeNode) {
|
||||
// 32-bit moves in x86_64 are represented as a Bfe, detect them.
|
||||
if (LastOp->Op == OP_BFE && LastOp->C<IR::IROp_Bfe>()->lsb == 0 && LastOp->C<IR::IROp_Bfe>()->Width == 32) {
|
||||
auto Op = IROp->Op;
|
||||
|
||||
if (Op == OP_AND) {
|
||||
// Rewrite "mov wA, wB; and xA, xA, xC" into "and wA, wB, wC", since
|
||||
// ((b & 0xffffffff) & c) == (b & c) & 0xffffffff.
|
||||
IROp->Size = OpSize::i32Bit;
|
||||
return true;
|
||||
} else if (IROp->Size == OpSize::i32Bit) {
|
||||
return Op == OP_OR || Op == OP_XOR || Op == OP_AND || Op == OP_SUB || Op == OP_LSHL || Op == OP_LSHR || Op == OP_ASHR;
|
||||
}
|
||||
}
|
||||
|
||||
return LastOp->Op == OP_STOREREGISTER;
|
||||
}
|
||||
|
||||
inline bool IsSignext(const IROp_Header* IROp, OrderedNodeWrapper Src, OpSize Size) {
|
||||
if (IROp->Op == OP_SBFE) {
|
||||
auto Sbfe = IROp->C<IR::IROp_Sbfe>();
|
||||
return Sbfe->Width == 1 && Sbfe->lsb == (IR::OpSizeAsBits(Size) - 1) && Sbfe->Src == Src;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool IsZero(const IROp_Header* IROp) {
|
||||
return IROp->Op == OP_CONSTANT && IROp->C<IROp_Constant>()->Constant == 0;
|
||||
}
|
||||
|
||||
bool ConstrainedRAPass::TryPostRAMerge(Ref LastNode, Ref CodeNode, IROp_Header* IROp) {
|
||||
if (IROp->Op == OP_PUSH) {
|
||||
auto LastOp = IR->GetOp<IROp_Header>(LastNode);
|
||||
if (LastOp->Op == OP_PUSH) {
|
||||
auto SP = PhysicalRegister(CodeNode);
|
||||
auto Push = IR->GetOp<IROp_Push>(CodeNode);
|
||||
auto LastPush = IR->GetOp<IROp_Push>(LastNode);
|
||||
auto LastOp = IR->GetOp<IROp_Header>(LastNode);
|
||||
|
||||
if (LastOp->Size == IROp->Size && LastPush->ValueSize == Push->ValueSize && SP == PhysicalRegister(LastNode) &&
|
||||
SP == PhysicalRegister(IROp->Args[1]) && SP == PhysicalRegister(LastOp->Args[1]) && SP != PhysicalRegister(IROp->Args[0]) &&
|
||||
SP != PhysicalRegister(LastOp->Args[0]) && Push->ValueSize >= OpSize::i32Bit) {
|
||||
if (IROp->Op == OP_PUSH && LastOp->Op == OP_PUSH) {
|
||||
auto SP = PhysicalRegister(CodeNode);
|
||||
auto Push = IR->GetOp<IROp_Push>(CodeNode);
|
||||
auto LastPush = IR->GetOp<IROp_Push>(LastNode);
|
||||
|
||||
IREmit->SetWriteCursorBefore(LastNode);
|
||||
IREmit->_PushTwo(IROp->Size, Push->ValueSize, IROp->Args[0], LastOp->Args[0], IROp->Args[1]);
|
||||
return true;
|
||||
}
|
||||
if (LastOp->Size == IROp->Size && LastPush->ValueSize == Push->ValueSize && SP == PhysicalRegister(LastNode) &&
|
||||
SP == PhysicalRegister(IROp->Args[1]) && SP == PhysicalRegister(LastOp->Args[1]) && SP != PhysicalRegister(IROp->Args[0]) &&
|
||||
SP != PhysicalRegister(LastOp->Args[0]) && Push->ValueSize >= OpSize::i32Bit) {
|
||||
|
||||
IREmit->SetWriteCursorBefore(LastNode);
|
||||
IREmit->_PushTwo(IROp->Size, Push->ValueSize, IROp->Args[0], LastOp->Args[0], IROp->Args[1]);
|
||||
IREmit->RemovePostRA(CodeNode);
|
||||
return true;
|
||||
}
|
||||
} else if (IROp->Op == OP_POP) {
|
||||
auto LastOp = IR->GetOp<IROp_Header>(LastNode);
|
||||
auto SP = PhysicalRegister(IROp->Args[0]);
|
||||
|
||||
if (LastOp->Op == OP_POP && LastOp->Size == IROp->Size && IROp->Size >= OpSize::i32Bit && SP == PhysicalRegister(LastOp->Args[0])) {
|
||||
IREmit->SetWriteCursorBefore(LastNode);
|
||||
IREmit->_PopTwo(IROp->Size, IROp->Args[0], LastOp->Args[1], IROp->Args[1]);
|
||||
IREmit->RemovePostRA(CodeNode);
|
||||
return true;
|
||||
}
|
||||
} else if ((IROp->Op == OP_DIV || IROp->Op == OP_UDIV) && IROp->Size >= OpSize::i32Bit) {
|
||||
// If Upper came from a sign/zero extension, we only need a 64-bit division.
|
||||
auto Op = IROp->CW<IR::IROp_Div>();
|
||||
if (!Op->Upper.IsInvalid() && PhysicalRegister(Op->Upper) == PhysicalRegister(LastNode)) {
|
||||
if (IROp->Op == OP_DIV ? IsSignext(LastOp, Op->Lower, IROp->Size) : IsZero(LastOp)) {
|
||||
Op->Upper.SetInvalid();
|
||||
return PhysicalRegister(LastNode) == PhysicalRegister(Op->OutRemainder);
|
||||
}
|
||||
}
|
||||
} else if (IROp->Op == OP_XGETBV && PhysicalRegister(IROp->Args[0]) == PhysicalRegister(LastNode) && LastOp->Op == OP_CONSTANT) {
|
||||
// Try to constant fold
|
||||
uint64_t ConstantFunction = LastOp->C<IROp_Constant>()->Constant;
|
||||
auto Op = IROp->CW<IR::IROp_XGetBV>();
|
||||
if (CPUID->DoesXCRFunctionReportConstantData(ConstantFunction)) {
|
||||
const auto Result = CPUID->RunXCRFunction(ConstantFunction);
|
||||
IREmit->SetWriteCursorBefore(CodeNode);
|
||||
IREmit->_Constant(Result.eax).Node->Reg = PhysicalRegister(Op->OutEAX).Raw;
|
||||
IREmit->_Constant(Result.edx).Node->Reg = PhysicalRegister(Op->OutEDX).Raw;
|
||||
IREmit->RemovePostRA(CodeNode);
|
||||
return false;
|
||||
}
|
||||
} else if (IROp->Op == OP_CPUID && PhysicalRegister(IROp->Args[0]) == PhysicalRegister(LastNode) && LastOp->Op == OP_CONSTANT) {
|
||||
// Try to constant fold. As a limitation of merging only 2 instructions, we
|
||||
// can only handle constant functions, not constant leafs. This could be
|
||||
// lifted if we generalized at a (significant) complexity cost.
|
||||
uint64_t ConstantFunction = LastOp->C<IROp_Constant>()->Constant;
|
||||
auto Op = IROp->CW<IR::IROp_CPUID>();
|
||||
|
||||
const auto SupportsConstant = CPUID->DoesFunctionReportConstantData(ConstantFunction);
|
||||
if (SupportsConstant.SupportsConstantFunction == CPUIDEmu::SupportsConstant::CONSTANT &&
|
||||
SupportsConstant.NeedsLeaf != CPUIDEmu::NeedsLeafConstant::NEEDSLEAFCONSTANT) {
|
||||
const auto Result = CPUID->RunFunction(ConstantFunction, 0 /* leaf */);
|
||||
|
||||
IREmit->SetWriteCursorBefore(CodeNode);
|
||||
IREmit->_Fence({FEXCore::IR::Fence_Inst});
|
||||
IREmit->_Constant(Result.eax).Node->Reg = PhysicalRegister(Op->OutEAX).Raw;
|
||||
IREmit->_Constant(Result.ebx).Node->Reg = PhysicalRegister(Op->OutEBX).Raw;
|
||||
IREmit->_Constant(Result.ecx).Node->Reg = PhysicalRegister(Op->OutECX).Raw;
|
||||
IREmit->_Constant(Result.edx).Node->Reg = PhysicalRegister(Op->OutEDX).Raw;
|
||||
IREmit->RemovePostRA(CodeNode);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Merge moves that are immediately consumed.
|
||||
//
|
||||
// x86 code inserts such moves to workaround x86's 2-address code. Because
|
||||
// arm64 is 3-address code, we can optimize these out.
|
||||
//
|
||||
// Note we rely on the short-circuiting here.
|
||||
if (PhysicalRegister(LastNode) == PhysicalRegister(CodeNode) && KillMove(LastOp, IROp, LastNode, CodeNode)) {
|
||||
LOGMAN_THROW_A_FMT(!PhysicalRegister(CodeNode).IsInvalid(), "invariant");
|
||||
|
||||
for (auto s = 0; s < IR::GetRAArgs(IROp->Op); ++s) {
|
||||
if (IROp->Args[s].IsImmediate() && PhysicalRegister(IROp->Args[s]) == PhysicalRegister(LastNode)) {
|
||||
IROp->Args[s].SetImmediate(PhysicalRegister(LastOp->Args[0]).Raw);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -473,7 +549,7 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) {
|
||||
// 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);
|
||||
auto Reg = DecodeSRAReg(IROp, CodeNode);
|
||||
|
||||
PreferredReg[IR->GetID(Node).Value] = Reg;
|
||||
GetClass(Reg)->RegToSSA[Reg.Reg] = CodeNode;
|
||||
@@ -519,23 +595,29 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) {
|
||||
|
||||
// Forward pass: Assign registers, spilling & optimizing as we go.
|
||||
for (auto [CodeNode, IROp] : IR->GetCode(BlockNode)) {
|
||||
// GuestOpcode does not read or write registers, and must be skipped for
|
||||
// push/pop merging. Since we'd be doing this check anyway for merging, do
|
||||
// the check now so we can skip the rest of the logic too.
|
||||
if (IROp->Op == OP_GUESTOPCODE) {
|
||||
// These do not read or write registers, and must be skipped for merging.
|
||||
// Since we'd be doing this check anyway for merging, do the check now so
|
||||
// we can skip the rest of the logic too.
|
||||
if (IROp->Op == OP_GUESTOPCODE || IROp->Op == OP_INLINECONSTANT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Static registers must be consistent at SRA load/store. Evict to ensure.
|
||||
if (auto Node = DecodeSRANode(IROp, CodeNode); Node != nullptr) {
|
||||
auto Reg = DecodeSRAReg(IROp);
|
||||
auto Reg = DecodeSRAReg(IROp, CodeNode);
|
||||
RegisterClass* Class = &Classes[Reg.Class];
|
||||
|
||||
if (!(Class->Available & (1u << Reg.Reg))) {
|
||||
Ref Old = Class->RegToSSA[Reg.Reg];
|
||||
|
||||
if (Old != Node) {
|
||||
// Before inserting instructions, we need to set the cursor and
|
||||
// reset LastNode so we don't merge across an inserted copy.
|
||||
// Otherwise, we would erroneously miss the copy when determining if
|
||||
// we can merge, and end up unsoundly merging a mov+xchg sequence.
|
||||
IREmit->SetWriteCursorBefore(CodeNode);
|
||||
LastNode = nullptr;
|
||||
|
||||
Ref Copy;
|
||||
|
||||
if (Reg.Class == FPRFixedClass) {
|
||||
@@ -566,6 +648,8 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) {
|
||||
|
||||
if (!IsInRegisterFile(Old)) {
|
||||
IREmit->SetWriteCursorBefore(CodeNode);
|
||||
LastNode = nullptr;
|
||||
|
||||
Ref Fill = InsertFill(Old);
|
||||
|
||||
AssignReg(IR->GetOp<IROp_Header>(Fill), Fill, IROp);
|
||||
@@ -599,7 +683,7 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) {
|
||||
}
|
||||
|
||||
// Assign destinations.
|
||||
if (GetHasDest(IROp->Op)) {
|
||||
if (GetHasDest(IROp->Op) && PhysicalRegister(CodeNode).IsInvalid()) {
|
||||
AssignReg(IROp, CodeNode, IROp);
|
||||
}
|
||||
|
||||
@@ -608,7 +692,6 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) {
|
||||
IREmit->RemovePostRA(CodeNode);
|
||||
} else if (LastNode && TryPostRAMerge(LastNode, CodeNode, IROp)) {
|
||||
// Merge adjacent instructions
|
||||
IREmit->RemovePostRA(CodeNode);
|
||||
IREmit->RemovePostRA(LastNode);
|
||||
LastNode = nullptr;
|
||||
} else {
|
||||
@@ -627,7 +710,7 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) {
|
||||
IR->GetHeader()->PostRA = true;
|
||||
}
|
||||
|
||||
fextl::unique_ptr<IR::RegisterAllocationPass> CreateRegisterAllocationPass() {
|
||||
return fextl::make_unique<ConstrainedRAPass>();
|
||||
fextl::unique_ptr<IR::RegisterAllocationPass> CreateRegisterAllocationPass(const FEXCore::CPUIDEmu* CPUID) {
|
||||
return fextl::make_unique<ConstrainedRAPass>(CPUID);
|
||||
}
|
||||
} // namespace FEXCore::IR
|
||||
@@ -161,6 +161,7 @@ private:
|
||||
const FEXCore::HostFeatures& Features;
|
||||
const OpSize GPROpSize;
|
||||
bool ReducedPrecisionMode;
|
||||
FEX_CONFIG_OPT(DisableVixlIndirectCalls, DISABLE_VIXL_INDIRECT_RUNTIME_CALLS);
|
||||
|
||||
// Helpers
|
||||
Ref RotateRight8(uint32_t V, Ref Amount);
|
||||
@@ -774,12 +775,26 @@ void X87StackOptimization::Run(IREmitter* Emit) {
|
||||
|
||||
Ref SinValue {};
|
||||
Ref CosValue {};
|
||||
if (ReducedPrecisionMode) {
|
||||
SinValue = IREmit->_F64SIN(St0);
|
||||
CosValue = IREmit->_F64COS(St0);
|
||||
} else {
|
||||
SinValue = IREmit->_F80SIN(St0);
|
||||
CosValue = IREmit->_F80COS(St0);
|
||||
|
||||
#ifdef VIXL_SIMULATOR
|
||||
if (DisableVixlIndirectCalls() == 0) {
|
||||
if (ReducedPrecisionMode) {
|
||||
SinValue = IREmit->_F64SIN(St0);
|
||||
CosValue = IREmit->_F64COS(St0);
|
||||
} else {
|
||||
SinValue = IREmit->_F80SIN(St0);
|
||||
CosValue = IREmit->_F80COS(St0);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
SinValue = IREmit->_AllocateFPR(OpSize::i128Bit, OpSize::i128Bit);
|
||||
CosValue = IREmit->_AllocateFPR(OpSize::i128Bit, OpSize::i128Bit);
|
||||
if (ReducedPrecisionMode) {
|
||||
IREmit->_F64SINCOS(St0, SinValue, CosValue);
|
||||
} else {
|
||||
IREmit->_F80SINCOS(St0, SinValue, CosValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Push values
|
||||
|
||||
@@ -31,11 +31,12 @@ union PhysicalRegister {
|
||||
: Raw(Node->Reg) {}
|
||||
|
||||
static const PhysicalRegister Invalid() {
|
||||
return PhysicalRegister(InvalidClass, InvalidReg);
|
||||
return PhysicalRegister(InvalidClass, 0);
|
||||
}
|
||||
|
||||
bool IsInvalid() const {
|
||||
return *this == Invalid();
|
||||
static_assert(InvalidClass == 0);
|
||||
return Raw == 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -209,6 +209,7 @@ enum FallbackHandlerIndex {
|
||||
OPINDEX_F80SQRT,
|
||||
OPINDEX_F80SIN,
|
||||
OPINDEX_F80COS,
|
||||
OPINDEX_F80SINCOS,
|
||||
OPINDEX_F80XTRACT_EXP,
|
||||
OPINDEX_F80XTRACT_SIG,
|
||||
OPINDEX_F80BCDSTORE,
|
||||
@@ -228,6 +229,7 @@ enum FallbackHandlerIndex {
|
||||
// Double Precision
|
||||
OPINDEX_F64SIN,
|
||||
OPINDEX_F64COS,
|
||||
OPINDEX_F64SINCOS,
|
||||
OPINDEX_F64TAN,
|
||||
OPINDEX_F64ATAN,
|
||||
OPINDEX_F64F2XM1,
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <cstdarg>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/color.h>
|
||||
|
||||
namespace LogMan {
|
||||
enum DebugLevels {
|
||||
@@ -14,23 +15,29 @@ enum DebugLevels {
|
||||
ERROR = 2, ///< Only Errors printed
|
||||
DEBUG = 3, ///< Debug messages added
|
||||
INFO = 4, ///< Info messages added
|
||||
STDOUT = 5, ///< Meant to go to STDOUT
|
||||
STDERR = 6, ///< Meant to go to STDERR
|
||||
};
|
||||
|
||||
static inline const char* DebugLevelStr(uint32_t Level) {
|
||||
switch (Level) {
|
||||
case NONE: return "NONE";
|
||||
case ASSERT: return "ASSERT";
|
||||
case ERROR: return "ERROR";
|
||||
case DEBUG: return "DEBUG";
|
||||
case INFO: return "INFO";
|
||||
case STDOUT: return "STDOUT";
|
||||
case STDERR: return "STDERR";
|
||||
case ASSERT: return "A";
|
||||
case ERROR: return "E";
|
||||
case DEBUG: return "D";
|
||||
case INFO: return "I";
|
||||
default: return "???"; break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline fmt::text_style DebugLevelStyle(uint32_t Level) {
|
||||
switch (Level) {
|
||||
case LogMan::ASSERT: return fmt::bg(fmt::color::red) | fmt::emphasis::bold | fmt::fg(fmt::color::white);
|
||||
case LogMan::ERROR: return fmt::fg(fmt::color::red);
|
||||
case LogMan::DEBUG: return fmt::fg(fmt::color::gray);
|
||||
case LogMan::INFO: return fmt::fg(fmt::color::green);
|
||||
default: return {}; break;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr DebugLevels MSG_LEVEL = INFO;
|
||||
|
||||
// Note that all logging functions with the Fmt or _FMT suffix on them expect
|
||||
@@ -104,16 +111,6 @@ namespace Msg {
|
||||
MFmtImpl(INFO, fmt, fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
static inline void OutFmt(const char* fmt, const Args&... args) {
|
||||
MFmtImpl(STDOUT, fmt, fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
static inline void ErrFmt(const char* fmt, const Args&... args) {
|
||||
MFmtImpl(STDERR, fmt, fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
|
||||
template<typename... Args>
|
||||
static inline void AFmt(const char* fmt, const Args&... args) {
|
||||
|
||||
@@ -445,6 +445,10 @@ public:
|
||||
: ThreadAllocator {Allocator}
|
||||
, Size {Size} {}
|
||||
|
||||
~PoolBufferWithTimedRetirement() {
|
||||
UnclaimBuffer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the owned buffer or allocate another one from the `Allocator`
|
||||
*
|
||||
|
||||
@@ -1,45 +1,36 @@
|
||||
[中文](https://github.com/FEX-Emu/FEX/blob/main/docs/Readme_CN.md)
|
||||
# FEX - Fast x86 emulation frontend
|
||||
FEX allows you to run x86 and x86-64 binaries on an AArch64 host, similar to qemu-user and box86.
|
||||
It has native support for a rootfs overlay, so you don't need to chroot, as well as some thunklibs so it can forward things like GL to the host.
|
||||
FEX presents a Linux 5.15+ interface to the guest, and supports only AArch64 as a host.
|
||||
FEX is very much work in progress, so expect things to change.
|
||||
# FEX: Emulate x86 Programs on ARM64
|
||||
FEX allows you to run x86 applications on ARM64 Linux devices, similar to qemu-user and box64.
|
||||
It offers broad compatibility with both 32-bit and 64-bit binaries, and it can be used alongside Wine/Proton to play Windows games.
|
||||
|
||||
It supports forwarding API calls to host system libraries like OpenGL or Vulkan to reduce emulation overhead.
|
||||
An experimental code cache helps minimize in-game stuttering as much as possible.
|
||||
Furthermore, a per-app configuration system allows tweaking performance per game, e.g. by skipping costly memory model emulation.
|
||||
We also provide a user-friendly FEXConfig GUI to explore and change these settings.
|
||||
|
||||
## Quick start guide
|
||||
## Prerequisites
|
||||
FEX requires ARMv8.0+ hardware. It has been tested with the following Linux distributions, though others are likely to work as well:
|
||||
|
||||
- Arch Linux
|
||||
- Fedora Linux
|
||||
- openSUSE
|
||||
- Ubuntu 22.04/24.04/24.10
|
||||
|
||||
An x86-64 RootFS is required and can be downloaded using our `FEXRootFSFetcher` tool for many distributions.
|
||||
For other distributions you will need to generate your own RootFS (our [wiki page](https://wiki.fex-emu.com/index.php/Development:Setting_up_RootFS) might help).
|
||||
|
||||
## Quick Start
|
||||
### For Ubuntu 22.04, 24.04 and 24.10
|
||||
Execute the following command in the terminal to install FEX through a PPA.
|
||||
|
||||
`curl --silent https://raw.githubusercontent.com/FEX-Emu/FEX/main/Scripts/InstallFEX.py --output /tmp/InstallFEX.py && python3 /tmp/InstallFEX.py && rm /tmp/InstallFEX.py`
|
||||
```sh
|
||||
curl --silent https://raw.githubusercontent.com/FEX-Emu/FEX/main/Scripts/InstallFEX.py | python3
|
||||
```
|
||||
|
||||
This command will walk you through installing FEX through a PPA, and downloading a RootFS for use with FEX.
|
||||
|
||||
Ubuntu PPA is updated with our monthly releases.
|
||||
|
||||
### For everyone else
|
||||
Please see [Building FEX](#building-fex).
|
||||
|
||||
## Getting Started
|
||||
FEX has been tested to build and run on ARMv8.0+ hardware.
|
||||
ARMv7 hardware will not work.
|
||||
Expected operating system usage is Linux. FEX has been tested with the following Linux OSes:
|
||||
|
||||
- Ubuntu 22.04
|
||||
- Ubuntu 24.04
|
||||
- Ubuntu 24.10
|
||||
- Arch Linux
|
||||
|
||||
On AArch64 hosts the user **MUST** have an x86-64 RootFS [Creating a RootFS](#RootFS-Generation).
|
||||
### For other Distributions
|
||||
Follow the guide on the official FEX-Emu Wiki [here](https://wiki.fex-emu.com/index.php/Development:Setting_up_FEX).
|
||||
|
||||
### Navigating the Source
|
||||
See the [Source Outline](docs/SourceOutline.md) for more information.
|
||||
|
||||
### Building FEX
|
||||
Follow the guide on the official FEX-Emu Wiki [here](https://wiki.fex-emu.com/index.php/Development:Setting_up_FEX).
|
||||
|
||||
### RootFS generation
|
||||
AArch64 hosts require a rootfs for running applications.
|
||||
Follow the guide on the wiki page for seeing how to set up the rootfs from scratch
|
||||
https://wiki.fex-emu.com/index.php/Development:Setting_up_RootFS
|
||||
|
||||

|
||||
@@ -186,7 +186,7 @@ void CodeSizeValidation::CalculateBaseStats(FEXCore::Context::Context* CTX, FEXC
|
||||
SetupInfoDisabled = false;
|
||||
}
|
||||
|
||||
static CodeSizeValidation Validation {};
|
||||
static CodeSizeValidation* Validation {};
|
||||
} // namespace CodeSize
|
||||
|
||||
void MsgHandler(LogMan::DebugLevels Level, const char* Message) {
|
||||
@@ -194,19 +194,19 @@ void MsgHandler(LogMan::DebugLevels Level, const char* Message) {
|
||||
|
||||
if (Level == LogMan::INFO) {
|
||||
// Disassemble information is sent through the Info log level.
|
||||
if (!CodeSize::Validation.ParseMessage(Message)) {
|
||||
if (!CodeSize::Validation->ParseMessage(Message)) {
|
||||
return;
|
||||
}
|
||||
if (CodeSize::Validation.InfoPrintingDisabled()) {
|
||||
if (CodeSize::Validation->InfoPrintingDisabled()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fextl::fmt::print("[{}] {}\n", CharLevel, Message);
|
||||
fextl::fmt::print("{} {}\n", CharLevel, Message);
|
||||
}
|
||||
|
||||
void AssertHandler(const char* Message) {
|
||||
fextl::fmt::print("[ASSERT] {}\n", Message);
|
||||
fextl::fmt::print("A {}\n", Message);
|
||||
|
||||
// make sure buffers are flushed
|
||||
fflush(nullptr);
|
||||
@@ -248,7 +248,7 @@ static bool TestInstructions(FEXCore::Context::Context* CTX, FEXCore::Core::Inte
|
||||
LogMan::Msg::IFmt("Compiling instruction '{}'", CurrentTest->TestInst);
|
||||
|
||||
TestData[i] =
|
||||
CodeSize::Validation.CompileAndGetStats(CTX, Thread, reinterpret_cast<void*>(CodeRIP), CurrentTest->CodeSize, CurrentTest->x86InstCount);
|
||||
CodeSize::Validation->CompileAndGetStats(CTX, Thread, reinterpret_cast<void*>(CodeRIP), CurrentTest->CodeSize, CurrentTest->x86InstCount);
|
||||
|
||||
// Go to the next test.
|
||||
CurrentTest = reinterpret_cast<const TestInfo*>(&CurrentTest->Code[CurrentTest->CodeSize]);
|
||||
@@ -467,6 +467,11 @@ public:
|
||||
|
||||
int main(int argc, char** argv, char** const envp) {
|
||||
FEXCore::Allocator::GLIBCScopedFault GLIBFaultScope;
|
||||
|
||||
// Initialize early as the message handlers use it.
|
||||
CodeSize::CodeSizeValidation Validation {};
|
||||
CodeSize::Validation = &Validation;
|
||||
|
||||
LogMan::Throw::InstallHandler(AssertHandler);
|
||||
LogMan::Msg::InstallHandler(MsgHandler);
|
||||
FEXCore::Config::Initialize();
|
||||
@@ -659,7 +664,7 @@ int main(int argc, char** argv, char** const envp) {
|
||||
auto ParentThread = CTX->CreateThread(0, 0);
|
||||
|
||||
// Calculate the base stats for instruction testing.
|
||||
CodeSize::Validation.CalculateBaseStats(CTX.get(), ParentThread);
|
||||
CodeSize::Validation->CalculateBaseStats(CTX.get(), ParentThread);
|
||||
|
||||
// Test all the instructions.
|
||||
auto Result = TestInstructions(CTX.get(), ParentThread, argc >= 2 ? argv[2] : nullptr) ? 0 : 1;
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
add_executable(FEXBash FEXBash.cpp)
|
||||
target_include_directories(FEXBash
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Source/
|
||||
${CMAKE_BINARY_DIR}/generated
|
||||
)
|
||||
target_include_directories(FEXBash PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Source/)
|
||||
|
||||
target_link_libraries(FEXBash
|
||||
PRIVATE
|
||||
|
||||
@@ -6,7 +6,6 @@ desc: Launches bash under FEX and passes arguments via -c to it
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "ConfigDefines.h"
|
||||
#include "Common/ArgumentLoader.h"
|
||||
|
||||
#include <FEXCore/Config/Config.h>
|
||||
@@ -25,11 +24,21 @@ int main(int argc, char** argv, char** const envp) {
|
||||
// Use /bin/sh for -c commands and /bin/bash for interactive mode
|
||||
const char* BashPath = Args.empty() ? "/bin/bash" : "/bin/sh";
|
||||
|
||||
std::string FEXInterpreterPath = std::filesystem::path(argv[0]).parent_path().string() + "FEXInterpreter";
|
||||
std::string FEXInterpreterPath = std::filesystem::path(argv[0]).parent_path().string() + "/FEXInterpreter";
|
||||
|
||||
// Check if a local FEXInterpreter to FEXBash exists
|
||||
// If it does then it takes priority over the installed one
|
||||
if (!std::filesystem::exists(FEXInterpreterPath)) {
|
||||
FEXInterpreterPath = FEXCore::Config::FindContainerPrefix() + FEXINTERPRETER_PATH;
|
||||
char FEXBashPath[PATH_MAX];
|
||||
auto Result = readlink("/proc/self/exe", FEXBashPath, PATH_MAX);
|
||||
if (Result != -1) {
|
||||
FEXInterpreterPath = std::filesystem::path(&FEXBashPath[0], &FEXBashPath[Result]).parent_path().string() + "/FEXInterpreter";
|
||||
}
|
||||
|
||||
if (!std::filesystem::exists(FEXInterpreterPath)) {
|
||||
fmt::print(stderr, "Could not locate FEXInterpreter executable\n");
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
const char* FEXArgs[] = {
|
||||
FEXInterpreterPath.c_str(),
|
||||
|
||||
@@ -446,6 +446,6 @@ int main(int Argc, char** Argv) {
|
||||
}
|
||||
|
||||
ConfigRuntime Runtime(ConfigFilename.c_str());
|
||||
|
||||
App.setWindowIcon(QIcon(":/icon.png"));
|
||||
return App.exec();
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
@@ -1,6 +1,7 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>main.qml</file>
|
||||
<file>icon.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="/dialogs">
|
||||
<file alias="FileDialog.qml">qt5/FileDialog.qml</file>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>main.qml</file>
|
||||
<file>icon.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="/dialogs">
|
||||
<file alias="FileDialog.qml">qt6/FileDialog.qml</file>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#include "ConfigDefines.h"
|
||||
#include "Common/cpp-optparse/OptionParser.h"
|
||||
#include "Common/Config.h"
|
||||
#include "Common/FEXServerClient.h"
|
||||
@@ -155,7 +154,13 @@ int main(int argc, char** argv, char** envp) {
|
||||
}
|
||||
|
||||
if (Options.is_set_by_user("install_prefix")) {
|
||||
fprintf(stdout, FEX_INSTALL_PREFIX "\n");
|
||||
char SelfPath[PATH_MAX];
|
||||
auto Result = readlink("/proc/self/exe", SelfPath, PATH_MAX);
|
||||
if (Result == -1) {
|
||||
Result = 0;
|
||||
}
|
||||
auto InstallPrefix = std::filesystem::path(&SelfPath[0], &SelfPath[Result]).parent_path().parent_path().string();
|
||||
fprintf(stdout, "%s\n", InstallPrefix.c_str());
|
||||
}
|
||||
|
||||
if (Options.is_set_by_user("current_rootfs")) {
|
||||
|
||||
@@ -68,24 +68,22 @@ namespace {
|
||||
static bool SilentLog {};
|
||||
static int OutputFD {STDERR_FILENO};
|
||||
|
||||
// Set an empty style to disable coloring when FEXServer output is e.g. piped to a file
|
||||
static bool DisableOutputColors {};
|
||||
|
||||
void MsgHandler(LogMan::DebugLevels Level, const char* Message) {
|
||||
if (SilentLog) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto Output = fextl::fmt::format("[{}] {}\n", LogMan::DebugLevelStr(Level), Message);
|
||||
const auto Style = DisableOutputColors ? fmt::text_style {} : LogMan::DebugLevelStyle(Level);
|
||||
const auto Output = fextl::fmt::format("{} {}\n", fmt::styled(LogMan::DebugLevelStr(Level), Style), Message);
|
||||
write(OutputFD, Output.c_str(), Output.size());
|
||||
fsync(OutputFD);
|
||||
}
|
||||
|
||||
void AssertHandler(const char* Message) {
|
||||
if (SilentLog) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto Output = fextl::fmt::format("[ASSERT] {}\n", Message);
|
||||
write(OutputFD, Output.c_str(), Output.size());
|
||||
fsync(OutputFD);
|
||||
return MsgHandler(LogMan::ASSERT, Message);
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
@@ -368,10 +366,11 @@ int main(int argc, char** argv, char** const envp) {
|
||||
//
|
||||
// We want to maintain the original output location otherwise we
|
||||
// can run in to problems of writing to some file
|
||||
auto LogFD = OutputFD;
|
||||
if (LogFile == "stderr") {
|
||||
OutputFD = dup(STDERR_FILENO);
|
||||
LogFD = dup(STDERR_FILENO);
|
||||
} else if (LogFile == "stdout") {
|
||||
OutputFD = dup(STDOUT_FILENO);
|
||||
LogFD = dup(STDOUT_FILENO);
|
||||
} else if (LogFile == "server") {
|
||||
FEXServerLogging::FEXServerFD = FEXServerClient::RequestLogFD(FEXServerClient::GetServerFD());
|
||||
if (FEXServerLogging::FEXServerFD != -1) {
|
||||
@@ -380,9 +379,17 @@ int main(int argc, char** argv, char** const envp) {
|
||||
}
|
||||
} else if (!LogFile.empty()) {
|
||||
constexpr int USER_PERMS = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
OutputFD = open(LogFile.c_str(), O_CREAT | O_CLOEXEC | O_WRONLY, USER_PERMS);
|
||||
LogFD = open(LogFile.c_str(), O_CREAT | O_CLOEXEC | O_WRONLY, USER_PERMS);
|
||||
}
|
||||
|
||||
if (LogFD == -1) {
|
||||
LogMan::Msg::EFmt("Couldn't open log file. Going Silent.");
|
||||
SilentLog = true;
|
||||
} else {
|
||||
OutputFD = LogFD;
|
||||
}
|
||||
}
|
||||
DisableOutputColors = !isatty(OutputFD);
|
||||
|
||||
if (StartupSleep() && (StartupSleepProcName().empty() || Program.ProgramName == StartupSleepProcName())) {
|
||||
LogMan::Msg::IFmt("[{}][{}] Sleeping for {} seconds", ::getpid(), Program.ProgramName, StartupSleep());
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include "Common/Config.h"
|
||||
#include "Common/FEXServerClient.h"
|
||||
|
||||
#include <fmt/color.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
@@ -23,23 +25,38 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/wait.h>
|
||||
#include <termios.h>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
|
||||
static timespec StartTime {};
|
||||
|
||||
// Set an empty style to disable coloring when FEXServer output is e.g. piped to a file
|
||||
static std::optional<fmt::text_style> DisableColors = isatty(STDOUT_FILENO) ? std::nullopt : std::optional {fmt::text_style {}};
|
||||
|
||||
namespace Logging {
|
||||
void MsgHandler(LogMan::DebugLevels Level, const char* Message) {
|
||||
const auto Output = fmt::format("[{}] {}\n", LogMan::DebugLevelStr(Level), Message);
|
||||
const auto Output = fmt::format("{} {}\n", fmt::styled(LogMan::DebugLevelStr(Level), DisableColors.value_or(DebugLevelStyle(Level))), Message);
|
||||
write(STDOUT_FILENO, Output.c_str(), Output.size());
|
||||
}
|
||||
|
||||
void AssertHandler(const char* Message) {
|
||||
const auto Output = fmt::format("[ASSERT] {}\n", Message);
|
||||
write(STDOUT_FILENO, Output.c_str(), Output.size());
|
||||
return MsgHandler(LogMan::ASSERT, Message);
|
||||
}
|
||||
|
||||
void ClientMsgHandler(int FD, FEXServerClient::Logging::PacketMsg* const Msg, const char* MsgStr) {
|
||||
const auto Output = fmt::format("[{}][{}.{}][{}.{}] {}\n", LogMan::DebugLevelStr(Msg->Level), Msg->Header.Timestamp.tv_sec,
|
||||
Msg->Header.Timestamp.tv_nsec, Msg->Header.PID, Msg->Header.TID, MsgStr);
|
||||
if (!StartTime.tv_sec && !StartTime.tv_nsec) {
|
||||
StartTime = Msg->Header.Timestamp;
|
||||
}
|
||||
auto seconds = Msg->Header.Timestamp.tv_sec - StartTime.tv_sec - (Msg->Header.Timestamp.tv_nsec < StartTime.tv_nsec);
|
||||
auto nanos = (1'000'000'000 + Msg->Header.Timestamp.tv_nsec - StartTime.tv_nsec) % 1'000'000'000;
|
||||
char Metadata[128];
|
||||
auto Cursor =
|
||||
fmt::format_to(&Metadata[0], DisableColors.value_or(LogMan::DebugLevelStyle(Msg->Level)), "{}", LogMan::DebugLevelStr(Msg->Level));
|
||||
Cursor = fmt::format_to(Cursor, DisableColors.value_or(fmt::fg(fmt::color::light_gray)), " {}|{} ", Msg->Header.PID, Msg->Header.TID);
|
||||
Cursor = fmt::format_to(Cursor, DisableColors.value_or(fmt::fg(fmt::color::gray)), "{}.{:03}", seconds, nanos / 1000000);
|
||||
*Cursor = 0;
|
||||
auto Output = fmt::format("{} {}\n", Metadata, MsgStr);
|
||||
write(STDERR_FILENO, Output.c_str(), Output.size());
|
||||
}
|
||||
} // namespace Logging
|
||||
@@ -50,6 +67,9 @@ void ActionHandler(int sig, siginfo_t* info, void* context) {
|
||||
if (sig == SIGINT) {
|
||||
// Someone trying to kill us. Shutdown.
|
||||
ProcessPipe::Shutdown();
|
||||
|
||||
// Clear "^C" string that most terminals print when pressing Ctrl+C.
|
||||
fprintf(stderr, "\r");
|
||||
return;
|
||||
}
|
||||
_exit(1);
|
||||
|
||||
@@ -545,6 +545,19 @@ EmulatedFDManager::EmulatedFDManager(FEXCore::Context::Context* ctx)
|
||||
return FD;
|
||||
};
|
||||
|
||||
// Wine reads this to ensure TSC is trusted by the kernel. Otherwise it falls back to maximum clock speed of the CPU cores.
|
||||
// Without this, games like Horizon Zero Dawn would run their physics in slow-motion.
|
||||
FDReadCreators["/sys/devices/system/clocksource/clocksource0/current_clocksource"] =
|
||||
[&](FEXCore::Context::Context* ctx, int32_t fd, const char* pathname, int32_t flags, mode_t mode) -> int32_t {
|
||||
int FD = GenTmpFD(pathname, flags);
|
||||
const char source[] = "tsc\n";
|
||||
// + 1 to ensure null at the end
|
||||
write(FD, source, strlen(source) + 1);
|
||||
lseek(FD, 0, SEEK_SET);
|
||||
SealTmpFD(FD);
|
||||
return FD;
|
||||
};
|
||||
|
||||
auto NumCPUCores = [&](FEXCore::Context::Context* ctx, int32_t fd, const char* pathname, int32_t flags, mode_t mode) -> int32_t {
|
||||
int FD = GenTmpFD(pathname, flags);
|
||||
write(FD, (void*)&cpus_online.at(0), cpus_online.size());
|
||||
|
||||
@@ -263,7 +263,13 @@ FileManager::FileManager(FEXCore::Context::Context* ctx)
|
||||
}
|
||||
|
||||
// Now that we loaded the thunks object, walk through and ensure dependencies are enabled as well
|
||||
const auto& ThunkGuestPath = Is64BitMode() ? ThunkGuestLibs() : ThunkGuestLibs32();
|
||||
auto ThunkGuestPath = ThunkGuestLibs();
|
||||
while (ThunkGuestPath.ends_with('/')) {
|
||||
ThunkGuestPath.pop_back();
|
||||
}
|
||||
if (!Is64BitMode()) {
|
||||
ThunkGuestPath += "_32";
|
||||
}
|
||||
for (const auto& DBObject : ThunkDB) {
|
||||
if (!DBObject.second.Enabled) {
|
||||
continue;
|
||||
|
||||
@@ -171,7 +171,6 @@ private:
|
||||
FEX_CONFIG_OPT(Filename, APP_FILENAME);
|
||||
FEX_CONFIG_OPT(LDPath, ROOTFS);
|
||||
FEX_CONFIG_OPT(ThunkGuestLibs, THUNKGUESTLIBS);
|
||||
FEX_CONFIG_OPT(ThunkGuestLibs32, THUNKGUESTLIBS32);
|
||||
FEX_CONFIG_OPT(ThunkConfig, THUNKCONFIG);
|
||||
FEX_CONFIG_OPT(AppConfigName, APP_CONFIG_NAME);
|
||||
FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE);
|
||||
|
||||
@@ -82,7 +82,7 @@ uint64_t SetSignal(GuestSAMask* Set, int Signal) {
|
||||
void SignalDelegator::HandleSignal(FEX::HLE::ThreadStateObject* Thread, int Signal, void* Info, void* UContext) {
|
||||
// Let the host take first stab at handling the signal
|
||||
if (!Thread) {
|
||||
LogMan::Msg::AFmt("[{}] Thread has received a signal and hasn't registered itself with the delegate! Programming error!",
|
||||
LogMan::Msg::AFmt("Thread {} has received a signal and hasn't registered itself with the delegate! Programming error!",
|
||||
FHU::Syscalls::gettid());
|
||||
} else {
|
||||
SignalHandler& Handler = HostHandlers[Signal];
|
||||
|
||||
@@ -93,7 +93,7 @@ template<typename T>
|
||||
static void SetXStateInfo(T* xstate, bool is_avx_enabled) {
|
||||
auto* fpstate = &xstate->fpstate;
|
||||
|
||||
fpstate->sw_reserved.magic1 = FEXCore::x86_64::fpx_sw_bytes::FP_XSTATE_MAGIC;
|
||||
fpstate->sw_reserved.magic1 = is_avx_enabled ? FEXCore::x86_64::fpx_sw_bytes::FP_XSTATE_MAGIC : 0;
|
||||
fpstate->sw_reserved.extended_size = is_avx_enabled ? sizeof(T) : 0;
|
||||
|
||||
fpstate->sw_reserved.xfeatures |= FEXCore::x86_64::fpx_sw_bytes::FEATURE_FP | FEXCore::x86_64::fpx_sw_bytes::FEATURE_SSE;
|
||||
|
||||
@@ -25,6 +25,7 @@ $end_info$
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/klog.h>
|
||||
#include <sys/personality.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <git_version.h>
|
||||
@@ -98,5 +99,24 @@ void RegisterInfo(FEX::HLE::SyscallHandler* Handler) {
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, unsigned int operation, unsigned int flags, void* args) -> uint64_t {
|
||||
return FEX::HLE::_SyscallHandler->SeccompEmulator.Handle(Frame, operation, flags, args);
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(
|
||||
ptrace, [](FEXCore::Core::CpuStateFrame* Frame, int /*enum __ptrace_request*/ request, pid_t pid, void* addr, void* data) -> uint64_t {
|
||||
uint64_t Result {};
|
||||
|
||||
switch (request) {
|
||||
case PTRACE_PEEKTEXT:
|
||||
case PTRACE_PEEKDATA:
|
||||
case PTRACE_POKETEXT:
|
||||
case PTRACE_POKEDATA:
|
||||
case PTRACE_ATTACH:
|
||||
case PTRACE_DETACH:
|
||||
// Passthrough these requests. Allows Wine to run the Ubisoft launcher.
|
||||
Result = ::syscall(SYSCALL_DEF(ptrace), request, pid, addr, data);
|
||||
SYSCALL_ERRNO();
|
||||
default: break;
|
||||
}
|
||||
// We don't support this
|
||||
return -EPERM;
|
||||
});
|
||||
}
|
||||
} // namespace FEX::HLE
|
||||
@@ -27,13 +27,6 @@ struct CpuStateFrame;
|
||||
|
||||
namespace FEX::HLE {
|
||||
void RegisterStubs(FEX::HLE::SyscallHandler* Handler) {
|
||||
|
||||
REGISTER_SYSCALL_IMPL(
|
||||
ptrace, [](FEXCore::Core::CpuStateFrame* Frame, int /*enum __ptrace_request*/ request, pid_t pid, void* addr, void* data) -> uint64_t {
|
||||
// We don't support this
|
||||
return -EPERM;
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(modify_ldt, [](FEXCore::Core::CpuStateFrame* Frame, int func, void* ptr, unsigned long bytecount) -> uint64_t {
|
||||
SYSCALL_STUB(modify_ldt);
|
||||
});
|
||||
|
||||
@@ -207,11 +207,14 @@ private:
|
||||
|
||||
FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE);
|
||||
FEX_CONFIG_OPT(ThunkHostLibsPath, THUNKHOSTLIBS);
|
||||
FEX_CONFIG_OPT(ThunkHostLibsPath32, THUNKHOSTLIBS32);
|
||||
};
|
||||
|
||||
void ThunkHandler_impl::LoadLib(std::string_view Name) {
|
||||
auto SOName = (Is64BitMode() ? ThunkHostLibsPath() : ThunkHostLibsPath32()) + "/" + Name.data() + "-host.so";
|
||||
auto SOName = ThunkHostLibsPath();
|
||||
while (SOName.ends_with('/')) {
|
||||
SOName.pop_back();
|
||||
}
|
||||
SOName = fmt::format("{}{}/{}-host.so", SOName, (Is64BitMode() ? "" : "_32"), Name);
|
||||
|
||||
LogMan::Msg::DFmt("LoadLib: {} -> {}", Name, SOName);
|
||||
|
||||
|
||||
@@ -656,8 +656,9 @@ void LoadGuestVDSOSymbols(bool Is64Bit, char* VDSOBase) {
|
||||
|
||||
void LoadUnique32BitSigreturn(VDSOMapping* Mapping, FEX::HLE::SyscallHandler* const Handler) {
|
||||
// Hardcoded to one page for now
|
||||
const auto PageSize = sysconf(_SC_PAGESIZE);
|
||||
Mapping->OptionalMappingSize = PageSize > 0 ? PageSize : FEXCore::Utils::FEX_PAGE_SIZE;
|
||||
auto PageSize = sysconf(_SC_PAGESIZE);
|
||||
PageSize = PageSize > 0 ? PageSize : FEXCore::Utils::FEX_PAGE_SIZE;
|
||||
Mapping->OptionalMappingSize = PageSize;
|
||||
|
||||
// First 64bit page
|
||||
constexpr uintptr_t LOCATION_MAX = 0x1'0000'0000;
|
||||
@@ -738,14 +739,11 @@ void UnloadVDSOMapping(const VDSOMapping& Mapping) {
|
||||
VDSOMapping LoadVDSOThunks(bool Is64Bit, FEX::HLE::SyscallHandler* const Handler) {
|
||||
VDSOMapping Mapping {};
|
||||
FEX_CONFIG_OPT(ThunkGuestLibs, THUNKGUESTLIBS);
|
||||
FEX_CONFIG_OPT(ThunkGuestLibs32, THUNKGUESTLIBS32);
|
||||
|
||||
fextl::string ThunkGuestPath {};
|
||||
if (Is64Bit) {
|
||||
ThunkGuestPath = fextl::fmt::format("{}/libVDSO-guest.so", ThunkGuestLibs());
|
||||
} else {
|
||||
ThunkGuestPath = fextl::fmt::format("{}/libVDSO-guest.so", ThunkGuestLibs32());
|
||||
fextl::string ThunkGuestPath = ThunkGuestLibs();
|
||||
while (ThunkGuestPath.ends_with('/')) {
|
||||
ThunkGuestPath.pop_back();
|
||||
}
|
||||
ThunkGuestPath = fextl::fmt::format("{}{}/libVDSO-guest.so", ThunkGuestPath, Is64Bit ? "" : "_32");
|
||||
// Load VDSO if we can
|
||||
int VDSOFD = ::open(ThunkGuestPath.c_str(), O_RDONLY);
|
||||
|
||||
|
||||
@@ -49,11 +49,11 @@ $end_info$
|
||||
#endif
|
||||
|
||||
void MsgHandler(LogMan::DebugLevels Level, const char* Message) {
|
||||
fextl::fmt::print("[{}] {}\n", LogMan::DebugLevelStr(Level), Message);
|
||||
fextl::fmt::print("{} {}\n", LogMan::DebugLevelStr(Level), Message);
|
||||
}
|
||||
|
||||
void AssertHandler(const char* Message) {
|
||||
fextl::fmt::print("[ASSERT] {}\n", Message);
|
||||
fextl::fmt::print("A {}\n", Message);
|
||||
|
||||
// make sure buffers are flushed
|
||||
fflush(nullptr);
|
||||
|
||||
@@ -14,7 +14,7 @@ void (*WineDbgOut)(const char* Message);
|
||||
FILE* LogFile;
|
||||
|
||||
static void MsgHandler(LogMan::DebugLevels Level, const char* Message) {
|
||||
const auto Output = fextl::fmt::format("[{}][{:X}] {}\n", LogMan::DebugLevelStr(Level), GetCurrentThreadId(), Message);
|
||||
const auto Output = fextl::fmt::format("{} {:X} {}\n", LogMan::DebugLevelStr(Level), GetCurrentThreadId(), Message);
|
||||
if (WineDbgOut) {
|
||||
WineDbgOut(Output.c_str());
|
||||
} else if (LogFile) {
|
||||
@@ -23,7 +23,7 @@ static void MsgHandler(LogMan::DebugLevels Level, const char* Message) {
|
||||
}
|
||||
|
||||
static void AssertHandler(const char* Message) {
|
||||
const auto Output = fextl::fmt::format("[ASSERT] {}\n", Message);
|
||||
const auto Output = fextl::fmt::format("A {}\n", Message);
|
||||
if (WineDbgOut) {
|
||||
WineDbgOut(Output.c_str());
|
||||
} else if (LogFile) {
|
||||
|
||||
@@ -806,8 +806,12 @@ void GenerateThunkLibsAction::OnAnalysisComplete(clang::ASTContext& context) {
|
||||
bool GenerateThunkLibsActionFactory::runInvocation(std::shared_ptr<clang::CompilerInvocation> Invocation, clang::FileManager* Files,
|
||||
std::shared_ptr<clang::PCHContainerOperations> PCHContainerOps,
|
||||
clang::DiagnosticConsumer* DiagConsumer) {
|
||||
#if LLVM_VERSION_MAJOR >= 21
|
||||
clang::CompilerInstance Compiler(std::move(Invocation), std::move(PCHContainerOps));
|
||||
#else
|
||||
clang::CompilerInstance Compiler(std::move(PCHContainerOps));
|
||||
Compiler.setInvocation(std::move(Invocation));
|
||||
#endif
|
||||
Compiler.setFileManager(Files);
|
||||
|
||||
GenerateThunkLibsAction Action(libname, output_filenames, abi);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(guest-thunks)
|
||||
include(${FEX_PROJECT_SOURCE_DIR}/CMakeFiles/version_to_variables.cmake)
|
||||
include(${FEX_PROJECT_SOURCE_DIR}/Data/CMake/version_to_variables.cmake)
|
||||
|
||||
option(ENABLE_CLANG_THUNKS "Enable building thunks with clang" FALSE)
|
||||
|
||||
@@ -35,7 +35,7 @@ if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
# uninstall target
|
||||
if(NOT TARGET uninstall)
|
||||
configure_file(
|
||||
"${FEX_PROJECT_SOURCE_DIR}/CMakeFiles/cmake_uninstall.cmake.in"
|
||||
"${FEX_PROJECT_SOURCE_DIR}/Data/CMake/cmake_uninstall.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/cmake_uninstall.cmake"
|
||||
IMMEDIATE @ONLY)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(host-thunks)
|
||||
include(${FEX_PROJECT_SOURCE_DIR}/CMakeFiles/version_to_variables.cmake)
|
||||
include(${FEX_PROJECT_SOURCE_DIR}/Data/CMake/version_to_variables.cmake)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
@@ -25,6 +25,17 @@ extern "C" const wl_interface wl_seat_interface {};
|
||||
extern "C" const wl_interface wl_surface_interface {};
|
||||
extern "C" const wl_interface wl_keyboard_interface {};
|
||||
extern "C" const wl_interface wl_callback_interface {};
|
||||
extern "C" const wl_interface wl_display_interface {};
|
||||
extern "C" const wl_interface wl_data_offer_interface {};
|
||||
extern "C" const wl_interface wl_data_source_interface {};
|
||||
extern "C" const wl_interface wl_data_device_interface {};
|
||||
extern "C" const wl_interface wl_data_device_manager_interface {};
|
||||
extern "C" const wl_interface wl_shell_interface {};
|
||||
extern "C" const wl_interface wl_shell_surface_interface {};
|
||||
extern "C" const wl_interface wl_touch_interface {};
|
||||
extern "C" const wl_interface wl_region_interface {};
|
||||
extern "C" const wl_interface wl_subcompositor_interface {};
|
||||
extern "C" const wl_interface wl_subsurface_interface {};
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
@@ -124,6 +135,8 @@ extern "C" int wl_proxy_add_listener(wl_proxy* proxy, void (**callback)(void), v
|
||||
} else if (signature == "ii") {
|
||||
// E.g. xdg_toplevel::configure_bounds
|
||||
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'i', 'i'>(callback[i]);
|
||||
} else if (signature == "iu") {
|
||||
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'i', 'u'>(callback[i]);
|
||||
} else if (signature == "iia") {
|
||||
// E.g. xdg_toplevel::configure
|
||||
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'i', 'i', 'a'>(callback[i]);
|
||||
@@ -142,6 +155,8 @@ extern "C" int wl_proxy_add_listener(wl_proxy* proxy, void (**callback)(void), v
|
||||
} else if (signature == "uff") {
|
||||
// E.g. wl_pointer_listener::motion
|
||||
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'f', 'f'>(callback[i]);
|
||||
} else if (signature == "uffff") {
|
||||
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'f', 'f', 'f', 'f'>(callback[i]);
|
||||
} else if (signature == "uhu") {
|
||||
// E.g. wl_keyboard_listener::keymap
|
||||
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'h', 'u'>(callback[i]);
|
||||
@@ -154,6 +169,8 @@ extern "C" int wl_proxy_add_listener(wl_proxy* proxy, void (**callback)(void), v
|
||||
} else if (signature == "uiii") {
|
||||
// E.g. wl_output_listener::mode
|
||||
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'i', 'i', 'i'>(callback[i]);
|
||||
} else if (signature == "uiiii") {
|
||||
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'i', 'i', 'i', 'i'>(callback[i]);
|
||||
} else if (signature == "uo") {
|
||||
// E.g. wl_pointer_listener::leave
|
||||
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'o'>(callback[i]);
|
||||
@@ -166,6 +183,10 @@ extern "C" int wl_proxy_add_listener(wl_proxy* proxy, void (**callback)(void), v
|
||||
} else if (signature == "uoffo") {
|
||||
// E.g. wl_data_device_listener::enter
|
||||
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'o', 'f', 'f', 'o'>(callback[i]);
|
||||
} else if (signature == "us") {
|
||||
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 's'>(callback[i]);
|
||||
} else if (signature == "uss") {
|
||||
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 's', 's'>(callback[i]);
|
||||
} else if (signature == "usu") {
|
||||
// E.g. wl_registry::global
|
||||
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 's', 'u'>(callback[i]);
|
||||
@@ -181,6 +202,8 @@ extern "C" int wl_proxy_add_listener(wl_proxy* proxy, void (**callback)(void), v
|
||||
} else if (signature == "uuoiff") {
|
||||
// E.g. wl_touch_listener::down
|
||||
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'u', 'o', 'i', 'f', 'f'>(callback[i]);
|
||||
} else if (signature == "uuou") {
|
||||
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'u', 'o', 'u'>(callback[i]);
|
||||
} else if (signature == "uuu") {
|
||||
// E.g. zwp_linux_dmabuf_v1::modifier
|
||||
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'u', 'u', 'u'>(callback[i]);
|
||||
@@ -193,6 +216,8 @@ extern "C" int wl_proxy_add_listener(wl_proxy* proxy, void (**callback)(void), v
|
||||
} else if (signature == "s") {
|
||||
// E.g. wl_seat::name
|
||||
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'s'>(callback[i]);
|
||||
} else if (signature == "ss") {
|
||||
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'s', 's'>(callback[i]);
|
||||
} else if (signature == "sii") {
|
||||
// E.g. zwp_text_input_v3::preedit_string
|
||||
host_callbacks[i] = WaylandAllocateHostTrampolineForGuestListener<'s', 'i', 'i'>(callback[i]);
|
||||
@@ -335,6 +360,17 @@ void OnInit() {
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_surface_interface), "wl_surface_interface");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_keyboard_interface), "wl_keyboard_interface");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_callback_interface), "wl_callback_interface");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_display_interface), "wl_display_interface");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_data_offer_interface), "wl_data_offer_interface");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_data_source_interface), "wl_data_source_interface");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_data_device_interface), "wl_data_device_interface");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_data_device_manager_interface), "wl_data_device_manager_interface");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_shell_interface), "wl_shell_interface");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_shell_surface_interface), "wl_shell_surface_interface");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_touch_interface), "wl_touch_interface");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_region_interface), "wl_region_interface");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_subcompositor_interface), "wl_subcompositor_interface");
|
||||
fex_wl_exchange_interface_pointer(const_cast<wl_interface*>(&wl_subsurface_interface), "wl_subsurface_interface");
|
||||
}
|
||||
|
||||
// Would insert spaces around -
|
||||
|
||||
@@ -213,7 +213,7 @@ static wl_proxy* fex_wl_proxy_marshal_array(wl_proxy* proxy, uint32_t opcode, gu
|
||||
#endif
|
||||
} else if (constructor && version && !flags) {
|
||||
return fexldr_ptr_libwayland_client_wl_proxy_marshal_array_constructor_versioned(proxy, opcode, host_args.data(), interface, version.value());
|
||||
} else if (constructor && version && !flags) {
|
||||
} else if (constructor && !version && !flags) {
|
||||
return fexldr_ptr_libwayland_client_wl_proxy_marshal_array_constructor(proxy, opcode, host_args.data(), interface);
|
||||
} else {
|
||||
fprintf(stderr, "Invalid configuration\n");
|
||||
@@ -352,6 +352,8 @@ fexfn_impl_libwayland_client_wl_proxy_add_listener(struct wl_proxy* proxy, guest
|
||||
} else if (signature == "ii") {
|
||||
// E.g. xdg_toplevel::configure_bounds
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'i', 'i'>(callback);
|
||||
} else if (signature == "iu") {
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'i', 'u'>(callback);
|
||||
} else if (signature == "iia") {
|
||||
// E.g. xdg_toplevel::configure
|
||||
FEX::HLE::FinalizeHostTrampolineForGuestFunction((FEX::HLE::HostToGuestTrampolinePtr*)callback,
|
||||
@@ -371,6 +373,8 @@ fexfn_impl_libwayland_client_wl_proxy_add_listener(struct wl_proxy* proxy, guest
|
||||
} else if (signature == "uff") {
|
||||
// E.g. wl_pointer_listener::motion
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'u', 'f', 'f'>(callback);
|
||||
} else if (signature == "uffff") {
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'u', 'f', 'f', 'f', 'f'>(callback);
|
||||
} else if (signature == "uhu") {
|
||||
// E.g. wl_keyboard_listener::keymap
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'u', 'h', 'u'>(callback);
|
||||
@@ -383,6 +387,8 @@ fexfn_impl_libwayland_client_wl_proxy_add_listener(struct wl_proxy* proxy, guest
|
||||
} else if (signature == "uiii") {
|
||||
// E.g. wl_output_listener::mode
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'u', 'i', 'i', 'i'>(callback);
|
||||
} else if (signature == "uiiii") {
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'u', 'i', 'i', 'i', 'i'>(callback);
|
||||
} else if (signature == "uo") {
|
||||
// E.g. wl_pointer_listener::leave
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'u', 'o'>(callback);
|
||||
@@ -396,6 +402,10 @@ fexfn_impl_libwayland_client_wl_proxy_add_listener(struct wl_proxy* proxy, guest
|
||||
} else if (signature == "uoffo") {
|
||||
// E.g. wl_data_device_listener::enter
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'u', 'o', 'f', 'f', 'o'>(callback);
|
||||
} else if (signature == "us") {
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'u', 's'>(callback);
|
||||
} else if (signature == "uss") {
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'u', 's', 's'>(callback);
|
||||
} else if (signature == "usu") {
|
||||
// E.g. wl_registry::global
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'u', 's', 'u'>(callback);
|
||||
@@ -411,6 +421,8 @@ fexfn_impl_libwayland_client_wl_proxy_add_listener(struct wl_proxy* proxy, guest
|
||||
} else if (signature == "uuoiff") {
|
||||
// E.g. wl_touch_listener::down
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'u', 'u', 'o', 'i', 'f', 'f'>(callback);
|
||||
} else if (signature == "uuou") {
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'u', 'u', 'o', 'u'>(callback);
|
||||
} else if (signature == "uuu") {
|
||||
// E.g. zwp_linux_dmabuf_v1::modifier
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'u', 'u', 'u'>(callback);
|
||||
@@ -426,6 +438,8 @@ fexfn_impl_libwayland_client_wl_proxy_add_listener(struct wl_proxy* proxy, guest
|
||||
} else if (signature == "sii") {
|
||||
// E.g. zwp_text_input_v3::preedit_string
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'s', 'i', 'i'>(callback);
|
||||
} else if (signature == "ss") {
|
||||
WaylandFinalizeHostTrampolineForGuestListener<'s', 's'>(callback);
|
||||
} else {
|
||||
fprintf(stderr, "TODO: Unknown wayland event signature descriptor %s\n", signature.data());
|
||||
std::abort();
|
||||
@@ -449,8 +463,11 @@ void fexfn_impl_libwayland_client_fex_wl_exchange_interface_pointer(guest_layout
|
||||
// them into the rodata section of the application itself instead of the
|
||||
// library. To copy the host information to them on startup, we must
|
||||
// temporarily disable write-protection on this data hence.
|
||||
auto page_begin = reinterpret_cast<uintptr_t>(guest_interface_raw.force_get_host_pointer()) & ~uintptr_t {0xfff};
|
||||
if (0 != mprotect((void*)page_begin, 0x1000, PROT_READ | PROT_WRITE)) {
|
||||
// NOTE: This may span page boundaries, so up to 2 pages may need to be changed
|
||||
const auto source_addr = reinterpret_cast<uintptr_t>(guest_interface_raw.force_get_host_pointer());
|
||||
const auto page_begin = source_addr & ~uintptr_t {0xfff};
|
||||
const auto remap_size = ((source_addr & 0xfff) + sizeof(*guest_interface_raw.force_get_host_pointer()) > 0x1000) ? 0x2000 : 0x1000;
|
||||
if (0 != mprotect((void*)page_begin, remap_size, PROT_READ | PROT_WRITE)) {
|
||||
fprintf(stderr, "ERROR: %s\n", strerror(errno));
|
||||
std::abort();
|
||||
}
|
||||
@@ -476,7 +493,7 @@ void fexfn_impl_libwayland_client_fex_wl_exchange_interface_pointer(guest_layout
|
||||
#endif
|
||||
|
||||
// TODO: Disabled until we ensure the interface data is indeed stored in rodata
|
||||
// mprotect((void*)page_begin, 0x1000, PROT_READ);
|
||||
// mprotect((void*)page_begin, remap_size, PROT_READ);
|
||||
}
|
||||
|
||||
void fexfn_impl_libwayland_client_fex_wl_get_method_signature(wl_proxy* proxy, uint32_t opcode, char* out) {
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="581px" height="551px" viewBox="-0.5 -0.5 581 551" content="<mxfile host="www.draw.io" modified="2019-10-12T15:22:42.933Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36" etag="KXzHp2X2mUPny1tpjJrR" version="12.1.0" type="google" pages="2"><diagram name="Page-1" id="44bbcf24-548e-d532-59d3-359de5b44cbb">7Vtbk6MoFP41eZwuxftj3zJbtTNbXZWpmu1HoiQybcRRcttfv2AgUSFpKxMTq+3OQ8sRAb9zvnMOCCPrcbH5msMs/k4ilIyAEW1G1tMIAGC4gP3jku1OYgLD3knmOY6E7CCY4P+QEBpCusQRKmoVKSEJxVldGJI0RSGtyWCek3W92owk9V4zOEeKYBLCRErvnIP8J45oLOSmGxxu/IXwPBad+8Dd3ZjC8G2ek2UqekxJinZ3FlA2I96yiGFE1hWR9TyyHnNC6O5qsXlECUdWYiafo1s50JH1ENNFwgomuyxvj488bLZ5mL1XjlJa7e5Yey6CyJ7B6cyEs6lrmV8sgcAKJkvZQ7PLdYwpmmQw5OU1s576GAqakzf0SBKSM0kJnRRKLfBqM5wkstIIWDOH/7icpFQYk2lrX0mAsEI5RZuKSLziV0QWiOZbVkUaMxCWK0zZtYUS1hWrMDxhLnHFIixD1ITCGOf7xg+osgsBbFuQnQGAbHttQTbdTkC2PyLIZh3kvX2+D7LdBcgqxv+wQKLgXMFUB3kDwPvxeHw/VvAf8bflf/xODDPewGIz59HrDuZhjJlvRtYdzLIEh5BikrKKrPRjW1ZN+cCYgNcUouL3kilDoymuENZGcp/gOWvliZLsMvqzZXAU+vNcVX+2b+jU53SgPtNSVIUiFkRFESVTsn4+CB7KkIgiocryNruWaDXJA/M9qFxnKI3ueVhn5WlCwredaIz5cJ9EhWr9X4jSrRDAJSVMRHIakzlJYfKNcJVoWMqsxDf4T6PWCBbxfvRHtVmQZR6iI4gJH8LebY7okTpCUxzJkzaRo4SZ6aqexFxUv0DjAt2Ecijwil3OaQnETjTNmxLWX6XeZFtQtNA0MCEzuuZE0j52U2dAUchY9cD/Sc5vi8m6G+dse3Xn7Fsa52xryN1J/JOR4pPbbbltteC22xtuW5fk9olHiwymehfAmqjePMMzDNtfuFZQ9xcGkFPW23iM4DYeg2Qovb7DYHrLt//y3u6AI8uvf+RAnBYORCaAPfAgmqWIoeobbTAV91xHlF/LsmF4ovyCcsxAR7kYcXc24l/aRMpHGfxwW6mQEZzSotLyCxdU0hkH1NyTXDU5mNuuxYPx7Yd2nj2qqyITlK9weNv5ZCHG0E3K6NZDgOtoUkZHEwCcTgKAMySHcB5/3TY+HvSCwEEjv3C9jglsukOyn0pAORFPPMbwWjwBMr5cJp60skezF/boN+0RdG2P3qc9Nu3Rdv3b2+PFc+Cz7NEDdXt0gq7t0R+UPR5yases25wBrm5z/cipXfPKLlD9nPvhc2rP6FNODT5z6nf567fgL7h4DnO2Sn2FU99JtExUSkkGhGSRkbSE5AGKT4ohK3K3d4JADcoFwYz9MTmjU4TZ440P0x1QyXLqVNp/mqxSydd8bfY7odKgphfnUUmmVqe5dPH56flLDsZgyOS4Xp/INKi50ZlkktsWT5LJ6g+ZpE1+fDJ5wX57Zz/oNKip3Zl0Am3oZPeHTmAwdAqCXiV6qh8bL9Ow3NL3Z9NWDmwQnDttPba1cMYGN6pvLdzzu5O03HMbzs/SbTXSzXG9LrYRApUoDNkETkkOe6u0sBzhqOWO0Evkf77dL62pO4juK1gB43nFX7aPukMr4WOvxTi/saZ0Y82p265fchKiouiltjI2tmsqyzTMnvnHQNHXDxTGKYN3vlVUxt6SNhNAzTmEquqESEk5mvvYFziKyvRTawsdaAI08gpPtlHRA9BtcANd5BWWuthQ83gDUoRj3lQRaoL3sCxwqvNgH0oLfiOMyPXV22ihw9Mfte3YPdwuflSbp3eIt5rB9udLhRzvqTNwV80QQsqrd3Iyzm5QC6gRXxfwgy4CvuWpAR9TzcKBcG5tPVpFVxUIgS3LomHzlK9TvOIFwJerAHL1QJdsBRrwL3Em8enn6wtKaOzOkmXwiv/+VVjZFzW+KNC/c/YTFtnuUPoMb8pE9QgmGuTawhRoDv/pzv6dscbCiocj6Lt9BIdT/tbz/w==</diagram><diagram id="YYJ6otxqrv_eHtO8LE0p" name="Page-2">7V1bd6I6FP41Po4LCBf72LE607PsWl3LmTntI4VUOUXigdjq+fUnlEQuCUJVIF7qQ2UjsXx759uX7NAeGC7WP0J7OX9ALvR7muKue+Cup2maYmrkVyzZJBJVU/REMgs9l8pSwdT7D1KhQqUrz4VR7oMYIR97y7zQQUEAHZyT2WGIPvIfe0V+/luX9gxygqlj+7z0b8/F80Q6MJRU/hN6szn7ZlWhZxY2+zAVRHPbRR8ZERj1wDBECCfvFush9GP0GC6B930Ef/y5G6zGzsR+UAb43fiWDDb+yiXbWwhhgI87NEiGfrf9FcWL3iveMABDtApcGA+i9MD3j7mH4XRpO/HZD2IzRDbHC58cqeTtq+f7Q+Sj8PNaMBzHLyKnXwNDDNcFvVTclLpFmtgoRAuIww25bmugyTCb/OFHqmljQGXzjJY1nQptal2z7cgpguQNBfELgOrNAmqM4ld7gILaiBpNIWoIEDV9HGODyI1moTX/XSF24lv0SUW35APAXK7Tk+TdLP49Hj2xgcjflYyVnOE0RjDGebVEOERvkCkmQAEs6IqKbN+bBeTQITqBRP491phH2OmWnlh4rht/jdAO8pbSlMqNos55lVsijTelcPPoCifOhc2gVAigGb+EhjFEIbwaR3yJrheMQ2AdqtGmedxUM+yMYLMsRYRGGvYL+7hyFKQ0y+obeawsAVaaACuzKaws6d37jRjTzhAbcIgNH38rjAteQkYDRKry0n6/f3b8UKIhxg+VAZjeJjmoWrXFw8C9jROLGFnfjiLPyasHrj38lHn/HENIpnZydLemiH4ebNhBQP76p+xB5qr4ML3s82iTUwt0uRymDhWRu0Kr0IHVBIDtcAZ3jqeItZwN+wR6ZLIQ+jb23vM3IVIu/YZH5H16cnHQaYKCeSS3SS/KpjiFcaz8ONtAlY2TwMCN82lp25s+wPhEoaoMrmmQ90vqTdd+SW047zxGmmRI5plUUWqZi4uFUa7zGr92RLnK7eP9xcS6JUplE6PmvGiseqCKcp893Vfqsp4zZ6rcV+qxnnMOqxv3BWq6L/3qvQ73XjWShU68F5eCdp5WMZ6QOK9KtCmT+1I5zKabiDgAn0+jRotVPCNRwJ86P5dUoig6DACFogILMrPcBng9FknoaHpkpi4dTQxkK75o8rNE8hdJxBIazxJDEpDGc7x64nMT+vjAsFmpFWelyc9Kg8dNGzSFW42E6pLqHsxoKyPHajd5jRwrja/GouclZS1q3bQl00Zwtb69ra9GztxJQALyuGwbSyrWVZuL3BpeDTpC0S3R5c4J0S5k/HLQITW30WQ8QbYLw0upt5XpkzEQ6IPcj1mYMdyEKbsiaw1qU9YAagRZnTCNqkpGNUD+NqhEmfJQDajsc/oS1UyQ8xZhuBRVUC6De8oUfKLcI2uUs11MlIZ75A9zgGRhDjhumPMLRvinHQYwii6Yfs4q9GFLMNmMnuTPU3qIQjxHMxTY/iiVFqBMPzNBaEl1+g/EeEM3DdgrjA4vCvQaTu612i01Jf6ndtZ+mMLUU1GYrFUcFsM2r2hxWUXVCyXmlusquqCId7WgL1nQiVCFrGltceFfN3iv2O7ylN5wXnuEJT29Oq9tGTM+sR17Pow2JD1dEHlmsf/MQsAyVci6tK+bXTL+OSw71iV8vaRhrSXCl7XTS7spbqHRTX5OiLYfNjcnRKmxZIxfvYbdLmT8Dq37oE7/xomx++7GLc3sm7kfPc/1ggS/5IpWEnwWWn15DT8iZId3L+2XhOdqBVmLUwFlp2NomuAZHVz7SlrIQA2+GUxaP3XTsZ8yNOn9lFECameQ8XnvX/e/zs5PlcF+on6qRgYuCScYWtecUGM/YtecUL1zp13I+GXWyeTPw/mRQgnuJ0oKNZK02g2o+5QhtqFsK2WIvevJLUWf3IMwuP3+ewagZtsBqLTPGtFk29HNBpa468KQ7Vkjpii/2b/tYvj4e2w7GMV/2aV2XZTpuPbj4oTPNmvMADotuJ+Ap6usx5h1t1qYJVst2qm4mzUClG72KBS690RTotVWNFP+J4MkypSne888bufw2Pb9F9t5u1wnUqbgktY9Kx+KSda5Z8oawxapRxTDtjuPNPmppzqEbRey4z6TKC60XCzpVESuJ0U6Vrfdp+cQ2FLukTywtWQtxxc23wo6B1slSkt+32JVl+LbhUyUCezvW6abxQuKv/TuYlxKmUZPcwuKZXTqU86gH52RUPVzKUq6Vw71KeQw/XcZSZE+/a8jYPQ/</diagram></mxfile>"><defs/><g><Line truncated
|
||||
|
Before Width: | Height: | Size: 23 KiB |
@@ -69,5 +69,3 @@ https://wiki.fex-emu.com/index.php/Development:Setting_up_FEX
|
||||
### 创建RootFS
|
||||
AArch64 host端需要一个rootfs去运行guest程序。参考以下维基页面从头开始创建一个rootfs
|
||||
https://wiki.fex-emu.com/index.php/Development:Setting_up_RootFS
|
||||
|
||||

|
||||
@@ -1,4 +1,4 @@
|
||||
# FEX-2506
|
||||
# FEX-2507.1
|
||||
|
||||
## FEXCore
|
||||
See [FEXCore/Readme.md](../FEXCore/Readme.md) for more details
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#define FEX_INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@"
|
||||
#define FEXINTERPRETER_PATH FEX_INSTALL_PREFIX "/bin/FEXInterpreter"
|
||||
@@ -0,0 +1,31 @@
|
||||
%ifdef CONFIG
|
||||
{
|
||||
"RegData": {
|
||||
"RAX": "0xaaaa",
|
||||
"RBX": "0xaaaa",
|
||||
"RSI": "0xbbbb"
|
||||
}
|
||||
}
|
||||
%endif
|
||||
|
||||
; FEX had a bug with mov+xchg back-to-back due to failing to account for a copy
|
||||
; inserted during RA. This resulted in a hang starting the game Hades due to the
|
||||
; mov+xchg code sequence found within Wine's x64 build of ucrtbase.dll.
|
||||
|
||||
mov rax, 0xaaaa
|
||||
mov rbx, 0xbbbb
|
||||
mov rsi, 0xcccc
|
||||
|
||||
; step 1
|
||||
mov rsi,rax
|
||||
; rax = 0xaaaa
|
||||
; rbx = 0xbbbb
|
||||
; rsi = 0xaaaa
|
||||
|
||||
; step 2
|
||||
xchg rbx,rsi
|
||||
; rax = 0xaaaa
|
||||
; rbx = 0xaaaa
|
||||
; rsi = 0xbbbb
|
||||
|
||||
hlt
|
||||
@@ -45,7 +45,7 @@ function(AddTests Tests BinDirectory Bitness)
|
||||
continue()
|
||||
endif()
|
||||
|
||||
set(THUNK_ARGS "-k" "${CMAKE_SOURCE_DIR}/CI/FEXLinuxTestsThunks.json")
|
||||
set(THUNK_ARGS "-k" "${CMAKE_SOURCE_DIR}/Data/CI/FEXLinuxTestsThunks.json")
|
||||
endif()
|
||||
|
||||
# Add jit test case
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# Passes on host but now FEX.
|
||||
noexec_protect.64
|
||||
@@ -23,3 +23,6 @@ sigtest_sigmask.64
|
||||
# Disabled since FEX's FaultSafeMemcpy is intentionally stub-implemented
|
||||
syscalls_efault.32
|
||||
syscalls_efault.64
|
||||
|
||||
# FEX doesn't support no-exec
|
||||
noexec_protect.64
|
||||
@@ -0,0 +1,97 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
#include <stdint.h>
|
||||
#include <cstdlib>
|
||||
#include <csetjmp>
|
||||
|
||||
bool Caught = false;
|
||||
static jmp_buf LongJump {};
|
||||
|
||||
static void SIGSEGV_Handler(int signal, siginfo_t* siginfo, void* context) {
|
||||
// Needs to be an access error.
|
||||
REQUIRE(siginfo->si_code == SEGV_ACCERR);
|
||||
Caught = true;
|
||||
longjmp(LongJump, 1);
|
||||
}
|
||||
|
||||
class SimpleX86Emit final {
|
||||
public:
|
||||
enum Reg {
|
||||
RAX = 0,
|
||||
RCX = 1,
|
||||
RDX = 2,
|
||||
RBX = 3,
|
||||
RSP = 4,
|
||||
RBP = 5,
|
||||
RSI = 6,
|
||||
RDI = 7,
|
||||
// r8 and higher not implemented.
|
||||
};
|
||||
SimpleX86Emit(void* Ptr)
|
||||
: Ptr {static_cast<uint8_t*>(Ptr)} {}
|
||||
|
||||
void ret() {
|
||||
db<uint8_t>(0xc3);
|
||||
}
|
||||
|
||||
void mov(Reg reg, uint32_t val) {
|
||||
db<uint8_t>(0xB8 + reg);
|
||||
db(val);
|
||||
}
|
||||
private:
|
||||
uint8_t* Ptr;
|
||||
|
||||
template<typename T>
|
||||
void db(T v) {
|
||||
static_assert(sizeof(uint32_t) == 4);
|
||||
for (size_t i = 0; i < sizeof(T); ++i) {
|
||||
*Ptr = v >> (i * 8);
|
||||
++Ptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE("Signals: Test No-Exec") {
|
||||
struct sigaction act {};
|
||||
act.sa_sigaction = SIGSEGV_Handler;
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
|
||||
sigaction(SIGSEGV, &act, nullptr);
|
||||
auto PageSize = sysconf(_SC_PAGESIZE);
|
||||
PageSize = PageSize > 0 ? PageSize : 0x1000;
|
||||
|
||||
void* Ptr = mmap(nullptr, PageSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
REQUIRE(Ptr != MAP_FAILED);
|
||||
|
||||
SimpleX86Emit emit(Ptr);
|
||||
emit.mov(SimpleX86Emit::Reg::RAX, 1);
|
||||
emit.ret();
|
||||
|
||||
using func_ptr = uint32_t (*)();
|
||||
func_ptr func = reinterpret_cast<func_ptr>(Ptr);
|
||||
|
||||
// First time should execute fine.
|
||||
Caught = false;
|
||||
if (setjmp(LongJump) == 0) {
|
||||
int res = func();
|
||||
REQUIRE(res == 1);
|
||||
} else {
|
||||
REQUIRE(Caught == false);
|
||||
}
|
||||
|
||||
// Protect as non-executable
|
||||
REQUIRE(mprotect(Ptr, PageSize, PROT_READ | PROT_WRITE) == 0);
|
||||
|
||||
// This should now fail to execute due to No-Exec.
|
||||
Caught = false;
|
||||
if (setjmp(LongJump) == 0) {
|
||||
int res = func();
|
||||
// Shouldn't get reached.
|
||||
REQUIRE(res == 1);
|
||||
REQUIRE(false);
|
||||
} else {
|
||||
REQUIRE(Caught == true);
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -33,7 +33,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -47,7 +47,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -61,7 +61,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -75,7 +75,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -89,7 +89,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -103,7 +103,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -117,7 +117,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -131,7 +131,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -145,7 +145,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -159,7 +159,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -173,7 +173,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -187,7 +187,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -201,7 +201,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -215,7 +215,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -229,7 +229,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -243,7 +243,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -257,7 +257,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -271,7 +271,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -285,7 +285,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -299,7 +299,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -313,7 +313,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -327,7 +327,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -341,7 +341,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -355,7 +355,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -369,7 +369,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -383,7 +383,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -397,7 +397,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -411,7 +411,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
},
|
||||
@@ -425,7 +425,7 @@
|
||||
"str x20, [x28, #24]",
|
||||
"mov w1, #0x401",
|
||||
"str x1, [x28, #1328]",
|
||||
"ldr x0, [x28, #2680]",
|
||||
"ldr x0, [x28, #2712]",
|
||||
"br x0"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -485,7 +485,7 @@
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ushr v2.4s, v16.4s, #31",
|
||||
"ldr q3, [x28, #2896]",
|
||||
"ldr q3, [x28, #2928]",
|
||||
"ushl v2.4s, v2.4s, v3.4s",
|
||||
"addv s2, v2.4s",
|
||||
"mov w4, v2.s[0]"
|
||||
@@ -499,7 +499,7 @@
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #32]",
|
||||
"ushr v3.4s, v16.4s, #31",
|
||||
"ldr q4, [x28, #2896]",
|
||||
"ldr q4, [x28, #2928]",
|
||||
"ushl v3.4s, v3.4s, v4.4s",
|
||||
"addv s3, v3.4s",
|
||||
"mov w20, v3.s[0]",
|
||||
@@ -1122,7 +1122,7 @@
|
||||
"Map 1 0b01 0x70 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr x0, [x28, #2440]",
|
||||
"ldr x0, [x28, #2472]",
|
||||
"ldr q2, [x0, #16]",
|
||||
"tbl v16.16b, {v17.16b}, v2.16b",
|
||||
"stp xzr, xzr, [x28, #32]"
|
||||
@@ -1134,7 +1134,7 @@
|
||||
"Map 1 0b01 0x70 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr x0, [x28, #2440]",
|
||||
"ldr x0, [x28, #2472]",
|
||||
"ldr q2, [x0, #32]",
|
||||
"tbl v16.16b, {v17.16b}, v2.16b",
|
||||
"stp xzr, xzr, [x28, #32]"
|
||||
@@ -1146,7 +1146,7 @@
|
||||
"Map 1 0b01 0x70 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr x0, [x28, #2440]",
|
||||
"ldr x0, [x28, #2472]",
|
||||
"ldr q2, [x0, #48]",
|
||||
"tbl v16.16b, {v17.16b}, v2.16b",
|
||||
"stp xzr, xzr, [x28, #32]"
|
||||
@@ -1171,7 +1171,7 @@
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #48]",
|
||||
"ldr x0, [x28, #2440]",
|
||||
"ldr x0, [x28, #2472]",
|
||||
"ldr q3, [x0, #16]",
|
||||
"tbl v16.16b, {v17.16b}, v3.16b",
|
||||
"tbl v2.16b, {v2.16b}, v3.16b",
|
||||
@@ -1185,7 +1185,7 @@
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #48]",
|
||||
"ldr x0, [x28, #2440]",
|
||||
"ldr x0, [x28, #2472]",
|
||||
"ldr q3, [x0, #32]",
|
||||
"tbl v16.16b, {v17.16b}, v3.16b",
|
||||
"tbl v2.16b, {v2.16b}, v3.16b",
|
||||
@@ -1199,7 +1199,7 @@
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #48]",
|
||||
"ldr x0, [x28, #2440]",
|
||||
"ldr x0, [x28, #2472]",
|
||||
"ldr q3, [x0, #48]",
|
||||
"tbl v16.16b, {v17.16b}, v3.16b",
|
||||
"tbl v2.16b, {v2.16b}, v3.16b",
|
||||
@@ -1223,7 +1223,7 @@
|
||||
"Map 1 0b10 0x70 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr x0, [x28, #2432]",
|
||||
"ldr x0, [x28, #2464]",
|
||||
"ldr q2, [x0, #16]",
|
||||
"tbl v16.16b, {v17.16b}, v2.16b",
|
||||
"stp xzr, xzr, [x28, #32]"
|
||||
@@ -1235,7 +1235,7 @@
|
||||
"Map 1 0b10 0x70 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr x0, [x28, #2432]",
|
||||
"ldr x0, [x28, #2464]",
|
||||
"ldr q2, [x0, #32]",
|
||||
"tbl v16.16b, {v17.16b}, v2.16b",
|
||||
"stp xzr, xzr, [x28, #32]"
|
||||
@@ -1247,7 +1247,7 @@
|
||||
"Map 1 0b10 0x70 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr x0, [x28, #2432]",
|
||||
"ldr x0, [x28, #2464]",
|
||||
"ldr q2, [x0, #48]",
|
||||
"tbl v16.16b, {v17.16b}, v2.16b",
|
||||
"stp xzr, xzr, [x28, #32]"
|
||||
@@ -1274,7 +1274,7 @@
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #48]",
|
||||
"ldr x0, [x28, #2432]",
|
||||
"ldr x0, [x28, #2464]",
|
||||
"ldr q3, [x0, #16]",
|
||||
"tbl v16.16b, {v17.16b}, v3.16b",
|
||||
"tbl v2.16b, {v2.16b}, v3.16b",
|
||||
@@ -1288,7 +1288,7 @@
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #48]",
|
||||
"ldr x0, [x28, #2432]",
|
||||
"ldr x0, [x28, #2464]",
|
||||
"ldr q3, [x0, #32]",
|
||||
"tbl v16.16b, {v17.16b}, v3.16b",
|
||||
"tbl v2.16b, {v2.16b}, v3.16b",
|
||||
@@ -1302,7 +1302,7 @@
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #48]",
|
||||
"ldr x0, [x28, #2432]",
|
||||
"ldr x0, [x28, #2464]",
|
||||
"ldr q3, [x0, #48]",
|
||||
"tbl v16.16b, {v17.16b}, v3.16b",
|
||||
"tbl v2.16b, {v2.16b}, v3.16b",
|
||||
@@ -1326,7 +1326,7 @@
|
||||
"Map 1 0b11 0x70 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr x0, [x28, #2424]",
|
||||
"ldr x0, [x28, #2456]",
|
||||
"ldr q2, [x0, #16]",
|
||||
"tbl v16.16b, {v17.16b}, v2.16b",
|
||||
"stp xzr, xzr, [x28, #32]"
|
||||
@@ -1338,7 +1338,7 @@
|
||||
"Map 1 0b11 0x70 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr x0, [x28, #2424]",
|
||||
"ldr x0, [x28, #2456]",
|
||||
"ldr q2, [x0, #32]",
|
||||
"tbl v16.16b, {v17.16b}, v2.16b",
|
||||
"stp xzr, xzr, [x28, #32]"
|
||||
@@ -1350,7 +1350,7 @@
|
||||
"Map 1 0b11 0x70 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr x0, [x28, #2424]",
|
||||
"ldr x0, [x28, #2456]",
|
||||
"ldr q2, [x0, #48]",
|
||||
"tbl v16.16b, {v17.16b}, v2.16b",
|
||||
"stp xzr, xzr, [x28, #32]"
|
||||
@@ -1377,7 +1377,7 @@
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #48]",
|
||||
"ldr x0, [x28, #2424]",
|
||||
"ldr x0, [x28, #2456]",
|
||||
"ldr q3, [x0, #16]",
|
||||
"tbl v16.16b, {v17.16b}, v3.16b",
|
||||
"tbl v2.16b, {v2.16b}, v3.16b",
|
||||
@@ -1391,7 +1391,7 @@
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #48]",
|
||||
"ldr x0, [x28, #2424]",
|
||||
"ldr x0, [x28, #2456]",
|
||||
"ldr q3, [x0, #32]",
|
||||
"tbl v16.16b, {v17.16b}, v3.16b",
|
||||
"tbl v2.16b, {v2.16b}, v3.16b",
|
||||
@@ -1405,7 +1405,7 @@
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #48]",
|
||||
"ldr x0, [x28, #2424]",
|
||||
"ldr x0, [x28, #2456]",
|
||||
"ldr q3, [x0, #48]",
|
||||
"tbl v16.16b, {v17.16b}, v3.16b",
|
||||
"tbl v2.16b, {v2.16b}, v3.16b",
|
||||
@@ -2288,7 +2288,7 @@
|
||||
"Map 1 0b00 0xC6 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr x0, [x28, #2448]",
|
||||
"ldr x0, [x28, #2480]",
|
||||
"ldr q2, [x0, #16]",
|
||||
"tbl v16.16b, {v17.16b, v18.16b}, v2.16b",
|
||||
"stp xzr, xzr, [x28, #32]"
|
||||
@@ -2302,7 +2302,7 @@
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #48]",
|
||||
"ldr q3, [x28, #64]",
|
||||
"ldr x0, [x28, #2448]",
|
||||
"ldr x0, [x28, #2480]",
|
||||
"ldr q4, [x0, #16]",
|
||||
"tbl v16.16b, {v17.16b, v18.16b}, v4.16b",
|
||||
"tbl v2.16b, {v2.16b, v3.16b}, v4.16b",
|
||||
@@ -2315,7 +2315,7 @@
|
||||
"Map 1 0b00 0xC6 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr x0, [x28, #2448]",
|
||||
"ldr x0, [x28, #2480]",
|
||||
"ldr q2, [x0, #32]",
|
||||
"tbl v16.16b, {v17.16b, v18.16b}, v2.16b",
|
||||
"stp xzr, xzr, [x28, #32]"
|
||||
@@ -2329,7 +2329,7 @@
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #48]",
|
||||
"ldr q3, [x28, #64]",
|
||||
"ldr x0, [x28, #2448]",
|
||||
"ldr x0, [x28, #2480]",
|
||||
"ldr q4, [x0, #32]",
|
||||
"tbl v16.16b, {v17.16b, v18.16b}, v4.16b",
|
||||
"tbl v2.16b, {v2.16b, v3.16b}, v4.16b",
|
||||
@@ -2342,7 +2342,7 @@
|
||||
"Map 1 0b00 0xC6 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr x0, [x28, #2448]",
|
||||
"ldr x0, [x28, #2480]",
|
||||
"ldr q2, [x0, #48]",
|
||||
"tbl v16.16b, {v17.16b, v18.16b}, v2.16b",
|
||||
"stp xzr, xzr, [x28, #32]"
|
||||
@@ -2356,7 +2356,7 @@
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #48]",
|
||||
"ldr q3, [x28, #64]",
|
||||
"ldr x0, [x28, #2448]",
|
||||
"ldr x0, [x28, #2480]",
|
||||
"ldr q4, [x0, #48]",
|
||||
"tbl v16.16b, {v17.16b, v18.16b}, v4.16b",
|
||||
"tbl v2.16b, {v2.16b, v3.16b}, v4.16b",
|
||||
@@ -3763,7 +3763,7 @@
|
||||
"Map 1 0b01 0xd0 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2800]",
|
||||
"ldr q2, [x28, #2832]",
|
||||
"eor v2.16b, v18.16b, v2.16b",
|
||||
"fadd v16.2d, v17.2d, v2.2d",
|
||||
"stp xzr, xzr, [x28, #32]"
|
||||
@@ -3777,7 +3777,7 @@
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #48]",
|
||||
"ldr q3, [x28, #64]",
|
||||
"ldr q4, [x28, #2800]",
|
||||
"ldr q4, [x28, #2832]",
|
||||
"eor v5.16b, v18.16b, v4.16b",
|
||||
"fadd v16.2d, v17.2d, v5.2d",
|
||||
"eor v3.16b, v3.16b, v4.16b",
|
||||
@@ -3791,7 +3791,7 @@
|
||||
"Map 1 0b11 0xd0 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2768]",
|
||||
"ldr q2, [x28, #2800]",
|
||||
"eor v2.16b, v18.16b, v2.16b",
|
||||
"fadd v16.4s, v17.4s, v2.4s",
|
||||
"stp xzr, xzr, [x28, #32]"
|
||||
@@ -3805,7 +3805,7 @@
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #48]",
|
||||
"ldr q3, [x28, #64]",
|
||||
"ldr q4, [x28, #2768]",
|
||||
"ldr q4, [x28, #2800]",
|
||||
"eor v5.16b, v18.16b, v4.16b",
|
||||
"fadd v16.4s, v17.4s, v5.4s",
|
||||
"eor v3.16b, v3.16b, v4.16b",
|
||||
@@ -3976,7 +3976,7 @@
|
||||
"Map 1 0b01 0xd7 256-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #3024]",
|
||||
"ldr q2, [x28, #3056]",
|
||||
"cmlt v3.16b, v16.16b, #0",
|
||||
"and v2.16b, v3.16b, v2.16b",
|
||||
"addp v2.16b, v2.16b, v2.16b",
|
||||
@@ -3992,7 +3992,7 @@
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #3024]",
|
||||
"ldr q3, [x28, #3056]",
|
||||
"cmlt v4.16b, v16.16b, #0",
|
||||
"and v4.16b, v4.16b, v3.16b",
|
||||
"addp v4.16b, v4.16b, v4.16b",
|
||||
|
||||
@@ -1917,7 +1917,7 @@
|
||||
"Map 2 0b01 0x41 256-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2736]",
|
||||
"ldr q2, [x28, #2768]",
|
||||
"zip1 v3.8h, v2.8h, v17.8h",
|
||||
"zip2 v2.8h, v2.8h, v17.8h",
|
||||
"umin v2.4s, v3.4s, v2.4s",
|
||||
@@ -4485,7 +4485,7 @@
|
||||
"Map 2 0b01 0x96 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2768]",
|
||||
"ldr q2, [x28, #2800]",
|
||||
"eor v2.16b, v17.16b, v2.16b",
|
||||
"mov v0.16b, v2.16b",
|
||||
"fmla v0.4s, v16.4s, v18.4s",
|
||||
@@ -4502,7 +4502,7 @@
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #48]",
|
||||
"ldr q4, [x28, #64]",
|
||||
"ldr q5, [x28, #2768]",
|
||||
"ldr q5, [x28, #2800]",
|
||||
"eor v6.16b, v17.16b, v5.16b",
|
||||
"mov v0.16b, v6.16b",
|
||||
"fmla v0.4s, v16.4s, v18.4s",
|
||||
@@ -4518,7 +4518,7 @@
|
||||
"Map 2 0b01 0x96 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2800]",
|
||||
"ldr q2, [x28, #2832]",
|
||||
"eor v2.16b, v17.16b, v2.16b",
|
||||
"mov v0.16b, v2.16b",
|
||||
"fmla v0.2d, v16.2d, v18.2d",
|
||||
@@ -4535,7 +4535,7 @@
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #48]",
|
||||
"ldr q4, [x28, #64]",
|
||||
"ldr q5, [x28, #2800]",
|
||||
"ldr q5, [x28, #2832]",
|
||||
"eor v6.16b, v17.16b, v5.16b",
|
||||
"mov v0.16b, v6.16b",
|
||||
"fmla v0.2d, v16.2d, v18.2d",
|
||||
@@ -4551,7 +4551,7 @@
|
||||
"Map 2 0b01 0x97 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2832]",
|
||||
"ldr q2, [x28, #2864]",
|
||||
"eor v2.16b, v17.16b, v2.16b",
|
||||
"mov v0.16b, v2.16b",
|
||||
"fmla v0.4s, v16.4s, v18.4s",
|
||||
@@ -4568,7 +4568,7 @@
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #48]",
|
||||
"ldr q4, [x28, #64]",
|
||||
"ldr q5, [x28, #2832]",
|
||||
"ldr q5, [x28, #2864]",
|
||||
"eor v6.16b, v17.16b, v5.16b",
|
||||
"mov v0.16b, v6.16b",
|
||||
"fmla v0.4s, v16.4s, v18.4s",
|
||||
@@ -4584,7 +4584,7 @@
|
||||
"Map 2 0b01 0x97 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2864]",
|
||||
"ldr q2, [x28, #2896]",
|
||||
"eor v2.16b, v17.16b, v2.16b",
|
||||
"mov v0.16b, v2.16b",
|
||||
"fmla v0.2d, v16.2d, v18.2d",
|
||||
@@ -4601,7 +4601,7 @@
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #48]",
|
||||
"ldr q4, [x28, #64]",
|
||||
"ldr q5, [x28, #2864]",
|
||||
"ldr q5, [x28, #2896]",
|
||||
"eor v6.16b, v17.16b, v5.16b",
|
||||
"mov v0.16b, v6.16b",
|
||||
"fmla v0.2d, v16.2d, v18.2d",
|
||||
@@ -5541,7 +5541,7 @@
|
||||
"Map 2 0b01 0xa6 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2768]",
|
||||
"ldr q2, [x28, #2800]",
|
||||
"eor v2.16b, v18.16b, v2.16b",
|
||||
"mov v0.16b, v2.16b",
|
||||
"fmla v0.4s, v17.4s, v16.4s",
|
||||
@@ -5558,7 +5558,7 @@
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #48]",
|
||||
"ldr q4, [x28, #64]",
|
||||
"ldr q5, [x28, #2768]",
|
||||
"ldr q5, [x28, #2800]",
|
||||
"eor v6.16b, v18.16b, v5.16b",
|
||||
"mov v0.16b, v6.16b",
|
||||
"fmla v0.4s, v17.4s, v16.4s",
|
||||
@@ -5574,7 +5574,7 @@
|
||||
"Map 2 0b01 0xa6 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2800]",
|
||||
"ldr q2, [x28, #2832]",
|
||||
"eor v2.16b, v18.16b, v2.16b",
|
||||
"mov v0.16b, v2.16b",
|
||||
"fmla v0.2d, v17.2d, v16.2d",
|
||||
@@ -5591,7 +5591,7 @@
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #48]",
|
||||
"ldr q4, [x28, #64]",
|
||||
"ldr q5, [x28, #2800]",
|
||||
"ldr q5, [x28, #2832]",
|
||||
"eor v6.16b, v18.16b, v5.16b",
|
||||
"mov v0.16b, v6.16b",
|
||||
"fmla v0.2d, v17.2d, v16.2d",
|
||||
@@ -5607,7 +5607,7 @@
|
||||
"Map 2 0b01 0xa7 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2832]",
|
||||
"ldr q2, [x28, #2864]",
|
||||
"eor v2.16b, v18.16b, v2.16b",
|
||||
"mov v0.16b, v2.16b",
|
||||
"fmla v0.4s, v17.4s, v16.4s",
|
||||
@@ -5624,7 +5624,7 @@
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #48]",
|
||||
"ldr q4, [x28, #64]",
|
||||
"ldr q5, [x28, #2832]",
|
||||
"ldr q5, [x28, #2864]",
|
||||
"eor v6.16b, v18.16b, v5.16b",
|
||||
"mov v0.16b, v6.16b",
|
||||
"fmla v0.4s, v17.4s, v16.4s",
|
||||
@@ -5640,7 +5640,7 @@
|
||||
"Map 2 0b01 0xa7 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2864]",
|
||||
"ldr q2, [x28, #2896]",
|
||||
"eor v2.16b, v18.16b, v2.16b",
|
||||
"mov v0.16b, v2.16b",
|
||||
"fmla v0.2d, v17.2d, v16.2d",
|
||||
@@ -5657,7 +5657,7 @@
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #48]",
|
||||
"ldr q4, [x28, #64]",
|
||||
"ldr q5, [x28, #2864]",
|
||||
"ldr q5, [x28, #2896]",
|
||||
"eor v6.16b, v18.16b, v5.16b",
|
||||
"mov v0.16b, v6.16b",
|
||||
"fmla v0.2d, v17.2d, v16.2d",
|
||||
@@ -5673,7 +5673,7 @@
|
||||
"Map 2 0b01 0xb6 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2768]",
|
||||
"ldr q2, [x28, #2800]",
|
||||
"eor v2.16b, v16.16b, v2.16b",
|
||||
"mov v16.16b, v2.16b",
|
||||
"fmla v16.4s, v17.4s, v18.4s",
|
||||
@@ -5689,7 +5689,7 @@
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #48]",
|
||||
"ldr q4, [x28, #64]",
|
||||
"ldr q5, [x28, #2768]",
|
||||
"ldr q5, [x28, #2800]",
|
||||
"eor v6.16b, v16.16b, v5.16b",
|
||||
"mov v16.16b, v6.16b",
|
||||
"fmla v16.4s, v17.4s, v18.4s",
|
||||
@@ -5704,7 +5704,7 @@
|
||||
"Map 2 0b01 0xb6 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2800]",
|
||||
"ldr q2, [x28, #2832]",
|
||||
"eor v2.16b, v16.16b, v2.16b",
|
||||
"mov v16.16b, v2.16b",
|
||||
"fmla v16.2d, v17.2d, v18.2d",
|
||||
@@ -5720,7 +5720,7 @@
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #48]",
|
||||
"ldr q4, [x28, #64]",
|
||||
"ldr q5, [x28, #2800]",
|
||||
"ldr q5, [x28, #2832]",
|
||||
"eor v6.16b, v16.16b, v5.16b",
|
||||
"mov v16.16b, v6.16b",
|
||||
"fmla v16.2d, v17.2d, v18.2d",
|
||||
@@ -5735,7 +5735,7 @@
|
||||
"Map 2 0b01 0xb7 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2832]",
|
||||
"ldr q2, [x28, #2864]",
|
||||
"eor v2.16b, v16.16b, v2.16b",
|
||||
"mov v16.16b, v2.16b",
|
||||
"fmla v16.4s, v17.4s, v18.4s",
|
||||
@@ -5751,7 +5751,7 @@
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #48]",
|
||||
"ldr q4, [x28, #64]",
|
||||
"ldr q5, [x28, #2832]",
|
||||
"ldr q5, [x28, #2864]",
|
||||
"eor v6.16b, v16.16b, v5.16b",
|
||||
"mov v16.16b, v6.16b",
|
||||
"fmla v16.4s, v17.4s, v18.4s",
|
||||
@@ -5766,7 +5766,7 @@
|
||||
"Map 2 0b01 0xb7 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2864]",
|
||||
"ldr q2, [x28, #2896]",
|
||||
"eor v2.16b, v16.16b, v2.16b",
|
||||
"mov v16.16b, v2.16b",
|
||||
"fmla v16.2d, v17.2d, v18.2d",
|
||||
@@ -5782,7 +5782,7 @@
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #48]",
|
||||
"ldr q4, [x28, #64]",
|
||||
"ldr q5, [x28, #2864]",
|
||||
"ldr q5, [x28, #2896]",
|
||||
"eor v6.16b, v16.16b, v5.16b",
|
||||
"mov v16.16b, v6.16b",
|
||||
"fmla v16.2d, v17.2d, v18.2d",
|
||||
|
||||
@@ -2843,7 +2843,7 @@
|
||||
"Map 2 0b01 0x96 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2768]",
|
||||
"ldr q2, [x28, #2800]",
|
||||
"eor v2.16b, v17.16b, v2.16b",
|
||||
"mov v0.16b, v2.16b",
|
||||
"fmla v0.4s, v16.4s, v18.4s",
|
||||
@@ -2860,7 +2860,7 @@
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #48]",
|
||||
"ldr q4, [x28, #64]",
|
||||
"ldr q5, [x28, #2768]",
|
||||
"ldr q5, [x28, #2800]",
|
||||
"eor v6.16b, v17.16b, v5.16b",
|
||||
"mov v0.16b, v6.16b",
|
||||
"fmla v0.4s, v16.4s, v18.4s",
|
||||
@@ -2876,7 +2876,7 @@
|
||||
"Map 2 0b01 0x96 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2800]",
|
||||
"ldr q2, [x28, #2832]",
|
||||
"eor v2.16b, v17.16b, v2.16b",
|
||||
"mov v0.16b, v2.16b",
|
||||
"fmla v0.2d, v16.2d, v18.2d",
|
||||
@@ -2893,7 +2893,7 @@
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #48]",
|
||||
"ldr q4, [x28, #64]",
|
||||
"ldr q5, [x28, #2800]",
|
||||
"ldr q5, [x28, #2832]",
|
||||
"eor v6.16b, v17.16b, v5.16b",
|
||||
"mov v0.16b, v6.16b",
|
||||
"fmla v0.2d, v16.2d, v18.2d",
|
||||
@@ -2909,7 +2909,7 @@
|
||||
"Map 2 0b01 0x97 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2832]",
|
||||
"ldr q2, [x28, #2864]",
|
||||
"eor v2.16b, v17.16b, v2.16b",
|
||||
"mov v0.16b, v2.16b",
|
||||
"fmla v0.4s, v16.4s, v18.4s",
|
||||
@@ -2926,7 +2926,7 @@
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #48]",
|
||||
"ldr q4, [x28, #64]",
|
||||
"ldr q5, [x28, #2832]",
|
||||
"ldr q5, [x28, #2864]",
|
||||
"eor v6.16b, v17.16b, v5.16b",
|
||||
"mov v0.16b, v6.16b",
|
||||
"fmla v0.4s, v16.4s, v18.4s",
|
||||
@@ -2942,7 +2942,7 @@
|
||||
"Map 2 0b01 0x97 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2864]",
|
||||
"ldr q2, [x28, #2896]",
|
||||
"eor v2.16b, v17.16b, v2.16b",
|
||||
"mov v0.16b, v2.16b",
|
||||
"fmla v0.2d, v16.2d, v18.2d",
|
||||
@@ -2959,7 +2959,7 @@
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #48]",
|
||||
"ldr q4, [x28, #64]",
|
||||
"ldr q5, [x28, #2864]",
|
||||
"ldr q5, [x28, #2896]",
|
||||
"eor v6.16b, v17.16b, v5.16b",
|
||||
"mov v0.16b, v6.16b",
|
||||
"fmla v0.2d, v16.2d, v18.2d",
|
||||
@@ -3879,7 +3879,7 @@
|
||||
"Map 2 0b01 0xa6 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2768]",
|
||||
"ldr q2, [x28, #2800]",
|
||||
"eor v2.16b, v18.16b, v2.16b",
|
||||
"mov v0.16b, v2.16b",
|
||||
"fmla v0.4s, v17.4s, v16.4s",
|
||||
@@ -3896,7 +3896,7 @@
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #48]",
|
||||
"ldr q4, [x28, #64]",
|
||||
"ldr q5, [x28, #2768]",
|
||||
"ldr q5, [x28, #2800]",
|
||||
"eor v6.16b, v18.16b, v5.16b",
|
||||
"mov v0.16b, v6.16b",
|
||||
"fmla v0.4s, v17.4s, v16.4s",
|
||||
@@ -3912,7 +3912,7 @@
|
||||
"Map 2 0b01 0xa6 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2800]",
|
||||
"ldr q2, [x28, #2832]",
|
||||
"eor v2.16b, v18.16b, v2.16b",
|
||||
"mov v0.16b, v2.16b",
|
||||
"fmla v0.2d, v17.2d, v16.2d",
|
||||
@@ -3929,7 +3929,7 @@
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #48]",
|
||||
"ldr q4, [x28, #64]",
|
||||
"ldr q5, [x28, #2800]",
|
||||
"ldr q5, [x28, #2832]",
|
||||
"eor v6.16b, v18.16b, v5.16b",
|
||||
"mov v0.16b, v6.16b",
|
||||
"fmla v0.2d, v17.2d, v16.2d",
|
||||
@@ -3945,7 +3945,7 @@
|
||||
"Map 2 0b01 0xa7 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2832]",
|
||||
"ldr q2, [x28, #2864]",
|
||||
"eor v2.16b, v18.16b, v2.16b",
|
||||
"mov v0.16b, v2.16b",
|
||||
"fmla v0.4s, v17.4s, v16.4s",
|
||||
@@ -3962,7 +3962,7 @@
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #48]",
|
||||
"ldr q4, [x28, #64]",
|
||||
"ldr q5, [x28, #2832]",
|
||||
"ldr q5, [x28, #2864]",
|
||||
"eor v6.16b, v18.16b, v5.16b",
|
||||
"mov v0.16b, v6.16b",
|
||||
"fmla v0.4s, v17.4s, v16.4s",
|
||||
@@ -3978,7 +3978,7 @@
|
||||
"Map 2 0b01 0xa7 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2864]",
|
||||
"ldr q2, [x28, #2896]",
|
||||
"eor v2.16b, v18.16b, v2.16b",
|
||||
"mov v0.16b, v2.16b",
|
||||
"fmla v0.2d, v17.2d, v16.2d",
|
||||
@@ -3995,7 +3995,7 @@
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #48]",
|
||||
"ldr q4, [x28, #64]",
|
||||
"ldr q5, [x28, #2864]",
|
||||
"ldr q5, [x28, #2896]",
|
||||
"eor v6.16b, v18.16b, v5.16b",
|
||||
"mov v0.16b, v6.16b",
|
||||
"fmla v0.2d, v17.2d, v16.2d",
|
||||
@@ -4011,7 +4011,7 @@
|
||||
"Map 2 0b01 0xb6 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2768]",
|
||||
"ldr q2, [x28, #2800]",
|
||||
"eor v2.16b, v16.16b, v2.16b",
|
||||
"mov v16.16b, v2.16b",
|
||||
"fmla v16.4s, v17.4s, v18.4s",
|
||||
@@ -4027,7 +4027,7 @@
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #48]",
|
||||
"ldr q4, [x28, #64]",
|
||||
"ldr q5, [x28, #2768]",
|
||||
"ldr q5, [x28, #2800]",
|
||||
"eor v6.16b, v16.16b, v5.16b",
|
||||
"mov v16.16b, v6.16b",
|
||||
"fmla v16.4s, v17.4s, v18.4s",
|
||||
@@ -4042,7 +4042,7 @@
|
||||
"Map 2 0b01 0xb6 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2800]",
|
||||
"ldr q2, [x28, #2832]",
|
||||
"eor v2.16b, v16.16b, v2.16b",
|
||||
"mov v16.16b, v2.16b",
|
||||
"fmla v16.2d, v17.2d, v18.2d",
|
||||
@@ -4058,7 +4058,7 @@
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #48]",
|
||||
"ldr q4, [x28, #64]",
|
||||
"ldr q5, [x28, #2800]",
|
||||
"ldr q5, [x28, #2832]",
|
||||
"eor v6.16b, v16.16b, v5.16b",
|
||||
"mov v16.16b, v6.16b",
|
||||
"fmla v16.2d, v17.2d, v18.2d",
|
||||
@@ -4073,7 +4073,7 @@
|
||||
"Map 2 0b01 0xb7 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2832]",
|
||||
"ldr q2, [x28, #2864]",
|
||||
"eor v2.16b, v16.16b, v2.16b",
|
||||
"mov v16.16b, v2.16b",
|
||||
"fmla v16.4s, v17.4s, v18.4s",
|
||||
@@ -4089,7 +4089,7 @@
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #48]",
|
||||
"ldr q4, [x28, #64]",
|
||||
"ldr q5, [x28, #2832]",
|
||||
"ldr q5, [x28, #2864]",
|
||||
"eor v6.16b, v16.16b, v5.16b",
|
||||
"mov v16.16b, v6.16b",
|
||||
"fmla v16.4s, v17.4s, v18.4s",
|
||||
@@ -4104,7 +4104,7 @@
|
||||
"Map 2 0b01 0xb7 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2864]",
|
||||
"ldr q2, [x28, #2896]",
|
||||
"eor v2.16b, v16.16b, v2.16b",
|
||||
"mov v16.16b, v2.16b",
|
||||
"fmla v16.2d, v17.2d, v18.2d",
|
||||
@@ -4120,7 +4120,7 @@
|
||||
"ldr q2, [x28, #32]",
|
||||
"ldr q3, [x28, #48]",
|
||||
"ldr q4, [x28, #64]",
|
||||
"ldr q5, [x28, #2864]",
|
||||
"ldr q5, [x28, #2896]",
|
||||
"eor v6.16b, v16.16b, v5.16b",
|
||||
"mov v16.16b, v6.16b",
|
||||
"fmla v16.2d, v17.2d, v18.2d",
|
||||
|
||||
@@ -337,7 +337,7 @@
|
||||
"Map 3 0b01 0x02 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2928]",
|
||||
"ldr q2, [x28, #2960]",
|
||||
"tbx v16.16b, {v17.16b}, v2.16b",
|
||||
"stp xzr, xzr, [x28, #32]"
|
||||
]
|
||||
@@ -348,7 +348,7 @@
|
||||
"Map 3 0b01 0x02 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2944]",
|
||||
"ldr q2, [x28, #2976]",
|
||||
"tbx v16.16b, {v17.16b}, v2.16b",
|
||||
"stp xzr, xzr, [x28, #32]"
|
||||
]
|
||||
@@ -369,7 +369,7 @@
|
||||
"Map 3 0b01 0x02 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2960]",
|
||||
"ldr q2, [x28, #2992]",
|
||||
"tbx v16.16b, {v17.16b}, v2.16b",
|
||||
"stp xzr, xzr, [x28, #32]"
|
||||
]
|
||||
@@ -391,7 +391,7 @@
|
||||
"Map 3 0b01 0x02 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2976]",
|
||||
"ldr q2, [x28, #3008]",
|
||||
"tbx v16.16b, {v17.16b}, v2.16b",
|
||||
"stp xzr, xzr, [x28, #32]"
|
||||
]
|
||||
@@ -412,7 +412,7 @@
|
||||
"Map 3 0b01 0x02 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2992]",
|
||||
"ldr q2, [x28, #3024]",
|
||||
"tbx v16.16b, {v17.16b}, v2.16b",
|
||||
"stp xzr, xzr, [x28, #32]"
|
||||
]
|
||||
@@ -423,7 +423,7 @@
|
||||
"Map 3 0b01 0x02 128-bit"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #3008]",
|
||||
"ldr q2, [x28, #3040]",
|
||||
"tbx v16.16b, {v17.16b}, v2.16b",
|
||||
"stp xzr, xzr, [x28, #32]"
|
||||
]
|
||||
@@ -3488,7 +3488,7 @@
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"movi v2.2d, #0x0",
|
||||
"ldr q3, [x28, #2912]",
|
||||
"ldr q3, [x28, #2944]",
|
||||
"mov v16.16b, v17.16b",
|
||||
"aese v16.16b, v2.16b",
|
||||
"tbl v16.16b, {v16.16b}, v3.16b",
|
||||
@@ -3502,7 +3502,7 @@
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"movi v2.2d, #0x0",
|
||||
"ldr q3, [x28, #2912]",
|
||||
"ldr q3, [x28, #2944]",
|
||||
"mov v16.16b, v17.16b",
|
||||
"aese v16.16b, v2.16b",
|
||||
"tbl v16.16b, {v16.16b}, v3.16b",
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
"0x66 0x0f 0x38 0xca"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr x0, [x28, #2440]",
|
||||
"ldr x0, [x28, #2472]",
|
||||
"ldr q2, [x0, #432]",
|
||||
"tbl v3.16b, {v16.16b}, v2.16b",
|
||||
"tbl v4.16b, {v17.16b}, v2.16b",
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
"0x66 0x0f 0x3a 0xdf"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2912]",
|
||||
"ldr q2, [x28, #2944]",
|
||||
"movi v3.2d, #0x0",
|
||||
"mov v16.16b, v17.16b",
|
||||
"aese v16.16b, v3.16b",
|
||||
@@ -68,7 +68,7 @@
|
||||
"0x66 0x0f 0x3a 0xdf"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #2912]",
|
||||
"ldr q2, [x28, #2944]",
|
||||
"movi v3.2d, #0x0",
|
||||
"mov v16.16b, v17.16b",
|
||||
"aese v16.16b, v3.16b",
|
||||
@@ -84,9 +84,9 @@
|
||||
"0x66 0x0f 0x3a 0xcc"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #3296]",
|
||||
"ldr q2, [x28, #3328]",
|
||||
"movi v3.2d, #0x0",
|
||||
"ldr x0, [x28, #2440]",
|
||||
"ldr x0, [x28, #2472]",
|
||||
"ldr q4, [x0, #432]",
|
||||
"tbl v5.16b, {v16.16b}, v4.16b",
|
||||
"tbl v6.16b, {v17.16b}, v4.16b",
|
||||
@@ -101,9 +101,9 @@
|
||||
"0x66 0x0f 0x3a 0xcc"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #3312]",
|
||||
"ldr q2, [x28, #3344]",
|
||||
"movi v3.2d, #0x0",
|
||||
"ldr x0, [x28, #2440]",
|
||||
"ldr x0, [x28, #2472]",
|
||||
"ldr q4, [x0, #432]",
|
||||
"tbl v5.16b, {v16.16b}, v4.16b",
|
||||
"tbl v6.16b, {v17.16b}, v4.16b",
|
||||
@@ -118,9 +118,9 @@
|
||||
"0x66 0x0f 0x3a 0xcc"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #3328]",
|
||||
"ldr q2, [x28, #3360]",
|
||||
"movi v3.2d, #0x0",
|
||||
"ldr x0, [x28, #2440]",
|
||||
"ldr x0, [x28, #2472]",
|
||||
"ldr q4, [x0, #432]",
|
||||
"tbl v5.16b, {v16.16b}, v4.16b",
|
||||
"tbl v6.16b, {v17.16b}, v4.16b",
|
||||
@@ -135,9 +135,9 @@
|
||||
"0x66 0x0f 0x3a 0xcc"
|
||||
],
|
||||
"ExpectedArm64ASM": [
|
||||
"ldr q2, [x28, #3344]",
|
||||
"ldr q2, [x28, #3376]",
|
||||
"movi v3.2d, #0x0",
|
||||
"ldr x0, [x28, #2440]",
|
||||
"ldr x0, [x28, #2472]",
|
||||
"ldr q4, [x0, #432]",
|
||||
"tbl v5.16b, {v16.16b}, v4.16b",
|
||||
"tbl v6.16b, {v17.16b}, v4.16b",
|
||||
|
||||
Loaded 100 of 133 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user