Compare commits
19 commits
b0b586f09d
...
ff70516807
Author | SHA1 | Date | |
---|---|---|---|
ff70516807 | |||
56525d79d5 | |||
f1d527f9b0 | |||
c9b561134f | |||
60ac09d2cf | |||
1a8c9a7972 | |||
36e775ccaf | |||
bd8ed48d11 | |||
02bec3ddb9 | |||
6706c4cb56 | |||
d4ff9e17b5 | |||
8c57b97eb8 | |||
a8cecd8467 | |||
8065298467 | |||
de798b56f6 | |||
85a3b5fa1b | |||
399a03a130 | |||
5aea560b9e | |||
428f136a75 |
112 changed files with 3176 additions and 3601 deletions
|
@ -1,6 +1,7 @@
|
||||||
#!/bin/sh -e
|
#!/bin/sh -e
|
||||||
|
|
||||||
HEADER="$(cat "$PWD/.ci/license/header.txt")"
|
HEADER="$(cat "$PWD/.ci/license/header.txt")"
|
||||||
|
HEADER_HASH="$(cat "$PWD/.ci/license/header-hash.txt")"
|
||||||
|
|
||||||
echo "Getting branch changes"
|
echo "Getting branch changes"
|
||||||
|
|
||||||
|
@ -13,41 +14,86 @@ FILES=`git diff-tree --no-commit-id --name-only ${RANGE} -r`
|
||||||
|
|
||||||
echo "Done"
|
echo "Done"
|
||||||
|
|
||||||
|
check_header() {
|
||||||
|
CONTENT="`head -n3 < $1`"
|
||||||
|
case "$CONTENT" in
|
||||||
|
"$HEADER"*) ;;
|
||||||
|
*) BAD_FILES="$BAD_FILES $1" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
check_cmake_header() {
|
||||||
|
CONTENT="`head -n3 < $1`"
|
||||||
|
|
||||||
|
case "$CONTENT" in
|
||||||
|
"$HEADER_HASH"*) ;;
|
||||||
|
*)
|
||||||
|
BAD_CMAKE="$BAD_CMAKE $1" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
for file in $FILES; do
|
for file in $FILES; do
|
||||||
[ -f "$file" ] || continue
|
[ -f "$file" ] || continue
|
||||||
|
|
||||||
|
if [ `basename -- "$file"` = "CMakeLists.txt" ]; then
|
||||||
|
check_cmake_header "$file"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
EXTENSION="${file##*.}"
|
EXTENSION="${file##*.}"
|
||||||
case "$EXTENSION" in
|
case "$EXTENSION" in
|
||||||
kts|kt|cpp|h)
|
kts|kt|cpp|h)
|
||||||
CONTENT="`cat $file`"
|
check_header "$file"
|
||||||
case "$CONTENT" in
|
;;
|
||||||
"$HEADER"*) ;;
|
cmake)
|
||||||
*) BAD_FILES="$BAD_FILES $file" ;;
|
check_cmake_header "$file"
|
||||||
esac
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ "$BAD_FILES" = "" ]; then
|
if [ "$BAD_FILES" = "" ] && [ "$BAD_CMAKE" = "" ]; then
|
||||||
echo
|
echo
|
||||||
echo "All good."
|
echo "All good."
|
||||||
|
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "The following files have incorrect license headers:"
|
if [ "$BAD_FILES" != "" ]; then
|
||||||
echo
|
echo "The following source files have incorrect license headers:"
|
||||||
|
echo
|
||||||
|
|
||||||
for file in $BAD_FILES; do echo $file; done
|
for file in $BAD_FILES; do echo $file; done
|
||||||
|
|
||||||
cat << EOF
|
cat << EOF
|
||||||
|
|
||||||
The following license header should be added to the start of all offending files:
|
The following license header should be added to the start of all offending SOURCE files:
|
||||||
|
|
||||||
=== BEGIN ===
|
=== BEGIN ===
|
||||||
$HEADER
|
$HEADER
|
||||||
=== END ===
|
=== END ===
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$BAD_CMAKE" != "" ]; then
|
||||||
|
echo "The following CMake files have incorrect license headers:"
|
||||||
|
echo
|
||||||
|
|
||||||
|
for file in $BAD_CMAKE; do echo $file; done
|
||||||
|
|
||||||
|
cat << EOF
|
||||||
|
|
||||||
|
The following license header should be added to the start of all offending CMake files:
|
||||||
|
|
||||||
|
=== BEGIN ===
|
||||||
|
$HEADER_HASH
|
||||||
|
=== END ===
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat << EOF
|
||||||
If some of the code in this PR is not being contributed by the original author,
|
If some of the code in this PR is not being contributed by the original author,
|
||||||
the files which have been exclusively changed by that code can be ignored.
|
the files which have been exclusively changed by that code can be ignored.
|
||||||
If this happens, this PR requirement can be bypassed once all other files are addressed.
|
If this happens, this PR requirement can be bypassed once all other files are addressed.
|
||||||
|
@ -70,6 +116,17 @@ if [ "$FIX" = "true" ]; then
|
||||||
git add $file
|
git add $file
|
||||||
done
|
done
|
||||||
|
|
||||||
|
for file in $BAD_CMAKE; do
|
||||||
|
cat $file > $file.bak
|
||||||
|
|
||||||
|
cat .ci/license/header-hash.txt > $file
|
||||||
|
echo >> $file
|
||||||
|
cat $file.bak >> $file
|
||||||
|
|
||||||
|
rm $file.bak
|
||||||
|
|
||||||
|
git add $file
|
||||||
|
done
|
||||||
echo "License headers fixed."
|
echo "License headers fixed."
|
||||||
|
|
||||||
if [ "$COMMIT" = "true" ]; then
|
if [ "$COMMIT" = "true" ]; then
|
||||||
|
|
2
.ci/license/header-hash.txt
Normal file
2
.ci/license/header-hash.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
6
.gitmodules
vendored
6
.gitmodules
vendored
|
@ -1,6 +0,0 @@
|
||||||
# SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
[submodule "libusb"]
|
|
||||||
path = externals/libusb/libusb
|
|
||||||
url = https://github.com/libusb/libusb.git
|
|
|
@ -48,12 +48,10 @@ endif()
|
||||||
# On Linux system SDL2 is likely to be lacking HIDAPI support which have drawbacks but is needed for SDL motion
|
# On Linux system SDL2 is likely to be lacking HIDAPI support which have drawbacks but is needed for SDL motion
|
||||||
CMAKE_DEPENDENT_OPTION(ENABLE_SDL2 "Enable the SDL2 frontend" ON "NOT ANDROID" OFF)
|
CMAKE_DEPENDENT_OPTION(ENABLE_SDL2 "Enable the SDL2 frontend" ON "NOT ANDROID" OFF)
|
||||||
|
|
||||||
set(EXT_DEFAULT ON)
|
set(EXT_DEFAULT OFF)
|
||||||
|
|
||||||
# See https://github.com/llvm/llvm-project/issues/123946
|
if (MSVC OR ANDROID)
|
||||||
# OpenBSD va_list doesn't play nice with precompiled headers
|
set(EXT_DEFAULT ON)
|
||||||
if (PLATFORM_FREEBSD OR PLATFORM_OPENBSD)
|
|
||||||
set(EXT_DEFAULT OFF)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
CMAKE_DEPENDENT_OPTION(YUZU_USE_EXTERNAL_SDL2 "Compile external SDL2" ${EXT_DEFAULT} "ENABLE_SDL2;NOT MSVC" OFF)
|
CMAKE_DEPENDENT_OPTION(YUZU_USE_EXTERNAL_SDL2 "Compile external SDL2" ${EXT_DEFAULT} "ENABLE_SDL2;NOT MSVC" OFF)
|
||||||
|
@ -69,14 +67,13 @@ option(ENABLE_QT_UPDATE_CHECKER "Enable update checker for the Qt frontend" OFF)
|
||||||
|
|
||||||
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSVC}" "ENABLE_QT" OFF)
|
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSVC}" "ENABLE_QT" OFF)
|
||||||
|
|
||||||
option(YUZU_USE_CPM "Use CPM to fetch Eden dependencies if needed" ON)
|
option(YUZU_USE_CPM "Use CPM to fetch system dependencies (fmt, boost, etc) if needed. Externals will still be fetched." ${EXT_DEFAULT})
|
||||||
|
|
||||||
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
|
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
|
||||||
option(ENABLE_WIFI_SCAN "Enable WiFi scanning" OFF)
|
option(ENABLE_WIFI_SCAN "Enable WiFi scanning" OFF)
|
||||||
|
|
||||||
option(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" ${EXT_DEFAULT})
|
option(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" ${EXT_DEFAULT})
|
||||||
option(YUZU_USE_EXTERNAL_VULKAN_HEADERS "Use Vulkan-Headers from externals" ${EXT_DEFAULT})
|
option(YUZU_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES "Use Vulkan Utility Headers from externals" ${EXT_DEFAULT})
|
||||||
option(YUZU_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES "Use Vulkan-Utility-Libraries from externals" ${EXT_DEFAULT})
|
|
||||||
option(YUZU_USE_EXTERNAL_VULKAN_SPIRV_TOOLS "Use SPIRV-Tools from externals" ${EXT_DEFAULT})
|
option(YUZU_USE_EXTERNAL_VULKAN_SPIRV_TOOLS "Use SPIRV-Tools from externals" ${EXT_DEFAULT})
|
||||||
|
|
||||||
option(YUZU_USE_QT_MULTIMEDIA "Use QtMultimedia for Camera" OFF)
|
option(YUZU_USE_QT_MULTIMEDIA "Use QtMultimedia for Camera" OFF)
|
||||||
|
@ -95,10 +92,12 @@ option(YUZU_TESTS "Compile tests" "${BUILD_TESTING}")
|
||||||
|
|
||||||
option(YUZU_USE_PRECOMPILED_HEADERS "Use precompiled headers" ${EXT_DEFAULT})
|
option(YUZU_USE_PRECOMPILED_HEADERS "Use precompiled headers" ${EXT_DEFAULT})
|
||||||
|
|
||||||
|
# TODO(crueter): CI this?
|
||||||
option(YUZU_DOWNLOAD_ANDROID_VVL "Download validation layer binary for android" ON)
|
option(YUZU_DOWNLOAD_ANDROID_VVL "Download validation layer binary for android" ON)
|
||||||
|
|
||||||
option(FORCE_DOWNLOAD_WIN_BUNDLES "Forcefully download bundled Windows dependencies (useful for CI)" OFF)
|
option(FORCE_DOWNLOAD_WIN_BUNDLES "Forcefully download bundled Windows dependencies (useful for CI)" OFF)
|
||||||
|
|
||||||
|
# TODO(crueter): Cleanup, each dep that has a bundled option should allow to choose between bundled, external, system
|
||||||
if (YUZU_USE_CPM AND ENABLE_SDL2)
|
if (YUZU_USE_CPM AND ENABLE_SDL2)
|
||||||
option(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 build" "${MSVC}")
|
option(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 build" "${MSVC}")
|
||||||
endif()
|
endif()
|
||||||
|
@ -107,12 +106,10 @@ CMAKE_DEPENDENT_OPTION(YUZU_ROOM "Enable dedicated room functionality" ON "NOT A
|
||||||
|
|
||||||
CMAKE_DEPENDENT_OPTION(YUZU_ROOM_STANDALONE "Enable standalone room executable" ON "YUZU_ROOM" OFF)
|
CMAKE_DEPENDENT_OPTION(YUZU_ROOM_STANDALONE "Enable standalone room executable" ON "YUZU_ROOM" OFF)
|
||||||
|
|
||||||
CMAKE_DEPENDENT_OPTION(YUZU_CMD "Compile the eden-cli executable" ON "NOT ANDROID" OFF)
|
CMAKE_DEPENDENT_OPTION(YUZU_CMD "Compile the eden-cli executable" ON "ENABLE_SDL2;NOT ANDROID" OFF)
|
||||||
|
|
||||||
CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR LINUX" OFF)
|
CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR LINUX" OFF)
|
||||||
|
|
||||||
option(YUZU_CHECK_SUBMODULES "Check if submodules are present" ${EXT_DEFAULT})
|
|
||||||
|
|
||||||
option(YUZU_ENABLE_LTO "Enable link-time optimization" OFF)
|
option(YUZU_ENABLE_LTO "Enable link-time optimization" OFF)
|
||||||
|
|
||||||
option(YUZU_DOWNLOAD_TIME_ZONE_DATA "Always download time zone binaries" ON)
|
option(YUZU_DOWNLOAD_TIME_ZONE_DATA "Always download time zone binaries" ON)
|
||||||
|
@ -194,53 +191,6 @@ if(EXISTS ${PROJECT_SOURCE_DIR}/hooks/pre-commit AND NOT EXISTS ${PROJECT_SOURCE
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Sanity check : Check that all submodules are present
|
|
||||||
# =======================================================================
|
|
||||||
|
|
||||||
function(check_submodules_present)
|
|
||||||
file(READ "${PROJECT_SOURCE_DIR}/.gitmodules" gitmodules)
|
|
||||||
string(REGEX MATCHALL "path *= *[^ \t\r\n]*" gitmodules ${gitmodules})
|
|
||||||
foreach(module ${gitmodules})
|
|
||||||
string(REGEX REPLACE "path *= *" "" module ${module})
|
|
||||||
|
|
||||||
file(GLOB RESULT "${PROJECT_SOURCE_DIR}/${module}/*")
|
|
||||||
list(LENGTH RESULT RES_LEN)
|
|
||||||
if(RES_LEN EQUAL 0)
|
|
||||||
message(FATAL_ERROR "Git submodule ${module} not found. "
|
|
||||||
"Please run: \ngit submodule update --init --recursive")
|
|
||||||
endif()
|
|
||||||
if (EXISTS "${PROJECT_SOURCE_DIR}/${module}/.git")
|
|
||||||
set(SUBMODULE_DIR "${PROJECT_SOURCE_DIR}/${module}")
|
|
||||||
|
|
||||||
execute_process(
|
|
||||||
COMMAND git rev-parse --short=10 HEAD
|
|
||||||
WORKING_DIRECTORY ${SUBMODULE_DIR}
|
|
||||||
OUTPUT_VARIABLE SUBMODULE_SHA
|
|
||||||
)
|
|
||||||
|
|
||||||
# would probably be better to do string parsing, but whatever
|
|
||||||
execute_process(
|
|
||||||
COMMAND git remote get-url origin
|
|
||||||
WORKING_DIRECTORY ${SUBMODULE_DIR}
|
|
||||||
OUTPUT_VARIABLE SUBMODULE_URL
|
|
||||||
)
|
|
||||||
|
|
||||||
string(REGEX REPLACE "\n|\r" "" SUBMODULE_SHA ${SUBMODULE_SHA})
|
|
||||||
string(REGEX REPLACE "\n|\r|\\.git" "" SUBMODULE_URL ${SUBMODULE_URL})
|
|
||||||
|
|
||||||
get_filename_component(SUBMODULE_NAME ${SUBMODULE_DIR} NAME)
|
|
||||||
|
|
||||||
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_NAMES ${SUBMODULE_NAME})
|
|
||||||
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS ${SUBMODULE_SHA})
|
|
||||||
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_URLS ${SUBMODULE_URL})
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
if(EXISTS ${PROJECT_SOURCE_DIR}/.gitmodules AND YUZU_CHECK_SUBMODULES)
|
|
||||||
check_submodules_present()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
configure_file(${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
|
configure_file(${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||||
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
|
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||||
COPYONLY)
|
COPYONLY)
|
||||||
|
@ -277,7 +227,7 @@ function(detect_architecture symbol arch)
|
||||||
if (ARCHITECTURE_${arch})
|
if (ARCHITECTURE_${arch})
|
||||||
set(ARCHITECTURE "${arch}" PARENT_SCOPE)
|
set(ARCHITECTURE "${arch}" PARENT_SCOPE)
|
||||||
set(ARCHITECTURE_${arch} 1 PARENT_SCOPE)
|
set(ARCHITECTURE_${arch} 1 PARENT_SCOPE)
|
||||||
add_definitions(-DARCHITECTURE_${arch}=1)
|
add_compile_definitions(ARCHITECTURE_${arch}=1)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
@ -299,7 +249,7 @@ endif()
|
||||||
if (NOT DEFINED ARCHITECTURE)
|
if (NOT DEFINED ARCHITECTURE)
|
||||||
set(ARCHITECTURE "GENERIC")
|
set(ARCHITECTURE "GENERIC")
|
||||||
set(ARCHITECTURE_GENERIC 1)
|
set(ARCHITECTURE_GENERIC 1)
|
||||||
add_definitions(-DARCHITECTURE_GENERIC=1)
|
add_compile_definitions(ARCHITECTURE_GENERIC=1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
message(STATUS "Target architecture: ${ARCHITECTURE}")
|
message(STATUS "Target architecture: ${ARCHITECTURE}")
|
||||||
|
@ -311,16 +261,16 @@ if (MSVC AND ARCHITECTURE_x86)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (UNIX)
|
if (UNIX)
|
||||||
add_definitions(-DYUZU_UNIX=1)
|
add_compile_definitions(YUZU_UNIX=1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ARCHITECTURE_arm64 AND (ANDROID OR PLATFORM_LINUX))
|
if (ARCHITECTURE_arm64 AND (ANDROID OR PLATFORM_LINUX))
|
||||||
set(HAS_NCE 1)
|
set(HAS_NCE 1)
|
||||||
add_definitions(-DHAS_NCE=1)
|
add_compile_definitions(HAS_NCE=1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (YUZU_ROOM)
|
if (YUZU_ROOM)
|
||||||
add_definitions(-DYUZU_ROOM)
|
add_compile_definitions(YUZU_ROOM)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Build/optimization presets
|
# Build/optimization presets
|
||||||
|
@ -489,14 +439,6 @@ if(NOT TARGET Boost::headers)
|
||||||
AddJsonPackage(boost_headers)
|
AddJsonPackage(boost_headers)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ENABLE_LIBUSB)
|
|
||||||
if (PLATFORM_FREEBSD)
|
|
||||||
find_package(libusb MODULE)
|
|
||||||
else()
|
|
||||||
find_package(libusb 1.0.24 MODULE)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# DiscordRPC
|
# DiscordRPC
|
||||||
if (USE_DISCORD_PRESENCE)
|
if (USE_DISCORD_PRESENCE)
|
||||||
AddJsonPackage(discord-rpc)
|
AddJsonPackage(discord-rpc)
|
||||||
|
@ -601,8 +543,8 @@ endfunction()
|
||||||
add_subdirectory(externals)
|
add_subdirectory(externals)
|
||||||
|
|
||||||
# pass targets from externals
|
# pass targets from externals
|
||||||
find_package(VulkanHeaders)
|
|
||||||
find_package(VulkanUtilityLibraries)
|
find_package(VulkanUtilityLibraries)
|
||||||
|
find_package(libusb)
|
||||||
find_package(VulkanMemoryAllocator)
|
find_package(VulkanMemoryAllocator)
|
||||||
find_package(SPIRV-Tools)
|
find_package(SPIRV-Tools)
|
||||||
|
|
||||||
|
@ -736,7 +678,7 @@ if (APPLE)
|
||||||
list(APPEND PLATFORM_LIBRARIES ${ICONV_LIBRARY})
|
list(APPEND PLATFORM_LIBRARIES ${ICONV_LIBRARY})
|
||||||
elseif (WIN32)
|
elseif (WIN32)
|
||||||
# Target Windows 10
|
# Target Windows 10
|
||||||
add_definitions(-D_WIN32_WINNT=0x0A00 -DWINVER=0x0A00)
|
add_compile_definitions(_WIN32_WINNT=0x0A00 WINVER=0x0A00)
|
||||||
set(PLATFORM_LIBRARIES winmm ws2_32 iphlpapi)
|
set(PLATFORM_LIBRARIES winmm ws2_32 iphlpapi)
|
||||||
if (MINGW)
|
if (MINGW)
|
||||||
# PSAPI is the Process Status API
|
# PSAPI is the Process Status API
|
||||||
|
|
|
@ -11,10 +11,11 @@
|
||||||
# Future crueter: Wow this was a lie and a half, at this point I might as well make my own CPN
|
# Future crueter: Wow this was a lie and a half, at this point I might as well make my own CPN
|
||||||
# haha just kidding... unless?
|
# haha just kidding... unless?
|
||||||
|
|
||||||
|
# TODO(crueter): Remember to get more than 6 hours of sleep whenever making giant cmake changes
|
||||||
if (MSVC OR ANDROID)
|
if (MSVC OR ANDROID)
|
||||||
set(BUNDLED_DEFAULT OFF)
|
|
||||||
else()
|
|
||||||
set(BUNDLED_DEFAULT ON)
|
set(BUNDLED_DEFAULT ON)
|
||||||
|
else()
|
||||||
|
set(BUNDLED_DEFAULT OFF)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
option(CPMUTIL_FORCE_BUNDLED
|
option(CPMUTIL_FORCE_BUNDLED
|
||||||
|
@ -26,8 +27,7 @@ option(CPMUTIL_FORCE_SYSTEM
|
||||||
cmake_minimum_required(VERSION 3.22)
|
cmake_minimum_required(VERSION 3.22)
|
||||||
include(CPM)
|
include(CPM)
|
||||||
|
|
||||||
# TODO(crueter): Better solution for separate cpmfiles e.g. per-directory
|
set(CPMUTIL_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cpmfile.json")
|
||||||
set(CPMUTIL_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cpmfile.json" CACHE STRING "Location of cpmfile.json")
|
|
||||||
|
|
||||||
if (EXISTS ${CPMUTIL_JSON_FILE})
|
if (EXISTS ${CPMUTIL_JSON_FILE})
|
||||||
file(READ ${CPMUTIL_JSON_FILE} CPMFILE_CONTENT)
|
file(READ ${CPMUTIL_JSON_FILE} CPMFILE_CONTENT)
|
||||||
|
@ -148,11 +148,32 @@ function(AddJsonPackage)
|
||||||
get_json_element("${object}" tag tag "")
|
get_json_element("${object}" tag tag "")
|
||||||
get_json_element("${object}" artifact artifact "")
|
get_json_element("${object}" artifact artifact "")
|
||||||
get_json_element("${object}" git_version git_version "")
|
get_json_element("${object}" git_version git_version "")
|
||||||
|
get_json_element("${object}" git_host git_host "")
|
||||||
get_json_element("${object}" source_subdir source_subdir "")
|
get_json_element("${object}" source_subdir source_subdir "")
|
||||||
get_json_element("${object}" bundled bundled "unset")
|
get_json_element("${object}" bundled bundled "unset")
|
||||||
get_json_element("${object}" find_args find_args "")
|
get_json_element("${object}" find_args find_args "")
|
||||||
get_json_element("${object}" raw_patches patches "")
|
get_json_element("${object}" raw_patches patches "")
|
||||||
|
|
||||||
|
# okay here comes the fun part: REPLACEMENTS!
|
||||||
|
# first: tag gets %VERSION% replaced if applicable, with either git_version (preferred) or version
|
||||||
|
# second: artifact gets %VERSION% and %TAG% replaced accordingly (same rules for VERSION)
|
||||||
|
|
||||||
|
if (git_version)
|
||||||
|
set(version_replace ${git_version})
|
||||||
|
else()
|
||||||
|
set(version_replace ${version})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# TODO(crueter): fmt module for cmake
|
||||||
|
if (tag)
|
||||||
|
string(REPLACE "%VERSION%" "${version_replace}" tag ${tag})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (artifact)
|
||||||
|
string(REPLACE "%VERSION%" "${version_replace}" artifact ${artifact})
|
||||||
|
string(REPLACE "%TAG%" "${tag}" artifact ${artifact})
|
||||||
|
endif()
|
||||||
|
|
||||||
# format patchdir
|
# format patchdir
|
||||||
if (raw_patches)
|
if (raw_patches)
|
||||||
math(EXPR range "${raw_patches_LENGTH} - 1")
|
math(EXPR range "${raw_patches_LENGTH} - 1")
|
||||||
|
@ -201,6 +222,8 @@ function(AddJsonPackage)
|
||||||
SOURCE_SUBDIR "${source_subdir}"
|
SOURCE_SUBDIR "${source_subdir}"
|
||||||
|
|
||||||
GIT_VERSION ${git_version}
|
GIT_VERSION ${git_version}
|
||||||
|
GIT_HOST ${git_host}
|
||||||
|
|
||||||
ARTIFACT ${artifact}
|
ARTIFACT ${artifact}
|
||||||
TAG ${tag}
|
TAG ${tag}
|
||||||
)
|
)
|
||||||
|
@ -240,6 +263,7 @@ function(AddPackage)
|
||||||
NAME
|
NAME
|
||||||
VERSION
|
VERSION
|
||||||
GIT_VERSION
|
GIT_VERSION
|
||||||
|
GIT_HOST
|
||||||
|
|
||||||
REPO
|
REPO
|
||||||
TAG
|
TAG
|
||||||
|
@ -272,11 +296,17 @@ function(AddPackage)
|
||||||
option(${PKG_ARGS_NAME}_FORCE_SYSTEM "Force the system package for ${PKG_ARGS_NAME}")
|
option(${PKG_ARGS_NAME}_FORCE_SYSTEM "Force the system package for ${PKG_ARGS_NAME}")
|
||||||
option(${PKG_ARGS_NAME}_FORCE_BUNDLED "Force the bundled package for ${PKG_ARGS_NAME}")
|
option(${PKG_ARGS_NAME}_FORCE_BUNDLED "Force the bundled package for ${PKG_ARGS_NAME}")
|
||||||
|
|
||||||
|
if (NOT DEFINED PKG_ARGS_GIT_HOST)
|
||||||
|
set(git_host github.com)
|
||||||
|
else()
|
||||||
|
set(git_host ${PKG_ARGS_GIT_HOST})
|
||||||
|
endif()
|
||||||
|
|
||||||
if (DEFINED PKG_ARGS_URL)
|
if (DEFINED PKG_ARGS_URL)
|
||||||
set(pkg_url ${PKG_ARGS_URL})
|
set(pkg_url ${PKG_ARGS_URL})
|
||||||
|
|
||||||
if (DEFINED PKG_ARGS_REPO)
|
if (DEFINED PKG_ARGS_REPO)
|
||||||
set(pkg_git_url https://github.com/${PKG_ARGS_REPO})
|
set(pkg_git_url https://${git_host}/${PKG_ARGS_REPO})
|
||||||
else()
|
else()
|
||||||
if (DEFINED PKG_ARGS_GIT_URL)
|
if (DEFINED PKG_ARGS_GIT_URL)
|
||||||
set(pkg_git_url ${PKG_ARGS_GIT_URL})
|
set(pkg_git_url ${PKG_ARGS_GIT_URL})
|
||||||
|
@ -285,7 +315,7 @@ function(AddPackage)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
elseif (DEFINED PKG_ARGS_REPO)
|
elseif (DEFINED PKG_ARGS_REPO)
|
||||||
set(pkg_git_url https://github.com/${PKG_ARGS_REPO})
|
set(pkg_git_url https://${git_host}/${PKG_ARGS_REPO})
|
||||||
|
|
||||||
if (DEFINED PKG_ARGS_TAG)
|
if (DEFINED PKG_ARGS_TAG)
|
||||||
set(pkg_key ${PKG_ARGS_TAG})
|
set(pkg_key ${PKG_ARGS_TAG})
|
||||||
|
@ -316,25 +346,23 @@ function(AddPackage)
|
||||||
|
|
||||||
cpm_utils_message(STATUS ${PKG_ARGS_NAME} "Download URL is ${pkg_url}")
|
cpm_utils_message(STATUS ${PKG_ARGS_NAME} "Download URL is ${pkg_url}")
|
||||||
|
|
||||||
if (DEFINED PKG_ARGS_GIT_VERSION)
|
|
||||||
set(git_version ${PKG_ARGS_GIT_VERSION})
|
|
||||||
elseif(DEFINED PKG_ARGS_VERSION)
|
|
||||||
set(git_version ${PKG_ARGS_VERSION})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (NOT DEFINED PKG_ARGS_KEY)
|
if (NOT DEFINED PKG_ARGS_KEY)
|
||||||
if (DEFINED PKG_ARGS_SHA)
|
if (DEFINED PKG_ARGS_SHA)
|
||||||
string(SUBSTRING ${PKG_ARGS_SHA} 0 4 pkg_key)
|
string(SUBSTRING ${PKG_ARGS_SHA} 0 4 pkg_key)
|
||||||
cpm_utils_message(DEBUG ${PKG_ARGS_NAME}
|
cpm_utils_message(DEBUG ${PKG_ARGS_NAME}
|
||||||
"No custom key defined, using ${pkg_key} from sha")
|
"No custom key defined, using ${pkg_key} from sha")
|
||||||
elseif (DEFINED git_version)
|
elseif(DEFINED PKG_ARGS_GIT_VERSION)
|
||||||
set(pkg_key ${git_version})
|
set(pkg_key ${PKG_ARGS_GIT_VERSION})
|
||||||
cpm_utils_message(DEBUG ${PKG_ARGS_NAME}
|
cpm_utils_message(DEBUG ${PKG_ARGS_NAME}
|
||||||
"No custom key defined, using ${pkg_key}")
|
"No custom key defined, using ${pkg_key}")
|
||||||
elseif (DEFINED PKG_ARGS_TAG)
|
elseif (DEFINED PKG_ARGS_TAG)
|
||||||
set(pkg_key ${PKG_ARGS_TAG})
|
set(pkg_key ${PKG_ARGS_TAG})
|
||||||
cpm_utils_message(DEBUG ${PKG_ARGS_NAME}
|
cpm_utils_message(DEBUG ${PKG_ARGS_NAME}
|
||||||
"No custom key defined, using ${pkg_key}")
|
"No custom key defined, using ${pkg_key}")
|
||||||
|
elseif (DEFINED PKG_ARGS_VERSION)
|
||||||
|
set(pkg_key ${PKG_ARGS_VERSION})
|
||||||
|
cpm_utils_message(DEBUG ${PKG_ARGS_NAME}
|
||||||
|
"No custom key defined, using ${pkg_key}")
|
||||||
else()
|
else()
|
||||||
cpm_utils_message(WARNING ${PKG_ARGS_NAME}
|
cpm_utils_message(WARNING ${PKG_ARGS_NAME}
|
||||||
"Could not determine cache key, using CPM defaults")
|
"Could not determine cache key, using CPM defaults")
|
||||||
|
@ -445,12 +473,15 @@ function(AddPackage)
|
||||||
if (DEFINED PKG_ARGS_SHA)
|
if (DEFINED PKG_ARGS_SHA)
|
||||||
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
|
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
|
||||||
${PKG_ARGS_SHA})
|
${PKG_ARGS_SHA})
|
||||||
elseif(DEFINED git_version)
|
elseif (DEFINED PKG_ARGS_GIT_VERSION)
|
||||||
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
|
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
|
||||||
${git_version})
|
${PKG_ARGS_GIT_VERSION})
|
||||||
elseif (DEFINED PKG_ARGS_TAG)
|
elseif (DEFINED PKG_ARGS_TAG)
|
||||||
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
|
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
|
||||||
${PKG_ARGS_TAG})
|
${PKG_ARGS_TAG})
|
||||||
|
elseif(DEFINED PKG_ARGS_VERSION)
|
||||||
|
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
|
||||||
|
${PKG_ARGS_VERSION})
|
||||||
else()
|
else()
|
||||||
cpm_utils_message(WARNING ${PKG_ARGS_NAME}
|
cpm_utils_message(WARNING ${PKG_ARGS_NAME}
|
||||||
"Package has no specified sha, tag, or version")
|
"Package has no specified sha, tag, or version")
|
||||||
|
@ -495,6 +526,7 @@ function(add_ci_package key)
|
||||||
set(ARTIFACT_DIR ${${ARTIFACT_PACKAGE}_SOURCE_DIR} PARENT_SCOPE)
|
set(ARTIFACT_DIR ${${ARTIFACT_PACKAGE}_SOURCE_DIR} PARENT_SCOPE)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
# TODO(crueter): we could do an AddMultiArchPackage, multiplatformpackage?
|
||||||
# name is the artifact name, package is for find_package override
|
# name is the artifact name, package is for find_package override
|
||||||
function(AddCIPackage)
|
function(AddCIPackage)
|
||||||
set(oneValueArgs
|
set(oneValueArgs
|
||||||
|
|
17
CMakeModules/Findmbedtls.cmake
Normal file
17
CMakeModules/Findmbedtls.cmake
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# SPDX-FileCopyrightText: 2025 Eden Emulator Project
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
|
||||||
|
find_package(PkgConfig QUIET)
|
||||||
|
pkg_search_module(mbedtls QUIET IMPORTED_TARGET mbedtls)
|
||||||
|
find_package_handle_standard_args(mbedtls
|
||||||
|
REQUIRED_VARS mbedtls_LINK_LIBRARIES
|
||||||
|
VERSION_VAR mbedtls_VERSION
|
||||||
|
)
|
||||||
|
|
||||||
|
pkg_search_module(mbedcrypto QUIET IMPORTED_TARGET mbedcrypto)
|
||||||
|
find_package_handle_standard_args(mbedcrypto
|
||||||
|
REQUIRED_VARS mbedcrypto_LINK_LIBRARIES
|
||||||
|
VERSION_VAR mbedcrypto_VERSION
|
||||||
|
)
|
|
@ -10,8 +10,8 @@
|
||||||
"boost": {
|
"boost": {
|
||||||
"package": "Boost",
|
"package": "Boost",
|
||||||
"repo": "boostorg/boost",
|
"repo": "boostorg/boost",
|
||||||
"tag": "boost-1.88.0",
|
"tag": "boost-%VERSION%",
|
||||||
"artifact": "boost-1.88.0-cmake.7z",
|
"artifact": "%TAG%-cmake.7z",
|
||||||
"hash": "e5b049e5b61964480ca816395f63f95621e66cb9bcf616a8b10e441e0e69f129e22443acb11e77bc1e8170f8e4171b9b7719891efc43699782bfcd4b3a365f01",
|
"hash": "e5b049e5b61964480ca816395f63f95621e66cb9bcf616a8b10e441e0e69f129e22443acb11e77bc1e8170f8e4171b9b7719891efc43699782bfcd4b3a365f01",
|
||||||
"git_version": "1.88.0",
|
"git_version": "1.88.0",
|
||||||
"version": "1.57"
|
"version": "1.57"
|
||||||
|
|
12
docs/CPM.md
12
docs/CPM.md
|
@ -23,7 +23,7 @@ CPMUtil is a wrapper around CPM that aims to reduce boilerplate and add useful u
|
||||||
|
|
||||||
- `NAME` (required): The package name (must be the same as the `find_package` name if applicable)
|
- `NAME` (required): The package name (must be the same as the `find_package` name if applicable)
|
||||||
- `VERSION`: The minimum version of this package that can be used on the system
|
- `VERSION`: The minimum version of this package that can be used on the system
|
||||||
- `GIT_VERSION`: The version found within git, only used for identification
|
- `GIT_VERSION`: The "version" found within git
|
||||||
- `URL`: The URL to fetch.
|
- `URL`: The URL to fetch.
|
||||||
- `REPO`: The GitHub repo to use (`owner/repo`).
|
- `REPO`: The GitHub repo to use (`owner/repo`).
|
||||||
* Only GitHub is supported for now, though other platforms will see support at some point
|
* Only GitHub is supported for now, though other platforms will see support at some point
|
||||||
|
@ -71,8 +71,9 @@ Hashing strategies, descending order of precedence:
|
||||||
- `KEY`: Custom cache key to use (stored as `.cache/cpm/${packagename_lower}/${key}`)
|
- `KEY`: Custom cache key to use (stored as `.cache/cpm/${packagename_lower}/${key}`)
|
||||||
* Default is based on, in descending order of precedence:
|
* Default is based on, in descending order of precedence:
|
||||||
- First 4 characters of the sha
|
- First 4 characters of the sha
|
||||||
- `GIT_VERSION`, or `VERSION` if not specified
|
- `GIT_VERSION`
|
||||||
- Tag
|
- Tag
|
||||||
|
- `VERSION`
|
||||||
- Otherwise, CPM defaults will be used. This is not recommended as it doesn't produce reproducible caches
|
- Otherwise, CPM defaults will be used. This is not recommended as it doesn't produce reproducible caches
|
||||||
- `DOWNLOAD_ONLY`: Whether or not to configure the downloaded package via CMake
|
- `DOWNLOAD_ONLY`: Whether or not to configure the downloaded package via CMake
|
||||||
* Useful to turn `OFF` if the project doesn't use CMake
|
* Useful to turn `OFF` if the project doesn't use CMake
|
||||||
|
@ -232,12 +233,9 @@ In order: OpenSSL CI, Boost (tag + artifact), discord-rpc (sha + options + patch
|
||||||
To include CPMUtil:
|
To include CPMUtil:
|
||||||
|
|
||||||
```cmake
|
```cmake
|
||||||
set(CPMUTIL_JSON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cpmfile.json)
|
|
||||||
include(CPMUtil)
|
include(CPMUtil)
|
||||||
```
|
```
|
||||||
|
|
||||||
You may omit the first line if you are not utilizing cpmfile.
|
|
||||||
|
|
||||||
## Prefetching
|
## Prefetching
|
||||||
|
|
||||||
- To prefetch a CPM dependency (requires cpmfile):
|
- To prefetch a CPM dependency (requires cpmfile):
|
||||||
|
@ -245,8 +243,8 @@ You may omit the first line if you are not utilizing cpmfile.
|
||||||
- To prefetch all CPM dependencies:
|
- To prefetch all CPM dependencies:
|
||||||
* `tools/cpm-fetch-all.sh`
|
* `tools/cpm-fetch-all.sh`
|
||||||
|
|
||||||
Currently, `cpm-fetch.sh` defines the following directories for cpmfiles:
|
Currently, `cpm-fetch.sh` defines the following directories for cpmfiles (max depth of 2, so subdirs are caught as well):
|
||||||
|
|
||||||
`externals src/yuzu/externals externals/ffmpeg src/dynarmic/externals externals/nx_tzdb`
|
`externals src/yuzu src/dynarmic .`
|
||||||
|
|
||||||
Whenever you add a new cpmfile, update the script accordingly
|
Whenever you add a new cpmfile, update the script accordingly
|
84
docs/build/Android.md
vendored
84
docs/build/Android.md
vendored
|
@ -1,42 +1,42 @@
|
||||||
# Note: These build instructions are a work-in-progress.
|
# Note: These build instructions are a work-in-progress.
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
* [Android Studio](https://developer.android.com/studio)
|
* [Android Studio](https://developer.android.com/studio)
|
||||||
* [NDK 25.2.9519653 and CMake 3.22.1](https://developer.android.com/studio/projects/install-ndk#default-version)
|
* [NDK 25.2.9519653 and CMake 3.22.1](https://developer.android.com/studio/projects/install-ndk#default-version)
|
||||||
* [Git](https://git-scm.com/download)
|
* [Git](https://git-scm.com/download)
|
||||||
|
|
||||||
### WINDOWS ONLY - Additional Dependencies
|
### WINDOWS ONLY - Additional Dependencies
|
||||||
* **[Visual Studio 2022 Community](https://visualstudio.microsoft.com/downloads/)** - **Make sure to select "Desktop development with C++" support in the installer. Make sure to update to the latest version if already installed.**
|
* **[Visual Studio 2022 Community](https://visualstudio.microsoft.com/downloads/)** - **Make sure to select "Desktop development with C++" support in the installer. Make sure to update to the latest version if already installed.**
|
||||||
* **[Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows)** - **Make sure to select Latest SDK.**
|
* **[Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows)** - **Make sure to select Latest SDK.**
|
||||||
- A convenience script to install the latest SDK is provided in `.ci\windows\install-vulkan-sdk.ps1`.
|
- A convenience script to install the latest SDK is provided in `.ci\windows\install-vulkan-sdk.ps1`.
|
||||||
|
|
||||||
## Cloning Eden with Git
|
## Cloning Eden with Git
|
||||||
```
|
```
|
||||||
git clone --recursive https://git.eden-emu.dev/eden-emu/eden.git
|
git clone --recursive https://git.eden-emu.dev/eden-emu/eden.git
|
||||||
```
|
```
|
||||||
Eden by default will be cloned into -
|
Eden by default will be cloned into -
|
||||||
* `C:\Users\<user-name>\eden` on Windows
|
* `C:\Users\<user-name>\eden` on Windows
|
||||||
* `~/eden` on Linux
|
* `~/eden` on Linux
|
||||||
* And wherever on macOS
|
* And wherever on macOS
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
1. Start Android Studio, on the startup dialog select `Open`.
|
1. Start Android Studio, on the startup dialog select `Open`.
|
||||||
2. Navigate to the `eden/src/android` directory and click on `OK`.
|
2. Navigate to the `eden/src/android` directory and click on `OK`.
|
||||||
3. In `Build > Select Build Variant`, select `release` or `relWithDebInfo` as the "Active build variant".
|
3. In `Build > Select Build Variant`, select `release` or `relWithDebInfo` as the "Active build variant".
|
||||||
4. Build the project with `Build > Make Project` or run it on an Android device with `Run > Run 'app'`.
|
4. Build the project with `Build > Make Project` or run it on an Android device with `Run > Run 'app'`.
|
||||||
|
|
||||||
## Building with Terminal
|
## Building with Terminal
|
||||||
1. Download the SDK and NDK from Android Studio.
|
1. Download the SDK and NDK from Android Studio.
|
||||||
2. Navigate to SDK and NDK paths.
|
2. Navigate to SDK and NDK paths.
|
||||||
3. Then set ANDROID_SDK_ROOT and ANDROID_NDK_ROOT in terminal via
|
3. Then set ANDROID_SDK_ROOT and ANDROID_NDK_ROOT in terminal via
|
||||||
`export ANDROID_SDK_ROOT=path/to/sdk`
|
`export ANDROID_SDK_ROOT=path/to/sdk`
|
||||||
`export ANDROID_NDK_ROOT=path/to/ndk`.
|
`export ANDROID_NDK_ROOT=path/to/ndk`.
|
||||||
4. Navigate to `eden/src/android`.
|
4. Navigate to `eden/src/android`.
|
||||||
5. Then Build with `./gradlew assemblerelWithDebInfo`.
|
5. Then Build with `./gradlew assemblerelWithDebInfo`.
|
||||||
6. To build the optimised build use `./gradlew assembleGenshinSpoofRelWithDebInfo`.
|
6. To build the optimised build use `./gradlew assembleGenshinSpoofRelWithDebInfo`.
|
||||||
|
|
||||||
### Script
|
### Script
|
||||||
A convenience script for building is provided in `.ci/android/build.sh`. The built APK can be put into an `artifacts` directory via `.ci/android/package.sh`. On Windows, these must be done in the Git Bash or MinGW terminal.
|
A convenience script for building is provided in `.ci/android/build.sh`. The built APK can be put into an `artifacts` directory via `.ci/android/package.sh`. On Windows, these must be done in the Git Bash or MinGW terminal.
|
||||||
|
|
||||||
### Additional Resources
|
### Additional Resources
|
||||||
https://developer.android.com/studio/intro
|
https://developer.android.com/studio/intro
|
||||||
|
|
164
docs/build/FreeBSD.md
vendored
164
docs/build/FreeBSD.md
vendored
|
@ -1,85 +1,81 @@
|
||||||
## One word of caution before proceeding.
|
Eden is not currently available as a port on FreeBSD, though it is in the works. For now, the recommended method of usage is to compile it yourself. Check back often, as the build process frequently changes.
|
||||||
|
|
||||||
This is not the usual or preferred way to build programs on FreeBSD.
|
## Dependencies.
|
||||||
As of writing there is no official fresh port available for Eden, but it is in the works.
|
Eden needs the following dependencies:
|
||||||
After it is available you can find a link to the eden-emu fresh port here and on Escary's github repo.
|
|
||||||
See this build as an AppImage alternative for FreeBSD.
|
```
|
||||||
|
devel/cmake
|
||||||
## Dependencies.
|
devel/sdl20
|
||||||
Before we start we need some dependencies.
|
devel/boost-libs
|
||||||
These dependencies are generally needed to build Eden on FreeBSD.
|
devel/catch2
|
||||||
|
devel/libfmt
|
||||||
```
|
devel/nlohmann-json
|
||||||
devel/cmake
|
devel/ninja
|
||||||
devel/sdl20
|
devel/nasm
|
||||||
devel/boost-libs
|
devel/autoconf
|
||||||
devel/catch2
|
devel/pkgconf
|
||||||
devel/libfmt
|
devel/qt6-base
|
||||||
devel/nlohmann-json
|
|
||||||
devel/ninja
|
net/enet
|
||||||
devel/nasm
|
|
||||||
devel/autoconf
|
multimedia/ffnvcodec-headers
|
||||||
devel/pkgconf
|
multimedia/ffmpeg
|
||||||
devel/qt6-base
|
|
||||||
|
audio/opus
|
||||||
multimedia/ffnvcodec-headers
|
|
||||||
multimedia/ffmpeg
|
archivers/liblz4
|
||||||
|
|
||||||
audio/opus
|
lang/gcc12
|
||||||
|
|
||||||
archivers/liblz4
|
graphics/glslang
|
||||||
|
graphics/vulkan-utility-libraries
|
||||||
lang/gcc12
|
```
|
||||||
|
|
||||||
graphics/glslang
|
If using FreeBSD 12 or prior, use `devel/pkg-config` instead.
|
||||||
graphics/vulkan-utility-libraries
|
|
||||||
```
|
---
|
||||||
|
|
||||||
If using FreeBSD 12 or prior, use `devel/pkg-config` instead.
|
### Build preparations:
|
||||||
|
Run the following command to clone eden with git:
|
||||||
---
|
```sh
|
||||||
|
git clone --recursive https://git.eden-emu.dev/eden-emu/eden
|
||||||
### Build preparations:
|
```
|
||||||
Run the following command to clone eden with git:
|
You usually want to add the `--recursive` parameter as it also takes care of the external dependencies for you.
|
||||||
```sh
|
|
||||||
git clone --recursive https://git.eden-emu.dev/eden-emu/eden
|
Now change into the eden directory and create a build directory there:
|
||||||
```
|
```sh
|
||||||
You usually want to add the `--recursive` parameter as it also takes care of the external dependencies for you.
|
cd eden
|
||||||
|
mkdir build
|
||||||
Now change into the eden directory and create a build directory there:
|
```
|
||||||
```sh
|
|
||||||
cd eden
|
Change into that build directory:
|
||||||
mkdir build
|
```sh
|
||||||
```
|
cd build
|
||||||
|
```
|
||||||
Change into that build directory:
|
|
||||||
```sh
|
#### 1. Building in Release Mode (usually preferred and the most performant choice):
|
||||||
cd build
|
```sh
|
||||||
```
|
cmake .. -GNinja -DYUZU_TESTS=OFF
|
||||||
|
```
|
||||||
#### 1. Building in Release Mode (usually preferred and the most performant choice):
|
|
||||||
```sh
|
#### 2. Building in Release Mode with debugging symbols (useful if you want to debug errors for a eventual fix):
|
||||||
cmake .. -GNinja -DYUZU_TESTS=OFF
|
```sh
|
||||||
```
|
cmake .. -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU_TESTS=ON
|
||||||
|
```
|
||||||
#### 2. Building in Release Mode with debugging symbols (useful if you want to debug errors for a eventual fix):
|
|
||||||
```sh
|
Build the emulator locally:
|
||||||
cmake .. -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU_TESTS=ON
|
```sh
|
||||||
```
|
ninja
|
||||||
|
```
|
||||||
Build the emulator locally:
|
|
||||||
```sh
|
Optional: If you wish to install eden globally onto your system issue the following command:
|
||||||
ninja
|
```sh
|
||||||
```
|
sudo ninja install
|
||||||
|
```
|
||||||
Optional: If you wish to install eden globally onto your system issue the following command:
|
OR
|
||||||
```sh
|
```sh
|
||||||
sudo ninja install
|
doas -- ninja install
|
||||||
```
|
```
|
||||||
OR
|
|
||||||
```sh
|
## OpenSSL
|
||||||
doas -- ninja install
|
|
||||||
```
|
|
||||||
|
|
||||||
## OpenSSL
|
|
||||||
The available OpenSSL port (3.0.17) is out-of-date, and using a bundled static library instead is recommended; to do so, add `-DYUZU_USE_CPM=ON` to your CMake configure command.
|
The available OpenSSL port (3.0.17) is out-of-date, and using a bundled static library instead is recommended; to do so, add `-DYUZU_USE_CPM=ON` to your CMake configure command.
|
276
docs/build/Linux.md
vendored
276
docs/build/Linux.md
vendored
|
@ -1,138 +1,138 @@
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
You'll need to download and install the following to build Eden:
|
You'll need to download and install the following to build Eden:
|
||||||
|
|
||||||
* [GCC](https://gcc.gnu.org/) v11+ (for C++20 support) & misc
|
* [GCC](https://gcc.gnu.org/) v11+ (for C++20 support) & misc
|
||||||
* If GCC 12 is installed, [Clang](https://clang.llvm.org/) v14+ is required for compiling
|
* If GCC 12 is installed, [Clang](https://clang.llvm.org/) v14+ is required for compiling
|
||||||
* [CMake](https://www.cmake.org/) 3.22+
|
* [CMake](https://www.cmake.org/) 3.22+
|
||||||
|
|
||||||
The following are handled by Eden's externals:
|
The following are handled by Eden's externals:
|
||||||
|
|
||||||
* [FFmpeg](https://ffmpeg.org/)
|
* [FFmpeg](https://ffmpeg.org/)
|
||||||
* [SDL2](https://www.libsdl.org/download-2.0.php) 2.0.18+
|
* [SDL2](https://www.libsdl.org/download-2.0.php) 2.0.18+
|
||||||
* [opus](https://opus-codec.org/downloads/) 1.3+
|
* [opus](https://opus-codec.org/downloads/) 1.3+
|
||||||
|
|
||||||
All other dependencies will be downloaded and built by [CPM](https://github.com/cpm-cmake/CPM.cmake/) if `YUZU_USE_CPM` is on, but will always use system dependencies if available:
|
All other dependencies will be downloaded and built by [CPM](https://github.com/cpm-cmake/CPM.cmake/) if `YUZU_USE_CPM` is on, but will always use system dependencies if available:
|
||||||
|
|
||||||
* [Boost](https://www.boost.org/users/download/) 1.79.0+
|
* [Boost](https://www.boost.org/users/download/) 1.79.0+
|
||||||
* [Catch2](https://github.com/catchorg/Catch2) 2.13.7 - 2.13.9
|
* [Catch2](https://github.com/catchorg/Catch2) 2.13.7 - 2.13.9
|
||||||
* [fmt](https://fmt.dev/) 8.0.1+
|
* [fmt](https://fmt.dev/) 8.0.1+
|
||||||
* [lz4](http://www.lz4.org) 1.8+
|
* [lz4](http://www.lz4.org) 1.8+
|
||||||
* [nlohmann_json](https://github.com/nlohmann/json) 3.8+
|
* [nlohmann_json](https://github.com/nlohmann/json) 3.8+
|
||||||
* [OpenSSL](https://www.openssl.org/source/) 1.1.1+
|
* [OpenSSL](https://www.openssl.org/source/) 1.1.1+
|
||||||
* [ZLIB](https://www.zlib.net/) 1.2+
|
* [ZLIB](https://www.zlib.net/) 1.2+
|
||||||
* [zstd](https://facebook.github.io/zstd/) 1.5+
|
* [zstd](https://facebook.github.io/zstd/) 1.5+
|
||||||
* [enet](http://enet.bespin.org/) 1.3+
|
* [enet](http://enet.bespin.org/) 1.3+
|
||||||
* [cubeb](https://github.com/mozilla/cubeb)
|
* [cubeb](https://github.com/mozilla/cubeb)
|
||||||
* [SimpleIni](https://github.com/brofield/simpleini)
|
* [SimpleIni](https://github.com/brofield/simpleini)
|
||||||
|
|
||||||
Certain other dependencies (httplib, jwt, sirit, etc.) will be fetched by CPM regardless. System packages *can* be used for these libraries but this is generally not recommended.
|
Certain other dependencies (httplib, jwt, sirit, etc.) will be fetched by CPM regardless. System packages *can* be used for these libraries but this is generally not recommended.
|
||||||
|
|
||||||
Dependencies are listed here as commands that can be copied/pasted. Of course, they should be inspected before being run.
|
Dependencies are listed here as commands that can be copied/pasted. Of course, they should be inspected before being run.
|
||||||
|
|
||||||
- Arch / Manjaro:
|
- Arch / Manjaro:
|
||||||
- `sudo pacman -Syu --needed base-devel boost catch2 cmake enet ffmpeg fmt git glslang libzip lz4 mbedtls ninja nlohmann-json openssl opus qt6-base qt6-multimedia sdl2 zlib zstd zip unzip`
|
- `sudo pacman -Syu --needed base-devel boost catch2 cmake enet ffmpeg fmt git glslang libzip lz4 mbedtls ninja nlohmann-json openssl opus qt6-base qt6-multimedia sdl2 zlib zstd zip unzip`
|
||||||
- Building with QT Web Engine requires `qt6-webengine` as well.
|
- Building with QT Web Engine requires `qt6-webengine` as well.
|
||||||
- Proper wayland support requires `qt6-wayland`
|
- Proper wayland support requires `qt6-wayland`
|
||||||
- GCC 11 or later is required.
|
- GCC 11 or later is required.
|
||||||
|
|
||||||
- Ubuntu / Linux Mint / Debian:
|
- Ubuntu / Linux Mint / Debian:
|
||||||
- `sudo apt-get install autoconf cmake g++ gcc git glslang-tools libasound2 libboost-context-dev libglu1-mesa-dev libhidapi-dev libpulse-dev libtool libudev-dev libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-xinerama0 libxcb-xkb1 libxext-dev libxkbcommon-x11-0 mesa-common-dev nasm ninja-build qt6-base-private-dev libmbedtls-dev catch2 libfmt-dev liblz4-dev nlohmann-json3-dev libzstd-dev libssl-dev libavfilter-dev libavcodec-dev libswscale-dev pkg-config zlib1g-dev libva-dev libvdpau-dev`
|
- `sudo apt-get install autoconf cmake g++ gcc git glslang-tools libasound2 libboost-context-dev libglu1-mesa-dev libhidapi-dev libpulse-dev libtool libudev-dev libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-xinerama0 libxcb-xkb1 libxext-dev libxkbcommon-x11-0 mesa-common-dev nasm ninja-build qt6-base-private-dev libmbedtls-dev catch2 libfmt-dev liblz4-dev nlohmann-json3-dev libzstd-dev libssl-dev libavfilter-dev libavcodec-dev libswscale-dev pkg-config zlib1g-dev libva-dev libvdpau-dev`
|
||||||
- Ubuntu 22.04, Linux Mint 20, or Debian 12 or later is required.
|
- Ubuntu 22.04, Linux Mint 20, or Debian 12 or later is required.
|
||||||
- Users need to manually specify building with QT Web Engine enabled. This is done using the parameter `-DYUZU_USE_QT_WEB_ENGINE=ON` when running CMake.
|
- Users need to manually specify building with QT Web Engine enabled. This is done using the parameter `-DYUZU_USE_QT_WEB_ENGINE=ON` when running CMake.
|
||||||
- Users need to manually disable building SDL2 from externals if they intend to use the version provided by their system by adding the parameters `-DYUZU_USE_EXTERNAL_SDL2=OFF`
|
- Users need to manually disable building SDL2 from externals if they intend to use the version provided by their system by adding the parameters `-DYUZU_USE_EXTERNAL_SDL2=OFF`
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git submodule update --init --recursive
|
git submodule update --init --recursive
|
||||||
cmake .. -GNinja -DCMAKE_C_COMPILER=gcc-11 -DCMAKE_CXX_COMPILER=g++-11
|
cmake .. -GNinja -DCMAKE_C_COMPILER=gcc-11 -DCMAKE_CXX_COMPILER=g++-11
|
||||||
```
|
```
|
||||||
|
|
||||||
- Fedora:
|
- Fedora:
|
||||||
- `sudo dnf install autoconf ccache cmake fmt-devel gcc{,-c++} glslang hidapi-devel json-devel libtool libusb1-devel libzstd-devel lz4-devel nasm ninja-build openssl-devel pulseaudio-libs-devel qt6-linguist qt6-qtbase{-private,}-devel qt6-qtwebengine-devel qt6-qtmultimedia-devel speexdsp-devel wayland-devel zlib-devel ffmpeg-devel libXext-devel`
|
- `sudo dnf install autoconf ccache cmake fmt-devel gcc{,-c++} glslang hidapi-devel json-devel libtool libusb1-devel libzstd-devel lz4-devel nasm ninja-build openssl-devel pulseaudio-libs-devel qt6-linguist qt6-qtbase{-private,}-devel qt6-qtwebengine-devel qt6-qtmultimedia-devel speexdsp-devel wayland-devel zlib-devel ffmpeg-devel libXext-devel`
|
||||||
- Fedora 32 or later is required.
|
- Fedora 32 or later is required.
|
||||||
- Due to GCC 12, Fedora 36 or later users need to install `clang`, and configure CMake to use it via `-DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang`
|
- Due to GCC 12, Fedora 36 or later users need to install `clang`, and configure CMake to use it via `-DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang`
|
||||||
- CMake arguments to force system libraries:
|
- CMake arguments to force system libraries:
|
||||||
- SDL2: `-DYUZU_USE_BUNDLED_SDL2=OFF -DYUZU_USE_EXTERNAL_SDL2=OFF`
|
- SDL2: `-DYUZU_USE_BUNDLED_SDL2=OFF -DYUZU_USE_EXTERNAL_SDL2=OFF`
|
||||||
- FFmpeg: `-DYUZU_USE_EXTERNAL_FFMPEG=OFF`
|
- FFmpeg: `-DYUZU_USE_EXTERNAL_FFMPEG=OFF`
|
||||||
- [RPM Fusion](https://rpmfusion.org/) (free) is required to install `ffmpeg-devel`
|
- [RPM Fusion](https://rpmfusion.org/) (free) is required to install `ffmpeg-devel`
|
||||||
|
|
||||||
### Cloning Eden with Git
|
### Cloning Eden with Git
|
||||||
|
|
||||||
**Master:**
|
**Master:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone --recursive https://git.eden-emu.dev/eden-emu/eden
|
git clone --recursive https://git.eden-emu.dev/eden-emu/eden
|
||||||
cd eden
|
cd eden
|
||||||
```
|
```
|
||||||
|
|
||||||
The `--recursive` option automatically clones the required Git submodules.
|
The `--recursive` option automatically clones the required Git submodules.
|
||||||
|
|
||||||
### Building Eden in Release Mode (Optimised)
|
### Building Eden in Release Mode (Optimised)
|
||||||
|
|
||||||
If you need to run ctests, you can disable `-DYUZU_TESTS=OFF` and install Catch2.
|
If you need to run ctests, you can disable `-DYUZU_TESTS=OFF` and install Catch2.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
cmake .. -GNinja -DYUZU_TESTS=OFF
|
cmake .. -GNinja -DYUZU_TESTS=OFF
|
||||||
ninja
|
ninja
|
||||||
sudo ninja install
|
sudo ninja install
|
||||||
```
|
```
|
||||||
You may also want to include support for Discord Rich Presence by adding `-DUSE_DISCORD_PRESENCE=ON` after `cmake ..`
|
You may also want to include support for Discord Rich Presence by adding `-DUSE_DISCORD_PRESENCE=ON` after `cmake ..`
|
||||||
|
|
||||||
`-DYUZU_USE_EXTERNAL_VULKAN_SPIRV_TOOLS=OFF` might be needed if ninja command failed with `undefined reference to symbol 'spvOptimizerOptionsCreate`, reason currently unknown
|
`-DYUZU_USE_EXTERNAL_VULKAN_SPIRV_TOOLS=OFF` might be needed if ninja command failed with `undefined reference to symbol 'spvOptimizerOptionsCreate`, reason currently unknown
|
||||||
|
|
||||||
Optionally, you can use `cmake-gui ..` to adjust various options (e.g. disable the Qt GUI).
|
Optionally, you can use `cmake-gui ..` to adjust various options (e.g. disable the Qt GUI).
|
||||||
|
|
||||||
### Building Eden in Debug Mode (Slow)
|
### Building Eden in Debug Mode (Slow)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
cmake .. -GNinja -DCMAKE_BUILD_TYPE=Debug -DYUZU_TESTS=OFF
|
cmake .. -GNinja -DCMAKE_BUILD_TYPE=Debug -DYUZU_TESTS=OFF
|
||||||
ninja
|
ninja
|
||||||
```
|
```
|
||||||
|
|
||||||
### Building with debug symbols
|
### Building with debug symbols
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
cmake .. -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU -DYUZU_TESTS=OFF
|
cmake .. -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU -DYUZU_TESTS=OFF
|
||||||
ninja
|
ninja
|
||||||
```
|
```
|
||||||
|
|
||||||
### Building with Scripts
|
### Building with Scripts
|
||||||
A convenience script for building is provided in `.ci/linux/build.sh`. You must provide an arch target for optimization, e.g. `.ci/linux/build.sh amd64`. Valid targets:
|
A convenience script for building is provided in `.ci/linux/build.sh`. You must provide an arch target for optimization, e.g. `.ci/linux/build.sh amd64`. Valid targets:
|
||||||
- `legacy`: x86_64 generic, only needed for CPUs older than 2013 or so
|
- `legacy`: x86_64 generic, only needed for CPUs older than 2013 or so
|
||||||
- `amd64`: x86_64-v3, for CPUs newer than 2013 or so
|
- `amd64`: x86_64-v3, for CPUs newer than 2013 or so
|
||||||
- `steamdeck` / `zen2`: For Steam Deck or Zen >= 2 AMD CPUs (untested on Intel)
|
- `steamdeck` / `zen2`: For Steam Deck or Zen >= 2 AMD CPUs (untested on Intel)
|
||||||
- `rog-ally` / `allyx` / `zen4`: For ROG Ally X or Zen >= 4 AMD CPUs (untested on Intel)
|
- `rog-ally` / `allyx` / `zen4`: For ROG Ally X or Zen >= 4 AMD CPUs (untested on Intel)
|
||||||
- `aarch64`: For armv8-a CPUs, older than mid-2021 or so
|
- `aarch64`: For armv8-a CPUs, older than mid-2021 or so
|
||||||
- `armv9`: For armv9-a CPUs, newer than mid-2021 or so
|
- `armv9`: For armv9-a CPUs, newer than mid-2021 or so
|
||||||
- `native`: Optimize to your native host architecture
|
- `native`: Optimize to your native host architecture
|
||||||
|
|
||||||
Extra flags to pass to CMake should be passed after the arch target.
|
Extra flags to pass to CMake should be passed after the arch target.
|
||||||
|
|
||||||
Additional environment variables can be used to control building:
|
Additional environment variables can be used to control building:
|
||||||
- `NPROC`: Number of threads to use for compilation (defaults to all)
|
- `NPROC`: Number of threads to use for compilation (defaults to all)
|
||||||
- `TARGET`: Set to `appimage` to disable standalone `eden-cli` and `eden-room` executables
|
- `TARGET`: Set to `appimage` to disable standalone `eden-cli` and `eden-room` executables
|
||||||
- `BUILD_TYPE`: Sets the build type to use. Defaults to `Release`
|
- `BUILD_TYPE`: Sets the build type to use. Defaults to `Release`
|
||||||
|
|
||||||
The following environment variables are boolean flags. Set to `true` to enable or `false` to disable:
|
The following environment variables are boolean flags. Set to `true` to enable or `false` to disable:
|
||||||
- `DEVEL` (default FALSE): Disable Qt update checker
|
- `DEVEL` (default FALSE): Disable Qt update checker
|
||||||
- `USE_WEBENGINE` (default FALSE): Enable Qt WebEngine
|
- `USE_WEBENGINE` (default FALSE): Enable Qt WebEngine
|
||||||
- `USE_MULTIMEDIA` (default TRUE): Enable Qt Multimedia
|
- `USE_MULTIMEDIA` (default TRUE): Enable Qt Multimedia
|
||||||
|
|
||||||
After building, an AppImage can be packaged via `.ci/linux/package.sh`. This script takes the same arch targets as the build script. If the build was created in a different directory, you can specify its path relative to the source directory, e.g. `.ci/linux/package.sh amd64 build-appimage`. Additionally, set the `DEVEL` environment variable to `true` to change the app name to `Eden Nightly`.
|
After building, an AppImage can be packaged via `.ci/linux/package.sh`. This script takes the same arch targets as the build script. If the build was created in a different directory, you can specify its path relative to the source directory, e.g. `.ci/linux/package.sh amd64 build-appimage`. Additionally, set the `DEVEL` environment variable to `true` to change the app name to `Eden Nightly`.
|
||||||
|
|
||||||
### Running without installing
|
### Running without installing
|
||||||
|
|
||||||
After building, the binaries `eden` and `eden-cmd` (depending on your build options) will end up in `build/bin/`.
|
After building, the binaries `eden` and `eden-cmd` (depending on your build options) will end up in `build/bin/`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# SDL
|
# SDL
|
||||||
cd build/bin/
|
cd build/bin/
|
||||||
./eden-cmd
|
./eden-cmd
|
||||||
|
|
||||||
# Qt
|
# Qt
|
||||||
cd build/bin/
|
cd build/bin/
|
||||||
./eden
|
./eden
|
||||||
```
|
```
|
||||||
|
|
100
docs/build/Solaris.md
vendored
100
docs/build/Solaris.md
vendored
|
@ -1,51 +1,51 @@
|
||||||
# Building for Solaris
|
# Building for Solaris
|
||||||
|
|
||||||
## Dependencies.
|
## Dependencies.
|
||||||
Always consult [the OpenIndiana package list](https://pkg.openindiana.org/hipster/en/index.shtml) to cross-verify availability.
|
Always consult [the OpenIndiana package list](https://pkg.openindiana.org/hipster/en/index.shtml) to cross-verify availability.
|
||||||
|
|
||||||
Run the usual update + install of essential toolings: `sudo pkg update && sudo pkg install git cmake`.
|
Run the usual update + install of essential toolings: `sudo pkg update && sudo pkg install git cmake`.
|
||||||
|
|
||||||
- **gcc**: `sudo pkg install developer/gcc-14`.
|
- **gcc**: `sudo pkg install developer/gcc-14`.
|
||||||
- **clang**: Version 20 is broken, use `sudo pkg install developer/clang-19`.
|
- **clang**: Version 20 is broken, use `sudo pkg install developer/clang-19`.
|
||||||
|
|
||||||
Then install the libraies: `sudo pkg install qt6 boost glslang libzip library/lz4 nlohmann-json openssl opus sdl2 zlib compress/zstd unzip pkg-config nasm autoconf mesa library/libdrm header-drm developer/fmt`.
|
Then install the libraies: `sudo pkg install qt6 boost glslang libzip library/lz4 nlohmann-json openssl opus sdl2 zlib compress/zstd unzip pkg-config nasm autoconf mesa library/libdrm header-drm developer/fmt`.
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
Clone eden with git `git clone --recursive https://git.eden-emu.dev/eden-emu/eden`
|
Clone eden with git `git clone --recursive https://git.eden-emu.dev/eden-emu/eden`
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# Needed for some dependencies that call cc directly (tz)
|
# Needed for some dependencies that call cc directly (tz)
|
||||||
echo '#!/bin/sh' >cc
|
echo '#!/bin/sh' >cc
|
||||||
echo 'gcc $@' >>cc
|
echo 'gcc $@' >>cc
|
||||||
chmod +x cc
|
chmod +x cc
|
||||||
export PATH="$PATH:$PWD"
|
export PATH="$PATH:$PWD"
|
||||||
```
|
```
|
||||||
|
|
||||||
Patch for FFmpeg:
|
Patch for FFmpeg:
|
||||||
```sh
|
```sh
|
||||||
sed -i 's/ make / gmake /' externals/ffmpeg/CMakeFiles/ffmpeg-build.dir/build.make
|
sed -i 's/ make / gmake /' externals/ffmpeg/CMakeFiles/ffmpeg-build.dir/build.make
|
||||||
```
|
```
|
||||||
|
|
||||||
- **Configure**: `cmake -B build -DYUZU_USE_CPM=ON -DCMAKE_CXX_FLAGS="-I/usr/include/SDL2" -DCMAKE_C_FLAGS="-I/usr/include/SDL2"`.
|
- **Configure**: `cmake -B build -DYUZU_USE_CPM=ON -DCMAKE_CXX_FLAGS="-I/usr/include/SDL2" -DCMAKE_C_FLAGS="-I/usr/include/SDL2"`.
|
||||||
- **Build**: `cmake --build build`.
|
- **Build**: `cmake --build build`.
|
||||||
- **Installing**: `sudo cmake --install build`.
|
- **Installing**: `sudo cmake --install build`.
|
||||||
|
|
||||||
### Running
|
### Running
|
||||||
|
|
||||||
Default Mesa is a bit outdated, the following environment variables should be set for a smoother experience:
|
Default Mesa is a bit outdated, the following environment variables should be set for a smoother experience:
|
||||||
```sh
|
```sh
|
||||||
export MESA_GL_VERSION_OVERRIDE=4.6
|
export MESA_GL_VERSION_OVERRIDE=4.6
|
||||||
export MESA_GLSL_VERSION_OVERRIDE=460
|
export MESA_GLSL_VERSION_OVERRIDE=460
|
||||||
export MESA_EXTENSION_MAX_YEAR=2025
|
export MESA_EXTENSION_MAX_YEAR=2025
|
||||||
export MESA_DEBUG=1
|
export MESA_DEBUG=1
|
||||||
export MESA_VK_VERSION_OVERRIDE=1.3
|
export MESA_VK_VERSION_OVERRIDE=1.3
|
||||||
# Only if nvidia/intel drm drivers cause crashes, will severely hinder performance
|
# Only if nvidia/intel drm drivers cause crashes, will severely hinder performance
|
||||||
export LIBGL_ALWAYS_SOFTWARE=1
|
export LIBGL_ALWAYS_SOFTWARE=1
|
||||||
```
|
```
|
||||||
|
|
||||||
### Notes
|
### Notes
|
||||||
|
|
||||||
- Modify the generated ffmpeg.make (in build dir) if using multiple threads (base system `make` doesn't use `-j4`, so change for `gmake`).
|
- Modify the generated ffmpeg.make (in build dir) if using multiple threads (base system `make` doesn't use `-j4`, so change for `gmake`).
|
||||||
- If using OpenIndiana, due to a bug in SDL2 cmake configuration; Audio driver defaults to SunOS `<sys/audioio.h>`, which does not exist on OpenIndiana.
|
- If using OpenIndiana, due to a bug in SDL2 cmake configuration; Audio driver defaults to SunOS `<sys/audioio.h>`, which does not exist on OpenIndiana.
|
||||||
- System OpenSSL generally does not work. Instead, use `-DYUZU_USE_CPM=ON` to use a bundled static OpenSSL, or build a system dependency from source.
|
- System OpenSSL generally does not work. Instead, use `-DYUZU_USE_CPM=ON` to use a bundled static OpenSSL, or build a system dependency from source.
|
386
docs/build/Windows.md
vendored
386
docs/build/Windows.md
vendored
|
@ -1,193 +1,193 @@
|
||||||
# THIS GUIDE IS INTENDED FOR DEVELOPERS ONLY, SUPPORT WILL ONLY BE GIVEN IF YOU'RE A DEVELOPER.
|
# THIS GUIDE IS INTENDED FOR DEVELOPERS ONLY, SUPPORT WILL ONLY BE GIVEN IF YOU'RE A DEVELOPER.
|
||||||
|
|
||||||
## Method I: MSVC Build for Windows
|
## Method I: MSVC Build for Windows
|
||||||
|
|
||||||
### Minimal Dependencies
|
### Minimal Dependencies
|
||||||
|
|
||||||
On Windows, all library dependencies are automatically included within the `externals` folder, or can be downloaded on-demand. To build Eden, you need to install:
|
On Windows, all library dependencies are automatically included within the `externals` folder, or can be downloaded on-demand. To build Eden, you need to install:
|
||||||
|
|
||||||
* **[Visual Studio 2022 Community](https://visualstudio.microsoft.com/downloads/)** - **Make sure to select C++ support in the installer. Make sure to update to the latest version if already installed.**
|
* **[Visual Studio 2022 Community](https://visualstudio.microsoft.com/downloads/)** - **Make sure to select C++ support in the installer. Make sure to update to the latest version if already installed.**
|
||||||
* **[CMake](https://cmake.org/download/)** - Used to generate Visual Studio project files. Does not matter if either 32-bit or 64-bit version is installed.
|
* **[CMake](https://cmake.org/download/)** - Used to generate Visual Studio project files. Does not matter if either 32-bit or 64-bit version is installed.
|
||||||
* **[Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows)** - **Make sure to select Latest SDK.**
|
* **[Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows)** - **Make sure to select Latest SDK.**
|
||||||
- A convenience script to install the latest SDK is provided in `.ci\windows\install-vulkan-sdk.ps1`.
|
- A convenience script to install the latest SDK is provided in `.ci\windows\install-vulkan-sdk.ps1`.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
* **Git** - We recommend [Git for Windows](https://gitforwindows.org).
|
* **Git** - We recommend [Git for Windows](https://gitforwindows.org).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
* While installing Git Bash, you should tell it to include Git in your system path. (Choose the "Git from the command line and also from 3rd-party software" option.) If you missed that, don't worry, you'll just have to manually tell CMake where your git.exe is, since it's used to include version info into the built executable.
|
* While installing Git Bash, you should tell it to include Git in your system path. (Choose the "Git from the command line and also from 3rd-party software" option.) If you missed that, don't worry, you'll just have to manually tell CMake where your git.exe is, since it's used to include version info into the built executable.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### Cloning Eden with Git
|
### Cloning Eden with Git
|
||||||
|
|
||||||
**Master:**
|
**Master:**
|
||||||
```cmd
|
```cmd
|
||||||
git clone --recursive https://git.eden-emu.dev/eden-emu/eden
|
git clone --recursive https://git.eden-emu.dev/eden-emu/eden
|
||||||
cd eden
|
cd eden
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
* *(Note: eden by default downloads to `C:\Users\<user-name>\eden` (Master)
|
* *(Note: eden by default downloads to `C:\Users\<user-name>\eden` (Master)
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
* Open the CMake GUI application and point it to the `eden` (Master)
|
* Open the CMake GUI application and point it to the `eden` (Master)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
* For the build directory, use a `/build` subdirectory inside the source directory or some other directory of your choice. (Tell CMake to create it.)
|
* For the build directory, use a `/build` subdirectory inside the source directory or some other directory of your choice. (Tell CMake to create it.)
|
||||||
|
|
||||||
* Click the "Configure" button and choose `Visual Studio 17 2022`, with `x64` for the optional platform.
|
* Click the "Configure" button and choose `Visual Studio 17 2022`, with `x64` for the optional platform.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
* *(Note: If you used GitHub's own app to clone, run `git submodule update --init --recursive` to get the remaining dependencies)*
|
* *(Note: If you used GitHub's own app to clone, run `git submodule update --init --recursive` to get the remaining dependencies)*
|
||||||
|
|
||||||
* *(You may also want to disable `YUZU_TESTS` in this case since Catch2 is not yet supported with this.)*
|
* *(You may also want to disable `YUZU_TESTS` in this case since Catch2 is not yet supported with this.)*
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
* Click "Generate" to create the project files.
|
* Click "Generate" to create the project files.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
* Open the solution file `yuzu.sln` in Visual Studio 2022, which is located in the build folder.
|
* Open the solution file `yuzu.sln` in Visual Studio 2022, which is located in the build folder.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
* Depending if you want a graphical user interface or not (`eden` has the graphical user interface, while `eden-cmd` doesn't), select `eden` or `eden-cmd` in the Solution Explorer, right-click and `Set as StartUp Project`.
|
* Depending if you want a graphical user interface or not (`eden` has the graphical user interface, while `eden-cmd` doesn't), select `eden` or `eden-cmd` in the Solution Explorer, right-click and `Set as StartUp Project`.
|
||||||
|
|
||||||
 
|
 
|
||||||
|
|
||||||
* Select the appropriate build type, Debug for debug purposes or Release for performance (in case of doubt choose Release).
|
* Select the appropriate build type, Debug for debug purposes or Release for performance (in case of doubt choose Release).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
* Right-click the project you want to build and press Build in the submenu or press F5.
|
* Right-click the project you want to build and press Build in the submenu or press F5.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Method II: MinGW-w64 Build with MSYS2
|
## Method II: MinGW-w64 Build with MSYS2
|
||||||
|
|
||||||
### Prerequisites to install
|
### Prerequisites to install
|
||||||
|
|
||||||
* [MSYS2](https://www.msys2.org)
|
* [MSYS2](https://www.msys2.org)
|
||||||
* [Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows) - **Make sure to select Latest SDK.**
|
* [Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows) - **Make sure to select Latest SDK.**
|
||||||
* Make sure to follow the instructions and update to the latest version by running `pacman -Syu` as many times as needed.
|
* Make sure to follow the instructions and update to the latest version by running `pacman -Syu` as many times as needed.
|
||||||
|
|
||||||
### Install eden dependencies for MinGW-w64
|
### Install eden dependencies for MinGW-w64
|
||||||
|
|
||||||
* Open the `MSYS2 MinGW 64-bit` (mingw64.exe) shell
|
* Open the `MSYS2 MinGW 64-bit` (mingw64.exe) shell
|
||||||
* Download and install all dependencies using: `pacman -Syu git make mingw-w64-x86_64-SDL2 mingw-w64-x86_64-cmake mingw-w64-x86_64-python-pip mingw-w64-x86_64-qt6 mingw-w64-x86_64-toolchain autoconf libtool automake-wrapper`
|
* Download and install all dependencies using: `pacman -Syu git make mingw-w64-x86_64-SDL2 mingw-w64-x86_64-cmake mingw-w64-x86_64-python-pip mingw-w64-x86_64-qt6 mingw-w64-x86_64-toolchain autoconf libtool automake-wrapper`
|
||||||
* Add MinGW binaries to the PATH: `echo 'PATH=/mingw64/bin:$PATH' >> ~/.bashrc`
|
* Add MinGW binaries to the PATH: `echo 'PATH=/mingw64/bin:$PATH' >> ~/.bashrc`
|
||||||
* Add glslangValidator to the PATH: `echo 'PATH=$(readlink -e /c/VulkanSDK/*/Bin/):$PATH' >> ~/.bashrc`
|
* Add glslangValidator to the PATH: `echo 'PATH=$(readlink -e /c/VulkanSDK/*/Bin/):$PATH' >> ~/.bashrc`
|
||||||
|
|
||||||
### Clone the eden repository with Git
|
### Clone the eden repository with Git
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone --recursive https://git.eden-emu.dev/eden-emu/eden
|
git clone --recursive https://git.eden-emu.dev/eden-emu/eden
|
||||||
cd eden
|
cd eden
|
||||||
```
|
```
|
||||||
|
|
||||||
### Run the following commands to build eden (dynamically linked build)
|
### Run the following commands to build eden (dynamically linked build)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
cmake -G "MSYS Makefiles" -DYUZU_TESTS=OFF ..
|
cmake -G "MSYS Makefiles" -DYUZU_TESTS=OFF ..
|
||||||
make -j$(nproc)
|
make -j$(nproc)
|
||||||
# test eden out with
|
# test eden out with
|
||||||
./bin/eden.exe
|
./bin/eden.exe
|
||||||
```
|
```
|
||||||
|
|
||||||
* *(Note: This build is not a static build meaning that you need to include all of the DLLs with the .exe in order to use it!)*
|
* *(Note: This build is not a static build meaning that you need to include all of the DLLs with the .exe in order to use it!)*
|
||||||
|
|
||||||
e.g.
|
e.g.
|
||||||
```Bash
|
```Bash
|
||||||
cp externals/ffmpeg-*/bin/*.dll bin/
|
cp externals/ffmpeg-*/bin/*.dll bin/
|
||||||
```
|
```
|
||||||
|
|
||||||
Bonus Note: Running programs from inside `MSYS2 MinGW x64` shell has a different %PATH% than directly from explorer. This different %PATH% has the locations of the other DLLs required.
|
Bonus Note: Running programs from inside `MSYS2 MinGW x64` shell has a different %PATH% than directly from explorer. This different %PATH% has the locations of the other DLLs required.
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
### Building without Qt (Optional)
|
### Building without Qt (Optional)
|
||||||
|
|
||||||
Doesn't require the rather large Qt dependency, but you will lack a GUI frontend:
|
Doesn't require the rather large Qt dependency, but you will lack a GUI frontend:
|
||||||
|
|
||||||
* Pass the `-DENABLE_QT=no` flag to cmake
|
* Pass the `-DENABLE_QT=no` flag to cmake
|
||||||
|
|
||||||
## Method III: CLion Environment Setup
|
## Method III: CLion Environment Setup
|
||||||
|
|
||||||
### Minimal Dependencies
|
### Minimal Dependencies
|
||||||
|
|
||||||
To build eden, you need to install the following:
|
To build eden, you need to install the following:
|
||||||
|
|
||||||
* [CLion](https://www.jetbrains.com/clion/) - This IDE is not free; for a free alternative, check Method I
|
* [CLion](https://www.jetbrains.com/clion/) - This IDE is not free; for a free alternative, check Method I
|
||||||
* [Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows) - Make sure to select the Latest SDK.
|
* [Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows) - Make sure to select the Latest SDK.
|
||||||
|
|
||||||
### Cloning eden with CLion
|
### Cloning eden with CLion
|
||||||
|
|
||||||
* Clone the Repository:
|
* Clone the Repository:
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Building & Setup
|
### Building & Setup
|
||||||
|
|
||||||
* Once Cloned, You will be taken to a prompt like the image below:
|
* Once Cloned, You will be taken to a prompt like the image below:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
* Set the settings to the image below:
|
* Set the settings to the image below:
|
||||||
* Change `Build type: Release`
|
* Change `Build type: Release`
|
||||||
* Change `Name: Release`
|
* Change `Name: Release`
|
||||||
* Change `Toolchain Visual Studio`
|
* Change `Toolchain Visual Studio`
|
||||||
* Change `Generator: Let CMake decide`
|
* Change `Generator: Let CMake decide`
|
||||||
* Change `Build directory: build`
|
* Change `Build directory: build`
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
* Click OK; now Clion will build a directory and index your code to allow for IntelliSense. Please be patient.
|
* Click OK; now Clion will build a directory and index your code to allow for IntelliSense. Please be patient.
|
||||||
* Once this process has been completed (No loading bar bottom right), you can now build eden
|
* Once this process has been completed (No loading bar bottom right), you can now build eden
|
||||||
* In the top right, click on the drop-down menu, select all configurations, then select eden
|
* In the top right, click on the drop-down menu, select all configurations, then select eden
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
* Now run by clicking the play button or pressing Shift+F10, and eden will auto-launch once built.
|
* Now run by clicking the play button or pressing Shift+F10, and eden will auto-launch once built.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Building from the command line with MSVC
|
## Building from the command line with MSVC
|
||||||
|
|
||||||
```cmd
|
```cmd
|
||||||
git clone --recursive https://git.eden-emu.dev/eden-emu/eden
|
git clone --recursive https://git.eden-emu.dev/eden-emu/eden
|
||||||
cd eden
|
cd eden
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake .. -G "Visual Studio 17 2022" -A x64
|
cmake .. -G "Visual Studio 17 2022" -A x64
|
||||||
cmake --build . --config Release
|
cmake --build . --config Release
|
||||||
```
|
```
|
||||||
|
|
||||||
### Building with Scripts
|
### Building with Scripts
|
||||||
A convenience script for building is provided in `.ci/windows/build.sh`. You must run this with Bash, e.g. Git Bash or MinGW TTY. To use this script, you must have windeployqt installed (usually bundled with Qt) and set the `WINDEPLOYQT` environment variable to its canonical Bash location, e.g. `WINDEPLOYQT="/c/Qt/6.9.1/msvc2022_64/bin/windeployqt6.exe" .ci/windows/build.sh`.
|
A convenience script for building is provided in `.ci/windows/build.sh`. You must run this with Bash, e.g. Git Bash or MinGW TTY. To use this script, you must have windeployqt installed (usually bundled with Qt) and set the `WINDEPLOYQT` environment variable to its canonical Bash location, e.g. `WINDEPLOYQT="/c/Qt/6.9.1/msvc2022_64/bin/windeployqt6.exe" .ci/windows/build.sh`.
|
||||||
|
|
||||||
Extra CMake flags should be placed in the arguments of the script.
|
Extra CMake flags should be placed in the arguments of the script.
|
||||||
|
|
||||||
Additional environment variables can be used to control building:
|
Additional environment variables can be used to control building:
|
||||||
- `BUILD_TYPE`: Sets the build type to use. Defaults to `Release`
|
- `BUILD_TYPE`: Sets the build type to use. Defaults to `Release`
|
||||||
|
|
||||||
The following environment variables are boolean flags. Set to `true` to enable or `false` to disable:
|
The following environment variables are boolean flags. Set to `true` to enable or `false` to disable:
|
||||||
- `DEVEL` (default FALSE): Disable Qt update checker
|
- `DEVEL` (default FALSE): Disable Qt update checker
|
||||||
- `USE_WEBENGINE` (default FALSE): Enable Qt WebEngine
|
- `USE_WEBENGINE` (default FALSE): Enable Qt WebEngine
|
||||||
- `USE_MULTIMEDIA` (default TRUE): Enable Qt Multimedia
|
- `USE_MULTIMEDIA` (default TRUE): Enable Qt Multimedia
|
||||||
- `BUNDLE_QT` (default FALSE): Use bundled Qt
|
- `BUNDLE_QT` (default FALSE): Use bundled Qt
|
||||||
* Note that using system Qt requires you to include the Qt CMake directory in `CMAKE_PREFIX_PATH`, e.g. `.ci/windows/build.sh -DCMAKE_PREFIX_PATH=C:/Qt/6.9.0/msvc2022_64/lib/cmake/Qt6`
|
* Note that using system Qt requires you to include the Qt CMake directory in `CMAKE_PREFIX_PATH`, e.g. `.ci/windows/build.sh -DCMAKE_PREFIX_PATH=C:/Qt/6.9.0/msvc2022_64/lib/cmake/Qt6`
|
||||||
|
|
||||||
After building, a zip can be packaged via `.ci/windows/package.sh`. Note that you must have 7-zip installed and in your PATH. The resulting zip will be placed into `artifacts` in the source directory.
|
After building, a zip can be packaged via `.ci/windows/package.sh`. Note that you must have 7-zip installed and in your PATH. The resulting zip will be placed into `artifacts` in the source directory.
|
||||||
|
|
183
docs/build/macOS.md
vendored
183
docs/build/macOS.md
vendored
|
@ -1,105 +1,78 @@
|
||||||
Please note this article is intended for development, and eden on macOS is not currently ready for regular use.
|
Please note this article is intended for development, and Eden on macOS is not currently ready for regular use.
|
||||||
|
|
||||||
This article was written for developers. eden support for macOS is not ready for casual use.
|
This article was written for developers. Eden support for macOS is not ready for casual use.
|
||||||
|
|
||||||
## Method I: ninja
|
## Dependencies
|
||||||
---
|
Install dependencies from Homebrew:
|
||||||
If you are compiling on Intel Mac or are using a Rosetta Homebrew installation, you must replace all references of `/opt/homebrew` to `/usr/local`.
|
```sh
|
||||||
|
brew install autoconf automake boost ffmpeg fmt glslang hidapi libtool libusb lz4 ninja nlohmann-json openssl pkg-config qt@6 sdl2 speexdsp zlib zstd cmake Catch2 molten-vk vulkan-loader spirv-tools
|
||||||
Install dependencies from Homebrew:
|
```
|
||||||
```sh
|
|
||||||
brew install autoconf automake boost ccache ffmpeg fmt glslang hidapi libtool libusb lz4 ninja nlohmann-json openssl pkg-config qt@6 sdl2 speexdsp zlib zlib zstd cmake Catch2 molten-vk vulkan-loader
|
If you are compiling on Intel Mac, or are using a Rosetta Homebrew installation, you must replace all references of `/opt/homebrew` with `/usr/local`.
|
||||||
```
|
|
||||||
|
Now, clone the repo:
|
||||||
Clone the repo
|
```sh
|
||||||
```sh
|
git clone --recursive https://git.eden-emu.dev/eden-emu/eden
|
||||||
git clone --recursive https://git.eden-emu.dev/eden-emu/eden
|
cd eden
|
||||||
|
```
|
||||||
cd eden
|
|
||||||
```
|
## Method I: ninja
|
||||||
|
|
||||||
Build for release
|
---
|
||||||
```sh
|
Build for release
|
||||||
mkdir build && cd build
|
```sh
|
||||||
|
export Qt6_DIR="/opt/homebrew/opt/qt@6/lib/cmake"
|
||||||
export Qt6_DIR="/opt/homebrew/opt/qt@6/lib/cmake"
|
export LIBVULKAN_PATH=/opt/homebrew/lib/libvulkan.dylib
|
||||||
|
cmake -B build -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU_TESTS=OFF -DENABLE_WEB_SERVICE=ON -DENABLE_LIBUSB=OFF -DCLANG_FORMAT=ON -DSDL2_DISABLE_INSTALL=ON -DSDL_ALTIVEC=ON
|
||||||
export LIBVULKAN_PATH=/opt/homebrew/lib/libvulkan.dylib
|
ninja
|
||||||
|
```
|
||||||
cmake .. -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU_USE_BUNDLED_VCPKG=OFF -DYUZU_TESTS=OFF -DENABLE_WEB_SERVICE=ON -DENABLE_LIBUSB=OFF -DCLANG_FORMAT=ON -DSDL2_DISABLE_INSTALL=ON -DSDL_ALTIVEC=ON
|
|
||||||
|
You may also want to include support for Discord Rich Presence by adding `-DUSE_DISCORD_PRESENCE=ON`
|
||||||
ninja
|
```sh
|
||||||
```
|
export Qt6_DIR="/opt/homebrew/opt/qt@6/lib/cmake"
|
||||||
|
cmake -B build -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU_TESTS=OFF -DENABLE_WEB_SERVICE=OFF -DENABLE_LIBUSB=OFF
|
||||||
You may also want to include support for Discord Rich Presence by adding `-DUSE_DISCORD_PRESENCE=ON` after `cmake ..`
|
ninja
|
||||||
|
```
|
||||||
Build with debug symbols (vcpkg is not currently used due to broken boost-context library):
|
|
||||||
```sh
|
Run the output:
|
||||||
mkdir build && cd build
|
```
|
||||||
export Qt6_DIR="/opt/homebrew/opt/qt@6/lib/cmake"
|
bin/eden.app/Contents/MacOS/eden
|
||||||
cmake .. -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU_USE_BUNDLED_VCPKG=OFF -DYUZU_TESTS=OFF -DENABLE_WEB_SERVICE=OFF -DENABLE_LIBUSB=OFF
|
```
|
||||||
ninja
|
|
||||||
```
|
## Method II: Xcode
|
||||||
|
|
||||||
Run the output:
|
---
|
||||||
```
|
Build for release
|
||||||
bin/eden.app/Contents/MacOS/eden
|
```sh
|
||||||
```
|
export Qt6_DIR="/opt/homebrew/opt/qt@6/lib/cmake"
|
||||||
|
export LIBVULKAN_PATH=/opt/homebrew/lib/libvulkan.dylib
|
||||||
## Method II: Xcode
|
# Only if having errors about Xcode 15.0
|
||||||
|
sudo /usr/bin/xcode-select --switch /Users/admin/Downloads/Xcode.ap
|
||||||
---
|
cmake -B build -GXcode -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU_TESTS=OFF -DENABLE_WEB_SERVICE=ON -DENABLE_LIBUSB=OFF -DCLANG_FORMAT=ON -DSDL2_DISABLE_INSTALL=ON -DSDL_ALTIVEC=ON
|
||||||
If you are compiling on Intel Mac or are using a Rosetta Homebrew installation, you must replace all references of `/opt/homebrew` to `/usr/local`.
|
xcodebuild build -project yuzu.xcodeproj -scheme "yuzu" -configuration "RelWithDebInfo"
|
||||||
|
```
|
||||||
Install dependencies from Homebrew:
|
|
||||||
```sh
|
Build with debug symbols:
|
||||||
brew install autoconf automake boost ccache ffmpeg fmt glslang hidapi libtool libusb lz4 ninja nlohmann-json openssl pkg-config qt@6 sdl2 speexdsp zlib zlib zstd cmake Catch2 molten-vk vulkan-loader
|
```sh
|
||||||
```
|
export Qt6_DIR="/opt/homebrew/opt/qt@6/lib/cmake"
|
||||||
|
cmake -B build -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU_TESTS=OFF -DENABLE_WEB_SERVICE=OFF -DENABLE_LIBUSB=OFF
|
||||||
Clone the repo
|
ninja
|
||||||
```sh
|
```
|
||||||
git clone --recursive https://git.eden-emu.dev/eden-emu/eden
|
|
||||||
|
Run the output:
|
||||||
cd eden
|
```
|
||||||
```
|
bin/eden.app/Contents/MacOS/eden
|
||||||
|
```
|
||||||
Build for release
|
|
||||||
```sh
|
---
|
||||||
mkdir build && cd build
|
|
||||||
|
To run with MoltenVK, install additional dependencies:
|
||||||
export Qt6_DIR="/opt/homebrew/opt/qt@6/lib/cmake"
|
```sh
|
||||||
|
brew install molten-vk vulkan-loader
|
||||||
export LIBVULKAN_PATH=/opt/homebrew/lib/libvulkan.dylib
|
```
|
||||||
|
|
||||||
cmake .. -GXcode -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU_USE_BUNDLED_VCPKG=OFF -DYUZU_TESTS=OFF -DENABLE_WEB_SERVICE=ON -DENABLE_LIBUSB=OFF -DCLANG_FORMAT=ON -DSDL2_DISABLE_INSTALL=ON -DSDL_ALTIVEC=ON
|
Run with Vulkan loader path:
|
||||||
|
```sh
|
||||||
xcodebuild build -project eden.xcodeproj -scheme "eden" -configuration "RelWithDebInfo"
|
export LIBVULKAN_PATH=/opt/homebrew/lib/libvulkan.dylib
|
||||||
```
|
bin/eden.app/Contents/MacOS/eden
|
||||||
|
```
|
||||||
You may also want to include support for Discord Rich Presence by adding `-DUSE_DISCORD_PRESENCE=ON` after `cmake ..`
|
|
||||||
|
|
||||||
Build with debug symbols (vcpkg is not currently used due to broken boost-context library):
|
|
||||||
```sh
|
|
||||||
mkdir build && cd build
|
|
||||||
export Qt6_DIR="/opt/homebrew/opt/qt@6/lib/cmake"
|
|
||||||
cmake .. -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DYUZU_USE_BUNDLED_VCPKG=OFF -DYUZU_TESTS=OFF -DENABLE_WEB_SERVICE=OFF -DENABLE_LIBUSB=OFF
|
|
||||||
ninja
|
|
||||||
```
|
|
||||||
|
|
||||||
Run the output:
|
|
||||||
```
|
|
||||||
bin/eden.app/Contents/MacOS/eden
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
To run with MoltenVK, install additional dependencies:
|
|
||||||
```sh
|
|
||||||
brew install molten-vk vulkan-loader
|
|
||||||
```
|
|
||||||
|
|
||||||
Run with Vulkan loader path:
|
|
||||||
```sh
|
|
||||||
export LIBVULKAN_PATH=/opt/homebrew/lib/libvulkan.dylib
|
|
||||||
bin/eden.app/Contents/MacOS/eden
|
|
||||||
```
|
|
||||||
|
|
27
externals/CMakeLists.txt
vendored
27
externals/CMakeLists.txt
vendored
|
@ -1,3 +1,6 @@
|
||||||
|
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
# SPDX-FileCopyrightText: 2025 Eden Emulator Project
|
# SPDX-FileCopyrightText: 2025 Eden Emulator Project
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
@ -7,8 +10,6 @@
|
||||||
# TODO(crueter): A lot of this should be moved to the root.
|
# TODO(crueter): A lot of this should be moved to the root.
|
||||||
# otherwise we have to do weird shenanigans with library linking and stuff
|
# otherwise we have to do weird shenanigans with library linking and stuff
|
||||||
|
|
||||||
# Explicitly include CPMUtil here since we have a separate cpmfile for externals
|
|
||||||
set(CPMUTIL_JSON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cpmfile.json)
|
|
||||||
include(CPMUtil)
|
include(CPMUtil)
|
||||||
|
|
||||||
# Explicitly declare this option here to propagate to the oaknut CPM call
|
# Explicitly declare this option here to propagate to the oaknut CPM call
|
||||||
|
@ -67,7 +68,7 @@ if (mbedtls_ADDED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# libusb
|
# libusb
|
||||||
if (ENABLE_LIBUSB AND NOT TARGET libusb::usb)
|
if (ENABLE_LIBUSB)
|
||||||
add_subdirectory(libusb)
|
add_subdirectory(libusb)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -107,21 +108,17 @@ if (YUZU_USE_BUNDLED_FFMPEG)
|
||||||
set(FFmpeg_INCLUDE_DIR "${FFmpeg_INCLUDE_DIR}" PARENT_SCOPE)
|
set(FFmpeg_INCLUDE_DIR "${FFmpeg_INCLUDE_DIR}" PARENT_SCOPE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Vulkan-Headers
|
# VulkanUtilityHeaders - pulls in headers and utility libs
|
||||||
|
|
||||||
# TODO(crueter): Vk1.4 impl
|
|
||||||
|
|
||||||
AddJsonPackage(
|
AddJsonPackage(
|
||||||
NAME vulkan-headers
|
NAME vulkan-utility-headers
|
||||||
BUNDLED_PACKAGE ${YUZU_USE_EXTERNAL_VULKAN_HEADERS}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Vulkan-Utility-Libraries
|
|
||||||
AddJsonPackage(
|
|
||||||
NAME vulkan-utility-libraries
|
|
||||||
BUNDLED_PACKAGE ${YUZU_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES}
|
BUNDLED_PACKAGE ${YUZU_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# small hack
|
||||||
|
if (NOT VulkanUtilityLibraries_ADDED)
|
||||||
|
find_package(VulkanHeaders 1.3.274 REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
# SPIRV Tools
|
# SPIRV Tools
|
||||||
AddJsonPackage(
|
AddJsonPackage(
|
||||||
NAME spirv-tools
|
NAME spirv-tools
|
||||||
|
@ -239,7 +236,7 @@ if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client)
|
||||||
file(GLOB_RECURSE LIBBREAKPAD_CLIENT_SOURCES ${breakpad_SOURCE_DIR}/src/client/mac/*.cc ${breakpad_SOURCE_DIR}/src/common/mac/*.cc)
|
file(GLOB_RECURSE LIBBREAKPAD_CLIENT_SOURCES ${breakpad_SOURCE_DIR}/src/client/mac/*.cc ${breakpad_SOURCE_DIR}/src/common/mac/*.cc)
|
||||||
list(APPEND LIBBREAKPAD_CLIENT_SOURCES ${breakpad_SOURCE_DIR}/src/common/mac/MachIPC.mm)
|
list(APPEND LIBBREAKPAD_CLIENT_SOURCES ${breakpad_SOURCE_DIR}/src/common/mac/MachIPC.mm)
|
||||||
else()
|
else()
|
||||||
target_compile_definitions(libbreakpad_client PUBLIC -DHAVE_A_OUT_H)
|
target_compile_definitions(libbreakpad_client PUBLIC HAVE_A_OUT_H)
|
||||||
file(GLOB_RECURSE LIBBREAKPAD_CLIENT_SOURCES ${breakpad_SOURCE_DIR}/src/client/linux/*.cc ${breakpad_SOURCE_DIR}/src/common/linux/*.cc)
|
file(GLOB_RECURSE LIBBREAKPAD_CLIENT_SOURCES ${breakpad_SOURCE_DIR}/src/client/linux/*.cc ${breakpad_SOURCE_DIR}/src/common/linux/*.cc)
|
||||||
endif()
|
endif()
|
||||||
list(APPEND LIBBREAKPAD_CLIENT_SOURCES ${LIBBREAKPAD_COMMON_SOURCES})
|
list(APPEND LIBBREAKPAD_CLIENT_SOURCES ${LIBBREAKPAD_COMMON_SOURCES})
|
||||||
|
|
18
externals/cpmfile.json
vendored
18
externals/cpmfile.json
vendored
|
@ -3,6 +3,7 @@
|
||||||
"repo": "Mbed-TLS/mbedtls",
|
"repo": "Mbed-TLS/mbedtls",
|
||||||
"sha": "8c88150ca1",
|
"sha": "8c88150ca1",
|
||||||
"hash": "769ad1e94c570671071e1f2a5c0f1027e0bf6bcdd1a80ea8ac970f2c86bc45ce4e31aa88d6d8110fc1bed1de81c48bc624df1b38a26f8b340a44e109d784a966",
|
"hash": "769ad1e94c570671071e1f2a5c0f1027e0bf6bcdd1a80ea8ac970f2c86bc45ce4e31aa88d6d8110fc1bed1de81c48bc624df1b38a26f8b340a44e109d784a966",
|
||||||
|
"find_args": "MODULE",
|
||||||
"patches": [
|
"patches": [
|
||||||
"0001-cmake-version.patch"
|
"0001-cmake-version.patch"
|
||||||
]
|
]
|
||||||
|
@ -42,18 +43,13 @@
|
||||||
"0002-missing-decl.patch"
|
"0002-missing-decl.patch"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"vulkan-headers": {
|
"vulkan-utility-headers": {
|
||||||
"package": "VulkanHeaders",
|
|
||||||
"version": "1.3.274",
|
|
||||||
"repo": "KhronosGroup/Vulkan-Headers",
|
|
||||||
"sha": "89268a6d17",
|
|
||||||
"hash": "3ab349f74298ba72cafb8561015690c0674d428a09fb91ccd3cd3daca83650d190d46d33fd97b0a8fd4223fe6df2bcabae89136fbbf7c0bfeb8776f9448304c8"
|
|
||||||
},
|
|
||||||
"vulkan-utility-libraries": {
|
|
||||||
"package": "VulkanUtilityLibraries",
|
"package": "VulkanUtilityLibraries",
|
||||||
"repo": "KhronosGroup/Vulkan-Utility-Libraries",
|
"repo": "scripts/VulkanUtilityHeaders",
|
||||||
"sha": "df2e358152",
|
"tag": "1.4.326",
|
||||||
"hash": "3e468c3d9ff93f6d418d71e5527abe0a12c8c7ab5b0b52278bbbee4d02bb87e99073906729b727e0147242b7e3fd5dedf68b803f1878cb4c0e4f730bc2238d79"
|
"artifact": "VulkanUtilityHeaders.tar.zst",
|
||||||
|
"git_host": "git.crueter.xyz",
|
||||||
|
"hash": "5924629755cb1605c4aa4eee20ef7957a9dd8d61e4df548be656d98054f2730c4109693c1bd35811f401f4705d2ccff9fc849be32b0d8480bc3f73541a5e0964"
|
||||||
},
|
},
|
||||||
"vulkan-memory-allocator": {
|
"vulkan-memory-allocator": {
|
||||||
"package": "VulkanMemoryAllocator",
|
"package": "VulkanMemoryAllocator",
|
||||||
|
|
2
externals/ffmpeg/CMakeLists.txt
vendored
2
externals/ffmpeg/CMakeLists.txt
vendored
|
@ -1,8 +1,6 @@
|
||||||
# SPDX-FileCopyrightText: 2021 yuzu Emulator Project
|
# SPDX-FileCopyrightText: 2021 yuzu Emulator Project
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
# Explicitly include CPMUtil here since we have a separate cpmfile for ffmpeg
|
|
||||||
set(CPMUTIL_JSON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cpmfile.json)
|
|
||||||
include(CPMUtil)
|
include(CPMUtil)
|
||||||
|
|
||||||
if (NOT WIN32 AND NOT ANDROID)
|
if (NOT WIN32 AND NOT ANDROID)
|
||||||
|
|
66
externals/libusb/CMakeLists.txt
vendored
66
externals/libusb/CMakeLists.txt
vendored
|
@ -1,7 +1,15 @@
|
||||||
# SPDX-FileCopyrightText: 2020 yuzu Emulator Project
|
# SPDX-FileCopyrightText: 2020 yuzu Emulator Project
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
if (MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux") OR APPLE)
|
include(CPMUtil)
|
||||||
|
|
||||||
|
AddJsonPackage(libusb)
|
||||||
|
|
||||||
|
if (NOT libusb_ADDED)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (MINGW OR PLATFORM_LINUX OR APPLE)
|
||||||
set(LIBUSB_FOUND ON CACHE BOOL "libusb is present" FORCE)
|
set(LIBUSB_FOUND ON CACHE BOOL "libusb is present" FORCE)
|
||||||
set(LIBUSB_VERSION "1.0.24" CACHE STRING "libusb version string" FORCE)
|
set(LIBUSB_VERSION "1.0.24" CACHE STRING "libusb version string" FORCE)
|
||||||
|
|
||||||
|
@ -19,8 +27,8 @@ if (MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux") OR APPLE)
|
||||||
message(FATAL_ERROR "Required program `libtoolize` not found.")
|
message(FATAL_ERROR "Required program `libtoolize` not found.")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(LIBUSB_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/libusb")
|
set(LIBUSB_PREFIX "${libusb_BINARY_DIR}")
|
||||||
set(LIBUSB_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libusb")
|
set(LIBUSB_SRC_DIR "${libusb_SOURCE_DIR}")
|
||||||
|
|
||||||
# Workarounds for MSYS/MinGW
|
# Workarounds for MSYS/MinGW
|
||||||
if (MSYS)
|
if (MSYS)
|
||||||
|
@ -118,27 +126,27 @@ else() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(usb
|
add_library(usb
|
||||||
libusb/libusb/core.c
|
${libusb_SOURCE_DIR}/libusb/core.c
|
||||||
libusb/libusb/core.c
|
${libusb_SOURCE_DIR}/libusb/core.c
|
||||||
libusb/libusb/descriptor.c
|
${libusb_SOURCE_DIR}/libusb/descriptor.c
|
||||||
libusb/libusb/hotplug.c
|
${libusb_SOURCE_DIR}/libusb/hotplug.c
|
||||||
libusb/libusb/io.c
|
${libusb_SOURCE_DIR}/libusb/io.c
|
||||||
libusb/libusb/strerror.c
|
${libusb_SOURCE_DIR}/libusb/strerror.c
|
||||||
libusb/libusb/sync.c
|
${libusb_SOURCE_DIR}/libusb/sync.c
|
||||||
)
|
)
|
||||||
set_target_properties(usb PROPERTIES VERSION 1.0.24)
|
set_target_properties(usb PROPERTIES VERSION 1.0.24)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
target_include_directories(usb
|
target_include_directories(usb
|
||||||
BEFORE
|
BEFORE
|
||||||
PUBLIC
|
PUBLIC
|
||||||
libusb/libusb
|
${libusb_SOURCE_DIR}/libusb
|
||||||
|
|
||||||
PRIVATE
|
PRIVATE
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||||
)
|
)
|
||||||
|
|
||||||
if (NOT MINGW)
|
if (NOT MINGW)
|
||||||
target_include_directories(usb BEFORE PRIVATE libusb/msvc)
|
target_include_directories(usb BEFORE PRIVATE ${libusb_SOURCE_DIR}/msvc)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
else()
|
else()
|
||||||
|
@ -148,7 +156,7 @@ else() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||||
BEFORE
|
BEFORE
|
||||||
|
|
||||||
PUBLIC
|
PUBLIC
|
||||||
libusb/libusb
|
${libusb_SOURCE_DIR}/libusb
|
||||||
|
|
||||||
PRIVATE
|
PRIVATE
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||||
|
@ -157,15 +165,15 @@ else() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||||
|
|
||||||
if(WIN32 OR CYGWIN)
|
if(WIN32 OR CYGWIN)
|
||||||
target_sources(usb PRIVATE
|
target_sources(usb PRIVATE
|
||||||
libusb/libusb/os/threads_windows.c
|
${libusb_SOURCE_DIR}/libusb/os/threads_windows.c
|
||||||
libusb/libusb/os/windows_winusb.c
|
${libusb_SOURCE_DIR}/libusb/os/windows_winusb.c
|
||||||
libusb/libusb/os/windows_usbdk.c
|
${libusb_SOURCE_DIR}/libusb/os/windows_usbdk.c
|
||||||
libusb/libusb/os/windows_common.c
|
${libusb_SOURCE_DIR}/libusb/os/windows_common.c
|
||||||
)
|
)
|
||||||
set(OS_WINDOWS TRUE)
|
set(OS_WINDOWS TRUE)
|
||||||
elseif(APPLE)
|
elseif(APPLE)
|
||||||
target_sources(usb PRIVATE
|
target_sources(usb PRIVATE
|
||||||
libusb/libusb/os/darwin_usb.c
|
${libusb_SOURCE_DIR}/libusb/os/darwin_usb.c
|
||||||
)
|
)
|
||||||
find_library(COREFOUNDATION_LIBRARY CoreFoundation)
|
find_library(COREFOUNDATION_LIBRARY CoreFoundation)
|
||||||
find_library(IOKIT_LIBRARY IOKit)
|
find_library(IOKIT_LIBRARY IOKit)
|
||||||
|
@ -178,20 +186,20 @@ else() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||||
set(OS_DARWIN TRUE)
|
set(OS_DARWIN TRUE)
|
||||||
elseif(ANDROID)
|
elseif(ANDROID)
|
||||||
target_sources(usb PRIVATE
|
target_sources(usb PRIVATE
|
||||||
libusb/libusb/os/linux_usbfs.c
|
${libusb_SOURCE_DIR}/libusb/os/linux_usbfs.c
|
||||||
libusb/libusb/os/linux_netlink.c
|
${libusb_SOURCE_DIR}/libusb/os/linux_netlink.c
|
||||||
)
|
)
|
||||||
find_library(LOG_LIBRARY log)
|
find_library(LOG_LIBRARY log)
|
||||||
target_link_libraries(usb PRIVATE ${LOG_LIBRARY})
|
target_link_libraries(usb PRIVATE ${LOG_LIBRARY})
|
||||||
set(OS_LINUX TRUE)
|
set(OS_LINUX TRUE)
|
||||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||||
target_sources(usb PRIVATE
|
target_sources(usb PRIVATE
|
||||||
libusb/libusb/os/linux_usbfs.c
|
${libusb_SOURCE_DIR}/libusb/os/linux_usbfs.c
|
||||||
)
|
)
|
||||||
find_package(Libudev)
|
find_package(Libudev)
|
||||||
if(LIBUDEV_FOUND)
|
if(LIBUDEV_FOUND)
|
||||||
target_sources(usb PRIVATE
|
target_sources(usb PRIVATE
|
||||||
libusb/libusb/os/linux_udev.c
|
${libusb_SOURCE_DIR}/libusb/os/linux_udev.c
|
||||||
)
|
)
|
||||||
target_link_libraries(usb PRIVATE "${LIBUDEV_LIBRARIES}")
|
target_link_libraries(usb PRIVATE "${LIBUDEV_LIBRARIES}")
|
||||||
target_include_directories(usb PRIVATE "${LIBUDEV_INCLUDE_DIR}")
|
target_include_directories(usb PRIVATE "${LIBUDEV_INCLUDE_DIR}")
|
||||||
|
@ -199,26 +207,26 @@ else() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||||
set(USE_UDEV TRUE)
|
set(USE_UDEV TRUE)
|
||||||
else()
|
else()
|
||||||
target_sources(usb PRIVATE
|
target_sources(usb PRIVATE
|
||||||
libusb/libusb/os/linux_netlink.c
|
${libusb_SOURCE_DIR}/libusb/os/linux_netlink.c
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
set(OS_LINUX TRUE)
|
set(OS_LINUX TRUE)
|
||||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "NetBSD")
|
elseif(${CMAKE_SYSTEM_NAME} MATCHES "NetBSD")
|
||||||
target_sources(usb PRIVATE
|
target_sources(usb PRIVATE
|
||||||
libusb/libusb/os/netbsd_usb.c
|
${libusb_SOURCE_DIR}/libusb/os/netbsd_usb.c
|
||||||
)
|
)
|
||||||
set(OS_NETBSD TRUE)
|
set(OS_NETBSD TRUE)
|
||||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD")
|
elseif(${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD")
|
||||||
target_sources(usb PRIVATE
|
target_sources(usb PRIVATE
|
||||||
libusb/libusb/os/openbsd_usb.c
|
${libusb_SOURCE_DIR}/libusb/os/openbsd_usb.c
|
||||||
)
|
)
|
||||||
set(OS_OPENBSD TRUE)
|
set(OS_OPENBSD TRUE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
target_sources(usb PRIVATE
|
target_sources(usb PRIVATE
|
||||||
libusb/libusb/os/events_posix.c
|
${libusb_SOURCE_DIR}/libusb/os/events_posix.c
|
||||||
libusb/libusb/os/threads_posix.c
|
${libusb_SOURCE_DIR}/libusb/os/threads_posix.c
|
||||||
)
|
)
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
if(THREADS_HAVE_PTHREAD_ARG)
|
if(THREADS_HAVE_PTHREAD_ARG)
|
||||||
|
@ -230,8 +238,8 @@ else() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||||
set(THREADS_POSIX TRUE)
|
set(THREADS_POSIX TRUE)
|
||||||
elseif(WIN32)
|
elseif(WIN32)
|
||||||
target_sources(usb PRIVATE
|
target_sources(usb PRIVATE
|
||||||
libusb/libusb/os/events_windows.c
|
${libusb_SOURCE_DIR}/libusb/os/events_windows.c
|
||||||
libusb/libusb/os/threads_windows.c
|
${libusb_SOURCE_DIR}/libusb/os/threads_windows.c
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
8
externals/libusb/cpmfile.json
vendored
Normal file
8
externals/libusb/cpmfile.json
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"libusb": {
|
||||||
|
"repo": "libusb/libusb",
|
||||||
|
"sha": "c060e9ce30",
|
||||||
|
"hash": "44647357ba1179020cfa6674d809fc35cf6f89bff1c57252fe3a610110f5013ad678fc6eb5918e751d4384c30e2fe678868dbffc5f85736157e546cb9d10accc",
|
||||||
|
"find_args": "MODULE"
|
||||||
|
}
|
||||||
|
}
|
1
externals/libusb/libusb
vendored
1
externals/libusb/libusb
vendored
|
@ -1 +0,0 @@
|
||||||
Subproject commit c060e9ce30ac2e3ffb49d94209c4dae77b6642f7
|
|
2
externals/nx_tzdb/CMakeLists.txt
vendored
2
externals/nx_tzdb/CMakeLists.txt
vendored
|
@ -4,8 +4,6 @@
|
||||||
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
# Explicitly include CPMUtil here since we have a separate cpmfile for nx_tzdb
|
|
||||||
set(CPMUTIL_JSON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cpmfile.json)
|
|
||||||
include(CPMUtil)
|
include(CPMUtil)
|
||||||
|
|
||||||
set(NX_TZDB_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/include")
|
set(NX_TZDB_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/include")
|
||||||
|
|
5
externals/nx_tzdb/cpmfile.json
vendored
5
externals/nx_tzdb/cpmfile.json
vendored
|
@ -1,7 +1,10 @@
|
||||||
{
|
{
|
||||||
"tzdb": {
|
"tzdb": {
|
||||||
"package": "nx_tzdb",
|
"package": "nx_tzdb",
|
||||||
"url": "https://github.com/crueter/tzdb_to_nx/releases/download/250725/250725.zip",
|
"repo": "misc/tzdb_to_nx",
|
||||||
|
"git_host": "git.crueter.xyz",
|
||||||
|
"artifact": "%VERSION%.zip",
|
||||||
|
"tag": "%VERSION%",
|
||||||
"hash": "8f60b4b29f285e39c0443f3d5572a73780f3dbfcfd5b35004451fadad77f3a215b2e2aa8d0fffe7e348e2a7b0660882b35228b6178dda8804a14ce44509fd2ca",
|
"hash": "8f60b4b29f285e39c0443f3d5572a73780f3dbfcfd5b35004451fadad77f3a215b2e2aa8d0fffe7e348e2a7b0660882b35228b6178dda8804a14ce44509fd2ca",
|
||||||
"version": "250725"
|
"version": "250725"
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,16 +22,16 @@ if (MSVC)
|
||||||
set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE)
|
set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE)
|
||||||
|
|
||||||
# Silence "deprecation" warnings
|
# Silence "deprecation" warnings
|
||||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS)
|
add_compile_definitions(_CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_DEPRECATE _SCL_SECURE_NO_WARNINGS)
|
||||||
|
|
||||||
# Avoid windows.h junk
|
# Avoid windows.h junk
|
||||||
add_definitions(-DNOMINMAX)
|
add_compile_definitions(NOMINMAX)
|
||||||
|
|
||||||
# Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors.
|
# Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors.
|
||||||
add_definitions(-DWIN32_LEAN_AND_MEAN)
|
add_compile_definitions(WIN32_LEAN_AND_MEAN)
|
||||||
|
|
||||||
# Ensure that projects are built with Unicode support.
|
# Ensure that projects are built with Unicode support.
|
||||||
add_definitions(-DUNICODE -D_UNICODE)
|
add_compile_definitions(UNICODE _UNICODE)
|
||||||
|
|
||||||
# /W4 - Level 4 warnings
|
# /W4 - Level 4 warnings
|
||||||
# /MP - Multi-threaded compilation
|
# /MP - Multi-threaded compilation
|
||||||
|
@ -169,15 +169,15 @@ else()
|
||||||
# glibc, which may default to 32 bits. glibc allows this to be configured
|
# glibc, which may default to 32 bits. glibc allows this to be configured
|
||||||
# by setting _FILE_OFFSET_BITS.
|
# by setting _FILE_OFFSET_BITS.
|
||||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR MINGW)
|
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR MINGW)
|
||||||
add_definitions(-D_FILE_OFFSET_BITS=64)
|
add_compile_definitions(_FILE_OFFSET_BITS=64)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (MINGW)
|
if (MINGW)
|
||||||
add_definitions(-DMINGW_HAS_SECURE_API)
|
add_compile_definitions(MINGW_HAS_SECURE_API)
|
||||||
add_compile_options("-msse4.1")
|
add_compile_options("-msse4.1")
|
||||||
|
|
||||||
if (MINGW_STATIC_BUILD)
|
if (MINGW_STATIC_BUILD)
|
||||||
add_definitions(-DQT_STATICPLUGIN)
|
add_compile_definitions(QT_STATICPLUGIN)
|
||||||
add_compile_options("-static")
|
add_compile_options("-static")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -17,7 +17,7 @@ add_library(yuzu-android SHARED
|
||||||
|
|
||||||
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
|
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
|
||||||
|
|
||||||
target_link_libraries(yuzu-android PRIVATE audio_core common core input_common frontend_common Vulkan::Headers GPUOpen::VulkanMemoryAllocator)
|
target_link_libraries(yuzu-android PRIVATE audio_core common core input_common frontend_common video_core)
|
||||||
target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad jnigraphics log)
|
target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad jnigraphics log)
|
||||||
if (ARCHITECTURE_arm64)
|
if (ARCHITECTURE_arm64)
|
||||||
target_link_libraries(yuzu-android PRIVATE adrenotools)
|
target_link_libraries(yuzu-android PRIVATE adrenotools)
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -229,9 +232,10 @@ endif()
|
||||||
target_include_directories(audio_core PRIVATE ${OPUS_INCLUDE_DIRS})
|
target_include_directories(audio_core PRIVATE ${OPUS_INCLUDE_DIRS})
|
||||||
target_link_libraries(audio_core PUBLIC common core opus)
|
target_link_libraries(audio_core PUBLIC common core opus)
|
||||||
|
|
||||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
# what?
|
||||||
target_link_libraries(audio_core PRIVATE dynarmic::dynarmic)
|
# if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
||||||
endif()
|
# target_link_libraries(audio_core PRIVATE dynarmic::dynarmic)
|
||||||
|
# endif()
|
||||||
|
|
||||||
if (ENABLE_CUBEB)
|
if (ENABLE_CUBEB)
|
||||||
target_sources(audio_core PRIVATE
|
target_sources(audio_core PRIVATE
|
||||||
|
@ -240,7 +244,7 @@ if (ENABLE_CUBEB)
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(audio_core PRIVATE cubeb)
|
target_link_libraries(audio_core PRIVATE cubeb)
|
||||||
target_compile_definitions(audio_core PRIVATE -DHAVE_CUBEB=1)
|
target_compile_definitions(audio_core PRIVATE HAVE_CUBEB=1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ENABLE_SDL2)
|
if (ENABLE_SDL2)
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -1155,7 +1158,7 @@ add_library(core STATIC
|
||||||
|
|
||||||
if (ENABLE_WIFI_SCAN)
|
if (ENABLE_WIFI_SCAN)
|
||||||
# find_package(libiw REQUIRED)
|
# find_package(libiw REQUIRED)
|
||||||
target_compile_definitions(core PRIVATE -DENABLE_WIFI_SCAN)
|
target_compile_definitions(core PRIVATE ENABLE_WIFI_SCAN)
|
||||||
target_link_libraries(core PRIVATE iw)
|
target_link_libraries(core PRIVATE iw)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -1196,13 +1199,13 @@ else()
|
||||||
target_link_libraries(core PUBLIC Boost::headers)
|
target_link_libraries(core PUBLIC Boost::headers)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(core PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls RenderDoc::API)
|
target_link_libraries(core PRIVATE fmt::fmt nlohmann_json::nlohmann_json RenderDoc::API mbedtls)
|
||||||
if (MINGW)
|
if (MINGW)
|
||||||
target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})
|
target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ENABLE_WEB_SERVICE)
|
if (ENABLE_WEB_SERVICE)
|
||||||
target_compile_definitions(core PUBLIC -DENABLE_WEB_SERVICE)
|
target_compile_definitions(core PUBLIC ENABLE_WEB_SERVICE)
|
||||||
target_link_libraries(core PUBLIC web_service)
|
target_link_libraries(core PUBLIC web_service)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
|
@ -210,12 +210,8 @@ std::shared_ptr<Dynarmic::A32::Jit> ArmDynarmic32::MakeJit(Common::PageTable* pa
|
||||||
config.wall_clock_cntpct = m_uses_wall_clock;
|
config.wall_clock_cntpct = m_uses_wall_clock;
|
||||||
config.enable_cycle_counting = !m_uses_wall_clock;
|
config.enable_cycle_counting = !m_uses_wall_clock;
|
||||||
|
|
||||||
// Code cache size
|
// Code cache size - max in ARM is 128MiB, max in x86_64 is 2GiB
|
||||||
#ifdef ARCHITECTURE_arm64
|
|
||||||
config.code_cache_size = std::uint32_t(128_MiB);
|
config.code_cache_size = std::uint32_t(128_MiB);
|
||||||
#else
|
|
||||||
config.code_cache_size = std::uint32_t(512_MiB);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Allow memory fault handling to work
|
// Allow memory fault handling to work
|
||||||
if (m_system.DebuggerEnabled()) {
|
if (m_system.DebuggerEnabled()) {
|
||||||
|
|
|
@ -269,12 +269,8 @@ std::shared_ptr<Dynarmic::A64::Jit> ArmDynarmic64::MakeJit(Common::PageTable* pa
|
||||||
config.wall_clock_cntpct = m_uses_wall_clock;
|
config.wall_clock_cntpct = m_uses_wall_clock;
|
||||||
config.enable_cycle_counting = !m_uses_wall_clock;
|
config.enable_cycle_counting = !m_uses_wall_clock;
|
||||||
|
|
||||||
// Code cache size
|
// Code cache size - max in ARM is 128MiB, max in x86_64 is 2GiB
|
||||||
#ifdef ARCHITECTURE_arm64
|
|
||||||
config.code_cache_size = std::uint32_t(128_MiB);
|
config.code_cache_size = std::uint32_t(128_MiB);
|
||||||
#else
|
|
||||||
config.code_cache_size = std::uint32_t(512_MiB);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Allow memory fault handling to work
|
// Allow memory fault handling to work
|
||||||
if (m_system.DebuggerEnabled()) {
|
if (m_system.DebuggerEnabled()) {
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -31,6 +34,10 @@
|
||||||
#include "core/hle/service/filesystem/filesystem.h"
|
#include "core/hle/service/filesystem/filesystem.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
|
|
||||||
|
#ifndef MBEDTLS_CMAC_C
|
||||||
|
#error mbedtls was compiled without CMAC support. Check your USE flags (Gentoo) or contact your package maintainer.
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Core::Crypto {
|
namespace Core::Crypto {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
# SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
# SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -13,7 +16,7 @@ add_library(yuzu-room STATIC EXCLUDE_FROM_ALL
|
||||||
|
|
||||||
target_link_libraries(yuzu-room PRIVATE common network)
|
target_link_libraries(yuzu-room PRIVATE common network)
|
||||||
if (ENABLE_WEB_SERVICE)
|
if (ENABLE_WEB_SERVICE)
|
||||||
target_compile_definitions(yuzu-room PRIVATE -DENABLE_WEB_SERVICE)
|
target_compile_definitions(yuzu-room PRIVATE ENABLE_WEB_SERVICE)
|
||||||
target_link_libraries(yuzu-room PRIVATE web_service)
|
target_link_libraries(yuzu-room PRIVATE web_service)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.12)
|
cmake_minimum_required(VERSION 3.12)
|
||||||
project(dynarmic LANGUAGES C CXX ASM VERSION 6.7.0)
|
project(dynarmic LANGUAGES C CXX ASM VERSION 6.7.0)
|
||||||
|
|
||||||
|
@ -36,9 +39,6 @@ option(DYNARMIC_INSTALL "Install dynarmic headers and CMake files" OFF)
|
||||||
option(DYNARMIC_USE_BUNDLED_EXTERNALS "Use all bundled externals (useful when e.g. cross-compiling)" OFF)
|
option(DYNARMIC_USE_BUNDLED_EXTERNALS "Use all bundled externals (useful when e.g. cross-compiling)" OFF)
|
||||||
option(DYNARMIC_WARNINGS_AS_ERRORS "Warnings as errors" ${MASTER_PROJECT})
|
option(DYNARMIC_WARNINGS_AS_ERRORS "Warnings as errors" ${MASTER_PROJECT})
|
||||||
option(DYNARMIC_ENABLE_LTO "Enable LTO" OFF)
|
option(DYNARMIC_ENABLE_LTO "Enable LTO" OFF)
|
||||||
if (NOT DEFINED DYNARMIC_FRONTENDS)
|
|
||||||
set(DYNARMIC_FRONTENDS "A32;A64" CACHE STRING "Selects which frontends to enable")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Default to a Release build
|
# Default to a Release build
|
||||||
if (NOT CMAKE_BUILD_TYPE)
|
if (NOT CMAKE_BUILD_TYPE)
|
||||||
|
@ -147,28 +147,26 @@ else()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Forced use of individual bundled libraries for non-REQUIRED library is possible with e.g. cmake -DCMAKE_DISABLE_FIND_PACKAGE_fmt=ON ...
|
|
||||||
|
|
||||||
if (DYNARMIC_USE_BUNDLED_EXTERNALS)
|
|
||||||
set(CMAKE_DISABLE_FIND_PACKAGE_biscuit ON)
|
|
||||||
set(CMAKE_DISABLE_FIND_PACKAGE_fmt ON)
|
|
||||||
set(CMAKE_DISABLE_FIND_PACKAGE_mcl ON)
|
|
||||||
set(CMAKE_DISABLE_FIND_PACKAGE_oaknut ON)
|
|
||||||
set(CMAKE_DISABLE_FIND_PACKAGE_unordered_dense ON)
|
|
||||||
set(CMAKE_DISABLE_FIND_PACKAGE_xbyak ON)
|
|
||||||
set(CMAKE_DISABLE_FIND_PACKAGE_Zydis ON)
|
|
||||||
set(CMAKE_DISABLE_FIND_PACKAGE_Zycore ON)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
find_package(Boost 1.57 REQUIRED)
|
find_package(Boost 1.57 REQUIRED)
|
||||||
find_package(fmt 9 CONFIG)
|
find_package(fmt 9 CONFIG)
|
||||||
|
|
||||||
|
# Pull in externals CMakeLists for libs where available
|
||||||
|
add_subdirectory(externals)
|
||||||
|
|
||||||
|
find_package(mcl 0.1.12 REQUIRED)
|
||||||
|
|
||||||
if ("arm64" IN_LIST ARCHITECTURE OR DYNARMIC_TESTS)
|
if ("arm64" IN_LIST ARCHITECTURE OR DYNARMIC_TESTS)
|
||||||
find_package(oaknut 2.0.1 CONFIG)
|
find_package(oaknut 2.0.1 CONFIG)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if ("riscv" IN_LIST ARCHITECTURE)
|
||||||
|
find_package(biscuit 0.9.1 REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
if ("x86_64" IN_LIST ARCHITECTURE)
|
if ("x86_64" IN_LIST ARCHITECTURE)
|
||||||
find_package(xbyak 7 CONFIG)
|
find_package(xbyak 7 CONFIG)
|
||||||
|
find_package(zycore REQUIRED)
|
||||||
|
find_package(zydis 4 REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (DYNARMIC_USE_LLVM)
|
if (DYNARMIC_USE_LLVM)
|
||||||
|
@ -183,9 +181,6 @@ if (DYNARMIC_TESTS)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Pull in externals CMakeLists for libs where available
|
|
||||||
add_subdirectory(externals)
|
|
||||||
|
|
||||||
# Dynarmic project files
|
# Dynarmic project files
|
||||||
add_subdirectory(src/dynarmic)
|
add_subdirectory(src/dynarmic)
|
||||||
if (DYNARMIC_TESTS)
|
if (DYNARMIC_TESTS)
|
||||||
|
|
19
src/dynarmic/docs/FastMemory.md
Normal file
19
src/dynarmic/docs/FastMemory.md
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# Fast memory (Fastmem)
|
||||||
|
|
||||||
|
The main way of accessing memory in JITed programs is via an invoked function, say "Read()" and "Write()". On our translator, such functions usually take a sizable amounts of code space (push + call + pop). Trash the i-cache (due to an indirect call) and overall make code emission more bloated.
|
||||||
|
|
||||||
|
The solution? Delegate invalid accesses to a dedicated arena, similar to a swap. The main idea behind such mechanism is to allow the OS to transmit page faults from invalid accesses into the JIT translator directly, bypassing address space calls, while this sacrifices i-cache coherency, it allows for smaller code-size and "faster" throguhput.
|
||||||
|
|
||||||
|
Many kernels however, do not support fast signal dispatching (Solaris, OpenBSD, FreeBSD). Only Linux and Windows support relatively "fast" signal dispatching. Hence this feature is better suited for them only.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
In x86_64 for example, when a page fault occurs, the CPU will transmit via control registers and the stack (see `IRETQ`) the appropriate arguments for a page fault handler, the OS then will transform that into something that can be sent into userspace.
|
||||||
|
|
||||||
|
Most modern OSes implement kernel-page-table-isolation, which means a set of system calls will invoke a context switch (not often used syscalls), whereas others are handled by the same process address space (the smaller kernel portion, often used syscalls) without needing a context switch. This effect can be negated on systems with PCID (up to 4096 unique IDs).
|
||||||
|
|
||||||
|
Signal dispatching takes a performance hit from reloading `%cr3` - but Linux does something more clever to avoid reloads: VDSO will take care of the entire thing in the same address space. Making dispatching as costly as an indirect call - without the hazards of increased code size.
|
||||||
|
|
||||||
|
The main downside from this is the constant i-cache trashing and pipeline hazards introduced by the VDSO signal handlers. However on most benchmarks fastmem does perform faster than without (Linux only). This also abuses the fact of continous address space emulation by using an arena - which can then be potentially transparently mapped into a hugepage, reducing TLB walk times.
|
4
src/dynarmic/docs/Fastmem.svg
Normal file
4
src/dynarmic/docs/Fastmem.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 128 KiB |
4
src/dynarmic/docs/HostToGuest.svg
Normal file
4
src/dynarmic/docs/HostToGuest.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 98 KiB |
|
@ -16,19 +16,31 @@ Note that `Use`ing a value decrements its `use_count` by one. When the `use_coun
|
||||||
|
|
||||||
The member functions on `RegAlloc` are just a combination of the above concepts.
|
The member functions on `RegAlloc` are just a combination of the above concepts.
|
||||||
|
|
||||||
|
The following registers are reserved for internal use and should NOT participate in register allocation:
|
||||||
|
- `%xmm0`, `%xmm1`, `%xmm2`: Used as scratch in exclusive memory access.
|
||||||
|
- `%rsp`: Stack pointer.
|
||||||
|
|
||||||
|
The layout convenes `%r15` as the JIT state pointer - while it may be tempting to turn it into a synthetic pointer, keeping an entire register (out of 12 available) is preferable over inlining a directly computed immediate.
|
||||||
|
|
||||||
|
Do NEVER modify `%r15`, we must make it clear that this register is "immutable" for the entirety of the JIT block duration.
|
||||||
|
|
||||||
### `Scratch`
|
### `Scratch`
|
||||||
|
|
||||||
Xbyak::Reg64 ScratchGpr(HostLocList desired_locations = any_gpr)
|
```c++
|
||||||
Xbyak::Xmm ScratchXmm(HostLocList desired_locations = any_xmm)
|
Xbyak::Reg64 ScratchGpr(HostLocList desired_locations = any_gpr);
|
||||||
|
Xbyak::Xmm ScratchXmm(HostLocList desired_locations = any_xmm);
|
||||||
|
```
|
||||||
|
|
||||||
At runtime, allocate one of the registers in `desired_locations`. You are free to modify the register. The register is discarded at the end of the allocation scope.
|
At runtime, allocate one of the registers in `desired_locations`. You are free to modify the register. The register is discarded at the end of the allocation scope.
|
||||||
|
|
||||||
### Pure `Use`
|
### Pure `Use`
|
||||||
|
|
||||||
Xbyak::Reg64 UseGpr(Argument& arg);
|
```c++
|
||||||
Xbyak::Xmm UseXmm(Argument& arg);
|
Xbyak::Reg64 UseGpr(Argument& arg);
|
||||||
OpArg UseOpArg(Argument& arg);
|
Xbyak::Xmm UseXmm(Argument& arg);
|
||||||
void Use(Argument& arg, HostLoc host_loc);
|
OpArg UseOpArg(Argument& arg);
|
||||||
|
void Use(Argument& arg, HostLoc host_loc);
|
||||||
|
```
|
||||||
|
|
||||||
At runtime, the value corresponding to `arg` will be placed a register. The actual register is determined by
|
At runtime, the value corresponding to `arg` will be placed a register. The actual register is determined by
|
||||||
which one of the above functions is called. `UseGpr` places it in an unused GPR, `UseXmm` places it
|
which one of the above functions is called. `UseGpr` places it in an unused GPR, `UseXmm` places it
|
||||||
|
@ -39,9 +51,11 @@ This register **must not** have it's value changed.
|
||||||
|
|
||||||
### `UseScratch`
|
### `UseScratch`
|
||||||
|
|
||||||
Xbyak::Reg64 UseScratchGpr(Argument& arg);
|
```c++
|
||||||
Xbyak::Xmm UseScratchXmm(Argument& arg);
|
Xbyak::Reg64 UseScratchGpr(Argument& arg);
|
||||||
void UseScratch(Argument& arg, HostLoc host_loc);
|
Xbyak::Xmm UseScratchXmm(Argument& arg);
|
||||||
|
void UseScratch(Argument& arg, HostLoc host_loc);
|
||||||
|
```
|
||||||
|
|
||||||
At runtime, the value corresponding to `arg` will be placed a register. The actual register is determined by
|
At runtime, the value corresponding to `arg` will be placed a register. The actual register is determined by
|
||||||
which one of the above functions is called. `UseScratchGpr` places it in an unused GPR, `UseScratchXmm` places it
|
which one of the above functions is called. `UseScratchGpr` places it in an unused GPR, `UseScratchXmm` places it
|
||||||
|
@ -55,7 +69,9 @@ You are free to modify the value in the register. The register is discarded at t
|
||||||
|
|
||||||
A `Define` is the defintion of a value. This is the only time when a value may be set.
|
A `Define` is the defintion of a value. This is the only time when a value may be set.
|
||||||
|
|
||||||
void DefineValue(IR::Inst* inst, const Xbyak::Reg& reg);
|
```c++
|
||||||
|
void DefineValue(IR::Inst* inst, const Xbyak::Reg& reg);
|
||||||
|
```
|
||||||
|
|
||||||
By calling `DefineValue`, you are stating that you wish to define the value for `inst`, and you have written the
|
By calling `DefineValue`, you are stating that you wish to define the value for `inst`, and you have written the
|
||||||
value to the specified register `reg`.
|
value to the specified register `reg`.
|
||||||
|
@ -64,7 +80,9 @@ value to the specified register `reg`.
|
||||||
|
|
||||||
Adding a `Define` to an existing value.
|
Adding a `Define` to an existing value.
|
||||||
|
|
||||||
void DefineValue(IR::Inst* inst, Argument& arg);
|
```c++
|
||||||
|
void DefineValue(IR::Inst* inst, Argument& arg);
|
||||||
|
```
|
||||||
|
|
||||||
You are declaring that the value for `inst` is the same as the value for `arg`. No host machine instructions are
|
You are declaring that the value for `inst` is the same as the value for `arg`. No host machine instructions are
|
||||||
emitted.
|
emitted.
|
||||||
|
|
|
@ -23,15 +23,17 @@ One complication dynarmic has is that a compiled block is not uniquely identifia
|
||||||
the PC alone, but bits in the FPSCR and CPSR are also relevant. We resolve this by
|
the PC alone, but bits in the FPSCR and CPSR are also relevant. We resolve this by
|
||||||
computing a 64-bit `UniqueHash` that is guaranteed to uniquely identify a block.
|
computing a 64-bit `UniqueHash` that is guaranteed to uniquely identify a block.
|
||||||
|
|
||||||
u64 LocationDescriptor::UniqueHash() const {
|
```c++
|
||||||
// This value MUST BE UNIQUE.
|
u64 LocationDescriptor::UniqueHash() const {
|
||||||
// This calculation has to match up with EmitX64::EmitTerminalPopRSBHint
|
// This value MUST BE UNIQUE.
|
||||||
u64 pc_u64 = u64(arm_pc) << 32;
|
// This calculation has to match up with EmitX64::EmitTerminalPopRSBHint
|
||||||
u64 fpscr_u64 = u64(fpscr.Value());
|
u64 pc_u64 = u64(arm_pc) << 32;
|
||||||
u64 t_u64 = cpsr.T() ? 1 : 0;
|
u64 fpscr_u64 = u64(fpscr.Value());
|
||||||
u64 e_u64 = cpsr.E() ? 2 : 0;
|
u64 t_u64 = cpsr.T() ? 1 : 0;
|
||||||
return pc_u64 | fpscr_u64 | t_u64 | e_u64;
|
u64 e_u64 = cpsr.E() ? 2 : 0;
|
||||||
}
|
return pc_u64 | fpscr_u64 | t_u64 | e_u64;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Our implementation isn't actually a stack
|
## Our implementation isn't actually a stack
|
||||||
|
|
||||||
|
@ -49,97 +51,107 @@ host addresses for the corresponding the compiled blocks.
|
||||||
size of the real RSB in hardware (which has 3 entries). Larger RSBs than 8
|
size of the real RSB in hardware (which has 3 entries). Larger RSBs than 8
|
||||||
showed degraded performance.
|
showed degraded performance.
|
||||||
|
|
||||||
struct JitState {
|
```c++
|
||||||
// ...
|
struct JitState {
|
||||||
|
// ...
|
||||||
|
|
||||||
static constexpr size_t RSBSize = 8; // MUST be a power of 2.
|
static constexpr size_t RSBSize = 8; // MUST be a power of 2.
|
||||||
u32 rsb_ptr = 0;
|
u32 rsb_ptr = 0;
|
||||||
std::array<u64, RSBSize> rsb_location_descriptors;
|
std::array<u64, RSBSize> rsb_location_descriptors;
|
||||||
std::array<u64, RSBSize> rsb_codeptrs;
|
std::array<u64, RSBSize> rsb_codeptrs;
|
||||||
void ResetRSB();
|
void ResetRSB();
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
};
|
};
|
||||||
|
```
|
||||||
|
|
||||||
### RSB Push
|
### RSB Push
|
||||||
|
|
||||||
We insert our prediction at the insertion point iff the RSB doesn't already
|
We insert our prediction at the insertion point iff the RSB doesn't already
|
||||||
contain a prediction with the same `UniqueHash`.
|
contain a prediction with the same `UniqueHash`.
|
||||||
|
|
||||||
void EmitX64::EmitPushRSB(IR::Block&, IR::Inst* inst) {
|
```c++
|
||||||
using namespace Xbyak::util;
|
void EmitX64::EmitPushRSB(IR::Block&, IR::Inst* inst) {
|
||||||
|
using namespace Xbyak::util;
|
||||||
|
|
||||||
ASSERT(inst->GetArg(0).IsImmediate());
|
ASSERT(inst->GetArg(0).IsImmediate());
|
||||||
u64 imm64 = inst->GetArg(0).GetU64();
|
u64 imm64 = inst->GetArg(0).GetU64();
|
||||||
|
|
||||||
Xbyak::Reg64 code_ptr_reg = reg_alloc.ScratchGpr({HostLoc::RCX});
|
Xbyak::Reg64 code_ptr_reg = reg_alloc.ScratchGpr({HostLoc::RCX});
|
||||||
Xbyak::Reg64 loc_desc_reg = reg_alloc.ScratchGpr();
|
Xbyak::Reg64 loc_desc_reg = reg_alloc.ScratchGpr();
|
||||||
Xbyak::Reg32 index_reg = reg_alloc.ScratchGpr().cvt32();
|
Xbyak::Reg32 index_reg = reg_alloc.ScratchGpr().cvt32();
|
||||||
u64 code_ptr = unique_hash_to_code_ptr.find(imm64) != unique_hash_to_code_ptr.end()
|
u64 code_ptr = unique_hash_to_code_ptr.find(imm64) != unique_hash_to_code_ptr.end()
|
||||||
? u64(unique_hash_to_code_ptr[imm64])
|
? u64(unique_hash_to_code_ptr[imm64])
|
||||||
: u64(code->GetReturnFromRunCodeAddress());
|
: u64(code->GetReturnFromRunCodeAddress());
|
||||||
|
|
||||||
code->mov(index_reg, dword[code.ABI_JIT_PTR + offsetof(JitState, rsb_ptr)]);
|
code->mov(index_reg, dword[code.ABI_JIT_PTR + offsetof(JitState, rsb_ptr)]);
|
||||||
code->add(index_reg, 1);
|
code->add(index_reg, 1);
|
||||||
code->and_(index_reg, u32(JitState::RSBSize - 1));
|
code->and_(index_reg, u32(JitState::RSBSize - 1));
|
||||||
|
|
||||||
code->mov(loc_desc_reg, u64(imm64));
|
code->mov(loc_desc_reg, u64(imm64));
|
||||||
CodePtr patch_location = code->getCurr<CodePtr>();
|
CodePtr patch_location = code->getCurr<CodePtr>();
|
||||||
patch_unique_hash_locations[imm64].emplace_back(patch_location);
|
patch_unique_hash_locations[imm64].emplace_back(patch_location);
|
||||||
code->mov(code_ptr_reg, u64(code_ptr)); // This line has to match up with EmitX64::Patch.
|
code->mov(code_ptr_reg, u64(code_ptr)); // This line has to match up with EmitX64::Patch.
|
||||||
code->EnsurePatchLocationSize(patch_location, 10);
|
code->EnsurePatchLocationSize(patch_location, 10);
|
||||||
|
|
||||||
Xbyak::Label label;
|
Xbyak::Label label;
|
||||||
for (size_t i = 0; i < JitState::RSBSize; ++i) {
|
for (size_t i = 0; i < JitState::RSBSize; ++i) {
|
||||||
code->cmp(loc_desc_reg, qword[code.ABI_JIT_PTR + offsetof(JitState, rsb_location_descriptors) + i * sizeof(u64)]);
|
code->cmp(loc_desc_reg, qword[code.ABI_JIT_PTR + offsetof(JitState, rsb_location_descriptors) + i * sizeof(u64)]);
|
||||||
code->je(label, code->T_SHORT);
|
code->je(label, code->T_SHORT);
|
||||||
}
|
|
||||||
|
|
||||||
code->mov(dword[code.ABI_JIT_PTR + offsetof(JitState, rsb_ptr)], index_reg);
|
|
||||||
code->mov(qword[code.ABI_JIT_PTR + index_reg.cvt64() * 8 + offsetof(JitState, rsb_location_descriptors)], loc_desc_reg);
|
|
||||||
code->mov(qword[code.ABI_JIT_PTR + index_reg.cvt64() * 8 + offsetof(JitState, rsb_codeptrs)], code_ptr_reg);
|
|
||||||
code->L(label);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
code->mov(dword[code.ABI_JIT_PTR + offsetof(JitState, rsb_ptr)], index_reg);
|
||||||
|
code->mov(qword[code.ABI_JIT_PTR + index_reg.cvt64() * 8 + offsetof(JitState, rsb_location_descriptors)], loc_desc_reg);
|
||||||
|
code->mov(qword[code.ABI_JIT_PTR + index_reg.cvt64() * 8 + offsetof(JitState, rsb_codeptrs)], code_ptr_reg);
|
||||||
|
code->L(label);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
In pseudocode:
|
In pseudocode:
|
||||||
|
|
||||||
for (i := 0 .. RSBSize-1)
|
```c++
|
||||||
if (rsb_location_descriptors[i] == imm64)
|
for (i := 0 .. RSBSize-1)
|
||||||
goto label;
|
if (rsb_location_descriptors[i] == imm64)
|
||||||
rsb_ptr++;
|
goto label;
|
||||||
rsb_ptr %= RSBSize;
|
rsb_ptr++;
|
||||||
rsb_location_desciptors[rsb_ptr] = imm64; //< The UniqueHash
|
rsb_ptr %= RSBSize;
|
||||||
rsb_codeptr[rsb_ptr] = /* codeptr corresponding to the UniqueHash */;
|
rsb_location_desciptors[rsb_ptr] = imm64; //< The UniqueHash
|
||||||
label:
|
rsb_codeptr[rsb_ptr] = /* codeptr corresponding to the UniqueHash */;
|
||||||
|
label:
|
||||||
|
```
|
||||||
|
|
||||||
## RSB Pop
|
## RSB Pop
|
||||||
|
|
||||||
To check if a predicition is in the RSB, we linearly scan the RSB.
|
To check if a predicition is in the RSB, we linearly scan the RSB.
|
||||||
|
|
||||||
void EmitX64::EmitTerminalPopRSBHint(IR::Term::PopRSBHint, IR::LocationDescriptor initial_location) {
|
```c++
|
||||||
using namespace Xbyak::util;
|
void EmitX64::EmitTerminalPopRSBHint(IR::Term::PopRSBHint, IR::LocationDescriptor initial_location) {
|
||||||
|
using namespace Xbyak::util;
|
||||||
|
|
||||||
// This calculation has to match up with IREmitter::PushRSB
|
// This calculation has to match up with IREmitter::PushRSB
|
||||||
code->mov(ecx, MJitStateReg(Arm::Reg::PC));
|
code->mov(ecx, MJitStateReg(Arm::Reg::PC));
|
||||||
code->shl(rcx, 32);
|
code->shl(rcx, 32);
|
||||||
code->mov(ebx, dword[code.ABI_JIT_PTR + offsetof(JitState, FPSCR_mode)]);
|
code->mov(ebx, dword[code.ABI_JIT_PTR + offsetof(JitState, FPSCR_mode)]);
|
||||||
code->or_(ebx, dword[code.ABI_JIT_PTR + offsetof(JitState, CPSR_et)]);
|
code->or_(ebx, dword[code.ABI_JIT_PTR + offsetof(JitState, CPSR_et)]);
|
||||||
code->or_(rbx, rcx);
|
code->or_(rbx, rcx);
|
||||||
|
|
||||||
code->mov(rax, u64(code->GetReturnFromRunCodeAddress()));
|
code->mov(rax, u64(code->GetReturnFromRunCodeAddress()));
|
||||||
for (size_t i = 0; i < JitState::RSBSize; ++i) {
|
for (size_t i = 0; i < JitState::RSBSize; ++i) {
|
||||||
code->cmp(rbx, qword[code.ABI_JIT_PTR + offsetof(JitState, rsb_location_descriptors) + i * sizeof(u64)]);
|
code->cmp(rbx, qword[code.ABI_JIT_PTR + offsetof(JitState, rsb_location_descriptors) + i * sizeof(u64)]);
|
||||||
code->cmove(rax, qword[code.ABI_JIT_PTR + offsetof(JitState, rsb_codeptrs) + i * sizeof(u64)]);
|
code->cmove(rax, qword[code.ABI_JIT_PTR + offsetof(JitState, rsb_codeptrs) + i * sizeof(u64)]);
|
||||||
}
|
|
||||||
|
|
||||||
code->jmp(rax);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
code->jmp(rax);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
In pseudocode:
|
In pseudocode:
|
||||||
|
|
||||||
rbx := ComputeUniqueHash()
|
```c++
|
||||||
rax := ReturnToDispatch
|
rbx := ComputeUniqueHash()
|
||||||
for (i := 0 .. RSBSize-1)
|
rax := ReturnToDispatch
|
||||||
if (rbx == rsb_location_descriptors[i])
|
for (i := 0 .. RSBSize-1)
|
||||||
rax = rsb_codeptrs[i]
|
if (rbx == rsb_location_descriptors[i])
|
||||||
goto rax
|
rax = rsb_codeptrs[i]
|
||||||
|
goto rax
|
||||||
|
```
|
||||||
|
|
50
src/dynarmic/externals/CMakeLists.txt
vendored
50
src/dynarmic/externals/CMakeLists.txt
vendored
|
@ -1,5 +1,6 @@
|
||||||
# Explicitly include CPMUtil here since we have a separate cpmfile for dynarmic
|
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
set(CPMUTIL_JSON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cpmfile.json)
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
include(CPMUtil)
|
include(CPMUtil)
|
||||||
|
|
||||||
# Always build externals as static libraries, even when dynarmic is built as shared
|
# Always build externals as static libraries, even when dynarmic is built as shared
|
||||||
|
@ -20,62 +21,25 @@ set(BUILD_TESTING OFF)
|
||||||
# biscuit
|
# biscuit
|
||||||
|
|
||||||
if ("riscv" IN_LIST ARCHITECTURE)
|
if ("riscv" IN_LIST ARCHITECTURE)
|
||||||
add_subdirectory(biscuit)
|
|
||||||
|
|
||||||
AddJsonPackage(
|
AddJsonPackage(
|
||||||
NAME biscuit
|
NAME biscuit
|
||||||
BUNDLED_PACKAGE ${DYNARMIC_USE_BUNDLED_EXTERNALS}
|
BUNDLED_PACKAGE ${DYNARMIC_USE_BUNDLED_EXTERNALS}
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# catch
|
|
||||||
|
|
||||||
# if (NOT TARGET Catch2::Catch2WithMain)
|
|
||||||
# if (DYNARMIC_TESTS)
|
|
||||||
# find_package(Catch2 3.0.1 REQUIRED)
|
|
||||||
# endif()
|
|
||||||
# endif()
|
|
||||||
|
|
||||||
# fmt
|
|
||||||
|
|
||||||
# if (NOT TARGET fmt::fmt)
|
|
||||||
# # fmtlib formatting library
|
|
||||||
# set(FMT_INSTALL ON)
|
|
||||||
# add_subdirectory(fmt)
|
|
||||||
# endif()
|
|
||||||
|
|
||||||
# mcl
|
# mcl
|
||||||
AddJsonPackage(
|
AddJsonPackage(
|
||||||
NAME mcl
|
NAME mcl
|
||||||
BUNDLED_PACKAGE ${DYNARMIC_USE_BUNDLED_EXTERNALS}
|
BUNDLED_PACKAGE ${DYNARMIC_USE_BUNDLED_EXTERNALS}
|
||||||
)
|
)
|
||||||
|
|
||||||
# oaknut
|
|
||||||
|
|
||||||
# if (NOT TARGET merry::oaknut)
|
|
||||||
# if ("arm64" IN_LIST ARCHITECTURE)
|
|
||||||
# add_subdirectory(oaknut)
|
|
||||||
# elseif (DYNARMIC_TESTS)
|
|
||||||
# add_subdirectory(oaknut EXCLUDE_FROM_ALL)
|
|
||||||
# endif()
|
|
||||||
# endif()
|
|
||||||
|
|
||||||
# xbyak
|
|
||||||
# uncomment if in an independent repo
|
|
||||||
|
|
||||||
# if (NOT TARGET xbyak::xbyak)
|
|
||||||
# if ("x86_64" IN_LIST ARCHITECTURE)
|
|
||||||
# add_subdirectory(xbyak)
|
|
||||||
# endif()
|
|
||||||
# endif()
|
|
||||||
|
|
||||||
# zydis
|
|
||||||
|
|
||||||
# TODO(crueter): maybe it's just Gentoo but zydis system package really sucks
|
# TODO(crueter): maybe it's just Gentoo but zydis system package really sucks
|
||||||
if ("x86_64" IN_LIST ARCHITECTURE)
|
if ("x86_64" IN_LIST ARCHITECTURE)
|
||||||
set(CMAKE_DISABLE_FIND_PACKAGE_Doxygen ON)
|
set(CMAKE_DISABLE_FIND_PACKAGE_Doxygen ON)
|
||||||
# TODO(crueter): system zycore doesn't work with zydis
|
AddJsonPackage(
|
||||||
AddJsonPackage(zycore)
|
NAME zycore
|
||||||
|
BUNDLED_PACKAGE ${DYNARMIC_USE_BUNDLED_EXTERNALS}
|
||||||
|
)
|
||||||
|
|
||||||
AddJsonPackage(
|
AddJsonPackage(
|
||||||
NAME zydis
|
NAME zydis
|
||||||
|
|
7
src/dynarmic/externals/cpmfile.json
vendored
7
src/dynarmic/externals/cpmfile.json
vendored
|
@ -15,14 +15,13 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"zycore": {
|
"zycore": {
|
||||||
"package": "Zycore",
|
"package": "zycore",
|
||||||
"repo": "zyantific/zycore-c",
|
"repo": "zyantific/zycore-c",
|
||||||
"sha": "75a36c45ae",
|
"sha": "75a36c45ae",
|
||||||
"hash": "15aa399f39713e042c4345bc3175c82f14dca849fde2a21d4f591f62c43e227b70d868d8bb86beb5f4eb68b1d6bd3792cdd638acf89009e787e3d10ee7401924",
|
"hash": "15aa399f39713e042c4345bc3175c82f14dca849fde2a21d4f591f62c43e227b70d868d8bb86beb5f4eb68b1d6bd3792cdd638acf89009e787e3d10ee7401924"
|
||||||
"bundled": true
|
|
||||||
},
|
},
|
||||||
"zydis": {
|
"zydis": {
|
||||||
"package": "Zydis",
|
"package": "zydis",
|
||||||
"version": "4",
|
"version": "4",
|
||||||
"repo": "zyantific/zydis",
|
"repo": "zyantific/zydis",
|
||||||
"sha": "c2d2bab025",
|
"sha": "c2d2bab025",
|
||||||
|
|
|
@ -56,8 +56,6 @@ add_library(dynarmic
|
||||||
common/lut_from_list.h
|
common/lut_from_list.h
|
||||||
common/math_util.cpp
|
common/math_util.cpp
|
||||||
common/math_util.h
|
common/math_util.h
|
||||||
common/memory_pool.cpp
|
|
||||||
common/memory_pool.h
|
|
||||||
common/safe_ops.h
|
common/safe_ops.h
|
||||||
common/spin_lock.h
|
common/spin_lock.h
|
||||||
common/string_util.h
|
common/string_util.h
|
||||||
|
@ -78,7 +76,6 @@ add_library(dynarmic
|
||||||
ir/basic_block.cpp
|
ir/basic_block.cpp
|
||||||
ir/basic_block.h
|
ir/basic_block.h
|
||||||
ir/cond.h
|
ir/cond.h
|
||||||
ir/ir_emitter.cpp
|
|
||||||
ir/ir_emitter.h
|
ir/ir_emitter.h
|
||||||
ir/location_descriptor.cpp
|
ir/location_descriptor.cpp
|
||||||
ir/location_descriptor.h
|
ir/location_descriptor.h
|
||||||
|
@ -87,84 +84,65 @@ add_library(dynarmic
|
||||||
ir/opcodes.cpp
|
ir/opcodes.cpp
|
||||||
ir/opcodes.h
|
ir/opcodes.h
|
||||||
ir/opcodes.inc
|
ir/opcodes.inc
|
||||||
ir/opt/constant_propagation_pass.cpp
|
ir/opt_passes.cpp
|
||||||
ir/opt/dead_code_elimination_pass.cpp
|
ir/opt_passes.h
|
||||||
ir/opt/identity_removal_pass.cpp
|
|
||||||
ir/opt/ir_matcher.h
|
|
||||||
ir/opt/naming_pass.cpp
|
|
||||||
ir/opt/passes.h
|
|
||||||
ir/opt/polyfill_pass.cpp
|
|
||||||
ir/opt/verification_pass.cpp
|
|
||||||
ir/terminal.h
|
ir/terminal.h
|
||||||
ir/type.cpp
|
ir/type.cpp
|
||||||
ir/type.h
|
ir/type.h
|
||||||
ir/value.cpp
|
ir/value.cpp
|
||||||
ir/value.h
|
ir/value.h
|
||||||
|
# A32
|
||||||
|
frontend/A32/a32_ir_emitter.cpp
|
||||||
|
frontend/A32/a32_ir_emitter.h
|
||||||
|
frontend/A32/a32_location_descriptor.cpp
|
||||||
|
frontend/A32/a32_location_descriptor.h
|
||||||
|
frontend/A32/decoder/arm.h
|
||||||
|
frontend/A32/decoder/arm.inc
|
||||||
|
frontend/A32/decoder/asimd.h
|
||||||
|
frontend/A32/decoder/asimd.inc
|
||||||
|
frontend/A32/decoder/thumb16.h
|
||||||
|
frontend/A32/decoder/thumb16.inc
|
||||||
|
frontend/A32/decoder/thumb32.h
|
||||||
|
frontend/A32/decoder/thumb32.inc
|
||||||
|
frontend/A32/decoder/vfp.h
|
||||||
|
frontend/A32/decoder/vfp.inc
|
||||||
|
frontend/A32/disassembler/disassembler.h
|
||||||
|
frontend/A32/disassembler/disassembler_arm.cpp
|
||||||
|
frontend/A32/disassembler/disassembler_thumb.cpp
|
||||||
|
frontend/A32/FPSCR.h
|
||||||
|
frontend/A32/ITState.h
|
||||||
|
frontend/A32/PSR.h
|
||||||
|
frontend/A32/translate/a32_translate.cpp
|
||||||
|
frontend/A32/translate/a32_translate.h
|
||||||
|
frontend/A32/translate/conditional_state.cpp
|
||||||
|
frontend/A32/translate/conditional_state.h
|
||||||
|
frontend/A32/translate/translate_arm.cpp
|
||||||
|
frontend/A32/translate/translate_thumb.cpp
|
||||||
|
interface/A32/a32.h
|
||||||
|
interface/A32/arch_version.h
|
||||||
|
interface/A32/config.h
|
||||||
|
interface/A32/coprocessor.h
|
||||||
|
interface/A32/coprocessor_util.h
|
||||||
|
interface/A32/disassembler.h
|
||||||
|
# A64
|
||||||
|
frontend/A64/a64_ir_emitter.cpp
|
||||||
|
frontend/A64/a64_ir_emitter.h
|
||||||
|
frontend/A64/a64_location_descriptor.cpp
|
||||||
|
frontend/A64/a64_location_descriptor.h
|
||||||
|
frontend/A64/decoder/a64.h
|
||||||
|
frontend/A64/decoder/a64.inc
|
||||||
|
frontend/A64/translate/a64_translate.cpp
|
||||||
|
frontend/A64/translate/a64_translate.h
|
||||||
|
interface/A64/a64.h
|
||||||
|
interface/A64/config.h
|
||||||
)
|
)
|
||||||
|
|
||||||
if ("A32" IN_LIST DYNARMIC_FRONTENDS)
|
|
||||||
target_sources(dynarmic PRIVATE
|
|
||||||
frontend/A32/a32_ir_emitter.cpp
|
|
||||||
frontend/A32/a32_ir_emitter.h
|
|
||||||
frontend/A32/a32_location_descriptor.cpp
|
|
||||||
frontend/A32/a32_location_descriptor.h
|
|
||||||
frontend/A32/decoder/arm.h
|
|
||||||
frontend/A32/decoder/arm.inc
|
|
||||||
frontend/A32/decoder/asimd.h
|
|
||||||
frontend/A32/decoder/asimd.inc
|
|
||||||
frontend/A32/decoder/thumb16.h
|
|
||||||
frontend/A32/decoder/thumb16.inc
|
|
||||||
frontend/A32/decoder/thumb32.h
|
|
||||||
frontend/A32/decoder/thumb32.inc
|
|
||||||
frontend/A32/decoder/vfp.h
|
|
||||||
frontend/A32/decoder/vfp.inc
|
|
||||||
frontend/A32/disassembler/disassembler.h
|
|
||||||
frontend/A32/disassembler/disassembler_arm.cpp
|
|
||||||
frontend/A32/disassembler/disassembler_thumb.cpp
|
|
||||||
frontend/A32/FPSCR.h
|
|
||||||
frontend/A32/ITState.h
|
|
||||||
frontend/A32/PSR.h
|
|
||||||
frontend/A32/translate/a32_translate.cpp
|
|
||||||
frontend/A32/translate/a32_translate.h
|
|
||||||
frontend/A32/translate/conditional_state.cpp
|
|
||||||
frontend/A32/translate/conditional_state.h
|
|
||||||
frontend/A32/translate/translate_arm.cpp
|
|
||||||
frontend/A32/translate/translate_thumb.cpp
|
|
||||||
interface/A32/a32.h
|
|
||||||
interface/A32/arch_version.h
|
|
||||||
interface/A32/config.h
|
|
||||||
interface/A32/coprocessor.h
|
|
||||||
interface/A32/coprocessor_util.h
|
|
||||||
interface/A32/disassembler.h
|
|
||||||
ir/opt/a32_constant_memory_reads_pass.cpp
|
|
||||||
ir/opt/a32_get_set_elimination_pass.cpp
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if ("A64" IN_LIST DYNARMIC_FRONTENDS)
|
|
||||||
target_sources(dynarmic PRIVATE
|
|
||||||
frontend/A64/a64_ir_emitter.cpp
|
|
||||||
frontend/A64/a64_ir_emitter.h
|
|
||||||
frontend/A64/a64_location_descriptor.cpp
|
|
||||||
frontend/A64/a64_location_descriptor.h
|
|
||||||
frontend/A64/decoder/a64.h
|
|
||||||
frontend/A64/decoder/a64.inc
|
|
||||||
frontend/A64/translate/a64_translate.cpp
|
|
||||||
frontend/A64/translate/a64_translate.h
|
|
||||||
interface/A64/a64.h
|
|
||||||
interface/A64/config.h
|
|
||||||
ir/opt/a64_callback_config_pass.cpp
|
|
||||||
ir/opt/a64_get_set_elimination_pass.cpp
|
|
||||||
ir/opt/a64_merge_interpret_blocks.cpp
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if ("x86_64" IN_LIST ARCHITECTURE)
|
if ("x86_64" IN_LIST ARCHITECTURE)
|
||||||
target_compile_definitions(dynarmic PRIVATE XBYAK_OLD_DISP_CHECK=1)
|
target_compile_definitions(dynarmic PRIVATE XBYAK_OLD_DISP_CHECK=1)
|
||||||
target_link_libraries(dynarmic
|
target_link_libraries(dynarmic
|
||||||
PRIVATE
|
PRIVATE
|
||||||
xbyak::xbyak
|
xbyak::xbyak
|
||||||
Zydis
|
Zydis::Zydis
|
||||||
)
|
)
|
||||||
|
|
||||||
target_architecture_specific_sources(dynarmic "x86_64"
|
target_architecture_specific_sources(dynarmic "x86_64"
|
||||||
|
@ -211,29 +189,21 @@ if ("x86_64" IN_LIST ARCHITECTURE)
|
||||||
common/spin_lock_x64.h
|
common/spin_lock_x64.h
|
||||||
common/x64_disassemble.cpp
|
common/x64_disassemble.cpp
|
||||||
common/x64_disassemble.h
|
common/x64_disassemble.h
|
||||||
|
# A32
|
||||||
|
backend/x64/a32_emit_x64.cpp
|
||||||
|
backend/x64/a32_emit_x64.h
|
||||||
|
backend/x64/a32_emit_x64_memory.cpp
|
||||||
|
backend/x64/a32_interface.cpp
|
||||||
|
backend/x64/a32_jitstate.cpp
|
||||||
|
backend/x64/a32_jitstate.h
|
||||||
|
# A64
|
||||||
|
backend/x64/a64_emit_x64.cpp
|
||||||
|
backend/x64/a64_emit_x64.h
|
||||||
|
backend/x64/a64_emit_x64_memory.cpp
|
||||||
|
backend/x64/a64_interface.cpp
|
||||||
|
backend/x64/a64_jitstate.cpp
|
||||||
|
backend/x64/a64_jitstate.h
|
||||||
)
|
)
|
||||||
|
|
||||||
if ("A32" IN_LIST DYNARMIC_FRONTENDS)
|
|
||||||
target_architecture_specific_sources(dynarmic "x86_64"
|
|
||||||
backend/x64/a32_emit_x64.cpp
|
|
||||||
backend/x64/a32_emit_x64.h
|
|
||||||
backend/x64/a32_emit_x64_memory.cpp
|
|
||||||
backend/x64/a32_interface.cpp
|
|
||||||
backend/x64/a32_jitstate.cpp
|
|
||||||
backend/x64/a32_jitstate.h
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if ("A64" IN_LIST DYNARMIC_FRONTENDS)
|
|
||||||
target_architecture_specific_sources(dynarmic "x86_64"
|
|
||||||
backend/x64/a64_emit_x64.cpp
|
|
||||||
backend/x64/a64_emit_x64.h
|
|
||||||
backend/x64/a64_emit_x64_memory.cpp
|
|
||||||
backend/x64/a64_interface.cpp
|
|
||||||
backend/x64/a64_jitstate.cpp
|
|
||||||
backend/x64/a64_jitstate.h
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if ("arm64" IN_LIST ARCHITECTURE)
|
if ("arm64" IN_LIST ARCHITECTURE)
|
||||||
|
@ -277,25 +247,17 @@ if ("arm64" IN_LIST ARCHITECTURE)
|
||||||
backend/arm64/verbose_debugging_output.h
|
backend/arm64/verbose_debugging_output.h
|
||||||
common/spin_lock_arm64.cpp
|
common/spin_lock_arm64.cpp
|
||||||
common/spin_lock_arm64.h
|
common/spin_lock_arm64.h
|
||||||
|
# A32
|
||||||
|
backend/arm64/a32_address_space.cpp
|
||||||
|
backend/arm64/a32_address_space.h
|
||||||
|
backend/arm64/a32_core.h
|
||||||
|
backend/arm64/a32_interface.cpp
|
||||||
|
# A64
|
||||||
|
backend/arm64/a64_address_space.cpp
|
||||||
|
backend/arm64/a64_address_space.h
|
||||||
|
backend/arm64/a64_core.h
|
||||||
|
backend/arm64/a64_interface.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if ("A32" IN_LIST DYNARMIC_FRONTENDS)
|
|
||||||
target_architecture_specific_sources(dynarmic "arm64"
|
|
||||||
backend/arm64/a32_address_space.cpp
|
|
||||||
backend/arm64/a32_address_space.h
|
|
||||||
backend/arm64/a32_core.h
|
|
||||||
backend/arm64/a32_interface.cpp
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if ("A64" IN_LIST DYNARMIC_FRONTENDS)
|
|
||||||
target_architecture_specific_sources(dynarmic "arm64"
|
|
||||||
backend/arm64/a64_address_space.cpp
|
|
||||||
backend/arm64/a64_address_space.h
|
|
||||||
backend/arm64/a64_core.h
|
|
||||||
backend/arm64/a64_interface.cpp
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if ("riscv" IN_LIST ARCHITECTURE)
|
if ("riscv" IN_LIST ARCHITECTURE)
|
||||||
|
@ -324,21 +286,14 @@ if ("riscv" IN_LIST ARCHITECTURE)
|
||||||
backend/riscv64/reg_alloc.cpp
|
backend/riscv64/reg_alloc.cpp
|
||||||
backend/riscv64/reg_alloc.h
|
backend/riscv64/reg_alloc.h
|
||||||
backend/riscv64/stack_layout.h
|
backend/riscv64/stack_layout.h
|
||||||
|
# A32
|
||||||
|
backend/riscv64/a32_address_space.cpp
|
||||||
|
backend/riscv64/a32_address_space.h
|
||||||
|
backend/riscv64/a32_core.h
|
||||||
|
backend/riscv64/a32_interface.cpp
|
||||||
|
backend/riscv64/code_block.h
|
||||||
)
|
)
|
||||||
|
message(FATAL_ERROR "TODO: Unimplemented frontend for this host architecture")
|
||||||
if ("A32" IN_LIST DYNARMIC_FRONTENDS)
|
|
||||||
target_sources(dynarmic PRIVATE
|
|
||||||
backend/riscv64/a32_address_space.cpp
|
|
||||||
backend/riscv64/a32_address_space.h
|
|
||||||
backend/riscv64/a32_core.h
|
|
||||||
backend/riscv64/a32_interface.cpp
|
|
||||||
backend/riscv64/code_block.h
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if ("A64" IN_LIST DYNARMIC_FRONTENDS)
|
|
||||||
message(FATAL_ERROR "TODO: Unimplemented frontend for this host architecture")
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
|
@ -416,7 +371,7 @@ target_link_libraries(dynarmic
|
||||||
)
|
)
|
||||||
|
|
||||||
if (BOOST_NO_HEADERS)
|
if (BOOST_NO_HEADERS)
|
||||||
target_link_libraries(dynarmic PRIVATE Boost::variant Boost::icl Boost::pool)
|
target_link_libraries(dynarmic PRIVATE Boost::variant Boost::icl Boost::pool)
|
||||||
else()
|
else()
|
||||||
target_link_libraries(dynarmic PRIVATE Boost::headers)
|
target_link_libraries(dynarmic PRIVATE Boost::headers)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
/* This file is part of the dynarmic project.
|
/* This file is part of the dynarmic project.
|
||||||
* Copyright (c) 2022 MerryMage
|
* Copyright (c) 2022 MerryMage
|
||||||
* SPDX-License-Identifier: 0BSD
|
* SPDX-License-Identifier: 0BSD
|
||||||
|
@ -16,7 +19,7 @@
|
||||||
#include "dynarmic/frontend/A32/translate/a32_translate.h"
|
#include "dynarmic/frontend/A32/translate/a32_translate.h"
|
||||||
#include "dynarmic/interface/A32/config.h"
|
#include "dynarmic/interface/A32/config.h"
|
||||||
#include "dynarmic/interface/exclusive_monitor.h"
|
#include "dynarmic/interface/exclusive_monitor.h"
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
#include "dynarmic/ir/opt_passes.h"
|
||||||
|
|
||||||
namespace Dynarmic::Backend::Arm64 {
|
namespace Dynarmic::Backend::Arm64 {
|
||||||
|
|
||||||
|
@ -163,21 +166,7 @@ A32AddressSpace::A32AddressSpace(const A32::UserConfig& conf)
|
||||||
|
|
||||||
IR::Block A32AddressSpace::GenerateIR(IR::LocationDescriptor descriptor) const {
|
IR::Block A32AddressSpace::GenerateIR(IR::LocationDescriptor descriptor) const {
|
||||||
IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
|
IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
|
||||||
|
Optimization::Optimize(ir_block, conf, {});
|
||||||
Optimization::PolyfillPass(ir_block, {});
|
|
||||||
Optimization::NamingPass(ir_block);
|
|
||||||
if (conf.HasOptimization(OptimizationFlag::GetSetElimination)) {
|
|
||||||
Optimization::A32GetSetElimination(ir_block, {.convert_nzc_to_nz = true});
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
}
|
|
||||||
if (conf.HasOptimization(OptimizationFlag::ConstProp)) {
|
|
||||||
Optimization::A32ConstantMemoryReads(ir_block, conf.callbacks);
|
|
||||||
Optimization::ConstantPropagation(ir_block);
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
}
|
|
||||||
Optimization::IdentityRemovalPass(ir_block);
|
|
||||||
Optimization::VerificationPass(ir_block);
|
|
||||||
|
|
||||||
return ir_block;
|
return ir_block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
/* This file is part of the dynarmic project.
|
/* This file is part of the dynarmic project.
|
||||||
* Copyright (c) 2022 MerryMage
|
* Copyright (c) 2022 MerryMage
|
||||||
* SPDX-License-Identifier: 0BSD
|
* SPDX-License-Identifier: 0BSD
|
||||||
|
@ -15,7 +18,7 @@
|
||||||
#include "dynarmic/frontend/A64/translate/a64_translate.h"
|
#include "dynarmic/frontend/A64/translate/a64_translate.h"
|
||||||
#include "dynarmic/interface/A64/config.h"
|
#include "dynarmic/interface/A64/config.h"
|
||||||
#include "dynarmic/interface/exclusive_monitor.h"
|
#include "dynarmic/interface/exclusive_monitor.h"
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
#include "dynarmic/ir/opt_passes.h"
|
||||||
|
|
||||||
namespace Dynarmic::Backend::Arm64 {
|
namespace Dynarmic::Backend::Arm64 {
|
||||||
|
|
||||||
|
@ -331,22 +334,7 @@ IR::Block A64AddressSpace::GenerateIR(IR::LocationDescriptor descriptor) const {
|
||||||
const auto get_code = [this](u64 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); };
|
const auto get_code = [this](u64 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); };
|
||||||
IR::Block ir_block = A64::Translate(A64::LocationDescriptor{descriptor}, get_code,
|
IR::Block ir_block = A64::Translate(A64::LocationDescriptor{descriptor}, get_code,
|
||||||
{conf.define_unpredictable_behaviour, conf.wall_clock_cntpct});
|
{conf.define_unpredictable_behaviour, conf.wall_clock_cntpct});
|
||||||
|
Optimization::Optimize(ir_block, conf, {});
|
||||||
Optimization::A64CallbackConfigPass(ir_block, conf);
|
|
||||||
Optimization::NamingPass(ir_block);
|
|
||||||
if (conf.HasOptimization(OptimizationFlag::GetSetElimination) && !conf.check_halt_on_memory_access) {
|
|
||||||
Optimization::A64GetSetElimination(ir_block);
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
}
|
|
||||||
if (conf.HasOptimization(OptimizationFlag::ConstProp)) {
|
|
||||||
Optimization::ConstantPropagation(ir_block);
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
}
|
|
||||||
if (conf.HasOptimization(OptimizationFlag::MiscIROpt)) {
|
|
||||||
Optimization::A64MergeInterpretBlocksPass(ir_block, conf.callbacks);
|
|
||||||
}
|
|
||||||
Optimization::VerificationPass(ir_block);
|
|
||||||
|
|
||||||
return ir_block;
|
return ir_block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,15 +15,15 @@
|
||||||
#include <mcl/macro/architecture.hpp>
|
#include <mcl/macro/architecture.hpp>
|
||||||
#include "dynarmic/common/common_types.h"
|
#include "dynarmic/common/common_types.h"
|
||||||
|
|
||||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
namespace Dynarmic::Backend::X64 {
|
namespace Dynarmic::Backend::X64 {
|
||||||
class BlockOfCode;
|
class BlockOfCode;
|
||||||
} // namespace Dynarmic::Backend::X64
|
} // namespace Dynarmic::Backend::X64
|
||||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
namespace oaknut {
|
namespace oaknut {
|
||||||
class CodeBlock;
|
class CodeBlock;
|
||||||
} // namespace oaknut
|
} // namespace oaknut
|
||||||
#elif defined(MCL_ARCHITECTURE_RISCV)
|
#elif defined(ARCHITECTURE_riscv64)
|
||||||
namespace Dynarmic::Backend::RV64 {
|
namespace Dynarmic::Backend::RV64 {
|
||||||
class CodeBlock;
|
class CodeBlock;
|
||||||
} // namespace Dynarmic::Backend::RV64
|
} // namespace Dynarmic::Backend::RV64
|
||||||
|
@ -33,16 +33,16 @@ class CodeBlock;
|
||||||
|
|
||||||
namespace Dynarmic::Backend {
|
namespace Dynarmic::Backend {
|
||||||
|
|
||||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
struct FakeCall {
|
struct FakeCall {
|
||||||
u64 call_rip;
|
u64 call_rip;
|
||||||
u64 ret_rip;
|
u64 ret_rip;
|
||||||
};
|
};
|
||||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
struct FakeCall {
|
struct FakeCall {
|
||||||
u64 call_pc;
|
u64 call_pc;
|
||||||
};
|
};
|
||||||
#elif defined(MCL_ARCHITECTURE_RISCV)
|
#elif defined(ARCHITECTURE_riscv64)
|
||||||
struct FakeCall {
|
struct FakeCall {
|
||||||
};
|
};
|
||||||
#else
|
#else
|
||||||
|
@ -54,11 +54,11 @@ public:
|
||||||
ExceptionHandler();
|
ExceptionHandler();
|
||||||
~ExceptionHandler();
|
~ExceptionHandler();
|
||||||
|
|
||||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
void Register(X64::BlockOfCode& code);
|
void Register(X64::BlockOfCode& code);
|
||||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
void Register(oaknut::CodeBlock& mem, std::size_t mem_size);
|
void Register(oaknut::CodeBlock& mem, std::size_t mem_size);
|
||||||
#elif defined(MCL_ARCHITECTURE_RISCV)
|
#elif defined(ARCHITECTURE_riscv64)
|
||||||
void Register(RV64::CodeBlock& mem, std::size_t mem_size);
|
void Register(RV64::CodeBlock& mem, std::size_t mem_size);
|
||||||
#else
|
#else
|
||||||
# error "Invalid architecture"
|
# error "Invalid architecture"
|
||||||
|
|
|
@ -13,15 +13,15 @@ struct ExceptionHandler::Impl final {
|
||||||
ExceptionHandler::ExceptionHandler() = default;
|
ExceptionHandler::ExceptionHandler() = default;
|
||||||
ExceptionHandler::~ExceptionHandler() = default;
|
ExceptionHandler::~ExceptionHandler() = default;
|
||||||
|
|
||||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
void ExceptionHandler::Register(X64::BlockOfCode&) {
|
void ExceptionHandler::Register(X64::BlockOfCode&) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
void ExceptionHandler::Register(oaknut::CodeBlock&, std::size_t) {
|
void ExceptionHandler::Register(oaknut::CodeBlock&, std::size_t) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
#elif defined(MCL_ARCHITECTURE_RISCV)
|
#elif defined(ARCHITECTURE_riscv64)
|
||||||
void ExceptionHandler::Register(RV64::CodeBlock&, std::size_t) {
|
void ExceptionHandler::Register(RV64::CodeBlock&, std::size_t) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
#include "dynarmic/backend/exception_handler.h"
|
#include "dynarmic/backend/exception_handler.h"
|
||||||
|
|
||||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
|
|
||||||
# include "dynarmic/backend/x64/block_of_code.h"
|
# include "dynarmic/backend/x64/block_of_code.h"
|
||||||
# define mig_external extern "C"
|
# define mig_external extern "C"
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
|
|
||||||
using dynarmic_thread_state_t = x86_thread_state64_t;
|
using dynarmic_thread_state_t = x86_thread_state64_t;
|
||||||
|
|
||||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
|
|
||||||
# include <oaknut/code_block.hpp>
|
# include <oaknut/code_block.hpp>
|
||||||
# define mig_external extern "C"
|
# define mig_external extern "C"
|
||||||
|
@ -133,7 +133,7 @@ void MachHandler::MessagePump() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
kern_return_t MachHandler::HandleRequest(x86_thread_state64_t* ts) {
|
kern_return_t MachHandler::HandleRequest(x86_thread_state64_t* ts) {
|
||||||
std::lock_guard<std::mutex> guard(code_block_infos_mutex);
|
std::lock_guard<std::mutex> guard(code_block_infos_mutex);
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ kern_return_t MachHandler::HandleRequest(x86_thread_state64_t* ts) {
|
||||||
|
|
||||||
return KERN_SUCCESS;
|
return KERN_SUCCESS;
|
||||||
}
|
}
|
||||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
kern_return_t MachHandler::HandleRequest(arm_thread_state64_t* ts) {
|
kern_return_t MachHandler::HandleRequest(arm_thread_state64_t* ts) {
|
||||||
std::lock_guard<std::mutex> guard(code_block_infos_mutex);
|
std::lock_guard<std::mutex> guard(code_block_infos_mutex);
|
||||||
|
|
||||||
|
@ -269,13 +269,13 @@ private:
|
||||||
ExceptionHandler::ExceptionHandler() = default;
|
ExceptionHandler::ExceptionHandler() = default;
|
||||||
ExceptionHandler::~ExceptionHandler() = default;
|
ExceptionHandler::~ExceptionHandler() = default;
|
||||||
|
|
||||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
void ExceptionHandler::Register(X64::BlockOfCode& code) {
|
void ExceptionHandler::Register(X64::BlockOfCode& code) {
|
||||||
const u64 code_begin = mcl::bit_cast<u64>(code.getCode());
|
const u64 code_begin = mcl::bit_cast<u64>(code.getCode());
|
||||||
const u64 code_end = code_begin + code.GetTotalCodeSize();
|
const u64 code_end = code_begin + code.GetTotalCodeSize();
|
||||||
impl = std::make_unique<Impl>(code_begin, code_end);
|
impl = std::make_unique<Impl>(code_begin, code_end);
|
||||||
}
|
}
|
||||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
void ExceptionHandler::Register(oaknut::CodeBlock& mem, std::size_t size) {
|
void ExceptionHandler::Register(oaknut::CodeBlock& mem, std::size_t size) {
|
||||||
const u64 code_begin = mcl::bit_cast<u64>(mem.ptr());
|
const u64 code_begin = mcl::bit_cast<u64>(mem.ptr());
|
||||||
const u64 code_end = code_begin + size;
|
const u64 code_end = code_begin + size;
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
|
|
||||||
#include <mcl/macro/architecture.hpp>
|
#include <mcl/macro/architecture.hpp>
|
||||||
|
|
||||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
# include "dynarmic/backend/x64/mig/mach_exc_server.c"
|
# include "dynarmic/backend/x64/mig/mach_exc_server.c"
|
||||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
# include "dynarmic/backend/arm64/mig/mach_exc_server.c"
|
# include "dynarmic/backend/arm64/mig/mach_exc_server.c"
|
||||||
#else
|
#else
|
||||||
# error "Invalid architecture"
|
# error "Invalid architecture"
|
||||||
|
|
|
@ -8,19 +8,6 @@
|
||||||
|
|
||||||
#include "dynarmic/backend/exception_handler.h"
|
#include "dynarmic/backend/exception_handler.h"
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
# include <signal.h>
|
|
||||||
# include <sys/ucontext.h>
|
|
||||||
#else
|
|
||||||
# include <signal.h>
|
|
||||||
# ifndef __OpenBSD__
|
|
||||||
# include <ucontext.h>
|
|
||||||
# endif
|
|
||||||
# ifdef __sun__
|
|
||||||
# include <sys/regset.h>
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -29,16 +16,17 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "dynarmic/common/assert.h"
|
#include "dynarmic/common/assert.h"
|
||||||
|
#include "dynarmic/common/context.h"
|
||||||
#include <mcl/bit_cast.hpp>
|
#include <mcl/bit_cast.hpp>
|
||||||
#include "dynarmic/common/common_types.h"
|
#include "dynarmic/common/common_types.h"
|
||||||
|
|
||||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
# include "dynarmic/backend/x64/block_of_code.h"
|
# include "dynarmic/backend/x64/block_of_code.h"
|
||||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
# include <oaknut/code_block.hpp>
|
# include <oaknut/code_block.hpp>
|
||||||
|
|
||||||
# include "dynarmic/backend/arm64/abi.h"
|
# include "dynarmic/backend/arm64/abi.h"
|
||||||
#elif defined(MCL_ARCHITECTURE_RISCV)
|
#elif defined(ARCHITECTURE_riscv64)
|
||||||
# include "dynarmic/backend/riscv64/code_block.h"
|
# include "dynarmic/backend/riscv64/code_block.h"
|
||||||
#else
|
#else
|
||||||
# error "Invalid architecture"
|
# error "Invalid architecture"
|
||||||
|
@ -131,139 +119,50 @@ SigHandler::~SigHandler() {
|
||||||
|
|
||||||
void SigHandler::AddCodeBlock(CodeBlockInfo cbi) {
|
void SigHandler::AddCodeBlock(CodeBlockInfo cbi) {
|
||||||
std::lock_guard<std::mutex> guard(code_block_infos_mutex);
|
std::lock_guard<std::mutex> guard(code_block_infos_mutex);
|
||||||
if (auto iter = FindCodeBlockInfo(cbi.code_begin); iter != code_block_infos.end()) {
|
if (auto const iter = FindCodeBlockInfo(cbi.code_begin); iter != code_block_infos.end())
|
||||||
code_block_infos.erase(iter);
|
code_block_infos.erase(iter);
|
||||||
}
|
|
||||||
code_block_infos.push_back(cbi);
|
code_block_infos.push_back(cbi);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SigHandler::RemoveCodeBlock(u64 host_pc) {
|
void SigHandler::RemoveCodeBlock(u64 host_pc) {
|
||||||
std::lock_guard<std::mutex> guard(code_block_infos_mutex);
|
std::lock_guard<std::mutex> guard(code_block_infos_mutex);
|
||||||
const auto iter = FindCodeBlockInfo(host_pc);
|
const auto iter = FindCodeBlockInfo(host_pc);
|
||||||
if (iter == code_block_infos.end()) {
|
if (iter != code_block_infos.end())
|
||||||
return;
|
code_block_infos.erase(iter);
|
||||||
}
|
|
||||||
code_block_infos.erase(iter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
|
void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
|
||||||
ASSERT(sig == SIGSEGV || sig == SIGBUS);
|
ASSERT(sig == SIGSEGV || sig == SIGBUS);
|
||||||
|
CTX_DECLARE(raw_context);
|
||||||
#ifndef MCL_ARCHITECTURE_RISCV
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(raw_context);
|
|
||||||
#ifndef __OpenBSD__
|
|
||||||
auto& mctx = ucontext->uc_mcontext;
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
|
||||||
|
|
||||||
# if defined(__APPLE__)
|
|
||||||
# define CTX_RIP (mctx->__ss.__rip)
|
|
||||||
# define CTX_RSP (mctx->__ss.__rsp)
|
|
||||||
# elif defined(__linux__)
|
|
||||||
# define CTX_RIP (mctx.gregs[REG_RIP])
|
|
||||||
# define CTX_RSP (mctx.gregs[REG_RSP])
|
|
||||||
# elif defined(__FreeBSD__)
|
|
||||||
# define CTX_RIP (mctx.mc_rip)
|
|
||||||
# define CTX_RSP (mctx.mc_rsp)
|
|
||||||
# elif defined(__NetBSD__)
|
|
||||||
# define CTX_RIP (mctx.__gregs[_REG_RIP])
|
|
||||||
# define CTX_RSP (mctx.__gregs[_REG_RSP])
|
|
||||||
# elif defined(__OpenBSD__)
|
|
||||||
# define CTX_RIP (ucontext->sc_rip)
|
|
||||||
# define CTX_RSP (ucontext->sc_rsp)
|
|
||||||
# elif defined(__sun__)
|
|
||||||
# define CTX_RIP (mctx.gregs[REG_RIP])
|
|
||||||
# define CTX_RSP (mctx.gregs[REG_RSP])
|
|
||||||
# else
|
|
||||||
# error "Unknown platform"
|
|
||||||
# endif
|
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard(sig_handler->code_block_infos_mutex);
|
std::lock_guard<std::mutex> guard(sig_handler->code_block_infos_mutex);
|
||||||
|
|
||||||
const auto iter = sig_handler->FindCodeBlockInfo(CTX_RIP);
|
const auto iter = sig_handler->FindCodeBlockInfo(CTX_RIP);
|
||||||
if (iter != sig_handler->code_block_infos.end()) {
|
if (iter != sig_handler->code_block_infos.end()) {
|
||||||
FakeCall fc = iter->cb(CTX_RIP);
|
FakeCall fc = iter->cb(CTX_RIP);
|
||||||
|
|
||||||
CTX_RSP -= sizeof(u64);
|
CTX_RSP -= sizeof(u64);
|
||||||
*mcl::bit_cast<u64*>(CTX_RSP) = fc.ret_rip;
|
*mcl::bit_cast<u64*>(CTX_RSP) = fc.ret_rip;
|
||||||
CTX_RIP = fc.call_rip;
|
CTX_RIP = fc.call_rip;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::print(stderr, "Unhandled {} at rip {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_RIP);
|
fmt::print(stderr, "Unhandled {} at rip {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_RIP);
|
||||||
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
|
||||||
|
|
||||||
# if defined(__APPLE__)
|
|
||||||
# define CTX_PC (mctx->__ss.__pc)
|
|
||||||
# define CTX_SP (mctx->__ss.__sp)
|
|
||||||
# define CTX_LR (mctx->__ss.__lr)
|
|
||||||
# define CTX_X(i) (mctx->__ss.__x[i])
|
|
||||||
# define CTX_Q(i) (mctx->__ns.__v[i])
|
|
||||||
# elif defined(__linux__)
|
|
||||||
# define CTX_PC (mctx.pc)
|
|
||||||
# define CTX_SP (mctx.sp)
|
|
||||||
# define CTX_LR (mctx.regs[30])
|
|
||||||
# define CTX_X(i) (mctx.regs[i])
|
|
||||||
# define CTX_Q(i) (fpctx->vregs[i])
|
|
||||||
[[maybe_unused]] const auto fpctx = [&mctx] {
|
|
||||||
_aarch64_ctx* header = (_aarch64_ctx*)&mctx.__reserved;
|
|
||||||
while (header->magic != FPSIMD_MAGIC) {
|
|
||||||
ASSERT(header->magic && header->size);
|
|
||||||
header = (_aarch64_ctx*)((char*)header + header->size);
|
|
||||||
}
|
|
||||||
return (fpsimd_context*)header;
|
|
||||||
}();
|
|
||||||
# elif defined(__FreeBSD__)
|
|
||||||
# define CTX_PC (mctx.mc_gpregs.gp_elr)
|
|
||||||
# define CTX_SP (mctx.mc_gpregs.gp_sp)
|
|
||||||
# define CTX_LR (mctx.mc_gpregs.gp_lr)
|
|
||||||
# define CTX_X(i) (mctx.mc_gpregs.gp_x[i])
|
|
||||||
# define CTX_Q(i) (mctx.mc_fpregs.fp_q[i])
|
|
||||||
# elif defined(__NetBSD__)
|
|
||||||
# define CTX_PC (mctx.mc_gpregs.gp_elr)
|
|
||||||
# define CTX_SP (mctx.mc_gpregs.gp_sp)
|
|
||||||
# define CTX_LR (mctx.mc_gpregs.gp_lr)
|
|
||||||
# define CTX_X(i) (mctx.mc_gpregs.gp_x[i])
|
|
||||||
# define CTX_Q(i) (mctx.mc_fpregs.fp_q[i])
|
|
||||||
# elif defined(__OpenBSD__)
|
|
||||||
# define CTX_PC (ucontext->sc_elr)
|
|
||||||
# define CTX_SP (ucontext->sc_sp)
|
|
||||||
# define CTX_LR (ucontext->sc_lr)
|
|
||||||
# define CTX_X(i) (ucontext->sc_x[i])
|
|
||||||
# define CTX_Q(i) (ucontext->sc_q[i])
|
|
||||||
# else
|
|
||||||
# error "Unknown platform"
|
|
||||||
# endif
|
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard(sig_handler->code_block_infos_mutex);
|
std::lock_guard<std::mutex> guard(sig_handler->code_block_infos_mutex);
|
||||||
|
|
||||||
const auto iter = sig_handler->FindCodeBlockInfo(CTX_PC);
|
const auto iter = sig_handler->FindCodeBlockInfo(CTX_PC);
|
||||||
if (iter != sig_handler->code_block_infos.end()) {
|
if (iter != sig_handler->code_block_infos.end()) {
|
||||||
FakeCall fc = iter->cb(CTX_PC);
|
FakeCall fc = iter->cb(CTX_PC);
|
||||||
|
|
||||||
CTX_PC = fc.call_pc;
|
CTX_PC = fc.call_pc;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
|
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
|
||||||
|
#elif defined(ARCHITECTURE_riscv64)
|
||||||
#elif defined(MCL_ARCHITECTURE_RISCV)
|
|
||||||
|
|
||||||
ASSERT_FALSE("Unimplemented");
|
ASSERT_FALSE("Unimplemented");
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
# error "Invalid architecture"
|
# error "Invalid architecture"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct sigaction* retry_sa = sig == SIGSEGV ? &sig_handler->old_sa_segv : &sig_handler->old_sa_bus;
|
struct sigaction* retry_sa = sig == SIGSEGV ? &sig_handler->old_sa_segv : &sig_handler->old_sa_bus;
|
||||||
|
@ -309,19 +208,19 @@ private:
|
||||||
ExceptionHandler::ExceptionHandler() = default;
|
ExceptionHandler::ExceptionHandler() = default;
|
||||||
ExceptionHandler::~ExceptionHandler() = default;
|
ExceptionHandler::~ExceptionHandler() = default;
|
||||||
|
|
||||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
void ExceptionHandler::Register(X64::BlockOfCode& code) {
|
void ExceptionHandler::Register(X64::BlockOfCode& code) {
|
||||||
const u64 code_begin = mcl::bit_cast<u64>(code.getCode());
|
const u64 code_begin = mcl::bit_cast<u64>(code.getCode());
|
||||||
const u64 code_end = code_begin + code.GetTotalCodeSize();
|
const u64 code_end = code_begin + code.GetTotalCodeSize();
|
||||||
impl = std::make_unique<Impl>(code_begin, code_end);
|
impl = std::make_unique<Impl>(code_begin, code_end);
|
||||||
}
|
}
|
||||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
void ExceptionHandler::Register(oaknut::CodeBlock& mem, std::size_t size) {
|
void ExceptionHandler::Register(oaknut::CodeBlock& mem, std::size_t size) {
|
||||||
const u64 code_begin = mcl::bit_cast<u64>(mem.ptr());
|
const u64 code_begin = mcl::bit_cast<u64>(mem.ptr());
|
||||||
const u64 code_end = code_begin + size;
|
const u64 code_end = code_begin + size;
|
||||||
impl = std::make_unique<Impl>(code_begin, code_end);
|
impl = std::make_unique<Impl>(code_begin, code_end);
|
||||||
}
|
}
|
||||||
#elif defined(MCL_ARCHITECTURE_RISCV)
|
#elif defined(ARCHITECTURE_riscv64)
|
||||||
void ExceptionHandler::Register(RV64::CodeBlock& mem, std::size_t size) {
|
void ExceptionHandler::Register(RV64::CodeBlock& mem, std::size_t size) {
|
||||||
const u64 code_begin = mcl::bit_cast<u64>(mem.ptr<u64>());
|
const u64 code_begin = mcl::bit_cast<u64>(mem.ptr<u64>());
|
||||||
const u64 code_end = code_begin + size;
|
const u64 code_end = code_begin + size;
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
|
|
||||||
#include <mcl/macro/architecture.hpp>
|
#include <mcl/macro/architecture.hpp>
|
||||||
|
|
||||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
# include "dynarmic/backend/x64/exception_handler_windows.cpp"
|
# include "dynarmic/backend/x64/exception_handler_windows.cpp"
|
||||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
# include "dynarmic/backend/exception_handler_generic.cpp"
|
# include "dynarmic/backend/exception_handler_generic.cpp"
|
||||||
#else
|
#else
|
||||||
# error "Invalid architecture"
|
# error "Invalid architecture"
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#include "dynarmic/backend/riscv64/stack_layout.h"
|
#include "dynarmic/backend/riscv64/stack_layout.h"
|
||||||
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
|
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
|
||||||
#include "dynarmic/frontend/A32/translate/a32_translate.h"
|
#include "dynarmic/frontend/A32/translate/a32_translate.h"
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
#include "dynarmic/ir/opt_passes.h"
|
||||||
|
|
||||||
namespace Dynarmic::Backend::RV64 {
|
namespace Dynarmic::Backend::RV64 {
|
||||||
|
|
||||||
|
@ -28,19 +28,7 @@ A32AddressSpace::A32AddressSpace(const A32::UserConfig& conf)
|
||||||
|
|
||||||
IR::Block A32AddressSpace::GenerateIR(IR::LocationDescriptor descriptor) const {
|
IR::Block A32AddressSpace::GenerateIR(IR::LocationDescriptor descriptor) const {
|
||||||
IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
|
IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
|
||||||
|
Optimization::Optimize(ir_block, conf, {});
|
||||||
Optimization::PolyfillPass(ir_block, {});
|
|
||||||
if (conf.HasOptimization(OptimizationFlag::GetSetElimination)) {
|
|
||||||
Optimization::A32GetSetElimination(ir_block, {.convert_nzc_to_nz = true});
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
}
|
|
||||||
if (conf.HasOptimization(OptimizationFlag::ConstProp)) {
|
|
||||||
Optimization::A32ConstantMemoryReads(ir_block, conf.callbacks);
|
|
||||||
Optimization::ConstantPropagation(ir_block);
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
}
|
|
||||||
Optimization::VerificationPass(ir_block);
|
|
||||||
|
|
||||||
return ir_block;
|
return ir_block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
#include "dynarmic/interface/A32/a32.h"
|
#include "dynarmic/interface/A32/a32.h"
|
||||||
#include "dynarmic/ir/basic_block.h"
|
#include "dynarmic/ir/basic_block.h"
|
||||||
#include "dynarmic/ir/location_descriptor.h"
|
#include "dynarmic/ir/location_descriptor.h"
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
#include "dynarmic/ir/opt_passes.h"
|
||||||
|
|
||||||
namespace Dynarmic::A32 {
|
namespace Dynarmic::A32 {
|
||||||
|
|
||||||
|
@ -217,19 +217,7 @@ private:
|
||||||
block_of_code.EnsureMemoryCommitted(MINIMUM_REMAINING_CODESIZE);
|
block_of_code.EnsureMemoryCommitted(MINIMUM_REMAINING_CODESIZE);
|
||||||
|
|
||||||
IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
|
IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
|
||||||
Optimization::PolyfillPass(ir_block, polyfill_options);
|
Optimization::Optimize(ir_block, conf, polyfill_options);
|
||||||
Optimization::NamingPass(ir_block);
|
|
||||||
if (conf.HasOptimization(OptimizationFlag::GetSetElimination) && !conf.check_halt_on_memory_access) {
|
|
||||||
Optimization::A32GetSetElimination(ir_block, {.convert_nz_to_nzc = true});
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
}
|
|
||||||
if (conf.HasOptimization(OptimizationFlag::ConstProp)) {
|
|
||||||
Optimization::A32ConstantMemoryReads(ir_block, conf.callbacks);
|
|
||||||
Optimization::ConstantPropagation(ir_block);
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
}
|
|
||||||
Optimization::IdentityRemovalPass(ir_block);
|
|
||||||
Optimization::VerificationPass(ir_block);
|
|
||||||
return emitter.Emit(ir_block);
|
return emitter.Emit(ir_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -764,7 +764,7 @@ void A64EmitX64::EmitPatchMovRcx(CodePtr target_code_ptr) {
|
||||||
target_code_ptr = code.GetReturnFromRunCodeAddress();
|
target_code_ptr = code.GetReturnFromRunCodeAddress();
|
||||||
}
|
}
|
||||||
const CodePtr patch_location = code.getCurr();
|
const CodePtr patch_location = code.getCurr();
|
||||||
code.mov(code.rcx, reinterpret_cast<u64>(target_code_ptr));
|
code.mov(code.rcx, u64(target_code_ptr));
|
||||||
code.EnsurePatchLocationSize(patch_location, 10);
|
code.EnsurePatchLocationSize(patch_location, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
#include "dynarmic/frontend/A64/translate/a64_translate.h"
|
#include "dynarmic/frontend/A64/translate/a64_translate.h"
|
||||||
#include "dynarmic/interface/A64/a64.h"
|
#include "dynarmic/interface/A64/a64.h"
|
||||||
#include "dynarmic/ir/basic_block.h"
|
#include "dynarmic/ir/basic_block.h"
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
#include "dynarmic/ir/opt_passes.h"
|
||||||
|
|
||||||
namespace Dynarmic::A64 {
|
namespace Dynarmic::A64 {
|
||||||
|
|
||||||
|
@ -80,16 +80,16 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Check code alignment
|
// TODO: Check code alignment
|
||||||
|
const CodePtr aligned_code_ptr = CodePtr((uintptr_t(GetCurrentBlock()) + 15) & ~uintptr_t(15));
|
||||||
const CodePtr current_code_ptr = [this] {
|
const CodePtr current_code_ptr = [this, aligned_code_ptr] {
|
||||||
// RSB optimization
|
// RSB optimization
|
||||||
const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A64JitState::RSBPtrMask;
|
const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A64JitState::RSBPtrMask;
|
||||||
if (jit_state.GetUniqueHash() == jit_state.rsb_location_descriptors[new_rsb_ptr]) {
|
if (jit_state.GetUniqueHash() == jit_state.rsb_location_descriptors[new_rsb_ptr]) {
|
||||||
jit_state.rsb_ptr = new_rsb_ptr;
|
jit_state.rsb_ptr = new_rsb_ptr;
|
||||||
return reinterpret_cast<CodePtr>(jit_state.rsb_codeptrs[new_rsb_ptr]);
|
return CodePtr(jit_state.rsb_codeptrs[new_rsb_ptr]);
|
||||||
}
|
}
|
||||||
|
return aligned_code_ptr;
|
||||||
return GetCurrentBlock();
|
//return GetCurrentBlock();
|
||||||
}();
|
}();
|
||||||
|
|
||||||
const HaltReason hr = block_of_code.RunCode(&jit_state, current_code_ptr);
|
const HaltReason hr = block_of_code.RunCode(&jit_state, current_code_ptr);
|
||||||
|
@ -275,21 +275,7 @@ private:
|
||||||
const auto get_code = [this](u64 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); };
|
const auto get_code = [this](u64 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); };
|
||||||
IR::Block ir_block = A64::Translate(A64::LocationDescriptor{current_location}, get_code,
|
IR::Block ir_block = A64::Translate(A64::LocationDescriptor{current_location}, get_code,
|
||||||
{conf.define_unpredictable_behaviour, conf.wall_clock_cntpct});
|
{conf.define_unpredictable_behaviour, conf.wall_clock_cntpct});
|
||||||
Optimization::PolyfillPass(ir_block, polyfill_options);
|
Optimization::Optimize(ir_block, conf, polyfill_options);
|
||||||
Optimization::A64CallbackConfigPass(ir_block, conf);
|
|
||||||
Optimization::NamingPass(ir_block);
|
|
||||||
if (conf.HasOptimization(OptimizationFlag::GetSetElimination) && !conf.check_halt_on_memory_access) {
|
|
||||||
Optimization::A64GetSetElimination(ir_block);
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
}
|
|
||||||
if (conf.HasOptimization(OptimizationFlag::ConstProp)) {
|
|
||||||
Optimization::ConstantPropagation(ir_block);
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
}
|
|
||||||
if (conf.HasOptimization(OptimizationFlag::MiscIROpt)) {
|
|
||||||
Optimization::A64MergeInterpretBlocksPass(ir_block, conf.callbacks);
|
|
||||||
}
|
|
||||||
Optimization::VerificationPass(ir_block);
|
|
||||||
return emitter.Emit(ir_block).entrypoint;
|
return emitter.Emit(ir_block).entrypoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include <mcl/iterator/reverse.hpp>
|
|
||||||
#include "dynarmic/common/common_types.h"
|
#include "dynarmic/common/common_types.h"
|
||||||
#include <xbyak/xbyak.h>
|
#include <xbyak/xbyak.h>
|
||||||
|
|
||||||
|
@ -76,7 +75,8 @@ void ABI_PopRegistersAndAdjustStack(BlockOfCode& code, const size_t frame_size,
|
||||||
const FrameInfo frame_info = CalculateFrameInfo(num_gprs, num_xmms, frame_size);
|
const FrameInfo frame_info = CalculateFrameInfo(num_gprs, num_xmms, frame_size);
|
||||||
|
|
||||||
size_t xmm_offset = frame_info.xmm_offset + (num_xmms * XMM_SIZE);
|
size_t xmm_offset = frame_info.xmm_offset + (num_xmms * XMM_SIZE);
|
||||||
for (auto const xmm : mcl::iterator::reverse(regs)) {
|
for (auto it = regs.rbegin(); it != regs.rend(); ++it) {
|
||||||
|
auto const xmm = *it;
|
||||||
if (HostLocIsXMM(xmm)) {
|
if (HostLocIsXMM(xmm)) {
|
||||||
xmm_offset -= XMM_SIZE;
|
xmm_offset -= XMM_SIZE;
|
||||||
if (code.HasHostFeature(HostFeature::AVX)) {
|
if (code.HasHostFeature(HostFeature::AVX)) {
|
||||||
|
@ -88,9 +88,11 @@ void ABI_PopRegistersAndAdjustStack(BlockOfCode& code, const size_t frame_size,
|
||||||
}
|
}
|
||||||
if (frame_info.stack_subtraction != 0)
|
if (frame_info.stack_subtraction != 0)
|
||||||
code.add(rsp, u32(frame_info.stack_subtraction));
|
code.add(rsp, u32(frame_info.stack_subtraction));
|
||||||
for (auto const gpr : mcl::iterator::reverse(regs))
|
for (auto it = regs.rbegin(); it != regs.rend(); ++it) {
|
||||||
|
auto const gpr = *it;
|
||||||
if (HostLocIsGPR(gpr))
|
if (HostLocIsGPR(gpr))
|
||||||
code.pop(HostLocToReg64(gpr));
|
code.pop(HostLocToReg64(gpr));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ABI_PushCalleeSaveRegistersAndAdjustStack(BlockOfCode& code, const std::size_t frame_size) {
|
void ABI_PushCalleeSaveRegistersAndAdjustStack(BlockOfCode& code, const std::size_t frame_size) {
|
||||||
|
|
|
@ -92,13 +92,10 @@ void ForceDenormalsToZero(BlockOfCode& code, std::initializer_list<Xbyak::Xmm> t
|
||||||
FpFixup::Norm_Src,
|
FpFixup::Norm_Src,
|
||||||
FpFixup::Norm_Src,
|
FpFixup::Norm_Src,
|
||||||
FpFixup::Norm_Src);
|
FpFixup::Norm_Src);
|
||||||
|
const Xbyak::Xmm tmp = xmm0;
|
||||||
const Xbyak::Xmm tmp = xmm16;
|
|
||||||
FCODE(vmovap)(tmp, code.BConst<fsize>(xword, denormal_to_zero));
|
FCODE(vmovap)(tmp, code.BConst<fsize>(xword, denormal_to_zero));
|
||||||
|
for (const Xbyak::Xmm& xmm : to_daz)
|
||||||
for (const Xbyak::Xmm& xmm : to_daz) {
|
|
||||||
FCODE(vfixupimms)(xmm, xmm, tmp, u8(0));
|
FCODE(vfixupimms)(xmm, xmm, tmp, u8(0));
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -609,8 +609,8 @@ void EmitX64::EmitVectorArithmeticVShift16(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
|
||||||
const Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]);
|
const Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
const Xbyak::Xmm left_shift = ctx.reg_alloc.UseScratchXmm(args[1]);
|
const Xbyak::Xmm left_shift = ctx.reg_alloc.UseScratchXmm(args[1]);
|
||||||
const Xbyak::Xmm right_shift = xmm16;
|
const Xbyak::Xmm right_shift = ctx.reg_alloc.ScratchXmm();
|
||||||
const Xbyak::Xmm tmp = xmm17;
|
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm();
|
||||||
|
|
||||||
code.vmovdqa32(tmp, code.Const(xword, 0x00FF00FF00FF00FF, 0x00FF00FF00FF00FF));
|
code.vmovdqa32(tmp, code.Const(xword, 0x00FF00FF00FF00FF, 0x00FF00FF00FF00FF));
|
||||||
code.vpxord(right_shift, right_shift, right_shift);
|
code.vpxord(right_shift, right_shift, right_shift);
|
||||||
|
@ -674,8 +674,8 @@ void EmitX64::EmitVectorArithmeticVShift64(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
|
||||||
const Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]);
|
const Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
const Xbyak::Xmm left_shift = ctx.reg_alloc.UseScratchXmm(args[1]);
|
const Xbyak::Xmm left_shift = ctx.reg_alloc.UseScratchXmm(args[1]);
|
||||||
const Xbyak::Xmm right_shift = xmm16;
|
const Xbyak::Xmm right_shift = ctx.reg_alloc.ScratchXmm();
|
||||||
const Xbyak::Xmm tmp = xmm17;
|
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm();
|
||||||
|
|
||||||
code.vmovdqa32(tmp, code.Const(xword, 0x00000000000000FF, 0x00000000000000FF));
|
code.vmovdqa32(tmp, code.Const(xword, 0x00000000000000FF, 0x00000000000000FF));
|
||||||
code.vpxorq(right_shift, right_shift, right_shift);
|
code.vpxorq(right_shift, right_shift, right_shift);
|
||||||
|
@ -1955,8 +1955,8 @@ void EmitX64::EmitVectorLogicalVShift16(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
|
||||||
const Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]);
|
const Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
const Xbyak::Xmm left_shift = ctx.reg_alloc.UseScratchXmm(args[1]);
|
const Xbyak::Xmm left_shift = ctx.reg_alloc.UseScratchXmm(args[1]);
|
||||||
const Xbyak::Xmm right_shift = xmm16;
|
const Xbyak::Xmm right_shift = ctx.reg_alloc.ScratchXmm();
|
||||||
const Xbyak::Xmm tmp = xmm17;
|
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm();
|
||||||
|
|
||||||
code.vmovdqa32(tmp, code.Const(xword, 0x00FF00FF00FF00FF, 0x00FF00FF00FF00FF));
|
code.vmovdqa32(tmp, code.Const(xword, 0x00FF00FF00FF00FF, 0x00FF00FF00FF00FF));
|
||||||
code.vpxord(right_shift, right_shift, right_shift);
|
code.vpxord(right_shift, right_shift, right_shift);
|
||||||
|
@ -2737,7 +2737,7 @@ void EmitX64::EmitVectorPairedAddSignedWiden32(EmitContext& ctx, IR::Inst* inst)
|
||||||
const Xbyak::Xmm a = ctx.reg_alloc.UseScratchXmm(args[0]);
|
const Xbyak::Xmm a = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
|
||||||
if (code.HasHostFeature(HostFeature::AVX512_Ortho)) {
|
if (code.HasHostFeature(HostFeature::AVX512_Ortho)) {
|
||||||
const Xbyak::Xmm c = xmm16;
|
const Xbyak::Xmm c = ctx.reg_alloc.ScratchXmm();
|
||||||
code.vpsraq(c, a, 32);
|
code.vpsraq(c, a, 32);
|
||||||
code.vpsllq(a, a, 32);
|
code.vpsllq(a, a, 32);
|
||||||
code.vpsraq(a, a, 32);
|
code.vpsraq(a, a, 32);
|
||||||
|
@ -5461,7 +5461,7 @@ void EmitX64::EmitVectorTableLookup128(EmitContext& ctx, IR::Inst* inst) {
|
||||||
if (code.HasHostFeature(HostFeature::AVX512_Ortho | HostFeature::AVX512BW)) {
|
if (code.HasHostFeature(HostFeature::AVX512_Ortho | HostFeature::AVX512BW)) {
|
||||||
const Xbyak::Xmm indicies = ctx.reg_alloc.UseXmm(args[2]);
|
const Xbyak::Xmm indicies = ctx.reg_alloc.UseXmm(args[2]);
|
||||||
const Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]);
|
const Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
const Xbyak::Xmm masked = xmm16;
|
const Xbyak::Xmm masked = ctx.reg_alloc.ScratchXmm();
|
||||||
|
|
||||||
code.vpandd(masked, indicies, code.Const(xword_b, 0xF0F0F0F0F0F0F0F0, 0xF0F0F0F0F0F0F0F0));
|
code.vpandd(masked, indicies, code.Const(xword_b, 0xF0F0F0F0F0F0F0F0, 0xF0F0F0F0F0F0F0F0));
|
||||||
|
|
||||||
|
|
|
@ -118,7 +118,7 @@ void HostLocInfo::AddValue(IR::Inst* inst) noexcept {
|
||||||
values.push_back(inst);
|
values.push_back(inst);
|
||||||
ASSERT(size_t(total_uses) + inst->UseCount() < std::numeric_limits<uint16_t>::max());
|
ASSERT(size_t(total_uses) + inst->UseCount() < std::numeric_limits<uint16_t>::max());
|
||||||
total_uses += inst->UseCount();
|
total_uses += inst->UseCount();
|
||||||
max_bit_width = std::max<uint8_t>(max_bit_width, GetBitWidth(inst->GetType()));
|
max_bit_width = std::max<uint8_t>(max_bit_width, std::countr_zero(GetBitWidth(inst->GetType())));
|
||||||
}
|
}
|
||||||
|
|
||||||
void HostLocInfo::EmitVerboseDebuggingOutput(BlockOfCode* code, size_t host_loc_index) const noexcept {
|
void HostLocInfo::EmitVerboseDebuggingOutput(BlockOfCode* code, size_t host_loc_index) const noexcept {
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
#include "boost/container/small_vector.hpp"
|
||||||
#include "dynarmic/common/common_types.h"
|
#include "dynarmic/common/common_types.h"
|
||||||
#include <xbyak/xbyak.h>
|
#include <xbyak/xbyak.h>
|
||||||
#include <boost/container/static_vector.hpp>
|
#include <boost/container/static_vector.hpp>
|
||||||
|
@ -77,13 +78,13 @@ public:
|
||||||
return std::find(values.begin(), values.end(), inst) != values.end();
|
return std::find(values.begin(), values.end(), inst) != values.end();
|
||||||
}
|
}
|
||||||
inline size_t GetMaxBitWidth() const noexcept {
|
inline size_t GetMaxBitWidth() const noexcept {
|
||||||
return max_bit_width;
|
return 1 << max_bit_width;
|
||||||
}
|
}
|
||||||
void AddValue(IR::Inst* inst) noexcept;
|
void AddValue(IR::Inst* inst) noexcept;
|
||||||
void EmitVerboseDebuggingOutput(BlockOfCode* code, size_t host_loc_index) const noexcept;
|
void EmitVerboseDebuggingOutput(BlockOfCode* code, size_t host_loc_index) const noexcept;
|
||||||
private:
|
private:
|
||||||
//non trivial
|
//non trivial
|
||||||
std::vector<IR::Inst*> values; //24
|
boost::container::small_vector<IR::Inst*, 3> values; //24
|
||||||
// Block state
|
// Block state
|
||||||
uint16_t total_uses = 0; //8
|
uint16_t total_uses = 0; //8
|
||||||
//sometimes zeroed
|
//sometimes zeroed
|
||||||
|
@ -93,10 +94,10 @@ private:
|
||||||
uint16_t is_being_used_count = 0; //8
|
uint16_t is_being_used_count = 0; //8
|
||||||
uint16_t current_references = 0; //8
|
uint16_t current_references = 0; //8
|
||||||
// Value state
|
// Value state
|
||||||
uint8_t max_bit_width = 0; //Valid values: 1,2,4,8,16,32,128
|
uint8_t max_bit_width : 4 = 0; //Valid values: log2(1,2,4,8,16,32,128) = (0, 1, 2, 3, 4, 5, 6)
|
||||||
|
uint8_t lru_counter : 2 = 0; //1
|
||||||
bool is_scratch : 1 = false; //1
|
bool is_scratch : 1 = false; //1
|
||||||
bool is_set_last_use : 1 = false; //1
|
bool is_set_last_use : 1 = false; //1
|
||||||
alignas(16) uint8_t lru_counter = 0; //1
|
|
||||||
friend class RegAlloc;
|
friend class RegAlloc;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(HostLocInfo) == 64);
|
static_assert(sizeof(HostLocInfo) == 64);
|
||||||
|
|
120
src/dynarmic/src/dynarmic/common/context.h
Normal file
120
src/dynarmic/src/dynarmic/common/context.h
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
# include <signal.h>
|
||||||
|
# include <sys/ucontext.h>
|
||||||
|
#else
|
||||||
|
# include <signal.h>
|
||||||
|
# ifndef __OpenBSD__
|
||||||
|
# include <ucontext.h>
|
||||||
|
# endif
|
||||||
|
# ifdef __sun__
|
||||||
|
# include <sys/regset.h>
|
||||||
|
# endif
|
||||||
|
# ifdef __linux__
|
||||||
|
# include <sys/syscall.h>
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ARCHITECTURE_x86_64
|
||||||
|
# ifdef __OpenBSD__
|
||||||
|
# define CTX_DECLARE(raw_context) ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(raw_context);
|
||||||
|
# else
|
||||||
|
# define CTX_DECLARE(raw_context) \
|
||||||
|
ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(raw_context); \
|
||||||
|
[[maybe_unused]] auto& mctx = ucontext->uc_mcontext;
|
||||||
|
# endif
|
||||||
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
|
# ifdef __OpenBSD__
|
||||||
|
# define CTX_DECLARE(raw_context) ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(raw_context);
|
||||||
|
# else
|
||||||
|
# define CTX_DECLARE(raw_context) \
|
||||||
|
ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(raw_context); \
|
||||||
|
[[maybe_unused]] auto& mctx = ucontext->uc_mcontext; \
|
||||||
|
[[maybe_unused]] const auto fpctx = GetFloatingPointState(mctx);
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
|
# if defined(__APPLE__)
|
||||||
|
# define CTX_RIP (mctx->__ss.__rip)
|
||||||
|
# define CTX_RSP (mctx->__ss.__rsp)
|
||||||
|
# elif defined(__linux__)
|
||||||
|
# define CTX_RIP (mctx.gregs[REG_RIP])
|
||||||
|
# define CTX_RSP (mctx.gregs[REG_RSP])
|
||||||
|
# elif defined(__FreeBSD__)
|
||||||
|
# define CTX_RIP (mctx.mc_rip)
|
||||||
|
# define CTX_RSP (mctx.mc_rsp)
|
||||||
|
# elif defined(__NetBSD__)
|
||||||
|
# define CTX_RIP (mctx.__gregs[_REG_RIP])
|
||||||
|
# define CTX_RSP (mctx.__gregs[_REG_RSP])
|
||||||
|
# elif defined(__OpenBSD__)
|
||||||
|
# define CTX_RIP (ucontext->sc_rip)
|
||||||
|
# define CTX_RSP (ucontext->sc_rsp)
|
||||||
|
# elif defined(__sun__)
|
||||||
|
# define CTX_RIP (mctx.gregs[REG_RIP])
|
||||||
|
# define CTX_RSP (mctx.gregs[REG_RSP])
|
||||||
|
# else
|
||||||
|
# error "Unknown platform"
|
||||||
|
# endif
|
||||||
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
|
# if defined(__APPLE__)
|
||||||
|
# define CTX_PC (mctx->__ss.__pc)
|
||||||
|
# define CTX_SP (mctx->__ss.__sp)
|
||||||
|
# define CTX_LR (mctx->__ss.__lr)
|
||||||
|
# define CTX_PSTATE (mctx->__ss.__cpsr)
|
||||||
|
# define CTX_X(i) (mctx->__ss.__x[i])
|
||||||
|
# define CTX_Q(i) (mctx->__ns.__v[i])
|
||||||
|
# define CTX_FPSR (mctx->__ns.__fpsr)
|
||||||
|
# define CTX_FPCR (mctx->__ns.__fpcr)
|
||||||
|
# elif defined(__linux__)
|
||||||
|
# define CTX_PC (mctx.pc)
|
||||||
|
# define CTX_SP (mctx.sp)
|
||||||
|
# define CTX_LR (mctx.regs[30])
|
||||||
|
# define CTX_PSTATE (mctx.pstate)
|
||||||
|
# define CTX_X(i) (mctx.regs[i])
|
||||||
|
# define CTX_Q(i) (fpctx->vregs[i])
|
||||||
|
# define CTX_FPSR (fpctx->fpsr)
|
||||||
|
# define CTX_FPCR (fpctx->fpcr)
|
||||||
|
# elif defined(__FreeBSD__)
|
||||||
|
# define CTX_PC (mctx.mc_gpregs.gp_elr)
|
||||||
|
# define CTX_SP (mctx.mc_gpregs.gp_sp)
|
||||||
|
# define CTX_LR (mctx.mc_gpregs.gp_lr)
|
||||||
|
# define CTX_X(i) (mctx.mc_gpregs.gp_x[i])
|
||||||
|
# define CTX_Q(i) (mctx.mc_fpregs.fp_q[i])
|
||||||
|
# elif defined(__NetBSD__)
|
||||||
|
# define CTX_PC (mctx.mc_gpregs.gp_elr)
|
||||||
|
# define CTX_SP (mctx.mc_gpregs.gp_sp)
|
||||||
|
# define CTX_LR (mctx.mc_gpregs.gp_lr)
|
||||||
|
# define CTX_X(i) (mctx.mc_gpregs.gp_x[i])
|
||||||
|
# define CTX_Q(i) (mctx.mc_fpregs.fp_q[i])
|
||||||
|
# elif defined(__OpenBSD__)
|
||||||
|
# define CTX_PC (ucontext->sc_elr)
|
||||||
|
# define CTX_SP (ucontext->sc_sp)
|
||||||
|
# define CTX_LR (ucontext->sc_lr)
|
||||||
|
# define CTX_X(i) (ucontext->sc_x[i])
|
||||||
|
# define CTX_Q(i) (ucontext->sc_q[i])
|
||||||
|
# else
|
||||||
|
# error "Unknown platform"
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
# error "unimplemented"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ARCHITECTURE_arm64
|
||||||
|
#ifdef __APPLE__
|
||||||
|
inline _STRUCT_ARM_NEON_STATE64* GetFloatingPointState(mcontext_t& host_ctx) {
|
||||||
|
return &(host_ctx->__ns);
|
||||||
|
}
|
||||||
|
#elif defined(__linux__)
|
||||||
|
inline fpsimd_context* GetFloatingPointState(mcontext_t& host_ctx) {
|
||||||
|
_aarch64_ctx* header = reinterpret_cast<_aarch64_ctx*>(&host_ctx.__reserved);
|
||||||
|
while (header->magic != FPSIMD_MAGIC)
|
||||||
|
header = reinterpret_cast<_aarch64_ctx*>(reinterpret_cast<char*>(header) + header->size);
|
||||||
|
return reinterpret_cast<fpsimd_context*>(header);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
|
@ -1,13 +0,0 @@
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2016 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dynarmic/common/memory_pool.h"
|
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
namespace Dynarmic::Common {
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Common
|
|
|
@ -1,61 +0,0 @@
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2016 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace Dynarmic::Common {
|
|
||||||
|
|
||||||
/// @tparam object_size Byte-size of objects to construct
|
|
||||||
/// @tparam slab_size Number of objects to have per slab
|
|
||||||
template<size_t object_size, size_t slab_size>
|
|
||||||
class Pool {
|
|
||||||
public:
|
|
||||||
inline Pool() noexcept {
|
|
||||||
AllocateNewSlab();
|
|
||||||
}
|
|
||||||
inline ~Pool() noexcept {
|
|
||||||
std::free(current_slab);
|
|
||||||
for (char* slab : slabs) {
|
|
||||||
std::free(slab);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Pool(const Pool&) = delete;
|
|
||||||
Pool(Pool&&) = delete;
|
|
||||||
|
|
||||||
Pool& operator=(const Pool&) = delete;
|
|
||||||
Pool& operator=(Pool&&) = delete;
|
|
||||||
|
|
||||||
/// @brief Returns a pointer to an `object_size`-bytes block of memory.
|
|
||||||
[[nodiscard]] void* Alloc() noexcept {
|
|
||||||
if (remaining == 0) {
|
|
||||||
slabs.push_back(current_slab);
|
|
||||||
AllocateNewSlab();
|
|
||||||
}
|
|
||||||
void* ret = static_cast<void*>(current_ptr);
|
|
||||||
current_ptr += object_size;
|
|
||||||
remaining--;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
/// @brief Allocates a completely new memory slab.
|
|
||||||
/// Used when an entirely new slab is needed
|
|
||||||
/// due the current one running out of usable space.
|
|
||||||
void AllocateNewSlab() noexcept {
|
|
||||||
current_slab = static_cast<char*>(std::malloc(object_size * slab_size));
|
|
||||||
current_ptr = current_slab;
|
|
||||||
remaining = slab_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<char*> slabs;
|
|
||||||
char* current_slab = nullptr;
|
|
||||||
char* current_ptr = nullptr;
|
|
||||||
size_t remaining = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Common
|
|
|
@ -9,12 +9,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include "dynarmic/common/assert.h"
|
#include "dynarmic/common/assert.h"
|
||||||
#include "dynarmic/common/common_types.h"
|
#include "dynarmic/common/common_types.h"
|
||||||
|
|
||||||
#include "dynarmic/interface/A32/coprocessor_util.h"
|
#include "dynarmic/interface/A32/coprocessor_util.h"
|
||||||
#include "dynarmic/ir/cond.h"
|
#include "dynarmic/ir/cond.h"
|
||||||
|
|
||||||
|
@ -89,23 +86,17 @@ constexpr bool IsQuadExtReg(ExtReg reg) {
|
||||||
|
|
||||||
inline size_t RegNumber(Reg reg) {
|
inline size_t RegNumber(Reg reg) {
|
||||||
ASSERT(reg != Reg::INVALID_REG);
|
ASSERT(reg != Reg::INVALID_REG);
|
||||||
return static_cast<size_t>(reg);
|
return size_t(reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t RegNumber(ExtReg reg) {
|
inline size_t RegNumber(ExtReg reg) {
|
||||||
if (IsSingleExtReg(reg)) {
|
if (IsSingleExtReg(reg)) {
|
||||||
return static_cast<size_t>(reg) - static_cast<size_t>(ExtReg::S0);
|
return size_t(reg) - size_t(ExtReg::S0);
|
||||||
|
} else if (IsDoubleExtReg(reg)) {
|
||||||
|
return size_t(reg) - size_t(ExtReg::D0);
|
||||||
}
|
}
|
||||||
|
ASSERT(IsQuadExtReg(reg));
|
||||||
if (IsDoubleExtReg(reg)) {
|
return size_t(reg) - size_t(ExtReg::Q0);
|
||||||
return static_cast<size_t>(reg) - static_cast<size_t>(ExtReg::D0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsQuadExtReg(reg)) {
|
|
||||||
return static_cast<size_t>(reg) - static_cast<size_t>(ExtReg::Q0);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT_MSG(false, "Invalid extended register");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Reg operator+(Reg reg, size_t number) {
|
inline Reg operator+(Reg reg, size_t number) {
|
||||||
|
|
|
@ -30,13 +30,13 @@ template<typename Visitor>
|
||||||
using ArmDecodeTable = std::array<std::vector<ArmMatcher<Visitor>>, 0x1000>;
|
using ArmDecodeTable = std::array<std::vector<ArmMatcher<Visitor>>, 0x1000>;
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
inline size_t ToFastLookupIndexArm(u32 instruction) {
|
inline size_t ToFastLookupIndexArm(u32 instruction) noexcept {
|
||||||
return ((instruction >> 4) & 0x00F) | ((instruction >> 16) & 0xFF0);
|
return ((instruction >> 4) & 0x00F) | ((instruction >> 16) & 0xFF0);
|
||||||
}
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
template<typename V>
|
template<typename V>
|
||||||
constexpr ArmDecodeTable<V> GetArmDecodeTable() {
|
constexpr ArmDecodeTable<V> GetArmDecodeTable() noexcept {
|
||||||
std::vector<ArmMatcher<V>> list = {
|
std::vector<ArmMatcher<V>> list = {
|
||||||
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(ArmMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
|
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(ArmMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
|
||||||
#include "./arm.inc"
|
#include "./arm.inc"
|
||||||
|
@ -62,15 +62,27 @@ constexpr ArmDecodeTable<V> GetArmDecodeTable() {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename V>
|
template<typename V>
|
||||||
std::optional<std::reference_wrapper<const ArmMatcher<V>>> DecodeArm(u32 instruction) {
|
std::optional<std::reference_wrapper<const ArmMatcher<V>>> DecodeArm(u32 instruction) noexcept {
|
||||||
alignas(64) static const auto table = GetArmDecodeTable<V>();
|
alignas(64) static const auto table = GetArmDecodeTable<V>();
|
||||||
const auto matches_instruction = [instruction](const auto& matcher) {
|
const auto matches_instruction = [instruction](const auto& matcher) {
|
||||||
return matcher.Matches(instruction);
|
return matcher.Matches(instruction);
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto& subtable = table[detail::ToFastLookupIndexArm(instruction)];
|
const auto& subtable = table[detail::ToFastLookupIndexArm(instruction)];
|
||||||
auto iter = std::find_if(subtable.begin(), subtable.end(), matches_instruction);
|
auto iter = std::find_if(subtable.begin(), subtable.end(), matches_instruction);
|
||||||
return iter != subtable.end() ? std::optional<std::reference_wrapper<const ArmMatcher<V>>>(*iter) : std::nullopt;
|
return iter != subtable.end() ? std::optional<std::reference_wrapper<const ArmMatcher<V>>>(*iter) : std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename V>
|
||||||
|
std::optional<std::string_view> GetNameARM(u32 inst) noexcept {
|
||||||
|
std::vector<std::pair<std::string_view, ArmMatcher<V>>> list = {
|
||||||
|
#define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(ArmMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)) },
|
||||||
|
#include "./arm.inc"
|
||||||
|
#undef INST
|
||||||
|
};
|
||||||
|
auto const iter = std::find_if(list.cbegin(), list.cend(), [inst](auto const& m) {
|
||||||
|
return m.second.Matches(inst);
|
||||||
|
});
|
||||||
|
return iter != list.cend() ? std::optional{iter->first} : std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Dynarmic::A32
|
} // namespace Dynarmic::A32
|
||||||
|
|
|
@ -26,15 +26,12 @@ template<typename Visitor>
|
||||||
using ASIMDMatcher = Decoder::Matcher<Visitor, u32>;
|
using ASIMDMatcher = Decoder::Matcher<Visitor, u32>;
|
||||||
|
|
||||||
template<typename V>
|
template<typename V>
|
||||||
std::vector<ASIMDMatcher<V>> GetASIMDDecodeTable() {
|
std::vector<ASIMDMatcher<V>> GetASIMDDecodeTable() noexcept {
|
||||||
std::vector<ASIMDMatcher<V>> table = {
|
std::vector<std::pair<const char*, ASIMDMatcher<V>>> table = {
|
||||||
|
#define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(ASIMDMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)) },
|
||||||
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(ASIMDMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
|
|
||||||
#include "./asimd.inc"
|
#include "./asimd.inc"
|
||||||
#undef INST
|
#undef INST
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Exceptions to the rule of thumb.
|
// Exceptions to the rule of thumb.
|
||||||
const std::set<std::string> comes_first{
|
const std::set<std::string> comes_first{
|
||||||
"VBIC, VMOV, VMVN, VORR (immediate)",
|
"VBIC, VMOV, VMVN, VORR (immediate)",
|
||||||
|
@ -53,29 +50,43 @@ std::vector<ASIMDMatcher<V>> GetASIMDDecodeTable() {
|
||||||
"VQDMULH (scalar)",
|
"VQDMULH (scalar)",
|
||||||
"VQRDMULH (scalar)",
|
"VQRDMULH (scalar)",
|
||||||
};
|
};
|
||||||
const auto sort_begin = std::stable_partition(table.begin(), table.end(), [&](const auto& matcher) {
|
const auto sort_begin = std::stable_partition(table.begin(), table.end(), [&](const auto& e) {
|
||||||
return comes_first.count(matcher.GetName()) > 0;
|
return comes_first.count(e.first) > 0;
|
||||||
});
|
});
|
||||||
const auto sort_end = std::stable_partition(table.begin(), table.end(), [&](const auto& matcher) {
|
const auto sort_end = std::stable_partition(table.begin(), table.end(), [&](const auto& e) {
|
||||||
return comes_last.count(matcher.GetName()) == 0;
|
return comes_last.count(e.first) == 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
// If a matcher has more bits in its mask it is more specific, so it should come first.
|
// If a matcher has more bits in its mask it is more specific, so it should come first.
|
||||||
std::stable_sort(sort_begin, sort_end, [](const auto& matcher1, const auto& matcher2) {
|
std::stable_sort(sort_begin, sort_end, [](const auto& a, const auto& b) {
|
||||||
return mcl::bit::count_ones(matcher1.GetMask()) > mcl::bit::count_ones(matcher2.GetMask());
|
return mcl::bit::count_ones(a.second.GetMask()) > mcl::bit::count_ones(b.second.GetMask());
|
||||||
});
|
});
|
||||||
|
std::vector<ASIMDMatcher<V>> final_table;
|
||||||
return table;
|
std::transform(table.cbegin(), table.cend(), std::back_inserter(final_table), [](auto const& e) {
|
||||||
|
return e.second;
|
||||||
|
});
|
||||||
|
return final_table;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename V>
|
template<typename V>
|
||||||
std::optional<std::reference_wrapper<const ASIMDMatcher<V>>> DecodeASIMD(u32 instruction) {
|
std::optional<std::reference_wrapper<const ASIMDMatcher<V>>> DecodeASIMD(u32 instruction) noexcept {
|
||||||
static const auto table = GetASIMDDecodeTable<V>();
|
alignas(64) static const auto table = GetASIMDDecodeTable<V>();
|
||||||
|
auto iter = std::find_if(table.begin(), table.end(), [instruction](const auto& matcher) {
|
||||||
const auto matches_instruction = [instruction](const auto& matcher) { return matcher.Matches(instruction); };
|
return matcher.Matches(instruction);
|
||||||
|
});
|
||||||
auto iter = std::find_if(table.begin(), table.end(), matches_instruction);
|
|
||||||
return iter != table.end() ? std::optional<std::reference_wrapper<const ASIMDMatcher<V>>>(*iter) : std::nullopt;
|
return iter != table.end() ? std::optional<std::reference_wrapper<const ASIMDMatcher<V>>>(*iter) : std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename V>
|
||||||
|
std::optional<std::string_view> GetNameASIMD(u32 inst) noexcept {
|
||||||
|
std::vector<std::pair<std::string_view, ASIMDMatcher<V>>> list = {
|
||||||
|
#define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(ASIMDMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)) },
|
||||||
|
#include "./asimd.inc"
|
||||||
|
#undef INST
|
||||||
|
};
|
||||||
|
auto const iter = std::find_if(list.cbegin(), list.cend(), [inst](auto const& m) {
|
||||||
|
return m.second.Matches(inst);
|
||||||
|
});
|
||||||
|
return iter != list.cend() ? std::optional{iter->first} : std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Dynarmic::A32
|
} // namespace Dynarmic::A32
|
||||||
|
|
|
@ -25,18 +25,28 @@ using Thumb16Matcher = Decoder::Matcher<Visitor, u16>;
|
||||||
|
|
||||||
template<typename V>
|
template<typename V>
|
||||||
std::optional<std::reference_wrapper<const Thumb16Matcher<V>>> DecodeThumb16(u16 instruction) {
|
std::optional<std::reference_wrapper<const Thumb16Matcher<V>>> DecodeThumb16(u16 instruction) {
|
||||||
static const std::vector<Thumb16Matcher<V>> table = {
|
alignas(64) static const std::vector<Thumb16Matcher<V>> table = {
|
||||||
|
|
||||||
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Thumb16Matcher, fn, name, Decoder::detail::StringToArray<16>(bitstring)),
|
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Thumb16Matcher, fn, name, Decoder::detail::StringToArray<16>(bitstring)),
|
||||||
#include "./thumb16.inc"
|
#include "./thumb16.inc"
|
||||||
#undef INST
|
#undef INST
|
||||||
|
|
||||||
};
|
};
|
||||||
|
auto iter = std::find_if(table.begin(), table.end(), [instruction](const auto& matcher) {
|
||||||
const auto matches_instruction = [instruction](const auto& matcher) { return matcher.Matches(instruction); };
|
return matcher.Matches(instruction);
|
||||||
|
});
|
||||||
auto iter = std::find_if(table.begin(), table.end(), matches_instruction);
|
|
||||||
return iter != table.end() ? std::optional<std::reference_wrapper<const Thumb16Matcher<V>>>(*iter) : std::nullopt;
|
return iter != table.end() ? std::optional<std::reference_wrapper<const Thumb16Matcher<V>>>(*iter) : std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename V>
|
||||||
|
std::optional<std::string_view> GetNameThumb16(u32 inst) noexcept {
|
||||||
|
std::vector<std::pair<std::string_view, Thumb16Matcher<V>>> list = {
|
||||||
|
#define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(Thumb16Matcher, fn, name, Decoder::detail::StringToArray<16>(bitstring)) },
|
||||||
|
#include "./thumb16.inc"
|
||||||
|
#undef INST
|
||||||
|
};
|
||||||
|
auto const iter = std::find_if(list.cbegin(), list.cend(), [inst](auto const& m) {
|
||||||
|
return m.second.Matches(inst);
|
||||||
|
});
|
||||||
|
return iter != list.cend() ? std::optional{iter->first} : std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Dynarmic::A32
|
} // namespace Dynarmic::A32
|
||||||
|
|
|
@ -24,18 +24,28 @@ using Thumb32Matcher = Decoder::Matcher<Visitor, u32>;
|
||||||
|
|
||||||
template<typename V>
|
template<typename V>
|
||||||
std::optional<std::reference_wrapper<const Thumb32Matcher<V>>> DecodeThumb32(u32 instruction) {
|
std::optional<std::reference_wrapper<const Thumb32Matcher<V>>> DecodeThumb32(u32 instruction) {
|
||||||
static const std::vector<Thumb32Matcher<V>> table = {
|
alignas(64) static const std::vector<Thumb32Matcher<V>> table = {
|
||||||
|
|
||||||
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Thumb32Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
|
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Thumb32Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
|
||||||
#include "./thumb32.inc"
|
#include "./thumb32.inc"
|
||||||
#undef INST
|
#undef INST
|
||||||
|
|
||||||
};
|
};
|
||||||
|
auto iter = std::find_if(table.begin(), table.end(), [instruction](const auto& matcher) {
|
||||||
const auto matches_instruction = [instruction](const auto& matcher) { return matcher.Matches(instruction); };
|
return matcher.Matches(instruction);
|
||||||
|
});
|
||||||
auto iter = std::find_if(table.begin(), table.end(), matches_instruction);
|
|
||||||
return iter != table.end() ? std::optional<std::reference_wrapper<const Thumb32Matcher<V>>>(*iter) : std::nullopt;
|
return iter != table.end() ? std::optional<std::reference_wrapper<const Thumb32Matcher<V>>>(*iter) : std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename V>
|
||||||
|
std::optional<std::string_view> GetNameThumb32(u32 inst) noexcept {
|
||||||
|
std::vector<std::pair<std::string_view, Thumb32Matcher<V>>> list = {
|
||||||
|
#define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(Thumb32Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)) },
|
||||||
|
#include "./thumb32.inc"
|
||||||
|
#undef INST
|
||||||
|
};
|
||||||
|
auto const iter = std::find_if(list.cbegin(), list.cend(), [inst](auto const& m) {
|
||||||
|
return m.second.Matches(inst);
|
||||||
|
});
|
||||||
|
return iter != list.cend() ? std::optional{iter->first} : std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Dynarmic::A32
|
} // namespace Dynarmic::A32
|
||||||
|
|
|
@ -26,36 +26,42 @@ using VFPMatcher = Decoder::Matcher<Visitor, u32>;
|
||||||
template<typename V>
|
template<typename V>
|
||||||
std::optional<std::reference_wrapper<const VFPMatcher<V>>> DecodeVFP(u32 instruction) {
|
std::optional<std::reference_wrapper<const VFPMatcher<V>>> DecodeVFP(u32 instruction) {
|
||||||
using Table = std::vector<VFPMatcher<V>>;
|
using Table = std::vector<VFPMatcher<V>>;
|
||||||
|
alignas(64) static const struct Tables {
|
||||||
static const struct Tables {
|
|
||||||
Table unconditional;
|
Table unconditional;
|
||||||
Table conditional;
|
Table conditional;
|
||||||
} tables = [] {
|
} tables = []() {
|
||||||
Table list = {
|
Table list = {
|
||||||
|
|
||||||
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(VFPMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
|
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(VFPMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
|
||||||
#include "./vfp.inc"
|
#include "./vfp.inc"
|
||||||
#undef INST
|
#undef INST
|
||||||
|
|
||||||
};
|
};
|
||||||
|
auto const it = std::stable_partition(list.begin(), list.end(), [&](const auto& matcher) {
|
||||||
const auto division = std::stable_partition(list.begin(), list.end(), [&](const auto& matcher) {
|
|
||||||
return (matcher.GetMask() & 0xF0000000) == 0xF0000000;
|
return (matcher.GetMask() & 0xF0000000) == 0xF0000000;
|
||||||
});
|
});
|
||||||
|
|
||||||
return Tables{
|
return Tables{
|
||||||
Table{list.begin(), division},
|
Table{list.begin(), it},
|
||||||
Table{division, list.end()},
|
Table{it, list.end()},
|
||||||
};
|
};
|
||||||
}();
|
}();
|
||||||
|
|
||||||
const bool is_unconditional = (instruction & 0xF0000000) == 0xF0000000;
|
const bool is_unconditional = (instruction & 0xF0000000) == 0xF0000000;
|
||||||
const Table& table = is_unconditional ? tables.unconditional : tables.conditional;
|
const Table& table = is_unconditional ? tables.unconditional : tables.conditional;
|
||||||
|
auto iter = std::find_if(table.begin(), table.end(), [instruction](const auto& matcher) {
|
||||||
const auto matches_instruction = [instruction](const auto& matcher) { return matcher.Matches(instruction); };
|
return matcher.Matches(instruction);
|
||||||
|
});
|
||||||
auto iter = std::find_if(table.begin(), table.end(), matches_instruction);
|
|
||||||
return iter != table.end() ? std::optional<std::reference_wrapper<const VFPMatcher<V>>>(*iter) : std::nullopt;
|
return iter != table.end() ? std::optional<std::reference_wrapper<const VFPMatcher<V>>>(*iter) : std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename V>
|
||||||
|
std::optional<std::string_view> GetNameVFP(u32 inst) noexcept {
|
||||||
|
std::vector<std::pair<std::string_view, VFPMatcher<V>>> list = {
|
||||||
|
#define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(VFPMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)) },
|
||||||
|
#include "./vfp.inc"
|
||||||
|
#undef INST
|
||||||
|
};
|
||||||
|
auto const iter = std::find_if(list.cbegin(), list.cend(), [inst](auto const& m) {
|
||||||
|
return m.second.Matches(inst);
|
||||||
|
});
|
||||||
|
return iter != list.cend() ? std::optional{iter->first} : std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Dynarmic::A32
|
} // namespace Dynarmic::A32
|
||||||
|
|
|
@ -97,7 +97,7 @@ u32 ConvertASIMDInstruction(u32 thumb_instruction) {
|
||||||
return 0xF7F0A000; // UDF
|
return 0xF7F0A000; // UDF
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MaybeVFPOrASIMDInstruction(u32 thumb_instruction) {
|
inline bool MaybeVFPOrASIMDInstruction(u32 thumb_instruction) noexcept {
|
||||||
return (thumb_instruction & 0xEC000000) == 0xEC000000 || (thumb_instruction & 0xFF100000) == 0xF9000000;
|
return (thumb_instruction & 0xEC000000) == 0xEC000000 || (thumb_instruction & 0xFF100000) == 0xF9000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,34 +37,31 @@ inline size_t ToFastLookupIndex(u32 instruction) {
|
||||||
|
|
||||||
template<typename V>
|
template<typename V>
|
||||||
constexpr DecodeTable<V> GetDecodeTable() {
|
constexpr DecodeTable<V> GetDecodeTable() {
|
||||||
std::vector<Matcher<V>> list = {
|
std::vector<std::pair<const char*, Matcher<V>>> list = {
|
||||||
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
|
#define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)) },
|
||||||
#include "./a64.inc"
|
#include "./a64.inc"
|
||||||
#undef INST
|
#undef INST
|
||||||
};
|
};
|
||||||
|
|
||||||
// If a matcher has more bits in its mask it is more specific, so it should come first.
|
// If a matcher has more bits in its mask it is more specific, so it should come first.
|
||||||
std::stable_sort(list.begin(), list.end(), [](const auto& matcher1, const auto& matcher2) {
|
std::stable_sort(list.begin(), list.end(), [](const auto& a, const auto& b) {
|
||||||
// If a matcher has more bits in its mask it is more specific, so it should come first.
|
// If a matcher has more bits in its mask it is more specific, so it should come first.
|
||||||
return mcl::bit::count_ones(matcher1.GetMask()) > mcl::bit::count_ones(matcher2.GetMask());
|
return mcl::bit::count_ones(a.second.GetMask()) > mcl::bit::count_ones(b.second.GetMask());
|
||||||
});
|
});
|
||||||
|
|
||||||
// Exceptions to the above rule of thumb.
|
// Exceptions to the above rule of thumb.
|
||||||
std::stable_partition(list.begin(), list.end(), [&](const auto& matcher) {
|
std::stable_partition(list.begin(), list.end(), [&](const auto& e) {
|
||||||
return std::set<std::string>{
|
return std::set<std::string>{
|
||||||
"MOVI, MVNI, ORR, BIC (vector, immediate)",
|
"MOVI, MVNI, ORR, BIC (vector, immediate)",
|
||||||
"FMOV (vector, immediate)",
|
"FMOV (vector, immediate)",
|
||||||
"Unallocated SIMD modified immediate",
|
"Unallocated SIMD modified immediate",
|
||||||
}.count(matcher.GetName()) > 0;
|
}.count(e.first) > 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
DecodeTable<V> table{};
|
DecodeTable<V> table{};
|
||||||
for (size_t i = 0; i < table.size(); ++i) {
|
for (size_t i = 0; i < table.size(); ++i) {
|
||||||
for (auto matcher : list) {
|
for (auto const& e : list) {
|
||||||
const auto expect = detail::ToFastLookupIndex(matcher.GetExpected());
|
const auto expect = detail::ToFastLookupIndex(e.second.GetExpected());
|
||||||
const auto mask = detail::ToFastLookupIndex(matcher.GetMask());
|
const auto mask = detail::ToFastLookupIndex(e.second.GetMask());
|
||||||
if ((i & mask) == expect) {
|
if ((i & mask) == expect) {
|
||||||
table[i].push_back(matcher);
|
table[i].push_back(e.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,12 +71,24 @@ constexpr DecodeTable<V> GetDecodeTable() {
|
||||||
template<typename V>
|
template<typename V>
|
||||||
std::optional<std::reference_wrapper<const Matcher<V>>> Decode(u32 instruction) {
|
std::optional<std::reference_wrapper<const Matcher<V>>> Decode(u32 instruction) {
|
||||||
alignas(64) static const auto table = GetDecodeTable<V>();
|
alignas(64) static const auto table = GetDecodeTable<V>();
|
||||||
const auto matches_instruction = [instruction](const auto& matcher) {
|
|
||||||
return matcher.Matches(instruction);
|
|
||||||
};
|
|
||||||
const auto& subtable = table[detail::ToFastLookupIndex(instruction)];
|
const auto& subtable = table[detail::ToFastLookupIndex(instruction)];
|
||||||
auto iter = std::find_if(subtable.begin(), subtable.end(), matches_instruction);
|
auto iter = std::find_if(subtable.begin(), subtable.end(), [instruction](const auto& matcher) {
|
||||||
|
return matcher.Matches(instruction);
|
||||||
|
});
|
||||||
return iter != subtable.end() ? std::optional<std::reference_wrapper<const Matcher<V>>>(*iter) : std::nullopt;
|
return iter != subtable.end() ? std::optional<std::reference_wrapper<const Matcher<V>>>(*iter) : std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename V>
|
||||||
|
std::optional<std::string_view> GetName(u32 inst) noexcept {
|
||||||
|
std::vector<std::pair<std::string_view, Matcher<V>>> list = {
|
||||||
|
#define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)) },
|
||||||
|
#include "./a64.inc"
|
||||||
|
#undef INST
|
||||||
|
};
|
||||||
|
auto const iter = std::find_if(list.cbegin(), list.cend(), [inst](auto const& m) {
|
||||||
|
return m.second.Matches(inst);
|
||||||
|
});
|
||||||
|
return iter != list.cend() ? std::optional{iter->first} : std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Dynarmic::A64
|
} // namespace Dynarmic::A64
|
||||||
|
|
|
@ -70,11 +70,9 @@ struct detail {
|
||||||
return std::make_tuple(mask, expect);
|
return std::make_tuple(mask, expect);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// @brief Generates the masks and shifts for each argument.
|
||||||
* Generates the masks and shifts for each argument.
|
/// A '-' in a bitstring indicates that we don't care about that value.
|
||||||
* A '-' in a bitstring indicates that we don't care about that value.
|
/// An argument is specified by a continuous string of the same character.
|
||||||
* An argument is specified by a continuous string of the same character.
|
|
||||||
*/
|
|
||||||
template<size_t N>
|
template<size_t N>
|
||||||
static consteval auto GetArgInfo(std::array<char, opcode_bitsize> bitstring) {
|
static consteval auto GetArgInfo(std::array<char, opcode_bitsize> bitstring) {
|
||||||
std::array<opcode_type, N> masks = {};
|
std::array<opcode_type, N> masks = {};
|
||||||
|
@ -98,7 +96,6 @@ struct detail {
|
||||||
|
|
||||||
if constexpr (N > 0) {
|
if constexpr (N > 0) {
|
||||||
const size_t bit_position = opcode_bitsize - i - 1;
|
const size_t bit_position = opcode_bitsize - i - 1;
|
||||||
|
|
||||||
if (arg_index >= N)
|
if (arg_index >= N)
|
||||||
throw std::out_of_range("Unexpected field");
|
throw std::out_of_range("Unexpected field");
|
||||||
|
|
||||||
|
@ -109,20 +106,16 @@ struct detail {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(DYNARMIC_IGNORE_ASSERTS) && !defined(__ANDROID__)
|
#if !defined(DYNARMIC_IGNORE_ASSERTS) && !defined(__ANDROID__)
|
||||||
// Avoids a MSVC ICE, and avoids Android NDK issue.
|
// Avoids a MSVC ICE, and avoids Android NDK issue.
|
||||||
ASSERT(std::all_of(masks.begin(), masks.end(), [](auto m) { return m != 0; }));
|
ASSERT(std::all_of(masks.begin(), masks.end(), [](auto m) { return m != 0; }));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return std::make_tuple(masks, shifts);
|
return std::make_tuple(masks, shifts);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// @brief This struct's Make member function generates a lambda which decodes an instruction
|
||||||
* This struct's Make member function generates a lambda which decodes an instruction based on
|
/// based on the provided arg_masks and arg_shifts. The Visitor member function to call is
|
||||||
* the provided arg_masks and arg_shifts. The Visitor member function to call is provided as a
|
/// provided as a template argument.
|
||||||
* template argument.
|
|
||||||
*/
|
|
||||||
template<typename FnT>
|
template<typename FnT>
|
||||||
struct VisitorCaller;
|
struct VisitorCaller;
|
||||||
|
|
||||||
|
@ -130,36 +123,36 @@ struct detail {
|
||||||
# pragma warning(push)
|
# pragma warning(push)
|
||||||
# pragma warning(disable : 4800) // forcing value to bool 'true' or 'false' (performance warning)
|
# pragma warning(disable : 4800) // forcing value to bool 'true' or 'false' (performance warning)
|
||||||
#endif
|
#endif
|
||||||
template<typename Visitor, typename... Args, typename CallRetT>
|
template<typename V, typename... Args, typename ReturnType>
|
||||||
struct VisitorCaller<CallRetT (Visitor::*)(Args...)> {
|
struct VisitorCaller<ReturnType (V::*)(Args...)> {
|
||||||
template<size_t... iota>
|
template<size_t... iota>
|
||||||
static auto Make(std::integer_sequence<size_t, iota...>,
|
static constexpr auto Make(std::integer_sequence<size_t, iota...>,
|
||||||
CallRetT (Visitor::*const fn)(Args...),
|
ReturnType (V::*const fn)(Args...),
|
||||||
const std::array<opcode_type, sizeof...(iota)> arg_masks,
|
const std::array<opcode_type, sizeof...(iota)> arg_masks,
|
||||||
const std::array<size_t, sizeof...(iota)> arg_shifts) {
|
const std::array<size_t, sizeof...(iota)> arg_shifts) {
|
||||||
static_assert(std::is_same_v<visitor_type, Visitor>, "Member function is not from Matcher's Visitor");
|
static_assert(std::is_same_v<visitor_type, V>, "Member function is not from Matcher's Visitor");
|
||||||
return [fn, arg_masks, arg_shifts](Visitor& v, opcode_type instruction) {
|
return [fn, arg_masks, arg_shifts](V& v, opcode_type instruction) {
|
||||||
(void)instruction;
|
(void)instruction;
|
||||||
(void)arg_masks;
|
(void)arg_masks;
|
||||||
(void)arg_shifts;
|
(void)arg_shifts;
|
||||||
return (v.*fn)(static_cast<Args>((instruction & arg_masks[iota]) >> arg_shifts[iota])...);
|
return (v.*fn)(Args((instruction & arg_masks[iota]) >> arg_shifts[iota])...);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Visitor, typename... Args, typename CallRetT>
|
template<typename V, typename... Args, typename ReturnType>
|
||||||
struct VisitorCaller<CallRetT (Visitor::*)(Args...) const> {
|
struct VisitorCaller<ReturnType (V::*)(Args...) const> {
|
||||||
template<size_t... iota>
|
template<size_t... iota>
|
||||||
static auto Make(std::integer_sequence<size_t, iota...>,
|
static constexpr auto Make(std::integer_sequence<size_t, iota...>,
|
||||||
CallRetT (Visitor::*const fn)(Args...) const,
|
ReturnType (V::*const fn)(Args...) const,
|
||||||
const std::array<opcode_type, sizeof...(iota)> arg_masks,
|
const std::array<opcode_type, sizeof...(iota)> arg_masks,
|
||||||
const std::array<size_t, sizeof...(iota)> arg_shifts) {
|
const std::array<size_t, sizeof...(iota)> arg_shifts) {
|
||||||
static_assert(std::is_same_v<visitor_type, const Visitor>, "Member function is not from Matcher's Visitor");
|
static_assert(std::is_same_v<visitor_type, const V>, "Member function is not from Matcher's Visitor");
|
||||||
return [fn, arg_masks, arg_shifts](const Visitor& v, opcode_type instruction) {
|
return [fn, arg_masks, arg_shifts](const V& v, opcode_type instruction) {
|
||||||
(void)instruction;
|
(void)instruction;
|
||||||
(void)arg_masks;
|
(void)arg_masks;
|
||||||
(void)arg_shifts;
|
(void)arg_shifts;
|
||||||
return (v.*fn)(static_cast<Args>((instruction & arg_masks[iota]) >> arg_shifts[iota])...);
|
return (v.*fn)(Args((instruction & arg_masks[iota]) >> arg_shifts[iota])...);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -167,27 +160,21 @@ struct detail {
|
||||||
# pragma warning(pop)
|
# pragma warning(pop)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/// @brief Creates a matcher that can match and parse instructions based on bitstring.
|
||||||
* Creates a matcher that can match and parse instructions based on bitstring.
|
/// See also: GetMaskAndExpect and GetArgInfo for format of bitstring.
|
||||||
* See also: GetMaskAndExpect and GetArgInfo for format of bitstring.
|
template<auto bitstring, typename F>
|
||||||
*/
|
static constexpr auto GetMatcher(F fn) {
|
||||||
template<auto bitstring, typename FnT>
|
constexpr size_t args_count = mcl::parameter_count_v<F>;
|
||||||
static auto GetMatcher(FnT fn, const char* const name) {
|
|
||||||
constexpr size_t args_count = mcl::parameter_count_v<FnT>;
|
|
||||||
|
|
||||||
constexpr auto mask = std::get<0>(GetMaskAndExpect(bitstring));
|
constexpr auto mask = std::get<0>(GetMaskAndExpect(bitstring));
|
||||||
constexpr auto expect = std::get<1>(GetMaskAndExpect(bitstring));
|
constexpr auto expect = std::get<1>(GetMaskAndExpect(bitstring));
|
||||||
constexpr auto arg_masks = std::get<0>(GetArgInfo<args_count>(bitstring));
|
constexpr auto arg_masks = std::get<0>(GetArgInfo<args_count>(bitstring));
|
||||||
constexpr auto arg_shifts = std::get<1>(GetArgInfo<args_count>(bitstring));
|
constexpr auto arg_shifts = std::get<1>(GetArgInfo<args_count>(bitstring));
|
||||||
|
const auto proxy_fn = VisitorCaller<F>::Make(std::make_index_sequence<args_count>(), fn, arg_masks, arg_shifts);
|
||||||
using Iota = std::make_index_sequence<args_count>;
|
return MatcherT(mask, expect, proxy_fn);
|
||||||
|
|
||||||
const auto proxy_fn = VisitorCaller<FnT>::Make(Iota(), fn, arg_masks, arg_shifts);
|
|
||||||
return MatcherT(name, mask, expect, proxy_fn);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#define DYNARMIC_DECODER_GET_MATCHER(MatcherT, fn, name, bitstring) Decoder::detail::detail<MatcherT<V>>::template GetMatcher<bitstring>(&V::fn, name)
|
#define DYNARMIC_DECODER_GET_MATCHER(MatcherT, fn, name, bitstring) Decoder::detail::detail<MatcherT<V>>::template GetMatcher<bitstring>(&V::fn)
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
} // namespace Dynarmic::Decoder
|
} // namespace Dynarmic::Decoder
|
||||||
|
|
|
@ -14,16 +14,12 @@
|
||||||
|
|
||||||
namespace Dynarmic::Decoder {
|
namespace Dynarmic::Decoder {
|
||||||
|
|
||||||
/**
|
/// Generic instruction handling construct.
|
||||||
* Generic instruction handling construct.
|
/// @tparam Visitor An arbitrary visitor type that will be passed through
|
||||||
*
|
/// to the function being handled. This type must be the
|
||||||
* @tparam Visitor An arbitrary visitor type that will be passed through
|
/// type of the first parameter in a handler function.
|
||||||
* to the function being handled. This type must be the
|
/// @tparam OpcodeType Type representing an opcode. This must be the
|
||||||
* type of the first parameter in a handler function.
|
/// type of the second parameter in a handler function.
|
||||||
*
|
|
||||||
* @tparam OpcodeType Type representing an opcode. This must be the
|
|
||||||
* type of the second parameter in a handler function.
|
|
||||||
*/
|
|
||||||
template<typename Visitor, typename OpcodeType>
|
template<typename Visitor, typename OpcodeType>
|
||||||
class Matcher {
|
class Matcher {
|
||||||
public:
|
public:
|
||||||
|
@ -31,46 +27,35 @@ public:
|
||||||
using visitor_type = Visitor;
|
using visitor_type = Visitor;
|
||||||
using handler_return_type = typename Visitor::instruction_return_type;
|
using handler_return_type = typename Visitor::instruction_return_type;
|
||||||
using handler_function = std::function<handler_return_type(Visitor&, opcode_type)>;
|
using handler_function = std::function<handler_return_type(Visitor&, opcode_type)>;
|
||||||
|
Matcher(opcode_type mask, opcode_type expected, handler_function func)
|
||||||
Matcher(const char* const name, opcode_type mask, opcode_type expected, handler_function func)
|
: mask{mask}, expected{expected}, fn{std::move(func)} {}
|
||||||
: name{name}, mask{mask}, expected{expected}, fn{std::move(func)} {}
|
|
||||||
|
|
||||||
/// Gets the name of this type of instruction.
|
|
||||||
const char* GetName() const {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the mask for this instruction.
|
/// Gets the mask for this instruction.
|
||||||
opcode_type GetMask() const {
|
inline opcode_type GetMask() const noexcept {
|
||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the expected value after masking for this instruction.
|
/// Gets the expected value after masking for this instruction.
|
||||||
opcode_type GetExpected() const {
|
inline opcode_type GetExpected() const noexcept {
|
||||||
return expected;
|
return expected;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// Tests to see if the given instruction is the instruction this matcher represents.
|
||||||
* Tests to see if the given instruction is the instruction this matcher represents.
|
/// @param instruction The instruction to test
|
||||||
* @param instruction The instruction to test
|
/// @returns true if the given instruction matches.
|
||||||
* @returns true if the given instruction matches.
|
inline bool Matches(opcode_type instruction) const noexcept {
|
||||||
*/
|
|
||||||
bool Matches(opcode_type instruction) const {
|
|
||||||
return (instruction & mask) == expected;
|
return (instruction & mask) == expected;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// Calls the corresponding instruction handler on visitor for this type of instruction.
|
||||||
* Calls the corresponding instruction handler on visitor for this type of instruction.
|
/// @param v The visitor to use
|
||||||
* @param v The visitor to use
|
/// @param instruction The instruction to decode.
|
||||||
* @param instruction The instruction to decode.
|
inline handler_return_type call(Visitor& v, opcode_type instruction) const noexcept {
|
||||||
*/
|
|
||||||
handler_return_type call(Visitor& v, opcode_type instruction) const {
|
|
||||||
ASSERT(Matches(instruction));
|
ASSERT(Matches(instruction));
|
||||||
return fn(v, instruction);
|
return fn(v, instruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const char* name;
|
|
||||||
opcode_type mask;
|
opcode_type mask;
|
||||||
opcode_type expected;
|
opcode_type expected;
|
||||||
handler_function fn;
|
handler_function fn;
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include "dynarmic/common/assert.h"
|
#include "dynarmic/common/assert.h"
|
||||||
|
|
||||||
#include "dynarmic/common/memory_pool.h"
|
|
||||||
#include "dynarmic/frontend/A32/a32_types.h"
|
#include "dynarmic/frontend/A32/a32_types.h"
|
||||||
#include "dynarmic/frontend/A64/a64_types.h"
|
#include "dynarmic/frontend/A64/a64_types.h"
|
||||||
#include "dynarmic/ir/cond.h"
|
#include "dynarmic/ir/cond.h"
|
||||||
|
@ -27,8 +25,7 @@ namespace Dynarmic::IR {
|
||||||
Block::Block(const LocationDescriptor& location)
|
Block::Block(const LocationDescriptor& location)
|
||||||
: location{location},
|
: location{location},
|
||||||
end_location{location},
|
end_location{location},
|
||||||
cond{Cond::AL},
|
cond{Cond::AL}
|
||||||
instruction_alloc_pool{std::make_unique<std::remove_reference_t<decltype(*instruction_alloc_pool)>>()}
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -40,7 +37,7 @@ Block::Block(const LocationDescriptor& location)
|
||||||
/// @param args A sequence of Value instances used as arguments for the instruction.
|
/// @param args A sequence of Value instances used as arguments for the instruction.
|
||||||
/// @returns Iterator to the newly created instruction.
|
/// @returns Iterator to the newly created instruction.
|
||||||
Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode opcode, std::initializer_list<Value> args) noexcept {
|
Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode opcode, std::initializer_list<Value> args) noexcept {
|
||||||
IR::Inst* inst = new (instruction_alloc_pool->Alloc()) IR::Inst(opcode);
|
IR::Inst* inst = new IR::Inst(opcode);
|
||||||
DEBUG_ASSERT(args.size() == inst->NumArgs());
|
DEBUG_ASSERT(args.size() == inst->NumArgs());
|
||||||
std::for_each(args.begin(), args.end(), [&inst, index = size_t(0)](const auto& arg) mutable {
|
std::for_each(args.begin(), args.end(), [&inst, index = size_t(0)](const auto& arg) mutable {
|
||||||
inst->SetArg(index, arg);
|
inst->SetArg(index, arg);
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#include "dynarmic/ir/terminal.h"
|
#include "dynarmic/ir/terminal.h"
|
||||||
#include "dynarmic/ir/value.h"
|
#include "dynarmic/ir/value.h"
|
||||||
#include "dynarmic/ir/dense_list.h"
|
#include "dynarmic/ir/dense_list.h"
|
||||||
#include "dynarmic/common/memory_pool.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::IR {
|
namespace Dynarmic::IR {
|
||||||
|
|
||||||
|
@ -174,8 +173,6 @@ private:
|
||||||
LocationDescriptor end_location;
|
LocationDescriptor end_location;
|
||||||
/// Conditional to pass in order to execute this block
|
/// Conditional to pass in order to execute this block
|
||||||
Cond cond;
|
Cond cond;
|
||||||
/// Memory pool for instruction list
|
|
||||||
std::unique_ptr<Common::Pool<sizeof(Inst), 2097152UL / sizeof(Inst)>> instruction_alloc_pool;
|
|
||||||
/// Terminal instruction of this block.
|
/// Terminal instruction of this block.
|
||||||
Terminal terminal = Term::Invalid{};
|
Terminal terminal = Term::Invalid{};
|
||||||
/// Number of cycles this block takes to execute if the conditional fails.
|
/// Number of cycles this block takes to execute if the conditional fails.
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2016 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dynarmic/ir/ir_emitter.h"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "dynarmic/common/assert.h"
|
|
||||||
#include <mcl/bit_cast.hpp>
|
|
||||||
|
|
||||||
#include "dynarmic/ir/opcodes.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::IR {
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace Dynarmic::IR
|
|
|
@ -1,70 +0,0 @@
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2016 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dynarmic/interface/A32/config.h"
|
|
||||||
#include "dynarmic/ir/basic_block.h"
|
|
||||||
#include "dynarmic/ir/opcodes.h"
|
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
|
||||||
|
|
||||||
void A32ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
|
|
||||||
for (auto& inst : block) {
|
|
||||||
switch (inst.GetOpcode()) {
|
|
||||||
case IR::Opcode::A32ReadMemory8: {
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const u32 vaddr = inst.GetArg(1).GetU32();
|
|
||||||
if (cb->IsReadOnlyMemory(vaddr)) {
|
|
||||||
const u8 value_from_memory = cb->MemoryRead8(vaddr);
|
|
||||||
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32ReadMemory16: {
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const u32 vaddr = inst.GetArg(1).GetU32();
|
|
||||||
if (cb->IsReadOnlyMemory(vaddr)) {
|
|
||||||
const u16 value_from_memory = cb->MemoryRead16(vaddr);
|
|
||||||
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32ReadMemory32: {
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const u32 vaddr = inst.GetArg(1).GetU32();
|
|
||||||
if (cb->IsReadOnlyMemory(vaddr)) {
|
|
||||||
const u32 value_from_memory = cb->MemoryRead32(vaddr);
|
|
||||||
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32ReadMemory64: {
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const u32 vaddr = inst.GetArg(1).GetU32();
|
|
||||||
if (cb->IsReadOnlyMemory(vaddr)) {
|
|
||||||
const u64 value_from_memory = cb->MemoryRead64(vaddr);
|
|
||||||
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization
|
|
|
@ -1,382 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2016 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <array>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
#include "dynarmic/common/assert.h"
|
|
||||||
#include "dynarmic/common/common_types.h"
|
|
||||||
|
|
||||||
#include "dynarmic/frontend/A32/a32_ir_emitter.h"
|
|
||||||
#include "dynarmic/frontend/A32/a32_types.h"
|
|
||||||
#include "dynarmic/ir/basic_block.h"
|
|
||||||
#include "dynarmic/ir/opcodes.h"
|
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
|
||||||
#include "dynarmic/ir/value.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
void FlagsPass(IR::Block& block) {
|
|
||||||
using Iterator = std::reverse_iterator<IR::Block::iterator>;
|
|
||||||
|
|
||||||
struct FlagInfo {
|
|
||||||
bool set_not_required = false;
|
|
||||||
bool has_value_request = false;
|
|
||||||
Iterator value_request = {};
|
|
||||||
};
|
|
||||||
struct ValuelessFlagInfo {
|
|
||||||
bool set_not_required = false;
|
|
||||||
};
|
|
||||||
ValuelessFlagInfo nzcvq;
|
|
||||||
ValuelessFlagInfo nzcv;
|
|
||||||
ValuelessFlagInfo nz;
|
|
||||||
FlagInfo c_flag;
|
|
||||||
FlagInfo ge;
|
|
||||||
|
|
||||||
auto do_set = [&](FlagInfo& info, IR::Value value, Iterator inst) {
|
|
||||||
if (info.has_value_request) {
|
|
||||||
info.value_request->ReplaceUsesWith(value);
|
|
||||||
}
|
|
||||||
info.has_value_request = false;
|
|
||||||
|
|
||||||
if (info.set_not_required) {
|
|
||||||
inst->Invalidate();
|
|
||||||
}
|
|
||||||
info.set_not_required = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto do_set_valueless = [&](ValuelessFlagInfo& info, Iterator inst) {
|
|
||||||
if (info.set_not_required) {
|
|
||||||
inst->Invalidate();
|
|
||||||
}
|
|
||||||
info.set_not_required = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto do_get = [](FlagInfo& info, Iterator inst) {
|
|
||||||
if (info.has_value_request) {
|
|
||||||
info.value_request->ReplaceUsesWith(IR::Value{&*inst});
|
|
||||||
}
|
|
||||||
info.has_value_request = true;
|
|
||||||
info.value_request = inst;
|
|
||||||
};
|
|
||||||
|
|
||||||
A32::IREmitter ir{block, A32::LocationDescriptor{block.Location()}, {}};
|
|
||||||
|
|
||||||
for (auto inst = block.rbegin(); inst != block.rend(); ++inst) {
|
|
||||||
auto const opcode = inst->GetOpcode();
|
|
||||||
switch (opcode) {
|
|
||||||
case IR::Opcode::A32GetCFlag: {
|
|
||||||
do_get(c_flag, inst);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32SetCpsrNZCV: {
|
|
||||||
if (c_flag.has_value_request) {
|
|
||||||
ir.SetInsertionPointBefore(inst.base()); // base is one ahead
|
|
||||||
IR::U1 c = ir.GetCFlagFromNZCV(IR::NZCV{inst->GetArg(0)});
|
|
||||||
c_flag.value_request->ReplaceUsesWith(c);
|
|
||||||
c_flag.has_value_request = false;
|
|
||||||
break; // This case will be executed again because of the above
|
|
||||||
}
|
|
||||||
|
|
||||||
do_set_valueless(nzcv, inst);
|
|
||||||
|
|
||||||
nz = {.set_not_required = true};
|
|
||||||
c_flag = {.set_not_required = true};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32SetCpsrNZCVRaw: {
|
|
||||||
if (c_flag.has_value_request) {
|
|
||||||
nzcv.set_not_required = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
do_set_valueless(nzcv, inst);
|
|
||||||
|
|
||||||
nzcvq = {};
|
|
||||||
nz = {.set_not_required = true};
|
|
||||||
c_flag = {.set_not_required = true};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32SetCpsrNZCVQ: {
|
|
||||||
if (c_flag.has_value_request) {
|
|
||||||
nzcvq.set_not_required = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
do_set_valueless(nzcvq, inst);
|
|
||||||
|
|
||||||
nzcv = {.set_not_required = true};
|
|
||||||
nz = {.set_not_required = true};
|
|
||||||
c_flag = {.set_not_required = true};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32SetCpsrNZ: {
|
|
||||||
do_set_valueless(nz, inst);
|
|
||||||
|
|
||||||
nzcvq = {};
|
|
||||||
nzcv = {};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32SetCpsrNZC: {
|
|
||||||
if (c_flag.has_value_request) {
|
|
||||||
c_flag.value_request->ReplaceUsesWith(inst->GetArg(1));
|
|
||||||
c_flag.has_value_request = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!inst->GetArg(1).IsImmediate() && inst->GetArg(1).GetInstRecursive()->GetOpcode() == IR::Opcode::A32GetCFlag) {
|
|
||||||
const auto nz_value = inst->GetArg(0);
|
|
||||||
|
|
||||||
inst->Invalidate();
|
|
||||||
|
|
||||||
ir.SetInsertionPointBefore(inst.base());
|
|
||||||
ir.SetCpsrNZ(IR::NZCV{nz_value});
|
|
||||||
|
|
||||||
nzcvq = {};
|
|
||||||
nzcv = {};
|
|
||||||
nz = {.set_not_required = true};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nz.set_not_required && c_flag.set_not_required) {
|
|
||||||
inst->Invalidate();
|
|
||||||
} else if (nz.set_not_required) {
|
|
||||||
inst->SetArg(0, IR::Value::EmptyNZCVImmediateMarker());
|
|
||||||
}
|
|
||||||
nz.set_not_required = true;
|
|
||||||
c_flag.set_not_required = true;
|
|
||||||
|
|
||||||
nzcv = {};
|
|
||||||
nzcvq = {};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32SetGEFlags: {
|
|
||||||
do_set(ge, inst->GetArg(0), inst);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32GetGEFlags: {
|
|
||||||
do_get(ge, inst);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32SetGEFlagsCompressed: {
|
|
||||||
ge = {.set_not_required = true};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32OrQFlag: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
if (ReadsFromCPSR(opcode) || WritesToCPSR(opcode)) {
|
|
||||||
nzcvq = {};
|
|
||||||
nzcv = {};
|
|
||||||
nz = {};
|
|
||||||
c_flag = {};
|
|
||||||
ge = {};
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RegisterPass(IR::Block& block) {
|
|
||||||
using Iterator = IR::Block::iterator;
|
|
||||||
|
|
||||||
struct RegInfo {
|
|
||||||
IR::Value register_value;
|
|
||||||
std::optional<Iterator> last_set_instruction;
|
|
||||||
};
|
|
||||||
std::array<RegInfo, 15> reg_info;
|
|
||||||
|
|
||||||
const auto do_get = [](RegInfo& info, Iterator get_inst) {
|
|
||||||
if (info.register_value.IsEmpty()) {
|
|
||||||
info.register_value = IR::Value(&*get_inst);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
get_inst->ReplaceUsesWith(info.register_value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto do_set = [](RegInfo& info, IR::Value value, Iterator set_inst) {
|
|
||||||
if (info.last_set_instruction) {
|
|
||||||
(*info.last_set_instruction)->Invalidate();
|
|
||||||
}
|
|
||||||
info = {
|
|
||||||
.register_value = value,
|
|
||||||
.last_set_instruction = set_inst,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class ExtValueType {
|
|
||||||
Empty,
|
|
||||||
Single,
|
|
||||||
Double,
|
|
||||||
VectorDouble,
|
|
||||||
VectorQuad,
|
|
||||||
};
|
|
||||||
struct ExtRegInfo {
|
|
||||||
ExtValueType value_type = {};
|
|
||||||
IR::Value register_value;
|
|
||||||
std::optional<Iterator> last_set_instruction;
|
|
||||||
};
|
|
||||||
std::array<ExtRegInfo, 64> ext_reg_info;
|
|
||||||
|
|
||||||
const auto do_ext_get = [](ExtValueType type, std::initializer_list<std::reference_wrapper<ExtRegInfo>> infos, Iterator get_inst) {
|
|
||||||
if (!std::all_of(infos.begin(), infos.end(), [type](const auto& info) { return info.get().value_type == type; })) {
|
|
||||||
for (auto& info : infos) {
|
|
||||||
info.get() = {
|
|
||||||
.value_type = type,
|
|
||||||
.register_value = IR::Value(&*get_inst),
|
|
||||||
.last_set_instruction = std::nullopt,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
get_inst->ReplaceUsesWith(std::data(infos)[0].get().register_value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto do_ext_set = [](ExtValueType type, std::initializer_list<std::reference_wrapper<ExtRegInfo>> infos, IR::Value value, Iterator set_inst) {
|
|
||||||
if (std::all_of(infos.begin(), infos.end(), [type](const auto& info) { return info.get().value_type == type; })) {
|
|
||||||
if (std::data(infos)[0].get().last_set_instruction) {
|
|
||||||
(*std::data(infos)[0].get().last_set_instruction)->Invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto& info : infos) {
|
|
||||||
info.get() = {
|
|
||||||
.value_type = type,
|
|
||||||
.register_value = value,
|
|
||||||
.last_set_instruction = set_inst,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Location and version don't matter here.
|
|
||||||
A32::IREmitter ir{block, A32::LocationDescriptor{block.Location()}, {}};
|
|
||||||
|
|
||||||
for (auto inst = block.begin(); inst != block.end(); ++inst) {
|
|
||||||
auto const opcode = inst->GetOpcode();
|
|
||||||
switch (opcode) {
|
|
||||||
case IR::Opcode::A32GetRegister: {
|
|
||||||
const A32::Reg reg = inst->GetArg(0).GetA32RegRef();
|
|
||||||
ASSERT(reg != A32::Reg::PC);
|
|
||||||
const size_t reg_index = static_cast<size_t>(reg);
|
|
||||||
do_get(reg_info[reg_index], inst);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32SetRegister: {
|
|
||||||
const A32::Reg reg = inst->GetArg(0).GetA32RegRef();
|
|
||||||
if (reg == A32::Reg::PC) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const auto reg_index = static_cast<size_t>(reg);
|
|
||||||
do_set(reg_info[reg_index], inst->GetArg(1), inst);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32GetExtendedRegister32: {
|
|
||||||
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
|
|
||||||
const size_t reg_index = A32::RegNumber(reg);
|
|
||||||
do_ext_get(ExtValueType::Single, {ext_reg_info[reg_index]}, inst);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32SetExtendedRegister32: {
|
|
||||||
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
|
|
||||||
const size_t reg_index = A32::RegNumber(reg);
|
|
||||||
do_ext_set(ExtValueType::Single, {ext_reg_info[reg_index]}, inst->GetArg(1), inst);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32GetExtendedRegister64: {
|
|
||||||
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
|
|
||||||
const size_t reg_index = A32::RegNumber(reg);
|
|
||||||
do_ext_get(ExtValueType::Double,
|
|
||||||
{
|
|
||||||
ext_reg_info[reg_index * 2 + 0],
|
|
||||||
ext_reg_info[reg_index * 2 + 1],
|
|
||||||
},
|
|
||||||
inst);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32SetExtendedRegister64: {
|
|
||||||
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
|
|
||||||
const size_t reg_index = A32::RegNumber(reg);
|
|
||||||
do_ext_set(ExtValueType::Double,
|
|
||||||
{
|
|
||||||
ext_reg_info[reg_index * 2 + 0],
|
|
||||||
ext_reg_info[reg_index * 2 + 1],
|
|
||||||
},
|
|
||||||
inst->GetArg(1),
|
|
||||||
inst);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32GetVector: {
|
|
||||||
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
|
|
||||||
const size_t reg_index = A32::RegNumber(reg);
|
|
||||||
if (A32::IsDoubleExtReg(reg)) {
|
|
||||||
do_ext_get(ExtValueType::VectorDouble,
|
|
||||||
{
|
|
||||||
ext_reg_info[reg_index * 2 + 0],
|
|
||||||
ext_reg_info[reg_index * 2 + 1],
|
|
||||||
},
|
|
||||||
inst);
|
|
||||||
} else {
|
|
||||||
DEBUG_ASSERT(A32::IsQuadExtReg(reg));
|
|
||||||
do_ext_get(ExtValueType::VectorQuad,
|
|
||||||
{
|
|
||||||
ext_reg_info[reg_index * 4 + 0],
|
|
||||||
ext_reg_info[reg_index * 4 + 1],
|
|
||||||
ext_reg_info[reg_index * 4 + 2],
|
|
||||||
ext_reg_info[reg_index * 4 + 3],
|
|
||||||
},
|
|
||||||
inst);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32SetVector: {
|
|
||||||
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
|
|
||||||
const size_t reg_index = A32::RegNumber(reg);
|
|
||||||
if (A32::IsDoubleExtReg(reg)) {
|
|
||||||
ir.SetInsertionPointAfter(inst);
|
|
||||||
const IR::U128 stored_value = ir.VectorZeroUpper(IR::U128{inst->GetArg(1)});
|
|
||||||
do_ext_set(ExtValueType::VectorDouble,
|
|
||||||
{
|
|
||||||
ext_reg_info[reg_index * 2 + 0],
|
|
||||||
ext_reg_info[reg_index * 2 + 1],
|
|
||||||
},
|
|
||||||
stored_value,
|
|
||||||
inst);
|
|
||||||
} else {
|
|
||||||
DEBUG_ASSERT(A32::IsQuadExtReg(reg));
|
|
||||||
do_ext_set(ExtValueType::VectorQuad,
|
|
||||||
{
|
|
||||||
ext_reg_info[reg_index * 4 + 0],
|
|
||||||
ext_reg_info[reg_index * 4 + 1],
|
|
||||||
ext_reg_info[reg_index * 4 + 2],
|
|
||||||
ext_reg_info[reg_index * 4 + 3],
|
|
||||||
},
|
|
||||||
inst->GetArg(1),
|
|
||||||
inst);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
if (ReadsFromCoreRegister(opcode) || WritesToCoreRegister(opcode)) {
|
|
||||||
reg_info = {};
|
|
||||||
ext_reg_info = {};
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void A32GetSetElimination(IR::Block& block, A32GetSetEliminationOptions) {
|
|
||||||
FlagsPass(block);
|
|
||||||
RegisterPass(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization
|
|
|
@ -1,57 +0,0 @@
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2018 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dynarmic/frontend/A64/a64_ir_emitter.h"
|
|
||||||
#include "dynarmic/interface/A64/config.h"
|
|
||||||
#include "dynarmic/ir/basic_block.h"
|
|
||||||
#include "dynarmic/ir/microinstruction.h"
|
|
||||||
#include "dynarmic/ir/opcodes.h"
|
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
|
||||||
|
|
||||||
void A64CallbackConfigPass(IR::Block& block, const A64::UserConfig& conf) {
|
|
||||||
if (conf.hook_data_cache_operations) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& inst : block) {
|
|
||||||
if (inst.GetOpcode() != IR::Opcode::A64DataCacheOperationRaised) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto op = static_cast<A64::DataCacheOperation>(inst.GetArg(1).GetU64());
|
|
||||||
if (op == A64::DataCacheOperation::ZeroByVA) {
|
|
||||||
A64::IREmitter ir{block};
|
|
||||||
ir.current_location = A64::LocationDescriptor{IR::LocationDescriptor{inst.GetArg(0).GetU64()}};
|
|
||||||
ir.SetInsertionPointBefore(&inst);
|
|
||||||
|
|
||||||
size_t bytes = 4 << static_cast<size_t>(conf.dczid_el0 & 0b1111);
|
|
||||||
IR::U64 addr{inst.GetArg(2)};
|
|
||||||
|
|
||||||
const IR::U128 zero_u128 = ir.ZeroExtendToQuad(ir.Imm64(0));
|
|
||||||
while (bytes >= 16) {
|
|
||||||
ir.WriteMemory128(addr, zero_u128, IR::AccType::DCZVA);
|
|
||||||
addr = ir.Add(addr, ir.Imm64(16));
|
|
||||||
bytes -= 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (bytes >= 8) {
|
|
||||||
ir.WriteMemory64(addr, ir.Imm64(0), IR::AccType::DCZVA);
|
|
||||||
addr = ir.Add(addr, ir.Imm64(8));
|
|
||||||
bytes -= 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (bytes >= 4) {
|
|
||||||
ir.WriteMemory32(addr, ir.Imm32(0), IR::AccType::DCZVA);
|
|
||||||
addr = ir.Add(addr, ir.Imm64(4));
|
|
||||||
bytes -= 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inst.Invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization
|
|
|
@ -1,165 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2016 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
#include "dynarmic/common/common_types.h"
|
|
||||||
|
|
||||||
#include "dynarmic/frontend/A64/a64_types.h"
|
|
||||||
#include "dynarmic/ir/basic_block.h"
|
|
||||||
#include "dynarmic/ir/opcodes.h"
|
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
|
||||||
#include "dynarmic/ir/value.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
|
||||||
|
|
||||||
void A64GetSetElimination(IR::Block& block) {
|
|
||||||
using Iterator = IR::Block::iterator;
|
|
||||||
|
|
||||||
enum class TrackingType {
|
|
||||||
W,
|
|
||||||
X,
|
|
||||||
S,
|
|
||||||
D,
|
|
||||||
Q,
|
|
||||||
SP,
|
|
||||||
NZCV,
|
|
||||||
NZCVRaw,
|
|
||||||
};
|
|
||||||
struct RegisterInfo {
|
|
||||||
IR::Value register_value;
|
|
||||||
TrackingType tracking_type;
|
|
||||||
bool set_instruction_present = false;
|
|
||||||
Iterator last_set_instruction;
|
|
||||||
};
|
|
||||||
std::array<RegisterInfo, 31> reg_info;
|
|
||||||
std::array<RegisterInfo, 32> vec_info;
|
|
||||||
RegisterInfo sp_info;
|
|
||||||
RegisterInfo nzcv_info;
|
|
||||||
|
|
||||||
const auto do_set = [&block](RegisterInfo& info, IR::Value value, Iterator set_inst, TrackingType tracking_type) {
|
|
||||||
if (info.set_instruction_present) {
|
|
||||||
info.last_set_instruction->Invalidate();
|
|
||||||
block.Instructions().erase(info.last_set_instruction);
|
|
||||||
}
|
|
||||||
|
|
||||||
info.register_value = value;
|
|
||||||
info.tracking_type = tracking_type;
|
|
||||||
info.set_instruction_present = true;
|
|
||||||
info.last_set_instruction = set_inst;
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto do_get = [](RegisterInfo& info, Iterator get_inst, TrackingType tracking_type) {
|
|
||||||
const auto do_nothing = [&] {
|
|
||||||
info = {};
|
|
||||||
info.register_value = IR::Value(&*get_inst);
|
|
||||||
info.tracking_type = tracking_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (info.register_value.IsEmpty()) {
|
|
||||||
do_nothing();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info.tracking_type == tracking_type) {
|
|
||||||
get_inst->ReplaceUsesWith(info.register_value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
do_nothing();
|
|
||||||
};
|
|
||||||
|
|
||||||
for (auto inst = block.begin(); inst != block.end(); ++inst) {
|
|
||||||
auto const opcode = inst->GetOpcode();
|
|
||||||
switch (opcode) {
|
|
||||||
case IR::Opcode::A64GetW: {
|
|
||||||
const size_t index = A64::RegNumber(inst->GetArg(0).GetA64RegRef());
|
|
||||||
do_get(reg_info.at(index), inst, TrackingType::W);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64GetX: {
|
|
||||||
const size_t index = A64::RegNumber(inst->GetArg(0).GetA64RegRef());
|
|
||||||
do_get(reg_info.at(index), inst, TrackingType::X);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64GetS: {
|
|
||||||
const size_t index = A64::VecNumber(inst->GetArg(0).GetA64VecRef());
|
|
||||||
do_get(vec_info.at(index), inst, TrackingType::S);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64GetD: {
|
|
||||||
const size_t index = A64::VecNumber(inst->GetArg(0).GetA64VecRef());
|
|
||||||
do_get(vec_info.at(index), inst, TrackingType::D);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64GetQ: {
|
|
||||||
const size_t index = A64::VecNumber(inst->GetArg(0).GetA64VecRef());
|
|
||||||
do_get(vec_info.at(index), inst, TrackingType::Q);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64GetSP: {
|
|
||||||
do_get(sp_info, inst, TrackingType::SP);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64GetNZCVRaw: {
|
|
||||||
do_get(nzcv_info, inst, TrackingType::NZCVRaw);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64SetW: {
|
|
||||||
const size_t index = A64::RegNumber(inst->GetArg(0).GetA64RegRef());
|
|
||||||
do_set(reg_info.at(index), inst->GetArg(1), inst, TrackingType::W);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64SetX: {
|
|
||||||
const size_t index = A64::RegNumber(inst->GetArg(0).GetA64RegRef());
|
|
||||||
do_set(reg_info.at(index), inst->GetArg(1), inst, TrackingType::X);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64SetS: {
|
|
||||||
const size_t index = A64::VecNumber(inst->GetArg(0).GetA64VecRef());
|
|
||||||
do_set(vec_info.at(index), inst->GetArg(1), inst, TrackingType::S);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64SetD: {
|
|
||||||
const size_t index = A64::VecNumber(inst->GetArg(0).GetA64VecRef());
|
|
||||||
do_set(vec_info.at(index), inst->GetArg(1), inst, TrackingType::D);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64SetQ: {
|
|
||||||
const size_t index = A64::VecNumber(inst->GetArg(0).GetA64VecRef());
|
|
||||||
do_set(vec_info.at(index), inst->GetArg(1), inst, TrackingType::Q);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64SetSP: {
|
|
||||||
do_set(sp_info, inst->GetArg(0), inst, TrackingType::SP);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64SetNZCV: {
|
|
||||||
do_set(nzcv_info, inst->GetArg(0), inst, TrackingType::NZCV);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64SetNZCVRaw: {
|
|
||||||
do_set(nzcv_info, inst->GetArg(0), inst, TrackingType::NZCVRaw);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
if (ReadsFromCPSR(opcode) || WritesToCPSR(opcode)) {
|
|
||||||
nzcv_info = {};
|
|
||||||
}
|
|
||||||
if (ReadsFromCoreRegister(opcode) || WritesToCoreRegister(opcode)) {
|
|
||||||
reg_info = {};
|
|
||||||
vec_info = {};
|
|
||||||
sp_info = {};
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization
|
|
|
@ -1,57 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2018 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <boost/variant/get.hpp>
|
|
||||||
#include "dynarmic/common/common_types.h"
|
|
||||||
|
|
||||||
#include "dynarmic/frontend/A64/a64_location_descriptor.h"
|
|
||||||
#include "dynarmic/frontend/A64/translate/a64_translate.h"
|
|
||||||
#include "dynarmic/interface/A64/config.h"
|
|
||||||
#include "dynarmic/ir/basic_block.h"
|
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
|
||||||
|
|
||||||
void A64MergeInterpretBlocksPass(IR::Block& block, A64::UserCallbacks* cb) {
|
|
||||||
const auto is_interpret_instruction = [cb](A64::LocationDescriptor location) {
|
|
||||||
const auto instruction = cb->MemoryReadCode(location.PC());
|
|
||||||
if (!instruction)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
IR::Block new_block{location};
|
|
||||||
A64::TranslateSingleInstruction(new_block, location, *instruction);
|
|
||||||
|
|
||||||
if (!new_block.Instructions().empty())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const IR::Terminal terminal = new_block.GetTerminal();
|
|
||||||
if (auto term = boost::get<IR::Term::Interpret>(&terminal)) {
|
|
||||||
return term->next == location;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
IR::Terminal terminal = block.GetTerminal();
|
|
||||||
auto term = boost::get<IR::Term::Interpret>(&terminal);
|
|
||||||
if (!term)
|
|
||||||
return;
|
|
||||||
|
|
||||||
A64::LocationDescriptor location{term->next};
|
|
||||||
size_t num_instructions = 1;
|
|
||||||
|
|
||||||
while (is_interpret_instruction(location.AdvancePC(static_cast<int>(num_instructions * 4)))) {
|
|
||||||
num_instructions++;
|
|
||||||
}
|
|
||||||
|
|
||||||
term->num_instructions = num_instructions;
|
|
||||||
block.ReplaceTerminal(terminal);
|
|
||||||
block.CycleCount() += num_instructions - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization
|
|
|
@ -1,559 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2016 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include "dynarmic/common/assert.h"
|
|
||||||
#include <mcl/bit/rotate.hpp>
|
|
||||||
#include <mcl/bit/swap.hpp>
|
|
||||||
#include "dynarmic/common/common_types.h"
|
|
||||||
|
|
||||||
#include "dynarmic/common/safe_ops.h"
|
|
||||||
#include "dynarmic/ir/basic_block.h"
|
|
||||||
#include "dynarmic/ir/ir_emitter.h"
|
|
||||||
#include "dynarmic/ir/opcodes.h"
|
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
|
||||||
|
|
||||||
using Op = Dynarmic::IR::Opcode;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// Tiny helper to avoid the need to store based off the opcode
|
|
||||||
// bit size all over the place within folding functions.
|
|
||||||
void ReplaceUsesWith(IR::Inst& inst, bool is_32_bit, u64 value) {
|
|
||||||
if (is_32_bit) {
|
|
||||||
inst.ReplaceUsesWith(IR::Value{static_cast<u32>(value)});
|
|
||||||
} else {
|
|
||||||
inst.ReplaceUsesWith(IR::Value{value});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IR::Value Value(bool is_32_bit, u64 value) {
|
|
||||||
return is_32_bit ? IR::Value{static_cast<u32>(value)} : IR::Value{value};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ImmFn>
|
|
||||||
bool FoldCommutative(IR::Inst& inst, bool is_32_bit, ImmFn imm_fn) {
|
|
||||||
const auto lhs = inst.GetArg(0);
|
|
||||||
const auto rhs = inst.GetArg(1);
|
|
||||||
|
|
||||||
const bool is_lhs_immediate = lhs.IsImmediate();
|
|
||||||
const bool is_rhs_immediate = rhs.IsImmediate();
|
|
||||||
|
|
||||||
if (is_lhs_immediate && is_rhs_immediate) {
|
|
||||||
const u64 result = imm_fn(lhs.GetImmediateAsU64(), rhs.GetImmediateAsU64());
|
|
||||||
ReplaceUsesWith(inst, is_32_bit, result);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_lhs_immediate && !is_rhs_immediate) {
|
|
||||||
const IR::Inst* rhs_inst = rhs.GetInstRecursive();
|
|
||||||
if (rhs_inst->GetOpcode() == inst.GetOpcode() && rhs_inst->GetArg(1).IsImmediate()) {
|
|
||||||
const u64 combined = imm_fn(lhs.GetImmediateAsU64(), rhs_inst->GetArg(1).GetImmediateAsU64());
|
|
||||||
inst.SetArg(0, rhs_inst->GetArg(0));
|
|
||||||
inst.SetArg(1, Value(is_32_bit, combined));
|
|
||||||
} else {
|
|
||||||
// Normalize
|
|
||||||
inst.SetArg(0, rhs);
|
|
||||||
inst.SetArg(1, lhs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_lhs_immediate && is_rhs_immediate) {
|
|
||||||
const IR::Inst* lhs_inst = lhs.GetInstRecursive();
|
|
||||||
if (lhs_inst->GetOpcode() == inst.GetOpcode() && lhs_inst->GetArg(1).IsImmediate()) {
|
|
||||||
const u64 combined = imm_fn(rhs.GetImmediateAsU64(), lhs_inst->GetArg(1).GetImmediateAsU64());
|
|
||||||
inst.SetArg(0, lhs_inst->GetArg(0));
|
|
||||||
inst.SetArg(1, Value(is_32_bit, combined));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FoldAdd(IR::Inst& inst, bool is_32_bit) {
|
|
||||||
const auto lhs = inst.GetArg(0);
|
|
||||||
const auto rhs = inst.GetArg(1);
|
|
||||||
const auto carry = inst.GetArg(2);
|
|
||||||
|
|
||||||
if (lhs.IsImmediate() && !rhs.IsImmediate()) {
|
|
||||||
// Normalize
|
|
||||||
inst.SetArg(0, rhs);
|
|
||||||
inst.SetArg(1, lhs);
|
|
||||||
FoldAdd(inst, is_32_bit);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inst.HasAssociatedPseudoOperation()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!lhs.IsImmediate() && rhs.IsImmediate()) {
|
|
||||||
const IR::Inst* lhs_inst = lhs.GetInstRecursive();
|
|
||||||
if (lhs_inst->GetOpcode() == inst.GetOpcode() && lhs_inst->GetArg(1).IsImmediate() && lhs_inst->GetArg(2).IsImmediate()) {
|
|
||||||
const u64 combined = rhs.GetImmediateAsU64() + lhs_inst->GetArg(1).GetImmediateAsU64() + lhs_inst->GetArg(2).GetU1();
|
|
||||||
if (combined == 0) {
|
|
||||||
inst.ReplaceUsesWith(lhs_inst->GetArg(0));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
inst.SetArg(0, lhs_inst->GetArg(0));
|
|
||||||
inst.SetArg(1, Value(is_32_bit, combined));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (rhs.IsZero() && carry.IsZero()) {
|
|
||||||
inst.ReplaceUsesWith(lhs);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inst.AreAllArgsImmediates()) {
|
|
||||||
const u64 result = lhs.GetImmediateAsU64() + rhs.GetImmediateAsU64() + carry.GetU1();
|
|
||||||
ReplaceUsesWith(inst, is_32_bit, result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Folds AND operations based on the following:
|
|
||||||
///
|
|
||||||
/// 1. imm_x & imm_y -> result
|
|
||||||
/// 2. x & 0 -> 0
|
|
||||||
/// 3. 0 & y -> 0
|
|
||||||
/// 4. x & y -> y (where x has all bits set to 1)
|
|
||||||
/// 5. x & y -> x (where y has all bits set to 1)
|
|
||||||
///
|
|
||||||
void FoldAND(IR::Inst& inst, bool is_32_bit) {
|
|
||||||
if (FoldCommutative(inst, is_32_bit, [](u64 a, u64 b) { return a & b; })) {
|
|
||||||
const auto rhs = inst.GetArg(1);
|
|
||||||
if (rhs.IsZero()) {
|
|
||||||
ReplaceUsesWith(inst, is_32_bit, 0);
|
|
||||||
} else if (rhs.HasAllBitsSet()) {
|
|
||||||
inst.ReplaceUsesWith(inst.GetArg(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Folds byte reversal opcodes based on the following:
|
|
||||||
///
|
|
||||||
/// 1. imm -> swap(imm)
|
|
||||||
///
|
|
||||||
void FoldByteReverse(IR::Inst& inst, Op op) {
|
|
||||||
const auto operand = inst.GetArg(0);
|
|
||||||
|
|
||||||
if (!operand.IsImmediate()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (op == Op::ByteReverseWord) {
|
|
||||||
const u32 result = mcl::bit::swap_bytes_32(static_cast<u32>(operand.GetImmediateAsU64()));
|
|
||||||
inst.ReplaceUsesWith(IR::Value{result});
|
|
||||||
} else if (op == Op::ByteReverseHalf) {
|
|
||||||
const u16 result = mcl::bit::swap_bytes_16(static_cast<u16>(operand.GetImmediateAsU64()));
|
|
||||||
inst.ReplaceUsesWith(IR::Value{result});
|
|
||||||
} else {
|
|
||||||
const u64 result = mcl::bit::swap_bytes_64(operand.GetImmediateAsU64());
|
|
||||||
inst.ReplaceUsesWith(IR::Value{result});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Folds division operations based on the following:
|
|
||||||
///
|
|
||||||
/// 1. x / 0 -> 0 (NOTE: This is an ARM-specific behavior defined in the architecture reference manual)
|
|
||||||
/// 2. imm_x / imm_y -> result
|
|
||||||
/// 3. x / 1 -> x
|
|
||||||
///
|
|
||||||
void FoldDivide(IR::Inst& inst, bool is_32_bit, bool is_signed) {
|
|
||||||
const auto rhs = inst.GetArg(1);
|
|
||||||
|
|
||||||
if (rhs.IsZero()) {
|
|
||||||
ReplaceUsesWith(inst, is_32_bit, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto lhs = inst.GetArg(0);
|
|
||||||
if (lhs.IsImmediate() && rhs.IsImmediate()) {
|
|
||||||
if (is_signed) {
|
|
||||||
const s64 result = lhs.GetImmediateAsS64() / rhs.GetImmediateAsS64();
|
|
||||||
ReplaceUsesWith(inst, is_32_bit, static_cast<u64>(result));
|
|
||||||
} else {
|
|
||||||
const u64 result = lhs.GetImmediateAsU64() / rhs.GetImmediateAsU64();
|
|
||||||
ReplaceUsesWith(inst, is_32_bit, result);
|
|
||||||
}
|
|
||||||
} else if (rhs.IsUnsignedImmediate(1)) {
|
|
||||||
inst.ReplaceUsesWith(IR::Value{lhs});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Folds EOR operations based on the following:
|
|
||||||
//
|
|
||||||
// 1. imm_x ^ imm_y -> result
|
|
||||||
// 2. x ^ 0 -> x
|
|
||||||
// 3. 0 ^ y -> y
|
|
||||||
//
|
|
||||||
void FoldEOR(IR::Inst& inst, bool is_32_bit) {
|
|
||||||
if (FoldCommutative(inst, is_32_bit, [](u64 a, u64 b) { return a ^ b; })) {
|
|
||||||
const auto rhs = inst.GetArg(1);
|
|
||||||
if (rhs.IsZero()) {
|
|
||||||
inst.ReplaceUsesWith(inst.GetArg(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FoldLeastSignificantByte(IR::Inst& inst) {
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto operand = inst.GetArg(0);
|
|
||||||
inst.ReplaceUsesWith(IR::Value{static_cast<u8>(operand.GetImmediateAsU64())});
|
|
||||||
}
|
|
||||||
|
|
||||||
void FoldLeastSignificantHalf(IR::Inst& inst) {
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto operand = inst.GetArg(0);
|
|
||||||
inst.ReplaceUsesWith(IR::Value{static_cast<u16>(operand.GetImmediateAsU64())});
|
|
||||||
}
|
|
||||||
|
|
||||||
void FoldLeastSignificantWord(IR::Inst& inst) {
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto operand = inst.GetArg(0);
|
|
||||||
inst.ReplaceUsesWith(IR::Value{static_cast<u32>(operand.GetImmediateAsU64())});
|
|
||||||
}
|
|
||||||
|
|
||||||
void FoldMostSignificantBit(IR::Inst& inst) {
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto operand = inst.GetArg(0);
|
|
||||||
inst.ReplaceUsesWith(IR::Value{(operand.GetImmediateAsU64() >> 31) != 0});
|
|
||||||
}
|
|
||||||
|
|
||||||
void FoldMostSignificantWord(IR::Inst& inst) {
|
|
||||||
IR::Inst* carry_inst = inst.GetAssociatedPseudoOperation(Op::GetCarryFromOp);
|
|
||||||
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto operand = inst.GetArg(0);
|
|
||||||
if (carry_inst) {
|
|
||||||
carry_inst->ReplaceUsesWith(IR::Value{mcl::bit::get_bit<31>(operand.GetImmediateAsU64())});
|
|
||||||
}
|
|
||||||
inst.ReplaceUsesWith(IR::Value{static_cast<u32>(operand.GetImmediateAsU64() >> 32)});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Folds multiplication operations based on the following:
|
|
||||||
//
|
|
||||||
// 1. imm_x * imm_y -> result
|
|
||||||
// 2. x * 0 -> 0
|
|
||||||
// 3. 0 * y -> 0
|
|
||||||
// 4. x * 1 -> x
|
|
||||||
// 5. 1 * y -> y
|
|
||||||
//
|
|
||||||
void FoldMultiply(IR::Inst& inst, bool is_32_bit) {
|
|
||||||
if (FoldCommutative(inst, is_32_bit, [](u64 a, u64 b) { return a * b; })) {
|
|
||||||
const auto rhs = inst.GetArg(1);
|
|
||||||
if (rhs.IsZero()) {
|
|
||||||
ReplaceUsesWith(inst, is_32_bit, 0);
|
|
||||||
} else if (rhs.IsUnsignedImmediate(1)) {
|
|
||||||
inst.ReplaceUsesWith(inst.GetArg(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Folds NOT operations if the contained value is an immediate.
|
|
||||||
void FoldNOT(IR::Inst& inst, bool is_32_bit) {
|
|
||||||
const auto operand = inst.GetArg(0);
|
|
||||||
|
|
||||||
if (!operand.IsImmediate()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const u64 result = ~operand.GetImmediateAsU64();
|
|
||||||
ReplaceUsesWith(inst, is_32_bit, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Folds OR operations based on the following:
|
|
||||||
//
|
|
||||||
// 1. imm_x | imm_y -> result
|
|
||||||
// 2. x | 0 -> x
|
|
||||||
// 3. 0 | y -> y
|
|
||||||
//
|
|
||||||
void FoldOR(IR::Inst& inst, bool is_32_bit) {
|
|
||||||
if (FoldCommutative(inst, is_32_bit, [](u64 a, u64 b) { return a | b; })) {
|
|
||||||
const auto rhs = inst.GetArg(1);
|
|
||||||
if (rhs.IsZero()) {
|
|
||||||
inst.ReplaceUsesWith(inst.GetArg(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FoldShifts(IR::Inst& inst) {
|
|
||||||
IR::Inst* carry_inst = inst.GetAssociatedPseudoOperation(Op::GetCarryFromOp);
|
|
||||||
|
|
||||||
// The 32-bit variants can contain 3 arguments, while the
|
|
||||||
// 64-bit variants only contain 2.
|
|
||||||
if (inst.NumArgs() == 3 && !carry_inst) {
|
|
||||||
inst.SetArg(2, IR::Value(false));
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto shift_amount = inst.GetArg(1);
|
|
||||||
|
|
||||||
if (shift_amount.IsZero()) {
|
|
||||||
if (carry_inst) {
|
|
||||||
carry_inst->ReplaceUsesWith(inst.GetArg(2));
|
|
||||||
}
|
|
||||||
inst.ReplaceUsesWith(inst.GetArg(0));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inst.NumArgs() == 3 && shift_amount.IsImmediate() && !shift_amount.IsZero()) {
|
|
||||||
inst.SetArg(2, IR::Value(false));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!inst.AreAllArgsImmediates() || carry_inst) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FoldSignExtendXToWord(IR::Inst& inst) {
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const s64 value = inst.GetArg(0).GetImmediateAsS64();
|
|
||||||
inst.ReplaceUsesWith(IR::Value{static_cast<u32>(value)});
|
|
||||||
}
|
|
||||||
|
|
||||||
void FoldSignExtendXToLong(IR::Inst& inst) {
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const s64 value = inst.GetArg(0).GetImmediateAsS64();
|
|
||||||
inst.ReplaceUsesWith(IR::Value{static_cast<u64>(value)});
|
|
||||||
}
|
|
||||||
|
|
||||||
void FoldSub(IR::Inst& inst, bool is_32_bit) {
|
|
||||||
if (!inst.AreAllArgsImmediates() || inst.HasAssociatedPseudoOperation()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto lhs = inst.GetArg(0);
|
|
||||||
const auto rhs = inst.GetArg(1);
|
|
||||||
const auto carry = inst.GetArg(2);
|
|
||||||
|
|
||||||
const u64 result = lhs.GetImmediateAsU64() + (~rhs.GetImmediateAsU64()) + carry.GetU1();
|
|
||||||
ReplaceUsesWith(inst, is_32_bit, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FoldZeroExtendXToWord(IR::Inst& inst) {
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const u64 value = inst.GetArg(0).GetImmediateAsU64();
|
|
||||||
inst.ReplaceUsesWith(IR::Value{static_cast<u32>(value)});
|
|
||||||
}
|
|
||||||
|
|
||||||
void FoldZeroExtendXToLong(IR::Inst& inst) {
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const u64 value = inst.GetArg(0).GetImmediateAsU64();
|
|
||||||
inst.ReplaceUsesWith(IR::Value{value});
|
|
||||||
}
|
|
||||||
} // Anonymous namespace
|
|
||||||
|
|
||||||
void ConstantPropagation(IR::Block& block) {
|
|
||||||
for (auto& inst : block) {
|
|
||||||
const auto opcode = inst.GetOpcode();
|
|
||||||
|
|
||||||
switch (opcode) {
|
|
||||||
case Op::LeastSignificantWord:
|
|
||||||
FoldLeastSignificantWord(inst);
|
|
||||||
break;
|
|
||||||
case Op::MostSignificantWord:
|
|
||||||
FoldMostSignificantWord(inst);
|
|
||||||
break;
|
|
||||||
case Op::LeastSignificantHalf:
|
|
||||||
FoldLeastSignificantHalf(inst);
|
|
||||||
break;
|
|
||||||
case Op::LeastSignificantByte:
|
|
||||||
FoldLeastSignificantByte(inst);
|
|
||||||
break;
|
|
||||||
case Op::MostSignificantBit:
|
|
||||||
FoldMostSignificantBit(inst);
|
|
||||||
break;
|
|
||||||
case Op::IsZero32:
|
|
||||||
if (inst.AreAllArgsImmediates()) {
|
|
||||||
inst.ReplaceUsesWith(IR::Value{inst.GetArg(0).GetU32() == 0});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::IsZero64:
|
|
||||||
if (inst.AreAllArgsImmediates()) {
|
|
||||||
inst.ReplaceUsesWith(IR::Value{inst.GetArg(0).GetU64() == 0});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::LogicalShiftLeft32:
|
|
||||||
if (FoldShifts(inst)) {
|
|
||||||
ReplaceUsesWith(inst, true, Safe::LogicalShiftLeft<u32>(inst.GetArg(0).GetU32(), inst.GetArg(1).GetU8()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::LogicalShiftLeft64:
|
|
||||||
if (FoldShifts(inst)) {
|
|
||||||
ReplaceUsesWith(inst, false, Safe::LogicalShiftLeft<u64>(inst.GetArg(0).GetU64(), inst.GetArg(1).GetU8()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::LogicalShiftRight32:
|
|
||||||
if (FoldShifts(inst)) {
|
|
||||||
ReplaceUsesWith(inst, true, Safe::LogicalShiftRight<u32>(inst.GetArg(0).GetU32(), inst.GetArg(1).GetU8()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::LogicalShiftRight64:
|
|
||||||
if (FoldShifts(inst)) {
|
|
||||||
ReplaceUsesWith(inst, false, Safe::LogicalShiftRight<u64>(inst.GetArg(0).GetU64(), inst.GetArg(1).GetU8()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::ArithmeticShiftRight32:
|
|
||||||
if (FoldShifts(inst)) {
|
|
||||||
ReplaceUsesWith(inst, true, Safe::ArithmeticShiftRight<u32>(inst.GetArg(0).GetU32(), inst.GetArg(1).GetU8()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::ArithmeticShiftRight64:
|
|
||||||
if (FoldShifts(inst)) {
|
|
||||||
ReplaceUsesWith(inst, false, Safe::ArithmeticShiftRight<u64>(inst.GetArg(0).GetU64(), inst.GetArg(1).GetU8()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::RotateRight32:
|
|
||||||
if (FoldShifts(inst)) {
|
|
||||||
ReplaceUsesWith(inst, true, mcl::bit::rotate_right<u32>(inst.GetArg(0).GetU32(), inst.GetArg(1).GetU8()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::RotateRight64:
|
|
||||||
if (FoldShifts(inst)) {
|
|
||||||
ReplaceUsesWith(inst, false, mcl::bit::rotate_right<u64>(inst.GetArg(0).GetU64(), inst.GetArg(1).GetU8()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::LogicalShiftLeftMasked32:
|
|
||||||
if (inst.AreAllArgsImmediates()) {
|
|
||||||
ReplaceUsesWith(inst, true, inst.GetArg(0).GetU32() << (inst.GetArg(1).GetU32() & 0x1f));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::LogicalShiftLeftMasked64:
|
|
||||||
if (inst.AreAllArgsImmediates()) {
|
|
||||||
ReplaceUsesWith(inst, false, inst.GetArg(0).GetU64() << (inst.GetArg(1).GetU64() & 0x3f));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::LogicalShiftRightMasked32:
|
|
||||||
if (inst.AreAllArgsImmediates()) {
|
|
||||||
ReplaceUsesWith(inst, true, inst.GetArg(0).GetU32() >> (inst.GetArg(1).GetU32() & 0x1f));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::LogicalShiftRightMasked64:
|
|
||||||
if (inst.AreAllArgsImmediates()) {
|
|
||||||
ReplaceUsesWith(inst, false, inst.GetArg(0).GetU64() >> (inst.GetArg(1).GetU64() & 0x3f));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::ArithmeticShiftRightMasked32:
|
|
||||||
if (inst.AreAllArgsImmediates()) {
|
|
||||||
ReplaceUsesWith(inst, true, static_cast<s32>(inst.GetArg(0).GetU32()) >> (inst.GetArg(1).GetU32() & 0x1f));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::ArithmeticShiftRightMasked64:
|
|
||||||
if (inst.AreAllArgsImmediates()) {
|
|
||||||
ReplaceUsesWith(inst, false, static_cast<s64>(inst.GetArg(0).GetU64()) >> (inst.GetArg(1).GetU64() & 0x3f));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::RotateRightMasked32:
|
|
||||||
if (inst.AreAllArgsImmediates()) {
|
|
||||||
ReplaceUsesWith(inst, true, mcl::bit::rotate_right<u32>(inst.GetArg(0).GetU32(), inst.GetArg(1).GetU32()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::RotateRightMasked64:
|
|
||||||
if (inst.AreAllArgsImmediates()) {
|
|
||||||
ReplaceUsesWith(inst, false, mcl::bit::rotate_right<u64>(inst.GetArg(0).GetU64(), inst.GetArg(1).GetU64()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::Add32:
|
|
||||||
case Op::Add64:
|
|
||||||
FoldAdd(inst, opcode == Op::Add32);
|
|
||||||
break;
|
|
||||||
case Op::Sub32:
|
|
||||||
case Op::Sub64:
|
|
||||||
FoldSub(inst, opcode == Op::Sub32);
|
|
||||||
break;
|
|
||||||
case Op::Mul32:
|
|
||||||
case Op::Mul64:
|
|
||||||
FoldMultiply(inst, opcode == Op::Mul32);
|
|
||||||
break;
|
|
||||||
case Op::SignedDiv32:
|
|
||||||
case Op::SignedDiv64:
|
|
||||||
FoldDivide(inst, opcode == Op::SignedDiv32, true);
|
|
||||||
break;
|
|
||||||
case Op::UnsignedDiv32:
|
|
||||||
case Op::UnsignedDiv64:
|
|
||||||
FoldDivide(inst, opcode == Op::UnsignedDiv32, false);
|
|
||||||
break;
|
|
||||||
case Op::And32:
|
|
||||||
case Op::And64:
|
|
||||||
FoldAND(inst, opcode == Op::And32);
|
|
||||||
break;
|
|
||||||
case Op::Eor32:
|
|
||||||
case Op::Eor64:
|
|
||||||
FoldEOR(inst, opcode == Op::Eor32);
|
|
||||||
break;
|
|
||||||
case Op::Or32:
|
|
||||||
case Op::Or64:
|
|
||||||
FoldOR(inst, opcode == Op::Or32);
|
|
||||||
break;
|
|
||||||
case Op::Not32:
|
|
||||||
case Op::Not64:
|
|
||||||
FoldNOT(inst, opcode == Op::Not32);
|
|
||||||
break;
|
|
||||||
case Op::SignExtendByteToWord:
|
|
||||||
case Op::SignExtendHalfToWord:
|
|
||||||
FoldSignExtendXToWord(inst);
|
|
||||||
break;
|
|
||||||
case Op::SignExtendByteToLong:
|
|
||||||
case Op::SignExtendHalfToLong:
|
|
||||||
case Op::SignExtendWordToLong:
|
|
||||||
FoldSignExtendXToLong(inst);
|
|
||||||
break;
|
|
||||||
case Op::ZeroExtendByteToWord:
|
|
||||||
case Op::ZeroExtendHalfToWord:
|
|
||||||
FoldZeroExtendXToWord(inst);
|
|
||||||
break;
|
|
||||||
case Op::ZeroExtendByteToLong:
|
|
||||||
case Op::ZeroExtendHalfToLong:
|
|
||||||
case Op::ZeroExtendWordToLong:
|
|
||||||
FoldZeroExtendXToLong(inst);
|
|
||||||
break;
|
|
||||||
case Op::ByteReverseWord:
|
|
||||||
case Op::ByteReverseHalf:
|
|
||||||
case Op::ByteReverseDual:
|
|
||||||
FoldByteReverse(inst, opcode);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization
|
|
|
@ -1,23 +0,0 @@
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2016 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <mcl/iterator/reverse.hpp>
|
|
||||||
|
|
||||||
#include "dynarmic/ir/basic_block.h"
|
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
|
||||||
|
|
||||||
void DeadCodeElimination(IR::Block& block) {
|
|
||||||
// We iterate over the instructions in reverse order.
|
|
||||||
// This is because removing an instruction reduces the number of uses for earlier instructions.
|
|
||||||
for (auto& inst : mcl::iterator::reverse(block)) {
|
|
||||||
if (!inst.HasUses() && !MayHaveSideEffects(inst.GetOpcode())) {
|
|
||||||
inst.Invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization
|
|
|
@ -1,44 +0,0 @@
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2020 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "dynarmic/ir/basic_block.h"
|
|
||||||
#include "dynarmic/ir/opcodes.h"
|
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
|
||||||
|
|
||||||
void IdentityRemovalPass(IR::Block& block) {
|
|
||||||
std::vector<IR::Inst*> to_invalidate;
|
|
||||||
|
|
||||||
auto iter = block.begin();
|
|
||||||
while (iter != block.end()) {
|
|
||||||
IR::Inst& inst = *iter;
|
|
||||||
|
|
||||||
const size_t num_args = inst.NumArgs();
|
|
||||||
for (size_t i = 0; i < num_args; i++) {
|
|
||||||
while (true) {
|
|
||||||
IR::Value arg = inst.GetArg(i);
|
|
||||||
if (!arg.IsIdentity())
|
|
||||||
break;
|
|
||||||
inst.SetArg(i, arg.GetInst()->GetArg(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inst.GetOpcode() == IR::Opcode::Identity || inst.GetOpcode() == IR::Opcode::Void) {
|
|
||||||
iter = block.Instructions().erase(inst);
|
|
||||||
to_invalidate.push_back(&inst);
|
|
||||||
} else {
|
|
||||||
++iter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (IR::Inst* inst : to_invalidate) {
|
|
||||||
inst->Invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization
|
|
|
@ -1,127 +0,0 @@
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2020 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
#include <mp/metafunction/apply.h>
|
|
||||||
#include <mp/typelist/concat.h>
|
|
||||||
#include <mp/typelist/drop.h>
|
|
||||||
#include <mp/typelist/get.h>
|
|
||||||
#include <mp/typelist/head.h>
|
|
||||||
#include <mp/typelist/list.h>
|
|
||||||
#include <mp/typelist/prepend.h>
|
|
||||||
|
|
||||||
#include "dynarmic/ir/microinstruction.h"
|
|
||||||
#include "dynarmic/ir/opcodes.h"
|
|
||||||
#include "dynarmic/ir/value.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization::IRMatcher {
|
|
||||||
|
|
||||||
struct CaptureValue {
|
|
||||||
using ReturnType = std::tuple<IR::Value>;
|
|
||||||
|
|
||||||
static std::optional<ReturnType> Match(IR::Value value) {
|
|
||||||
return std::tuple(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CaptureInst {
|
|
||||||
using ReturnType = std::tuple<IR::Inst*>;
|
|
||||||
|
|
||||||
static std::optional<ReturnType> Match(IR::Value value) {
|
|
||||||
if (value.IsImmediate())
|
|
||||||
return std::nullopt;
|
|
||||||
return std::tuple(value.GetInstRecursive());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CaptureUImm {
|
|
||||||
using ReturnType = std::tuple<u64>;
|
|
||||||
|
|
||||||
static std::optional<ReturnType> Match(IR::Value value) {
|
|
||||||
return std::tuple(value.GetImmediateAsU64());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CaptureSImm {
|
|
||||||
using ReturnType = std::tuple<s64>;
|
|
||||||
|
|
||||||
static std::optional<ReturnType> Match(IR::Value value) {
|
|
||||||
return std::tuple(value.GetImmediateAsS64());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<u64 Value>
|
|
||||||
struct UImm {
|
|
||||||
using ReturnType = std::tuple<>;
|
|
||||||
|
|
||||||
static std::optional<std::tuple<>> Match(IR::Value value) {
|
|
||||||
if (value.GetImmediateAsU64() == Value)
|
|
||||||
return std::tuple();
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<s64 Value>
|
|
||||||
struct SImm {
|
|
||||||
using ReturnType = std::tuple<>;
|
|
||||||
|
|
||||||
static std::optional<std::tuple<>> Match(IR::Value value) {
|
|
||||||
if (value.GetImmediateAsS64() == Value)
|
|
||||||
return std::tuple();
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<IR::Opcode Opcode, typename... Args>
|
|
||||||
struct Inst {
|
|
||||||
public:
|
|
||||||
using ReturnType = mp::concat<std::tuple<>, typename Args::ReturnType...>;
|
|
||||||
|
|
||||||
static std::optional<ReturnType> Match(const IR::Inst& inst) {
|
|
||||||
if (inst.GetOpcode() != Opcode)
|
|
||||||
return std::nullopt;
|
|
||||||
if (inst.HasAssociatedPseudoOperation())
|
|
||||||
return std::nullopt;
|
|
||||||
return MatchArgs<0>(inst);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::optional<ReturnType> Match(IR::Value value) {
|
|
||||||
if (value.IsImmediate())
|
|
||||||
return std::nullopt;
|
|
||||||
return Match(*value.GetInstRecursive());
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
template<size_t I>
|
|
||||||
static auto MatchArgs(const IR::Inst& inst) -> std::optional<mp::apply<mp::concat, mp::prepend<mp::drop<I, mp::list<typename Args::ReturnType...>>, std::tuple<>>>> {
|
|
||||||
if constexpr (I >= sizeof...(Args)) {
|
|
||||||
return std::tuple();
|
|
||||||
} else {
|
|
||||||
using Arg = mp::get<I, mp::list<Args...>>;
|
|
||||||
|
|
||||||
if (const auto arg = Arg::Match(inst.GetArg(I))) {
|
|
||||||
if (const auto rest = MatchArgs<I + 1>(inst)) {
|
|
||||||
return std::tuple_cat(*arg, *rest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline bool IsSameInst(std::tuple<IR::Inst*, IR::Inst*> t) {
|
|
||||||
return std::get<0>(t) == std::get<1>(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool IsSameInst(std::tuple<IR::Inst*, IR::Inst*, IR::Inst*> t) {
|
|
||||||
return std::get<0>(t) == std::get<1>(t) && std::get<0>(t) == std::get<2>(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization::IRMatcher
|
|
|
@ -1,18 +0,0 @@
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2023 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dynarmic/ir/basic_block.h"
|
|
||||||
#include "dynarmic/ir/microinstruction.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
|
||||||
|
|
||||||
void NamingPass(IR::Block& block) {
|
|
||||||
unsigned name = 1;
|
|
||||||
for (auto& inst : block) {
|
|
||||||
inst.SetName(name++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization
|
|
|
@ -1,47 +0,0 @@
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2016 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
namespace Dynarmic::A32 {
|
|
||||||
struct UserCallbacks;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Dynarmic::A64 {
|
|
||||||
struct UserCallbacks;
|
|
||||||
struct UserConfig;
|
|
||||||
} // namespace Dynarmic::A64
|
|
||||||
|
|
||||||
namespace Dynarmic::IR {
|
|
||||||
class Block;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
|
||||||
|
|
||||||
struct PolyfillOptions {
|
|
||||||
bool sha256 = false;
|
|
||||||
bool vector_multiply_widen = false;
|
|
||||||
|
|
||||||
bool operator==(const PolyfillOptions&) const = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct A32GetSetEliminationOptions {
|
|
||||||
bool convert_nzc_to_nz = false;
|
|
||||||
bool convert_nz_to_nzc = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
void PolyfillPass(IR::Block& block, const PolyfillOptions& opt);
|
|
||||||
void A32ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb);
|
|
||||||
void A32GetSetElimination(IR::Block& block, A32GetSetEliminationOptions opt);
|
|
||||||
void A64CallbackConfigPass(IR::Block& block, const A64::UserConfig& conf);
|
|
||||||
void A64GetSetElimination(IR::Block& block);
|
|
||||||
void A64MergeInterpretBlocksPass(IR::Block& block, A64::UserCallbacks* cb);
|
|
||||||
void ConstantPropagation(IR::Block& block);
|
|
||||||
void DeadCodeElimination(IR::Block& block);
|
|
||||||
void IdentityRemovalPass(IR::Block& block);
|
|
||||||
void VerificationPass(const IR::Block& block);
|
|
||||||
void NamingPass(IR::Block& block);
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization
|
|
|
@ -1,218 +0,0 @@
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2022 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dynarmic/ir/basic_block.h"
|
|
||||||
#include "dynarmic/ir/ir_emitter.h"
|
|
||||||
#include "dynarmic/ir/microinstruction.h"
|
|
||||||
#include "dynarmic/ir/opcodes.h"
|
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
void PolyfillSHA256MessageSchedule0(IR::IREmitter& ir, IR::Inst& inst) {
|
|
||||||
const IR::U128 x = (IR::U128)inst.GetArg(0);
|
|
||||||
const IR::U128 y = (IR::U128)inst.GetArg(1);
|
|
||||||
|
|
||||||
const IR::U128 t = ir.VectorExtract(x, y, 32);
|
|
||||||
|
|
||||||
IR::U128 result = ir.ZeroVector();
|
|
||||||
for (size_t i = 0; i < 4; i++) {
|
|
||||||
const IR::U32 modified_element = [&] {
|
|
||||||
const IR::U32 element = ir.VectorGetElement(32, t, i);
|
|
||||||
const IR::U32 tmp1 = ir.RotateRight(element, ir.Imm8(7));
|
|
||||||
const IR::U32 tmp2 = ir.RotateRight(element, ir.Imm8(18));
|
|
||||||
const IR::U32 tmp3 = ir.LogicalShiftRight(element, ir.Imm8(3));
|
|
||||||
|
|
||||||
return ir.Eor(tmp1, ir.Eor(tmp2, tmp3));
|
|
||||||
}();
|
|
||||||
|
|
||||||
result = ir.VectorSetElement(32, result, i, modified_element);
|
|
||||||
}
|
|
||||||
result = ir.VectorAdd(32, result, x);
|
|
||||||
|
|
||||||
inst.ReplaceUsesWith(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PolyfillSHA256MessageSchedule1(IR::IREmitter& ir, IR::Inst& inst) {
|
|
||||||
const IR::U128 x = (IR::U128)inst.GetArg(0);
|
|
||||||
const IR::U128 y = (IR::U128)inst.GetArg(1);
|
|
||||||
const IR::U128 z = (IR::U128)inst.GetArg(2);
|
|
||||||
|
|
||||||
const IR::U128 T0 = ir.VectorExtract(y, z, 32);
|
|
||||||
|
|
||||||
const IR::U128 lower_half = [&] {
|
|
||||||
const IR::U128 T = ir.VectorRotateWholeVectorRight(z, 64);
|
|
||||||
const IR::U128 tmp1 = ir.VectorRotateRight(32, T, 17);
|
|
||||||
const IR::U128 tmp2 = ir.VectorRotateRight(32, T, 19);
|
|
||||||
const IR::U128 tmp3 = ir.VectorLogicalShiftRight(32, T, 10);
|
|
||||||
const IR::U128 tmp4 = ir.VectorEor(tmp1, ir.VectorEor(tmp2, tmp3));
|
|
||||||
const IR::U128 tmp5 = ir.VectorAdd(32, tmp4, ir.VectorAdd(32, x, T0));
|
|
||||||
return ir.VectorZeroUpper(tmp5);
|
|
||||||
}();
|
|
||||||
|
|
||||||
const IR::U64 upper_half = [&] {
|
|
||||||
const IR::U128 tmp1 = ir.VectorRotateRight(32, lower_half, 17);
|
|
||||||
const IR::U128 tmp2 = ir.VectorRotateRight(32, lower_half, 19);
|
|
||||||
const IR::U128 tmp3 = ir.VectorLogicalShiftRight(32, lower_half, 10);
|
|
||||||
const IR::U128 tmp4 = ir.VectorEor(tmp1, ir.VectorEor(tmp2, tmp3));
|
|
||||||
|
|
||||||
// Shuffle the top two 32-bit elements downwards [3, 2, 1, 0] -> [1, 0, 3, 2]
|
|
||||||
const IR::U128 shuffled_d = ir.VectorRotateWholeVectorRight(x, 64);
|
|
||||||
const IR::U128 shuffled_T0 = ir.VectorRotateWholeVectorRight(T0, 64);
|
|
||||||
|
|
||||||
const IR::U128 tmp5 = ir.VectorAdd(32, tmp4, ir.VectorAdd(32, shuffled_d, shuffled_T0));
|
|
||||||
return ir.VectorGetElement(64, tmp5, 0);
|
|
||||||
}();
|
|
||||||
|
|
||||||
const IR::U128 result = ir.VectorSetElement(64, lower_half, 1, upper_half);
|
|
||||||
|
|
||||||
inst.ReplaceUsesWith(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
IR::U32 SHAchoose(IR::IREmitter& ir, IR::U32 x, IR::U32 y, IR::U32 z) {
|
|
||||||
return ir.Eor(ir.And(ir.Eor(y, z), x), z);
|
|
||||||
}
|
|
||||||
|
|
||||||
IR::U32 SHAmajority(IR::IREmitter& ir, IR::U32 x, IR::U32 y, IR::U32 z) {
|
|
||||||
return ir.Or(ir.And(x, y), ir.And(ir.Or(x, y), z));
|
|
||||||
}
|
|
||||||
|
|
||||||
IR::U32 SHAhashSIGMA0(IR::IREmitter& ir, IR::U32 x) {
|
|
||||||
const IR::U32 tmp1 = ir.RotateRight(x, ir.Imm8(2));
|
|
||||||
const IR::U32 tmp2 = ir.RotateRight(x, ir.Imm8(13));
|
|
||||||
const IR::U32 tmp3 = ir.RotateRight(x, ir.Imm8(22));
|
|
||||||
|
|
||||||
return ir.Eor(tmp1, ir.Eor(tmp2, tmp3));
|
|
||||||
}
|
|
||||||
|
|
||||||
IR::U32 SHAhashSIGMA1(IR::IREmitter& ir, IR::U32 x) {
|
|
||||||
const IR::U32 tmp1 = ir.RotateRight(x, ir.Imm8(6));
|
|
||||||
const IR::U32 tmp2 = ir.RotateRight(x, ir.Imm8(11));
|
|
||||||
const IR::U32 tmp3 = ir.RotateRight(x, ir.Imm8(25));
|
|
||||||
|
|
||||||
return ir.Eor(tmp1, ir.Eor(tmp2, tmp3));
|
|
||||||
}
|
|
||||||
|
|
||||||
void PolyfillSHA256Hash(IR::IREmitter& ir, IR::Inst& inst) {
|
|
||||||
IR::U128 x = (IR::U128)inst.GetArg(0);
|
|
||||||
IR::U128 y = (IR::U128)inst.GetArg(1);
|
|
||||||
const IR::U128 w = (IR::U128)inst.GetArg(2);
|
|
||||||
const bool part1 = inst.GetArg(3).GetU1();
|
|
||||||
|
|
||||||
for (size_t i = 0; i < 4; i++) {
|
|
||||||
const IR::U32 low_x = ir.VectorGetElement(32, x, 0);
|
|
||||||
const IR::U32 after_low_x = ir.VectorGetElement(32, x, 1);
|
|
||||||
const IR::U32 before_high_x = ir.VectorGetElement(32, x, 2);
|
|
||||||
const IR::U32 high_x = ir.VectorGetElement(32, x, 3);
|
|
||||||
|
|
||||||
const IR::U32 low_y = ir.VectorGetElement(32, y, 0);
|
|
||||||
const IR::U32 after_low_y = ir.VectorGetElement(32, y, 1);
|
|
||||||
const IR::U32 before_high_y = ir.VectorGetElement(32, y, 2);
|
|
||||||
const IR::U32 high_y = ir.VectorGetElement(32, y, 3);
|
|
||||||
|
|
||||||
const IR::U32 choice = SHAchoose(ir, low_y, after_low_y, before_high_y);
|
|
||||||
const IR::U32 majority = SHAmajority(ir, low_x, after_low_x, before_high_x);
|
|
||||||
|
|
||||||
const IR::U32 t = [&] {
|
|
||||||
const IR::U32 w_element = ir.VectorGetElement(32, w, i);
|
|
||||||
const IR::U32 sig = SHAhashSIGMA1(ir, low_y);
|
|
||||||
|
|
||||||
return ir.Add(high_y, ir.Add(sig, ir.Add(choice, w_element)));
|
|
||||||
}();
|
|
||||||
|
|
||||||
const IR::U32 new_low_x = ir.Add(t, ir.Add(SHAhashSIGMA0(ir, low_x), majority));
|
|
||||||
const IR::U32 new_low_y = ir.Add(t, high_x);
|
|
||||||
|
|
||||||
// Shuffle all words left by 1 element: [3, 2, 1, 0] -> [2, 1, 0, 3]
|
|
||||||
const IR::U128 shuffled_x = ir.VectorRotateWholeVectorRight(x, 96);
|
|
||||||
const IR::U128 shuffled_y = ir.VectorRotateWholeVectorRight(y, 96);
|
|
||||||
|
|
||||||
x = ir.VectorSetElement(32, shuffled_x, 0, new_low_x);
|
|
||||||
y = ir.VectorSetElement(32, shuffled_y, 0, new_low_y);
|
|
||||||
}
|
|
||||||
|
|
||||||
inst.ReplaceUsesWith(part1 ? x : y);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<size_t esize, bool is_signed>
|
|
||||||
void PolyfillVectorMultiplyWiden(IR::IREmitter& ir, IR::Inst& inst) {
|
|
||||||
IR::U128 n = (IR::U128)inst.GetArg(0);
|
|
||||||
IR::U128 m = (IR::U128)inst.GetArg(1);
|
|
||||||
|
|
||||||
const IR::U128 wide_n = is_signed ? ir.VectorSignExtend(esize, n) : ir.VectorZeroExtend(esize, n);
|
|
||||||
const IR::U128 wide_m = is_signed ? ir.VectorSignExtend(esize, m) : ir.VectorZeroExtend(esize, m);
|
|
||||||
|
|
||||||
const IR::U128 result = ir.VectorMultiply(esize * 2, wide_n, wide_m);
|
|
||||||
|
|
||||||
inst.ReplaceUsesWith(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void PolyfillPass(IR::Block& block, const PolyfillOptions& polyfill) {
|
|
||||||
if (polyfill == PolyfillOptions{}) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
IR::IREmitter ir{block};
|
|
||||||
|
|
||||||
for (auto& inst : block) {
|
|
||||||
ir.SetInsertionPointBefore(&inst);
|
|
||||||
|
|
||||||
switch (inst.GetOpcode()) {
|
|
||||||
case IR::Opcode::SHA256MessageSchedule0:
|
|
||||||
if (polyfill.sha256) {
|
|
||||||
PolyfillSHA256MessageSchedule0(ir, inst);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IR::Opcode::SHA256MessageSchedule1:
|
|
||||||
if (polyfill.sha256) {
|
|
||||||
PolyfillSHA256MessageSchedule1(ir, inst);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IR::Opcode::SHA256Hash:
|
|
||||||
if (polyfill.sha256) {
|
|
||||||
PolyfillSHA256Hash(ir, inst);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IR::Opcode::VectorMultiplySignedWiden8:
|
|
||||||
if (polyfill.vector_multiply_widen) {
|
|
||||||
PolyfillVectorMultiplyWiden<8, true>(ir, inst);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IR::Opcode::VectorMultiplySignedWiden16:
|
|
||||||
if (polyfill.vector_multiply_widen) {
|
|
||||||
PolyfillVectorMultiplyWiden<16, true>(ir, inst);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IR::Opcode::VectorMultiplySignedWiden32:
|
|
||||||
if (polyfill.vector_multiply_widen) {
|
|
||||||
PolyfillVectorMultiplyWiden<32, true>(ir, inst);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IR::Opcode::VectorMultiplyUnsignedWiden8:
|
|
||||||
if (polyfill.vector_multiply_widen) {
|
|
||||||
PolyfillVectorMultiplyWiden<8, false>(ir, inst);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IR::Opcode::VectorMultiplyUnsignedWiden16:
|
|
||||||
if (polyfill.vector_multiply_widen) {
|
|
||||||
PolyfillVectorMultiplyWiden<16, false>(ir, inst);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IR::Opcode::VectorMultiplyUnsignedWiden32:
|
|
||||||
if (polyfill.vector_multiply_widen) {
|
|
||||||
PolyfillVectorMultiplyWiden<32, false>(ir, inst);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization
|
|
|
@ -1,51 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2016 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include "dynarmic/common/assert.h"
|
|
||||||
#include "dynarmic/common/common_types.h"
|
|
||||||
#include <ankerl/unordered_dense.h>
|
|
||||||
|
|
||||||
#include "dynarmic/ir/basic_block.h"
|
|
||||||
#include "dynarmic/ir/microinstruction.h"
|
|
||||||
#include "dynarmic/ir/opcodes.h"
|
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
|
||||||
#include "dynarmic/ir/type.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
|
||||||
|
|
||||||
void VerificationPass(const IR::Block& block) {
|
|
||||||
for (const auto& inst : block) {
|
|
||||||
for (size_t i = 0; i < inst.NumArgs(); i++) {
|
|
||||||
const IR::Type t1 = inst.GetArg(i).GetType();
|
|
||||||
const IR::Type t2 = IR::GetArgTypeOf(inst.GetOpcode(), i);
|
|
||||||
if (!IR::AreTypesCompatible(t1, t2)) {
|
|
||||||
std::puts(IR::DumpBlock(block).c_str());
|
|
||||||
ASSERT_FALSE("above block failed validation");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ankerl::unordered_dense::map<IR::Inst*, size_t> actual_uses;
|
|
||||||
for (const auto& inst : block) {
|
|
||||||
for (size_t i = 0; i < inst.NumArgs(); i++) {
|
|
||||||
const auto arg = inst.GetArg(i);
|
|
||||||
if (!arg.IsImmediate()) {
|
|
||||||
actual_uses[arg.GetInst()]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& pair : actual_uses) {
|
|
||||||
ASSERT(pair.first->UseCount() == pair.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization
|
|
1491
src/dynarmic/src/dynarmic/ir/opt_passes.cpp
Normal file
1491
src/dynarmic/src/dynarmic/ir/opt_passes.cpp
Normal file
File diff suppressed because it is too large
Load diff
37
src/dynarmic/src/dynarmic/ir/opt_passes.h
Normal file
37
src/dynarmic/src/dynarmic/ir/opt_passes.h
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
/* This file is part of the dynarmic project.
|
||||||
|
* Copyright (c) 2016 MerryMage
|
||||||
|
* SPDX-License-Identifier: 0BSD
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Dynarmic::A32 {
|
||||||
|
struct UserCallbacks;
|
||||||
|
struct UserConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Dynarmic::A64 {
|
||||||
|
struct UserCallbacks;
|
||||||
|
struct UserConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Dynarmic::IR {
|
||||||
|
class Block;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Dynarmic::Optimization {
|
||||||
|
|
||||||
|
struct PolyfillOptions {
|
||||||
|
bool sha256 = false;
|
||||||
|
bool vector_multiply_widen = false;
|
||||||
|
|
||||||
|
bool operator==(const PolyfillOptions&) const = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
void Optimize(IR::Block& block, const A32::UserConfig& conf, const Optimization::PolyfillOptions& polyfill_options);
|
||||||
|
void Optimize(IR::Block& block, const A64::UserConfig& conf, const Optimization::PolyfillOptions& polyfill_options);
|
||||||
|
|
||||||
|
} // namespace Dynarmic::Optimization
|
|
@ -24,6 +24,7 @@
|
||||||
#include "../rand_int.h"
|
#include "../rand_int.h"
|
||||||
#include "../unicorn_emu/a32_unicorn.h"
|
#include "../unicorn_emu/a32_unicorn.h"
|
||||||
#include "./testenv.h"
|
#include "./testenv.h"
|
||||||
|
#include "../native/testenv.h"
|
||||||
#include "dynarmic/common/fp/fpcr.h"
|
#include "dynarmic/common/fp/fpcr.h"
|
||||||
#include "dynarmic/common/fp/fpsr.h"
|
#include "dynarmic/common/fp/fpsr.h"
|
||||||
#include "dynarmic/common/llvm_disassemble.h"
|
#include "dynarmic/common/llvm_disassemble.h"
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "../rand_int.h"
|
#include "../rand_int.h"
|
||||||
#include "../unicorn_emu/a32_unicorn.h"
|
#include "../unicorn_emu/a32_unicorn.h"
|
||||||
#include "./testenv.h"
|
#include "./testenv.h"
|
||||||
|
#include "../native/testenv.h"
|
||||||
#include "dynarmic/frontend/A32/FPSCR.h"
|
#include "dynarmic/frontend/A32/FPSCR.h"
|
||||||
#include "dynarmic/frontend/A32/PSR.h"
|
#include "dynarmic/frontend/A32/PSR.h"
|
||||||
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
|
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
|
||||||
|
@ -29,7 +30,7 @@
|
||||||
#include "dynarmic/frontend/A32/translate/a32_translate.h"
|
#include "dynarmic/frontend/A32/translate/a32_translate.h"
|
||||||
#include "dynarmic/interface/A32/a32.h"
|
#include "dynarmic/interface/A32/a32.h"
|
||||||
#include "dynarmic/ir/basic_block.h"
|
#include "dynarmic/ir/basic_block.h"
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
#include "dynarmic/ir/opt_passes.h"
|
||||||
|
|
||||||
using namespace Dynarmic;
|
using namespace Dynarmic;
|
||||||
|
|
||||||
|
@ -179,13 +180,7 @@ static void RunInstance(size_t run_number, ThumbTestEnv& test_env, A32Unicorn<Th
|
||||||
while (num_insts < instructions_to_execute_count) {
|
while (num_insts < instructions_to_execute_count) {
|
||||||
A32::LocationDescriptor descriptor = {u32(num_insts * 4), cpsr, A32::FPSCR{}};
|
A32::LocationDescriptor descriptor = {u32(num_insts * 4), cpsr, A32::FPSCR{}};
|
||||||
IR::Block ir_block = A32::Translate(descriptor, &test_env, {});
|
IR::Block ir_block = A32::Translate(descriptor, &test_env, {});
|
||||||
Optimization::NamingPass(ir_block);
|
Optimization::Optimize(ir_block, &test_env, {});
|
||||||
Optimization::A32GetSetElimination(ir_block, {.convert_nz_to_nzc = true});
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
Optimization::A32ConstantMemoryReads(ir_block, &test_env);
|
|
||||||
Optimization::ConstantPropagation(ir_block);
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
Optimization::VerificationPass(ir_block);
|
|
||||||
printf("\n\nIR:\n%s", IR::DumpBlock(ir_block).c_str());
|
printf("\n\nIR:\n%s", IR::DumpBlock(ir_block).c_str());
|
||||||
printf("\n\nx86_64:\n");
|
printf("\n\nx86_64:\n");
|
||||||
jit.DumpDisassembly();
|
jit.DumpDisassembly();
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <catch2/catch_test_macros.hpp>
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
#include "./testenv.h"
|
#include "./testenv.h"
|
||||||
|
#include "../native/testenv.h"
|
||||||
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
|
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
|
||||||
#include "dynarmic/interface/A32/a32.h"
|
#include "dynarmic/interface/A32/a32.h"
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <catch2/catch_test_macros.hpp>
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
#include "./testenv.h"
|
#include "./testenv.h"
|
||||||
|
#include "../native/testenv.h"
|
||||||
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
|
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
|
||||||
#include "dynarmic/interface/A32/a32.h"
|
#include "dynarmic/interface/A32/a32.h"
|
||||||
#include "dynarmic/interface/A32/coprocessor.h"
|
#include "dynarmic/interface/A32/coprocessor.h"
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <catch2/catch_test_macros.hpp>
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
#include "./testenv.h"
|
#include "./testenv.h"
|
||||||
|
#include "../native/testenv.h"
|
||||||
|
|
||||||
using namespace Dynarmic;
|
using namespace Dynarmic;
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "dynarmic/common/common_types.h"
|
#include "dynarmic/common/common_types.h"
|
||||||
|
|
||||||
#include "./testenv.h"
|
#include "./testenv.h"
|
||||||
|
#include "../native/testenv.h"
|
||||||
#include "dynarmic/interface/A32/a32.h"
|
#include "dynarmic/interface/A32/a32.h"
|
||||||
|
|
||||||
static Dynarmic::A32::UserConfig GetUserConfig(ThumbTestEnv* testenv) {
|
static Dynarmic::A32::UserConfig GetUserConfig(ThumbTestEnv* testenv) {
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
#include "dynarmic/common/assert.h"
|
#include "dynarmic/common/assert.h"
|
||||||
#include "dynarmic/common/common_types.h"
|
#include "dynarmic/common/common_types.h"
|
||||||
#include "dynarmic/interface/A32/a32.h"
|
#include "dynarmic/interface/A32/a32.h"
|
||||||
#include "../native/testenv.h"
|
|
||||||
|
|
||||||
template<typename InstructionType_, u32 infinite_loop_u32>
|
template<typename InstructionType_, u32 infinite_loop_u32>
|
||||||
class A32TestEnv : public Dynarmic::A32::UserCallbacks {
|
class A32TestEnv : public Dynarmic::A32::UserCallbacks {
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <oaknut/oaknut.hpp>
|
#include <oaknut/oaknut.hpp>
|
||||||
|
|
||||||
#include "./testenv.h"
|
#include "./testenv.h"
|
||||||
|
#include "../native/testenv.h"
|
||||||
#include "dynarmic/common/fp/fpsr.h"
|
#include "dynarmic/common/fp/fpsr.h"
|
||||||
#include "dynarmic/interface/exclusive_monitor.h"
|
#include "dynarmic/interface/exclusive_monitor.h"
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "dynarmic/common/common_types.h"
|
#include "dynarmic/common/common_types.h"
|
||||||
|
|
||||||
#include "./testenv.h"
|
#include "./testenv.h"
|
||||||
|
#include "../native/testenv.h"
|
||||||
|
|
||||||
using namespace Dynarmic;
|
using namespace Dynarmic;
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "../rand_int.h"
|
#include "../rand_int.h"
|
||||||
#include "../unicorn_emu/a64_unicorn.h"
|
#include "../unicorn_emu/a64_unicorn.h"
|
||||||
#include "./testenv.h"
|
#include "./testenv.h"
|
||||||
|
#include "../native/testenv.h"
|
||||||
#include "dynarmic/common/fp/fpcr.h"
|
#include "dynarmic/common/fp/fpcr.h"
|
||||||
#include "dynarmic/common/fp/fpsr.h"
|
#include "dynarmic/common/fp/fpsr.h"
|
||||||
#include "dynarmic/common/llvm_disassemble.h"
|
#include "dynarmic/common/llvm_disassemble.h"
|
||||||
|
@ -28,7 +29,7 @@
|
||||||
#include "dynarmic/frontend/A64/translate/a64_translate.h"
|
#include "dynarmic/frontend/A64/translate/a64_translate.h"
|
||||||
#include "dynarmic/ir/basic_block.h"
|
#include "dynarmic/ir/basic_block.h"
|
||||||
#include "dynarmic/ir/opcodes.h"
|
#include "dynarmic/ir/opcodes.h"
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
#include "dynarmic/ir/opt_passes.h"
|
||||||
|
|
||||||
// Must be declared last for all necessary operator<< to be declared prior to this.
|
// Must be declared last for all necessary operator<< to be declared prior to this.
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <catch2/catch_test_macros.hpp>
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
#include "./testenv.h"
|
#include "./testenv.h"
|
||||||
|
#include "../native/testenv.h"
|
||||||
#include "dynarmic/interface/A64/a64.h"
|
#include "dynarmic/interface/A64/a64.h"
|
||||||
|
|
||||||
TEST_CASE("misaligned load/store do not use page_table when detect_misaligned_access_via_page_table is set", "[a64]") {
|
TEST_CASE("misaligned load/store do not use page_table when detect_misaligned_access_via_page_table is set", "[a64]") {
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <oaknut/oaknut.hpp>
|
#include <oaknut/oaknut.hpp>
|
||||||
|
|
||||||
#include "./testenv.h"
|
#include "./testenv.h"
|
||||||
|
#include "../native/testenv.h"
|
||||||
#include "dynarmic/interface/A64/a64.h"
|
#include "dynarmic/interface/A64/a64.h"
|
||||||
|
|
||||||
using namespace Dynarmic;
|
using namespace Dynarmic;
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <catch2/catch_test_macros.hpp>
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
#include "./testenv.h"
|
#include "./testenv.h"
|
||||||
|
#include "../native/testenv.h"
|
||||||
#include "dynarmic/interface/A64/a64.h"
|
#include "dynarmic/interface/A64/a64.h"
|
||||||
|
|
||||||
using namespace Dynarmic;
|
using namespace Dynarmic;
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue