mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-07 12:00:17 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85c2889652 | ||
|
|
23dd056b60 | ||
|
|
71043e372a | ||
|
|
c412d073b9 | ||
|
|
8fb03ff1b9 | ||
|
|
c2b6aef6f4 | ||
|
|
24547318c6 | ||
|
|
b693112c80 | ||
|
|
48d1184066 | ||
|
|
8da9ebc2e0 | ||
|
|
5ba510474b | ||
|
|
51214d1be1 | ||
|
|
4d6e15d7af | ||
|
|
4721894427 | ||
|
|
d429865b6e | ||
|
|
ca5881a72c | ||
|
|
7151b9daff | ||
|
|
d9b5e28b22 | ||
|
|
aa7954a7d6 | ||
|
|
802c70d1ab | ||
|
|
8f905988e9 | ||
|
|
478c5595ad | ||
|
|
3977e1f29e | ||
|
|
2b1ef97354 | ||
|
|
eaddf7f1a5 | ||
|
|
3237de3085 | ||
|
|
df3d398d31 | ||
|
|
b44b3401b7 | ||
|
|
c28ca0fac9 | ||
|
|
235e2b6c2c | ||
|
|
d68b84bc27 | ||
|
|
edca528608 | ||
|
|
b75e8f2abf | ||
|
|
ec3158e4cd | ||
|
|
9c8c8041e0 | ||
|
|
e3adaacb51 | ||
|
|
c49e11484f | ||
|
|
7b4b9a80fa | ||
|
|
c7ad066987 | ||
|
|
f4d229f1ba | ||
|
|
25a8a00771 | ||
|
|
a67f7422b2 | ||
|
|
ed8150cfb6 | ||
|
|
462a163ba7 | ||
|
|
6374175a64 | ||
|
|
280b15ba2a | ||
|
|
2a0b488e99 | ||
|
|
3ef7c4ab51 | ||
|
|
e72d746036 | ||
|
|
0bb4091e34 | ||
|
|
6822fc595c | ||
|
|
a506a589dd | ||
|
|
684a5977dd | ||
|
|
ac3682e058 | ||
|
|
d5faf01f5a | ||
|
|
ecba1b6838 | ||
|
|
8d8b029285 | ||
|
|
bf6f855868 | ||
|
|
aa6a499329 | ||
|
|
0971650ef9 | ||
|
|
64c4fdccf7 | ||
|
|
d715ffbc8e | ||
|
|
aef801b5b5 | ||
|
|
6c9e29796b | ||
|
|
364bb3ac1e | ||
|
|
428ea68507 | ||
|
|
5cf59408a7 | ||
|
|
1596843015 | ||
|
|
af6582ff5b | ||
|
|
1799d4c675 | ||
|
|
808e1c0330 | ||
|
|
dacd96cab5 | ||
|
|
ca4d3bf64d | ||
|
|
ea38b043c1 | ||
|
|
a39746df2e | ||
|
|
2367a8e50b | ||
|
|
cb121d7f17 | ||
|
|
412793c21d | ||
|
|
ce2286c48b | ||
|
|
d5694d6de0 | ||
|
|
121218aa8a | ||
|
|
6bb53fa758 | ||
|
|
89aa0c5471 | ||
|
|
53fcbf6afa | ||
|
|
2f5643ae6b | ||
|
|
d162ac8b3d | ||
|
|
c9a704fbde | ||
|
|
9d5a822a3a | ||
|
|
50eba4066a | ||
|
|
6116ae5330 | ||
|
|
447226576f | ||
|
|
3f8b872f17 | ||
|
|
e573ddc2db | ||
|
|
fe9aa681f0 | ||
|
|
1b2f2c1559 | ||
|
|
84c75a86c3 | ||
|
|
eedbde6f15 | ||
|
|
4e441e5a08 | ||
|
|
3e287a36c2 | ||
|
|
eadc477695 | ||
|
|
59aa324678 | ||
|
|
219bce1467 | ||
|
|
46bde401bd | ||
|
|
01beac4956 | ||
|
|
fcd981e6b7 | ||
|
|
825833cfcc | ||
|
|
4a4c49bf68 | ||
|
|
763cea423a | ||
|
|
0fee355ff5 | ||
|
|
6f6f3c9dc5 | ||
|
|
25e5d88ab2 | ||
|
|
87013340bb | ||
|
|
47c075ccc9 | ||
|
|
b1a32d4ccf | ||
|
|
7af6a8dbdf | ||
|
|
383e99e4ef | ||
|
|
8c7cfc4d11 | ||
|
|
496ee730c8 | ||
|
|
71f7ff5101 | ||
|
|
1ea00f68a2 | ||
|
|
8df7c2d84f | ||
|
|
46557a7a1f | ||
|
|
d8c2a8271f | ||
|
|
cc4c705fc0 | ||
|
|
22f249fcf6 | ||
|
|
c262362a03 | ||
|
|
212df9aa7b | ||
|
|
691e39ec76 | ||
|
|
107cae2975 | ||
|
|
6742e0c376 | ||
|
|
8f70137b1a | ||
|
|
707db51b1b | ||
|
|
5b5fa1aa29 | ||
|
|
2b9cc9666a | ||
|
|
82eba22292 | ||
|
|
5c84e8f23c | ||
|
|
081b61677a | ||
|
|
9c54814b98 | ||
|
|
35d7b855ed | ||
|
|
0b8799274c | ||
|
|
ace2b737d8 | ||
|
|
ad85268524 | ||
|
|
832a320e22 | ||
|
|
83763df6fd | ||
|
|
bee868e9ba | ||
|
|
c41de81694 | ||
|
|
41aaeb1ff0 | ||
|
|
16be2792ab | ||
|
|
6610bb355c | ||
|
|
4312fd7291 | ||
|
|
89a225a96d | ||
|
|
a590977639 | ||
|
|
0d0d116bde | ||
|
|
341bdb5a54 | ||
|
|
2f3dbfb289 | ||
|
|
169cfbbeed | ||
|
|
f97a4afd8f | ||
|
|
868e4a6d81 | ||
|
|
6f48f7d3ac | ||
|
|
1ed3ecb409 | ||
|
|
2cb455b9d4 | ||
|
|
d4b5bf0f78 | ||
|
|
69013772c1 | ||
|
|
3448c83431 | ||
|
|
e4c84542ea | ||
|
|
acddc0323b | ||
|
|
c4285f0d30 | ||
|
|
977d6dd247 | ||
|
|
2bb27fffb7 | ||
|
|
0261ed353d | ||
|
|
96fecfd7c5 | ||
|
|
9fac1b8105 | ||
|
|
95fbcd7b9a | ||
|
|
6adf227611 | ||
|
|
b5cb429243 | ||
|
|
26ba8079a3 | ||
|
|
f999d30bc5 | ||
|
|
ecb1cc4ed4 | ||
|
|
5622bcae16 | ||
|
|
f4539ee289 | ||
|
|
d2138694b4 | ||
|
|
1767e21273 | ||
|
|
8f9d799342 | ||
|
|
a3b0b246f4 | ||
|
|
dee85f14fe | ||
|
|
27309114be | ||
|
|
704afed97b | ||
|
|
121f0a2c6c | ||
|
|
1c580ec92c | ||
|
|
ab8fc721a0 | ||
|
|
790447115c | ||
|
|
80abeac28a | ||
|
|
54915f87ce | ||
|
|
f34f1309a7 | ||
|
|
0ad52b7d19 | ||
|
|
809f60df06 | ||
|
|
cbdcd8253c | ||
|
|
7ac2cd7cc8 | ||
|
|
cf1bb1348c | ||
|
|
b60a26ff9e | ||
|
|
b805c07342 | ||
|
|
8d69f539ac | ||
|
|
c8c0054f67 | ||
|
|
1085385bbe | ||
|
|
56460b220c | ||
|
|
a583ebe590 | ||
|
|
ce8175a800 | ||
|
|
c987e1ef44 | ||
|
|
b36ec152d2 | ||
|
|
44c62e703e | ||
|
|
0f59c1d5e3 | ||
|
|
5739f0b459 | ||
|
|
4145fabfb6 |
No files matched your search
@@ -64,7 +64,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 -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DENABLE_INTERPRETER=True -DBUILD_FEX_LINUX_TESTS=True -DBUILD_THUNKS=True
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DENABLE_INTERPRETER=True -DBUILD_FEX_LINUX_TESTS=True -DBUILD_THUNKS=True -DCMAKE_INSTALL_PREFIX=${{runner.workspace}}/build/install
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
@@ -188,6 +188,40 @@ jobs:
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ThunkgenTests.log || true
|
||||
|
||||
- name: Install
|
||||
if: matrix.arch[1] == 'x64'
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target install
|
||||
|
||||
- name: Test GL No-Thunks
|
||||
if: matrix.arch[1] == 'x64'
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
env:
|
||||
DISPLAY: ":0"
|
||||
run: cmake --build . --config $BUILD_TYPE --target thunk_functional_tests_nothunks
|
||||
|
||||
- name: No thunks Results move
|
||||
if: ${{ always() }}
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_NoThunkResults.log || true
|
||||
|
||||
- name: Test GL Thunks
|
||||
if: matrix.arch[1] == 'x64'
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
env:
|
||||
DISPLAY: ":0"
|
||||
run: cmake --build . --config $BUILD_TYPE --target thunk_functional_tests_thunks
|
||||
|
||||
- name: Thunks Results move
|
||||
if: ${{ always() }}
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ThunkResults.log || true
|
||||
|
||||
- name: Truncate test results
|
||||
if: ${{ always() }}
|
||||
shell: bash
|
||||
@@ -207,4 +241,3 @@ jobs:
|
||||
name: Results-${{ env.runner_name }}
|
||||
path: ${{runner.workspace}}/build/Testing/Temporary/LastTest_*.log
|
||||
retention-days: 3
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
name: Vixl Simulator run
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Release
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.arch }}
|
||||
strategy:
|
||||
matrix:
|
||||
# Only the x86-64 runner is fast enough to run this
|
||||
arch: [[self-hosted, x64], [self-hosted, ARMv8.4]]
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set runner label
|
||||
run: echo "runner_label=${{ matrix.arch[1] }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Set rootfs paths
|
||||
run: |
|
||||
echo "FEX_ROOTFS_MOUNT=/mnt/AutoNFS/rootfs/" >> $GITHUB_ENV
|
||||
echo "FEX_ROOTFS_PATH=$HOME/Rootfs/" >> $GITHUB_ENV
|
||||
echo "FEX_ROOTFS=$HOME/Rootfs/" >> $GITHUB_ENV
|
||||
echo "ROOTFS=$HOME/Rootfs/" >> $GITHUB_ENV
|
||||
|
||||
- name: Update RootFS cache
|
||||
# Use a bash shell so we can use the same syntax for environment variable
|
||||
# access regardless of the host operating system
|
||||
shell: bash
|
||||
run: $GITHUB_WORKSPACE/Scripts/CI_FetchRootFS.py
|
||||
|
||||
- name : submodule checkout
|
||||
# Need to update submodules
|
||||
run: |
|
||||
git submodule sync --recursive
|
||||
git submodule update --init --depth 1
|
||||
|
||||
- name: Clean Build Environment
|
||||
run: rm -Rf ${{runner.workspace}}/build
|
||||
|
||||
- name: Create Build Environment
|
||||
# Some projects don't allow in-source building, so create a separate build directory
|
||||
# We'll use this as our working directory for all subsequent commands
|
||||
run: cmake -E make_directory ${{runner.workspace}}/build
|
||||
|
||||
- name: Configure CMake
|
||||
# Use a bash shell so we can use the same syntax for environment variable
|
||||
# access regardless of the host operating system
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
# 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 -G Ninja -DENABLE_VIXL_SIMULATOR=True -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
# Execute the build. You can specify a specific target with "--target <NAME>"
|
||||
run: cmake --build . --config $BUILD_TYPE
|
||||
|
||||
- name: ASM Tests
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
# Execute the unit tests
|
||||
run: cmake --build . --config $BUILD_TYPE --target asm_tests
|
||||
|
||||
- name: ASM Test Results move
|
||||
if: ${{ always() }}
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ASM.log || true
|
||||
|
||||
- name: IR Tests
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
# Execute the unit tests
|
||||
run: cmake --build . --config $BUILD_TYPE --target ir_tests
|
||||
|
||||
- name: IR Test Results move
|
||||
if: ${{ always() }}
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_IR.log || true
|
||||
|
||||
- name: Truncate test results
|
||||
if: ${{ always() }}
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
# Cap out the log files at 20M in case something crash spins and dumps fault text
|
||||
# ASM tests get quite close to 10MB
|
||||
run: truncate --size=<20M ${{runner.workspace}}/build/Testing/Temporary/LastTest_*.log || true
|
||||
|
||||
- name: Set runner name
|
||||
if: ${{ always() }}
|
||||
run: echo "runner_name=$(hostname)" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload results
|
||||
if: ${{ always() }}
|
||||
uses: 'actions/upload-artifact@v2'
|
||||
with:
|
||||
name: Results-${{ env.runner_name }}
|
||||
path: ${{runner.workspace}}/build/Testing/Temporary/LastTest_*.log
|
||||
retention-days: 3
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"ThunksDB": {
|
||||
"GL": 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"ThunksDB": {
|
||||
"Vulkan": 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
|
||||
message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
|
||||
endif()
|
||||
|
||||
file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
|
||||
string(REGEX REPLACE "\n" ";" files "${files}")
|
||||
foreach(file ${files})
|
||||
message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
|
||||
if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
|
||||
exec_program(
|
||||
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
|
||||
OUTPUT_VARIABLE rm_out
|
||||
RETURN_VALUE rm_retval
|
||||
)
|
||||
if(NOT "${rm_retval}" STREQUAL 0)
|
||||
message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
|
||||
endif()
|
||||
else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
|
||||
message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
|
||||
endif()
|
||||
endforeach()
|
||||
+59
-6
@@ -7,6 +7,7 @@ CHECK_INCLUDE_FILES ("gdb/jit-reader.h" HAVE_GDB_JIT_READER_H)
|
||||
option(BUILD_TESTS "Build unit tests to ensure sanity" TRUE)
|
||||
option(BUILD_FEX_LINUX_TESTS "Build FEXLinuxTests, requires x86 compiler" FALSE)
|
||||
option(BUILD_THUNKS "Build thunks" FALSE)
|
||||
option(BUILD_CLANG_THUNKS "Build thunks with clang" FALSE)
|
||||
option(ENABLE_CLANG_FORMAT "Run clang format over the source" FALSE)
|
||||
option(ENABLE_IWYU "Enables include what you use program" FALSE)
|
||||
option(ENABLE_LTO "Enable LTO with compilation" TRUE)
|
||||
@@ -27,10 +28,23 @@ option(ENABLE_LIBCXX "Enables LLVM libc++" FALSE)
|
||||
option(ENABLE_INTERPRETER "Enables FEX's Interpreter" FALSE)
|
||||
option(ENABLE_CCACHE "Enables ccache for compile caching" TRUE)
|
||||
option(ENABLE_TERMUX_BUILD "Forces building for Termux on a non-Termux build machine" FALSE)
|
||||
option(ENABLE_VIXL_SIMULATOR "Forces the FEX JIT to use the VIXL simulator" FALSE)
|
||||
|
||||
set (X86_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/toolchain_x86.cmake" CACHE FILEPATH "Toolchain file for the x86 (cross-)compiler")
|
||||
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 (DATA_DIRECTORY "${CMAKE_INSTALL_PREFIX}/share/fex-emu" CACHE PATH "global data directory")
|
||||
|
||||
# uninstall target
|
||||
if(NOT TARGET uninstall)
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/CMakeFiles/cmake_uninstall.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/cmake_uninstall.cmake"
|
||||
IMMEDIATE @ONLY)
|
||||
|
||||
add_custom_target(uninstall
|
||||
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/cmake_uninstall.cmake)
|
||||
endif()
|
||||
|
||||
# These options are meant for package management
|
||||
set (TUNE_CPU "native" CACHE STRING "Override the CPU the build is tuned for")
|
||||
set (TUNE_ARCH "generic" CACHE STRING "Override the Arch the build is tuned for")
|
||||
@@ -84,10 +98,9 @@ if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
|
||||
set(_M_X86_64 1)
|
||||
add_definitions(-D_M_X86_64=1)
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcx16")
|
||||
set (X86_TOOLCHAIN_FILE "")
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64|^arm64|^armv8\.*")
|
||||
set(_M_ARM_64 1)
|
||||
add_definitions(-D_M_ARM_64=1)
|
||||
endif()
|
||||
@@ -363,8 +376,6 @@ if (BUILD_TESTS)
|
||||
endif()
|
||||
|
||||
add_subdirectory(FEXHeaderUtils/)
|
||||
include_directories(FEXHeaderUtils/)
|
||||
|
||||
add_subdirectory(External/FEXCore)
|
||||
|
||||
# Binfmt_misc files must be installed prior to Source/ installs
|
||||
@@ -403,8 +414,28 @@ if (BUILD_THUNKS)
|
||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ThunkLibs/GuestLibs"
|
||||
BINARY_DIR "Guest"
|
||||
CMAKE_ARGS
|
||||
"-DBITNESS=64"
|
||||
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
|
||||
"-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${X86_TOOLCHAIN_FILE}"
|
||||
"-DENABLE_CLANG_THUNKS=${ENABLE_CLANG_THUNKS}"
|
||||
"-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${X86_64_TOOLCHAIN_FILE}"
|
||||
"-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}"
|
||||
"-DSTRUCT_VERIFIER=${CMAKE_SOURCE_DIR}/Scripts/StructPackVerifier.py"
|
||||
"-DFEX_PROJECT_SOURCE_DIR=${FEX_PROJECT_SOURCE_DIR}"
|
||||
"-DGENERATOR_EXE=$<TARGET_FILE:thunkgen>"
|
||||
INSTALL_COMMAND ""
|
||||
BUILD_ALWAYS ON
|
||||
DEPENDS thunkgen
|
||||
)
|
||||
|
||||
ExternalProject_Add(guest-libs-32
|
||||
PREFIX guest-libs-32
|
||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ThunkLibs/GuestLibs"
|
||||
BINARY_DIR "Guest_32"
|
||||
CMAKE_ARGS
|
||||
"-DBITNESS=32"
|
||||
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
|
||||
"-DENABLE_CLANG_THUNKS=${ENABLE_CLANG_THUNKS}"
|
||||
"-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${X86_32_TOOLCHAIN_FILE}"
|
||||
"-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}"
|
||||
"-DSTRUCT_VERIFIER=${CMAKE_SOURCE_DIR}/Scripts/StructPackVerifier.py"
|
||||
"-DFEX_PROJECT_SOURCE_DIR=${FEX_PROJECT_SOURCE_DIR}"
|
||||
@@ -422,6 +453,28 @@ if (BUILD_THUNKS)
|
||||
)"
|
||||
DEPENDS guest-libs
|
||||
)
|
||||
|
||||
install(
|
||||
CODE "MESSAGE(\"-- Installing: guest-libs-32\")"
|
||||
CODE "
|
||||
EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} --build . --target install
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/Guest_32
|
||||
)"
|
||||
DEPENDS guest-libs-32
|
||||
)
|
||||
|
||||
add_custom_target(uninstall_guest-libs
|
||||
COMMAND ${CMAKE_COMMAND} "--build" "." "--target" "uninstall"
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/Guest
|
||||
)
|
||||
|
||||
add_custom_target(uninstall_guest-libs-32
|
||||
COMMAND ${CMAKE_COMMAND} "--build" "." "--target" "uninstall"
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/Guest_32
|
||||
)
|
||||
|
||||
add_dependencies(uninstall uninstall_guest-libs)
|
||||
add_dependencies(uninstall uninstall_guest-libs-32)
|
||||
endif()
|
||||
|
||||
set(FEX_VERSION_MAJOR "0")
|
||||
|
||||
@@ -165,6 +165,14 @@
|
||||
"@PREFIX_LIB@/x86_64-linux-gnu/libXfixes.so.3.1.0"
|
||||
]
|
||||
},
|
||||
"OpenCL": {
|
||||
"Library" : "libOpenCL-guest.so",
|
||||
"Overlay": [
|
||||
"@PREFIX_LIB@/x86_64-linux-gnu/libOpenCL.so",
|
||||
"@PREFIX_LIB@/x86_64-linux-gnu/libOpenCL.so.1",
|
||||
"@PREFIX_LIB@/x86_64-linux-gnu/libOpenCL.so.1.0.0"
|
||||
]
|
||||
},
|
||||
"":{}
|
||||
}
|
||||
}
|
||||
Vendored
+10
-4
@@ -9,12 +9,19 @@ if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcx16")
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64|^arm64|^armv8\.*")
|
||||
set(_M_ARM_64 1)
|
||||
endif()
|
||||
|
||||
set(ENABLE_JIT_X86_64 ${_M_X86_64} CACHE BOOL "Enable the x86_64 JIT")
|
||||
set(ENABLE_JIT_ARM64 ${_M_ARM_64} CACHE BOOL "Enable the ARM64 JIT")
|
||||
if (ENABLE_VIXL_SIMULATOR)
|
||||
# If the vixl simulator is enabled then we are using the ARM64 JIT
|
||||
option(ENABLE_JIT_X86_64 "Enable the x86_64 JIT" FALSE)
|
||||
option(ENABLE_JIT_ARM64 "Enable the ARM64 JIT" TRUE)
|
||||
else()
|
||||
option(ENABLE_JIT_X86_64 "Enable the x86_64 JIT" ${_M_X86_64})
|
||||
option(ENABLE_JIT_ARM64 "Enable the ARM64 JIT" ${_M_ARM_64})
|
||||
endif()
|
||||
|
||||
option(ENABLE_CLANG_FORMAT "Run clang format over the source" FALSE)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
@@ -27,7 +34,6 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
include(CheckCXXCompilerFlag)
|
||||
include(CheckIncludeFileCXX)
|
||||
|
||||
|
||||
if (EXISTS ${CMAKE_CURRENT_DIR}/External/vixl/)
|
||||
# Useful to have for freestanding libFEXCore
|
||||
add_subdirectory(External/vixl/)
|
||||
|
||||
+9
-4
@@ -177,6 +177,11 @@ if (_M_ARM_64)
|
||||
list(APPEND DEFINES -D_M_ARM_64=1)
|
||||
endif()
|
||||
|
||||
if (ENABLE_VIXL_SIMULATOR)
|
||||
# We can run the simulator on both x86-64 or AArch64 hosts
|
||||
list(APPEND DEFINES -DVIXL_SIMULATOR=1 -DVIXL_INCLUDE_SIMULATOR_AARCH64=1)
|
||||
endif()
|
||||
|
||||
if (ENABLE_JIT_X86_64)
|
||||
list(APPEND SRCS
|
||||
Interface/Core/JIT/x86_64/JIT.cpp
|
||||
@@ -213,7 +218,7 @@ if (ENABLE_JIT_ARM64)
|
||||
)
|
||||
endif()
|
||||
|
||||
set (LIBS vixl dl xxhash tiny-json)
|
||||
set (LIBS fmt::fmt vixl dl xxhash tiny-json FEXHeaderUtils)
|
||||
if (ENABLE_JEMALLOC)
|
||||
list (APPEND LIBS FEX_jemalloc)
|
||||
endif()
|
||||
@@ -359,14 +364,14 @@ endfunction()
|
||||
|
||||
# Build FEXCore_Config static library
|
||||
add_library(FEXCore_Base STATIC ${FEXCORE_BASE_SRCS})
|
||||
target_link_libraries(FEXCore_Base fmt::fmt tiny-json)
|
||||
target_link_libraries(FEXCore_Base ${LIBS})
|
||||
AddDefaultOptionsToTarget(FEXCore_Base)
|
||||
|
||||
function(AddObject Name Type)
|
||||
add_library(${Name} ${Type} ${SRCS})
|
||||
add_dependencies(${Name} IR_INC)
|
||||
|
||||
target_link_libraries(${Name} FEXCore_Base ${LIBS})
|
||||
target_link_libraries(${Name} FEXCore_Base)
|
||||
AddDefaultOptionsToTarget(${Name})
|
||||
|
||||
set_target_properties(${Name} PROPERTIES OUTPUT_NAME FEXCore)
|
||||
@@ -374,7 +379,7 @@ endfunction()
|
||||
|
||||
function(AddLibrary Name Type)
|
||||
add_library(${Name} ${Type} $<TARGET_OBJECTS:${PROJECT_NAME}_object>)
|
||||
target_link_libraries(${Name} FEXCore_Base ${LIBS})
|
||||
target_link_libraries(${Name} FEXCore_Base)
|
||||
set_target_properties(${Name} PROPERTIES OUTPUT_NAME FEXCore)
|
||||
|
||||
AddDefaultOptionsToTarget(${Name})
|
||||
|
||||
@@ -90,6 +90,13 @@
|
||||
"Folder to find the guest-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": "",
|
||||
|
||||
+2
-1
@@ -15,6 +15,7 @@
|
||||
#include <FEXCore/Debug/InternalThreadState.h>
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
#include <FEXCore/Utils/Event.h>
|
||||
#include <FEXHeaderUtils/Syscalls.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
@@ -189,7 +190,7 @@ namespace FEXCore::Context {
|
||||
static void ThreadRemoveCodeEntryFromJit(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP) {
|
||||
auto Thread = Frame->Thread;
|
||||
|
||||
LogMan::Throw::AFmt(Thread->ThreadManager.GetTID() == gettid(), "Must be called from owning thread {}, not {}", Thread->ThreadManager.GetTID(), gettid());
|
||||
LogMan::Throw::AFmt(Thread->ThreadManager.GetTID() == FHU::Syscalls::gettid(), "Must be called from owning thread {}, not {}", Thread->ThreadManager.GetTID(), FHU::Syscalls::gettid());
|
||||
|
||||
FHU::ScopedSignalMaskWithUniqueLock lk(Thread->CTX->CodeInvalidationMutex);
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include <utility>
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
#define STATE x28
|
||||
|
||||
// We want vixl to not allocate a default buffer. Jit and dispatcher will manually create one.
|
||||
Arm64Emitter::Arm64Emitter(FEXCore::Context::Context *ctx, size_t size)
|
||||
@@ -25,11 +24,15 @@ Arm64Emitter::Arm64Emitter(FEXCore::Context::Context *ctx, size_t size)
|
||||
, EmitterCTX {ctx} {
|
||||
CPU.SetUp();
|
||||
|
||||
#ifdef VIXL_SIMULATOR
|
||||
auto Features = vixl::CPUFeatures::All();
|
||||
#else
|
||||
auto Features = vixl::CPUFeatures::InferFromOS();
|
||||
if (ctx->HostFeatures.SupportsAtomics) {
|
||||
// Hypervisor can hide this on the c630?
|
||||
Features.Combine(vixl::CPUFeatures::Feature::kLORegions);
|
||||
}
|
||||
#endif
|
||||
|
||||
SetCPUFeatures(Features);
|
||||
}
|
||||
@@ -214,7 +217,8 @@ void Arm64Emitter::SpillStaticRegs(bool FPRs, uint32_t GPRSpillMask, uint32_t FP
|
||||
const auto Reg = SRAFPR[i];
|
||||
|
||||
if (((1U << Reg.GetCode()) & FPRSpillMask) != 0) {
|
||||
str(Reg.Q(), MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.xmm.avx.data[i][0])));
|
||||
mov(TMP4, offsetof(Core::CpuStateFrame, State.xmm.avx.data[i][0]));
|
||||
st1b(Reg.Z().VnB(), PRED_TMP_32B, SVEMemOperand(STATE, TMP4));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -257,11 +261,19 @@ void Arm64Emitter::FillStaticRegs(bool FPRs, uint32_t GPRFillMask, uint32_t FPRF
|
||||
|
||||
if (FPRs) {
|
||||
if (EmitterCTX->HostFeatures.SupportsAVX) {
|
||||
// Set up predicate registers.
|
||||
// We don't bother spilling these in SpillStaticRegs,
|
||||
// since all that matters is we restore them on a fill.
|
||||
// It's not a concern if they get trounced by something else.
|
||||
ptrue(PRED_TMP_16B.VnB(), SVE_VL16);
|
||||
ptrue(PRED_TMP_32B.VnB(), SVE_VL32);
|
||||
|
||||
for (size_t i = 0; i < SRAFPR.size(); i++) {
|
||||
const auto Reg = SRAFPR[i];
|
||||
|
||||
if (((1U << Reg.GetCode()) & FPRFillMask) != 0) {
|
||||
ldr(Reg.Q(), MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.xmm.avx.data[i][0])));
|
||||
mov(TMP4, offsetof(Core::CpuStateFrame, State.xmm.avx.data[i][0]));
|
||||
ld1b(Reg.Z().VnB(), PRED_TMP_32B.Zeroing(), SVEMemOperand(STATE, TMP4));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -286,20 +298,31 @@ void Arm64Emitter::FillStaticRegs(bool FPRs, uint32_t GPRFillMask, uint32_t FPRF
|
||||
}
|
||||
|
||||
void Arm64Emitter::PushDynamicRegsAndLR() {
|
||||
uint64_t SPOffset = AlignUp((RA64.size() + 1) * 8 + RAFPR.size() * 16, 16);
|
||||
const auto CanUseSVE = EmitterCTX->HostFeatures.SupportsAVX;
|
||||
const auto GPRSize = (RA64.size() + 1) * Core::CPUState::GPR_REG_SIZE;
|
||||
const auto FPRRegSize = CanUseSVE ? Core::CPUState::XMM_AVX_REG_SIZE
|
||||
: Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
const auto FPRSize = RAFPR.size() * FPRRegSize;
|
||||
const uint64_t SPOffset = AlignUp(GPRSize + FPRSize, 16);
|
||||
|
||||
sub(sp, sp, SPOffset);
|
||||
int i = 0;
|
||||
|
||||
for (auto RA : RAFPR)
|
||||
{
|
||||
str(RA.Q(), MemOperand(sp, i * 8));
|
||||
i+=2;
|
||||
if (CanUseSVE) {
|
||||
for (const auto& RA : RAFPR) {
|
||||
mov(TMP4, i * 8);
|
||||
st1b(RA.Z().VnB(), PRED_TMP_32B, SVEMemOperand(sp, TMP4));
|
||||
i += 4;
|
||||
}
|
||||
} else {
|
||||
for (const auto& RA : RAFPR) {
|
||||
str(RA.Q(), MemOperand(sp, i * 8));
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 // All GPRs should be caller saved
|
||||
for (auto RA : RA64)
|
||||
{
|
||||
for (const auto& RA : RA64) {
|
||||
str(RA, MemOperand(sp, i * 8));
|
||||
i++;
|
||||
}
|
||||
@@ -309,18 +332,29 @@ void Arm64Emitter::PushDynamicRegsAndLR() {
|
||||
}
|
||||
|
||||
void Arm64Emitter::PopDynamicRegsAndLR() {
|
||||
uint64_t SPOffset = AlignUp((RA64.size() + 1) * 8 + RAFPR.size() * 16, 16);
|
||||
const auto CanUseSVE = EmitterCTX->HostFeatures.SupportsAVX;
|
||||
const auto GPRSize = (RA64.size() + 1) * Core::CPUState::GPR_REG_SIZE;
|
||||
const auto FPRRegSize = CanUseSVE ? Core::CPUState::XMM_AVX_REG_SIZE
|
||||
: Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
const auto FPRSize = RAFPR.size() * FPRRegSize;
|
||||
const uint64_t SPOffset = AlignUp(GPRSize + FPRSize, 16);
|
||||
int i = 0;
|
||||
|
||||
for (auto RA : RAFPR)
|
||||
{
|
||||
ldr(RA.Q(), MemOperand(sp, i * 8));
|
||||
i+=2;
|
||||
if (CanUseSVE) {
|
||||
for (const auto& RA : RAFPR) {
|
||||
mov(TMP4, i * 8);
|
||||
ld1b(RA.Z().VnB(), PRED_TMP_32B.Zeroing(), SVEMemOperand(sp, TMP4));
|
||||
i += 4;
|
||||
}
|
||||
} else {
|
||||
for (const auto& RA : RAFPR) {
|
||||
ldr(RA.Q(), MemOperand(sp, i * 8));
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 // All GPRs should be caller saved
|
||||
for (auto RA : RA64)
|
||||
{
|
||||
for (const auto& RA : RA64) {
|
||||
ldr(RA, MemOperand(sp, i * 8));
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
#include <aarch64/cpu-aarch64.h>
|
||||
#include <aarch64/operands-aarch64.h>
|
||||
#include <platform-vixl.h>
|
||||
#ifdef VIXL_SIMULATOR
|
||||
#include <aarch64/simulator-aarch64.h>
|
||||
#include <aarch64/simulator-constants-aarch64.h>
|
||||
#endif
|
||||
|
||||
#include <FEXCore/Config/Config.h>
|
||||
|
||||
@@ -58,6 +62,27 @@ const std::array<aarch64::VRegister, 12> RAFPR = {
|
||||
v8, v9, v10, v11, v12, v13, v14, v15
|
||||
};
|
||||
|
||||
// Contains the address to the currently available CPU state
|
||||
#define STATE x28
|
||||
|
||||
// GPR temporaries. Only x3 can be used across spill boundaries
|
||||
// so if these ever need to change, be very careful about that.
|
||||
#define TMP1 x0
|
||||
#define TMP2 x1
|
||||
#define TMP3 x2
|
||||
#define TMP4 x3
|
||||
|
||||
// Vector temporaries
|
||||
#define VTMP1 v1
|
||||
#define VTMP2 v2
|
||||
#define VTMP3 v3
|
||||
|
||||
// Predicate register temporaries (used when AVX support is enabled)
|
||||
// PRED_TMP_16B indicates a predicate register that indicates the first 16 bytes set to 1.
|
||||
// PRED_TMP_32B indicates a predicate register that indicates the first 32 bytes set to 1.
|
||||
#define PRED_TMP_16B p6
|
||||
#define PRED_TMP_32B p7
|
||||
|
||||
// This class contains common emitter utility functions that can
|
||||
// be used by both Arm64 JIT and ARM64 Dispatcher
|
||||
class Arm64Emitter : public vixl::aarch64::Assembler {
|
||||
@@ -67,6 +92,10 @@ protected:
|
||||
FEXCore::Context::Context *EmitterCTX;
|
||||
vixl::aarch64::CPU CPU;
|
||||
void LoadConstant(vixl::aarch64::Register Reg, uint64_t Constant, bool NOPPad = false);
|
||||
|
||||
// NOTE: These functions WILL clobber the register TMP4 if AVX support is enabled
|
||||
// and FPRs are being spilled or filled. If only GPRs are spilled/filled, then
|
||||
// TMP4 is left alone.
|
||||
void SpillStaticRegs(bool FPRs = true, uint32_t GPRSpillMask = ~0U, uint32_t FPRSpillMask = ~0U);
|
||||
void FillStaticRegs(bool FPRs = true, uint32_t GPRFillMask = ~0U, uint32_t FPRFillMask = ~0U);
|
||||
|
||||
@@ -83,6 +112,71 @@ protected:
|
||||
void PopCalleeSavedRegisters();
|
||||
|
||||
void Align16B();
|
||||
#ifdef VIXL_SIMULATOR
|
||||
// Generates a vixl simulator runtime call.
|
||||
//
|
||||
// This matches behaviour of vixl's macro assembler, but we need to reimplement it since we aren't using the macro assembler.
|
||||
// This isn't too complex with how vixl emits this.
|
||||
//
|
||||
// Emit:
|
||||
// 1) hlt(kRuntimeCallOpcode)
|
||||
// 2) Simulator wrapper handler
|
||||
// 3) Function to call
|
||||
// 4) Style of the function call (Call versus tail-call)
|
||||
template<typename R, typename... P>
|
||||
void GenerateRuntimeCall(R (*Function)(P...)) {
|
||||
uintptr_t SimulatorWrapperAddress = reinterpret_cast<uintptr_t>(
|
||||
&(Simulator::RuntimeCallStructHelper<R, P...>::Wrapper));
|
||||
|
||||
uintptr_t FunctionAddress = reinterpret_cast<uintptr_t>(Function);
|
||||
|
||||
hlt(kRuntimeCallOpcode);
|
||||
|
||||
// Simulator wrapper address pointer.
|
||||
dc(SimulatorWrapperAddress);
|
||||
|
||||
// Runtime function address to call
|
||||
dc(FunctionAddress);
|
||||
|
||||
// Call type
|
||||
dc32(kCallRuntime);
|
||||
}
|
||||
|
||||
template<typename R, typename... P>
|
||||
void GenerateIndirectRuntimeCall(vixl::aarch64::Register Reg) {
|
||||
uintptr_t SimulatorWrapperAddress = reinterpret_cast<uintptr_t>(
|
||||
&(Simulator::RuntimeCallStructHelper<R, P...>::Wrapper));
|
||||
|
||||
hlt(kIndirectRuntimeCallOpcode);
|
||||
|
||||
// Simulator wrapper address pointer.
|
||||
dc(SimulatorWrapperAddress);
|
||||
|
||||
// Register that contains the function to call
|
||||
dc(Reg.GetCode());
|
||||
|
||||
// Call type
|
||||
dc32(kCallRuntime);
|
||||
}
|
||||
|
||||
template<>
|
||||
void GenerateIndirectRuntimeCall<float, __uint128_t>(vixl::aarch64::Register Reg) {
|
||||
uintptr_t SimulatorWrapperAddress = reinterpret_cast<uintptr_t>(
|
||||
&(Simulator::RuntimeCallStructHelper<float, __uint128_t>::Wrapper));
|
||||
|
||||
hlt(kIndirectRuntimeCallOpcode);
|
||||
|
||||
// Simulator wrapper address pointer.
|
||||
dc(SimulatorWrapperAddress);
|
||||
|
||||
// Register that contains the function to call
|
||||
dc(Reg.GetCode());
|
||||
|
||||
// Call type
|
||||
dc32(kCallRuntime);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
FEX_CONFIG_OPT(StaticRegisterAllocation, SRA);
|
||||
};
|
||||
|
||||
+13
-13
@@ -221,7 +221,7 @@ namespace FEXCore::Context {
|
||||
#if (_M_X86_64 && JIT_X86_64)
|
||||
FEXCore::CPU::InitializeX86JITSignalHandlers(this);
|
||||
BackendFeatures = FEXCore::CPU::GetX86JITBackendFeatures();
|
||||
#elif (_M_ARM_64 && JIT_ARM64)
|
||||
#elif (_M_ARM_64 && JIT_ARM64) || defined(VIXL_SIMULATOR)
|
||||
FEXCore::CPU::InitializeArm64JITSignalHandlers(this);
|
||||
BackendFeatures = FEXCore::CPU::GetArm64JITBackendFeatures();
|
||||
#else
|
||||
@@ -238,16 +238,16 @@ namespace FEXCore::Context {
|
||||
|
||||
DispatcherConfig.StaticRegisterAllocation = Config.StaticRegisterAllocation && BackendFeatures.SupportsStaticRegisterAllocation;
|
||||
|
||||
#if (_M_X86_64)
|
||||
Dispatcher = FEXCore::CPU::Dispatcher::CreateX86(this, DispatcherConfig);
|
||||
#elif (_M_ARM_64)
|
||||
#if JIT_ARM64
|
||||
Dispatcher = FEXCore::CPU::Dispatcher::CreateArm64(this, DispatcherConfig);
|
||||
#elif JIT_X86_64
|
||||
Dispatcher = FEXCore::CPU::Dispatcher::CreateX86(this, DispatcherConfig);
|
||||
#else
|
||||
ERROR_AND_DIE_FMT("FEXCore has been compiled with an unknown target");
|
||||
#endif
|
||||
|
||||
// Initialize common signal handlers
|
||||
|
||||
|
||||
auto PauseHandler = [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
|
||||
return Thread->CTX->Dispatcher->HandleSignalPause(Thread, Signal, info, ucontext);
|
||||
};
|
||||
@@ -573,7 +573,7 @@ namespace FEXCore::Context {
|
||||
|
||||
#if (_M_X86_64 && JIT_X86_64)
|
||||
Thread->CPUBackend = FEXCore::CPU::CreateX86JITCore(this, Thread);
|
||||
#elif (_M_ARM_64 && JIT_ARM64)
|
||||
#elif (_M_ARM_64 && JIT_ARM64) || defined(VIXL_SIMULATOR)
|
||||
Thread->CPUBackend = FEXCore::CPU::CreateArm64JITCore(this, Thread);
|
||||
#else
|
||||
ERROR_AND_DIE_FMT("FEXCore has been compiled without a viable JIT core");
|
||||
@@ -740,7 +740,7 @@ namespace FEXCore::Context {
|
||||
}
|
||||
}
|
||||
|
||||
Context::GenerateIRResult Context::GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP, bool ExtendedDebugInfo) {
|
||||
Context::GenerateIRResult Context::GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP, bool ExtendedDebugInfo) {
|
||||
Thread->OpDispatcher->ReownOrClaimBuffer();
|
||||
Thread->OpDispatcher->ResetWorkingList();
|
||||
|
||||
@@ -749,7 +749,7 @@ namespace FEXCore::Context {
|
||||
|
||||
|
||||
std::shared_lock lk(CustomIRMutex);
|
||||
|
||||
|
||||
auto Handler = CustomIRHandlers.find(GuestRIP);
|
||||
if (Handler != CustomIRHandlers.end()) {
|
||||
TotalInstructions = 1;
|
||||
@@ -872,7 +872,7 @@ namespace FEXCore::Context {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Thread->OpDispatcher->Finalize();
|
||||
|
||||
Thread->FrontendDecoder->DelayedDisownBuffer();
|
||||
@@ -1182,7 +1182,7 @@ namespace FEXCore::Context {
|
||||
|
||||
static void InvalidateGuestCodeRangeInternal(FEXCore::Context::Context *CTX, uint64_t Start, uint64_t Length) {
|
||||
std::lock_guard lk(CTX->ThreadCreationMutex);
|
||||
|
||||
|
||||
for (auto &Thread : CTX->Threads) {
|
||||
InvalidateGuestThreadCodeRange(Thread, Start, Length);
|
||||
}
|
||||
@@ -1190,7 +1190,7 @@ namespace FEXCore::Context {
|
||||
|
||||
void InvalidateGuestCodeRange(FEXCore::Context::Context *CTX, uint64_t Start, uint64_t Length) {
|
||||
FHU::ScopedSignalMaskWithUniqueLock CodeInvalidationLock(CTX->CodeInvalidationMutex);
|
||||
|
||||
|
||||
InvalidateGuestCodeRangeInternal(CTX, Start, Length);
|
||||
}
|
||||
|
||||
@@ -1233,9 +1233,9 @@ namespace FEXCore::Context {
|
||||
Thread->LookupCache->AddBlockLink(GuestDestination, HostLink, delinker);
|
||||
}
|
||||
|
||||
void Context::ThreadRemoveCodeEntry(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
|
||||
void Context::ThreadRemoveCodeEntry(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
|
||||
LogMan::Throw::AFmt(Thread->CTX->CodeInvalidationMutex.try_lock() == false, "CodeInvalidationMutex needs to be unique_locked here");
|
||||
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lk(Thread->LookupCache->WriteLock);
|
||||
|
||||
Thread->DebugStore.erase(GuestRIP);
|
||||
|
||||
@@ -38,11 +38,20 @@ namespace FEXCore::CPU {
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
|
||||
static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096;
|
||||
#define STATE x28
|
||||
constexpr size_t MAX_DISPATCHER_CODE_SIZE = 8192;
|
||||
|
||||
Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const DispatcherConfig &config)
|
||||
: FEXCore::CPU::Dispatcher(ctx, config), Arm64Emitter(ctx, MAX_DISPATCHER_CODE_SIZE) {
|
||||
: FEXCore::CPU::Dispatcher(ctx, config), Arm64Emitter(ctx, MAX_DISPATCHER_CODE_SIZE)
|
||||
#ifdef VIXL_SIMULATOR
|
||||
, Simulator {&Decoder}
|
||||
#endif
|
||||
{
|
||||
|
||||
#ifdef VIXL_SIMULATOR
|
||||
// Hardcode a 256-bit vector width if we are running in the simulator.
|
||||
Simulator.SetVectorLengthInBits(256);
|
||||
#endif
|
||||
|
||||
SetAllowAssembler(true);
|
||||
|
||||
DispatchPtr = GetCursorAddress<AsmDispatch>();
|
||||
@@ -178,7 +187,12 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
|
||||
ret();
|
||||
}
|
||||
|
||||
#ifdef VIXL_SIMULATOR
|
||||
// VIXL simulator can't run syscalls.
|
||||
constexpr bool SignalSafeCompile = false;
|
||||
#else
|
||||
constexpr bool SignalSafeCompile = true;
|
||||
#endif
|
||||
{
|
||||
ExitFunctionLinkerAddress = GetCursorAddress<uint64_t>();
|
||||
if (config.StaticRegisterAllocation)
|
||||
@@ -206,8 +220,12 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
|
||||
mov(x0, STATE);
|
||||
mov(x1, lr);
|
||||
|
||||
ldr(x3, STATE_PTR(CpuStateFrame, Pointers.Common.ExitFunctionLink));
|
||||
blr(x3);
|
||||
ldr(x2, STATE_PTR(CpuStateFrame, Pointers.Common.ExitFunctionLink));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<uintptr_t, void *, void *>(x2);
|
||||
#else
|
||||
blr(x2);
|
||||
#endif
|
||||
|
||||
if (SignalSafeCompile) {
|
||||
// Now restore the signal mask
|
||||
@@ -266,8 +284,11 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
|
||||
ldr(x3, &l_CompileBlock);
|
||||
|
||||
// X2 contains our guest RIP
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<void, void *, uint64_t, void *>(x3);
|
||||
#else
|
||||
blr(x3); // { CTX, Frame, RIP}
|
||||
|
||||
#endif
|
||||
|
||||
if (SignalSafeCompile) {
|
||||
// Now restore the signal mask
|
||||
@@ -349,7 +370,11 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
|
||||
ldr(x0, &l_CTX);
|
||||
mov(x1, STATE);
|
||||
ldr(x2, &l_Sleep);
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<void, void *, void *>(x2);
|
||||
#else
|
||||
blr(x2);
|
||||
#endif
|
||||
|
||||
PauseReturnInstruction = GetCursorAddress<uint64_t>();
|
||||
// Fault to start running again
|
||||
@@ -412,11 +437,14 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
|
||||
LUDIVHandlerAddress = GetCursorAddress<uint64_t>();
|
||||
|
||||
PushDynamicRegsAndLR();
|
||||
SpillStaticRegs();
|
||||
|
||||
ldr(x3, STATE_PTR(CpuStateFrame, Pointers.AArch64.LUDIV));
|
||||
|
||||
SpillStaticRegs();
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<uint64_t, uint64_t, uint64_t, uint64_t>(x3);
|
||||
#else
|
||||
blr(x3);
|
||||
#endif
|
||||
FillStaticRegs();
|
||||
|
||||
// Result is now in x0
|
||||
@@ -431,11 +459,14 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
|
||||
LDIVHandlerAddress = GetCursorAddress<uint64_t>();
|
||||
|
||||
PushDynamicRegsAndLR();
|
||||
SpillStaticRegs();
|
||||
|
||||
ldr(x3, STATE_PTR(CpuStateFrame, Pointers.AArch64.LDIV));
|
||||
|
||||
SpillStaticRegs();
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<uint64_t, uint64_t, uint64_t, uint64_t>(x3);
|
||||
#else
|
||||
blr(x3);
|
||||
#endif
|
||||
FillStaticRegs();
|
||||
|
||||
// Result is now in x0
|
||||
@@ -450,11 +481,14 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
|
||||
LUREMHandlerAddress = GetCursorAddress<uint64_t>();
|
||||
|
||||
PushDynamicRegsAndLR();
|
||||
SpillStaticRegs();
|
||||
|
||||
ldr(x3, STATE_PTR(CpuStateFrame, Pointers.AArch64.LUREM));
|
||||
|
||||
SpillStaticRegs();
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<uint64_t, uint64_t, uint64_t, uint64_t>(x3);
|
||||
#else
|
||||
blr(x3);
|
||||
#endif
|
||||
FillStaticRegs();
|
||||
|
||||
// Result is now in x0
|
||||
@@ -469,11 +503,14 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
|
||||
LREMHandlerAddress = GetCursorAddress<uint64_t>();
|
||||
|
||||
PushDynamicRegsAndLR();
|
||||
SpillStaticRegs();
|
||||
|
||||
ldr(x3, STATE_PTR(CpuStateFrame, Pointers.AArch64.LREM));
|
||||
|
||||
SpillStaticRegs();
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<uint64_t, uint64_t, uint64_t, uint64_t>(x3);
|
||||
#else
|
||||
blr(x3);
|
||||
#endif
|
||||
FillStaticRegs();
|
||||
|
||||
// Result is now in x0
|
||||
@@ -504,13 +541,27 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef VIXL_SIMULATOR
|
||||
void Arm64Dispatcher::ExecuteDispatch(FEXCore::Core::CpuStateFrame *Frame) {
|
||||
Simulator.WriteXRegister(0, reinterpret_cast<int64_t>(Frame));
|
||||
Simulator.RunFrom(reinterpret_cast<Instruction const*>(DispatchPtr));
|
||||
}
|
||||
|
||||
void Arm64Dispatcher::ExecuteJITCallback(FEXCore::Core::CpuStateFrame *Frame, uint64_t RIP) {
|
||||
Simulator.WriteXRegister(0, reinterpret_cast<int64_t>(Frame));
|
||||
Simulator.WriteXRegister(1, RIP);
|
||||
Simulator.RunFrom(reinterpret_cast<Instruction const*>(CallbackPtr));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Used by GenerateGDBPauseCheck, GenerateInterpreterTrampoline, destination buffer is set before use
|
||||
static thread_local vixl::aarch64::Assembler emit((uint8_t*)&emit, 1);
|
||||
|
||||
size_t Arm64Dispatcher::GenerateGDBPauseCheck(uint8_t *CodeBuffer, uint64_t GuestRIP) {
|
||||
|
||||
*emit.GetBuffer() = vixl::CodeBuffer(CodeBuffer, MaxGDBPauseCheckSize);
|
||||
|
||||
|
||||
vixl::CodeBufferCheckScope scope(&emit, MaxGDBPauseCheckSize, vixl::CodeBufferCheckScope::kDontReserveBufferSpace, vixl::CodeBufferCheckScope::kNoAssert);
|
||||
|
||||
aarch64::Label RunBlock;
|
||||
@@ -546,7 +597,7 @@ size_t Arm64Dispatcher::GenerateGDBPauseCheck(uint8_t *CodeBuffer, uint64_t Gues
|
||||
|
||||
size_t Arm64Dispatcher::GenerateInterpreterTrampoline(uint8_t *CodeBuffer) {
|
||||
LOGMAN_THROW_AA_FMT(!config.StaticRegisterAllocation, "GenerateInterpreterTrampoline dispatcher does not support SRA");
|
||||
|
||||
|
||||
*emit.GetBuffer() = vixl::CodeBuffer(CodeBuffer, MaxInterpreterTrampolineSize);
|
||||
|
||||
vixl::CodeBufferCheckScope scope(&emit, MaxInterpreterTrampolineSize, vixl::CodeBufferCheckScope::kDontReserveBufferSpace, vixl::CodeBufferCheckScope::kNoAssert);
|
||||
@@ -594,7 +645,7 @@ void Arm64Dispatcher::SpillSRA(FEXCore::Core::InternalThreadState *Thread, void
|
||||
}
|
||||
|
||||
void Arm64Dispatcher::InitThreadPointers(FEXCore::Core::InternalThreadState *Thread) {
|
||||
// Setup dispatcher specific pointers that need to be accessed from JIT code
|
||||
// Setup dispatcher specific pointers that need to be accessed from JIT code
|
||||
{
|
||||
auto &Common = Thread->CurrentFrame->Pointers.Common;
|
||||
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
#include "Interface/Core/ArchHelpers/Arm64Emitter.h"
|
||||
#include "Interface/Core/Dispatcher/Dispatcher.h"
|
||||
|
||||
#ifdef VIXL_SIMULATOR
|
||||
#include <aarch64/simulator-aarch64.h>
|
||||
#endif
|
||||
|
||||
namespace FEXCore::Context {
|
||||
struct Context;
|
||||
}
|
||||
@@ -20,6 +24,11 @@ class Arm64Dispatcher final : public Dispatcher, public Arm64Emitter {
|
||||
size_t GenerateGDBPauseCheck(uint8_t *CodeBuffer, uint64_t GuestRIP) override;
|
||||
size_t GenerateInterpreterTrampoline(uint8_t *CodeBuffer) override;
|
||||
|
||||
#ifdef VIXL_SIMULATOR
|
||||
void ExecuteDispatch(FEXCore::Core::CpuStateFrame *Frame) override;
|
||||
void ExecuteJITCallback(FEXCore::Core::CpuStateFrame *Frame, uint64_t RIP) override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void SpillSRA(FEXCore::Core::InternalThreadState *Thread, void *ucontext, uint32_t IgnoreMask) override;
|
||||
|
||||
@@ -29,6 +38,11 @@ class Arm64Dispatcher final : public Dispatcher, public Arm64Emitter {
|
||||
uint64_t LDIVHandlerAddress{};
|
||||
uint64_t LUREMHandlerAddress{};
|
||||
uint64_t LREMHandlerAddress{};
|
||||
|
||||
#ifdef VIXL_SIMULATOR
|
||||
vixl::aarch64::Decoder Decoder;
|
||||
vixl::aarch64::Simulator Simulator;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
@@ -32,7 +32,7 @@ struct DispatcherConfig {
|
||||
class Dispatcher {
|
||||
public:
|
||||
virtual ~Dispatcher() = default;
|
||||
|
||||
|
||||
/**
|
||||
* @name Dispatch Helper functions
|
||||
* @{ */
|
||||
@@ -75,12 +75,12 @@ public:
|
||||
|
||||
static std::unique_ptr<Dispatcher> CreateX86(FEXCore::Context::Context *CTX, const DispatcherConfig &Config);
|
||||
static std::unique_ptr<Dispatcher> CreateArm64(FEXCore::Context::Context *CTX, const DispatcherConfig &Config);
|
||||
|
||||
void ExecuteDispatch(FEXCore::Core::CpuStateFrame *Frame) {
|
||||
|
||||
virtual void ExecuteDispatch(FEXCore::Core::CpuStateFrame *Frame) {
|
||||
DispatchPtr(Frame);
|
||||
}
|
||||
|
||||
void ExecuteJITCallback(FEXCore::Core::CpuStateFrame *Frame, uint64_t RIP) {
|
||||
virtual void ExecuteJITCallback(FEXCore::Core::CpuStateFrame *Frame, uint64_t RIP) {
|
||||
CallbackPtr(Frame, RIP);
|
||||
}
|
||||
|
||||
|
||||
+42
-25
@@ -1,7 +1,7 @@
|
||||
#include "Interface/Core/CPUID.h"
|
||||
#include <FEXCore/Core/HostFeatures.h>
|
||||
|
||||
#ifdef _M_ARM_64
|
||||
#if defined(_M_ARM_64) || defined(VIXL_SIMULATOR)
|
||||
#include "aarch64/assembler-aarch64.h"
|
||||
#include "aarch64/cpu-aarch64.h"
|
||||
#include "aarch64/disasm-aarch64.h"
|
||||
@@ -50,8 +50,12 @@ static uint32_t GetDCZID() {
|
||||
|
||||
|
||||
HostFeatures::HostFeatures() {
|
||||
#ifdef _M_ARM_64
|
||||
#if defined(_M_ARM_64) || defined(VIXL_SIMULATOR)
|
||||
#ifdef VIXL_SIMULATOR
|
||||
auto Features = vixl::CPUFeatures::All();
|
||||
#else
|
||||
auto Features = vixl::CPUFeatures::InferFromOS();
|
||||
#endif
|
||||
SupportsAES = Features.Has(vixl::CPUFeatures::Feature::kAES);
|
||||
SupportsCRC = Features.Has(vixl::CPUFeatures::Feature::kCRC32);
|
||||
SupportsAtomics = Features.Has(vixl::CPUFeatures::Feature::kAtomics);
|
||||
@@ -64,12 +68,22 @@ HostFeatures::HostFeatures() {
|
||||
|
||||
Supports3DNow = true;
|
||||
SupportsSSE4A = true;
|
||||
#ifdef VIXL_SIMULATOR
|
||||
// Hardcode enable SVE with 256-bit wide registers.
|
||||
SupportsAVX = true;
|
||||
#else
|
||||
SupportsAVX = Features.Has(vixl::CPUFeatures::Feature::kSVE2) &&
|
||||
vixl::aarch64::CPU::ReadSVEVectorLengthInBits() >= 256;
|
||||
#endif
|
||||
SupportsSHA = true;
|
||||
SupportsBMI1 = true;
|
||||
SupportsBMI2 = true;
|
||||
|
||||
if (!SupportsAtomics) {
|
||||
WARN_ONCE_FMT("Host CPU doesn't support atomics. Expect bad performance");
|
||||
}
|
||||
|
||||
#ifdef _M_ARM_64
|
||||
// We need to get the CPU's cache line size
|
||||
// We expect sane targets that have correct cacheline sizes across clusters
|
||||
uint64_t CTR;
|
||||
@@ -79,11 +93,28 @@ HostFeatures::HostFeatures() {
|
||||
DCacheLineSize = 4 << ((CTR >> 16) & 0xF);
|
||||
ICacheLineSize = 4 << (CTR & 0xF);
|
||||
|
||||
if (!SupportsAtomics) {
|
||||
WARN_ONCE_FMT("Host CPU doesn't support atomics. Expect bad performance");
|
||||
}
|
||||
// Test if this CPU supports float exception trapping by attempting to enable
|
||||
// On unsupported these bits are architecturally defined as RAZ/WI
|
||||
constexpr uint32_t ExceptionEnableTraps =
|
||||
(1U << 8) | // Invalid Operation float exception trap enable
|
||||
(1U << 9) | // Divide by zero float exception trap enable
|
||||
(1U << 10) | // Overflow float exception trap enable
|
||||
(1U << 11) | // Underflow float exception trap enable
|
||||
(1U << 12) | // Inexact float exception trap enable
|
||||
(1U << 15); // Input Denormal float exception trap enable
|
||||
|
||||
uint32_t OriginalFPCR = GetFPCR();
|
||||
uint32_t FPCR = OriginalFPCR | ExceptionEnableTraps;
|
||||
SetFPCR(FPCR);
|
||||
FPCR = GetFPCR();
|
||||
SupportsFloatExceptions = (FPCR & ExceptionEnableTraps) == ExceptionEnableTraps;
|
||||
|
||||
// Set FPCR back to original just in case anything changed
|
||||
SetFPCR(OriginalFPCR);
|
||||
#endif
|
||||
#ifdef _M_X86_64
|
||||
|
||||
#endif
|
||||
#if defined(_M_X86_64) && !defined(VIXL_SIMULATOR)
|
||||
Xbyak::util::Cpu Features{};
|
||||
SupportsAES = Features.has(Xbyak::util::Cpu::tAESNI);
|
||||
SupportsCRC = Features.has(Xbyak::util::Cpu::tSSE42);
|
||||
@@ -109,27 +140,12 @@ HostFeatures::HostFeatures() {
|
||||
|
||||
SupportsFlushInputsToZero = true;
|
||||
SupportsFloatExceptions = true;
|
||||
#else
|
||||
// Test if this CPU supports float exception trapping by attempting to enable
|
||||
// On unsupported these bits are architecturally defined as RAZ/WI
|
||||
constexpr uint32_t ExceptionEnableTraps =
|
||||
(1U << 8) | // Invalid Operation float exception trap enable
|
||||
(1U << 9) | // Divide by zero float exception trap enable
|
||||
(1U << 10) | // Overflow float exception trap enable
|
||||
(1U << 11) | // Underflow float exception trap enable
|
||||
(1U << 12) | // Inexact float exception trap enable
|
||||
(1U << 15); // Input Denormal float exception trap enable
|
||||
|
||||
uint32_t OriginalFPCR = GetFPCR();
|
||||
uint32_t FPCR = OriginalFPCR | ExceptionEnableTraps;
|
||||
SetFPCR(FPCR);
|
||||
FPCR = GetFPCR();
|
||||
SupportsFloatExceptions = (FPCR & ExceptionEnableTraps) == ExceptionEnableTraps;
|
||||
|
||||
// Set FPCR back to original just in case anything changed
|
||||
SetFPCR(OriginalFPCR);
|
||||
#endif
|
||||
|
||||
#ifdef VIXL_SIMULATOR
|
||||
// simulator doesn't support dc(ZVA)
|
||||
SupportsCLZERO = false;
|
||||
#else
|
||||
// Check if we can support cacheline clears
|
||||
uint32_t DCZID = GetDCZID();
|
||||
if ((DCZID & DCZID_DZP_MASK) == 0) {
|
||||
@@ -139,5 +155,6 @@ HostFeatures::HostFeatures() {
|
||||
// This means we can use the instruction
|
||||
SupportsCLZERO = DCZID_Bytes == CPUIDEmu::CACHELINE_SIZE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -181,13 +181,10 @@ constexpr OpHandlerArray InterpreterOpHandlers = [] {
|
||||
// Move ops
|
||||
REGISTER_OP(EXTRACTELEMENTPAIR, ExtractElementPair);
|
||||
REGISTER_OP(CREATEELEMENTPAIR, CreateElementPair);
|
||||
REGISTER_OP(MOV, Mov);
|
||||
|
||||
// Vector ops
|
||||
REGISTER_OP(VECTORZERO, VectorZero);
|
||||
REGISTER_OP(VECTORIMM, VectorImm);
|
||||
REGISTER_OP(SPLATVECTOR2, SplatVector);
|
||||
REGISTER_OP(SPLATVECTOR4, SplatVector);
|
||||
REGISTER_OP(VMOV, VMov);
|
||||
REGISTER_OP(VAND, VAnd);
|
||||
REGISTER_OP(VBIC, VBic);
|
||||
@@ -246,8 +243,6 @@ constexpr OpHandlerArray InterpreterOpHandlers = [] {
|
||||
REGISTER_OP(VUSHRS, VUShrS);
|
||||
REGISTER_OP(VSSHRS, VSShrS);
|
||||
REGISTER_OP(VINSELEMENT, VInsElement);
|
||||
REGISTER_OP(VINSSCALARELEMENT, VInsScalarElement);
|
||||
REGISTER_OP(VEXTRACTELEMENT, VExtractElement);
|
||||
REGISTER_OP(VDUPELEMENT, VDupElement);
|
||||
REGISTER_OP(VEXTR, VExtr);
|
||||
REGISTER_OP(VSLI, VSLI);
|
||||
@@ -257,7 +252,6 @@ constexpr OpHandlerArray InterpreterOpHandlers = [] {
|
||||
REGISTER_OP(VSHLI, VShlI);
|
||||
REGISTER_OP(VUSHRNI, VUShrNI);
|
||||
REGISTER_OP(VUSHRNI2, VUShrNI2);
|
||||
REGISTER_OP(VBITCAST, VBitcast);
|
||||
REGISTER_OP(VSXTL, VSXTL);
|
||||
REGISTER_OP(VSXTL2, VSXTL2);
|
||||
REGISTER_OP(VUXTL, VUXTL);
|
||||
|
||||
@@ -207,7 +207,6 @@ namespace FEXCore::CPU {
|
||||
///< Vector ops
|
||||
DEF_OP(VectorZero);
|
||||
DEF_OP(VectorImm);
|
||||
DEF_OP(SplatVector);
|
||||
DEF_OP(VMov);
|
||||
DEF_OP(VAnd);
|
||||
DEF_OP(VBic);
|
||||
@@ -264,8 +263,6 @@ namespace FEXCore::CPU {
|
||||
DEF_OP(VUShrS);
|
||||
DEF_OP(VSShrS);
|
||||
DEF_OP(VInsElement);
|
||||
DEF_OP(VInsScalarElement);
|
||||
DEF_OP(VExtractElement);
|
||||
DEF_OP(VDupElement);
|
||||
DEF_OP(VExtr);
|
||||
DEF_OP(VSLI);
|
||||
@@ -275,7 +272,6 @@ namespace FEXCore::CPU {
|
||||
DEF_OP(VShlI);
|
||||
DEF_OP(VUShrNI);
|
||||
DEF_OP(VUShrNI2);
|
||||
DEF_OP(VBitcast);
|
||||
DEF_OP(VSXTL);
|
||||
DEF_OP(VSXTL2);
|
||||
DEF_OP(VUXTL);
|
||||
|
||||
@@ -30,13 +30,6 @@ DEF_OP(CreateElementPair) {
|
||||
memcpy(Dst + IROp->ElementSize, Src_Upper, IROp->ElementSize);
|
||||
}
|
||||
|
||||
DEF_OP(Mov) {
|
||||
auto Op = IROp->C<IR::IROp_Mov>();
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
memcpy(GDP, GetSrc<void*>(Data->SSAData, Op->Value), OpSize);
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
|
||||
} // namespace FEXCore::CPU
|
||||
+591
-438
File diff suppressed because it is too large.
Load diff
@@ -40,7 +40,7 @@ DEF_OP(CallbackReturn) {
|
||||
ResetStack();
|
||||
|
||||
// We can now lower the ref counter again
|
||||
|
||||
|
||||
ldr(w2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, SignalHandlerRefCounter)));
|
||||
sub(w2, w2, 1);
|
||||
str(w2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, SignalHandlerRefCounter)));
|
||||
@@ -197,7 +197,11 @@ DEF_OP(Syscall) {
|
||||
ldr(x3, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.SyscallHandlerFunc)));
|
||||
mov(x1, STATE);
|
||||
mov(x2, sp);
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<uint64_t, void*, void*, void*>(x3);
|
||||
#else
|
||||
blr(x3);
|
||||
#endif
|
||||
|
||||
add(sp, sp, SPOffset);
|
||||
|
||||
@@ -381,7 +385,11 @@ DEF_OP(Thunk) {
|
||||
|
||||
auto thunkFn = ThreadState->CTX->ThunkHandler->LookupThunk(Op->ThunkNameHash);
|
||||
LoadConstant(x2, (uintptr_t)thunkFn);
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<void, void*, void*>(x2);
|
||||
#else
|
||||
blr(x2);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -448,7 +456,11 @@ DEF_OP(ThreadRemoveCodeEntry) {
|
||||
|
||||
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.ThreadRemoveCodeEntryFromJIT)));
|
||||
SpillStaticRegs();
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<void, void*, void*>(x2);
|
||||
#else
|
||||
blr(x2);
|
||||
#endif
|
||||
FillStaticRegs();
|
||||
|
||||
// Fix the stack and any values that were stepped on
|
||||
@@ -459,6 +471,7 @@ DEF_OP(CPUID) {
|
||||
auto Op = IROp->C<IR::IROp_CPUID>();
|
||||
|
||||
PushDynamicRegsAndLR();
|
||||
SpillStaticRegs();
|
||||
|
||||
// x0 = CPUID Handler
|
||||
// x1 = CPUID Function
|
||||
@@ -467,10 +480,13 @@ DEF_OP(CPUID) {
|
||||
ldr(x3, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.CPUIDFunction)));
|
||||
mov(x1, GetReg<RA_64>(Op->Function.ID()));
|
||||
mov(x2, GetReg<RA_64>(Op->Leaf.ID()));
|
||||
SpillStaticRegs();
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<__uint128_t, void*, uint64_t, uint64_t>(x3);
|
||||
#else
|
||||
blr(x3);
|
||||
FillStaticRegs();
|
||||
#endif
|
||||
|
||||
FillStaticRegs();
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
// Results are in x0, x1
|
||||
|
||||
+74
-11
@@ -94,7 +94,12 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
|
||||
uxth(w0, GetReg<RA_32>(IROp->Args[0].ID()));
|
||||
ldr(x1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<void, uint16_t>(x1);
|
||||
#else
|
||||
blr(x1);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -109,7 +114,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
|
||||
fmov(v0.S(), GetSrc(IROp->Args[0].ID()).S()) ;
|
||||
ldr(x0, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<__uint128_t, float>(x0);
|
||||
#else
|
||||
blr(x0);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -128,7 +137,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
|
||||
mov(v0.D(), GetSrc(IROp->Args[0].ID()).D());
|
||||
ldr(x0, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<__uint128_t, double>(x0);
|
||||
#else
|
||||
blr(x0);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -153,7 +166,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
mov(w0, GetReg<RA_32>(IROp->Args[0].ID()));
|
||||
}
|
||||
ldr(x1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<__uint128_t, uint32_t>(x1);
|
||||
#else
|
||||
blr(x1);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -174,7 +191,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
umov(w1, GetSrc(IROp->Args[0].ID()).V8H(), 4);
|
||||
|
||||
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<float, uint64_t, uint64_t>(x2);
|
||||
#else
|
||||
blr(x2);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -193,7 +214,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
umov(w1, GetSrc(IROp->Args[0].ID()).V8H(), 4);
|
||||
|
||||
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<double, uint64_t, uint64_t>(x2);
|
||||
#else
|
||||
blr(x2);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -210,7 +235,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
|
||||
mov(v0.D(), GetSrc(IROp->Args[0].ID()).D());
|
||||
ldr(x0, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<double, double>(x0);
|
||||
#else
|
||||
blr(x0);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -229,7 +258,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
mov(v0.D(), GetSrc(IROp->Args[0].ID()).D());
|
||||
mov(v1.D(), GetSrc(IROp->Args[1].ID()).D());
|
||||
ldr(x0, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<double, double, double>(x0);
|
||||
#else
|
||||
blr(x0);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -249,7 +282,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
umov(w1, GetSrc(IROp->Args[0].ID()).V8H(), 4);
|
||||
|
||||
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<uint32_t, uint64_t, uint64_t>(x2);
|
||||
#else
|
||||
blr(x2);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -267,7 +304,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
umov(w1, GetSrc(IROp->Args[0].ID()).V8H(), 4);
|
||||
|
||||
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<uint32_t, uint64_t, uint64_t>(x2);
|
||||
#else
|
||||
blr(x2);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -285,7 +326,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
umov(w1, GetSrc(IROp->Args[0].ID()).V8H(), 4);
|
||||
|
||||
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<uint64_t, uint64_t, uint64_t>(x2);
|
||||
#else
|
||||
blr(x2);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -306,8 +351,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
umov(w3, GetSrc(IROp->Args[1].ID()).V8H(), 4);
|
||||
|
||||
ldr(x4, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<uint64_t, uint64_t, uint64_t, uint64_t, uint64_t>(x4);
|
||||
#else
|
||||
blr(x4);
|
||||
|
||||
#endif
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
FillStaticRegs();
|
||||
@@ -324,7 +372,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
umov(w1, GetSrc(IROp->Args[0].ID()).V8H(), 4);
|
||||
|
||||
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<__uint128_t, uint64_t, uint64_t>(x2);
|
||||
#else
|
||||
blr(x2);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -347,7 +399,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
umov(w3, GetSrc(IROp->Args[1].ID()).V8H(), 4);
|
||||
|
||||
ldr(x4, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<__uint128_t, uint64_t, uint64_t, uint64_t, uint64_t>(x4);
|
||||
#else
|
||||
blr(x4);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -473,7 +529,7 @@ Arm64JITCore::Arm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::Intern
|
||||
|
||||
// Common
|
||||
auto &Common = ThreadState->CurrentFrame->Pointers.Common;
|
||||
|
||||
|
||||
Common.PrintValue = reinterpret_cast<uint64_t>(PrintValue);
|
||||
Common.PrintVectorValue = reinterpret_cast<uint64_t>(PrintVectorValue);
|
||||
Common.ThreadRemoveCodeEntryFromJIT = reinterpret_cast<uintptr_t>(&Context::Context::ThreadRemoveCodeEntryFromJit);
|
||||
@@ -494,7 +550,7 @@ Arm64JITCore::Arm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::Intern
|
||||
|
||||
// Platform Specific
|
||||
auto &AArch64 = ThreadState->CurrentFrame->Pointers.AArch64;
|
||||
|
||||
|
||||
AArch64.LUDIV = reinterpret_cast<uint64_t>(LUDIV);
|
||||
AArch64.LDIV = reinterpret_cast<uint64_t>(LDIV);
|
||||
AArch64.LUREM = reinterpret_cast<uint64_t>(LUREM);
|
||||
@@ -511,6 +567,7 @@ void Arm64JITCore::InitializeSignalHandlers(FEXCore::Context::Context *CTX) {
|
||||
return Thread->CTX->Dispatcher->HandleSIGILL(Thread, Signal, info, ucontext);
|
||||
}, true);
|
||||
|
||||
#ifdef _M_ARM_64
|
||||
CTX->SignalDelegation->RegisterHostSignalHandler(SIGBUS, [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
|
||||
if (!Thread->CPUBackend->IsAddressInCodeBuffer(ArchHelpers::Context::GetPc(ucontext))) {
|
||||
// Wasn't a sigbus in JIT code
|
||||
@@ -519,6 +576,7 @@ void Arm64JITCore::InitializeSignalHandlers(FEXCore::Context::Context *CTX) {
|
||||
|
||||
return FEXCore::ArchHelpers::Arm64::HandleSIGBUS(Thread->CTX->Config.ParanoidTSO(), Signal, info, ucontext);
|
||||
}, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Arm64JITCore::EmitDetectionString() {
|
||||
@@ -530,7 +588,7 @@ void Arm64JITCore::EmitDetectionString() {
|
||||
|
||||
void Arm64JITCore::ClearCache() {
|
||||
// Get the backing code buffer
|
||||
|
||||
|
||||
auto CodeBuffer = GetEmptyCodeBuffer();
|
||||
*GetBuffer() = vixl::CodeBuffer(CodeBuffer->Ptr, CodeBuffer->Size);
|
||||
EmitDetectionString();
|
||||
@@ -725,10 +783,12 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
|
||||
SpillSlots = RAData->SpillSlots();
|
||||
|
||||
if (SpillSlots) {
|
||||
if (IsImmAddSub(SpillSlots * 16)) {
|
||||
sub(sp, sp, SpillSlots * 16);
|
||||
const auto TotalSpillSlotsSize = SpillSlots * MaxSpillSlotSize;
|
||||
|
||||
if (IsImmAddSub(TotalSpillSlotsSize)) {
|
||||
sub(sp, sp, TotalSpillSlotsSize);
|
||||
} else {
|
||||
LoadConstant(x0, SpillSlots * 16);
|
||||
LoadConstant(x0, TotalSpillSlotsSize);
|
||||
sub(sp, sp, x0);
|
||||
}
|
||||
}
|
||||
@@ -796,14 +856,17 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
|
||||
}
|
||||
|
||||
void Arm64JITCore::ResetStack() {
|
||||
if (SpillSlots == 0)
|
||||
if (SpillSlots == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsImmAddSub(SpillSlots * 16)) {
|
||||
add(sp, sp, SpillSlots * 16);
|
||||
const auto TotalSpillSlotsSize = SpillSlots * MaxSpillSlotSize;
|
||||
|
||||
if (IsImmAddSub(TotalSpillSlotsSize)) {
|
||||
add(sp, sp, TotalSpillSlotsSize);
|
||||
} else {
|
||||
// Too big to fit in a 12bit immediate
|
||||
LoadConstant(x0, SpillSlots * 16);
|
||||
LoadConstant(x0, TotalSpillSlotsSize);
|
||||
add(sp, sp, x0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,16 +23,6 @@ $end_info$
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#define STATE x28
|
||||
#define TMP1 x0
|
||||
#define TMP2 x1
|
||||
#define TMP3 x2
|
||||
#define TMP4 x3
|
||||
|
||||
#define VTMP1 v1
|
||||
#define VTMP2 v2
|
||||
#define VTMP3 v3
|
||||
|
||||
namespace FEXCore::Core {
|
||||
struct InternalThreadState;
|
||||
}
|
||||
@@ -365,13 +355,10 @@ private:
|
||||
///< Move ops
|
||||
DEF_OP(ExtractElementPair);
|
||||
DEF_OP(CreateElementPair);
|
||||
DEF_OP(Mov);
|
||||
|
||||
///< Vector ops
|
||||
DEF_OP(VectorZero);
|
||||
DEF_OP(VectorImm);
|
||||
DEF_OP(SplatVector2);
|
||||
DEF_OP(SplatVector4);
|
||||
DEF_OP(VMov);
|
||||
DEF_OP(VAnd);
|
||||
DEF_OP(VBic);
|
||||
@@ -430,8 +417,6 @@ private:
|
||||
DEF_OP(VUShrS);
|
||||
DEF_OP(VSShrS);
|
||||
DEF_OP(VInsElement);
|
||||
DEF_OP(VInsScalarElement);
|
||||
DEF_OP(VExtractElement);
|
||||
DEF_OP(VDupElement);
|
||||
DEF_OP(VExtr);
|
||||
DEF_OP(VSLI);
|
||||
@@ -441,7 +426,6 @@ private:
|
||||
DEF_OP(VShlI);
|
||||
DEF_OP(VUShrNI);
|
||||
DEF_OP(VUShrNI2);
|
||||
DEF_OP(VBitcast);
|
||||
DEF_OP(VSXTL);
|
||||
DEF_OP(VSXTL2);
|
||||
DEF_OP(VUXTL);
|
||||
|
||||
+41
-15
@@ -446,9 +446,9 @@ DEF_OP(StoreContextIndexed) {
|
||||
}
|
||||
|
||||
DEF_OP(SpillRegister) {
|
||||
auto Op = IROp->C<IR::IROp_SpillRegister>();
|
||||
const auto Op = IROp->C<IR::IROp_SpillRegister>();
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
const uint32_t SlotOffset = Op->Slot * 16;
|
||||
const uint32_t SlotOffset = Op->Slot * MaxSpillSlotSize;
|
||||
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
switch (OpSize) {
|
||||
@@ -468,23 +468,36 @@ DEF_OP(SpillRegister) {
|
||||
str(GetReg<RA_64>(Op->Value.ID()), MemOperand(sp, SlotOffset));
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize);
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize);
|
||||
break;
|
||||
}
|
||||
} else if (Op->Class == FEXCore::IR::FPRClass) {
|
||||
const auto Src = GetSrc(Op->Value.ID());
|
||||
|
||||
switch (OpSize) {
|
||||
case 4: {
|
||||
str(GetSrc(Op->Value.ID()).S(), MemOperand(sp, SlotOffset));
|
||||
str(Src.S(), MemOperand(sp, SlotOffset));
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
str(GetSrc(Op->Value.ID()).D(), MemOperand(sp, SlotOffset));
|
||||
str(Src.D(), MemOperand(sp, SlotOffset));
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
str(GetSrc(Op->Value.ID()), MemOperand(sp, SlotOffset));
|
||||
str(Src, MemOperand(sp, SlotOffset));
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize);
|
||||
case 32: {
|
||||
// TODO: Eliminate ptrue with statically allocated predicate register.
|
||||
ptrue(p7.VnB(), SVE_VL32);
|
||||
mov(TMP3, SlotOffset);
|
||||
st1b(Src.Z().VnB(), p7, SVEMemOperand(sp, TMP3));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LOGMAN_MSG_A_FMT("Unhandled SpillRegister class: {}", Op->Class.Val);
|
||||
@@ -492,9 +505,9 @@ DEF_OP(SpillRegister) {
|
||||
}
|
||||
|
||||
DEF_OP(FillRegister) {
|
||||
auto Op = IROp->C<IR::IROp_FillRegister>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
uint32_t SlotOffset = Op->Slot * 16;
|
||||
const auto Op = IROp->C<IR::IROp_FillRegister>();
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
const uint32_t SlotOffset = Op->Slot * MaxSpillSlotSize;
|
||||
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
switch (OpSize) {
|
||||
@@ -514,23 +527,36 @@ DEF_OP(FillRegister) {
|
||||
ldr(GetReg<RA_64>(Node), MemOperand(sp, SlotOffset));
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize);
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize);
|
||||
break;
|
||||
}
|
||||
} else if (Op->Class == FEXCore::IR::FPRClass) {
|
||||
const auto Dst = GetDst(Node);
|
||||
|
||||
switch (OpSize) {
|
||||
case 4: {
|
||||
ldr(GetDst(Node).S(), MemOperand(sp, SlotOffset));
|
||||
ldr(Dst.S(), MemOperand(sp, SlotOffset));
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
ldr(GetDst(Node).D(), MemOperand(sp, SlotOffset));
|
||||
ldr(Dst.D(), MemOperand(sp, SlotOffset));
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
ldr(GetDst(Node), MemOperand(sp, SlotOffset));
|
||||
ldr(Dst, MemOperand(sp, SlotOffset));
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize);
|
||||
case 32: {
|
||||
// TODO: Eliminate ptrue with statically allocated predicate register.
|
||||
ptrue(p7.VnB(), SVE_VL32);
|
||||
mov(TMP3, SlotOffset);
|
||||
ld1b(Dst.Z().VnB(), p7.Zeroing(), SVEMemOperand(sp, TMP3));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LOGMAN_MSG_A_FMT("Unhandled FillRegister class: {}", Op->Class.Val);
|
||||
|
||||
@@ -119,8 +119,11 @@ DEF_OP(SetRoundingMode) {
|
||||
|
||||
mrs(TMP1, FPCR);
|
||||
|
||||
// vixl simulator doesn't support anything beyond ties-to-even rounding
|
||||
#ifndef VIXL_SIMULATOR
|
||||
// Insert the rounding flags
|
||||
bfi(TMP1, TMP2, 22, 2);
|
||||
#endif
|
||||
|
||||
// Insert the FTZ flag
|
||||
lsr(TMP2, Src, 2);
|
||||
@@ -134,6 +137,7 @@ DEF_OP(Print) {
|
||||
auto Op = IROp->C<IR::IROp_Print>();
|
||||
|
||||
PushDynamicRegsAndLR();
|
||||
SpillStaticRegs();
|
||||
|
||||
if (IsGPR(Op->Value.ID())) {
|
||||
mov(x0, GetReg<RA_64>(Op->Value.ID()));
|
||||
@@ -145,10 +149,10 @@ DEF_OP(Print) {
|
||||
fmov(x1, GetSrc(Op->Value.ID()).V1D(), 1);
|
||||
ldr(x3, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.PrintVectorValue)));
|
||||
}
|
||||
SpillStaticRegs();
|
||||
blr(x3);
|
||||
FillStaticRegs();
|
||||
|
||||
blr(x3);
|
||||
|
||||
FillStaticRegs();
|
||||
PopDynamicRegsAndLR();
|
||||
}
|
||||
|
||||
|
||||
@@ -68,17 +68,11 @@ DEF_OP(CreateElementPair) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Mov) {
|
||||
auto Op = IROp->C<IR::IROp_Mov>();
|
||||
mov(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Value.ID()));
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void Arm64JITCore::RegisterMoveHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
|
||||
REGISTER_OP(EXTRACTELEMENTPAIR, ExtractElementPair);
|
||||
REGISTER_OP(CREATEELEMENTPAIR, CreateElementPair);
|
||||
REGISTER_OP(MOV, Mov);
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
}
|
||||
|
||||
+3038
-1094
File diff suppressed because it is too large.
Load diff
@@ -33,7 +33,7 @@ namespace FEXCore::CPU {
|
||||
DEF_OP(SignalReturn) {
|
||||
// Adjust the stack first for a regular return
|
||||
if (SpillSlots) {
|
||||
add(rsp, SpillSlots * 16); // + 8 to consume return address
|
||||
add(rsp, SpillSlots * MaxSpillSlotSize); // + 8 to consume return address
|
||||
}
|
||||
|
||||
jmp(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.SignalReturnHandler)]);
|
||||
@@ -42,7 +42,7 @@ DEF_OP(SignalReturn) {
|
||||
DEF_OP(CallbackReturn) {
|
||||
// Adjust the stack first for a regular return
|
||||
if (SpillSlots) {
|
||||
add(rsp, SpillSlots * 16); // + 8 to consume return address
|
||||
add(rsp, SpillSlots * MaxSpillSlotSize); // + 8 to consume return address
|
||||
}
|
||||
|
||||
// Make sure to adjust the refcounter so we don't clear the cache now
|
||||
@@ -71,7 +71,7 @@ DEF_OP(ExitFunction) {
|
||||
|
||||
|
||||
if (SpillSlots) {
|
||||
add(rsp, SpillSlots * 16);
|
||||
add(rsp, SpillSlots * MaxSpillSlotSize);
|
||||
}
|
||||
|
||||
uint64_t NewRIP;
|
||||
|
||||
+26
-16
@@ -60,32 +60,42 @@ static void PrintVectorValue(uint64_t Value, uint64_t ValueUpper) {
|
||||
namespace FEXCore::CPU {
|
||||
|
||||
void X86JITCore::PushRegs() {
|
||||
sub(rsp, 16 * RAXMM_x.size());
|
||||
const auto AVXRegSize = Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
sub(rsp, AVXRegSize * RAXMM_x.size());
|
||||
for (size_t i = 0; i < RAXMM_x.size(); ++i) {
|
||||
movaps(ptr[rsp + i * 16], RAXMM_x[i]);
|
||||
vmovups(ptr[rsp + i * AVXRegSize], ToYMM(RAXMM_x[i]));
|
||||
}
|
||||
|
||||
for (auto &Reg : RA64)
|
||||
for (const auto &Reg : RA64) {
|
||||
push(Reg);
|
||||
}
|
||||
|
||||
auto NumPush = RA64.size();
|
||||
if (NumPush & 1)
|
||||
sub(rsp, 8); // Align
|
||||
const auto NumPush = RA64.size();
|
||||
if ((NumPush & 1) != 0) {
|
||||
// Align
|
||||
sub(rsp, 8);
|
||||
}
|
||||
}
|
||||
|
||||
void X86JITCore::PopRegs() {
|
||||
auto NumPush = RA64.size();
|
||||
const auto AVXRegSize = Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
const auto NumPush = RA64.size();
|
||||
|
||||
if (NumPush & 1)
|
||||
add(rsp, 8); // Align
|
||||
for (uint32_t i = RA64.size(); i > 0; --i)
|
||||
pop(RA64[i - 1]);
|
||||
|
||||
for (size_t i = 0; i < RAXMM_x.size(); ++i) {
|
||||
movaps(RAXMM_x[i], ptr[rsp + i * 16]);
|
||||
if ((NumPush & 1) != 0) {
|
||||
// Align
|
||||
add(rsp, 8);
|
||||
}
|
||||
|
||||
add(rsp, 16 * RAXMM_x.size());
|
||||
for (uint32_t i = RA64.size(); i > 0; --i) {
|
||||
pop(RA64[i - 1]);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < RAXMM_x.size(); ++i) {
|
||||
vmovups(ToYMM(RAXMM_x[i]), ptr[rsp + i * AVXRegSize]);
|
||||
}
|
||||
|
||||
add(rsp, AVXRegSize * RAXMM_x.size());
|
||||
}
|
||||
|
||||
void X86JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
@@ -599,7 +609,7 @@ void *X86JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRLi
|
||||
SpillSlots = RAData->SpillSlots();
|
||||
|
||||
if (SpillSlots) {
|
||||
sub(rsp, SpillSlots * 16);
|
||||
sub(rsp, SpillSlots * MaxSpillSlotSize);
|
||||
}
|
||||
|
||||
#ifdef BLOCKSTATS
|
||||
|
||||
@@ -209,7 +209,7 @@ private:
|
||||
* @brief Current guest RIP entrypoint
|
||||
*/
|
||||
uint8_t *GuestEntry{};
|
||||
|
||||
|
||||
using SetCC = void (X86JITCore::*)(const Operand& op);
|
||||
using CMovCC = void (X86JITCore::*)(const Reg& reg, const Operand& op);
|
||||
using JCC = void (X86JITCore::*)(const Label& label, LabelType type);
|
||||
@@ -366,12 +366,10 @@ private:
|
||||
///< Move ops
|
||||
DEF_OP(ExtractElementPair);
|
||||
DEF_OP(CreateElementPair);
|
||||
DEF_OP(Mov);
|
||||
|
||||
///< Vector ops
|
||||
DEF_OP(VectorZero);
|
||||
DEF_OP(VectorImm);
|
||||
DEF_OP(SplatVector);
|
||||
DEF_OP(VMov);
|
||||
DEF_OP(VAnd);
|
||||
DEF_OP(VBic);
|
||||
@@ -430,8 +428,6 @@ private:
|
||||
DEF_OP(VUShrS);
|
||||
DEF_OP(VSShrS);
|
||||
DEF_OP(VInsElement);
|
||||
DEF_OP(VInsScalarElement);
|
||||
DEF_OP(VExtractElement);
|
||||
DEF_OP(VDupElement);
|
||||
DEF_OP(VExtr);
|
||||
DEF_OP(VSLI);
|
||||
@@ -441,7 +437,6 @@ private:
|
||||
DEF_OP(VShlI);
|
||||
DEF_OP(VUShrNI);
|
||||
DEF_OP(VUShrNI2);
|
||||
DEF_OP(VBitcast);
|
||||
DEF_OP(VSXTL);
|
||||
DEF_OP(VSXTL2);
|
||||
DEF_OP(VUXTL);
|
||||
|
||||
+36
-18
@@ -333,10 +333,10 @@ DEF_OP(StoreContextIndexed) {
|
||||
}
|
||||
|
||||
DEF_OP(SpillRegister) {
|
||||
auto Op = IROp->C<IR::IROp_SpillRegister>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const auto Op = IROp->C<IR::IROp_SpillRegister>();
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
const uint32_t SlotOffset = Op->Slot * MaxSpillSlotSize;
|
||||
|
||||
uint32_t SlotOffset = Op->Slot * 16;
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
switch (OpSize) {
|
||||
case 1: {
|
||||
@@ -355,36 +355,44 @@ DEF_OP(SpillRegister) {
|
||||
mov(qword [rsp + SlotOffset], GetSrc<RA_64>(Op->Value.ID()));
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize);
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize);
|
||||
break;
|
||||
}
|
||||
} else if (Op->Class == FEXCore::IR::FPRClass) {
|
||||
const auto Src = GetSrc(Op->Value.ID());
|
||||
|
||||
switch (OpSize) {
|
||||
case 4: {
|
||||
movss(dword [rsp + SlotOffset], GetSrc(Op->Value.ID()));
|
||||
movss(dword [rsp + SlotOffset], Src);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
movsd(qword [rsp + SlotOffset], GetSrc(Op->Value.ID()));
|
||||
movsd(qword [rsp + SlotOffset], Src);
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
movaps(xword [rsp + SlotOffset], GetSrc(Op->Value.ID()));
|
||||
movaps(xword [rsp + SlotOffset], Src);
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize);
|
||||
case 32: {
|
||||
vmovaps(yword [rsp + SlotOffset], ToYMM(Src));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LOGMAN_MSG_A_FMT("Unhandled SpillRegister class: {}", Op->Class.Val);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
DEF_OP(FillRegister) {
|
||||
auto Op = IROp->C<IR::IROp_FillRegister>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const auto Op = IROp->C<IR::IROp_FillRegister>();
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
const uint32_t SlotOffset = Op->Slot * MaxSpillSlotSize;
|
||||
|
||||
uint32_t SlotOffset = Op->Slot * 16;
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
switch (OpSize) {
|
||||
case 1: {
|
||||
@@ -403,23 +411,33 @@ DEF_OP(FillRegister) {
|
||||
mov(GetDst<RA_64>(Node), qword [rsp + SlotOffset]);
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize);
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize);
|
||||
break;
|
||||
}
|
||||
} else if (Op->Class == FEXCore::IR::FPRClass) {
|
||||
const auto Dst = GetDst(Node);
|
||||
|
||||
switch (OpSize) {
|
||||
case 4: {
|
||||
movss(GetDst(Node), dword [rsp + SlotOffset]);
|
||||
movss(Dst, dword [rsp + SlotOffset]);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
movsd(GetDst(Node), qword [rsp + SlotOffset]);
|
||||
movsd(Dst, qword [rsp + SlotOffset]);
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
movaps(GetDst(Node), xword [rsp + SlotOffset]);
|
||||
movaps(Dst, xword [rsp + SlotOffset]);
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize);
|
||||
case 32: {
|
||||
vmovaps(ToYMM(Dst), yword [rsp + SlotOffset]);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LOGMAN_MSG_A_FMT("Unhandled FillRegister class: {}", Op->Class.Val);
|
||||
|
||||
@@ -47,7 +47,7 @@ DEF_OP(Break) {
|
||||
auto Op = IROp->C<IR::IROp_Break>();
|
||||
|
||||
if (SpillSlots) {
|
||||
add(rsp, SpillSlots * 16);
|
||||
add(rsp, SpillSlots * MaxSpillSlotSize);
|
||||
}
|
||||
|
||||
mov(byte [STATE + offsetof(FEXCore::Core::CpuStateFrame, SynchronousFaultData.FaultToTopAndGeneratedException)], 1);
|
||||
|
||||
@@ -73,17 +73,11 @@ DEF_OP(CreateElementPair) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Mov) {
|
||||
auto Op = IROp->C<IR::IROp_Mov>();
|
||||
mov (GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Value.ID()));
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void X86JITCore::RegisterMoveHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
|
||||
REGISTER_OP(EXTRACTELEMENTPAIR, ExtractElementPair);
|
||||
REGISTER_OP(CREATEELEMENTPAIR, CreateElementPair);
|
||||
REGISTER_OP(MOV, Mov);
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
}
|
||||
|
||||
+1100
-633
File diff suppressed because it is too large.
Load diff
+14
-4
@@ -126,10 +126,20 @@ void OpDispatchBuilder::ThunkOp(OpcodeArgs) {
|
||||
const uint8_t GPRSize = CTX->GetGPRSize();
|
||||
uint8_t *sha256 = (uint8_t *)(Op->PC + 2);
|
||||
|
||||
_Thunk(
|
||||
_LoadContext(GPRSize, GPRClass, GPROffset(X86State::REG_RDI)),
|
||||
*reinterpret_cast<SHA256Sum*>(sha256)
|
||||
);
|
||||
if (CTX->Config.Is64BitMode) {
|
||||
// x86-64 ABI puts the function argument in RDI
|
||||
_Thunk(
|
||||
_LoadContext(GPRSize, GPRClass, GPROffset(X86State::REG_RDI)),
|
||||
*reinterpret_cast<SHA256Sum*>(sha256)
|
||||
);
|
||||
}
|
||||
else {
|
||||
// x86 fastcall ABI puts the function argument in ECX
|
||||
_Thunk(
|
||||
_LoadContext(GPRSize, GPRClass, GPROffset(X86State::REG_RCX)),
|
||||
*reinterpret_cast<SHA256Sum*>(sha256)
|
||||
);
|
||||
}
|
||||
|
||||
auto Constant = _Constant(GPRSize);
|
||||
auto OldSP = _LoadContext(GPRSize, GPRClass, RSPOffset);
|
||||
|
||||
@@ -74,13 +74,12 @@ void OpDispatchBuilder::MOVLPOp(OpcodeArgs) {
|
||||
// xmm, xmm is movhlps special case
|
||||
if (Op->Src[0].IsGPR()) {
|
||||
OrderedNode *Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags, 8, 16);
|
||||
Src = _VExtractElement(16, 8, Src, 1);
|
||||
auto Result = _VInsScalarElement(16, 8, 0, Dest, Src);
|
||||
auto Result = _VInsElement(16, 8, 0, 1, Dest, Src);
|
||||
StoreResult_WithOpSize(FPRClass, Op, Op->Dest, Result, 16, 16);
|
||||
}
|
||||
else {
|
||||
OrderedNode *Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags, 8, 16);
|
||||
auto Result = _VInsScalarElement(16, 8, 0, Dest, Src);
|
||||
auto Result = _VInsElement(16, 8, 0, 0, Dest, Src);
|
||||
StoreResult_WithOpSize(FPRClass, Op, Op->Dest, Result, 8, 16);
|
||||
}
|
||||
}
|
||||
@@ -112,7 +111,7 @@ void OpDispatchBuilder::MOVSSOp(OpcodeArgs) {
|
||||
// MOVSS xmm1, xmm2
|
||||
OrderedNode *Dest = LoadSource_WithOpSize(FPRClass, Op, Op->Dest, 16, Op->Flags, -1);
|
||||
OrderedNode *Src = LoadSource_WithOpSize(FPRClass, Op, Op->Src[0], 4, Op->Flags, -1);
|
||||
auto Result = _VInsScalarElement(16, 4, 0, Dest, Src);
|
||||
auto Result = _VInsElement(16, 4, 0, 0, Dest, Src);
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
}
|
||||
else if (Op->Dest.IsGPR()) {
|
||||
@@ -133,7 +132,7 @@ void OpDispatchBuilder::MOVSDOp(OpcodeArgs) {
|
||||
// xmm1[63:0] <- xmm2[63:0]
|
||||
OrderedNode *Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags, -1);
|
||||
OrderedNode *Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
auto Result = _VInsScalarElement(16, 8, 0, Dest, Src);
|
||||
auto Result = _VInsElement(16, 8, 0, 0, Dest, Src);
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
}
|
||||
else if (Op->Dest.IsGPR()) {
|
||||
@@ -335,7 +334,7 @@ void OpDispatchBuilder::VectorScalarALUOp(OpcodeArgs) {
|
||||
|
||||
if (Size != ElementSize) {
|
||||
// Insert the lower bits
|
||||
Result = _VInsScalarElement(Size, ElementSize, 0, Dest, Result);
|
||||
Result = _VInsElement(Size, ElementSize, 0, 0, Dest, Result);
|
||||
}
|
||||
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
@@ -381,7 +380,7 @@ void OpDispatchBuilder::VectorUnaryOp(OpcodeArgs) {
|
||||
|
||||
if constexpr (Scalar) {
|
||||
// Insert the lower bits
|
||||
auto Result = _VInsScalarElement(GetSrcSize(Op), ElementSize, 0, Dest, ALUOp);
|
||||
auto Result = _VInsElement(GetSrcSize(Op), ElementSize, 0, 0, Dest, ALUOp);
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
}
|
||||
else {
|
||||
@@ -978,7 +977,8 @@ void OpDispatchBuilder::PAVGOp<2>(OpcodeArgs);
|
||||
|
||||
void OpDispatchBuilder::MOVDDUPOp(OpcodeArgs) {
|
||||
OrderedNode *Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
OrderedNode *Res = _SplatVector2(Src);
|
||||
OrderedNode *Res = _VDupElement(16, GetSrcSize(Op), Src, 0);
|
||||
|
||||
StoreResult(FPRClass, Op, Res, -1);
|
||||
}
|
||||
|
||||
@@ -992,7 +992,7 @@ void OpDispatchBuilder::CVTGPR_To_FPR(OpcodeArgs) {
|
||||
|
||||
OrderedNode *Dest = LoadSource_WithOpSize(FPRClass, Op, Op->Dest, 16, Op->Flags, -1);
|
||||
|
||||
Src = _VInsScalarElement(16, DstElementSize, 0, Dest, Src);
|
||||
Src = _VInsElement(16, DstElementSize, 0, 0, Dest, Src);
|
||||
|
||||
StoreResult(FPRClass, Op, Src, -1);
|
||||
}
|
||||
@@ -1091,7 +1091,7 @@ void OpDispatchBuilder::Scalar_CVT_Float_To_Float(OpcodeArgs) {
|
||||
OrderedNode *Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
|
||||
Src = _Float_FToF(DstElementSize, SrcElementSize, Src);
|
||||
Src = _VInsScalarElement(16, DstElementSize, 0, Dest, Src);
|
||||
Src = _VInsElement(16, DstElementSize, 0, 0, Dest, Src);
|
||||
|
||||
StoreResult(FPRClass, Op, Src, -1);
|
||||
}
|
||||
@@ -1281,7 +1281,7 @@ void OpDispatchBuilder::VFCMPOp(OpcodeArgs) {
|
||||
|
||||
if constexpr (Scalar) {
|
||||
// Insert the lower bits
|
||||
Result = _VInsScalarElement(GetDstSize(Op), ElementSize, 0, Dest, Result);
|
||||
Result = _VInsElement(GetDstSize(Op), ElementSize, 0, 0, Dest, Result);
|
||||
}
|
||||
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
@@ -1714,8 +1714,8 @@ void OpDispatchBuilder::PFNACCOp(OpcodeArgs) {
|
||||
|
||||
OrderedNode *ResSubSrc{};
|
||||
OrderedNode *ResSubDest{};
|
||||
auto UpperSubDest = _VExtractElement(Size, 4, Dest, 1);
|
||||
auto UpperSubSrc = _VExtractElement(Size, 4, Src, 1);
|
||||
auto UpperSubDest = _VDupElement(Size, 4, Dest, 1);
|
||||
auto UpperSubSrc = _VDupElement(Size, 4, Src, 1);
|
||||
|
||||
ResSubDest = _VFSub(4, 4, Dest, UpperSubDest);
|
||||
ResSubSrc = _VFSub(4, 4, Src, UpperSubSrc);
|
||||
@@ -1733,7 +1733,7 @@ void OpDispatchBuilder::PFPNACCOp(OpcodeArgs) {
|
||||
|
||||
OrderedNode *ResAdd{};
|
||||
OrderedNode *ResSub{};
|
||||
auto UpperSubDest = _VExtractElement(Size, 4, Dest, 1);
|
||||
auto UpperSubDest = _VDupElement(Size, 4, Dest, 1);
|
||||
|
||||
ResSub = _VFSub(4, 4, Dest, UpperSubDest);
|
||||
ResAdd = _VFAddP(Size, 4, Src, Src);
|
||||
@@ -1866,8 +1866,6 @@ void OpDispatchBuilder::PMADDWD(OpcodeArgs) {
|
||||
|
||||
if (Size == 8) {
|
||||
Size <<= 1;
|
||||
Src1 = _VBitcast(Size, 2, Src1);
|
||||
Src2 = _VBitcast(Size, 2, Src2);
|
||||
}
|
||||
|
||||
auto Src1_L = _VSXTL(Size, 2, Src1); // [15:0 ], [31:16], [32:47 ], [63:48 ]
|
||||
@@ -1954,9 +1952,6 @@ void OpDispatchBuilder::PMULHW(OpcodeArgs) {
|
||||
|
||||
OrderedNode *Res{};
|
||||
if (Size == 8) {
|
||||
Dest = _VBitcast(Size * 2, 2, Dest);
|
||||
Src = _VBitcast(Size * 2, 2, Src);
|
||||
|
||||
// Implementation is more efficient for 8byte registers
|
||||
if (Signed)
|
||||
Res = _VSMull(Size * 2, 2, Dest, Src);
|
||||
@@ -2333,7 +2328,7 @@ void OpDispatchBuilder::VectorRound(OpcodeArgs) {
|
||||
if constexpr (Scalar) {
|
||||
// Insert the lower bits
|
||||
OrderedNode *Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags, -1);
|
||||
auto Result = _VInsScalarElement(GetDstSize(Op), ElementSize, 0, Dest, Src);
|
||||
auto Result = _VInsElement(GetDstSize(Op), ElementSize, 0, 0, Dest, Src);
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -1185,7 +1185,7 @@ void OpDispatchBuilder::X87FNSAVE(OpcodeArgs) {
|
||||
// upper 16 bits [79:64]
|
||||
_StoreMem(FPRClass, 8, ST0Location, data, 1);
|
||||
ST0Location = _Add(ST0Location, _Constant(8));
|
||||
auto topBytes = _VExtractElement(16, 2, data, 4);
|
||||
auto topBytes = _VDupElement(16, 2, data, 4);
|
||||
_StoreMem(FPRClass, 2, ST0Location, topBytes, 1);
|
||||
|
||||
// reset to default
|
||||
|
||||
@@ -996,7 +996,7 @@ void OpDispatchBuilder::X87FNSAVEF64(OpcodeArgs) {
|
||||
// upper 16 bits [79:64]
|
||||
_StoreMem(FPRClass, 8, ST0Location, data, 1);
|
||||
ST0Location = _Add(ST0Location, _Constant(8));
|
||||
auto topBytes = _VExtractElement(16, 2, data, 4);
|
||||
auto topBytes = _VDupElement(16, 2, data, 4);
|
||||
_StoreMem(FPRClass, 2, ST0Location, topBytes, 1);
|
||||
|
||||
// reset to default
|
||||
|
||||
+25
-53
@@ -302,10 +302,6 @@
|
||||
}
|
||||
},
|
||||
"Moves": {
|
||||
"GPR = Mov GPR:$Value": {
|
||||
"DestSize": "GetOpSize(_Value)"
|
||||
},
|
||||
|
||||
"GPR = ExtractElementPair GPRPair:$Pair, u8:$Element": {
|
||||
"Desc": ["Extracts a register for the register pair"],
|
||||
"DestSize": "GetOpSize(_Pair) >> 1"
|
||||
@@ -364,11 +360,11 @@
|
||||
},
|
||||
|
||||
"StoreContext u8:#ByteSize, RegisterClass:$Class, SSA:$Value, u32:$Offset": {
|
||||
"Desc": ["Stores a value to the context with offset",
|
||||
"Ctx[Offset] = Value",
|
||||
"Zero Extends if value's type is too small",
|
||||
"Truncates if value's type is too large"
|
||||
],
|
||||
"Desc": ["Stores a value to the context with offset",
|
||||
"Ctx[Offset] = Value",
|
||||
"Zero Extends if value's type is too small",
|
||||
"Truncates if value's type is too large"
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"DestSize": "ByteSize",
|
||||
"EmitValidation": [
|
||||
@@ -845,27 +841,25 @@
|
||||
"DestSize": "std::max<uint8_t>(4, std::max<uint8_t>(GetOpSize(_TrueVal), GetOpSize(_FalseVal)))"
|
||||
},
|
||||
"GPR = Extr GPR:$Upper, GPR:$Lower, u8:$LSB": {
|
||||
"Desc": ["Concats the two GPRs to create a value that is the size of the full two GPRs",
|
||||
"It then extracts a bitfield width that size of a GPR from the LSB",
|
||||
"Valid LSB range is 0-31 for 32bit and 0-63 for 64bit",
|
||||
"<Size * 2> ConcatValue = $Upper:$Lower",
|
||||
"Result = ConcatValue<LSB+Size - 1: LSB>"
|
||||
]
|
||||
"Desc": ["Concats the two GPRs to create a value that is the size of the full two GPRs",
|
||||
"It then extracts a bitfield width that size of a GPR from the LSB",
|
||||
"Valid LSB range is 0-31 for 32bit and 0-63 for 64bit",
|
||||
"<Size * 2> ConcatValue = $Upper:$Lower",
|
||||
"Result = ConcatValue<LSB+Size - 1: LSB>"
|
||||
]
|
||||
},
|
||||
"GPR = PDep GPR:$Input, GPR:$Mask": {
|
||||
"Desc": [
|
||||
"Performs a parallel bit deposit.",
|
||||
"Takes the contiguous low-order bits and deposits them into",
|
||||
"the destination at the locations specified by the Mask."
|
||||
]
|
||||
"Desc": ["Performs a parallel bit deposit.",
|
||||
"Takes the contiguous low-order bits and deposits them into",
|
||||
"the destination at the locations specified by the Mask."
|
||||
]
|
||||
},
|
||||
|
||||
"GPR = PExt GPR:$Input, GPR:$Mask": {
|
||||
"Desc": [
|
||||
"Performs a parallel bit extract.",
|
||||
"Each bit set in the mask will select the corresponding bit in the Input",
|
||||
"and transfers them to the lower contiguous bits in the destination."
|
||||
]
|
||||
"Desc": ["Performs a parallel bit extract.",
|
||||
"Each bit set in the mask will select the corresponding bit in the Input",
|
||||
"and transfers them to the lower contiguous bits in the destination."
|
||||
]
|
||||
},
|
||||
|
||||
"GPR = LDiv GPR:$Lower, GPR:$Upper, GPR:$Divisor": {
|
||||
@@ -924,15 +918,6 @@
|
||||
}
|
||||
},
|
||||
"Vector": {
|
||||
"FPR = SplatVector2 FPR:$Scalar": {
|
||||
"NumElements": "2",
|
||||
"DestSize": "GetOpSize(_Scalar) * 2"
|
||||
},
|
||||
"FPR = SplatVector4 FPR:$Scalar": {
|
||||
"NumElements": "4",
|
||||
"DestSize": "GetOpSize(_Scalar) * 4"
|
||||
},
|
||||
|
||||
"FPR = VMov u8:#RegisterSize, FPR:$Source": {
|
||||
"Desc" : ["Copy vector register",
|
||||
"When Register size is smaller than Source register size,",
|
||||
@@ -941,12 +926,6 @@
|
||||
"DestSize": "RegisterSize"
|
||||
},
|
||||
|
||||
"FPR = VBitcast u8:#RegisterSize, u8:#ElementSize, FPR:$Source": {
|
||||
"Desc": ["Workaround for issue with LLVM breaking when loading scalar elements to vectors"],
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
},
|
||||
|
||||
"FPR = VectorZero u8:#RegisterSize": {
|
||||
"Desc": ["Generates a vector zero",
|
||||
"Useful to generate a zero vector without any previous dependencies"
|
||||
@@ -1038,9 +1017,6 @@
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
},
|
||||
"FPR = VExtractElement u8:#RegisterSize, u8:#ElementSize, FPR:$Vector, u8:$Index": {
|
||||
"DestSize": "ElementSize"
|
||||
},
|
||||
"FPR = VDupElement u8:#RegisterSize, u8:#ElementSize, FPR:$Vector, u8:$Index": {
|
||||
"Desc": ["Duplicates one element from the source register across the whole register"],
|
||||
"DestSize": "RegisterSize",
|
||||
@@ -1124,9 +1100,9 @@
|
||||
},
|
||||
|
||||
"FPR = VRev64 u8:#RegisterSize, u8:#ElementSize, FPR:$Vector": {
|
||||
"Desc" : ["Reverses elements in 64-bit halfwords",
|
||||
"Available element size: 1byte, 2 byte, 4 byte"
|
||||
],
|
||||
"Desc" : ["Reverses elements in 64-bit halfwords",
|
||||
"Available element size: 1byte, 2 byte, 4 byte"
|
||||
],
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
},
|
||||
@@ -1320,10 +1296,6 @@
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
},
|
||||
"FPR = VInsScalarElement u8:#RegisterSize, u8:#ElementSize, u8:$DestIdx, FPR:$DestVector, FPR:$SrcScalar": {
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
},
|
||||
"FPR = VInsGPR u8:#RegisterSize, u8:#ElementSize, u8:$DestIdx, FPR:$DestVector, GPR:$Src": {
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
@@ -1587,9 +1559,9 @@
|
||||
"DestSize": "16"
|
||||
},
|
||||
"GPR = F80Cmp FPR:$X80Src1, FPR:$X80Src2, u32:$Flags": {
|
||||
"Desc": ["Does a scalar unordered compare and stores the asked for flags in to a GPR",
|
||||
"Ordering flag result is true if either float input is NaN"
|
||||
],
|
||||
"Desc": ["Does a scalar unordered compare and stores the asked for flags in to a GPR",
|
||||
"Ordering flag result is true if either float input is NaN"
|
||||
],
|
||||
"DestSize": "4"
|
||||
},
|
||||
"FPR = F80BCDLoad FPR:$X80Src": {
|
||||
|
||||
@@ -6,7 +6,7 @@ $end_info$
|
||||
*/
|
||||
|
||||
|
||||
#if defined(_M_ARM_64)
|
||||
#if JIT_ARM64
|
||||
//aarch64 heuristics
|
||||
#include "aarch64/assembler-aarch64.h"
|
||||
#include "aarch64/cpu-aarch64.h"
|
||||
@@ -45,20 +45,20 @@ uint64_t getMask(IROp_Header* Op) {
|
||||
return (~0ULL) >> (64 - NumBits);
|
||||
}
|
||||
|
||||
#ifdef _M_X86_64
|
||||
// very lazy heuristics
|
||||
static bool IsImmLogical(uint64_t imm, unsigned width) { return imm < 0x8000'0000; }
|
||||
static bool IsImmAddSub(uint64_t imm) { return imm < 0x8000'0000; }
|
||||
static bool IsMemoryScale(uint64_t Scale, uint8_t AccessSize) {
|
||||
return Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8;
|
||||
}
|
||||
#elif defined(_M_ARM_64)
|
||||
#if JIT_ARM64
|
||||
//aarch64 heuristics
|
||||
static bool IsImmLogical(uint64_t imm, unsigned width) { if (width < 32) width = 32; return vixl::aarch64::Assembler::IsImmLogical(imm, width); }
|
||||
static bool IsImmAddSub(uint64_t imm) { return vixl::aarch64::Assembler::IsImmAddSub(imm); }
|
||||
static bool IsMemoryScale(uint64_t Scale, uint8_t AccessSize) {
|
||||
return Scale == AccessSize;
|
||||
}
|
||||
#elif JIT_X86_64
|
||||
// very lazy heuristics
|
||||
static bool IsImmLogical(uint64_t imm, unsigned width) { return imm < 0x8000'0000; }
|
||||
static bool IsImmAddSub(uint64_t imm) { return imm < 0x8000'0000; }
|
||||
static bool IsMemoryScale(uint64_t Scale, uint8_t AccessSize) {
|
||||
return Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8;
|
||||
}
|
||||
#else
|
||||
#error No inline constant heuristics for this target
|
||||
#endif
|
||||
|
||||
+2
-5
@@ -449,7 +449,6 @@ void RCLSE::CalculateControlFlowInfo(FEXCore::IR::IREmitter *IREmit) {
|
||||
* %ssa26 i128 = LoadMem %ssa25 i64, 0x10
|
||||
* (%%ssa27) StoreContext %ssa26 i128, 0x10, 0xb0
|
||||
* %ssa28 i128 = LoadContext 0x10, 0x90
|
||||
* %ssa29 i128 = VBitcast %ssa26 i128
|
||||
*
|
||||
* eg.
|
||||
* %ssa6 i128 = LoadContext 0x10, 0x90
|
||||
@@ -462,13 +461,11 @@ void RCLSE::CalculateControlFlowInfo(FEXCore::IR::IREmitter *IREmit) {
|
||||
* eg.
|
||||
* (%%ssa189) StoreContext %ssa188 i128, 0x10, 0xa0
|
||||
* %ssa190 i128 = LoadContext 0x10, 0x90
|
||||
* %ssa191 i128 = VBitcast %ssa188 i128
|
||||
* %ssa192 i128 = VAdd %ssa191 i128, %ssa190 i128, 0x10, 0x4
|
||||
* %ssa192 i128 = VAdd %ssa188 i128, %ssa190 i128, 0x10, 0x4
|
||||
* (%%ssa193) StoreContext %ssa192 i128, 0x10, 0xa0
|
||||
* Converts to
|
||||
* %ssa173 i128 = LoadContext 0x10, 0x90
|
||||
* %ssa174 i128 = VBitcast %ssa172 i128
|
||||
* %ssa175 i128 = VAdd %ssa174 i128, %ssa173 i128, 0x10, 0x4
|
||||
* %ssa175 i128 = VAdd %ssa172 i128, %ssa173 i128, 0x10, 0x4
|
||||
* (%%ssa176) StoreContext %ssa175 i128, 0x10, 0xa0
|
||||
|
||||
*/
|
||||
|
||||
+7
@@ -45,6 +45,8 @@ namespace FEXCore::Allocator {
|
||||
FREE_Hook free {::free};
|
||||
#endif
|
||||
|
||||
uint64_t HostVASize{};
|
||||
|
||||
using GLIBC_MALLOC_Hook = void*(*)(size_t, const void *caller);
|
||||
using GLIBC_REALLOC_Hook = void*(*)(void*, size_t, const void *caller);
|
||||
using GLIBC_FREE_Hook = void(*)(void*, const void *caller);
|
||||
@@ -97,6 +99,10 @@ namespace FEXCore::Allocator {
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
FEX_DEFAULT_VISIBILITY size_t DetermineVASize() {
|
||||
if (HostVASize) {
|
||||
return HostVASize;
|
||||
}
|
||||
|
||||
static constexpr std::array<uintptr_t, 7> TLBSizes = {
|
||||
57,
|
||||
52,
|
||||
@@ -127,6 +133,7 @@ namespace FEXCore::Allocator {
|
||||
};
|
||||
|
||||
if (Find(Size)) {
|
||||
HostVASize = Bits;
|
||||
return Bits;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +122,10 @@ namespace CPU {
|
||||
bool IsAddressInCodeBuffer(uintptr_t Address) const;
|
||||
|
||||
protected:
|
||||
// Max spill slot size in bytes. We need at most 32 bytes
|
||||
// to be able to handle a 256-bit vector store to a slot.
|
||||
constexpr static uint32_t MaxSpillSlotSize = 32;
|
||||
|
||||
FEXCore::Core::InternalThreadState *ThreadState;
|
||||
|
||||
size_t InitialCodeSize, MaxCodeSize;
|
||||
|
||||
Vendored
+1
-1
Submodule External/vixl updated: 423cd04a70...bbc7bdc609.
@@ -1,74 +1,64 @@
|
||||
add_library(FEXHeaderUtils INTERFACE)
|
||||
|
||||
# Check for syscall support here
|
||||
check_cxx_source_compiles(
|
||||
"
|
||||
#include <sched.h>
|
||||
int main() {
|
||||
return ::getcpu(nullptr, nullptr);
|
||||
}"
|
||||
"
|
||||
#include <sched.h>
|
||||
int main() {
|
||||
return ::getcpu(nullptr, nullptr);
|
||||
}"
|
||||
compiles)
|
||||
if (compiles)
|
||||
message(STATUS "Has getcpu helper")
|
||||
add_definitions(-DHAS_SYSCALL_GETCPU=1)
|
||||
target_compile_definitions(FEXHeaderUtils INTERFACE HAS_SYSCALL_GETCPU=1)
|
||||
endif ()
|
||||
|
||||
check_cxx_source_compiles(
|
||||
"
|
||||
#include <unistd.h>
|
||||
int main() {
|
||||
return ::gettid();
|
||||
}"
|
||||
"
|
||||
#include <unistd.h>
|
||||
int main() {
|
||||
return ::gettid();
|
||||
}"
|
||||
compiles)
|
||||
if (compiles)
|
||||
message(STATUS "Has gettid helper")
|
||||
add_definitions(-DHAS_SYSCALL_GETTID=1)
|
||||
target_compile_definitions(FEXHeaderUtils INTERFACE HAS_SYSCALL_GETTID=1)
|
||||
endif ()
|
||||
|
||||
check_cxx_source_compiles(
|
||||
"
|
||||
#include <signal.h>
|
||||
int main() {
|
||||
return ::tgkill(0, 0, 0);
|
||||
}"
|
||||
"
|
||||
#include <signal.h>
|
||||
int main() {
|
||||
return ::tgkill(0, 0, 0);
|
||||
}"
|
||||
compiles)
|
||||
if (compiles)
|
||||
message(STATUS "Has tgkill helper")
|
||||
add_definitions(-DHAS_SYSCALL_TGKILL=1)
|
||||
target_compile_definitions(FEXHeaderUtils INTERFACE HAS_SYSCALL_TGKILL=1)
|
||||
endif ()
|
||||
|
||||
check_cxx_source_compiles(
|
||||
"
|
||||
#include <sys/stat.h>
|
||||
int main() {
|
||||
return ::statx(0, nullptr, 0, 0, nullptr);
|
||||
}"
|
||||
"
|
||||
#include <sys/stat.h>
|
||||
int main() {
|
||||
return ::statx(0, nullptr, 0, 0, nullptr);
|
||||
}"
|
||||
compiles)
|
||||
if (compiles)
|
||||
message(STATUS "Has statx helper")
|
||||
add_definitions(-DHAS_SYSCALL_STATX=1)
|
||||
target_compile_definitions(FEXHeaderUtils INTERFACE HAS_SYSCALL_STATX=1)
|
||||
endif ()
|
||||
|
||||
check_cxx_source_compiles(
|
||||
"
|
||||
#include <stdio.h>
|
||||
int main() {
|
||||
return ::renameat2(0, nullptr, 0, nullptr, 0);
|
||||
}"
|
||||
"
|
||||
#include <stdio.h>
|
||||
int main() {
|
||||
return ::renameat2(0, nullptr, 0, nullptr, 0);
|
||||
}"
|
||||
compiles)
|
||||
if (compiles)
|
||||
message(STATUS "Has renameat2 helper")
|
||||
add_definitions(-DHAS_SYSCALL_RENAMEAT2=1)
|
||||
endif ()
|
||||
|
||||
check_cxx_source_compiles(
|
||||
"
|
||||
#include <stdio.h>
|
||||
#include <syscall.h>
|
||||
int main() {
|
||||
return ::syscall(SYS_pidfd_open, ::getpid(), 0);
|
||||
}"
|
||||
compiles)
|
||||
if (compiles)
|
||||
message(STATUS "Has pidfd_open helper")
|
||||
add_definitions(-DHAS_SYSCALL_PIDFD_OPEN=1)
|
||||
target_compile_definitions(FEXHeaderUtils INTERFACE HAS_SYSCALL_RENAMEAT2=1)
|
||||
endif ()
|
||||
|
||||
target_include_directories(FEXHeaderUtils INTERFACE .)
|
||||
@@ -38,10 +38,15 @@ namespace FHU::Syscalls {
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Common syscall numbers
|
||||
#ifndef SYS_pidfd_open
|
||||
#define SYS_pidfd_open 434
|
||||
#endif
|
||||
|
||||
inline int32_t getcpu(uint32_t *cpu, uint32_t *node) {
|
||||
// Third argument is unused
|
||||
#if defined(HAS_SYSCALL_GETCPU) && HAS_SYSCALL_GETCPU
|
||||
return ::getcpu(cpu, node, nullptr);
|
||||
return ::getcpu(cpu, node);
|
||||
#else
|
||||
return ::syscall(SYS_getcpu, cpu, node, nullptr);
|
||||
#endif
|
||||
@@ -57,7 +62,7 @@ inline int32_t gettid() {
|
||||
|
||||
inline int32_t tgkill(pid_t tgid, pid_t tid, int sig) {
|
||||
#if defined(HAS_SYSCALL_GETTID) && HAS_SYSCALL_GETTID
|
||||
return ::tgkill(tggid, tid, sig);
|
||||
return ::tgkill(tgid, tid, sig);
|
||||
#else
|
||||
return ::syscall(SYS_tgkill, tgid, tid, sig);
|
||||
#endif
|
||||
@@ -65,7 +70,7 @@ inline int32_t tgkill(pid_t tgid, pid_t tid, int sig) {
|
||||
|
||||
inline int32_t statx(int dirfd, const char *pathname, int32_t flags, uint32_t mask, void *statxbuf) {
|
||||
#if defined(HAS_SYSCALL_STATX) && HAS_SYSCALL_STATX
|
||||
return ::statx(dirfd, pathname, flags, mask, statxbuf);
|
||||
return ::statx(dirfd, pathname, flags, mask, reinterpret_cast<struct statx *__restrict>(statxbuf));
|
||||
#else
|
||||
return ::syscall(SYS_statx, dirfd, pathname, flags, mask, statxbuf);
|
||||
#endif
|
||||
@@ -80,11 +85,7 @@ inline int32_t renameat2(int olddirfd, const char *oldpath, int newdirfd, const
|
||||
}
|
||||
|
||||
inline int32_t pidfd_open(pid_t pid, unsigned int flags) {
|
||||
#if defined(DHAS_SYSCALL_PIDFD_OPEN) && DHAS_SYSCALL_PIDFD_OPEN
|
||||
return ::syscall(SYS_pidfd_open, pid_t pid, unsigned int flags);
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
return ::syscall(SYS_pidfd_open, pid, flags);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -83,6 +83,12 @@ def HashFile(file):
|
||||
|
||||
return int.from_bytes(x.digest(), "big")
|
||||
|
||||
def RemoveRootFSFolder(RootFSPath):
|
||||
print("Removing previous rootfs extraction before copying")
|
||||
shutil.rmtree(RootFSPath, ignore_errors = True)
|
||||
# Recreate the folder
|
||||
os.makedirs(RootFSPath)
|
||||
|
||||
def CheckFilesystemForFS(RootFSMountPath, RootFSPath, DistroFit):
|
||||
# Check if rootfs mount path exists
|
||||
if (not os.path.exists(RootFSMountPath) or
|
||||
@@ -105,6 +111,7 @@ def CheckFilesystemForFS(RootFSMountPath, RootFSPath, DistroFit):
|
||||
MountRootFSImagePath = RootFSMountPath + DistroFit[3]
|
||||
RootFSImagePath = RootFSPath + "/" + os.path.basename(DistroFit[3])
|
||||
NeedsExtraction = False
|
||||
PreviouslyExistingRootFS = False
|
||||
|
||||
if not os.path.exists(MountRootFSImagePath):
|
||||
print("Image {} doesn't exist".format(MountRootFSImagePath))
|
||||
@@ -113,29 +120,39 @@ def CheckFilesystemForFS(RootFSMountPath, RootFSPath, DistroFit):
|
||||
if not os.path.exists(RootFSImagePath):
|
||||
# Copy over
|
||||
print("RootFS image doesn't exist. Copying")
|
||||
shutil.copyfile(MountRootFSImagePath, RootFSImagePath)
|
||||
NeedsExtraction = True
|
||||
|
||||
# Now hash the image
|
||||
RootFSHash = HashFile(RootFSImagePath)
|
||||
if RootFSHash != DistroFit[4]:
|
||||
print("Hash {} did not match {}, copying new image".format(hex(RootFSHash), hex(DistroFit[4])))
|
||||
RemoveRootFSFolder(RootFSPath)
|
||||
shutil.copyfile(MountRootFSImagePath, RootFSImagePath)
|
||||
NeedsExtraction = True
|
||||
|
||||
# Check if the image needs to be extracted
|
||||
if not os.path.exists(RootFSPath + "/usr"):
|
||||
NeedsExtraction = True
|
||||
else:
|
||||
PreviouslyExistingRootFS = True
|
||||
|
||||
# Now hash the image
|
||||
RootFSHash = HashFile(RootFSImagePath)
|
||||
if RootFSHash != DistroFit[4]:
|
||||
print("Hash {} did not match {}, copying new image".format(hex(RootFSHash), hex(DistroFit[4])))
|
||||
|
||||
if PreviouslyExistingRootFS:
|
||||
RemoveRootFSFolder(RootFSPath)
|
||||
|
||||
shutil.copyfile(MountRootFSImagePath, RootFSImagePath)
|
||||
NeedsExtraction = True
|
||||
|
||||
if NeedsExtraction:
|
||||
print("Extracting rootfs")
|
||||
|
||||
CmdResult = subprocess.call(["unsquashfs", "-f", "-d", RootFSPath, RootFSImagePath])
|
||||
if CmdResult != 0:
|
||||
print("Couldn't extract squashfs")
|
||||
print("Couldn't extract squashfs. Removing image file to be safe")
|
||||
os.remove(RootFSImagePath)
|
||||
return False
|
||||
|
||||
if not os.path.exists(RootFSPath + "/usr"):
|
||||
print("Couldn't extract squashfs")
|
||||
print("Couldn't extract squashfs. Removing image file to be safe")
|
||||
os.remove(RootFSImagePath)
|
||||
return False
|
||||
|
||||
print("RootFS successfully checked and extracted")
|
||||
|
||||
@@ -154,6 +154,9 @@ def HandleFunctionDeclCursor(Arch, Cursor):
|
||||
elif (Child.kind == CursorKind.ASM_LABEL_ATTR):
|
||||
# Whatever you are we don't care about you
|
||||
return Arch
|
||||
elif (Child.kind == CursorKind.WARN_UNUSED_RESULT_ATTR):
|
||||
# Whatever you are we don't care about you
|
||||
return Arch
|
||||
elif (Child.kind == CursorKind.VISIBILITY_ATTR):
|
||||
pass
|
||||
else:
|
||||
@@ -165,7 +168,7 @@ def HandleFunctionDeclCursor(Arch, Cursor):
|
||||
|
||||
def PrintFunctionDecls():
|
||||
for Decl in FunctionDecls:
|
||||
print("fn(\"{0} {1}({2})\")".format(Decl.Ret, Decl.Name, ", ".join(Decl.Params)))
|
||||
print("template<> struct fex_gen_config<{}> {{}};".format(Decl.Name))
|
||||
|
||||
def FindClangArguments(OriginalArguments):
|
||||
AddedArguments = ["clang"]
|
||||
|
||||
@@ -10,6 +10,18 @@ import logging
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.WARNING)
|
||||
|
||||
# These defines are temporarily defined since python3-clang doesn't yet support these.
|
||||
# Once this tool gets switched over to C++ then this won't be an issue.
|
||||
|
||||
# Expression that references a C++20 concept.
|
||||
CursorKind.CONCEPTSPECIALIZATIONEXPR = CursorKind(153),
|
||||
|
||||
# C++2a std::bit_cast expression.
|
||||
CursorKind.BUILTINBITCASTEXPR = CursorKind(280)
|
||||
|
||||
# a concept declaration.
|
||||
CursorKind.CONCEPTDECL = CursorKind(604),
|
||||
|
||||
@dataclass
|
||||
class TypeDefinition:
|
||||
TYPE_UNKNOWN = 0
|
||||
@@ -268,7 +280,7 @@ def HandleTypeDefDeclCursor(Arch, Cursor):
|
||||
if (len(TypeDefName) != 0):
|
||||
HandleTypeDefDecl(Arch, Cursor, TypeDefName)
|
||||
|
||||
# Append namespace
|
||||
# Append namespace
|
||||
Arch.NamespaceScope.append(TypeDefName)
|
||||
SetNamespace(Arch)
|
||||
|
||||
@@ -404,19 +416,20 @@ def HandleCursor(Arch, Cursor):
|
||||
return
|
||||
|
||||
for Child in Cursor.get_children():
|
||||
if (Child.kind == CursorKind.TRANSLATION_UNIT):
|
||||
kind = Child.kind
|
||||
if (kind == CursorKind.TRANSLATION_UNIT):
|
||||
Arch = HandleCursor(Arch, Child)
|
||||
elif (Child.kind == CursorKind.FIELD_DECL):
|
||||
elif (kind == CursorKind.FIELD_DECL):
|
||||
pass
|
||||
elif (Child.kind == CursorKind.UNION_DECL):
|
||||
elif (kind == CursorKind.UNION_DECL):
|
||||
Arch = HandleUnionDeclCursor(Arch, Child)
|
||||
elif (Child.kind == CursorKind.STRUCT_DECL):
|
||||
elif (kind == CursorKind.STRUCT_DECL):
|
||||
Arch = HandleStructDeclCursor(Arch, Child)
|
||||
elif (Child.kind == CursorKind.TYPEDEF_DECL):
|
||||
elif (kind == CursorKind.TYPEDEF_DECL):
|
||||
Arch = HandleTypeDefDeclCursor(Arch, Child)
|
||||
elif (Child.kind == CursorKind.VAR_DECL):
|
||||
elif (kind == CursorKind.VAR_DECL):
|
||||
Arch = HandleVarDeclCursor(Arch, Child)
|
||||
elif (Child.kind == CursorKind.NAMESPACE):
|
||||
elif (kind == CursorKind.NAMESPACE):
|
||||
# Append namespace
|
||||
Arch.NamespaceScope.append(Child.spelling)
|
||||
SetNamespace(Arch)
|
||||
@@ -427,7 +440,7 @@ def HandleCursor(Arch, Cursor):
|
||||
# Pop namespace off
|
||||
Arch.NamespaceScope.pop()
|
||||
SetNamespace(Arch)
|
||||
elif (Child.kind == CursorKind.TYPE_REF):
|
||||
elif (kind == CursorKind.TYPE_REF):
|
||||
# Safe to pass on
|
||||
pass
|
||||
else:
|
||||
@@ -638,25 +651,21 @@ def main():
|
||||
BaseArgs.append(sys.argv[ArgIndex])
|
||||
|
||||
args_x86_32 = [
|
||||
"-I/usr/i686-linux-gnu/include/c++/10/i686-linux-gnu/",
|
||||
"-I/usr/i686-linux-gnu/include/",
|
||||
"-I/usr/i686-linux-gnu/include",
|
||||
"-O2",
|
||||
"-m32",
|
||||
"--target=i686-linux-unknown",
|
||||
]
|
||||
|
||||
args_x86_64 = [
|
||||
"-I/usr/include/x86_64-linux-gnu",
|
||||
"-I/usr/x86_64-linux-gnu/include/c++/10/x86_64-linux-gnu/",
|
||||
"-I/usr/x86_64-linux-gnu/include/",
|
||||
"-I/usr/x86_64-linux-gnu/include",
|
||||
"-O2",
|
||||
"--target=x86_64-linux-unknown",
|
||||
"-D_M_X86_64",
|
||||
]
|
||||
|
||||
args_aarch64 = [
|
||||
"-I/usr/aarch64-linux-gnu/include/c++/10/aarch64-linux-gnu/",
|
||||
"-I/usr/aarch64-linux-gnu/include/",
|
||||
"-I/usr/aarch64-linux-gnu/include",
|
||||
"-O2",
|
||||
"--target=aarch64-linux-unknown",
|
||||
"-D_M_ARM_64",
|
||||
|
||||
@@ -3,44 +3,55 @@ import os
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
# Args: <Known Failures file> <ExpectedOutputsFile> <DisabledTestsFile> <TestName> <FexExecutable> <FexArgs>...
|
||||
def LoadTestsFile(File):
|
||||
Dict = {}
|
||||
if not os.path.exists(File):
|
||||
return Dict
|
||||
|
||||
with open(File) as dtf:
|
||||
for line in dtf:
|
||||
test = line.split("#")[0].strip() # remove comments and empty spaces
|
||||
if len(test) > 0:
|
||||
Dict[test] = 1
|
||||
|
||||
return Dict
|
||||
|
||||
def LoadTestsFileResults(File):
|
||||
Dict = {}
|
||||
if not os.path.exists(File):
|
||||
return Dict
|
||||
|
||||
with open(File) as dtf:
|
||||
for line in dtf:
|
||||
test = line.split("#")[0].strip() # remove comments and empty spaces
|
||||
if len(test) > 0:
|
||||
parts = line.split(" ")
|
||||
Dict[parts[0]] = int(parts[1])
|
||||
|
||||
return Dict
|
||||
|
||||
|
||||
# Args: <Known Failures file> <ExpectedOutputsFile> <DisabledTestsFile> <FlakeTestsFile> <TestName> <Mode> <FexExecutable> <FexArgs>...
|
||||
|
||||
# fexargs should also include the test executable
|
||||
|
||||
if (len(sys.argv) < 6):
|
||||
if (len(sys.argv) < 7):
|
||||
sys.exit()
|
||||
|
||||
known_failures_file = sys.argv[1]
|
||||
expected_output_file = sys.argv[2]
|
||||
disabled_tests_file = sys.argv[3]
|
||||
test_name = sys.argv[4]
|
||||
mode = sys.argv[5]
|
||||
fexecutable = sys.argv[6]
|
||||
flake_tests_file = sys.argv[4]
|
||||
test_name = sys.argv[5]
|
||||
mode = sys.argv[6]
|
||||
fexecutable = sys.argv[7]
|
||||
StartingFEXArgsOffset = 8
|
||||
|
||||
known_failures = { }
|
||||
expected_output = { }
|
||||
disabled_tests = { }
|
||||
|
||||
# Open the known failures file and add it to a dictionary
|
||||
with open(known_failures_file) as kff:
|
||||
for line in kff:
|
||||
test = line.split("#")[0].strip() # remove comments and empty spaces
|
||||
if len(test) > 0:
|
||||
known_failures[test] = 1
|
||||
|
||||
# Open expected outputs and add it to dictionary
|
||||
with open(expected_output_file) as eof:
|
||||
for line in eof:
|
||||
line = test = line.split("#")[0].strip() # remove comments and empty spaces
|
||||
if len(line) > 0:
|
||||
parts = line.split(" ")
|
||||
expected_output[parts[0]] = int(parts[1])
|
||||
|
||||
with open(disabled_tests_file) as dtf:
|
||||
for line in dtf:
|
||||
test = line.split("#")[0].strip() # remove comments and empty spaces
|
||||
if len(test) > 0:
|
||||
disabled_tests[test] = 1
|
||||
# Open test expected information files and load in to dictionaries.
|
||||
known_failures = LoadTestsFile(known_failures_file)
|
||||
expected_output = LoadTestsFileResults(expected_output_file)
|
||||
disabled_tests = LoadTestsFile(disabled_tests_file)
|
||||
flake_tests = LoadTestsFile(flake_tests_file)
|
||||
|
||||
# run with timeout to avoid locking up
|
||||
RunnerArgs = []
|
||||
@@ -54,25 +65,37 @@ if (mode == "guest"):
|
||||
RunnerArgs.append(ROOTFS_ENV)
|
||||
|
||||
# Add the rest of the arguments
|
||||
for i in range(len(sys.argv) - 7):
|
||||
RunnerArgs.append(sys.argv[7 + i])
|
||||
for i in range(len(sys.argv) - StartingFEXArgsOffset):
|
||||
RunnerArgs.append(sys.argv[StartingFEXArgsOffset + i])
|
||||
|
||||
#print(RunnerArgs)
|
||||
|
||||
ResultCode = 0
|
||||
|
||||
# Handle flakes
|
||||
TryCount = 1
|
||||
if (flake_tests.get(test_name)):
|
||||
TryCount = 5
|
||||
|
||||
if (disabled_tests.get(test_name)):
|
||||
ResultCode = -73
|
||||
else:
|
||||
# Run the test and wait for it to end to get the result
|
||||
Process = subprocess.Popen(RunnerArgs)
|
||||
Process.wait()
|
||||
ResultCode = Process.returncode
|
||||
|
||||
# expect zero by default
|
||||
if (not test_name in expected_output):
|
||||
expected_output[test_name] = 0
|
||||
|
||||
if ResultCode == 0:
|
||||
for Try in range(TryCount):
|
||||
# Run the test and wait for it to end to get the result
|
||||
print(RunnerArgs)
|
||||
Process = subprocess.Popen(RunnerArgs)
|
||||
Process.wait()
|
||||
ResultCode = Process.returncode
|
||||
|
||||
# Break if the expected output is the result code
|
||||
if (expected_output[test_name] == ResultCode):
|
||||
break
|
||||
|
||||
if (expected_output[test_name] != ResultCode):
|
||||
if (test_name in expected_output):
|
||||
print("test failed, expected is", expected_output[test_name], "but got", ResultCode)
|
||||
|
||||
@@ -8,6 +8,6 @@ set(SRCS
|
||||
StringUtil.cpp)
|
||||
|
||||
add_library(${NAME} STATIC ${SRCS})
|
||||
target_link_libraries(${NAME} FEXCore_Base cpp-optparse json-maker)
|
||||
target_link_libraries(${NAME} FEXCore_Base cpp-optparse json-maker FEXHeaderUtils)
|
||||
target_include_directories(${NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/External/cpp-optparse/)
|
||||
target_include_directories(${NAME} PRIVATE ${CMAKE_BINARY_DIR}/generated)
|
||||
@@ -51,10 +51,10 @@ if(TERMUX_BUILD)
|
||||
)
|
||||
|
||||
install(
|
||||
CODE "MESSAGE(\"-- Installing: ${CMAKE_INSTALL_PREFIX}/bin/FEXInterpreter\")"
|
||||
CODE "MESSAGE(\"-- Installing: $ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/FEXInterpreter\")"
|
||||
CODE "
|
||||
EXECUTE_PROCESS(COMMAND cp FEXLoader FEXInterpreter
|
||||
WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/bin/
|
||||
WORKING_DIRECTORY $ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/
|
||||
)"
|
||||
)
|
||||
else()
|
||||
@@ -64,12 +64,20 @@ else()
|
||||
)
|
||||
|
||||
install(
|
||||
CODE "MESSAGE(\"-- Installing: ${CMAKE_INSTALL_PREFIX}/bin/FEXInterpreter\")"
|
||||
CODE "MESSAGE(\"-- Installing: $ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/FEXInterpreter\")"
|
||||
CODE "
|
||||
EXECUTE_PROCESS(COMMAND ln -f FEXLoader FEXInterpreter
|
||||
WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/bin/
|
||||
WORKING_DIRECTORY $ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/
|
||||
)"
|
||||
)
|
||||
|
||||
if(TARGET uninstall)
|
||||
add_custom_target(uninstall_FEXInterpreter
|
||||
COMMAND "rm" "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/FEXInterpreter"
|
||||
)
|
||||
|
||||
add_dependencies(uninstall uninstall_FEXInterpreter)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
install(PROGRAMS "${PROJECT_SOURCE_DIR}/Scripts/FEXUpdateAOTIRCache.sh" DESTINATION bin RENAME FEXUpdateAOTIRCache)
|
||||
@@ -101,6 +109,17 @@ if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
|
||||
COMMAND ${CMAKE_COMMAND} -E
|
||||
echo "binfmt_misc FEX-x86_64 installed"
|
||||
)
|
||||
if(TARGET uninstall)
|
||||
add_custom_target(uninstall_binfmt_misc_32
|
||||
COMMAND update-binfmts --unimport FEX-x86 || (exit 0)
|
||||
)
|
||||
add_custom_target(uninstall_binfmt_misc_64
|
||||
COMMAND update-binfmts --unimport FEX-x86_64 || (exit 0)
|
||||
)
|
||||
|
||||
add_dependencies(uninstall uninstall_binfmt_misc_32)
|
||||
add_dependencies(uninstall uninstall_binfmt_misc_64)
|
||||
endif()
|
||||
else()
|
||||
# In the case of update-binfmts not being available (Arch for example) then we need to install manually
|
||||
add_custom_target(binfmt_misc_32
|
||||
@@ -129,6 +148,20 @@ if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
|
||||
COMMAND ${CMAKE_COMMAND} -E
|
||||
echo "binfmt_misc FEX-x86_64 installed"
|
||||
)
|
||||
|
||||
if(TARGET uninstall)
|
||||
add_custom_target(uninstall_binfmt_misc_32
|
||||
COMMAND ${CMAKE_COMMAND} -E
|
||||
echo -1 > /proc/sys/fs/binfmt_misc/FEX-x86 || (exit 0)
|
||||
)
|
||||
add_custom_target(uninstall_binfmt_misc_64
|
||||
COMMAND ${CMAKE_COMMAND} -E
|
||||
echo -1 > /proc/sys/fs/binfmt_misc/FEX-x86_64 || (exit 0)
|
||||
)
|
||||
|
||||
add_dependencies(uninstall uninstall_binfmt_misc_32)
|
||||
add_dependencies(uninstall uninstall_binfmt_misc_64)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_custom_target(binfmt_misc
|
||||
|
||||
+127
-40
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "Common/Config.h"
|
||||
#include "Common/FDUtils.h"
|
||||
#include "FEXCore/Utils/Allocator.h"
|
||||
#include "Tests/LinuxSyscalls/Syscalls.h"
|
||||
#include "Linux/Utils/ELFParser.h"
|
||||
#include "Linux/Utils/ELFSymbolDatabase.h"
|
||||
@@ -13,6 +14,7 @@
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -21,6 +23,7 @@
|
||||
#include <FEXCore/Core/X86Enums.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
#include <FEXHeaderUtils/Syscalls.h>
|
||||
#include <FEXHeaderUtils/TypeDefines.h>
|
||||
|
||||
#include <elf.h>
|
||||
#include <fcntl.h>
|
||||
@@ -103,7 +106,7 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
|
||||
}
|
||||
|
||||
template <typename TMap, typename TUnmap>
|
||||
std::optional<uintptr_t> LoadElfFile(ELFParser& Elf, uintptr_t *BrkBase, TMap Mapper, TUnmap Unmapper) {
|
||||
std::optional<uintptr_t> LoadElfFile(ELFParser& Elf, uintptr_t *BrkBase, TMap Mapper, TUnmap Unmapper, uint64_t LoadHint = 0) {
|
||||
|
||||
uintptr_t LoadBase = 0;
|
||||
|
||||
@@ -114,14 +117,11 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
|
||||
if (Elf.ehdr.e_type == ET_DYN) {
|
||||
// needs base address
|
||||
auto TotalSize = CalculateTotalElfSize(Elf.phdrs) + (BrkBase ? BRK_SIZE : 0);
|
||||
LoadBase = (uintptr_t)Mapper(0, TotalSize, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
LoadBase = (uintptr_t)Mapper(reinterpret_cast<void*>(LoadHint), TotalSize, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
if ((void*)LoadBase == MAP_FAILED) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (Unmapper((void*)LoadBase, TotalSize) == -1) {
|
||||
return {};
|
||||
}
|
||||
//fprintf(stderr, "elf %d: %lx-%lx\n", Elf.fd, LoadBase, LoadBase + TotalSize);
|
||||
if (BrkBase) {
|
||||
*BrkBase = LoadBase + (TotalSize - BRK_SIZE);
|
||||
@@ -132,10 +132,10 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
|
||||
if (Header.p_type != PT_LOAD)
|
||||
continue;
|
||||
|
||||
int MapProt = MapFlags(Header);
|
||||
int MapType = MAP_PRIVATE | MAP_DENYWRITE | MAP_FIXED_NOREPLACE;
|
||||
int MapProt = MapFlags(Header);
|
||||
int MapType = MAP_PRIVATE | MAP_DENYWRITE | MAP_FIXED;
|
||||
|
||||
if (!MapFile(Elf, LoadBase, Header, MapProt, MapType, Mapper)) {
|
||||
if (!MapFile(Elf, LoadBase, Header, MapProt, MapType, Mapper)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -360,33 +360,43 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
|
||||
return false;
|
||||
}
|
||||
|
||||
// load the main elf
|
||||
|
||||
uintptr_t BrkBase = 0;
|
||||
|
||||
uintptr_t LoadBase = 0;
|
||||
|
||||
if (auto elf = LoadElfFile(MainElf, &BrkBase, Mapper, Unmapper)) {
|
||||
LoadBase = *elf;
|
||||
if (MainElf.ehdr.e_type == ET_DYN) {
|
||||
BaseOffset = LoadBase;
|
||||
}
|
||||
} else {
|
||||
LogMan::Msg::EFmt("Failed to load elf file");
|
||||
return false;
|
||||
}
|
||||
|
||||
// XXX Randomise brk?
|
||||
|
||||
BrkStart = (uint64_t)Mapper((void*)BrkBase, BRK_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0);
|
||||
|
||||
if ((void*)BrkStart == MAP_FAILED) {
|
||||
LogMan::Msg::EFmt("Failed to allocate BRK @ {:x}, {}\n", BrkBase, errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
MainElfBase = LoadBase + MainElf.phdrs.front().p_vaddr - MainElf.phdrs.front().p_offset;
|
||||
MainElfEntrypoint = LoadBase + MainElf.ehdr.e_entry;
|
||||
// Load the interpreter ELF first.
|
||||
// This allows the top-down allocation of the kernel to put this at the top of the VA space.
|
||||
// This matches behaviour of native execution more closely.
|
||||
//
|
||||
// eg:
|
||||
// 555555554000-555555558000 r--p 00000000 103:0a 1311400 /usr/bin/ls
|
||||
// 555555558000-55555556c000 r-xp 00004000 103:0a 1311400 /usr/bin/ls
|
||||
// 55555556c000-555555574000 r--p 00018000 103:0a 1311400 /usr/bin/ls
|
||||
// 555555575000-555555577000 rw-p 00020000 103:0a 1311400 /usr/bin/ls
|
||||
// 555555577000-555555578000 rw-p 00000000 00:00 0 [heap]
|
||||
// 7ffff7fbb000-7ffff7fbd000 rw-p 00000000 00:00 0
|
||||
// 7ffff7fbd000-7ffff7fc1000 r--p 00000000 00:00 0 [vvar]
|
||||
// 7ffff7fc1000-7ffff7fc3000 r-xp 00000000 00:00 0 [vdso]
|
||||
// 7ffff7fc3000-7ffff7fc5000 r--p 00000000 103:0a 1316948 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
|
||||
// 7ffff7fc5000-7ffff7fef000 r-xp 00002000 103:0a 1316948 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
|
||||
// 7ffff7fef000-7ffff7ffa000 r--p 0002c000 103:0a 1316948 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
|
||||
// 7ffff7ffb000-7ffff7fff000 rw-p 00037000 103:0a 1316948 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
|
||||
// 7ffffffdd000-7ffffffff000 rw-p 00000000 00:00 0 [stack]
|
||||
// ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]
|
||||
//
|
||||
// ARM:
|
||||
// 55ccaf8b1000-55ccaf8b5000 r--p 00000000 00:2a 4 /tmp/.FEXMount178532-oiFrTF/usr/bin/ls
|
||||
// 55ccaf8b5000-55ccaf8c9000 r-xp 00004000 00:2a 4 /tmp/.FEXMount178532-oiFrTF/usr/bin/ls
|
||||
// 55ccaf8c9000-55ccaf8d1000 r--p 00018000 00:2a 4 /tmp/.FEXMount178532-oiFrTF/usr/bin/ls
|
||||
// 55ccaf8d1000-55ccaf8d2000 ---p 00000000 00:00 0
|
||||
// 55ccaf8d2000-55ccaf8d4000 rw-p 00020000 00:2a 4 /tmp/.FEXMount178532-oiFrTF/usr/bin/ls
|
||||
// 55ccaf8d4000-55ccb00d5000 rw-p 00000000 00:00 0
|
||||
// <... Snip of misc allocations ...>
|
||||
// 7fffff6c2000-7fffff6c4000 r--p 00000000 00:2a 22 /tmp/.FEXMount178532-oiFrTF/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
|
||||
// 7fffff6c4000-7fffff6ee000 r-xp 00002000 00:2a 22 /tmp/.FEXMount178532-oiFrTF/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
|
||||
// 7fffff6ee000-7fffff6f9000 r--p 0002c000 00:2a 22 /tmp/.FEXMount178532-oiFrTF/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
|
||||
// 7fffff6f9000-7fffff6fa000 ---p 00000000 00:00 0
|
||||
// 7fffff6fa000-7fffff6fe000 rw-p 00037000 00:2a 22 /tmp/.FEXMount178532-oiFrTF/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
|
||||
// 7fffff7fe000-7fffffffe000 rw-p 00000000 00:00 0
|
||||
// 7fffffffe000-7ffffffff000 r--p 00000000 08:82 7082611 /usr/share/fex-emu/GuestThunks/libVDSO-guest.so
|
||||
// 7ffffffff000-800000000000 rw-p 00000000 00:00 0
|
||||
uint64_t ELFLoadHint = 0;
|
||||
|
||||
if (!MainElf.InterpreterElf.empty()) {
|
||||
uint64_t InterpLoadBase = 0;
|
||||
@@ -399,7 +409,83 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
|
||||
|
||||
InterpeterElfBase = InterpLoadBase + InterpElf.phdrs.front().p_vaddr - InterpElf.phdrs.front().p_offset;
|
||||
Entrypoint = InterpLoadBase + InterpElf.ehdr.e_entry;
|
||||
|
||||
// If the ELF has an interpreter and is dynamic then we should provide a address hint for loading.
|
||||
// The kernel calculates this `load_bias` by dividing the task size by three then multiplying by two.
|
||||
// It then also offsets by a random number for ASLR purposes.
|
||||
//
|
||||
// Random number that gets added to the base needs to be in the number of bits (multiplied by pages):
|
||||
// 64-bit: [28, 32] bits
|
||||
// 32-bit: [8, 16] bits
|
||||
// By default the /minimum/ number of bits is used here.
|
||||
constexpr uint64_t TASK_SIZE_64 = (1ULL << 47);
|
||||
constexpr uint64_t TASK_SIZE_32 = (1ULL << 32);
|
||||
if (Is64BitMode()) {
|
||||
// Ensure that if we are running on a 36-bit VA system, we don't try hinting that an ELF should
|
||||
// live way outside the VA space.
|
||||
uint64_t HostVASize = 1ULL << FEXCore::Allocator::DetermineVASize();
|
||||
ELFLoadHint = std::min(HostVASize, TASK_SIZE_64) / 3 * 2;
|
||||
}
|
||||
else {
|
||||
ELFLoadHint = TASK_SIZE_32 / 3 * 2;
|
||||
}
|
||||
#define ASLR_LOAD
|
||||
#ifdef ASLR_LOAD
|
||||
// Only enable ASLR randomization if the personality has it enabled.
|
||||
uint32_t Personality = personality(~0ULL);
|
||||
bool NoRandomize = (Personality & ADDR_NO_RANDOMIZE) == ADDR_NO_RANDOMIZE;
|
||||
|
||||
if (!NoRandomize) {
|
||||
constexpr uint64_t ASLR_BITS_64 = 28;
|
||||
constexpr uint64_t ASLR_BITS_32 = 8;
|
||||
std::random_device rd;
|
||||
std::uniform_int_distribution<uint64_t> d(0);
|
||||
uint64_t ASLR_Offset = d(rd);
|
||||
|
||||
if (Is64BitMode()) {
|
||||
ASLR_Offset &= (1ULL << ASLR_BITS_64) - 1;
|
||||
}
|
||||
else {
|
||||
ASLR_Offset &= (1ULL << ASLR_BITS_32) - 1;
|
||||
}
|
||||
|
||||
ASLR_Offset <<= FHU::FEX_PAGE_SHIFT;
|
||||
ELFLoadHint += ASLR_Offset;
|
||||
}
|
||||
#endif
|
||||
// Align the mapping
|
||||
ELFLoadHint &= FHU::FEX_PAGE_MASK;
|
||||
}
|
||||
|
||||
// load the main elf
|
||||
|
||||
uintptr_t BrkBase = 0;
|
||||
|
||||
uintptr_t LoadBase = 0;
|
||||
|
||||
if (auto elf = LoadElfFile(MainElf, &BrkBase, Mapper, Unmapper, ELFLoadHint)) {
|
||||
LoadBase = *elf;
|
||||
if (MainElf.ehdr.e_type == ET_DYN) {
|
||||
BaseOffset = LoadBase;
|
||||
}
|
||||
} else {
|
||||
LogMan::Msg::EFmt("Failed to load elf file");
|
||||
return false;
|
||||
}
|
||||
|
||||
// XXX Randomise brk?
|
||||
|
||||
BrkStart = (uint64_t)Mapper((void*)BrkBase, BRK_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
|
||||
|
||||
if ((void*)BrkStart == MAP_FAILED) {
|
||||
LogMan::Msg::EFmt("Failed to allocate BRK @ {:x}, {}\n", BrkBase, errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
MainElfBase = LoadBase + MainElf.phdrs.front().p_vaddr - MainElf.phdrs.front().p_offset;
|
||||
MainElfEntrypoint = LoadBase + MainElf.ehdr.e_entry;
|
||||
|
||||
if (MainElf.InterpreterElf.empty()) {
|
||||
InterpeterElfBase = 0;
|
||||
Entrypoint = MainElfEntrypoint;
|
||||
}
|
||||
@@ -429,17 +515,18 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
|
||||
|
||||
// we don't support vsyscall so we don't set those
|
||||
//AuxVariables.emplace_back(auxv_t{32, 0}); // AT_SYSINFO - Entry point to syscall
|
||||
if (VDSOBase) {
|
||||
AuxVariables.emplace_back(auxv_t{33, reinterpret_cast<uint64_t>(VDSOBase)}); // AT_SYSINFO_EHDR - Address of the start of VDSO
|
||||
}
|
||||
}
|
||||
else {
|
||||
AuxVariables.emplace_back(auxv_t{4, 0x20}); // AT_PHENT
|
||||
|
||||
// we don't support vsyscall or vDSO so we don't set those
|
||||
// we don't support vsyscall so we don't set those
|
||||
//AuxVariables.emplace_back(auxv_t{32, 0}); // AT_SYSINFO - Entry point to syscall
|
||||
//AuxVariables.emplace_back(auxv_t{33, 0}); // AT_SYSINFO_EHDR - Address of the start of VDSO
|
||||
}
|
||||
|
||||
if (VDSOBase) {
|
||||
AuxVariables.emplace_back(auxv_t{33, reinterpret_cast<uint64_t>(VDSOBase)}); // AT_SYSINFO_EHDR - Address of the start of VDSO
|
||||
}
|
||||
|
||||
AuxVariables.emplace_back(auxv_t{3, MainElfBase + MainElf.ehdr.e_phoff}); // Program header
|
||||
AuxVariables.emplace_back(auxv_t{7, InterpeterElfBase}); // AT_BASE - Interpreter address
|
||||
AuxVariables.emplace_back(auxv_t{9, MainElfEntrypoint}); // AT_ENTRY
|
||||
|
||||
@@ -328,9 +328,9 @@ int main(int argc, char **argv, char **const envp) {
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
FEXCore::Config::Set(FEXCore::Config::CONFIG_APP_FILENAME, std::filesystem::canonical(Program.first).string());
|
||||
FEXCore::Config::Set(FEXCore::Config::CONFIG_APP_CONFIG_NAME, Program.second);
|
||||
FEXCore::Config::Set(FEXCore::Config::CONFIG_IS64BIT_MODE, Loader.Is64BitMode() ? "1" : "0");
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_APP_FILENAME, std::filesystem::canonical(Program.first).string());
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_APP_CONFIG_NAME, Program.second);
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_IS64BIT_MODE, Loader.Is64BitMode() ? "1" : "0");
|
||||
|
||||
std::unique_ptr<FEX::HLE::MemAllocator> Allocator;
|
||||
std::vector<FEXCore::Allocator::MemoryRegion> Base48Bit;
|
||||
@@ -390,11 +390,9 @@ int main(int argc, char **argv, char **const envp) {
|
||||
auto Mapper = std::bind_front(&FEX::HLE::SyscallHandler::GuestMmap, SyscallHandler.get());
|
||||
auto Unmapper = std::bind_front(&FEX::HLE::SyscallHandler::GuestMunmap, SyscallHandler.get());
|
||||
|
||||
if (Loader.Is64BitMode()) {
|
||||
// Load VDSO in to memory prior to mapping our ELFs.
|
||||
void* VDSOBase = FEX::VDSO::LoadVDSOThunks(Mapper);
|
||||
Loader.SetVDSOBase(VDSOBase);
|
||||
}
|
||||
// Load VDSO in to memory prior to mapping our ELFs.
|
||||
void* VDSOBase = FEX::VDSO::LoadVDSOThunks(Loader.Is64BitMode(), Mapper);
|
||||
Loader.SetVDSOBase(VDSOBase);
|
||||
|
||||
if (!Loader.MapMemory(Mapper, Unmapper)) {
|
||||
// failed to map
|
||||
|
||||
@@ -341,9 +341,12 @@ void SyscallHandler::TrackShmat(int shmid, uintptr_t Base, int shmflg) {
|
||||
}
|
||||
|
||||
void SyscallHandler::TrackShmdt(uintptr_t Base) {
|
||||
FHU::ScopedSignalMaskWithUniqueLock lk(_SyscallHandler->VMATracking.Mutex);
|
||||
uintptr_t Length = 0;
|
||||
{
|
||||
FHU::ScopedSignalMaskWithUniqueLock lk(_SyscallHandler->VMATracking.Mutex);
|
||||
|
||||
auto Length = VMATracking.ClearShmUnsafe(CTX, Base);
|
||||
Length = VMATracking.ClearShmUnsafe(CTX, Base);
|
||||
}
|
||||
|
||||
if (SMCChecks != FEXCore::Config::CONFIG_SMC_NONE) {
|
||||
// This might over flush if the shm has holes in it
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#include "VDSO_Emulation.h"
|
||||
#include "FEXCore/IR/IR.h"
|
||||
#include "Tests/LinuxSyscalls/x32/Types.h"
|
||||
|
||||
#include <FEXCore/Config/Config.h>
|
||||
#include <FEXCore/Utils/MathUtils.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
#include <FEXHeaderUtils/Syscalls.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <fcntl.h>
|
||||
@@ -17,13 +19,13 @@ namespace FEX::VDSO {
|
||||
using GetTimeOfDayType = decltype(::gettimeofday)*;
|
||||
using ClockGetTimeType = decltype(::clock_gettime)*;
|
||||
using ClockGetResType = decltype(::clock_getres)*;
|
||||
using GetCPUType = decltype(::getcpu)*;
|
||||
using GetCPUType = decltype(FHU::Syscalls::getcpu)*;
|
||||
|
||||
TimeType TimePtr = ::time;
|
||||
GetTimeOfDayType GetTimeOfDayPtr = ::gettimeofday;
|
||||
ClockGetTimeType ClockGetTimePtr = ::clock_gettime;
|
||||
ClockGetResType ClockGetResPtr = ::clock_getres;
|
||||
GetCPUType GetCPUPtr = ::getcpu;
|
||||
GetCPUType GetCPUPtr = FHU::Syscalls::getcpu;
|
||||
|
||||
static void time(void* ArgsRV) {
|
||||
struct ArgsRV_t {
|
||||
@@ -74,7 +76,94 @@ namespace FEX::VDSO {
|
||||
args->rv = GetCPUPtr(args->cpu, args->node);
|
||||
}
|
||||
|
||||
namespace x32 {
|
||||
static void time(void* ArgsRV) {
|
||||
struct ArgsRV_t {
|
||||
HLE::x32::compat_ptr<FEX::HLE::x32::old_time32_t> a_0;
|
||||
int rv;
|
||||
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
|
||||
|
||||
time_t Host{};
|
||||
args->rv = TimePtr(&Host);
|
||||
if (args->a_0) {
|
||||
*args->a_0 = Host;
|
||||
}
|
||||
}
|
||||
|
||||
static void gettimeofday(void* ArgsRV) {
|
||||
struct ArgsRV_t {
|
||||
HLE::x32::compat_ptr<FEX::HLE::x32::timeval32> tv;
|
||||
HLE::x32::compat_ptr<struct timezone> tz;
|
||||
int rv;
|
||||
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
|
||||
|
||||
struct timeval tv64{};
|
||||
struct timeval *tv_ptr{};
|
||||
if (args->tv) {
|
||||
tv_ptr = &tv64;
|
||||
}
|
||||
|
||||
args->rv = GetTimeOfDayPtr(tv_ptr, args->tz);
|
||||
|
||||
if (args->tv) {
|
||||
*args->tv = tv64;
|
||||
}
|
||||
}
|
||||
|
||||
static void clock_gettime(void* ArgsRV) {
|
||||
struct ArgsRV_t {
|
||||
clockid_t clk_id;
|
||||
HLE::x32::compat_ptr<HLE::x32::timespec32> tp;
|
||||
int rv;
|
||||
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
|
||||
|
||||
struct timespec tp64{};
|
||||
args->rv = ClockGetTimePtr(args->clk_id, &tp64);
|
||||
|
||||
if (args->tp) {
|
||||
*args->tp = tp64;
|
||||
}
|
||||
}
|
||||
|
||||
static void clock_gettime64(void* ArgsRV) {
|
||||
struct ArgsRV_t {
|
||||
clockid_t clk_id;
|
||||
HLE::x32::compat_ptr<struct timespec> tp;
|
||||
int rv;
|
||||
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
|
||||
|
||||
args->rv = ClockGetTimePtr(args->clk_id, args->tp);
|
||||
}
|
||||
|
||||
static void clock_getres(void* ArgsRV) {
|
||||
struct ArgsRV_t {
|
||||
clockid_t clk_id;
|
||||
HLE::x32::compat_ptr<HLE::x32::timespec32> tp;
|
||||
int rv;
|
||||
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
|
||||
|
||||
struct timespec tp64{};
|
||||
|
||||
args->rv = ClockGetResPtr(args->clk_id, &tp64);
|
||||
|
||||
if (args->tp) {
|
||||
*args->tp = tp64;
|
||||
}
|
||||
}
|
||||
|
||||
static void getcpu(void* ArgsRV) {
|
||||
struct ArgsRV_t {
|
||||
HLE::x32::compat_ptr<uint32_t> cpu;
|
||||
HLE::x32::compat_ptr<uint32_t> node;
|
||||
int rv;
|
||||
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
|
||||
|
||||
args->rv = GetCPUPtr(args->cpu, args->node);
|
||||
}
|
||||
}
|
||||
|
||||
void LoadHostVDSO() {
|
||||
|
||||
void *vdso = dlopen("linux-vdso.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
|
||||
if (!vdso) {
|
||||
vdso = dlopen("linux-gate.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
|
||||
@@ -117,36 +206,67 @@ namespace FEX::VDSO {
|
||||
{
|
||||
// sha256(libVDSO:time)
|
||||
{ 0x37, 0x63, 0x46, 0xb0, 0x79, 0x06, 0x5f, 0x9d, 0x00, 0xb6, 0x8d, 0xfd, 0x9e, 0x4a, 0x62, 0xcd, 0x1e, 0x6c, 0xcc, 0x22, 0xcd, 0xb2, 0xc0, 0x17, 0x7d, 0x42, 0x6a, 0x40, 0xd1, 0xeb, 0xfa, 0xe0 },
|
||||
&FEX::VDSO::time
|
||||
nullptr,
|
||||
},
|
||||
{
|
||||
// sha256(libVDSO:gettimeofday)
|
||||
{ 0x77, 0x2a, 0xde, 0x1c, 0x13, 0x2d, 0xe9, 0x48, 0xaf, 0xe0, 0xba, 0xcc, 0x6a, 0x89, 0xff, 0xca, 0x4a, 0xdc, 0xd5, 0x63, 0x2c, 0xc5, 0x62, 0x8b, 0x5d, 0xde, 0x0b, 0x15, 0x35, 0xc6, 0xc7, 0x14 },
|
||||
&FEX::VDSO::gettimeofday
|
||||
nullptr,
|
||||
},
|
||||
{
|
||||
// sha256(libVDSO:clock_gettime)
|
||||
{ 0x3c, 0x96, 0x9b, 0x2d, 0xc3, 0xad, 0x2b, 0x3b, 0x9c, 0x4e, 0x4d, 0xca, 0x1c, 0xe8, 0x18, 0x4a, 0x12, 0x8a, 0xe4, 0xc1, 0x56, 0x92, 0x73, 0xce, 0x65, 0x85, 0x5f, 0x65, 0x7e, 0x94, 0x26, 0xbe },
|
||||
&FEX::VDSO::clock_gettime
|
||||
nullptr,
|
||||
},
|
||||
|
||||
{
|
||||
// sha256(libVDSO:clock_gettime64)
|
||||
{ 0xba, 0xe9, 0x6d, 0x30, 0xc0, 0x68, 0xc6, 0xd7, 0x59, 0x04, 0xf7, 0x10, 0x06, 0x72, 0x88, 0xfd, 0x4c, 0x57, 0x0f, 0x31, 0xa5, 0xea, 0xa9, 0xb9, 0xd3, 0x8d, 0x03, 0x81, 0x50, 0x16, 0x22, 0x71 },
|
||||
nullptr,
|
||||
},
|
||||
|
||||
{
|
||||
// sha256(libVDSO:clock_getres)
|
||||
{ 0xe4, 0xa1, 0xf6, 0x23, 0x35, 0xae, 0xb7, 0xb6, 0xb0, 0x37, 0xc5, 0xc3, 0xa3, 0xfd, 0xbf, 0xa2, 0xa1, 0xc8, 0x95, 0x78, 0xe5, 0x76, 0x86, 0xdb, 0x3e, 0x6c, 0x54, 0xd5, 0x02, 0x60, 0xd8, 0x6d },
|
||||
&FEX::VDSO::clock_getres
|
||||
nullptr,
|
||||
},
|
||||
{
|
||||
// sha256(libVDSO:getcpu)
|
||||
{ 0x39, 0x83, 0x39, 0x36, 0x0f, 0x68, 0xd6, 0xfc, 0xc2, 0x3a, 0x97, 0x11, 0x85, 0x09, 0xc7, 0x25, 0xbb, 0x50, 0x49, 0x55, 0x6b, 0x0c, 0x9f, 0x50, 0x37, 0xf5, 0x9d, 0xb0, 0x38, 0x58, 0x57, 0x12 },
|
||||
&FEX::VDSO::getcpu
|
||||
nullptr,
|
||||
},
|
||||
};
|
||||
|
||||
void* LoadVDSOThunks(MapperFn Mapper) {
|
||||
void* LoadVDSOThunks(bool Is64Bit, MapperFn Mapper) {
|
||||
void* VDSOBase{};
|
||||
FEX_CONFIG_OPT(ThunkGuestLibs, THUNKGUESTLIBS);
|
||||
FEX_CONFIG_OPT(ThunkGuestLibs32, THUNKGUESTLIBS32);
|
||||
|
||||
std::filesystem::path ThunkGuestPath{};
|
||||
if (Is64Bit) {
|
||||
ThunkGuestPath = std::filesystem::path(ThunkGuestLibs()) / "libVDSO-guest.so";
|
||||
|
||||
// Set the Thunk definition pointers for x86-64
|
||||
VDSODefinitions[0].ThunkFunction = &FEX::VDSO::time;
|
||||
VDSODefinitions[1].ThunkFunction = &FEX::VDSO::gettimeofday;
|
||||
VDSODefinitions[2].ThunkFunction = &FEX::VDSO::clock_gettime;
|
||||
VDSODefinitions[3].ThunkFunction = &FEX::VDSO::clock_gettime;
|
||||
VDSODefinitions[4].ThunkFunction = &FEX::VDSO::clock_getres;
|
||||
VDSODefinitions[5].ThunkFunction = &FEX::VDSO::getcpu;
|
||||
}
|
||||
else {
|
||||
ThunkGuestPath = std::filesystem::path(ThunkGuestLibs32()) / "libVDSO-guest.so";
|
||||
|
||||
// Set the Thunk definition pointers for x86
|
||||
VDSODefinitions[0].ThunkFunction = &FEX::VDSO::x32::time;
|
||||
VDSODefinitions[1].ThunkFunction = &FEX::VDSO::x32::gettimeofday;
|
||||
VDSODefinitions[2].ThunkFunction = &FEX::VDSO::x32::clock_gettime;
|
||||
VDSODefinitions[3].ThunkFunction = &FEX::VDSO::x32::clock_gettime64;
|
||||
VDSODefinitions[4].ThunkFunction = &FEX::VDSO::x32::clock_getres;
|
||||
VDSODefinitions[5].ThunkFunction = &FEX::VDSO::x32::getcpu;
|
||||
}
|
||||
|
||||
// Load VDSO if we can
|
||||
auto ThunkGuestPath = std::filesystem::path(ThunkGuestLibs()) / "libVDSO-guest.so";
|
||||
int VDSOFD = ::open(ThunkGuestPath.string().c_str(), O_RDONLY);
|
||||
|
||||
if (VDSOFD != -1) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
namespace FEX::VDSO {
|
||||
using MapperFn = std::function<void *(void *addr, size_t length, int prot, int flags, int fd, off_t offset)>;
|
||||
void* LoadVDSOThunks(MapperFn Mapper);
|
||||
void* LoadVDSOThunks(bool Is64Bit, MapperFn Mapper);
|
||||
|
||||
std::vector<FEXCore::IR::ThunkDefinition> const& GetVDSOThunkDefinitions();
|
||||
}
|
||||
@@ -74,6 +74,7 @@ namespace {
|
||||
LoadedConfig->Erase(FEXCore::Config::ConfigOption::CONFIG_IS_INTERPRETER);
|
||||
LoadedConfig->Erase(FEXCore::Config::ConfigOption::CONFIG_INTERPRETER_INSTALLED);
|
||||
LoadedConfig->Erase(FEXCore::Config::ConfigOption::CONFIG_APP_FILENAME);
|
||||
LoadedConfig->Erase(FEXCore::Config::ConfigOption::CONFIG_APP_CONFIG_NAME);
|
||||
LoadedConfig->Erase(FEXCore::Config::ConfigOption::CONFIG_IS64BIT_MODE);
|
||||
}
|
||||
|
||||
@@ -106,6 +107,7 @@ namespace {
|
||||
LoadedConfig->Erase(FEXCore::Config::ConfigOption::CONFIG_IS_INTERPRETER);
|
||||
LoadedConfig->Erase(FEXCore::Config::ConfigOption::CONFIG_INTERPRETER_INSTALLED);
|
||||
LoadedConfig->Erase(FEXCore::Config::ConfigOption::CONFIG_APP_FILENAME);
|
||||
LoadedConfig->Erase(FEXCore::Config::ConfigOption::CONFIG_APP_CONFIG_NAME);
|
||||
LoadedConfig->Erase(FEXCore::Config::ConfigOption::CONFIG_IS64BIT_MODE);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -401,10 +401,29 @@ namespace ProcessPipe {
|
||||
case FEXServerClient::PacketType::TYPE_GET_PID_FD: {
|
||||
int FD = FHU::Syscalls::pidfd_open(::getpid(), 0);
|
||||
|
||||
SendFDSuccessPacket(Socket, FD);
|
||||
if (FD < 0) {
|
||||
// Couldn't get PIDFD due to too old of kernel.
|
||||
// Return a pipe to track the same information.
|
||||
//
|
||||
int fds[2];
|
||||
pipe2(fds, O_CLOEXEC);
|
||||
SendFDSuccessPacket(Socket, fds[0]);
|
||||
|
||||
// Close the FD now since we've sent it
|
||||
close(FD);
|
||||
// Close the read side now, doesn't matter to us
|
||||
close(fds[0]);
|
||||
|
||||
// Check if we need to increase the FD limit.
|
||||
++NumFilesOpened;
|
||||
CheckRaiseFDLimit();
|
||||
|
||||
// Write side will naturally close on process exit, letting the other process know we have exited.
|
||||
}
|
||||
else {
|
||||
SendFDSuccessPacket(Socket, FD);
|
||||
|
||||
// Close the FD now since we've sent it
|
||||
close(FD);
|
||||
}
|
||||
|
||||
CurrentOffset += sizeof(FEXServerClient::FEXServerRequestPacket::Header);
|
||||
break;
|
||||
|
||||
@@ -6,6 +6,7 @@ target_include_directories(thunkgenlib INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_include_directories(thunkgenlib SYSTEM PUBLIC ${CLANG_INCLUDE_DIRS})
|
||||
target_link_libraries(thunkgenlib PUBLIC clang-cpp LLVM)
|
||||
target_link_libraries(thunkgenlib PRIVATE OpenSSL::Crypto)
|
||||
target_link_libraries(thunkgenlib PRIVATE fmt::fmt)
|
||||
|
||||
# Query clang's global resource directory for system include directories
|
||||
if (NOT CLANG_RESOURCE_DIR)
|
||||
|
||||
+364
-322
@@ -1,13 +1,16 @@
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include "clang/Frontend/CompilerInstance.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <numeric>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include "interface.h"
|
||||
@@ -84,12 +87,9 @@ struct ThunkedAPIFunction : FunctionParams {
|
||||
std::optional<std::size_t> symtable_namespace;
|
||||
};
|
||||
|
||||
static std::vector<ThunkedFunction> thunks;
|
||||
static std::vector<ThunkedAPIFunction> thunked_api;
|
||||
static std::unordered_set<const clang::Type*> funcptr_types;
|
||||
static std::optional<unsigned> lib_version;
|
||||
|
||||
struct NamespaceInfo {
|
||||
clang::DeclContext* context;
|
||||
|
||||
std::string name;
|
||||
|
||||
// Function to load native host library functions with.
|
||||
@@ -101,299 +101,185 @@ struct NamespaceInfo {
|
||||
bool indirect_guest_calls;
|
||||
};
|
||||
|
||||
// List of namespaces with a non-specialized fex_gen_config definition (including the global namespace, represented with an empty name)
|
||||
static std::vector<NamespaceInfo> namespaces;
|
||||
static std::vector<clang::DeclContext*> decl_contexts;
|
||||
|
||||
class ASTVisitor : public clang::RecursiveASTVisitor<ASTVisitor> {
|
||||
struct ClangDiagnosticAsException {
|
||||
std::pair<clang::SourceLocation, unsigned> diagnostic;
|
||||
|
||||
void Report(clang::DiagnosticsEngine& diagnostics) const {
|
||||
diagnostics.Report(diagnostic.first, diagnostic.second);
|
||||
}
|
||||
};
|
||||
|
||||
// Helper class to build a custom DiagID from the given message and store it in a throwable object
|
||||
struct ErrorReporter {
|
||||
clang::ASTContext& context;
|
||||
|
||||
enum class CallbackStrategy {
|
||||
Default,
|
||||
Stub,
|
||||
Guest,
|
||||
};
|
||||
|
||||
struct NamespaceAnnotations {
|
||||
std::optional<unsigned> version;
|
||||
std::optional<std::string> load_host_endpoint_via;
|
||||
bool generate_guest_symtable = false;
|
||||
bool indirect_guest_calls = false;
|
||||
};
|
||||
|
||||
struct Annotations {
|
||||
bool custom_host_impl = false;
|
||||
bool custom_guest_entrypoint = false;
|
||||
|
||||
bool returns_guest_pointer = false;
|
||||
|
||||
std::optional<clang::QualType> uniform_va_type;
|
||||
|
||||
CallbackStrategy callback_strategy = CallbackStrategy::Default;
|
||||
};
|
||||
|
||||
NamespaceAnnotations GetNamespaceAnnotations(clang::CXXRecordDecl* decl) {
|
||||
if (!decl->hasDefinition()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
NamespaceAnnotations ret;
|
||||
|
||||
for (const clang::CXXBaseSpecifier& base : decl->bases()) {
|
||||
auto annotation = base.getType().getAsString();
|
||||
if (annotation == "fexgen::generate_guest_symtable") {
|
||||
ret.generate_guest_symtable = true;
|
||||
} else if (annotation == "fexgen::indirect_guest_calls") {
|
||||
ret.indirect_guest_calls = true;
|
||||
} else {
|
||||
throw Error(base.getSourceRange().getBegin(), "Unknown namespace annotation");
|
||||
}
|
||||
}
|
||||
|
||||
for (const clang::FieldDecl* field : decl->fields()) {
|
||||
auto name = field->getNameAsString();
|
||||
if (name == "load_host_endpoint_via") {
|
||||
auto loader_function_expr = field->getInClassInitializer()->IgnoreCasts();
|
||||
auto loader_function_str = llvm::dyn_cast_or_null<clang::StringLiteral>(loader_function_expr);
|
||||
if (loader_function_expr && !loader_function_str) {
|
||||
throw Error(loader_function_expr->getBeginLoc(),
|
||||
"Must initialize load_host_endpoint_via with a string");
|
||||
}
|
||||
if (loader_function_str) {
|
||||
ret.load_host_endpoint_via = loader_function_str->getString();
|
||||
}
|
||||
} else if (name == "version") {
|
||||
auto initializer = field->getInClassInitializer()->IgnoreCasts();
|
||||
auto version_literal = llvm::dyn_cast_or_null<clang::IntegerLiteral>(initializer);
|
||||
if (!initializer || !version_literal) {
|
||||
throw Error(field->getBeginLoc(), "No version given (expected integral typed member, e.g. \"int version = 5;\")");
|
||||
}
|
||||
ret.version = version_literal->getValue().getZExtValue();
|
||||
} else {
|
||||
throw Error(field->getBeginLoc(), "Unknown namespace annotation");
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Annotations GetAnnotations(clang::CXXRecordDecl* decl) {
|
||||
Annotations ret;
|
||||
|
||||
for (const auto& base : decl->bases()) {
|
||||
auto annotation = base.getType().getAsString();
|
||||
if (annotation == "fexgen::returns_guest_pointer") {
|
||||
ret.returns_guest_pointer = true;
|
||||
} else if (annotation == "fexgen::custom_host_impl") {
|
||||
ret.custom_host_impl = true;
|
||||
} else if (annotation == "fexgen::callback_stub") {
|
||||
ret.callback_strategy = CallbackStrategy::Stub;
|
||||
} else if (annotation == "fexgen::callback_guest") {
|
||||
ret.callback_strategy = CallbackStrategy::Guest;
|
||||
} else if (annotation == "fexgen::custom_guest_entrypoint") {
|
||||
ret.custom_guest_entrypoint = true;
|
||||
} else {
|
||||
throw Error(base.getSourceRange().getBegin(), "Unknown annotation");
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& child_decl : decl->getPrimaryContext()->decls()) {
|
||||
if (auto field = llvm::dyn_cast_or_null<clang::FieldDecl>(child_decl)) {
|
||||
throw Error(field->getBeginLoc(), "Unknown field annotation");
|
||||
} else if (auto type_alias = llvm::dyn_cast_or_null<clang::TypedefNameDecl>(child_decl)) {
|
||||
auto name = type_alias->getNameAsString();
|
||||
if (name == "uniform_va_type") {
|
||||
ret.uniform_va_type = type_alias->getUnderlyingType();
|
||||
} else {
|
||||
throw Error(type_alias->getBeginLoc(), "Unknown type alias annotation");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
using ClangDiagnosticAsException = std::pair<clang::SourceLocation, unsigned>;
|
||||
|
||||
template<std::size_t N>
|
||||
[[nodiscard]] ClangDiagnosticAsException Error(clang::SourceLocation loc, const char (&message)[N]) {
|
||||
[[nodiscard]] ClangDiagnosticAsException operator()(clang::SourceLocation loc, const char (&message)[N]) {
|
||||
auto id = context.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, message);
|
||||
return std::pair(loc, id);
|
||||
return { std::pair(loc, id) };
|
||||
}
|
||||
};
|
||||
|
||||
struct NamespaceAnnotations {
|
||||
std::optional<unsigned> version;
|
||||
std::optional<std::string> load_host_endpoint_via;
|
||||
bool generate_guest_symtable = false;
|
||||
bool indirect_guest_calls = false;
|
||||
};
|
||||
|
||||
static NamespaceAnnotations GetNamespaceAnnotations(clang::ASTContext& context, clang::CXXRecordDecl* decl) {
|
||||
if (!decl->hasDefinition()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorReporter report_error { context };
|
||||
NamespaceAnnotations ret;
|
||||
|
||||
for (const clang::CXXBaseSpecifier& base : decl->bases()) {
|
||||
auto annotation = base.getType().getAsString();
|
||||
if (annotation == "fexgen::generate_guest_symtable") {
|
||||
ret.generate_guest_symtable = true;
|
||||
} else if (annotation == "fexgen::indirect_guest_calls") {
|
||||
ret.indirect_guest_calls = true;
|
||||
} else {
|
||||
throw report_error(base.getSourceRange().getBegin(), "Unknown namespace annotation");
|
||||
}
|
||||
}
|
||||
|
||||
for (const clang::FieldDecl* field : decl->fields()) {
|
||||
auto name = field->getNameAsString();
|
||||
if (name == "load_host_endpoint_via") {
|
||||
auto loader_function_expr = field->getInClassInitializer()->IgnoreCasts();
|
||||
auto loader_function_str = llvm::dyn_cast_or_null<clang::StringLiteral>(loader_function_expr);
|
||||
if (loader_function_expr && !loader_function_str) {
|
||||
throw report_error(loader_function_expr->getBeginLoc(),
|
||||
"Must initialize load_host_endpoint_via with a string");
|
||||
}
|
||||
if (loader_function_str) {
|
||||
ret.load_host_endpoint_via = loader_function_str->getString();
|
||||
}
|
||||
} else if (name == "version") {
|
||||
auto initializer = field->getInClassInitializer()->IgnoreCasts();
|
||||
auto version_literal = llvm::dyn_cast_or_null<clang::IntegerLiteral>(initializer);
|
||||
if (!initializer || !version_literal) {
|
||||
throw report_error(field->getBeginLoc(), "No version given (expected integral typed member, e.g. \"int version = 5;\")");
|
||||
}
|
||||
ret.version = version_literal->getValue().getZExtValue();
|
||||
} else {
|
||||
throw report_error(field->getBeginLoc(), "Unknown namespace annotation");
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum class CallbackStrategy {
|
||||
Default,
|
||||
Stub,
|
||||
Guest,
|
||||
};
|
||||
|
||||
struct Annotations {
|
||||
bool custom_host_impl = false;
|
||||
bool custom_guest_entrypoint = false;
|
||||
|
||||
bool returns_guest_pointer = false;
|
||||
|
||||
std::optional<clang::QualType> uniform_va_type;
|
||||
|
||||
CallbackStrategy callback_strategy = CallbackStrategy::Default;
|
||||
};
|
||||
|
||||
static Annotations GetAnnotations(clang::ASTContext& context, clang::CXXRecordDecl* decl) {
|
||||
ErrorReporter report_error { context };
|
||||
Annotations ret;
|
||||
|
||||
for (const auto& base : decl->bases()) {
|
||||
auto annotation = base.getType().getAsString();
|
||||
if (annotation == "fexgen::returns_guest_pointer") {
|
||||
ret.returns_guest_pointer = true;
|
||||
} else if (annotation == "fexgen::custom_host_impl") {
|
||||
ret.custom_host_impl = true;
|
||||
} else if (annotation == "fexgen::callback_stub") {
|
||||
ret.callback_strategy = CallbackStrategy::Stub;
|
||||
} else if (annotation == "fexgen::callback_guest") {
|
||||
ret.callback_strategy = CallbackStrategy::Guest;
|
||||
} else if (annotation == "fexgen::custom_guest_entrypoint") {
|
||||
ret.custom_guest_entrypoint = true;
|
||||
} else {
|
||||
throw report_error(base.getSourceRange().getBegin(), "Unknown annotation");
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& child_decl : decl->getPrimaryContext()->decls()) {
|
||||
if (auto field = llvm::dyn_cast_or_null<clang::FieldDecl>(child_decl)) {
|
||||
throw report_error(field->getBeginLoc(), "Unknown field annotation");
|
||||
} else if (auto type_alias = llvm::dyn_cast_or_null<clang::TypedefNameDecl>(child_decl)) {
|
||||
auto name = type_alias->getNameAsString();
|
||||
if (name == "uniform_va_type") {
|
||||
ret.uniform_va_type = type_alias->getUnderlyingType();
|
||||
} else {
|
||||
throw report_error(type_alias->getBeginLoc(), "Unknown type alias annotation");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
class ASTVisitor : public clang::RecursiveASTVisitor<ASTVisitor> {
|
||||
public:
|
||||
ASTVisitor(clang::ASTContext& context_) : context(context_) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches "template<auto> struct fex_gen_config { ... }"
|
||||
*/
|
||||
bool VisitClassTemplateDecl(clang::ClassTemplateDecl* decl) try {
|
||||
bool VisitClassTemplateDecl(clang::ClassTemplateDecl* decl) {
|
||||
if (decl->getName() != "fex_gen_config") {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto annotations = GetNamespaceAnnotations(decl->getTemplatedDecl());
|
||||
|
||||
auto namespace_decl = llvm::dyn_cast<clang::NamespaceDecl>(decl->getDeclContext());
|
||||
namespaces.push_back({ namespace_decl ? namespace_decl->getNameAsString() : "",
|
||||
annotations.load_host_endpoint_via.value_or(""),
|
||||
annotations.generate_guest_symtable,
|
||||
annotations.indirect_guest_calls });
|
||||
|
||||
if (annotations.version) {
|
||||
if (namespace_decl) {
|
||||
throw Error(decl->getBeginLoc(), "Library version must be defined in the global namespace");
|
||||
}
|
||||
lib_version = annotations.version;
|
||||
if (llvm::dyn_cast<clang::NamespaceDecl>(decl->getDeclContext())) {
|
||||
decl_contexts.push_back(decl->getDeclContext());
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (ClangDiagnosticAsException& exception) {
|
||||
context.getDiagnostics().Report(exception.first, exception.second);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches "template<> struct fex_gen_config<LibraryFunc> { ... }"
|
||||
*/
|
||||
bool VisitClassTemplateSpecializationDecl(clang::ClassTemplateSpecializationDecl* decl) try {
|
||||
if (decl->getName() == "fex_gen_type") {
|
||||
const auto& template_args = decl->getTemplateArgs();
|
||||
assert(template_args.size() == 1);
|
||||
|
||||
// NOTE: Function types that are equivalent but use differently
|
||||
// named types (e.g. GLuint/GLenum) are represented by
|
||||
// different Type instances. The canonical type they refer
|
||||
// to is unique, however.
|
||||
auto type = context.getCanonicalType(template_args[0].getAsType()).getTypePtr();
|
||||
funcptr_types.insert(type);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (decl->getName() != "fex_gen_config") {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (decl->getSpecializationKind() == clang::TSK_ExplicitInstantiationDefinition) {
|
||||
throw Error(decl->getBeginLoc(), "fex_gen_config may not be partially specialized\n");
|
||||
}
|
||||
|
||||
std::string namespace_name;
|
||||
if (auto namespace_decl = llvm::dyn_cast<clang::NamespaceDecl>(decl->getDeclContext())) {
|
||||
namespace_name = namespace_decl->getNameAsString();
|
||||
}
|
||||
const auto namespace_idx = std::distance( namespaces.begin(),
|
||||
std::find_if( namespaces.begin(), namespaces.end(),
|
||||
[&](auto& info) { return info.name == namespace_name; }));
|
||||
const NamespaceInfo& namespace_info = namespaces[namespace_idx];
|
||||
|
||||
const auto& template_args = decl->getTemplateArgs();
|
||||
assert(template_args.size() == 1);
|
||||
|
||||
auto emitted_function = llvm::dyn_cast<clang::FunctionDecl>(template_args[0].getAsDecl());
|
||||
assert(emitted_function && "Argument is not a function");
|
||||
auto return_type = emitted_function->getReturnType();
|
||||
|
||||
const auto annotations = GetAnnotations(decl);
|
||||
if (return_type->isFunctionPointerType() && !annotations.returns_guest_pointer) {
|
||||
throw Error(decl->getBeginLoc(),
|
||||
"Function pointer return types require explicit annotation\n");
|
||||
}
|
||||
|
||||
// TODO: Use the types as written in the signature instead?
|
||||
ThunkedFunction data;
|
||||
data.function_name = emitted_function->getName().str();
|
||||
data.return_type = return_type;
|
||||
data.is_variadic = emitted_function->isVariadic();
|
||||
|
||||
data.decl = emitted_function;
|
||||
|
||||
data.custom_host_impl = annotations.custom_host_impl;
|
||||
|
||||
for (std::size_t param_idx = 0; param_idx < emitted_function->param_size(); ++param_idx) {
|
||||
auto* param = emitted_function->getParamDecl(param_idx);
|
||||
data.param_types.push_back(param->getType());
|
||||
|
||||
if (param->getType()->isFunctionPointerType()) {
|
||||
auto funcptr = param->getFunctionType()->getAs<clang::FunctionProtoType>();
|
||||
ThunkedCallback callback;
|
||||
callback.return_type = funcptr->getReturnType();
|
||||
for (auto& cb_param : funcptr->getParamTypes()) {
|
||||
callback.param_types.push_back(cb_param);
|
||||
}
|
||||
callback.is_stub = annotations.callback_strategy == CallbackStrategy::Stub;
|
||||
callback.is_guest = annotations.callback_strategy == CallbackStrategy::Guest;
|
||||
callback.is_variadic = funcptr->isVariadic();
|
||||
|
||||
if (callback.is_guest && !data.custom_host_impl) {
|
||||
throw Error(decl->getBeginLoc(), "callback_guest can only be used with custom_host_impl");
|
||||
}
|
||||
|
||||
data.callbacks.emplace(param_idx, callback);
|
||||
if (!callback.is_stub && !callback.is_guest) {
|
||||
funcptr_types.insert(context.getCanonicalType(funcptr));
|
||||
}
|
||||
|
||||
if (data.callbacks.size() != 1) {
|
||||
throw Error(decl->getBeginLoc(), "Support for more than one callback is untested");
|
||||
}
|
||||
if (funcptr->isVariadic() && !callback.is_stub) {
|
||||
throw Error(decl->getBeginLoc(), "Variadic callbacks are not supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
thunked_api.push_back(ThunkedAPIFunction { (const FunctionParams&)data, data.function_name, data.return_type,
|
||||
namespace_info.host_loader.empty() ? "dlsym" : namespace_info.host_loader,
|
||||
data.is_variadic || annotations.custom_guest_entrypoint,
|
||||
data.is_variadic,
|
||||
std::nullopt });
|
||||
if (namespace_info.generate_guest_symtable) {
|
||||
thunked_api.back().symtable_namespace = namespace_idx;
|
||||
}
|
||||
|
||||
if (data.is_variadic) {
|
||||
if (!annotations.uniform_va_type) {
|
||||
throw Error(decl->getBeginLoc(), "Variadic functions must be annotated with parameter type using uniform_va_type");
|
||||
}
|
||||
|
||||
// Convert variadic argument list into a count + pointer pair
|
||||
data.param_types.push_back(context.getSizeType());
|
||||
data.param_types.push_back(context.getPointerType(*annotations.uniform_va_type));
|
||||
}
|
||||
|
||||
if (data.is_variadic) {
|
||||
// This function is thunked through an "_internal" symbol since its signature
|
||||
// is different from the one in the native host/guest libraries.
|
||||
data.function_name = data.function_name + "_internal";
|
||||
if (data.custom_host_impl) {
|
||||
throw Error(decl->getBeginLoc(), "Custom host impl requested but this is implied by the function signature already");
|
||||
}
|
||||
data.custom_host_impl = true;
|
||||
}
|
||||
|
||||
// For indirect calls, register the function signature as a function pointer type
|
||||
if (namespace_info.indirect_guest_calls) {
|
||||
funcptr_types.insert(context.getCanonicalType(emitted_function->getFunctionType()));
|
||||
}
|
||||
|
||||
thunks.push_back(std::move(data));
|
||||
|
||||
return true;
|
||||
} catch (ClangDiagnosticAsException& exception) {
|
||||
context.getDiagnostics().Report(exception.first, exception.second);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class ASTConsumer : public clang::ASTConsumer {
|
||||
public:
|
||||
void HandleTranslationUnit(clang::ASTContext& context) override {
|
||||
ASTVisitor{context}.TraverseDecl(context.getTranslationUnitDecl());
|
||||
ASTVisitor{}.TraverseDecl(context.getTranslationUnitDecl());
|
||||
}
|
||||
};
|
||||
|
||||
class GenerateThunkLibsAction : public clang::ASTFrontendAction {
|
||||
public:
|
||||
GenerateThunkLibsAction(const std::string& libname, const OutputFilenames&);
|
||||
|
||||
void ExecuteAction() override;
|
||||
|
||||
std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(clang::CompilerInstance&, clang::StringRef /*file*/) override;
|
||||
|
||||
private:
|
||||
// Build the internal API representation by processing fex_gen_config and other annotated entities
|
||||
void ParseInterface(clang::ASTContext&);
|
||||
|
||||
// Generate helper code for thunk libraries and write them to the output file
|
||||
void EmitOutput();
|
||||
|
||||
const std::string& libfilename;
|
||||
std::string libname; // sanitized filename, usable as part of emitted function names
|
||||
const OutputFilenames& output_filenames;
|
||||
|
||||
std::vector<ThunkedFunction> thunks;
|
||||
std::vector<ThunkedAPIFunction> thunked_api;
|
||||
std::unordered_set<const clang::Type*> funcptr_types;
|
||||
std::optional<unsigned> lib_version;
|
||||
std::vector<NamespaceInfo> namespaces;
|
||||
};
|
||||
|
||||
GenerateThunkLibsAction::GenerateThunkLibsAction(const std::string& libname_, const OutputFilenames& output_filenames_)
|
||||
: libfilename(libname_), libname(libname_), output_filenames(output_filenames_) {
|
||||
for (auto& c : libname) {
|
||||
@@ -402,11 +288,8 @@ GenerateThunkLibsAction::GenerateThunkLibsAction(const std::string& libname_, co
|
||||
}
|
||||
}
|
||||
|
||||
thunks.clear();
|
||||
thunked_api.clear();
|
||||
funcptr_types.clear();
|
||||
namespaces.clear();
|
||||
lib_version = std::nullopt;
|
||||
decl_contexts.clear();
|
||||
decl_contexts.push_back(nullptr); // global namespace (replaced by getTranslationUnitDecl later)
|
||||
}
|
||||
|
||||
template<typename Fn>
|
||||
@@ -420,8 +303,185 @@ static std::string format_function_args(const FunctionParams& params, Fn&& forma
|
||||
return ret;
|
||||
};
|
||||
|
||||
void GenerateThunkLibsAction::EndSourceFileAction() {
|
||||
static clang::ClassTemplateDecl*
|
||||
FindClassTemplateDeclByName(clang::DeclContext& decl_context, std::string_view symbol_name) {
|
||||
auto& ast_context = decl_context.getParentASTContext();
|
||||
auto* ident = &ast_context.Idents.get(symbol_name);
|
||||
auto declname = ast_context.DeclarationNames.getIdentifier(ident);
|
||||
auto result = decl_context.noload_lookup(declname);
|
||||
if (result.empty()) {
|
||||
return nullptr;
|
||||
} else if (std::next(result.begin()) == result.end()) {
|
||||
return llvm::dyn_cast<clang::ClassTemplateDecl>(*result.begin());
|
||||
} else {
|
||||
throw std::runtime_error("Found multiple matches to symbol " + std::string { symbol_name });
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateThunkLibsAction::ExecuteAction() {
|
||||
clang::ASTFrontendAction::ExecuteAction();
|
||||
|
||||
// Post-processing happens here rather than in an overridden EndSourceFileAction implementation.
|
||||
// We can't move the logic to the latter since this code might still raise errors, but
|
||||
// clang's diagnostics engine is already shut down by the time EndSourceFileAction is called.
|
||||
|
||||
auto& context = getCompilerInstance().getASTContext();
|
||||
if (context.getDiagnostics().hasErrorOccurred()) {
|
||||
return;
|
||||
}
|
||||
decl_contexts.front() = context.getTranslationUnitDecl();
|
||||
|
||||
try {
|
||||
ParseInterface(context);
|
||||
EmitOutput();
|
||||
} catch (ClangDiagnosticAsException& exception) {
|
||||
exception.Report(context.getDiagnostics());
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateThunkLibsAction::ParseInterface(clang::ASTContext& context) {
|
||||
ErrorReporter report_error { context };
|
||||
|
||||
if (auto template_decl = FindClassTemplateDeclByName(*context.getTranslationUnitDecl(), "fex_gen_type")) {
|
||||
for (auto* decl : template_decl->specializations()) {
|
||||
const auto& template_args = decl->getTemplateArgs();
|
||||
assert(template_args.size() == 1);
|
||||
|
||||
// NOTE: Function types that are equivalent but use differently
|
||||
// named types (e.g. GLuint/GLenum) are represented by
|
||||
// different Type instances. The canonical type they refer
|
||||
// to is unique, however.
|
||||
auto type = context.getCanonicalType(template_args[0].getAsType()).getTypePtr();
|
||||
funcptr_types.insert(type);
|
||||
}
|
||||
}
|
||||
|
||||
// Process declarations and specializations of fex_gen_config,
|
||||
// i.e. the function descriptions of the thunked API
|
||||
for (auto& decl_context : decl_contexts) {
|
||||
if (const auto template_decl = FindClassTemplateDeclByName(*decl_context, "fex_gen_config")) {
|
||||
// Gather general information about symbols in this namespace
|
||||
const auto annotations = GetNamespaceAnnotations(context, template_decl->getTemplatedDecl());
|
||||
|
||||
auto namespace_decl = llvm::dyn_cast<clang::NamespaceDecl>(decl_context);
|
||||
namespaces.push_back({ namespace_decl,
|
||||
namespace_decl ? namespace_decl->getNameAsString() : "",
|
||||
annotations.load_host_endpoint_via.value_or(""),
|
||||
annotations.generate_guest_symtable,
|
||||
annotations.indirect_guest_calls });
|
||||
const auto namespace_idx = namespaces.size() - 1;
|
||||
const NamespaceInfo& namespace_info = namespaces.back();
|
||||
|
||||
if (annotations.version) {
|
||||
if (namespace_decl) {
|
||||
throw report_error(template_decl->getBeginLoc(), "Library version must be defined in the global namespace");
|
||||
}
|
||||
lib_version = annotations.version;
|
||||
}
|
||||
|
||||
// Process specializations of template fex_gen_config
|
||||
for (auto* decl : template_decl->specializations()) {
|
||||
if (decl->getSpecializationKind() == clang::TSK_ExplicitInstantiationDefinition) {
|
||||
throw report_error(decl->getBeginLoc(), "fex_gen_config may not be partially specialized\n");
|
||||
}
|
||||
|
||||
const auto& template_args = decl->getTemplateArgs();
|
||||
assert(template_args.size() == 1);
|
||||
|
||||
auto emitted_function = llvm::dyn_cast<clang::FunctionDecl>(template_args[0].getAsDecl());
|
||||
assert(emitted_function && "Argument is not a function");
|
||||
auto return_type = emitted_function->getReturnType();
|
||||
|
||||
const auto annotations = GetAnnotations(context, decl);
|
||||
if (return_type->isFunctionPointerType() && !annotations.returns_guest_pointer) {
|
||||
throw report_error( decl->getBeginLoc(),
|
||||
"Function pointer return types require explicit annotation\n");
|
||||
}
|
||||
|
||||
// TODO: Use the types as written in the signature instead?
|
||||
ThunkedFunction data;
|
||||
data.function_name = emitted_function->getName().str();
|
||||
data.return_type = return_type;
|
||||
data.is_variadic = emitted_function->isVariadic();
|
||||
|
||||
data.decl = emitted_function;
|
||||
|
||||
data.custom_host_impl = annotations.custom_host_impl;
|
||||
|
||||
for (std::size_t param_idx = 0; param_idx < emitted_function->param_size(); ++param_idx) {
|
||||
auto* param = emitted_function->getParamDecl(param_idx);
|
||||
data.param_types.push_back(param->getType());
|
||||
|
||||
if (param->getType()->isFunctionPointerType()) {
|
||||
auto funcptr = param->getFunctionType()->getAs<clang::FunctionProtoType>();
|
||||
ThunkedCallback callback;
|
||||
callback.return_type = funcptr->getReturnType();
|
||||
for (auto& cb_param : funcptr->getParamTypes()) {
|
||||
callback.param_types.push_back(cb_param);
|
||||
}
|
||||
callback.is_stub = annotations.callback_strategy == CallbackStrategy::Stub;
|
||||
callback.is_guest = annotations.callback_strategy == CallbackStrategy::Guest;
|
||||
callback.is_variadic = funcptr->isVariadic();
|
||||
|
||||
if (callback.is_guest && !data.custom_host_impl) {
|
||||
throw report_error(decl->getBeginLoc(), "callback_guest can only be used with custom_host_impl");
|
||||
}
|
||||
|
||||
data.callbacks.emplace(param_idx, callback);
|
||||
if (!callback.is_stub && !callback.is_guest) {
|
||||
funcptr_types.insert(context.getCanonicalType(funcptr));
|
||||
}
|
||||
|
||||
if (data.callbacks.size() != 1) {
|
||||
throw report_error(decl->getBeginLoc(), "Support for more than one callback is untested");
|
||||
}
|
||||
if (funcptr->isVariadic() && !callback.is_stub) {
|
||||
throw report_error(decl->getBeginLoc(), "Variadic callbacks are not supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
thunked_api.push_back(ThunkedAPIFunction { (const FunctionParams&)data, data.function_name, data.return_type,
|
||||
namespace_info.host_loader.empty() ? "dlsym" : namespace_info.host_loader,
|
||||
data.is_variadic || annotations.custom_guest_entrypoint,
|
||||
data.is_variadic,
|
||||
std::nullopt });
|
||||
if (namespace_info.generate_guest_symtable) {
|
||||
thunked_api.back().symtable_namespace = namespace_idx;
|
||||
}
|
||||
|
||||
if (data.is_variadic) {
|
||||
if (!annotations.uniform_va_type) {
|
||||
throw report_error(decl->getBeginLoc(), "Variadic functions must be annotated with parameter type using uniform_va_type");
|
||||
}
|
||||
|
||||
// Convert variadic argument list into a count + pointer pair
|
||||
data.param_types.push_back(context.getSizeType());
|
||||
data.param_types.push_back(context.getPointerType(*annotations.uniform_va_type));
|
||||
}
|
||||
|
||||
if (data.is_variadic) {
|
||||
// This function is thunked through an "_internal" symbol since its signature
|
||||
// is different from the one in the native host/guest libraries.
|
||||
data.function_name = data.function_name + "_internal";
|
||||
if (data.custom_host_impl) {
|
||||
throw report_error(decl->getBeginLoc(), "Custom host impl requested but this is implied by the function signature already");
|
||||
}
|
||||
data.custom_host_impl = true;
|
||||
}
|
||||
|
||||
// For indirect calls, register the function signature as a function pointer type
|
||||
if (namespace_info.indirect_guest_calls) {
|
||||
funcptr_types.insert(context.getCanonicalType(emitted_function->getFunctionType()));
|
||||
}
|
||||
|
||||
thunks.push_back(std::move(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateThunkLibsAction::EmitOutput() {
|
||||
static auto format_decl = [](clang::QualType type, const std::string_view& name) {
|
||||
if (type->isFunctionPointerType()) {
|
||||
auto signature = type.getAsString();
|
||||
@@ -429,7 +489,7 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
|
||||
auto it = std::search(signature.begin(), signature.end(), std::begin(needle), std::end(needle));
|
||||
if (it == signature.end()) {
|
||||
// It's *probably* a typedef, so this should be safe after all
|
||||
return signature + " " + std::string(name);
|
||||
return fmt::format("{} {}", signature, name);
|
||||
} else {
|
||||
signature.insert(it + 2, name.begin(), name.end());
|
||||
return signature;
|
||||
@@ -442,7 +502,7 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
|
||||
auto format_struct_members = [](const FunctionParams& params, const char* indent) {
|
||||
std::string ret;
|
||||
for (std::size_t idx = 0; idx < params.param_types.size(); ++idx) {
|
||||
ret += indent + format_decl(params.param_types[idx].getUnqualifiedType(), "a_" + std::to_string(idx)) + ";\n";
|
||||
ret += indent + format_decl(params.param_types[idx].getUnqualifiedType(), fmt::format("a_{}", idx)) + ";\n";
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
@@ -451,7 +511,7 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
|
||||
std::string ret;
|
||||
for (std::size_t idx = 0; idx < params.param_types.size(); ++idx) {
|
||||
auto& type = params.param_types[idx];
|
||||
ret += format_decl(type, "a_" + std::to_string(idx)) + ", ";
|
||||
ret += format_decl(type, fmt::format("a_{}", idx)) + ", ";
|
||||
}
|
||||
// drop trailing ", "
|
||||
ret.resize(ret.size() > 2 ? ret.size() - 2 : 0);
|
||||
@@ -468,7 +528,7 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
|
||||
};
|
||||
|
||||
auto get_callback_name = [](std::string_view function_name, unsigned param_index) -> std::string {
|
||||
return std::string { function_name } + "CBFN" + std::to_string(param_index);
|
||||
return fmt::format("{}CBFN{}", function_name, param_index);
|
||||
};
|
||||
|
||||
// Files used guest-side
|
||||
@@ -480,13 +540,8 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
|
||||
for (auto& thunk : thunks) {
|
||||
const auto& function_name = thunk.function_name;
|
||||
auto sha256 = get_sha256(function_name);
|
||||
file << "MAKE_THUNK(" << libname << ", " << function_name << ", \"";
|
||||
bool first = true;
|
||||
for (auto c : sha256) {
|
||||
file << (first ? "" : ", ") << "0x" << std::hex << std::setw(2) << std::setfill('0') << +c << std::dec;
|
||||
first = false;
|
||||
}
|
||||
file << "\")\n";
|
||||
fmt::print( file, "MAKE_THUNK({}, {}, \"{:#02x}\")\n",
|
||||
libname, function_name, fmt::join(sha256, ", "));
|
||||
}
|
||||
file << "}\n";
|
||||
|
||||
@@ -496,18 +551,12 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
|
||||
std::string funcptr_signature = clang::QualType { type, 0 }.getAsString();
|
||||
|
||||
auto cb_sha256 = get_sha256("fexcallback_" + funcptr_signature);
|
||||
std::stringstream cb_sha256_ss;
|
||||
for (auto c : cb_sha256) {
|
||||
cb_sha256_ss << "0x" << std::hex << std::setw(2) << std::setfill('0') << +c << ", ";
|
||||
}
|
||||
auto cb_sha256_str = std::move(cb_sha256_ss).str();
|
||||
cb_sha256_str.pop_back();
|
||||
cb_sha256_str.pop_back();
|
||||
|
||||
// Thunk used for guest-side calls to host function pointers
|
||||
file << " // " << funcptr_signature << "\n";
|
||||
auto funcptr_idx = std::distance(funcptr_types.begin(), type_it);
|
||||
file << " MAKE_CALLBACK_THUNK(callback_" << funcptr_idx << ", " << funcptr_signature << ", \"" << cb_sha256_str << "\");\n";
|
||||
fmt::print( file, " MAKE_CALLBACK_THUNK(callback_{}, {}, \"{:#02x}\");\n",
|
||||
funcptr_idx, funcptr_signature, fmt::join(cb_sha256, ", "));
|
||||
}
|
||||
|
||||
// Thunks-internal packing functions
|
||||
@@ -518,14 +567,14 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
|
||||
file << "FEX_PACKFN_LINKAGE auto fexfn_pack_" << function_name << "(";
|
||||
for (std::size_t idx = 0; idx < data.param_types.size(); ++idx) {
|
||||
auto& type = data.param_types[idx];
|
||||
file << (idx == 0 ? "" : ", ") << format_decl(type, "a_" + std::to_string(idx));
|
||||
file << (idx == 0 ? "" : ", ") << format_decl(type, fmt::format("a_{}", idx));
|
||||
}
|
||||
// Using trailing return type as it makes handling function pointer returns much easier
|
||||
file << ") -> " << data.return_type.getAsString() << " {\n";
|
||||
file << " struct {\n";
|
||||
for (std::size_t idx = 0; idx < data.param_types.size(); ++idx) {
|
||||
auto& type = data.param_types[idx];
|
||||
file << " " << format_decl(type.getUnqualifiedType(), "a_" + std::to_string(idx)) << ";\n";
|
||||
file << " " << format_decl(type.getUnqualifiedType(), fmt::format("a_{}", idx)) << ";\n";
|
||||
}
|
||||
if (!is_void) {
|
||||
file << " " << format_decl(data.return_type, "rv") << ";\n";
|
||||
@@ -543,7 +592,7 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
|
||||
file << "a_" << idx << ";\n";
|
||||
} else {
|
||||
// Before passing guest function pointers to the host, wrap them in a host-callable trampoline
|
||||
file << "AllocateHostTrampolineForGuestFunction(a_" << std::to_string(idx) << ");\n";
|
||||
fmt::print(file, "AllocateHostTrampolineForGuestFunction(a_{});\n", idx);
|
||||
}
|
||||
}
|
||||
file << " fexthunks_" << libname << "_" << function_name << "(&args);\n";
|
||||
@@ -628,7 +677,7 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
|
||||
if (cb != thunk.callbacks.end() && cb->second.is_guest) {
|
||||
file << "fex_guest_function_ptr a_" << idx;
|
||||
} else {
|
||||
file << format_decl(type, "a_" + std::to_string(idx));
|
||||
file << format_decl(type, fmt::format("a_{}", idx));
|
||||
}
|
||||
}
|
||||
// Using trailing return type as it makes handling function pointer returns much easier
|
||||
@@ -666,14 +715,14 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
|
||||
if (cb != thunk.callbacks.end() && cb->second.is_stub) {
|
||||
return "fexfn_unpack_" + get_callback_name(function_name, cb->first) + "_stub";
|
||||
} else if (cb != thunk.callbacks.end() && cb->second.is_guest) {
|
||||
return "fex_guest_function_ptr { args->a_" + std::to_string(idx) + " }";
|
||||
return fmt::format("fex_guest_function_ptr {{ args->a_{} }}", idx);
|
||||
} else if (cb != thunk.callbacks.end()) {
|
||||
auto arg_name = "args->a_" + std::to_string(idx);
|
||||
auto arg_name = fmt::format("args->a_{}", idx);
|
||||
// Use comma operator to inject a function call before returning the argument
|
||||
return "(FinalizeHostTrampolineForGuestFunction(" + arg_name + "), " + arg_name + ")";
|
||||
|
||||
} else {
|
||||
return "args->a_" + std::to_string(idx);
|
||||
return fmt::format("args->a_{}", idx);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -689,27 +738,16 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
|
||||
for (auto& thunk : thunks) {
|
||||
const auto& function_name = thunk.function_name;
|
||||
auto sha256 = get_sha256(function_name);
|
||||
|
||||
file << "{(uint8_t*)\"";
|
||||
for (auto c : sha256) {
|
||||
file << "\\x" << std::hex << std::setw(2) << std::setfill('0') << +c << std::dec;
|
||||
}
|
||||
file << "\", (void(*)(void *))&fexfn_unpack_" << libname << "_" << function_name << "}, // " << libname << ":" << function_name << "\n";
|
||||
fmt::print( file, " {{(uint8_t*)\"\\x{:02x}\", (void(*)(void *))&fexfn_unpack_{}_{}}}, // {}:{}\n",
|
||||
fmt::join(sha256, "\\x"), libname, function_name, libname, function_name);
|
||||
}
|
||||
|
||||
// Endpoints for Guest->Host invocation of runtime host-function pointers
|
||||
for (auto& type : funcptr_types) {
|
||||
std::string mangled_name = clang::QualType { type, 0 }.getAsString();
|
||||
{
|
||||
auto cb_sha256 = get_sha256("fexcallback_" + mangled_name);
|
||||
|
||||
std::stringstream cb_sha256_ss;
|
||||
for (auto c : cb_sha256) {
|
||||
cb_sha256_ss << "\\x" << std::hex << std::setw(2) << std::setfill('0') << +c;
|
||||
}
|
||||
auto cb_sha256_str = std::move(cb_sha256_ss).str();
|
||||
file << " {(uint8_t*)\"" << cb_sha256_str << "\", (void(*)(void *))&CallbackUnpack<" << mangled_name << ">::ForIndirectCall},\n";
|
||||
}
|
||||
auto cb_sha256 = get_sha256("fexcallback_" + mangled_name);
|
||||
fmt::print( file, " {{(uint8_t*)\"\\x{:02x}\", (void(*)(void *))&CallbackUnpack<{}>::ForIndirectCall}},\n",
|
||||
fmt::join(cb_sha256, "\\x"), mangled_name);
|
||||
}
|
||||
file << " { nullptr, nullptr }\n";
|
||||
file << "};\n";
|
||||
@@ -727,14 +765,18 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
|
||||
|
||||
file << " if (!fexldr_ptr_" << libname << "_so) { return false; }\n\n";
|
||||
for (auto& import : thunked_api) {
|
||||
file << " (void*&)fexldr_ptr_" << libname << "_" << import.function_name << " = " << import.host_loader << "(fexldr_ptr_" << libname << "_so, \"" << import.function_name << "\");\n";
|
||||
fmt::print( file, " (void*&)fexldr_ptr_{}_{} = {}(fexldr_ptr_{}_so, \"{}\");\n",
|
||||
libname, import.function_name, import.host_loader, libname, import.function_name);
|
||||
}
|
||||
file << " return true;\n";
|
||||
file << "}\n";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<clang::ASTConsumer> GenerateThunkLibsAction::CreateASTConsumer(clang::CompilerInstance&, clang::StringRef) {
|
||||
return std::make_unique<ASTConsumer>();
|
||||
}
|
||||
|
||||
std::unique_ptr<clang::FrontendAction> GenerateThunkLibsActionFactory::create() {
|
||||
return std::make_unique<GenerateThunkLibsAction>(libname, output_filenames);
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
#include <clang/Frontend/FrontendAction.h>
|
||||
#include <clang/Tooling/Tooling.h>
|
||||
|
||||
#include <optional>
|
||||
@@ -9,29 +8,13 @@ struct OutputFilenames {
|
||||
std::string guest;
|
||||
};
|
||||
|
||||
class GenerateThunkLibsAction : public clang::ASTFrontendAction {
|
||||
public:
|
||||
GenerateThunkLibsAction(const std::string& libname, const OutputFilenames&);
|
||||
|
||||
void EndSourceFileAction() override;
|
||||
|
||||
std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(clang::CompilerInstance&, clang::StringRef /*file*/) override;
|
||||
|
||||
private:
|
||||
const std::string& libfilename;
|
||||
std::string libname; // sanitized filename, usable as part of emitted function names
|
||||
const OutputFilenames& output_filenames;
|
||||
};
|
||||
|
||||
class GenerateThunkLibsActionFactory : public clang::tooling::FrontendActionFactory {
|
||||
public:
|
||||
GenerateThunkLibsActionFactory(std::string_view libname_, OutputFilenames output_filenames_)
|
||||
: libname(std::move(libname_)), output_filenames(std::move(output_filenames_)) {
|
||||
}
|
||||
|
||||
std::unique_ptr<clang::FrontendAction> create() override {
|
||||
return std::make_unique<GenerateThunkLibsAction>(libname, output_filenames);
|
||||
}
|
||||
std::unique_ptr<clang::FrontendAction> create() override;
|
||||
|
||||
private:
|
||||
std::string libname;
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(guest-thunks)
|
||||
|
||||
option(BITNESS "Which bitness the thunks are building for" 64)
|
||||
option(ENABLE_CLANG_THUNKS "Enable building thunks with clang" FALSE)
|
||||
|
||||
if (ENABLE_CLANG_THUNKS)
|
||||
set (LD_OVERRIDE "-fuse-ld=lld")
|
||||
add_link_options(${LD_OVERRIDE})
|
||||
endif()
|
||||
|
||||
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
# We've been included using ExternalProject_add, so set up the actual thunk libraries to be cross-compiled
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
@@ -10,6 +18,17 @@ if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
|
||||
set(TARGET_TYPE SHARED)
|
||||
set(GENERATE_GUEST_INSTALL_TARGETS TRUE)
|
||||
|
||||
# uninstall target
|
||||
if(NOT TARGET uninstall)
|
||||
configure_file(
|
||||
"${FEX_PROJECT_SOURCE_DIR}/CMakeFiles/cmake_uninstall.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/cmake_uninstall.cmake"
|
||||
IMMEDIATE @ONLY)
|
||||
|
||||
add_custom_target(uninstall
|
||||
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/cmake_uninstall.cmake)
|
||||
endif()
|
||||
else()
|
||||
# We've been included using add_subdirectory, so set up targets for IDE integration using the host toolchain
|
||||
set(GENERATOR_EXE thunkgen)
|
||||
@@ -37,11 +56,16 @@ function(generate NAME SOURCE_FILE)
|
||||
|
||||
file(MAKE_DIRECTORY "${OUTFOLDER}")
|
||||
|
||||
set (BITNESS_FLAGS "")
|
||||
if (BITNESS EQUAL 32)
|
||||
set (BITNESS_FLAGS "-m32" "--target=i686-linux-unknown" "-isystem" "/usr/i686-linux-gnu/include/")
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${OUTFILE}"
|
||||
DEPENDS "${GENERATOR_EXE}"
|
||||
DEPENDS "${SOURCE_FILE}"
|
||||
COMMAND "${GENERATOR_EXE}" "${SOURCE_FILE}" "${NAME}" "-guest" "${OUTFILE}" -- -std=c++17
|
||||
COMMAND "${GENERATOR_EXE}" "${SOURCE_FILE}" "${NAME}" "-guest" "${OUTFILE}" -- -std=c++17 ${BITNESS_FLAGS}
|
||||
# Expand compile definitions to space-separated list of -D parameters
|
||||
"$<$<BOOL:${compile_prop}>:;-D$<JOIN:${compile_prop},;-D>>"
|
||||
# Expand include directories to space-separated list of -isystem parameters
|
||||
@@ -61,6 +85,9 @@ function(add_guest_lib NAME SONAME)
|
||||
set (SOURCE_LDS_FILE ../lib${NAME}/lib${NAME}_Guest.lds)
|
||||
get_filename_component(SOURCE_LDS_FILE_ABS "${SOURCE_LDS_FILE}" ABSOLUTE)
|
||||
|
||||
set (SOURCE_LDS_32_FILE ../lib${NAME}/lib${NAME}_Guest_32.lds)
|
||||
get_filename_component(SOURCE_LDS_32_FILE_ABS "${SOURCE_LDS_32_FILE}" ABSOLUTE)
|
||||
|
||||
if (NOT EXISTS "${SOURCE_FILE_ABS}")
|
||||
set (SOURCE_FILE ../lib${NAME}/Guest.cpp)
|
||||
get_filename_component(SOURCE_FILE_ABS "${SOURCE_FILE}" ABSOLUTE)
|
||||
@@ -77,9 +104,21 @@ function(add_guest_lib NAME SONAME)
|
||||
## Make signed overflow well defined 2's complement overflow
|
||||
target_compile_options(${NAME}-guest PRIVATE -fwrapv)
|
||||
|
||||
if (BITNESS EQUAL 32)
|
||||
# Makes the GOT/PLT lookups slightly less painful
|
||||
target_compile_options(${NAME}-guest PRIVATE -fno-plt)
|
||||
target_link_options(VDSO-guest PRIVATE "LINKER:-z,now" "LINKER:-z,relro" "LINKER:-z,notext")
|
||||
endif()
|
||||
|
||||
# Add linker script if set
|
||||
if (EXISTS "${SOURCE_LDS_FILE_ABS}")
|
||||
if (BITNESS EQUAL 64 AND EXISTS "${SOURCE_LDS_FILE_ABS}")
|
||||
target_link_options(${NAME}-guest PRIVATE "-T" "${CMAKE_CURRENT_SOURCE_DIR}/../lib${NAME}/lib${NAME}_Guest.lds")
|
||||
set_property(TARGET ${NAME}-guest APPEND PROPERTY LINK_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../lib${NAME}/lib${NAME}_Guest.lds")
|
||||
endif()
|
||||
|
||||
if (BITNESS EQUAL 32 AND EXISTS "${SOURCE_LDS_32_FILE_ABS}")
|
||||
target_link_options(${NAME}-guest PRIVATE "-T" "${CMAKE_CURRENT_SOURCE_DIR}/../lib${NAME}/lib${NAME}_Guest_32.lds")
|
||||
set_property(TARGET ${NAME}-guest APPEND PROPERTY LINK_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../lib${NAME}/lib${NAME}_Guest_32.lds")
|
||||
endif()
|
||||
|
||||
# We need to override the soname for the linker.
|
||||
@@ -100,108 +139,119 @@ function(add_guest_lib NAME SONAME)
|
||||
set_target_properties(${NAME}-guest PROPERTIES NO_SONAME ON)
|
||||
|
||||
if (GENERATE_GUEST_INSTALL_TARGETS)
|
||||
install(TARGETS ${NAME}-guest DESTINATION ${DATA_DIRECTORY}/GuestThunks/)
|
||||
if (BITNESS EQUAL 64)
|
||||
install(TARGETS ${NAME}-guest DESTINATION ${DATA_DIRECTORY}/GuestThunks/)
|
||||
else()
|
||||
install(TARGETS ${NAME}-guest DESTINATION ${DATA_DIRECTORY}/GuestThunks_32/)
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
#add_guest_lib(fex_malloc_loader)
|
||||
#target_link_libraries(fex_malloc_loader-guest PRIVATE dl)
|
||||
# These thunks only support 64-bit
|
||||
if (BITNESS EQUAL 64)
|
||||
#add_guest_lib(fex_malloc_loader)
|
||||
#target_link_libraries(fex_malloc_loader-guest PRIVATE dl)
|
||||
|
||||
#generate(libfex_malloc)
|
||||
#add_guest_lib(fex_malloc)
|
||||
#generate(libfex_malloc)
|
||||
#add_guest_lib(fex_malloc)
|
||||
|
||||
generate(libasound ${CMAKE_CURRENT_SOURCE_DIR}/../libasound/libasound_interface.cpp)
|
||||
add_guest_lib(asound "libasound.so.2")
|
||||
generate(libasound ${CMAKE_CURRENT_SOURCE_DIR}/../libasound/libasound_interface.cpp)
|
||||
add_guest_lib(asound "libasound.so.2")
|
||||
|
||||
generate(libEGL ${CMAKE_CURRENT_SOURCE_DIR}/../libEGL/libEGL_interface.cpp)
|
||||
add_guest_lib(EGL "libEGL.so.1")
|
||||
generate(libEGL ${CMAKE_CURRENT_SOURCE_DIR}/../libEGL/libEGL_interface.cpp)
|
||||
add_guest_lib(EGL "libEGL.so.1")
|
||||
|
||||
generate(libGL ${CMAKE_CURRENT_SOURCE_DIR}/../libGL/libGL_interface.cpp)
|
||||
add_guest_lib(GL "libGL.so.1")
|
||||
generate(libGL ${CMAKE_CURRENT_SOURCE_DIR}/../libGL/libGL_interface.cpp)
|
||||
add_guest_lib(GL "libGL.so.1")
|
||||
|
||||
# libGL must pull in libX11.so, so generate a placeholder libX11.so to link against
|
||||
add_library(X11 SHARED ../libX11/libX11_NativeGuest.cpp)
|
||||
target_link_libraries(GL-guest PRIVATE X11)
|
||||
# libGL must pull in libX11.so, so generate a placeholder libX11.so to link against
|
||||
add_library(X11 SHARED ../libX11/libX11_NativeGuest.cpp)
|
||||
target_link_libraries(GL-guest PRIVATE X11)
|
||||
|
||||
# disabled for now, headers are platform specific
|
||||
# find_package(SDL2 REQUIRED)
|
||||
# generate(libSDL2)
|
||||
# add_guest_lib(SDL2)
|
||||
# target_include_directories(SDL2-guest PRIVATE ${SDL2_INCLUDE_DIRS})
|
||||
# target_link_libraries(SDL2-guest PRIVATE GL)
|
||||
# target_link_libraries(SDL2-guest PRIVATE dl)
|
||||
# disabled for now, headers are platform specific
|
||||
# find_package(SDL2 REQUIRED)
|
||||
# generate(libSDL2)
|
||||
# add_guest_lib(SDL2)
|
||||
# target_include_directories(SDL2-guest PRIVATE ${SDL2_INCLUDE_DIRS})
|
||||
# target_link_libraries(SDL2-guest PRIVATE GL)
|
||||
# target_link_libraries(SDL2-guest PRIVATE dl)
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_search_module(X11 REQUIRED x11)
|
||||
find_package(PkgConfig)
|
||||
pkg_search_module(X11 REQUIRED x11)
|
||||
|
||||
string(REGEX MATCH "([0-9]*)\.([0-9]*)\.([0-9]*)" _ "${X11_VERSION}")
|
||||
set(X11_VERSION_MAJOR ${CMAKE_MATCH_1})
|
||||
set(X11_VERSION_MINOR ${CMAKE_MATCH_2})
|
||||
set(X11_VERSION_PATCH ${CMAKE_MATCH_3})
|
||||
string(REGEX MATCH "([0-9]*)\.([0-9]*)\.([0-9]*)" _ "${X11_VERSION}")
|
||||
set(X11_VERSION_MAJOR ${CMAKE_MATCH_1})
|
||||
set(X11_VERSION_MINOR ${CMAKE_MATCH_2})
|
||||
set(X11_VERSION_PATCH ${CMAKE_MATCH_3})
|
||||
|
||||
generate(libX11 ${CMAKE_CURRENT_SOURCE_DIR}/../libX11/libX11_interface.cpp)
|
||||
add_guest_lib(X11 "libX11.so.6")
|
||||
generate(libX11 ${CMAKE_CURRENT_SOURCE_DIR}/../libX11/libX11_interface.cpp)
|
||||
add_guest_lib(X11 "libX11.so.6")
|
||||
|
||||
target_compile_definitions(libX11-guest-deps INTERFACE -DX11_VERSION_MAJOR=${X11_VERSION_MAJOR})
|
||||
target_compile_definitions(libX11-guest-deps INTERFACE -DX11_VERSION_MINOR=${X11_VERSION_MINOR})
|
||||
target_compile_definitions(libX11-guest-deps INTERFACE -DX11_VERSION_PATCH=${X11_VERSION_PATCH})
|
||||
target_compile_definitions(libX11-guest-deps INTERFACE -DX11_VERSION_MAJOR=${X11_VERSION_MAJOR})
|
||||
target_compile_definitions(libX11-guest-deps INTERFACE -DX11_VERSION_MINOR=${X11_VERSION_MINOR})
|
||||
target_compile_definitions(libX11-guest-deps INTERFACE -DX11_VERSION_PATCH=${X11_VERSION_PATCH})
|
||||
|
||||
generate(libXext ${CMAKE_CURRENT_SOURCE_DIR}/../libXext/libXext_interface.cpp)
|
||||
add_guest_lib(Xext "libXext.so.6")
|
||||
generate(libXext ${CMAKE_CURRENT_SOURCE_DIR}/../libXext/libXext_interface.cpp)
|
||||
add_guest_lib(Xext "libXext.so.6")
|
||||
|
||||
target_compile_definitions(libXext-guest-deps INTERFACE -DX11_VERSION_MAJOR=${X11_VERSION_MAJOR})
|
||||
target_compile_definitions(libXext-guest-deps INTERFACE -DX11_VERSION_MINOR=${X11_VERSION_MINOR})
|
||||
target_compile_definitions(libXext-guest-deps INTERFACE -DX11_VERSION_PATCH=${X11_VERSION_PATCH})
|
||||
target_compile_definitions(libXext-guest-deps INTERFACE -DX11_VERSION_MAJOR=${X11_VERSION_MAJOR})
|
||||
target_compile_definitions(libXext-guest-deps INTERFACE -DX11_VERSION_MINOR=${X11_VERSION_MINOR})
|
||||
target_compile_definitions(libXext-guest-deps INTERFACE -DX11_VERSION_PATCH=${X11_VERSION_PATCH})
|
||||
|
||||
generate(libXrender ${CMAKE_CURRENT_SOURCE_DIR}/../libXrender/libXrender_interface.cpp)
|
||||
add_guest_lib(Xrender "libXrender.so.1")
|
||||
generate(libXrender ${CMAKE_CURRENT_SOURCE_DIR}/../libXrender/libXrender_interface.cpp)
|
||||
add_guest_lib(Xrender "libXrender.so.1")
|
||||
|
||||
generate(libXfixes ${CMAKE_CURRENT_SOURCE_DIR}/../libXfixes/libXfixes_interface.cpp)
|
||||
add_guest_lib(Xfixes "libXfixes.so.3")
|
||||
generate(libXfixes ${CMAKE_CURRENT_SOURCE_DIR}/../libXfixes/libXfixes_interface.cpp)
|
||||
add_guest_lib(Xfixes "libXfixes.so.3")
|
||||
|
||||
generate(libvulkan ${CMAKE_CURRENT_SOURCE_DIR}/../libvulkan/libvulkan_interface.cpp)
|
||||
target_include_directories(libvulkan-guest-deps INTERFACE ${FEX_PROJECT_SOURCE_DIR}/External/Vulkan-Headers/include/)
|
||||
add_guest_lib(vulkan "libvulkan.so.1")
|
||||
generate(libvulkan ${CMAKE_CURRENT_SOURCE_DIR}/../libvulkan/libvulkan_interface.cpp)
|
||||
target_include_directories(libvulkan-guest-deps INTERFACE ${FEX_PROJECT_SOURCE_DIR}/External/Vulkan-Headers/include/)
|
||||
add_guest_lib(vulkan "libvulkan.so.1")
|
||||
|
||||
generate(libxcb ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb/libxcb_interface.cpp)
|
||||
add_guest_lib(xcb "libxcb.so.1")
|
||||
generate(libxcb ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb/libxcb_interface.cpp)
|
||||
add_guest_lib(xcb "libxcb.so.1")
|
||||
|
||||
generate(libxcb-dri2 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri2/libxcb-dri2_interface.cpp)
|
||||
add_guest_lib(xcb-dri2 "libxcb-dri2.so.0")
|
||||
generate(libxcb-dri2 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri2/libxcb-dri2_interface.cpp)
|
||||
add_guest_lib(xcb-dri2 "libxcb-dri2.so.0")
|
||||
|
||||
generate(libxcb-dri3 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri3/libxcb-dri3_interface.cpp)
|
||||
add_guest_lib(xcb-dri3 "libxcb-dri3.so.0")
|
||||
generate(libxcb-dri3 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri3/libxcb-dri3_interface.cpp)
|
||||
add_guest_lib(xcb-dri3 "libxcb-dri3.so.0")
|
||||
|
||||
generate(libxcb-xfixes ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-xfixes/libxcb-xfixes_interface.cpp)
|
||||
add_guest_lib(xcb-xfixes "libxcb-xfixes.so.0")
|
||||
generate(libxcb-xfixes ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-xfixes/libxcb-xfixes_interface.cpp)
|
||||
add_guest_lib(xcb-xfixes "libxcb-xfixes.so.0")
|
||||
|
||||
generate(libxcb-shm ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-shm/libxcb-shm_interface.cpp)
|
||||
add_guest_lib(xcb-shm "libxcb-shm.so.0")
|
||||
generate(libxcb-shm ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-shm/libxcb-shm_interface.cpp)
|
||||
add_guest_lib(xcb-shm "libxcb-shm.so.0")
|
||||
|
||||
generate(libxcb-sync ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-sync/libxcb-sync_interface.cpp)
|
||||
add_guest_lib(xcb-sync "libxcb-sync.so.1")
|
||||
generate(libxcb-sync ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-sync/libxcb-sync_interface.cpp)
|
||||
add_guest_lib(xcb-sync "libxcb-sync.so.1")
|
||||
|
||||
generate(libxcb-present ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-present/libxcb-present_interface.cpp)
|
||||
add_guest_lib(xcb-present "libxcb-present.so.0")
|
||||
generate(libxcb-present ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-present/libxcb-present_interface.cpp)
|
||||
add_guest_lib(xcb-present "libxcb-present.so.0")
|
||||
|
||||
generate(libxcb-randr ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-randr/libxcb-randr_interface.cpp)
|
||||
add_guest_lib(xcb-randr "libxcb-randr.so.0")
|
||||
generate(libxcb-randr ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-randr/libxcb-randr_interface.cpp)
|
||||
add_guest_lib(xcb-randr "libxcb-randr.so.0")
|
||||
|
||||
generate(libxcb-glx ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-glx/libxcb-glx_interface.cpp)
|
||||
add_guest_lib(xcb-glx "libxcb-glx.so.0")
|
||||
generate(libxcb-glx ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-glx/libxcb-glx_interface.cpp)
|
||||
add_guest_lib(xcb-glx "libxcb-glx.so.0")
|
||||
|
||||
generate(libxshmfence ${CMAKE_CURRENT_SOURCE_DIR}/../libxshmfence/libxshmfence_interface.cpp)
|
||||
add_guest_lib(xshmfence "libxshmfence.so.1")
|
||||
generate(libxshmfence ${CMAKE_CURRENT_SOURCE_DIR}/../libxshmfence/libxshmfence_interface.cpp)
|
||||
add_guest_lib(xshmfence "libxshmfence.so.1")
|
||||
|
||||
generate(libdrm ${CMAKE_CURRENT_SOURCE_DIR}/../libdrm/libdrm_interface.cpp)
|
||||
target_include_directories(libdrm-guest-deps INTERFACE /usr/include/drm/)
|
||||
target_include_directories(libdrm-guest-deps INTERFACE /usr/include/libdrm/)
|
||||
add_guest_lib(drm "libdrm.so.2")
|
||||
generate(libdrm ${CMAKE_CURRENT_SOURCE_DIR}/../libdrm/libdrm_interface.cpp)
|
||||
target_include_directories(libdrm-guest-deps INTERFACE /usr/include/drm/)
|
||||
target_include_directories(libdrm-guest-deps INTERFACE /usr/include/libdrm/)
|
||||
add_guest_lib(drm "libdrm.so.2")
|
||||
endif()
|
||||
|
||||
generate(libVDSO ${CMAKE_CURRENT_SOURCE_DIR}/../libVDSO/libVDSO_interface.cpp)
|
||||
add_guest_lib(VDSO "linux-vdso.so.1")
|
||||
# Can't use a stack protector because otherwise cross-compiling fails
|
||||
# Not necessary anyway because it only trampolines
|
||||
target_compile_options(VDSO-guest PRIVATE "-fno-stack-protector")
|
||||
target_link_options(VDSO-guest PRIVATE "-T" "${CMAKE_CURRENT_SOURCE_DIR}/../libVDSO/libVDSO_Guest.lds" "-nostdlib"
|
||||
"LINKER:--no-undefined" "LINKER:-z,max-page-size=4096" "LINKER:--hash-style=both")
|
||||
target_link_options(VDSO-guest PRIVATE "-nostdlib" "LINKER:--no-undefined" "LINKER:-z,max-page-size=4096" "LINKER:--hash-style=both")
|
||||
|
||||
if (BITNESS EQUAL 32)
|
||||
# 32-bit entrypoint points to __kernel_vsyscall and needs to exist
|
||||
target_link_options(VDSO-guest PRIVATE "LINKER:-e,__kernel_vsyscall")
|
||||
endif()
|
||||
@@ -3,6 +3,12 @@ project(host-thunks)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set (HOSTLIBS_DATA_DIRECTORY "${CMAKE_INSTALL_PREFIX}/lib/fex-emu" CACHE PATH "global data directory")
|
||||
option(ENABLE_CLANG_THUNKS "Enable building thunks with clang" FALSE)
|
||||
|
||||
if (ENABLE_CLANG_THUNKS)
|
||||
set (LD_OVERRIDE "-fuse-ld=lld")
|
||||
add_link_options(${LD_OVERRIDE})
|
||||
endif()
|
||||
|
||||
# Syntax: generate(libxyz libxyz-interface.cpp)
|
||||
# This defines two targets and a custom command:
|
||||
|
||||
@@ -4,18 +4,29 @@
|
||||
|
||||
#include "PackedArguments.h"
|
||||
|
||||
#if __SIZEOF_POINTER__ == 8
|
||||
#define THUNK_ABI
|
||||
#else
|
||||
#ifdef __clang__
|
||||
#define THUNK_ABI __fastcall
|
||||
#else
|
||||
#define THUNK_ABI [[gnu::fastcall]]
|
||||
#endif
|
||||
#endif
|
||||
|
||||
template<typename signature>
|
||||
THUNK_ABI
|
||||
const int (*fexthunks_invoke_callback)(void*);
|
||||
|
||||
#ifndef _M_ARM_64
|
||||
#define MAKE_THUNK(lib, name, hash) \
|
||||
extern "C" int fexthunks_##lib##_##name(void *args); \
|
||||
extern "C" __attribute__((visibility("hidden"))) THUNK_ABI int fexthunks_##lib##_##name(void *args); \
|
||||
asm(".text\nfexthunks_" #lib "_" #name ":\n.byte 0xF, 0x3F\n.byte " hash );
|
||||
|
||||
#define MAKE_CALLBACK_THUNK(name, signature, hash) \
|
||||
extern "C" int fexthunks_##name(void *args); \
|
||||
extern "C" __attribute__((visibility("hidden"))) THUNK_ABI int fexthunks_##name(void *args); \
|
||||
asm(".text\nfexthunks_" #name ":\n.byte 0xF, 0x3F\n.byte " hash ); \
|
||||
template<> inline constexpr int (*fexthunks_invoke_callback<signature>)(void*) = fexthunks_##name;
|
||||
template<> THUNK_ABI inline constexpr int (*fexthunks_invoke_callback<signature>)(void*) = fexthunks_##name;
|
||||
|
||||
#else
|
||||
// We're compiling for IDE integration, so provide a dummy-implementation that just calls an undefined function.
|
||||
@@ -112,14 +123,14 @@ inline Result CallHostFunction(Args... args) {
|
||||
// Convenience wrapper that returns the function pointer to a CallHostFunction
|
||||
// instantiation matching the function signature of `host_func`
|
||||
template<typename Result, typename...Args>
|
||||
static auto GetCallerForHostFunction(Result (*host_func)(Args...))
|
||||
-> Result(*)(Args...) {
|
||||
return &CallHostFunction<fexthunks_invoke_callback<Result(Args...)>, Result, Args...>;
|
||||
static auto GetCallerForHostFunction(THUNK_ABI Result (*host_func)(Args...))
|
||||
-> THUNK_ABI Result(*)(Args...) {
|
||||
return &CallHostFunction<fexthunks_invoke_callback<THUNK_ABI Result(Args...)>, Result, Args...>;
|
||||
}
|
||||
|
||||
// Ensures the given host function can safely be called from guest code.
|
||||
template<typename Result, typename...Args>
|
||||
inline void MakeHostFunctionGuestCallable(Result (*host_func)(Args...)) {
|
||||
inline void MakeHostFunctionGuestCallable(THUNK_ABI Result (*host_func)(Args...)) {
|
||||
auto caller = (uintptr_t)GetCallerForHostFunction(host_func);
|
||||
LinkAddressToFunction((uintptr_t)host_func, (uintptr_t)caller);
|
||||
}
|
||||
|
||||
@@ -61,13 +61,17 @@ typedef void fex_call_callback_t(uintptr_t callback, void *arg0, void* arg1);
|
||||
*/
|
||||
struct fex_guest_function_ptr {
|
||||
private:
|
||||
[[maybe_unused]] void* value = nullptr;
|
||||
void* value = nullptr;
|
||||
|
||||
public:
|
||||
fex_guest_function_ptr() = default;
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
fex_guest_function_ptr(Ret (*ptr)(Args...)) : value(reinterpret_cast<void*>(ptr)) {}
|
||||
|
||||
inline operator bool() const {
|
||||
return value != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
#define EXPORTS(name) \
|
||||
|
||||
@@ -34,9 +34,10 @@ template<typename R, typename A0, typename A1, typename A2, typename A3, typenam
|
||||
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; R rv; };
|
||||
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12>
|
||||
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; R rv; };
|
||||
|
||||
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13>
|
||||
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; R rv; };
|
||||
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14>
|
||||
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; R rv; };
|
||||
|
||||
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9,
|
||||
typename A10, typename A11, typename A12, typename A13, typename A14, typename A15, typename A16, typename A17, typename A18, typename A19,
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
struct timespec64 {
|
||||
int64_t tv_sec;
|
||||
int64_t tv_nsec;
|
||||
};
|
||||
@@ -13,6 +13,7 @@ $end_info$
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "Types.h"
|
||||
#include "common/Guest.h"
|
||||
|
||||
#include "thunkgen_guest_libVDSO.inl"
|
||||
@@ -23,4 +24,19 @@ int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz) __attribute__((
|
||||
int __vdso_clock_gettime(clockid_t, struct timespec *) __attribute__((alias("fexfn_pack_clock_gettime")));
|
||||
int __vdso_clock_getres(clockid_t, struct timespec *) __attribute__((alias("fexfn_pack_clock_getres")));
|
||||
int __vdso_getcpu(uint32_t *, uint32_t *) __attribute__((alias("fexfn_pack_getcpu")));
|
||||
|
||||
#if __SIZEOF_POINTER__ == 4
|
||||
int __vdso_clock_gettime64(clockid_t, struct timespec64 *) __attribute__((alias("fexfn_pack_clock_gettime64")));
|
||||
|
||||
__attribute__((naked))
|
||||
int __kernel_vsyscall() {
|
||||
asm volatile(R"(
|
||||
.intel_syntax noprefix
|
||||
int 0x80;
|
||||
ret;
|
||||
.att_syntax prefix
|
||||
)"
|
||||
::: "memory");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -29,9 +29,9 @@ SECTIONS {
|
||||
}
|
||||
|
||||
PHDRS {
|
||||
text PT_LOAD FLAGS(PF_R | PF_X) FILEHDR PHDRS;
|
||||
dynamic PT_DYNAMIC FLAGS(PF_R);
|
||||
note PT_NOTE FLAGS(PF_R);
|
||||
text PT_LOAD FLAGS(4 | 1) FILEHDR PHDRS;
|
||||
dynamic PT_DYNAMIC FLAGS(4);
|
||||
note PT_NOTE FLAGS(4);
|
||||
}
|
||||
|
||||
VERSION {
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
SECTIONS {
|
||||
. = SIZEOF_HEADERS;
|
||||
.hash : { *(.hash) } :text
|
||||
.gnu.hash : { *(.gnu.hash) }
|
||||
.dynsym : { *(.dynsym) }
|
||||
.dynstr : { *(.dynstr) }
|
||||
.gnu.version : { *(.gnu.version) }
|
||||
.gnu.version_d : { *(.gnu.version_d) }
|
||||
.gnu.version_r : { *(.gnu.version_r) }
|
||||
.dynamic : { *(.dynamic) } :text :dynamic
|
||||
.rodata : {
|
||||
*(.rodata*)
|
||||
*(.data*)
|
||||
*(.sdata*)
|
||||
*(.got.plt) *(.got)
|
||||
*(.gnu.linkonce.d.*)
|
||||
*(.bss*)
|
||||
*(.dynbss*)
|
||||
*(.gnu.linkonce.b.*)
|
||||
} :text
|
||||
|
||||
/DISCARD/ : {
|
||||
*(.note)
|
||||
*(.note.gnu.property)
|
||||
*(.eh_frame_hdr)
|
||||
*(.eh_frame)
|
||||
*(.symtab)
|
||||
}
|
||||
}
|
||||
|
||||
PHDRS {
|
||||
text PT_LOAD FLAGS(4 | 1) FILEHDR PHDRS;
|
||||
dynamic PT_DYNAMIC FLAGS(4);
|
||||
note PT_NOTE FLAGS(4);
|
||||
}
|
||||
|
||||
VERSION {
|
||||
LINUX_2.6 {
|
||||
global:
|
||||
__vdso_time;
|
||||
time;
|
||||
__vdso_gettimeofday;
|
||||
gettimeofday;
|
||||
__vdso_clock_gettime;
|
||||
clock_gettime;
|
||||
__vdso_clock_getres;
|
||||
clock_getres;
|
||||
__vdso_getcpu;
|
||||
getcpu;
|
||||
__vdso_clock_gettime64;
|
||||
clock_gettime64;
|
||||
local: *;
|
||||
};
|
||||
LINUX_2.5 {
|
||||
global:
|
||||
__kernel_vsyscall;
|
||||
local: *;
|
||||
};
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
template<auto>
|
||||
struct fex_gen_config {
|
||||
};
|
||||
@@ -13,3 +15,8 @@ template<> struct fex_gen_config<gettimeofday> {};
|
||||
template<> struct fex_gen_config<clock_gettime> {};
|
||||
template<> struct fex_gen_config<clock_getres> {};
|
||||
template<> struct fex_gen_config<getcpu> {};
|
||||
|
||||
#if __SIZEOF_POINTER__ == 4
|
||||
extern int clock_gettime64 (clockid_t __clock_id, struct timespec64 *__tp) __THROW;
|
||||
template<> struct fex_gen_config<clock_gettime64> {};
|
||||
#endif
|
||||
@@ -601,10 +601,12 @@ template<> struct fex_gen_config<XESetError> : fexgen::returns_guest_pointer {};
|
||||
|
||||
template<> struct fex_gen_config<XSetIOErrorHandler> : fexgen::returns_guest_pointer {};
|
||||
|
||||
#if __SIZEOF_POINTER__ == 8
|
||||
template<> struct fex_gen_config<_XData32> {};
|
||||
template<> struct fex_gen_config<_XRead32> {};
|
||||
#endif
|
||||
template<> struct fex_gen_config<_XRead> {};
|
||||
template<> struct fex_gen_config<_XReadPad> {};
|
||||
template<> struct fex_gen_config<_XData32> {};
|
||||
template<> struct fex_gen_config<_XEatData> {};
|
||||
template<> struct fex_gen_config<_XEatDataWords> {};
|
||||
|
||||
|
||||
@@ -126,8 +126,10 @@ template<> struct fex_gen_config<XextRemoveDisplay> {};
|
||||
template<> struct fex_gen_config<XextFindDisplay> {};
|
||||
template<> struct fex_gen_config<_XGetRequest> {};
|
||||
template<> struct fex_gen_config<_XFlushGCCache> {};
|
||||
#if __SIZEOF_POINTER__ == 8
|
||||
template<> struct fex_gen_config<_XData32> {};
|
||||
template<> struct fex_gen_config<_XRead32> {};
|
||||
#endif
|
||||
template<> struct fex_gen_config<_XDeqAsyncHandler> {};
|
||||
template<> struct fex_gen_config<_XError> {};
|
||||
template<> struct fex_gen_config<_XIOError> {};
|
||||
|
||||
@@ -56,7 +56,7 @@ static PFN_vkVoidFunction MakeGuestCallable(const char* origin, PFN_vkVoidFuncti
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
fprintf(stderr, "Linking address %p to host invoker %#lx\n", func, It->second);
|
||||
fprintf(stderr, "Linking address %p to host invoker %#zx\n", func, It->second);
|
||||
LinkAddressToFunction((uintptr_t)func, It->second);
|
||||
return func;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#include <common/GeneratorInterface.h>
|
||||
|
||||
template<auto>
|
||||
struct fex_gen_config;
|
||||
struct fex_gen_config {
|
||||
unsigned version = 1;
|
||||
};
|
||||
|
||||
#define VK_USE_PLATFORM_XLIB_XRANDR_EXT
|
||||
#define VK_USE_PLATFORM_XLIB_KHR
|
||||
|
||||
+15
-11
@@ -16,20 +16,29 @@ There are multiple locations that need to be updated during a release
|
||||
|
||||
## Github Steps
|
||||
* Check out the commit that will be the branch
|
||||
$ git checkout upstream/main
|
||||
|
||||
$ git checkout upstream/main
|
||||
|
||||
* Make local main branch be the selected commit
|
||||
$ git branch -D main
|
||||
$ git checkout -b main
|
||||
|
||||
$ git branch -D main
|
||||
$ git checkout -b main
|
||||
|
||||
* Run the release script
|
||||
$ Scripts/generate_release.sh
|
||||
|
||||
$ Scripts/generate_release.sh
|
||||
|
||||
* Push the branches upstream
|
||||
* This requires administrative push rights
|
||||
* Both the tag and the main branch needs to be committed
|
||||
$ git push upstream $CURRENT
|
||||
$ git push upstream main
|
||||
|
||||
$ git push upstream $CURRENT
|
||||
$ git push upstream main
|
||||
|
||||
## Launchpad PPA steps
|
||||
Follow the steps in: https://github.com/FEX-Emu/FEX-ppa/blob/main/README.md
|
||||
* Requires PPA GPG key signing access
|
||||
* Wait the 20-30 minutes for Ubuntu PPA to build and publish the binaries
|
||||
|
||||
## Github releases page Steps
|
||||
* Requires administrative rights
|
||||
@@ -55,11 +64,6 @@ $ git push upstream main
|
||||
* Jekyll will automatically regenerate the website with a github action
|
||||
* Verify that the post shows up on the site at fex-emu.com
|
||||
|
||||
## Launchpad PPA steps
|
||||
Follow the steps in: https://github.com/FEX-Emu/FEX-ppa/blob/main/README_ppa.md
|
||||
* Requires PPA GPG key signing access
|
||||
* Wait the 20-30 minutes for Ubuntu PPA to build and publish the binaries
|
||||
|
||||
## Termux package update steps
|
||||
* Clone https://github.com/termux/termux-packages
|
||||
* Update the package script with thew new version tag (https://github.com/termux/termux-packages/blob/master/packages/fex/build.sh)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# FEX-2209
|
||||
# FEX-2210
|
||||
|
||||
## External/FEXCore
|
||||
See [FEXCore/Readme.md](../External/FEXCore/Readme.md) for more details
|
||||
@@ -210,6 +210,10 @@ These are generated + glue logic 1:1 thunks unless noted otherwise
|
||||
- [libasound_Guest.cpp](../ThunkLibs/libasound/libasound_Guest.cpp)
|
||||
- [libasound_Host.cpp](../ThunkLibs/libasound/libasound_Host.cpp)
|
||||
|
||||
#### cef
|
||||
- [libcef_Guest.cpp](../ThunkLibs/libcef/libcef_Guest.cpp)
|
||||
- [libcef_Host.cpp](../ThunkLibs/libcef/libcef_Host.cpp)
|
||||
|
||||
#### drm
|
||||
- [Guest.cpp](../ThunkLibs/libdrm/Guest.cpp)
|
||||
- [Host.cpp](../ThunkLibs/libdrm/Host.cpp)
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
set(CMAKE_C_COMPILER x86_64-linux-gnu-gcc)
|
||||
set(CMAKE_CXX_COMPILER x86_64-linux-gnu-g++)
|
||||
@@ -0,0 +1,20 @@
|
||||
option(ENABLE_CLANG_THUNKS "Enable building thunks with clang" FALSE)
|
||||
|
||||
set(CMAKE_SYSTEM_PROCESSOR i686)
|
||||
|
||||
if (ENABLE_CLANG_THUNKS)
|
||||
message(STATUS "Enabling thunk clang building. Force enabling LLD as well")
|
||||
|
||||
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_C_COMPILER clang)
|
||||
set(CMAKE_CXX_COMPILER clang++)
|
||||
set(CLANG_FLAGS "-target i686-linux-gnu -msse2 -mfpmath=sse")
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CLANG_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CLANG_FLAGS}")
|
||||
else()
|
||||
set(CMAKE_C_COMPILER x86_64-linux-gnu-gcc -m32)
|
||||
set(CMAKE_CXX_COMPILER x86_64-linux-gnu-g++ -m32)
|
||||
endif()
|
||||
@@ -0,0 +1,20 @@
|
||||
option(ENABLE_CLANG_THUNKS "Enable building thunks with clang" FALSE)
|
||||
|
||||
set(CMAKE_SYSTEM_PROCESSOR x86_64)
|
||||
|
||||
if (ENABLE_CLANG_THUNKS)
|
||||
message(STATUS "Enabling thunk clang building. Force enabling LLD as well")
|
||||
|
||||
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_C_COMPILER clang)
|
||||
set(CMAKE_CXX_COMPILER clang++)
|
||||
set(CLANG_FLAGS "-target x86_64-linux-gnu")
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CLANG_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CLANG_FLAGS}")
|
||||
else()
|
||||
set(CMAKE_C_COMPILER x86_64-linux-gnu-gcc)
|
||||
set(CMAKE_CXX_COMPILER x86_64-linux-gnu-g++)
|
||||
endif()
|
||||
@@ -54,12 +54,6 @@ foreach(ASM_SRC ${ASM_SOURCES})
|
||||
"--no-silent -g -c irjit -n 500 --no-multiblock" "jit_500" "jit"
|
||||
"--no-silent -g -c irjit -n 500 --multiblock" "jit_500_m" "jit"
|
||||
)
|
||||
if (_M_X86_64)
|
||||
list(APPEND TEST_ARGS
|
||||
"--no-silent -g -c host" "host" "host"
|
||||
)
|
||||
endif()
|
||||
|
||||
if (ENABLE_INTERPRETER)
|
||||
list(APPEND TEST_ARGS
|
||||
"--no-silent -g -c irint -n 1 --no-multiblock" "int_1" "int"
|
||||
@@ -68,6 +62,16 @@ foreach(ASM_SRC ${ASM_SOURCES})
|
||||
)
|
||||
endif()
|
||||
|
||||
if (ENABLE_VIXL_SIMULATOR)
|
||||
set(CPU_CLASS Simulator)
|
||||
else()
|
||||
if (_M_X86_64)
|
||||
list(APPEND TEST_ARGS
|
||||
"--no-silent -g -c host" "host" "host"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
list(LENGTH TEST_ARGS ARG_COUNT)
|
||||
math(EXPR ARG_COUNT "${ARG_COUNT}-1")
|
||||
foreach(Index RANGE 0 ${ARG_COUNT} 3)
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
# AES unsupported in simulator
|
||||
Test_H0F38/66_DB.asm
|
||||
Test_H0F38/66_DC.asm
|
||||
Test_H0F38/66_DD.asm
|
||||
Test_H0F38/66_DE.asm
|
||||
Test_H0F38/66_DF.asm
|
||||
Test_H0F3A/0_66_DF.asm
|
||||
|
||||
# PCMUL considered to be part of crypto operations. Simulator doesn't support this.
|
||||
Test_H0F3A/pclmulqdq.asm
|
||||
Test_VEX/vpclmulqdq.asm
|
||||
|
||||
# Simulator can't handle self-modifying code
|
||||
Test_SelfModifyingCode/Delinking.asm
|
||||
Test_SelfModifyingCode/DifferentBlock.asm
|
||||
Test_SelfModifyingCode/SameBlock.asm
|
||||
|
||||
# Simulator can't handle unaligned accesses
|
||||
Test_Primary/Primary_01_Atomic16.asm
|
||||
Test_Primary/Primary_01_Atomic32.asm
|
||||
Test_Primary/Primary_01_Atomic64.asm
|
||||
Test_Primary/Primary_09_Atomic16.asm
|
||||
Test_Primary/Primary_09_Atomic32.asm
|
||||
Test_Primary/Primary_09_Atomic64.asm
|
||||
Test_Primary/Primary_23_Atomic16.asm
|
||||
Test_Primary/Primary_23_Atomic32.asm
|
||||
Test_Primary/Primary_23_Atomic64.asm
|
||||
Test_Primary/Primary_29_Atomic16.asm
|
||||
Test_Primary/Primary_29_Atomic32.asm
|
||||
Test_Primary/Primary_29_Atomic64.asm
|
||||
Test_Primary/Primary_31_Atomic16.asm
|
||||
Test_Primary/Primary_31_Atomic32.asm
|
||||
Test_Primary/Primary_31_Atomic64.asm
|
||||
Test_Primary/Primary_87_Atomic16.asm
|
||||
Test_Primary/Primary_87_Atomic32.asm
|
||||
Test_Primary/Primary_87_Atomic64.asm
|
||||
Test_Primary/Primary_FF_0_Atomic16.asm
|
||||
Test_Primary/Primary_FF_0_Atomic32.asm
|
||||
Test_Primary/Primary_FF_0_Atomic64.asm
|
||||
Test_Primary/Primary_FF_1_Atomic16.asm
|
||||
Test_Primary/Primary_FF_1_Atomic32.asm
|
||||
Test_Primary/Primary_FF_1_Atomic64.asm
|
||||
|
||||
Test_PrimaryGroup/3_F7_02_2.asm
|
||||
Test_PrimaryGroup/3_F7_02_3.asm
|
||||
Test_PrimaryGroup/3_F7_03_2.asm
|
||||
|
||||
Test_TwoByte/0F_B0_3.asm
|
||||
Test_TwoByte/0F_B0_4.asm
|
||||
Test_TwoByte/0F_B0_5.asm
|
||||
Test_TwoByte/0F_B0_6.asm
|
||||
Test_TwoByte/0F_B0_7.asm
|
||||
Test_TwoByte/0F_C0_Atomic16.asm
|
||||
Test_TwoByte/0F_C0_Atomic32.asm
|
||||
Test_TwoByte/0F_C0_Atomic64.asm
|
||||
|
||||
Test_Secondary/09_XX_01_8.asm
|
||||
Test_Secondary/09_XX_01_9.asm
|
||||
Test_Secondary/09_XX_01_12.asm
|
||||
Test_Secondary/09_XX_01_13.asm
|
||||
Test_Secondary/09_XX_01_15.asm
|
||||
Test_Secondary/09_XX_01_18.asm
|
||||
Test_Secondary/09_XX_01_19.asm
|
||||
|
||||
# Simulator doesn't handle rounding mode changes
|
||||
Test_Secondary/15_XX_2.asm
|
||||
Test_X87_F64/FLDCW_F64.asm
|
||||
Test_H0F3A/66_08.asm
|
||||
Test_H0F3A/66_09.asm
|
||||
Test_H0F3A/66_0A.asm
|
||||
Test_H0F3A/66_0B.asm
|
||||
Test_OpSize/66_5B.asm
|
||||
|
||||
# Simulator has a bug in narrowing or widening operations
|
||||
Test_H0F38/66_03.asm
|
||||
Test_H0F38/66_04.asm
|
||||
Test_H0F38/66_07.asm
|
||||
Test_H0F38/66_2B.asm
|
||||
Test_H0F38/XX_03.asm
|
||||
Test_H0F38/XX_04.asm
|
||||
Test_H0F38/XX_07.asm
|
||||
Test_OpSize/66_63.asm
|
||||
Test_OpSize/66_67.asm
|
||||
Test_OpSize/66_6B.asm
|
||||
@@ -6,8 +6,10 @@ add_subdirectory(POSIX/)
|
||||
add_subdirectory(gvisor-tests/)
|
||||
add_subdirectory(gcc-target-tests-32/)
|
||||
add_subdirectory(gcc-target-tests-64/)
|
||||
|
||||
if (BUILD_THUNKS)
|
||||
add_subdirectory(ThunkLibs)
|
||||
add_subdirectory(ThunkFunctionalTests)
|
||||
endif()
|
||||
|
||||
if (BUILD_FEX_LINUX_TESTS)
|
||||
|
||||
@@ -5,27 +5,34 @@ ExternalProject_Add(FEXLinuxTests
|
||||
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/FEXLinuxTests"
|
||||
CMAKE_ARGS
|
||||
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
|
||||
"-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${X86_TOOLCHAIN_FILE}"
|
||||
"-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${X86_64_TOOLCHAIN_FILE}"
|
||||
"-DBITNESS=64"
|
||||
INSTALL_COMMAND ""
|
||||
BUILD_ALWAYS ON
|
||||
)
|
||||
|
||||
ExternalProject_Add(FEXLinuxTests_32
|
||||
PREFIX FEXLinuxTests_32
|
||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests-32"
|
||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests"
|
||||
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/FEXLinuxTests_32"
|
||||
CMAKE_ARGS
|
||||
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
|
||||
"-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${X86_TOOLCHAIN_FILE}"
|
||||
"-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${X86_32_TOOLCHAIN_FILE}"
|
||||
"-DBITNESS=32"
|
||||
INSTALL_COMMAND ""
|
||||
BUILD_ALWAYS ON
|
||||
)
|
||||
|
||||
# this kind of sucks, but reglob
|
||||
file(GLOB_RECURSE TESTS CONFIGURE_DEPENDS tests/*.cpp)
|
||||
file(GLOB_RECURSE TESTS_32 CONFIGURE_DEPENDS tests-32/*.cpp)
|
||||
file(GLOB_RECURSE TESTS_32_ONLY CONFIGURE_DEPENDS tests/*.32.cpp)
|
||||
|
||||
function(AddTests Tests BinDirectory BitnessList)
|
||||
set(TESTS_32 ${TESTS})
|
||||
|
||||
# Apply bitness-specific exclude lists
|
||||
list(REMOVE_ITEM TESTS ${TESTS_32_ONLY})
|
||||
|
||||
function(AddTests Tests BinDirectory Bitness)
|
||||
foreach(TEST ${Tests})
|
||||
get_filename_component(TEST_NAME ${TEST} NAME_WLE)
|
||||
|
||||
@@ -34,59 +41,38 @@ function(AddTests Tests BinDirectory BitnessList)
|
||||
# Used to insert a configuration dependency to the test file
|
||||
CONFIGURE_FILE(${TEST} ${CMAKE_BINARY_DIR}/junk.file)
|
||||
|
||||
set(ARGS_REGEX "auto args = \"([^\"]+)\";")
|
||||
string(REGEX MATCH ${ARGS_REGEX} TEST_ARGS ${TEST_CODE})
|
||||
# if cannot handle multiline variables, so we have to match the line first
|
||||
if(${TEST_ARGS} MATCHES ${ARGS_REGEX})
|
||||
string(REGEX REPLACE " |," ";" ARGS "${CMAKE_MATCH_1}")
|
||||
set(VARIATIONS "")
|
||||
foreach(ARG ${ARGS})
|
||||
list(APPEND VARIATIONS "${TEST_NAME}-${ARG}:${ARG}")
|
||||
endforeach()
|
||||
else()
|
||||
set(VARIATIONS "${TEST_NAME}:")
|
||||
set(BIN_PATH "${CMAKE_CURRENT_BINARY_DIR}/${BinDirectory}/${TEST_NAME}.${Bitness}")
|
||||
set(TEST_CASE "${TEST_NAME}.${Bitness}")
|
||||
|
||||
# Add jit test case
|
||||
add_test(NAME "${TEST_CASE}.jit.flt"
|
||||
COMMAND "python3" "${CMAKE_SOURCE_DIR}/Scripts/guest_test_runner.py"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Known_Failures"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Expected_Output"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Disabled_Tests"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Flake_Tests"
|
||||
"${TEST_CASE}"
|
||||
"guest"
|
||||
"$<TARGET_FILE:FEXLoader>"
|
||||
"--no-silent" "-c" "irjit" "-n" "500" "--"
|
||||
"${BIN_PATH}")
|
||||
if (_M_X86_64)
|
||||
# Add host test case
|
||||
add_test(NAME "${TEST_CASE}.host.flt"
|
||||
COMMAND "python3" "${CMAKE_SOURCE_DIR}/Scripts/guest_test_runner.py"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Known_Failures_Host"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Expected_Output"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Disabled_Tests_Host"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Flake_Tests"
|
||||
"${TEST_CASE}"
|
||||
"host"
|
||||
"${BIN_PATH}")
|
||||
endif()
|
||||
|
||||
foreach(VARIATION ${VARIATIONS})
|
||||
foreach(BITNESS ${BitnessList})
|
||||
string(REGEX REPLACE ":" ";" VARIATION "${VARIATION}")
|
||||
list(GET VARIATION 0 VARIATION_NAME)
|
||||
list(GET VARIATION 1 VARIATION_ARG)
|
||||
set(BIN_PATH "${CMAKE_CURRENT_BINARY_DIR}/${BinDirectory}/${TEST_NAME}.${BITNESS}")
|
||||
|
||||
set(TEST_CASE "${VARIATION_NAME}.${BITNESS}")
|
||||
|
||||
# Add jit test case
|
||||
add_test(NAME "${TEST_CASE}.jit.flt"
|
||||
COMMAND "python3" "${CMAKE_SOURCE_DIR}/Scripts/guest_test_runner.py"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Known_Failures"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Expected_Output"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Disabled_Tests"
|
||||
"${TEST_CASE}"
|
||||
"guest"
|
||||
"$<TARGET_FILE:FEXLoader>"
|
||||
"--no-silent" "-c" "irjit" "-n" "500" "--"
|
||||
"${BIN_PATH}"
|
||||
"${VARIATION_ARG}")
|
||||
if (_M_X86_64)
|
||||
# Add host test case
|
||||
add_test(NAME "${TEST_CASE}.host.flt"
|
||||
COMMAND "python3" "${CMAKE_SOURCE_DIR}/Scripts/guest_test_runner.py"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Known_Failures_Host"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Expected_Output"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Disabled_Tests_Host"
|
||||
"${TEST_CASE}"
|
||||
"host"
|
||||
"${BIN_PATH}"
|
||||
"${VARIATION_ARG}")
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
AddTests("${TESTS}" "FEXLinuxTests" "32;64")
|
||||
AddTests("${TESTS_32}" "FEXLinuxTests_32" "32")
|
||||
AddTests("${TESTS}" "FEXLinuxTests" 64)
|
||||
AddTests("${TESTS_32}" "FEXLinuxTests_32" 32)
|
||||
|
||||
execute_process(COMMAND "nproc" OUTPUT_VARIABLE CORES)
|
||||
string(STRIP ${CORES} CORES)
|
||||
@@ -97,7 +83,7 @@ add_custom_target(
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||
USES_TERMINAL
|
||||
COMMAND "ctest" "--timeout" "30" "-j${CORES}" "-R" "\.*\.jit\.flt$$" "--output-on-failure"
|
||||
DEPENDS FEXLinuxTests FEXLoader
|
||||
DEPENDS FEXLinuxTests FEXLinuxTests_32 FEXLoader
|
||||
)
|
||||
|
||||
# Only host
|
||||
@@ -106,7 +92,7 @@ add_custom_target(
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||
USES_TERMINAL
|
||||
COMMAND "ctest" "--timeout" "30" "-j${CORES}" "-R" "\.*\.host\.flt$$" "--output-on-failure"
|
||||
DEPENDS FEXLinuxTests
|
||||
DEPENDS FEXLinuxTests FEXLinuxTests_32
|
||||
)
|
||||
|
||||
# Both host and emulated
|
||||
@@ -115,5 +101,5 @@ add_custom_target(
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||
USES_TERMINAL
|
||||
COMMAND "ctest" "--timeout" "30" "-j${CORES}" "-R" "\.*\.flt$$" "--output-on-failure"
|
||||
DEPENDS FEXLinuxTests FEXLoader
|
||||
DEPENDS FEXLinuxTests FEXLinuxTests_32 FEXLoader
|
||||
)
|
||||
@@ -5,13 +5,3 @@
|
||||
# These sometimes crash FEX with SIGSEGV
|
||||
timer-sigev-thread.32
|
||||
timer-sigev-thread.64
|
||||
|
||||
# These fail on arm because of sigbus handling
|
||||
synchronous-signal-block-sbus.32
|
||||
synchronous-signal-block-sbus.64
|
||||
synchronous-signal-block-abus.32
|
||||
synchronous-signal-block-abus.64
|
||||
|
||||
# These fail on arm because we don't raise FPE
|
||||
synchronous-signal-block-sfpe.32
|
||||
synchronous-signal-block-sfpe.64
|
||||
@@ -1,8 +0,0 @@
|
||||
synchronous-signal-block-ssegv.32 -11
|
||||
synchronous-signal-block-ssegv.64 -11
|
||||
synchronous-signal-block-sill.32 -4
|
||||
synchronous-signal-block-sill.64 -4
|
||||
synchronous-signal-block-sbus.32 -7
|
||||
synchronous-signal-block-sbus.64 -7
|
||||
synchronous-signal-block-sfpe.32 -8
|
||||
synchronous-signal-block-sfpe.64 -8
|
||||
@@ -0,0 +1,6 @@
|
||||
smc-mt-1.32
|
||||
smc-mt-2.32
|
||||
smc-mt-1.64
|
||||
smc-mt-2.64
|
||||
pthread_cancel.64
|
||||
pthread_cancel.32
|
||||
@@ -6,15 +6,9 @@
|
||||
timer-sigev-thread.32
|
||||
timer-sigev-thread.64
|
||||
|
||||
# These fail on arm because of sigbus handling
|
||||
synchronous-signal-block-sbus.32
|
||||
synchronous-signal-block-sbus.64
|
||||
synchronous-signal-block-abus.32
|
||||
synchronous-signal-block-abus.64
|
||||
|
||||
# These fail on arm because we don't raise FPE
|
||||
synchronous-signal-block-sfpe.32
|
||||
synchronous-signal-block-sfpe.64
|
||||
# These trigger various quirks in FEX's signal handling
|
||||
synchronous-signal-block.32
|
||||
synchronous-signal-block.64
|
||||
|
||||
###
|
||||
### Failing Tests ###
|
||||
@@ -25,15 +19,3 @@ sigtest_samask.32
|
||||
sigtest_samask.64
|
||||
sigtest_sigmask.32
|
||||
sigtest_sigmask.64
|
||||
|
||||
# These fail to do default signal catching behaviour
|
||||
synchronous-signal-block-ssegv.32
|
||||
synchronous-signal-block-ssegv.64
|
||||
synchronous-signal-block-sill.32
|
||||
synchronous-signal-block-sill.64
|
||||
|
||||
# These fail to queue the signals
|
||||
synchronous-signal-block-asegv.32
|
||||
synchronous-signal-block-asegv.64
|
||||
synchronous-signal-block-aill.32
|
||||
synchronous-signal-block-aill.64
|
||||
@@ -1,56 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(FEXLinuxTests_32)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
unset (CMAKE_C_FLAGS)
|
||||
unset (CMAKE_CXX_FLAGS)
|
||||
|
||||
set(GENERATE_GUEST_INSTALL_TARGETS TRUE)
|
||||
|
||||
file(GLOB_RECURSE TESTS CONFIGURE_DEPENDS *.cpp)
|
||||
|
||||
foreach(TEST ${TESTS})
|
||||
get_filename_component(TEST_NAME ${TEST} NAME_WLE)
|
||||
|
||||
# Used to insert a configuration dependency to the test file
|
||||
CONFIGURE_FILE(${TEST} ${CMAKE_BINARY_DIR}/junk.file)
|
||||
|
||||
file(READ ${TEST} TEST_CODE)
|
||||
set(FLAGS_REGEX "//[ ]*append cxxflags: ([^\n]+)")
|
||||
|
||||
string(REGEX MATCH ${FLAGS_REGEX} APPEND_CXX_FLAGS ${TEST_CODE})
|
||||
# if cannot handle multiline variables, so we have to match the line first
|
||||
if(${APPEND_CXX_FLAGS} MATCHES ${FLAGS_REGEX})
|
||||
set(APPEND_CXX_FLAGS "${CMAKE_MATCH_1}")
|
||||
else()
|
||||
set(APPEND_CXX_FLAGS "")
|
||||
endif()
|
||||
|
||||
set(FLAGS_REGEX "//[ ]*append ldflags: ([^\n]+)")
|
||||
|
||||
string(REGEX MATCH ${FLAGS_REGEX} APPEND_LD_FLAGS ${TEST_CODE})
|
||||
# if cannot handle multiline variables, so we have to match the line first
|
||||
if(${APPEND_LD_FLAGS} MATCHES ${FLAGS_REGEX})
|
||||
set(APPEND_LD_FLAGS "${CMAKE_MATCH_1}" )
|
||||
else()
|
||||
set(APPEND_LD_FLAGS "")
|
||||
endif()
|
||||
|
||||
set(FLAGS_REGEX "//[ ]*libs: ([^\n]+)")
|
||||
|
||||
string(REGEX MATCH ${FLAGS_REGEX} LIBS ${TEST_CODE})
|
||||
# if cannot handle multiline variables, so we have to match the line first
|
||||
if(${LIBS} MATCHES ${FLAGS_REGEX})
|
||||
set(LIBS "${CMAKE_MATCH_1}" )
|
||||
string(REGEX REPLACE " |," ";" LIBS "${LIBS}")
|
||||
else()
|
||||
set(LIBS "")
|
||||
endif()
|
||||
|
||||
set(BIN_NAME_32 "${TEST_NAME}.32")
|
||||
|
||||
add_executable(${BIN_NAME_32} ${TEST})
|
||||
set_target_properties(${BIN_NAME_32} PROPERTIES COMPILE_FLAGS "${APPEND_CXX_FLAGS} -m32 -g -O2 " LINK_FLAGS "${APPEND_LD_FLAGS} -m32")
|
||||
target_link_libraries(${BIN_NAME_32} ${LIBS})
|
||||
endforeach()
|
||||
@@ -9,54 +9,32 @@ unset (CMAKE_CXX_FLAGS)
|
||||
set(GENERATE_GUEST_INSTALL_TARGETS TRUE)
|
||||
|
||||
file(GLOB_RECURSE TESTS CONFIGURE_DEPENDS *.cpp)
|
||||
if(BITNESS EQUAL 64)
|
||||
file(GLOB_RECURSE TESTS_32_ONLY CONFIGURE_DEPENDS *.32.cpp)
|
||||
list(REMOVE_ITEM TESTS ${TESTS_32_ONLY})
|
||||
endif()
|
||||
|
||||
option(CATCH_BUILD_STATIC_LIBRARY "" ON)
|
||||
set(CATCH_BUILD_STATIC_LIBRARY ON)
|
||||
add_subdirectory(../../../External/Catch2/ Catch2)
|
||||
|
||||
foreach(TEST ${TESTS})
|
||||
get_filename_component(TEST_NAME ${TEST} NAME_WLE)
|
||||
|
||||
# Used to insert a configuration dependency to the test file
|
||||
CONFIGURE_FILE(${TEST} ${CMAKE_BINARY_DIR}/junk.file)
|
||||
|
||||
file(READ ${TEST} TEST_CODE)
|
||||
set(FLAGS_REGEX "//[ ]*append cxxflags: ([^\n]+)")
|
||||
|
||||
string(REGEX MATCH ${FLAGS_REGEX} APPEND_CXX_FLAGS ${TEST_CODE})
|
||||
# if cannot handle multiline variables, so we have to match the line first
|
||||
if(${APPEND_CXX_FLAGS} MATCHES ${FLAGS_REGEX})
|
||||
set(APPEND_CXX_FLAGS "${CMAKE_MATCH_1}")
|
||||
else()
|
||||
set(APPEND_CXX_FLAGS "")
|
||||
endif()
|
||||
|
||||
set(FLAGS_REGEX "//[ ]*append ldflags: ([^\n]+)")
|
||||
|
||||
string(REGEX MATCH ${FLAGS_REGEX} APPEND_LD_FLAGS ${TEST_CODE})
|
||||
# if cannot handle multiline variables, so we have to match the line first
|
||||
if(${APPEND_LD_FLAGS} MATCHES ${FLAGS_REGEX})
|
||||
set(APPEND_LD_FLAGS "${CMAKE_MATCH_1}" )
|
||||
else()
|
||||
set(APPEND_LD_FLAGS "")
|
||||
endif()
|
||||
|
||||
set(FLAGS_REGEX "//[ ]*libs: ([^\n]+)")
|
||||
|
||||
string(REGEX MATCH ${FLAGS_REGEX} LIBS ${TEST_CODE})
|
||||
# if cannot handle multiline variables, so we have to match the line first
|
||||
if(${LIBS} MATCHES ${FLAGS_REGEX})
|
||||
set(LIBS "${CMAKE_MATCH_1}" )
|
||||
string(REGEX REPLACE " |," ";" LIBS "${LIBS}")
|
||||
else()
|
||||
set(LIBS "")
|
||||
endif()
|
||||
|
||||
set(BIN_NAME_32 "${TEST_NAME}.32")
|
||||
set(BIN_NAME_64 "${TEST_NAME}.64")
|
||||
|
||||
add_executable(${BIN_NAME_32} ${TEST})
|
||||
set_target_properties(${BIN_NAME_32} PROPERTIES COMPILE_FLAGS "${APPEND_CXX_FLAGS} -m32 -g -O2 " LINK_FLAGS "${APPEND_LD_FLAGS} -m32")
|
||||
target_link_libraries(${BIN_NAME_32} ${LIBS})
|
||||
|
||||
add_executable(${BIN_NAME_64} ${TEST})
|
||||
set_target_properties(${BIN_NAME_64} PROPERTIES COMPILE_FLAGS "${APPEND_CXX_FLAGS} -g -O2" LINK_FLAGS "${APPEND_LD_FLAGS}")
|
||||
target_link_libraries(${BIN_NAME_64} ${LIBS})
|
||||
|
||||
add_executable(${TEST_NAME}.${BITNESS} ${TEST})
|
||||
target_link_libraries(${TEST_NAME}.${BITNESS} PRIVATE Catch2::Catch2WithMain)
|
||||
endforeach()
|
||||
|
||||
target_link_libraries(pthread_cancel.${BITNESS} PRIVATE pthread)
|
||||
|
||||
target_link_options(smc-1-dynamic.${BITNESS} PRIVATE -z execstack)
|
||||
|
||||
target_link_libraries(smc-mt-1.${BITNESS} PRIVATE pthread)
|
||||
|
||||
target_link_libraries(smc-mt-2.${BITNESS} PRIVATE pthread)
|
||||
|
||||
target_link_libraries(smc-shared-1.${BITNESS} PRIVATE rt pthread)
|
||||
|
||||
target_link_libraries(smc-shared-2.${BITNESS} PRIVATE rt pthread)
|
||||
|
||||
target_link_libraries(timer-sigev-thread.${BITNESS} PRIVATE rt pthread)
|
||||
@@ -1,7 +1,9 @@
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <unistd.h>
|
||||
|
||||
int main() {
|
||||
TEST_CASE("Close Range") {
|
||||
int fd_base = dup(STDOUT_FILENO);
|
||||
for (size_t i = 0; i < 15; ++i) {
|
||||
dup(fd_base);
|
||||
@@ -12,6 +14,5 @@ int main() {
|
||||
::syscall(SYS_close_range, fd_base + 1, ~0U, 0);
|
||||
|
||||
// Ensure that fd_base itself wasn't closed in close_range
|
||||
int Result = close(fd_base);
|
||||
return Result;
|
||||
CHECK(close(fd_base) == 0);
|
||||
}
|
||||
File renamed without changes.
@@ -1,3 +1,7 @@
|
||||
#include "invalid_util.h"
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
@@ -9,7 +13,7 @@ __attribute__((naked, nocf_check))
|
||||
static void InvalidINT() {
|
||||
__asm volatile(R"(
|
||||
hlt;
|
||||
ret; # Just incase it gets past the int
|
||||
ret;
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -19,31 +23,25 @@ constexpr int EXPECTED_ERR = 0;
|
||||
constexpr int EXPECTED_SI_CODE = 128;
|
||||
constexpr int EXPECTED_SIGNAL = SIGSEGV;
|
||||
|
||||
static void handler(int signal, siginfo_t *siginfo, void* context) {
|
||||
ucontext_t* _context = (ucontext_t*)context;
|
||||
#ifndef REG_RIP
|
||||
#define REG_RIP REG_EIP
|
||||
#endif
|
||||
if (_context->uc_mcontext.gregs[REG_RIP] == EXPECTED_RIP &&
|
||||
_context->uc_mcontext.gregs[REG_TRAPNO] == EXPECTED_TRAPNO &&
|
||||
_context->uc_mcontext.gregs[REG_ERR] == EXPECTED_ERR &&
|
||||
siginfo->si_code == EXPECTED_SI_CODE &&
|
||||
signal == EXPECTED_SIGNAL) {
|
||||
exit(0);
|
||||
}
|
||||
else {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
TEST_CASE("Signals: Invalid HLT") {
|
||||
capturing_handler_skip = 1;
|
||||
struct sigaction act{};
|
||||
act.sa_sigaction = handler;
|
||||
act.sa_sigaction = CapturingHandler;
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
sigaction(SIGSEGV, &act, nullptr);
|
||||
sigaction(SIGTRAP, &act, nullptr);
|
||||
sigaction(SIGILL, &act, nullptr);
|
||||
|
||||
InvalidINT();
|
||||
return 1;
|
||||
|
||||
#ifndef REG_RIP
|
||||
#define REG_RIP REG_EIP
|
||||
#endif
|
||||
|
||||
REQUIRE(from_handler.has_value());
|
||||
CHECK(from_handler->mctx.gregs[REG_RIP] == EXPECTED_RIP);
|
||||
CHECK(from_handler->mctx.gregs[REG_TRAPNO] == EXPECTED_TRAPNO);
|
||||
CHECK(from_handler->mctx.gregs[REG_ERR] == EXPECTED_ERR);
|
||||
CHECK(from_handler->si_code == EXPECTED_SI_CODE);
|
||||
CHECK(from_handler->signal == EXPECTED_SIGNAL);
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
#include "invalid_util.h"
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
@@ -9,7 +13,7 @@ __attribute__((naked, nocf_check))
|
||||
static void InvalidINT() {
|
||||
__asm volatile(R"(
|
||||
int $0x2d;
|
||||
ret; # Just incase it gets past the int
|
||||
ret;
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -19,33 +23,25 @@ constexpr int EXPECTED_ERR = 362;
|
||||
constexpr int EXPECTED_SI_CODE = 128;
|
||||
constexpr int EXPECTED_SIGNAL = SIGSEGV;
|
||||
|
||||
|
||||
static void handler(int signal, siginfo_t *siginfo, void* context) {
|
||||
ucontext_t* _context = (ucontext_t*)context;
|
||||
#ifndef REG_RIP
|
||||
#define REG_RIP REG_EIP
|
||||
#endif
|
||||
|
||||
if (_context->uc_mcontext.gregs[REG_RIP] == EXPECTED_RIP &&
|
||||
_context->uc_mcontext.gregs[REG_TRAPNO] == EXPECTED_TRAPNO &&
|
||||
_context->uc_mcontext.gregs[REG_ERR] == EXPECTED_ERR &&
|
||||
siginfo->si_code == EXPECTED_SI_CODE &&
|
||||
signal == EXPECTED_SIGNAL) {
|
||||
exit(0);
|
||||
}
|
||||
else {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
TEST_CASE("Signals: Invalid INT") {
|
||||
capturing_handler_skip = 2;
|
||||
struct sigaction act{};
|
||||
act.sa_sigaction = handler;
|
||||
act.sa_sigaction = CapturingHandler;
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
sigaction(SIGSEGV, &act, nullptr);
|
||||
sigaction(SIGTRAP, &act, nullptr);
|
||||
sigaction(SIGILL, &act, nullptr);
|
||||
|
||||
InvalidINT();
|
||||
return 1;
|
||||
|
||||
#ifndef REG_RIP
|
||||
#define REG_RIP REG_EIP
|
||||
#endif
|
||||
|
||||
REQUIRE(from_handler.has_value());
|
||||
CHECK(from_handler->mctx.gregs[REG_RIP] == EXPECTED_RIP);
|
||||
CHECK(from_handler->mctx.gregs[REG_TRAPNO] == EXPECTED_TRAPNO);
|
||||
CHECK(from_handler->mctx.gregs[REG_ERR] == EXPECTED_ERR);
|
||||
CHECK(from_handler->si_code == EXPECTED_SI_CODE);
|
||||
CHECK(from_handler->signal == EXPECTED_SIGNAL);
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
#include "invalid_util.h"
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
@@ -9,7 +13,7 @@ __attribute__((naked, nocf_check))
|
||||
static void InvalidINT() {
|
||||
__asm volatile(R"(
|
||||
.byte 0xF1; # int1
|
||||
ret; # Just incase it gets past the int
|
||||
ret;
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -19,31 +23,25 @@ constexpr int EXPECTED_ERR = 0;
|
||||
constexpr int EXPECTED_SI_CODE = 1;
|
||||
constexpr int EXPECTED_SIGNAL = SIGTRAP;
|
||||
|
||||
static void handler(int signal, siginfo_t *siginfo, void* context) {
|
||||
ucontext_t* _context = (ucontext_t*)context;
|
||||
#ifndef REG_RIP
|
||||
#define REG_RIP REG_EIP
|
||||
#endif
|
||||
if (_context->uc_mcontext.gregs[REG_RIP] == EXPECTED_RIP &&
|
||||
_context->uc_mcontext.gregs[REG_TRAPNO] == EXPECTED_TRAPNO &&
|
||||
_context->uc_mcontext.gregs[REG_ERR] == EXPECTED_ERR &&
|
||||
siginfo->si_code == EXPECTED_SI_CODE &&
|
||||
signal == EXPECTED_SIGNAL) {
|
||||
exit(0);
|
||||
}
|
||||
else {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
TEST_CASE("Signals: Invalid INT1") {
|
||||
capturing_handler_skip = 0;
|
||||
struct sigaction act{};
|
||||
act.sa_sigaction = handler;
|
||||
act.sa_sigaction = CapturingHandler;
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
sigaction(SIGSEGV, &act, nullptr);
|
||||
sigaction(SIGTRAP, &act, nullptr);
|
||||
sigaction(SIGILL, &act, nullptr);
|
||||
|
||||
InvalidINT();
|
||||
return 1;
|
||||
|
||||
#ifndef REG_RIP
|
||||
#define REG_RIP REG_EIP
|
||||
#endif
|
||||
|
||||
REQUIRE(from_handler.has_value());
|
||||
CHECK(from_handler->mctx.gregs[REG_RIP] == EXPECTED_RIP);
|
||||
CHECK(from_handler->mctx.gregs[REG_TRAPNO] == EXPECTED_TRAPNO);
|
||||
CHECK(from_handler->mctx.gregs[REG_ERR] == EXPECTED_ERR);
|
||||
CHECK(from_handler->si_code == EXPECTED_SI_CODE);
|
||||
CHECK(from_handler->signal == EXPECTED_SIGNAL);
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
#include "invalid_util.h"
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
@@ -9,7 +13,7 @@ __attribute__((naked, nocf_check))
|
||||
static void InvalidINT() {
|
||||
__asm volatile(R"(
|
||||
int3;
|
||||
ret; # Just incase it gets past the int
|
||||
ret;
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -19,32 +23,25 @@ constexpr int EXPECTED_ERR = 0;
|
||||
constexpr int EXPECTED_SI_CODE = 128;
|
||||
constexpr int EXPECTED_SIGNAL = SIGTRAP;
|
||||
|
||||
static void handler(int signal, siginfo_t *siginfo, void* context) {
|
||||
ucontext_t* _context = (ucontext_t*)context;
|
||||
#ifndef REG_RIP
|
||||
#define REG_RIP REG_EIP
|
||||
#endif
|
||||
|
||||
if (_context->uc_mcontext.gregs[REG_RIP] == EXPECTED_RIP &&
|
||||
_context->uc_mcontext.gregs[REG_TRAPNO] == EXPECTED_TRAPNO &&
|
||||
_context->uc_mcontext.gregs[REG_ERR] == EXPECTED_ERR &&
|
||||
siginfo->si_code == EXPECTED_SI_CODE &&
|
||||
signal == EXPECTED_SIGNAL) {
|
||||
exit(0);
|
||||
}
|
||||
else {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
TEST_CASE("Signals: Invalid INT3") {
|
||||
capturing_handler_skip = 0;
|
||||
struct sigaction act{};
|
||||
act.sa_sigaction = handler;
|
||||
act.sa_sigaction = CapturingHandler;
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
sigaction(SIGSEGV, &act, nullptr);
|
||||
sigaction(SIGTRAP, &act, nullptr);
|
||||
sigaction(SIGILL, &act, nullptr);
|
||||
|
||||
InvalidINT();
|
||||
return 1;
|
||||
|
||||
#ifndef REG_RIP
|
||||
#define REG_RIP REG_EIP
|
||||
#endif
|
||||
|
||||
REQUIRE(from_handler.has_value());
|
||||
CHECK(from_handler->mctx.gregs[REG_RIP] == EXPECTED_RIP);
|
||||
CHECK(from_handler->mctx.gregs[REG_TRAPNO] == EXPECTED_TRAPNO);
|
||||
CHECK(from_handler->mctx.gregs[REG_ERR] == EXPECTED_ERR);
|
||||
CHECK(from_handler->si_code == EXPECTED_SI_CODE);
|
||||
CHECK(from_handler->signal == EXPECTED_SIGNAL);
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
#include "invalid_util.h"
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
@@ -9,7 +13,7 @@ __attribute__((naked, nocf_check))
|
||||
static void InvalidINT() {
|
||||
__asm volatile(R"(
|
||||
ud2;
|
||||
ret; # Just incase it gets past the int
|
||||
ret;
|
||||
)");
|
||||
}
|
||||
|
||||
@@ -19,31 +23,25 @@ constexpr int EXPECTED_ERR = 0;
|
||||
constexpr int EXPECTED_SI_CODE = 2;
|
||||
constexpr int EXPECTED_SIGNAL = SIGILL;
|
||||
|
||||
static void handler(int signal, siginfo_t *siginfo, void* context) {
|
||||
ucontext_t* _context = (ucontext_t*)context;
|
||||
#ifndef REG_RIP
|
||||
#define REG_RIP REG_EIP
|
||||
#endif
|
||||
if (_context->uc_mcontext.gregs[REG_RIP] == EXPECTED_RIP &&
|
||||
_context->uc_mcontext.gregs[REG_TRAPNO] == EXPECTED_TRAPNO &&
|
||||
_context->uc_mcontext.gregs[REG_ERR] == EXPECTED_ERR &&
|
||||
siginfo->si_code == EXPECTED_SI_CODE &&
|
||||
signal == EXPECTED_SIGNAL) {
|
||||
exit(0);
|
||||
}
|
||||
else {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
TEST_CASE("Signals: Invalid UD2") {
|
||||
capturing_handler_skip = 2;
|
||||
struct sigaction act{};
|
||||
act.sa_sigaction = handler;
|
||||
act.sa_sigaction = CapturingHandler;
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
sigaction(SIGSEGV, &act, nullptr);
|
||||
sigaction(SIGTRAP, &act, nullptr);
|
||||
sigaction(SIGILL, &act, nullptr);
|
||||
|
||||
InvalidINT();
|
||||
return 1;
|
||||
|
||||
#ifndef REG_RIP
|
||||
#define REG_RIP REG_EIP
|
||||
#endif
|
||||
|
||||
REQUIRE(from_handler.has_value());
|
||||
CHECK(from_handler->mctx.gregs[REG_RIP] == EXPECTED_RIP);
|
||||
CHECK(from_handler->mctx.gregs[REG_TRAPNO] == EXPECTED_TRAPNO);
|
||||
CHECK(from_handler->mctx.gregs[REG_ERR] == EXPECTED_ERR);
|
||||
CHECK(from_handler->si_code == EXPECTED_SI_CODE);
|
||||
CHECK(from_handler->signal == EXPECTED_SIGNAL);
|
||||
}
|
||||
Loaded 100 of 124 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user