mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-16 16:24:51 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5679d20e2a | |||
| 64cec648f5 | |||
| ea11b44946 |
@@ -0,0 +1,28 @@
|
||||
From cc15da16e533b2a801934eab2dfeaf3c3949a1dc Mon Sep 17 00:00:00 2001
|
||||
From: crueter <crueter@eden-emu.dev>
|
||||
Date: Mon, 8 Sep 2025 12:28:55 -0400
|
||||
Subject: [PATCH] [cmake] disable NEON runtime check on clang-cl
|
||||
|
||||
When enabling runtime NEON checking for clang-cl, the linker would error out with `undefined symbol: __emit`, since clang doesn't actually implement this instruction. Therefore it makes sense to disable the runtime check by default on this platform, until either this is fixed or a clang-cl compatible intrinsic check is added (I don't have enough knowledge of MSVC to do this)
|
||||
---
|
||||
cmake/OpusConfig.cmake | 7 ++++++-
|
||||
1 file changed, 6 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/cmake/OpusConfig.cmake b/cmake/OpusConfig.cmake
|
||||
index e9319fbad..d0f459e88 100644
|
||||
--- a/cmake/OpusConfig.cmake
|
||||
+++ b/cmake/OpusConfig.cmake
|
||||
@@ -71,7 +71,12 @@ elseif(OPUS_CPU_ARM AND NOT OPUS_DISABLE_INTRINSICS)
|
||||
opus_detect_neon(COMPILER_SUPPORT_NEON)
|
||||
if(COMPILER_SUPPORT_NEON)
|
||||
option(OPUS_USE_NEON "Option to enable NEON" ON)
|
||||
- option(OPUS_MAY_HAVE_NEON "Does runtime check for neon support" ON)
|
||||
+ if (MSVC AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
+ set(NEON_RUNTIME_CHECK_DEFAULT OFF)
|
||||
+ else()
|
||||
+ set(NEON_RUNTIME_CHECK_DEFAULT ON)
|
||||
+ endif()
|
||||
+ option(OPUS_MAY_HAVE_NEON "Does runtime check for neon support" ${NEON_RUNTIME_CHECK_DEFAULT})
|
||||
option(OPUS_PRESUME_NEON "Assume target CPU has NEON support" OFF)
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
|
||||
set(OPUS_PRESUME_NEON ON)
|
||||
@@ -0,0 +1,153 @@
|
||||
From bf455b67b4eaa446ffae5d25410b141b7b1b1082 Mon Sep 17 00:00:00 2001
|
||||
From: crueter <crueter@eden-emu.dev>
|
||||
Date: Mon, 8 Sep 2025 12:08:20 -0400
|
||||
Subject: [PATCH] [cmake] `OPUS_INSTALL` option; only default install if root
|
||||
project
|
||||
|
||||
Signed-off-by: crueter <crueter@eden-emu.dev>
|
||||
---
|
||||
CMakeLists.txt | 112 ++++++++++++++++++++++++++++---------------------
|
||||
1 file changed, 64 insertions(+), 48 deletions(-)
|
||||
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index fcf034b19..08b5e16f8 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -4,6 +4,13 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
include(OpusPackageVersion)
|
||||
get_package_version(PACKAGE_VERSION PROJECT_VERSION)
|
||||
|
||||
+# root project detection
|
||||
+if(DEFINED PROJECT_NAME)
|
||||
+ set(root_project OFF)
|
||||
+else()
|
||||
+ set(root_project ON)
|
||||
+endif()
|
||||
+
|
||||
project(Opus LANGUAGES C VERSION ${PROJECT_VERSION})
|
||||
|
||||
include(OpusFunctions)
|
||||
@@ -83,12 +90,16 @@ set(OPUS_DNN_FLOAT_DEBUG_HELP_STR "Run DNN computations as float for debugging p
|
||||
option(OPUS_DNN_FLOAT_DEBUG ${OPUS_DNN_FLOAT_DEBUG_HELP_STR} OFF)
|
||||
add_feature_info(OPUS_DNN_FLOAT_DEBUG OPUS_DNN_FLOAT_DEBUG ${OPUS_DNN_FLOAT_DEBUG_HELP_STR})
|
||||
|
||||
+set(OPUS_INSTALL_HELP_STR "Install Opus targets")
|
||||
+option(OPUS_INSTALL ${OPUS_INSTALL_HELP_STR} ${root_project})
|
||||
+add_feature_info(OPUS_INSTALL OPUS_INSTALL ${OPUS_INSTALL_HELP_STR})
|
||||
+
|
||||
set(OPUS_INSTALL_PKG_CONFIG_MODULE_HELP_STR "install pkg-config module.")
|
||||
-option(OPUS_INSTALL_PKG_CONFIG_MODULE ${OPUS_INSTALL_PKG_CONFIG_MODULE_HELP_STR} ON)
|
||||
+option(OPUS_INSTALL_PKG_CONFIG_MODULE ${OPUS_INSTALL_PKG_CONFIG_MODULE_HELP_STR} ${OPUS_INSTALL})
|
||||
add_feature_info(OPUS_INSTALL_PKG_CONFIG_MODULE OPUS_INSTALL_PKG_CONFIG_MODULE ${OPUS_INSTALL_PKG_CONFIG_MODULE_HELP_STR})
|
||||
|
||||
set(OPUS_INSTALL_CMAKE_CONFIG_MODULE_HELP_STR "install CMake package config module.")
|
||||
-option(OPUS_INSTALL_CMAKE_CONFIG_MODULE ${OPUS_INSTALL_CMAKE_CONFIG_MODULE_HELP_STR} ON)
|
||||
+option(OPUS_INSTALL_CMAKE_CONFIG_MODULE ${OPUS_INSTALL_CMAKE_CONFIG_MODULE_HELP_STR} ${OPUS_INSTALL})
|
||||
add_feature_info(OPUS_INSTALL_CMAKE_CONFIG_MODULE OPUS_INSTALL_CMAKE_CONFIG_MODULE ${OPUS_INSTALL_CMAKE_CONFIG_MODULE_HELP_STR})
|
||||
|
||||
set(OPUS_DRED_HELP_STR "enable DRED.")
|
||||
@@ -613,53 +624,58 @@ if(OPUS_BUILD_FRAMEWORK)
|
||||
OUTPUT_NAME Opus)
|
||||
endif()
|
||||
|
||||
-install(TARGETS opus
|
||||
- EXPORT OpusTargets
|
||||
- ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
- LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
- RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
- FRAMEWORK DESTINATION ${CMAKE_INSTALL_PREFIX}
|
||||
- PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/opus)
|
||||
-
|
||||
-if(OPUS_INSTALL_PKG_CONFIG_MODULE)
|
||||
- set(prefix ${CMAKE_INSTALL_PREFIX})
|
||||
- set(exec_prefix ${CMAKE_INSTALL_PREFIX})
|
||||
- set(libdir ${CMAKE_INSTALL_FULL_LIBDIR})
|
||||
- set(includedir ${CMAKE_INSTALL_FULL_INCLUDEDIR})
|
||||
- set(VERSION ${PACKAGE_VERSION})
|
||||
- if(HAVE_LIBM)
|
||||
- set(LIBM "-lm")
|
||||
+if (OPUS_INSTALL)
|
||||
+ install(TARGETS opus
|
||||
+ EXPORT OpusTargets
|
||||
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
+ FRAMEWORK DESTINATION ${CMAKE_INSTALL_PREFIX}
|
||||
+ PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/opus)
|
||||
+
|
||||
+ if(OPUS_INSTALL_PKG_CONFIG_MODULE)
|
||||
+ set(prefix ${CMAKE_INSTALL_PREFIX})
|
||||
+ set(exec_prefix ${CMAKE_INSTALL_PREFIX})
|
||||
+ set(libdir ${CMAKE_INSTALL_FULL_LIBDIR})
|
||||
+ set(includedir ${CMAKE_INSTALL_FULL_INCLUDEDIR})
|
||||
+ set(VERSION ${PACKAGE_VERSION})
|
||||
+ if(HAVE_LIBM)
|
||||
+ set(LIBM "-lm")
|
||||
+ endif()
|
||||
+ configure_file(opus.pc.in opus.pc)
|
||||
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/opus.pc
|
||||
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
+ endif()
|
||||
+
|
||||
+ if(OPUS_INSTALL_CMAKE_CONFIG_MODULE)
|
||||
+ set(CPACK_GENERATOR TGZ)
|
||||
+ include(CPack)
|
||||
+ set(CMAKE_INSTALL_PACKAGEDIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
|
||||
+ install(EXPORT OpusTargets
|
||||
+ NAMESPACE Opus::
|
||||
+ DESTINATION ${CMAKE_INSTALL_PACKAGEDIR})
|
||||
+
|
||||
+ include(CMakePackageConfigHelpers)
|
||||
+
|
||||
+ set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
+ configure_package_config_file(
|
||||
+ ${PROJECT_SOURCE_DIR}/cmake/OpusConfig.cmake.in
|
||||
+ OpusConfig.cmake
|
||||
+ INSTALL_DESTINATION
|
||||
+ ${CMAKE_INSTALL_PACKAGEDIR}
|
||||
+ PATH_VARS
|
||||
+ INCLUDE_INSTALL_DIR
|
||||
+ INSTALL_PREFIX
|
||||
+ ${CMAKE_INSTALL_PREFIX})
|
||||
+
|
||||
+ write_basic_package_version_file(OpusConfigVersion.cmake
|
||||
+ VERSION ${PROJECT_VERSION}
|
||||
+ COMPATIBILITY SameMajorVersion)
|
||||
+
|
||||
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/OpusConfig.cmake
|
||||
+ ${CMAKE_CURRENT_BINARY_DIR}/OpusConfigVersion.cmake
|
||||
+ DESTINATION ${CMAKE_INSTALL_PACKAGEDIR})
|
||||
endif()
|
||||
- configure_file(opus.pc.in opus.pc)
|
||||
- install(FILES ${CMAKE_CURRENT_BINARY_DIR}/opus.pc
|
||||
- DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
-endif()
|
||||
-
|
||||
-if(OPUS_INSTALL_CMAKE_CONFIG_MODULE)
|
||||
- set(CPACK_GENERATOR TGZ)
|
||||
- include(CPack)
|
||||
- set(CMAKE_INSTALL_PACKAGEDIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
|
||||
- install(EXPORT OpusTargets
|
||||
- NAMESPACE Opus::
|
||||
- DESTINATION ${CMAKE_INSTALL_PACKAGEDIR})
|
||||
-
|
||||
- include(CMakePackageConfigHelpers)
|
||||
-
|
||||
- set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
- configure_package_config_file(${PROJECT_SOURCE_DIR}/cmake/OpusConfig.cmake.in
|
||||
- OpusConfig.cmake
|
||||
- INSTALL_DESTINATION
|
||||
- ${CMAKE_INSTALL_PACKAGEDIR}
|
||||
- PATH_VARS
|
||||
- INCLUDE_INSTALL_DIR
|
||||
- INSTALL_PREFIX
|
||||
- ${CMAKE_INSTALL_PREFIX})
|
||||
- write_basic_package_version_file(OpusConfigVersion.cmake
|
||||
- VERSION ${PROJECT_VERSION}
|
||||
- COMPATIBILITY SameMajorVersion)
|
||||
- install(FILES ${CMAKE_CURRENT_BINARY_DIR}/OpusConfig.cmake
|
||||
- ${CMAKE_CURRENT_BINARY_DIR}/OpusConfigVersion.cmake
|
||||
- DESTINATION ${CMAKE_INSTALL_PACKAGEDIR})
|
||||
endif()
|
||||
|
||||
if(OPUS_BUILD_PROGRAMS)
|
||||
@@ -464,6 +464,21 @@ if (NOT YUZU_STATIC_ROOM)
|
||||
if (ZLIB_ADDED)
|
||||
add_library(ZLIB::ZLIB ALIAS zlibstatic)
|
||||
endif()
|
||||
|
||||
# Opus
|
||||
AddJsonPackage(opus)
|
||||
|
||||
if (Opus_ADDED)
|
||||
if (MSVC AND CXX_CLANG)
|
||||
target_compile_options(opus PRIVATE
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-implicit-function-declaration>
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT TARGET Opus::opus)
|
||||
add_library(Opus::opus ALIAS opus)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT TARGET Boost::headers)
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_search_module(OPUS QUIET IMPORTED_TARGET opus)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Opus
|
||||
REQUIRED_VARS OPUS_LINK_LIBRARIES
|
||||
VERSION_VAR OPUS_VERSION
|
||||
)
|
||||
|
||||
if (MSYS2)
|
||||
FixMsysPath(PkgConfig::OPUS)
|
||||
endif()
|
||||
|
||||
if (Opus_FOUND AND NOT TARGET Opus::opus)
|
||||
add_library(Opus::opus ALIAS PkgConfig::OPUS)
|
||||
endif()
|
||||
+16
-1
@@ -82,7 +82,7 @@
|
||||
"name": "ffmpeg",
|
||||
"package": "FFmpeg",
|
||||
"repo": "crueter-ci/FFmpeg",
|
||||
"version": "9.0.1-1788303113-bf1b838f2a"
|
||||
"version": "9.0.1-1788120736-bf1b838f2a"
|
||||
},
|
||||
"fmt": {
|
||||
"hash": "f0da82c545b01692e9fd30fdfb613dbb8dd9716983dcd0ff19ac2a8d36f74beb5540ef38072fdecc1e34191b3682a8542ecbf3a61ef287dbba0a2679d4e023f2",
|
||||
@@ -210,6 +210,21 @@
|
||||
"repo": "jimmy-park/openssl-cmake",
|
||||
"version": "3.6.2"
|
||||
},
|
||||
"opus": {
|
||||
"find_args": "MODULE",
|
||||
"hash": "9506147b0de35befda8633ff272981cc2575c860874791bd455b752f797fd7dbd1079f0ba42ccdd7bb1fe6773fa5e84b3d75667c2883dd1fb2d0e4a5fa4f8387",
|
||||
"min_version": "1.3",
|
||||
"options": [
|
||||
"OPUS_PRESUME_NEON ON"
|
||||
],
|
||||
"package": "Opus",
|
||||
"patches": [
|
||||
"0001-disable-clang-runtime-neon.patch",
|
||||
"0002-no-install.patch"
|
||||
],
|
||||
"repo": "xiph/opus",
|
||||
"version": "a3f0ec02b3"
|
||||
},
|
||||
"quazip": {
|
||||
"hash": "609c240c7f029ac26a37d8fbab51bc16284e05e128b78b9b9c0e95d083538c36047a67d682759ac990e4adb0eeb90f04f1ea7fe2253bbda7e7e3bcce32e53dd8",
|
||||
"min_version": "1.3",
|
||||
|
||||
+10
-9
@@ -60,6 +60,7 @@ All other dependencies will be downloaded and built by [CPM](https://github.com/
|
||||
* [ZLIB](https://www.zlib.net/) 1.2+
|
||||
* [zstd](https://facebook.github.io/zstd/) 1.5+
|
||||
* [enet](http://enet.bespin.org/) 1.3+
|
||||
* [Opus](https://opus-codec.org/) 1.3+
|
||||
|
||||
Vulkan 1.3.274+ is also needed:
|
||||
|
||||
@@ -120,7 +121,7 @@ sudo emerge -a \
|
||||
dev-libs/boost dev-libs/openssl dev-libs/discord-rpc \
|
||||
dev-util/spirv-tools dev-util/spirv-headers dev-util/vulkan-headers \
|
||||
dev-util/vulkan-utility-libraries dev-util/glslang \
|
||||
media-gfx/renderdoc media-libs/libva media-video/ffmpeg \
|
||||
media-gfx/renderdoc media-libs/libva media-libs/opus media-video/ffmpeg \
|
||||
media-libs/VulkanMemoryAllocator media-libs/libsdl3 media-libs/cubeb \
|
||||
net-libs/enet \
|
||||
sys-libs/zlib \
|
||||
@@ -152,7 +153,7 @@ Required USE flags:
|
||||
<summary>Arch Linux</summary>
|
||||
|
||||
```sh
|
||||
sudo pacman -Syu --needed base-devel boost catch2 cmake enet ffmpeg fmt git glslang libzip lz4 ninja nlohmann-json openssl qt6-base qt6-multimedia qt6-charts sdl3 zlib zstd zip unzip vulkan-headers vulkan-utility-libraries libusb spirv-tools spirv-headers
|
||||
sudo pacman -Syu --needed base-devel boost catch2 cmake enet ffmpeg fmt git glslang libzip lz4 ninja nlohmann-json openssl opus qt6-base qt6-multimedia qt6-charts sdl3 zlib zstd zip unzip vulkan-headers vulkan-utility-libraries libusb spirv-tools spirv-headers
|
||||
```
|
||||
|
||||
* Building with QT Web Engine requires `qt6-webengine` as well.
|
||||
@@ -165,7 +166,7 @@ sudo pacman -Syu --needed base-devel boost catch2 cmake enet ffmpeg fmt git glsl
|
||||
<summary>Ubuntu, Debian, Mint Linux</summary>
|
||||
|
||||
```sh
|
||||
sudo apt-get install autoconf cmake g++ gcc git glslang-tools 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 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 qt6-tools-dev qt6-charts-dev libvulkan-dev spirv-tools spirv-headers libusb-1.0-0-dev libxbyak-dev libboost-dev libboost-fiber-dev libboost-context-dev libsdl3-dev libasound2t64 vulkan-utility-libraries-dev
|
||||
sudo apt-get install autoconf cmake g++ gcc git glslang-tools 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 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 qt6-tools-dev qt6-charts-dev libvulkan-dev spirv-tools spirv-headers libusb-1.0-0-dev libxbyak-dev libboost-dev libboost-fiber-dev libboost-context-dev libsdl3-dev libopus-dev libasound2t64 vulkan-utility-libraries-dev
|
||||
```
|
||||
|
||||
* Ubuntu 26.04, Linux Mint 22.3, or Debian 13 or later is required.
|
||||
@@ -212,7 +213,7 @@ First, enable the community repository; [see here](https://wiki.alpinelinux.org/
|
||||
# Enable the community repository
|
||||
setup-apkrepos -c
|
||||
# Install
|
||||
apk add g++ git cmake make mesa-dev qt6-qtbase-dev qt6-qtbase-private-dev libquazip1-qt6 ffmpeg-dev qt6-charts-dev libusb-dev libtool boost-dev sdl3-dev zstd-dev vulkan-utility-libraries spirv-tools-dev openssl-dev nlohmann-json lz4-dev jq patch
|
||||
apk add g++ git cmake make mesa-dev qt6-qtbase-dev qt6-qtbase-private-dev libquazip1-qt6 ffmpeg-dev qt6-charts-dev libusb-dev libtool boost-dev sdl3-dev zstd-dev vulkan-utility-libraries spirv-tools-dev openssl-dev nlohmann-json lz4-dev opus-dev jq patch
|
||||
```
|
||||
|
||||
</details>
|
||||
@@ -260,7 +261,7 @@ brew install molten-vk
|
||||
|
||||
As root run:
|
||||
```sh
|
||||
pkg install devel/cmake devel/sdl3 devel/boost-libs devel/catch2 devel/libfmt devel/nlohmann-json devel/ninja devel/nasm devel/autoconf devel/pkgconf devel/qt6-base x11-toolkits/qt6-charts devel/simpleini net/enet multimedia/ffnvcodec-headers multimedia/ffmpeg archivers/liblz4 lang/gcc12 graphics/glslang graphics/vulkan-utility-libraries graphics/spirv-tools www/cpp-httplib graphics/vulkan-utility-libraries graphics/vulkan-headers graphics/spirv-headers quazip-qt6
|
||||
pkg install devel/cmake devel/sdl3 devel/boost-libs devel/catch2 devel/libfmt devel/nlohmann-json devel/ninja devel/nasm devel/autoconf devel/pkgconf devel/qt6-base x11-toolkits/qt6-charts devel/simpleini net/enet multimedia/ffnvcodec-headers multimedia/ffmpeg audio/opus archivers/liblz4 lang/gcc12 graphics/glslang graphics/vulkan-utility-libraries graphics/spirv-tools www/cpp-httplib graphics/vulkan-utility-libraries graphics/vulkan-headers graphics/spirv-headers quazip-qt6
|
||||
```
|
||||
|
||||
If using FreeBSD 12 or prior, use `devel/pkg-config` instead.
|
||||
@@ -274,7 +275,7 @@ If using FreeBSD 12 or prior, use `devel/pkg-config` instead.
|
||||
For NetBSD +10.1:
|
||||
|
||||
```sh
|
||||
pkgin install git cmake boost fmtlib SDL3 catch2 libjwt spirv-headers spirv-tools ffmpeg7 libva nlohmann-json jq qt6-qtbase qt6-qtcharts qt6-qtmultimedia qt6-qttools cpp-httplib lz4 vulkan-headers nasm autoconf enet pkg-config libusb1 libcxx frozen
|
||||
pkgin install git cmake boost fmtlib SDL3 catch2 libjwt spirv-headers spirv-tools ffmpeg7 libva nlohmann-json jq libopus qt6-qtbase qt6-qtcharts qt6-qtmultimedia qt6-qttools cpp-httplib lz4 vulkan-headers nasm autoconf enet pkg-config libusb1 libcxx frozen
|
||||
```
|
||||
|
||||
[Caveats](./Caveats.md#netbsd).
|
||||
@@ -305,7 +306,7 @@ pkg install gcc14 git cmake unzip nasm autoconf bash pkgconf ffmpeg glslang gmak
|
||||
<summary>OpenIndiana</summary>
|
||||
|
||||
```sh
|
||||
sudo pkg install git cmake qt6 boost glslang libzip library/lz4 libusb-1 nlohmann-json openssl sdl3 zlib compress/zstd unzip pkg-config nasm autoconf mesa library/libdrm header-drm developer/fmt
|
||||
sudo pkg install git cmake qt6 boost glslang libzip library/lz4 libusb-1 nlohmann-json openssl opus sdl3 zlib compress/zstd unzip pkg-config nasm autoconf mesa library/libdrm header-drm developer/fmt
|
||||
```
|
||||
|
||||
[Caveats](./Caveats.md#openindiana).
|
||||
@@ -329,7 +330,7 @@ sudo pkgin install git cmake autoconf build-essential libusb-1 nasm gcc13
|
||||
|
||||
```sh
|
||||
BASE="git make autoconf libtool automake-wrapper jq patch"
|
||||
MINGW="qt6-base qt6-charts qt6-tools qt6-translations qt6-svg cmake toolchain clang python-pip openssl vulkan-memory-allocator vulkan-devel glslang boost fmt lz4 nlohmann-json zlib zstd enet libusb openssl SDL3"
|
||||
MINGW="qt6-base qt6-charts qt6-tools qt6-translations qt6-svg cmake toolchain clang python-pip openssl vulkan-memory-allocator vulkan-devel glslang boost fmt lz4 nlohmann-json zlib zstd enet opus libusb openssl SDL3"
|
||||
# Either x86_64 or clang-aarch64 (Windows on ARM)
|
||||
packages="$BASE"
|
||||
for pkg in $MINGW; do
|
||||
@@ -355,7 +356,7 @@ pacman -Syuu --needed --noconfirm $packages
|
||||
<summary>HaikuOS</summary>
|
||||
|
||||
```sh
|
||||
pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel boost1.90_devel vulkan_devel qt6_base_devel qt6_declarative_devel libsdl3_devel ffmpeg7_devel libx11_devel enet_devel catch2_devel quazip1_qt5_devel qt6_5compat_devel glslang qt6_devel qt6_charts_devel cubeb_devel simpleini quazip_qt6_devel
|
||||
pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel boost1.90_devel vulkan_devel qt6_base_devel qt6_declarative_devel libsdl3_devel ffmpeg7_devel libx11_devel enet_devel catch2_devel quazip1_qt5_devel qt6_5compat_devel glslang qt6_devel qt6_charts_devel cubeb_devel simpleini quazip_qt6_devel
|
||||
```
|
||||
|
||||
[Caveats](./Caveats.md#haikuos).
|
||||
|
||||
@@ -12,7 +12,7 @@ pkgs.mkShellNoCC {
|
||||
git cmake clang gnumake patch jq pkg-config
|
||||
# libraries
|
||||
openssl boost fmt nlohmann_json lz4 zlib zstd
|
||||
enet vulkan-headers vulkan-utility-libraries
|
||||
enet libopus vulkan-headers vulkan-utility-libraries
|
||||
spirv-tools spirv-headers vulkan-loader unzip
|
||||
glslang python3 httplib cpp-jwt ffmpeg-headless
|
||||
libusb1 cubeb
|
||||
|
||||
@@ -79,6 +79,13 @@ class LicensesFragment : Fragment() {
|
||||
R.string.license_ffmpeg_copyright,
|
||||
R.string.license_ffmpeg_text
|
||||
),
|
||||
License(
|
||||
R.string.license_opus,
|
||||
R.string.license_opus_description,
|
||||
R.string.license_opus_link,
|
||||
R.string.license_opus_copyright,
|
||||
R.string.license_opus_text
|
||||
),
|
||||
License(
|
||||
R.string.license_sirit,
|
||||
R.string.license_sirit_description,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
@@ -37,7 +37,7 @@ class Game(
|
||||
|
||||
val settingsName: String
|
||||
get() {
|
||||
val programIdLong = programId.toLongOrNull() ?: 0L
|
||||
val programIdLong = programId.toLong()
|
||||
return if (programIdLong == 0L) {
|
||||
FileUtil.getFilename(Uri.parse(path))
|
||||
} else {
|
||||
@@ -47,7 +47,7 @@ class Game(
|
||||
|
||||
val programIdHex: String
|
||||
get() {
|
||||
val programIdLong = programId.toLongOrNull() ?: 0L
|
||||
val programIdLong = programId.toLong()
|
||||
return if (programIdLong == 0L) {
|
||||
"0"
|
||||
} else {
|
||||
|
||||
@@ -1783,6 +1783,51 @@ RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
</string>
|
||||
<string name="license_opus" translatable="false">Opus</string>
|
||||
<string name="license_opus_description" translatable="false">Modern audio compression for the internet</string>
|
||||
<string name="license_opus_link" translatable="false">https://github.com/xiph/opus</string>
|
||||
<string name="license_opus_copyright" translatable="false">Copyright 2001–2011 Xiph.Org, Skype Limited, Octasic, Jean-Marc Valin, Timothy B. Terriberry, CSIRO, Gregory Maxwell, Mark Borgerding, Erik de Castro Lopo</string>
|
||||
<string name="license_opus_text" translatable="false">
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:\n\n
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.\n\n
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.\n\n
|
||||
|
||||
- Neither the name of Internet Society, IETF or IETF Trust, nor the
|
||||
names of specific contributors, may be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.\n\n
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS\'\' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n
|
||||
|
||||
Opus is subject to the royalty-free patent licenses which are
|
||||
specified at:\n\n
|
||||
|
||||
Xiph.Org Foundation:
|
||||
https://datatracker.ietf.org/ipr/1524/ \n\n
|
||||
|
||||
Microsoft Corporation:
|
||||
https://datatracker.ietf.org/ipr/1914/ \n\n
|
||||
|
||||
Broadcom Corporation:
|
||||
https://datatracker.ietf.org/ipr/1526/
|
||||
</string>
|
||||
<string name="license_sirit" translatable="false">Sirit</string>
|
||||
<string name="license_sirit_description" translatable="false">A runtime SPIR-V assembler</string>
|
||||
|
||||
@@ -15,6 +15,10 @@ add_library(audio_core STATIC
|
||||
adsp/apps/audio_renderer/command_list_processor.h
|
||||
adsp/apps/opus/opus_decoder.cpp
|
||||
adsp/apps/opus/opus_decoder.h
|
||||
adsp/apps/opus/opus_decode_object.cpp
|
||||
adsp/apps/opus/opus_decode_object.h
|
||||
adsp/apps/opus/opus_multistream_decode_object.cpp
|
||||
adsp/apps/opus/opus_multistream_decode_object.h
|
||||
adsp/apps/opus/shared_memory.h
|
||||
audio_core.cpp
|
||||
audio_core.h
|
||||
@@ -222,14 +226,8 @@ else()
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-sign-conversion>)
|
||||
endif()
|
||||
|
||||
if (YUZU_USE_EXTERNAL_FFMPEG)
|
||||
add_dependencies(audio_core ffmpeg-build)
|
||||
endif()
|
||||
target_include_directories(audio_core PUBLIC ${FFmpeg_INCLUDE_DIR})
|
||||
target_link_libraries(audio_core PRIVATE ${FFmpeg_LIBRARIES})
|
||||
target_link_options(audio_core PRIVATE ${FFmpeg_LDFLAGS})
|
||||
|
||||
target_link_libraries(audio_core PUBLIC common core)
|
||||
target_include_directories(audio_core PRIVATE ${OPUS_INCLUDE_DIRS})
|
||||
target_link_libraries(audio_core PUBLIC common core Opus::opus)
|
||||
|
||||
if (ENABLE_CUBEB)
|
||||
target_sources(audio_core PRIVATE
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/adsp/apps/opus/opus_decode_object.h"
|
||||
#include "common/assert.h"
|
||||
|
||||
namespace AudioCore::ADSP::OpusDecoder {
|
||||
namespace {
|
||||
bool IsValidChannelCount(u32 channel_count) {
|
||||
return channel_count == 1 || channel_count == 2;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) {
|
||||
if (!IsValidChannelCount(channel_count)) {
|
||||
return 0;
|
||||
}
|
||||
return static_cast<u32>(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count);
|
||||
}
|
||||
|
||||
OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) {
|
||||
auto* new_decoder = reinterpret_cast<OpusDecodeObject*>(buffer);
|
||||
auto* comparison = reinterpret_cast<OpusDecodeObject*>(buffer2);
|
||||
|
||||
if (new_decoder->magic == DecodeObjectMagic) {
|
||||
if (!new_decoder->initialized ||
|
||||
(new_decoder->initialized && new_decoder->self == comparison)) {
|
||||
new_decoder->state_valid = true;
|
||||
}
|
||||
} else {
|
||||
new_decoder->initialized = false;
|
||||
new_decoder->state_valid = true;
|
||||
}
|
||||
return *new_decoder;
|
||||
}
|
||||
|
||||
s32 OpusDecodeObject::InitializeDecoder(u32 sample_rate, u32 channel_count) {
|
||||
if (!state_valid) {
|
||||
return OPUS_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (initialized) {
|
||||
return OPUS_OK;
|
||||
}
|
||||
|
||||
// Unfortunately libopus does not expose the OpusDecoder struct publicly, so we can't include
|
||||
// it in this class. Nintendo does not allocate memory, which is why we have a workbuffer
|
||||
// provided.
|
||||
// We could use _create and have libopus allocate it for us, but then we have to separately
|
||||
// track which decoder is being used between this and multistream in order to call the correct
|
||||
// destroy from the host side.
|
||||
// This is a bit cringe, but is safe as these objects are only ever initialized inside the given
|
||||
// workbuffer, and GetWorkBufferSize will guarantee there's enough space to follow.
|
||||
decoder = (LibOpusDecoder*)(this + 1);
|
||||
s32 ret = opus_decoder_init(decoder, sample_rate, channel_count);
|
||||
if (ret == OPUS_OK) {
|
||||
magic = DecodeObjectMagic;
|
||||
initialized = true;
|
||||
state_valid = true;
|
||||
self = this;
|
||||
final_range = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
s32 OpusDecodeObject::Shutdown() {
|
||||
if (!state_valid) {
|
||||
return OPUS_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (initialized) {
|
||||
magic = 0x0;
|
||||
initialized = false;
|
||||
state_valid = false;
|
||||
self = nullptr;
|
||||
final_range = 0;
|
||||
decoder = nullptr;
|
||||
}
|
||||
return OPUS_OK;
|
||||
}
|
||||
|
||||
s32 OpusDecodeObject::ResetDecoder() {
|
||||
return opus_decoder_ctl(decoder, OPUS_RESET_STATE);
|
||||
}
|
||||
|
||||
s32 OpusDecodeObject::Decode(u32& out_sample_count, u64 output_data, u64 output_data_size,
|
||||
u64 input_data, u64 input_data_size) {
|
||||
ASSERT(initialized);
|
||||
out_sample_count = 0;
|
||||
|
||||
if (!state_valid) {
|
||||
return OPUS_INVALID_STATE;
|
||||
}
|
||||
|
||||
auto ret_code_or_samples = opus_decode(
|
||||
decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size),
|
||||
reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0);
|
||||
|
||||
if (ret_code_or_samples < OPUS_OK) {
|
||||
return ret_code_or_samples;
|
||||
}
|
||||
|
||||
out_sample_count = ret_code_or_samples;
|
||||
return opus_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range);
|
||||
}
|
||||
|
||||
} // namespace AudioCore::ADSP::OpusDecoder
|
||||
@@ -0,0 +1,38 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <opus.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore::ADSP::OpusDecoder {
|
||||
using LibOpusDecoder = ::OpusDecoder;
|
||||
static constexpr u32 DecodeObjectMagic = 0xDEADBEEF;
|
||||
|
||||
class OpusDecodeObject {
|
||||
public:
|
||||
static u32 GetWorkBufferSize(u32 channel_count);
|
||||
static OpusDecodeObject& Initialize(u64 buffer, u64 buffer2);
|
||||
|
||||
s32 InitializeDecoder(u32 sample_rate, u32 channel_count);
|
||||
s32 Shutdown();
|
||||
s32 ResetDecoder();
|
||||
s32 Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, u64 input_data,
|
||||
u64 input_data_size);
|
||||
u32 GetFinalRange() const noexcept {
|
||||
return final_range;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 magic;
|
||||
bool initialized;
|
||||
bool state_valid;
|
||||
OpusDecodeObject* self;
|
||||
u32 final_range;
|
||||
LibOpusDecoder* decoder;
|
||||
};
|
||||
static_assert(std::is_trivially_constructible_v<OpusDecodeObject>);
|
||||
|
||||
} // namespace AudioCore::ADSP::OpusDecoder
|
||||
@@ -5,388 +5,55 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
|
||||
extern "C" {
|
||||
#include <libswresample/swresample.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavcodec/codec.h>
|
||||
#include <libavcodec/packet.h>
|
||||
#include <libavutil/channel_layout.h>
|
||||
#include <libavutil/frame.h>
|
||||
#include <libavutil/opt.h>
|
||||
#include <libavutil/samplefmt.h>
|
||||
}
|
||||
|
||||
#include "audio_core/adsp/apps/opus/opus_decode_object.h"
|
||||
#include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h"
|
||||
#include "audio_core/adsp/apps/opus/shared_memory.h"
|
||||
#include "audio_core/audio_core.h"
|
||||
#include "audio_core/common/common.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/service/audio/errors.h"
|
||||
|
||||
namespace AudioCore::ADSP::OpusDecoder {
|
||||
|
||||
namespace {
|
||||
constexpr u32 OPUS_STREAM_COUNT_MAX = 255;
|
||||
// https://git.ffmpeg.org/gitweb/ffmpeg.git/blob_plain/HEAD:/libavcodec/libopusdec.c
|
||||
constexpr u32 OPUS_HEAD_SIZE = 19;
|
||||
constexpr u32 OPUS_MAX_CHANNELS = 2;
|
||||
constexpr size_t OpusStreamCountMax = 255;
|
||||
|
||||
bool IsValidChannelCount(u32 channel_count) {
|
||||
return channel_count >= 1 || channel_count <= OPUS_MAX_CHANNELS;
|
||||
return channel_count == 1 || channel_count == 2;
|
||||
}
|
||||
|
||||
bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
|
||||
return total_stream_count > 0 && total_stream_count <= OPUS_STREAM_COUNT_MAX
|
||||
&& s32(stereo_stream_count) >= 0 && stereo_stream_count <= total_stream_count;
|
||||
bool IsValidMultiStreamChannelCount(u32 channel_count) {
|
||||
return channel_count <= OpusStreamCountMax;
|
||||
}
|
||||
|
||||
class OpusGenericDecodeObject {
|
||||
public:
|
||||
static u32 GetWorkBufferSizeMultistream(u32 total_stream_count, u32 stereo_stream_count) {
|
||||
if (IsValidStreamCounts(total_stream_count, stereo_stream_count))
|
||||
return 48 + 2556 * (total_stream_count * stereo_stream_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 GetWorkBufferSize(u32 channel_count) {
|
||||
if (channel_count == 1 || channel_count == 2)
|
||||
return 48 + 16 * channel_count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// idempotency of initialize is guaranteed
|
||||
Result InitializeDecoder(u32 sample_rate, u32 total_stream_count, u32 channel_count, u32 stereo_stream_count, u8 const* mappings) {
|
||||
// prefer libopus, ffmpeg docs say to use libopus **if** available
|
||||
// However, native opus can also work with swrescale:
|
||||
// it uses planarfloat, we can resample to s16
|
||||
AVCodec const* codec = avcodec_find_decoder_by_name("libopus");
|
||||
bool is_libopus = codec != nullptr;
|
||||
if (!codec) {
|
||||
LOG_WARNING(Audio_DSP, "using ffmpeg native opus decoder");
|
||||
codec = avcodec_find_decoder(AV_CODEC_ID_OPUS);
|
||||
}
|
||||
if (codec) {
|
||||
if ((avc = avc ? avc : avcodec_alloc_context3(codec))) {
|
||||
if (is_libopus) {
|
||||
const std::array<u8, 2> mapping_arr{0, 1};
|
||||
mappings = mappings ? mappings : mapping_arr.data();
|
||||
|
||||
// freed by avcodec_context_free()
|
||||
u8 *edata = reinterpret_cast<u8*>(av_mallocz(OPUS_HEAD_SIZE + 2 * OPUS_MAX_CHANNELS + AV_INPUT_BUFFER_PADDING_SIZE));
|
||||
ASSERT(edata);
|
||||
edata[9] = u8(channel_count); //channels
|
||||
edata[10] = u8(0); //opus->pre_skip
|
||||
edata[16] = u8(0); //gain_db
|
||||
edata[18] = u8(0); //channel_map
|
||||
edata[OPUS_HEAD_SIZE + 0] = u8(total_stream_count);
|
||||
edata[OPUS_HEAD_SIZE + 1] = u8(stereo_stream_count);
|
||||
if (channel_count >= 1) edata[OPUS_HEAD_SIZE + 2] = mappings[0];
|
||||
if (channel_count >= 2) edata[OPUS_HEAD_SIZE + 3] = mappings[1];
|
||||
avc->extradata = edata;
|
||||
avc->extradata_size = OPUS_HEAD_SIZE + 2 * channel_count;
|
||||
}
|
||||
|
||||
|
||||
// FFmpeg hardcodes sample rate
|
||||
avc->sample_rate = sample_rate;
|
||||
avc->request_sample_fmt = AV_SAMPLE_FMT_S16;
|
||||
av_channel_layout_default(&avc->ch_layout, channel_count);
|
||||
if (avcodec_open2(avc, codec, nullptr) >= 0) {
|
||||
avpkt = av_packet_alloc();
|
||||
frame = av_frame_alloc();
|
||||
return ResultSuccess;
|
||||
} else {
|
||||
avcodec_free_context(&avc);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Service::Audio::ResultLibOpusInternalError;
|
||||
}
|
||||
|
||||
Result Shutdown() {
|
||||
avcodec_free_context(&avc);
|
||||
av_frame_free(&frame);
|
||||
av_packet_free(&avpkt);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ResetDecoder() {
|
||||
if (avc) {
|
||||
if (avcodec_is_open(avc)) avcodec_flush_buffers(avc);
|
||||
return ResultSuccess;
|
||||
}
|
||||
return Service::Audio::ResultLibOpusInvalidState;
|
||||
}
|
||||
|
||||
Result Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, u64 input_data, u64 input_data_size) {
|
||||
out_sample_count = 0;
|
||||
if (avc) {
|
||||
int rem_output_bytes = int(output_data_size);
|
||||
while (rem_output_bytes > 0) {
|
||||
int r = avcodec_receive_frame(avc, frame);
|
||||
if (r == AVERROR(EAGAIN)) {
|
||||
av_packet_unref(avpkt);
|
||||
av_new_packet(avpkt, int(input_data_size));
|
||||
std::memcpy(avpkt->data, reinterpret_cast<const u8*>(input_data), input_data_size);
|
||||
r = avcodec_send_packet(avc, avpkt);
|
||||
ASSERT(r >= 0);
|
||||
} else if (r == AVERROR_EOF) {
|
||||
break;
|
||||
} else if (r >= 0) {
|
||||
auto const bsize = av_samples_get_buffer_size(nullptr, frame->ch_layout.nb_channels, frame->nb_samples, AV_SAMPLE_FMT_S16, 1);
|
||||
if (frame->format == AV_SAMPLE_FMT_S16) {
|
||||
std::memcpy(reinterpret_cast<s16*>(output_data) + (int(output_data_size) - rem_output_bytes), frame->data[0], size_t(bsize));
|
||||
} else {
|
||||
SwrContext *swr = nullptr;
|
||||
if (swr_alloc_set_opts2(
|
||||
&swr,
|
||||
&avc->ch_layout,
|
||||
AV_SAMPLE_FMT_S16,
|
||||
48000,
|
||||
&avc->ch_layout,
|
||||
(enum AVSampleFormat)frame->format,
|
||||
48000,
|
||||
0,
|
||||
nullptr
|
||||
) >= 0) {
|
||||
if (swr_init(swr) >= 0) {
|
||||
AVFrame *s16_frame = av_frame_alloc();
|
||||
s16_frame->format = AV_SAMPLE_FMT_S16;
|
||||
s16_frame->sample_rate = frame->sample_rate;
|
||||
av_channel_layout_copy(&s16_frame->ch_layout, &frame->ch_layout);
|
||||
s16_frame->nb_samples = frame->nb_samples;
|
||||
av_frame_get_buffer(s16_frame, 0);
|
||||
swr_convert(swr, s16_frame->data, s16_frame->nb_samples, (const uint8_t **)frame->data, frame->nb_samples);
|
||||
std::memcpy(reinterpret_cast<s16*>(output_data) + (int(output_data_size) - rem_output_bytes), s16_frame->data[0], size_t(bsize));
|
||||
swr_free(&swr);
|
||||
}
|
||||
}
|
||||
}
|
||||
out_sample_count += frame->nb_samples;
|
||||
rem_output_bytes -= bsize;
|
||||
} else {
|
||||
LOG_ERROR(Audio_DSP, "{}", r);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT(rem_output_bytes == 0 && "remaining bytes!");
|
||||
return ResultSuccess;
|
||||
}
|
||||
return Service::Audio::ResultLibOpusInvalidState;
|
||||
}
|
||||
|
||||
AVCodecContext* avc = nullptr;
|
||||
AVPacket* avpkt = nullptr;
|
||||
AVFrame* frame = nullptr;
|
||||
};
|
||||
bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 stereo_stream_count) {
|
||||
return IsValidMultiStreamChannelCount(total_stream_count) && total_stream_count > 0 &&
|
||||
stereo_stream_count >= 0 && stereo_stream_count <= total_stream_count;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
OpusDecoder::OpusDecoder(Core::System& system) {
|
||||
dsp_thread = std::jthread([this, &system](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("DSP_OpusDecoder");
|
||||
if (Receive(Direction::DSP, stop_token) != Message::Start) {
|
||||
LOG_ERROR(Service_Audio, "DSP OpusDecoder failed to receive Start message. Opus initialization failed.");
|
||||
return;
|
||||
}
|
||||
Send(Direction::Host, Message::StartOK);
|
||||
|
||||
// Main OpusDecoder thread, responsible for processing the incoming Opus packets.
|
||||
::Common::unordered_map<u64, OpusGenericDecodeObject> decode_objects;
|
||||
while (!stop_token.stop_requested()) {
|
||||
auto msg = Receive(Direction::DSP, stop_token);
|
||||
switch (msg) {
|
||||
case Shutdown:
|
||||
Send(Direction::Host, Message::ShutdownOK);
|
||||
return;
|
||||
case GetWorkBufferSize: {
|
||||
auto channel_count = s32(shared_memory->host_send_data[0]);
|
||||
|
||||
ASSERT(IsValidChannelCount(channel_count));
|
||||
|
||||
shared_memory->dsp_return_data[0] = OpusGenericDecodeObject::GetWorkBufferSize(channel_count);
|
||||
Send(Direction::Host, Message::GetWorkBufferSizeOK);
|
||||
break;
|
||||
}
|
||||
case InitializeDecodeObject: {
|
||||
auto buffer = shared_memory->host_send_data[0];
|
||||
auto buffer_size = shared_memory->host_send_data[1];
|
||||
auto sample_rate = s32(shared_memory->host_send_data[2]);
|
||||
auto channel_count = s32(shared_memory->host_send_data[3]);
|
||||
|
||||
ASSERT(sample_rate >= 0);
|
||||
ASSERT(IsValidChannelCount(channel_count));
|
||||
ASSERT(buffer_size >= OpusGenericDecodeObject::GetWorkBufferSize(channel_count));
|
||||
|
||||
if (auto const it = decode_objects.find(buffer); it != decode_objects.end()) {
|
||||
it->second.Shutdown();
|
||||
shared_memory->dsp_return_data[0] = it->second.InitializeDecoder(sample_rate, 1, channel_count, channel_count == 2 ? 1 : 0, nullptr).raw;
|
||||
} else {
|
||||
OpusGenericDecodeObject obj{};
|
||||
shared_memory->dsp_return_data[0] = obj.InitializeDecoder(sample_rate, 1, channel_count, channel_count == 2 ? 1 : 0, nullptr).raw;
|
||||
decode_objects.insert_or_assign(buffer, obj);
|
||||
}
|
||||
Send(Direction::Host, Message::InitializeDecodeObjectOK);
|
||||
break;
|
||||
}
|
||||
case ShutdownDecodeObject: {
|
||||
auto buffer = shared_memory->host_send_data[0];
|
||||
//[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
||||
if (auto const it = decode_objects.find(buffer); it != decode_objects.end()) {
|
||||
shared_memory->dsp_return_data[0] = it->second.Shutdown().raw;
|
||||
} else {
|
||||
LOG_ERROR(Audio_DSP, "operating unregistered buffer {}", buffer);
|
||||
shared_memory->dsp_return_data[0] = Service::Audio::ResultLibOpusInvalidState.raw;
|
||||
}
|
||||
Send(Direction::Host, Message::ShutdownDecodeObjectOK);
|
||||
break;
|
||||
}
|
||||
case DecodeInterleaved: {
|
||||
auto start_time = system.CoreTiming().GetGlobalTimeUs();
|
||||
|
||||
auto buffer = shared_memory->host_send_data[0];
|
||||
auto input_data = shared_memory->host_send_data[1];
|
||||
auto input_data_size = shared_memory->host_send_data[2];
|
||||
auto output_data = shared_memory->host_send_data[3];
|
||||
auto output_data_size = shared_memory->host_send_data[4];
|
||||
//auto final_range = static_cast<u32>(shared_memory->host_send_data[5]);
|
||||
auto reset_requested = shared_memory->host_send_data[6];
|
||||
|
||||
u32 decoded_samples{0};
|
||||
|
||||
if (auto const it = decode_objects.find(buffer); it != decode_objects.end()) {
|
||||
auto res = ResultSuccess;
|
||||
if (reset_requested)
|
||||
res = it->second.ResetDecoder();
|
||||
if (res == ResultSuccess)
|
||||
res = it->second.Decode(decoded_samples, output_data, output_data_size, input_data, input_data_size);
|
||||
|
||||
auto end_time = system.CoreTiming().GetGlobalTimeUs();
|
||||
shared_memory->dsp_return_data[0] = res.raw;
|
||||
shared_memory->dsp_return_data[1] = decoded_samples;
|
||||
shared_memory->dsp_return_data[2] = (end_time - start_time).count();
|
||||
} else {
|
||||
LOG_ERROR(Audio_DSP, "operating unregistered buffer {}", buffer);
|
||||
shared_memory->dsp_return_data[0] = Service::Audio::ResultLibOpusInvalidState.raw;
|
||||
}
|
||||
Send(Direction::Host, Message::DecodeInterleavedOK);
|
||||
break;
|
||||
}
|
||||
case MapMemory: {
|
||||
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
|
||||
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
||||
Send(Direction::Host, Message::MapMemoryOK);
|
||||
break;
|
||||
}
|
||||
case UnmapMemory: {
|
||||
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
|
||||
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
||||
Send(Direction::Host, Message::UnmapMemoryOK);
|
||||
break;
|
||||
}
|
||||
case GetWorkBufferSizeForMultiStream: {
|
||||
auto total_stream_count = s32(shared_memory->host_send_data[0]);
|
||||
auto stereo_stream_count = s32(shared_memory->host_send_data[1]);
|
||||
|
||||
ASSERT(IsValidStreamCounts(total_stream_count, stereo_stream_count));
|
||||
|
||||
shared_memory->dsp_return_data[0] = OpusGenericDecodeObject::GetWorkBufferSizeMultistream(total_stream_count, stereo_stream_count);
|
||||
Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK);
|
||||
break;
|
||||
}
|
||||
case InitializeMultiStreamDecodeObject: {
|
||||
auto buffer = shared_memory->host_send_data[0];
|
||||
auto buffer_size = shared_memory->host_send_data[1];
|
||||
auto sample_rate = s32(shared_memory->host_send_data[2]);
|
||||
auto channel_count = s32(shared_memory->host_send_data[3]);
|
||||
auto total_stream_count = s32(shared_memory->host_send_data[4]);
|
||||
auto stereo_stream_count = s32(shared_memory->host_send_data[5]);
|
||||
// Nintendo seem to have a bug here, they try to use &host_send_data[6] for the channel
|
||||
// mappings, but [6] is never set, and there is not enough room in the argument data for
|
||||
// more than 40 channels, when 255 are possible.
|
||||
// It also means the mapping values are undefined, though likely always 0,
|
||||
// and the mappings given by the game are ignored. The mappings are copied to this
|
||||
// dedicated buffer host side, so let's do as intended.
|
||||
auto mappings = shared_memory->channel_mapping.data();
|
||||
|
||||
ASSERT(IsValidStreamCounts(total_stream_count, stereo_stream_count));
|
||||
ASSERT(sample_rate >= 0);
|
||||
ASSERT(buffer_size >= OpusGenericDecodeObject::GetWorkBufferSizeMultistream(total_stream_count, stereo_stream_count));
|
||||
if (auto const it = decode_objects.find(buffer); it != decode_objects.end()) {
|
||||
it->second.Shutdown();
|
||||
shared_memory->dsp_return_data[0] = it->second.InitializeDecoder(sample_rate, total_stream_count, channel_count, stereo_stream_count, mappings).raw;
|
||||
} else {
|
||||
OpusGenericDecodeObject obj{};
|
||||
shared_memory->dsp_return_data[0] = obj.InitializeDecoder(sample_rate, total_stream_count, channel_count, stereo_stream_count, mappings).raw;
|
||||
decode_objects.insert_or_assign(buffer, obj);
|
||||
}
|
||||
Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK);
|
||||
break;
|
||||
}
|
||||
case ShutdownMultiStreamDecodeObject: {
|
||||
auto buffer = shared_memory->host_send_data[0];
|
||||
//[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
||||
if (auto const it = decode_objects.find(buffer); it != decode_objects.end()) {
|
||||
shared_memory->dsp_return_data[0] = it->second.Shutdown().raw;
|
||||
} else {
|
||||
LOG_ERROR(Audio_DSP, "operating unregistered buffer {}", buffer);
|
||||
shared_memory->dsp_return_data[0] = Service::Audio::ResultLibOpusInvalidState.raw;
|
||||
}
|
||||
Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK);
|
||||
break;
|
||||
}
|
||||
case DecodeInterleavedForMultiStream: {
|
||||
auto start_time = system.CoreTiming().GetGlobalTimeUs();
|
||||
|
||||
auto buffer = shared_memory->host_send_data[0];
|
||||
auto input_data = shared_memory->host_send_data[1];
|
||||
auto input_data_size = shared_memory->host_send_data[2];
|
||||
auto output_data = shared_memory->host_send_data[3];
|
||||
auto output_data_size = shared_memory->host_send_data[4];
|
||||
//auto final_range = static_cast<u32>(shared_memory->host_send_data[5]);
|
||||
auto reset_requested = shared_memory->host_send_data[6];
|
||||
|
||||
u32 decoded_samples{0};
|
||||
|
||||
if (auto const it = decode_objects.find(buffer); it != decode_objects.end()) {
|
||||
auto res = ResultSuccess;
|
||||
if (reset_requested)
|
||||
res = it->second.ResetDecoder();
|
||||
if (res == ResultSuccess)
|
||||
res = it->second.Decode(decoded_samples, output_data, output_data_size, input_data, input_data_size);
|
||||
|
||||
auto end_time = system.CoreTiming().GetGlobalTimeUs();
|
||||
shared_memory->dsp_return_data[0] = res.raw;
|
||||
shared_memory->dsp_return_data[1] = decoded_samples;
|
||||
shared_memory->dsp_return_data[2] = (end_time - start_time).count();
|
||||
} else {
|
||||
LOG_ERROR(Audio_DSP, "operating unregistered buffer {}", buffer);
|
||||
shared_memory->dsp_return_data[0] = Service::Audio::ResultLibOpusInvalidState.raw;
|
||||
}
|
||||
Send(Direction::Host, Message::DecodeInterleavedForMultiStreamOK);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_ERROR(Audio_DSP, "Invalid OpusDecoder command {}", msg);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for (auto e : decode_objects)
|
||||
e.second.Shutdown();
|
||||
});
|
||||
OpusDecoder::OpusDecoder(Core::System& system_) : system{system_} {
|
||||
init_thread = std::jthread([this](std::stop_token stop_token) { Init(stop_token); });
|
||||
}
|
||||
|
||||
OpusDecoder::~OpusDecoder() {
|
||||
if (dsp_thread.joinable()) {
|
||||
// Shutdown the thread
|
||||
auto const stop_token = dsp_thread.get_stop_token();
|
||||
Send(Direction::DSP, Message::Shutdown);
|
||||
auto msg = Receive(Direction::Host, stop_token);
|
||||
ASSERT_MSG(msg == Message::ShutdownOK, "Expected Opus shutdown code {}, got {}", Message::ShutdownOK, msg);
|
||||
dsp_thread.request_stop();
|
||||
dsp_thread.join();
|
||||
if (!running) {
|
||||
init_thread.request_stop();
|
||||
return;
|
||||
}
|
||||
|
||||
// Shutdown the thread
|
||||
Send(Direction::DSP, Message::Shutdown);
|
||||
auto msg = Receive(Direction::Host);
|
||||
ASSERT_MSG(msg == Message::ShutdownOK, "Expected Opus shutdown code {}, got {}",
|
||||
Message::ShutdownOK, msg);
|
||||
main_thread.request_stop();
|
||||
main_thread.join();
|
||||
running = false;
|
||||
}
|
||||
|
||||
void OpusDecoder::Send(Direction dir, u32 message) {
|
||||
@@ -397,4 +64,206 @@ u32 OpusDecoder::Receive(Direction dir, std::stop_token stop_token) {
|
||||
return mailbox.Receive(dir, stop_token);
|
||||
}
|
||||
|
||||
void OpusDecoder::Init(std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("DSP_OpusDecoder_Init");
|
||||
|
||||
if (Receive(Direction::DSP, stop_token) != Message::Start) {
|
||||
LOG_ERROR(Service_Audio,
|
||||
"DSP OpusDecoder failed to receive Start message. Opus initialization failed.");
|
||||
return;
|
||||
}
|
||||
main_thread = std::jthread([this](std::stop_token st) { Main(st); });
|
||||
running = true;
|
||||
Send(Direction::Host, Message::StartOK);
|
||||
}
|
||||
|
||||
void OpusDecoder::Main(std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("DSP_OpusDecoder_Main");
|
||||
|
||||
while (!stop_token.stop_requested()) {
|
||||
auto msg = Receive(Direction::DSP, stop_token);
|
||||
switch (msg) {
|
||||
case Shutdown:
|
||||
Send(Direction::Host, Message::ShutdownOK);
|
||||
return;
|
||||
|
||||
case GetWorkBufferSize: {
|
||||
auto channel_count = static_cast<s32>(shared_memory->host_send_data[0]);
|
||||
|
||||
ASSERT(IsValidChannelCount(channel_count));
|
||||
|
||||
shared_memory->dsp_return_data[0] = OpusDecodeObject::GetWorkBufferSize(channel_count);
|
||||
Send(Direction::Host, Message::GetWorkBufferSizeOK);
|
||||
} break;
|
||||
|
||||
case InitializeDecodeObject: {
|
||||
auto buffer = shared_memory->host_send_data[0];
|
||||
auto buffer_size = shared_memory->host_send_data[1];
|
||||
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
|
||||
auto channel_count = static_cast<s32>(shared_memory->host_send_data[3]);
|
||||
|
||||
ASSERT(sample_rate >= 0);
|
||||
ASSERT(IsValidChannelCount(channel_count));
|
||||
ASSERT(buffer_size >= OpusDecodeObject::GetWorkBufferSize(channel_count));
|
||||
|
||||
auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
|
||||
shared_memory->dsp_return_data[0] =
|
||||
decoder_object.InitializeDecoder(sample_rate, channel_count);
|
||||
|
||||
Send(Direction::Host, Message::InitializeDecodeObjectOK);
|
||||
} break;
|
||||
|
||||
case ShutdownDecodeObject: {
|
||||
auto buffer = shared_memory->host_send_data[0];
|
||||
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
||||
|
||||
auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
|
||||
shared_memory->dsp_return_data[0] = decoder_object.Shutdown();
|
||||
|
||||
Send(Direction::Host, Message::ShutdownDecodeObjectOK);
|
||||
} break;
|
||||
|
||||
case DecodeInterleaved: {
|
||||
auto start_time = system.CoreTiming().GetGlobalTimeUs();
|
||||
|
||||
auto buffer = shared_memory->host_send_data[0];
|
||||
auto input_data = shared_memory->host_send_data[1];
|
||||
auto input_data_size = shared_memory->host_send_data[2];
|
||||
auto output_data = shared_memory->host_send_data[3];
|
||||
auto output_data_size = shared_memory->host_send_data[4];
|
||||
auto final_range = static_cast<u32>(shared_memory->host_send_data[5]);
|
||||
auto reset_requested = shared_memory->host_send_data[6];
|
||||
|
||||
u32 decoded_samples{0};
|
||||
|
||||
auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
|
||||
s32 error_code{OPUS_OK};
|
||||
if (reset_requested) {
|
||||
error_code = decoder_object.ResetDecoder();
|
||||
}
|
||||
|
||||
if (error_code == OPUS_OK) {
|
||||
error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size,
|
||||
input_data, input_data_size);
|
||||
}
|
||||
|
||||
if (error_code == OPUS_OK) {
|
||||
if (final_range && decoder_object.GetFinalRange() != final_range) {
|
||||
error_code = OPUS_INVALID_PACKET;
|
||||
}
|
||||
}
|
||||
|
||||
auto end_time = system.CoreTiming().GetGlobalTimeUs();
|
||||
shared_memory->dsp_return_data[0] = error_code;
|
||||
shared_memory->dsp_return_data[1] = decoded_samples;
|
||||
shared_memory->dsp_return_data[2] = (end_time - start_time).count();
|
||||
|
||||
Send(Direction::Host, Message::DecodeInterleavedOK);
|
||||
} break;
|
||||
|
||||
case MapMemory: {
|
||||
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
|
||||
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
||||
Send(Direction::Host, Message::MapMemoryOK);
|
||||
} break;
|
||||
|
||||
case UnmapMemory: {
|
||||
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
|
||||
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
||||
Send(Direction::Host, Message::UnmapMemoryOK);
|
||||
} break;
|
||||
|
||||
case GetWorkBufferSizeForMultiStream: {
|
||||
auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[0]);
|
||||
auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[1]);
|
||||
|
||||
ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count));
|
||||
|
||||
shared_memory->dsp_return_data[0] = OpusMultiStreamDecodeObject::GetWorkBufferSize(
|
||||
total_stream_count, stereo_stream_count);
|
||||
Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK);
|
||||
} break;
|
||||
|
||||
case InitializeMultiStreamDecodeObject: {
|
||||
auto buffer = shared_memory->host_send_data[0];
|
||||
auto buffer_size = shared_memory->host_send_data[1];
|
||||
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
|
||||
auto channel_count = static_cast<s32>(shared_memory->host_send_data[3]);
|
||||
auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[4]);
|
||||
auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[5]);
|
||||
// Nintendo seem to have a bug here, they try to use &host_send_data[6] for the channel
|
||||
// mappings, but [6] is never set, and there is not enough room in the argument data for
|
||||
// more than 40 channels, when 255 are possible.
|
||||
// It also means the mapping values are undefined, though likely always 0,
|
||||
// and the mappings given by the game are ignored. The mappings are copied to this
|
||||
// dedicated buffer host side, so let's do as intended.
|
||||
auto mappings = shared_memory->channel_mapping.data();
|
||||
|
||||
ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count));
|
||||
ASSERT(sample_rate >= 0);
|
||||
ASSERT(buffer_size >= OpusMultiStreamDecodeObject::GetWorkBufferSize(
|
||||
total_stream_count, stereo_stream_count));
|
||||
|
||||
auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
|
||||
shared_memory->dsp_return_data[0] = decoder_object.InitializeDecoder(
|
||||
sample_rate, total_stream_count, channel_count, stereo_stream_count, mappings);
|
||||
|
||||
Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK);
|
||||
} break;
|
||||
|
||||
case ShutdownMultiStreamDecodeObject: {
|
||||
auto buffer = shared_memory->host_send_data[0];
|
||||
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
||||
|
||||
auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
|
||||
shared_memory->dsp_return_data[0] = decoder_object.Shutdown();
|
||||
|
||||
Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK);
|
||||
} break;
|
||||
|
||||
case DecodeInterleavedForMultiStream: {
|
||||
auto start_time = system.CoreTiming().GetGlobalTimeUs();
|
||||
|
||||
auto buffer = shared_memory->host_send_data[0];
|
||||
auto input_data = shared_memory->host_send_data[1];
|
||||
auto input_data_size = shared_memory->host_send_data[2];
|
||||
auto output_data = shared_memory->host_send_data[3];
|
||||
auto output_data_size = shared_memory->host_send_data[4];
|
||||
auto final_range = static_cast<u32>(shared_memory->host_send_data[5]);
|
||||
auto reset_requested = shared_memory->host_send_data[6];
|
||||
|
||||
u32 decoded_samples{0};
|
||||
|
||||
auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
|
||||
s32 error_code{OPUS_OK};
|
||||
if (reset_requested) {
|
||||
error_code = decoder_object.ResetDecoder();
|
||||
}
|
||||
|
||||
if (error_code == OPUS_OK) {
|
||||
error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size,
|
||||
input_data, input_data_size);
|
||||
}
|
||||
|
||||
if (error_code == OPUS_OK) {
|
||||
if (final_range && decoder_object.GetFinalRange() != final_range) {
|
||||
error_code = OPUS_INVALID_PACKET;
|
||||
}
|
||||
}
|
||||
|
||||
auto end_time = system.CoreTiming().GetGlobalTimeUs();
|
||||
shared_memory->dsp_return_data[0] = error_code;
|
||||
shared_memory->dsp_return_data[1] = decoded_samples;
|
||||
shared_memory->dsp_return_data[2] = (end_time - start_time).count();
|
||||
|
||||
Send(Direction::Host, Message::DecodeInterleavedForMultiStreamOK);
|
||||
} break;
|
||||
|
||||
default:
|
||||
LOG_ERROR(Service_Audio, "Invalid OpusDecoder command {}", msg);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AudioCore::ADSP::OpusDecoder
|
||||
|
||||
@@ -6,13 +6,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include "common/container/unordered_map.h"
|
||||
#include "audio_core/adsp/apps/opus/shared_memory.h"
|
||||
#include "audio_core/adsp/mailbox.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
@@ -49,14 +48,16 @@ enum Message : u32 {
|
||||
DecodeInterleavedForMultiStreamOK = 50,
|
||||
};
|
||||
|
||||
/// @brief The AudioRenderer application running on the ADSP.
|
||||
/**
|
||||
* The AudioRenderer application running on the ADSP.
|
||||
*/
|
||||
class OpusDecoder {
|
||||
public:
|
||||
explicit OpusDecoder(Core::System& system);
|
||||
~OpusDecoder();
|
||||
|
||||
bool IsRunning() const noexcept {
|
||||
return dsp_thread.joinable();
|
||||
return running;
|
||||
}
|
||||
|
||||
void Send(Direction dir, u32 message);
|
||||
@@ -67,12 +68,28 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Initializing thread, launched at audio_core boot to avoid blocking the main emu boot thread.
|
||||
*/
|
||||
void Init(std::stop_token stop_token);
|
||||
/**
|
||||
* Main OpusDecoder thread, responsible for processing the incoming Opus packets.
|
||||
*/
|
||||
void Main(std::stop_token stop_token);
|
||||
|
||||
/// Core system
|
||||
Core::System& system;
|
||||
/// Mailbox to communicate messages with the host, drives the main thread
|
||||
Mailbox mailbox;
|
||||
/// Init thread
|
||||
std::jthread init_thread{};
|
||||
/// Main thread
|
||||
std::jthread main_thread{};
|
||||
/// The current state
|
||||
bool running{};
|
||||
/// Structure shared with the host, input data set by the host before sending a mailbox message,
|
||||
/// and the responses are written back by the OpusDecoder.
|
||||
SharedMemory* shared_memory{};
|
||||
std::jthread dsp_thread{};
|
||||
};
|
||||
|
||||
} // namespace AudioCore::ADSP::OpusDecoder
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h"
|
||||
#include "common/assert.h"
|
||||
|
||||
namespace AudioCore::ADSP::OpusDecoder {
|
||||
|
||||
namespace {
|
||||
constexpr u32 OpusStreamCountMax = 255;
|
||||
|
||||
bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
|
||||
return total_stream_count > 0 && total_stream_count <= OpusStreamCountMax &&
|
||||
static_cast<s32>(stereo_stream_count) >= 0 &&
|
||||
stereo_stream_count <= total_stream_count;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
u32 OpusMultiStreamDecodeObject::GetWorkBufferSize(u32 total_stream_count,
|
||||
u32 stereo_stream_count) {
|
||||
if (IsValidStreamCounts(total_stream_count, stereo_stream_count)) {
|
||||
return static_cast<u32>(sizeof(OpusMultiStreamDecodeObject)) +
|
||||
opus_multistream_decoder_get_size(total_stream_count, stereo_stream_count);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
OpusMultiStreamDecodeObject& OpusMultiStreamDecodeObject::Initialize(u64 buffer, u64 buffer2) {
|
||||
auto* new_decoder = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer);
|
||||
auto* comparison = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer2);
|
||||
|
||||
if (new_decoder->magic == DecodeMultiStreamObjectMagic) {
|
||||
if (!new_decoder->initialized ||
|
||||
(new_decoder->initialized && new_decoder->self == comparison)) {
|
||||
new_decoder->state_valid = true;
|
||||
}
|
||||
} else {
|
||||
new_decoder->initialized = false;
|
||||
new_decoder->state_valid = true;
|
||||
}
|
||||
return *new_decoder;
|
||||
}
|
||||
|
||||
s32 OpusMultiStreamDecodeObject::InitializeDecoder(u32 sample_rate, u32 total_stream_count,
|
||||
u32 channel_count, u32 stereo_stream_count,
|
||||
u8* mappings) {
|
||||
if (!state_valid) {
|
||||
return OPUS_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (initialized) {
|
||||
return OPUS_OK;
|
||||
}
|
||||
|
||||
// See OpusDecodeObject::InitializeDecoder for an explanation of this
|
||||
decoder = (LibOpusMSDecoder*)(this + 1);
|
||||
s32 ret = opus_multistream_decoder_init(decoder, sample_rate, channel_count, total_stream_count,
|
||||
stereo_stream_count, mappings);
|
||||
if (ret == OPUS_OK) {
|
||||
magic = DecodeMultiStreamObjectMagic;
|
||||
initialized = true;
|
||||
state_valid = true;
|
||||
self = this;
|
||||
final_range = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
s32 OpusMultiStreamDecodeObject::Shutdown() {
|
||||
if (!state_valid) {
|
||||
return OPUS_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (initialized) {
|
||||
magic = 0x0;
|
||||
initialized = false;
|
||||
state_valid = false;
|
||||
self = nullptr;
|
||||
final_range = 0;
|
||||
decoder = nullptr;
|
||||
}
|
||||
return OPUS_OK;
|
||||
}
|
||||
|
||||
s32 OpusMultiStreamDecodeObject::ResetDecoder() {
|
||||
return opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE);
|
||||
}
|
||||
|
||||
s32 OpusMultiStreamDecodeObject::Decode(u32& out_sample_count, u64 output_data,
|
||||
u64 output_data_size, u64 input_data, u64 input_data_size) {
|
||||
ASSERT(initialized);
|
||||
out_sample_count = 0;
|
||||
|
||||
if (!state_valid) {
|
||||
return OPUS_INVALID_STATE;
|
||||
}
|
||||
|
||||
auto ret_code_or_samples = opus_multistream_decode(
|
||||
decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size),
|
||||
reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0);
|
||||
|
||||
if (ret_code_or_samples < OPUS_OK) {
|
||||
return ret_code_or_samples;
|
||||
}
|
||||
|
||||
out_sample_count = ret_code_or_samples;
|
||||
return opus_multistream_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range);
|
||||
}
|
||||
|
||||
} // namespace AudioCore::ADSP::OpusDecoder
|
||||
@@ -0,0 +1,39 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <opus_multistream.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore::ADSP::OpusDecoder {
|
||||
using LibOpusMSDecoder = ::OpusMSDecoder;
|
||||
static constexpr u32 DecodeMultiStreamObjectMagic = 0xDEADBEEF;
|
||||
|
||||
class OpusMultiStreamDecodeObject {
|
||||
public:
|
||||
static u32 GetWorkBufferSize(u32 total_stream_count, u32 stereo_stream_count);
|
||||
static OpusMultiStreamDecodeObject& Initialize(u64 buffer, u64 buffer2);
|
||||
|
||||
s32 InitializeDecoder(u32 sample_rate, u32 total_stream_count, u32 channel_count,
|
||||
u32 stereo_stream_count, u8* mappings);
|
||||
s32 Shutdown();
|
||||
s32 ResetDecoder();
|
||||
s32 Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, u64 input_data,
|
||||
u64 input_data_size);
|
||||
u32 GetFinalRange() const noexcept {
|
||||
return final_range;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 magic;
|
||||
bool initialized;
|
||||
bool state_valid;
|
||||
OpusMultiStreamDecodeObject* self;
|
||||
u32 final_range;
|
||||
LibOpusMSDecoder* decoder;
|
||||
};
|
||||
static_assert(std::is_trivially_constructible_v<OpusMultiStreamDecodeObject>);
|
||||
|
||||
} // namespace AudioCore::ADSP::OpusDecoder
|
||||
@@ -1,32 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore::ADSP {
|
||||
|
||||
static constexpr u32 DECODE_OBJECT_MAGIC = 0xDEADBEEF;
|
||||
struct LibOpusDecoder {
|
||||
u32 magic;
|
||||
bool initialized;
|
||||
bool state_valid;
|
||||
LibOpusDecoder* self;
|
||||
u32 final_range;
|
||||
void* decoder;
|
||||
};
|
||||
static_assert(sizeof(LibOpusDecoder) == 32);
|
||||
|
||||
static constexpr u32 DECODE_MULTISTREAM_OBJECT_MAGIC = 0xDEADBEEF;
|
||||
struct LibOpusMultistreamDecoder {
|
||||
u32 magic;
|
||||
bool initialized;
|
||||
bool state_valid;
|
||||
LibOpusMultistreamDecoder* self;
|
||||
u32 final_range;
|
||||
void* decoder;
|
||||
};
|
||||
static_assert(sizeof(LibOpusMultistreamDecoder) == 32);
|
||||
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore::ADSP::OpusDecoder {
|
||||
|
||||
@@ -10,18 +10,39 @@
|
||||
#include "audio_core/audio_core.h"
|
||||
#include "audio_core/opus/hardware_opus.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace AudioCore::OpusDecoder {
|
||||
|
||||
namespace {
|
||||
using namespace Service::Audio;
|
||||
|
||||
static constexpr Result ResultCodeFromLibOpusErrorCode(u64 error_code) {
|
||||
s32 error{static_cast<s32>(error_code)};
|
||||
ASSERT(error <= OPUS_OK);
|
||||
switch (error) {
|
||||
case OPUS_ALLOC_FAIL:
|
||||
R_THROW(ResultLibOpusAllocFail);
|
||||
case OPUS_INVALID_STATE:
|
||||
R_THROW(ResultLibOpusInvalidState);
|
||||
case OPUS_UNIMPLEMENTED:
|
||||
R_THROW(ResultLibOpusUnimplemented);
|
||||
case OPUS_INVALID_PACKET:
|
||||
R_THROW(ResultLibOpusInvalidPacket);
|
||||
case OPUS_INTERNAL_ERROR:
|
||||
R_THROW(ResultLibOpusInternalError);
|
||||
case OPUS_BUFFER_TOO_SMALL:
|
||||
R_THROW(ResultBufferTooSmall);
|
||||
case OPUS_BAD_ARG:
|
||||
R_THROW(ResultLibOpusBadArg);
|
||||
case OPUS_OK:
|
||||
R_RETURN(ResultSuccess);
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
HardwareOpus::HardwareOpus(Core::System& system_)
|
||||
: system{system_}
|
||||
, opus_decoder{system.AudioCore().ADSP().OpusDecoder()}
|
||||
{
|
||||
: system{system_}, opus_decoder{system.AudioCore().ADSP().OpusDecoder()} {
|
||||
opus_decoder.SetSharedMemory(shared_memory);
|
||||
}
|
||||
|
||||
@@ -91,7 +112,7 @@ Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count,
|
||||
R_THROW(ResultInvalidOpusDSPReturnCode);
|
||||
}
|
||||
|
||||
R_RETURN(Result(u32(shared_memory.dsp_return_data[0])));
|
||||
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
|
||||
}
|
||||
|
||||
Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
|
||||
@@ -119,7 +140,7 @@ Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 chan
|
||||
R_THROW(ResultInvalidOpusDSPReturnCode);
|
||||
}
|
||||
|
||||
R_RETURN(Result(u32(shared_memory.dsp_return_data[0])));
|
||||
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
|
||||
}
|
||||
|
||||
Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) {
|
||||
@@ -133,7 +154,7 @@ Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) {
|
||||
"Expected Opus shutdown code {}, got {}",
|
||||
ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, msg);
|
||||
|
||||
R_RETURN(Result(u32(shared_memory.dsp_return_data[0])));
|
||||
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
|
||||
}
|
||||
|
||||
Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size) {
|
||||
@@ -148,7 +169,7 @@ Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_si
|
||||
"Expected Opus shutdown code {}, got {}",
|
||||
ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, msg);
|
||||
|
||||
R_RETURN(Result(u32(shared_memory.dsp_return_data[0])));
|
||||
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
|
||||
}
|
||||
|
||||
Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data,
|
||||
@@ -172,12 +193,12 @@ Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data,
|
||||
R_THROW(ResultInvalidOpusDSPReturnCode);
|
||||
}
|
||||
|
||||
auto error_code = s32(shared_memory.dsp_return_data[0]);
|
||||
if (error_code == ResultSuccess.raw) {
|
||||
out_sample_count = u32(shared_memory.dsp_return_data[1]);
|
||||
auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])};
|
||||
if (error_code == OPUS_OK) {
|
||||
out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]);
|
||||
out_time_taken = 1000 * shared_memory.dsp_return_data[2];
|
||||
}
|
||||
R_RETURN(Result(u32(error_code)));
|
||||
R_RETURN(ResultCodeFromLibOpusErrorCode(error_code));
|
||||
}
|
||||
|
||||
Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data,
|
||||
@@ -186,27 +207,29 @@ Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void
|
||||
void* buffer, u64& out_time_taken,
|
||||
bool reset) {
|
||||
std::scoped_lock l{mutex};
|
||||
shared_memory.host_send_data[0] = u64(buffer);
|
||||
shared_memory.host_send_data[1] = u64(input_data);
|
||||
shared_memory.host_send_data[0] = (u64)buffer;
|
||||
shared_memory.host_send_data[1] = (u64)input_data;
|
||||
shared_memory.host_send_data[2] = input_data_size;
|
||||
shared_memory.host_send_data[3] = u64(output_data);
|
||||
shared_memory.host_send_data[3] = (u64)output_data;
|
||||
shared_memory.host_send_data[4] = output_data_size;
|
||||
shared_memory.host_send_data[5] = 0;
|
||||
shared_memory.host_send_data[6] = reset;
|
||||
|
||||
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStream);
|
||||
opus_decoder.Send(ADSP::Direction::DSP,
|
||||
ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStream);
|
||||
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
|
||||
if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK) {
|
||||
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK, msg);
|
||||
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
|
||||
ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK, msg);
|
||||
R_THROW(ResultInvalidOpusDSPReturnCode);
|
||||
}
|
||||
|
||||
auto const error_code = shared_memory.dsp_return_data[0];
|
||||
if (error_code == ResultSuccess.raw) {
|
||||
auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])};
|
||||
if (error_code == OPUS_OK) {
|
||||
out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]);
|
||||
out_time_taken = 1000 * shared_memory.dsp_return_data[2];
|
||||
}
|
||||
R_RETURN(Result(u32(shared_memory.dsp_return_data[0])));
|
||||
R_RETURN(ResultCodeFromLibOpusErrorCode(error_code));
|
||||
}
|
||||
|
||||
Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) {
|
||||
@@ -217,7 +240,8 @@ Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) {
|
||||
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::MapMemory);
|
||||
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
|
||||
if (msg != ADSP::OpusDecoder::Message::MapMemoryOK) {
|
||||
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", ADSP::OpusDecoder::Message::MapMemoryOK, msg);
|
||||
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
|
||||
ADSP::OpusDecoder::Message::MapMemoryOK, msg);
|
||||
R_THROW(ResultInvalidOpusDSPReturnCode);
|
||||
}
|
||||
R_SUCCEED();
|
||||
@@ -231,7 +255,8 @@ Result HardwareOpus::UnmapMemory(void* buffer, u64 buffer_size) {
|
||||
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::UnmapMemory);
|
||||
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
|
||||
if (msg != ADSP::OpusDecoder::Message::UnmapMemoryOK) {
|
||||
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", ADSP::OpusDecoder::Message::UnmapMemoryOK, msg);
|
||||
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
|
||||
ADSP::OpusDecoder::Message::UnmapMemoryOK, msg);
|
||||
R_THROW(ResultInvalidOpusDSPReturnCode);
|
||||
}
|
||||
R_SUCCEED();
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#include <array>
|
||||
#include <mutex>
|
||||
#include <opus.h>
|
||||
|
||||
#include "audio_core/adsp/apps/opus/opus_decoder.h"
|
||||
#include "audio_core/adsp/apps/opus/shared_memory.h"
|
||||
#include "audio_core/adsp/mailbox.h"
|
||||
|
||||
@@ -108,6 +108,8 @@ add_library(
|
||||
settings_setting.h
|
||||
slot_vector.h
|
||||
socket_types.h
|
||||
sparse_large_vector.cpp
|
||||
sparse_large_vector.h
|
||||
spin_lock.h
|
||||
stb.cpp
|
||||
stb.h
|
||||
@@ -137,8 +139,6 @@ add_library(
|
||||
uuid.cpp
|
||||
uuid.h
|
||||
vector_math.h
|
||||
virtual_buffer.cpp
|
||||
virtual_buffer.h
|
||||
zstd_compression.cpp
|
||||
zstd_compression.h
|
||||
fs/ryujinx_compat.h fs/ryujinx_compat.cpp
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/fiber.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
#include <boost/context/detail/fcontext.hpp>
|
||||
|
||||
|
||||
+40
-12
@@ -178,6 +178,14 @@ public:
|
||||
Release();
|
||||
}
|
||||
|
||||
void* Allocate(size_t size) {
|
||||
auto* ptr = VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||
if (ptr == nullptr) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to allocate fallback buffer with size {:#x}, error {}", size, GetLastError());
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
|
||||
std::unique_lock lock{placeholder_mutex};
|
||||
if (!IsNiechePlaceholder(virtual_offset, length)) {
|
||||
@@ -398,6 +406,10 @@ private:
|
||||
// For managarm: see https://github.com/managarm/managarm/issues/1370
|
||||
#else // ^^^ Windows ^^^ vvv POSIX vvv
|
||||
|
||||
#ifndef MAP_NOCORE
|
||||
#define MAP_NOCORE 0
|
||||
#endif
|
||||
|
||||
#ifdef ARCHITECTURE_arm64
|
||||
|
||||
static void* ChooseVirtualBase(size_t virtual_size) {
|
||||
@@ -422,7 +434,7 @@ static void* ChooseVirtualBase(size_t virtual_size) {
|
||||
// Note: we may be able to take advantage of MAP_FIXED_NOREPLACE here.
|
||||
void* map_pointer =
|
||||
mmap(reinterpret_cast<void*>(hint_address), virtual_size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_NOCORE, -1, 0);
|
||||
|
||||
// If we successfully mapped, we're done.
|
||||
if (reinterpret_cast<uintptr_t>(map_pointer) == hint_address) {
|
||||
@@ -442,11 +454,11 @@ static void* ChooseVirtualBase(size_t virtual_size) {
|
||||
|
||||
static void* ChooseVirtualBase(size_t virtual_size) {
|
||||
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__managarm__) || defined(__AIX__)
|
||||
void* virtual_base = mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER, -1, 0);
|
||||
void* virtual_base = mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER | MAP_NOCORE, -1, 0);
|
||||
if (virtual_base != MAP_FAILED)
|
||||
return virtual_base;
|
||||
#endif
|
||||
return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
|
||||
return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_NOCORE, -1, 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -540,13 +552,13 @@ public:
|
||||
}
|
||||
if (use_anon) {
|
||||
LOG_WARNING(Common_Memory, "Using private mappings instead of shared ones");
|
||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
|
||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_NOCORE, -1, 0));
|
||||
if (fd > 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
}
|
||||
} else {
|
||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
|
||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NOCORE, fd, 0));
|
||||
}
|
||||
if (backing_base == MAP_FAILED) {
|
||||
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
|
||||
@@ -570,6 +582,14 @@ public:
|
||||
Release();
|
||||
}
|
||||
|
||||
void* Allocate(size_t size) {
|
||||
auto* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
if (ptr == MAP_FAILED) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to allocate fallback buffer with size {:#x}, {}", size, strerror(errno));
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
|
||||
// Intersect the range with our address space.
|
||||
AdjustMap(&virtual_offset, &length);
|
||||
@@ -690,12 +710,10 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
|
||||
{
|
||||
#if defined(__OPENORBIS__) || defined(__managarm__)
|
||||
LOG_WARNING(HW_Memory, "Platform doesn't support fastmem");
|
||||
fallback_buffer.emplace(backing_size);
|
||||
backing_base = fallback_buffer->data();
|
||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
virtual_base = nullptr;
|
||||
#else
|
||||
// Try to allocate a fastmem arena.
|
||||
// The implementation will fail with std::bad_alloc on errors.
|
||||
impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize);
|
||||
if (impl->Init()) {
|
||||
backing_base = impl->backing_base;
|
||||
@@ -706,16 +724,26 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
|
||||
virtual_base_offset = virtual_base - impl->virtual_base;
|
||||
}
|
||||
} else {
|
||||
impl.reset();
|
||||
LOG_WARNING(HW_Memory, "Platform can support fastmem, but can't create it");
|
||||
fallback_buffer.emplace(backing_size);
|
||||
backing_base = fallback_buffer->data();
|
||||
fallback_buffer = true;
|
||||
backing_base = static_cast<u8*>(impl->Allocate(backing_size));
|
||||
virtual_base = nullptr;
|
||||
impl.reset();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
HostMemory::~HostMemory() = default;
|
||||
HostMemory::~HostMemory() {
|
||||
#ifdef _WIN32
|
||||
if (fallback_buffer) {
|
||||
VirtualFree(backing_base, backing_size, MEM_RELEASE);
|
||||
}
|
||||
#else
|
||||
if (fallback_buffer) {
|
||||
munmap(backing_base, backing_size);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
HostMemory::HostMemory(HostMemory&&) noexcept = default;
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <optional>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
@@ -86,7 +85,7 @@ private:
|
||||
u8* virtual_base{};
|
||||
size_t virtual_base_offset{};
|
||||
// Windows requires it for kernels whom lack proper support for some functions!
|
||||
std::optional<Common::VirtualBuffer<u8>> fallback_buffer;
|
||||
bool fallback_buffer{false};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -13,39 +13,11 @@ PageTable::PageTable() = default;
|
||||
|
||||
PageTable::~PageTable() noexcept = default;
|
||||
|
||||
bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
|
||||
Common::ProcessAddress address) const {
|
||||
out_context->next_offset = GetInteger(address);
|
||||
out_context->next_page = address / page_size;
|
||||
|
||||
return this->ContinueTraversal(out_entry, out_context);
|
||||
}
|
||||
|
||||
bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const {
|
||||
// Setup invalid defaults.
|
||||
out_entry->phys_addr = 0;
|
||||
out_entry->block_size = page_size;
|
||||
// Validate that we can read the actual entry.
|
||||
if (auto const page = context->next_page; page < entries.size()) {
|
||||
// Validate that the entry is mapped.
|
||||
if (auto const paddr = entries[page].addr; paddr != 0) {
|
||||
// Populate the results.
|
||||
out_entry->phys_addr = paddr + context->next_offset;
|
||||
context->next_page += 1;
|
||||
context->next_offset += page_size;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
context->next_page += 1;
|
||||
context->next_offset += page_size;
|
||||
return false;
|
||||
}
|
||||
|
||||
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits) {
|
||||
auto const num_page_table_entries = 1ULL << (address_space_width_in_bits - page_size_in_bits);
|
||||
entries.resize(num_page_table_entries);
|
||||
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_bits) {
|
||||
auto const num_page_table_entries = 1ULL << (address_space_width_in_bits - page_bits);
|
||||
entries.ResizeAndClear(num_page_table_entries);
|
||||
current_address_space_width_in_bits = address_space_width_in_bits;
|
||||
page_size = 1ULL << page_size_in_bits;
|
||||
current_page_bits = page_bits;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
+64
-56
@@ -9,22 +9,22 @@
|
||||
#include <atomic>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/sparse_large_vector.h"
|
||||
#include "common/typed_address.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
enum class PageType : u8 {
|
||||
/// Page is unmapped and should cause an access error.
|
||||
Unmapped,
|
||||
Unmapped = 0b00,
|
||||
/// Page is mapped to regular memory. This is the only type you can get pointers to.
|
||||
Memory,
|
||||
Memory = 0b01,
|
||||
/// Page is mapped to regular memory, but inaccessible from CPU fastmem and must use
|
||||
/// the callbacks.
|
||||
DebugMemory,
|
||||
DebugMemory = 0b10,
|
||||
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
||||
/// invalidation
|
||||
RasterizerCachedMemory,
|
||||
RasterizerCachedMemory = 0b11,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -42,57 +42,86 @@ struct PageTable {
|
||||
u64 next_offset{};
|
||||
};
|
||||
|
||||
/// Number of bits reserved for attribute tagging.
|
||||
/// This can be at most the guaranteed alignment of the pointers in the page table.
|
||||
static constexpr int ATTRIBUTE_BITS = 2;
|
||||
/// Masks out bits reserved for attribute tagging.
|
||||
static constexpr u64 ATTRIBUTE_MASK = ((1ULL << 44) - 1) << 12;
|
||||
|
||||
/// Specifies sign bit for page table entries.
|
||||
static constexpr u64 SIGN_BIT = 45 + 12; // 44 bits of data + page offset
|
||||
|
||||
/**
|
||||
* Pair of host pointer and page type attribute.
|
||||
* This uses the lower bits of a given pointer to store the attribute tag.
|
||||
* Atomic tuple of host pointer, page type, and block id.
|
||||
* This uses the lower bits of a given pointer to store the attributes.
|
||||
* Writing and reading the pointer attribute pair is guaranteed to be atomic for the same method
|
||||
* call. In other words, they are guaranteed to be synchronized at all times.
|
||||
*/
|
||||
class PageInfo {
|
||||
class PageEntryData {
|
||||
public:
|
||||
struct Data {
|
||||
Data(bool marked_, PageType type_, u16 block_, u64 page_)
|
||||
: marked(static_cast<u64>(marked_) & 0b1)
|
||||
, type(static_cast<u64>(type_) & ((1ULL << 2) - 1))
|
||||
, block(static_cast<u64>(block_) & ((1ULL << 9) - 1))
|
||||
, page((page_ >> 12) & ((1ULL << 45) - 1))
|
||||
, block2((static_cast<u64>(block_) >> 9) & ((1ULL << 7) - 1)) {}
|
||||
u64 marked : 1;
|
||||
u64 type : 2;
|
||||
u64 block : 9;
|
||||
u64 page : 45; // 44 bits of actual data (64 - page offset (12) - reserved (8)) + a sign bit
|
||||
u64 block2 : 7;
|
||||
};
|
||||
|
||||
[[nodiscard]] Data Raw() const noexcept {
|
||||
return std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
/// Returns the page pointer
|
||||
[[nodiscard]] uintptr_t Pointer() const noexcept {
|
||||
return ExtractPointer(raw.load(std::memory_order_relaxed));
|
||||
[[nodiscard]] uintptr_t Pointer(bool ignored_marked = false) const noexcept {
|
||||
return ExtractPointer(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)), ignored_marked);
|
||||
}
|
||||
|
||||
/// Returns the page type attribute
|
||||
[[nodiscard]] PageType Type() const noexcept {
|
||||
return ExtractType(raw.load(std::memory_order_relaxed));
|
||||
return static_cast<PageType>(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)).type);
|
||||
}
|
||||
|
||||
/// Returns the block identifier.
|
||||
[[nodiscard]] u16 Block() const noexcept {
|
||||
return ExtractBlock(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)));
|
||||
}
|
||||
|
||||
/// Returns the page pointer and attribute pair, extracted from the same atomic read
|
||||
[[nodiscard]] std::pair<uintptr_t, PageType> PointerType() const noexcept {
|
||||
const uintptr_t non_atomic_raw = raw.load(std::memory_order_relaxed);
|
||||
return {ExtractPointer(non_atomic_raw), ExtractType(non_atomic_raw)};
|
||||
[[nodiscard]] std::tuple<uintptr_t, PageType, u16> PointerTypeBlock(bool ignore_marked = false) const noexcept {
|
||||
const auto non_atomic_raw = std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed));
|
||||
return {ExtractPointer(non_atomic_raw, ignore_marked), static_cast<PageType>(non_atomic_raw.type), ExtractBlock(non_atomic_raw)};
|
||||
}
|
||||
|
||||
/// Returns the raw representation of the page information.
|
||||
/// Use ExtractPointer and ExtractType to unpack the value.
|
||||
[[nodiscard]] uintptr_t Raw() const noexcept {
|
||||
return raw.load(std::memory_order_relaxed);
|
||||
/// Write page info atomically
|
||||
constexpr void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept {
|
||||
data_raw.store(std::bit_cast<u64>(Data{marked, type, block, pointer}));
|
||||
}
|
||||
|
||||
/// Write a page pointer and type pair atomically
|
||||
void Store(uintptr_t pointer, PageType type) noexcept {
|
||||
raw.store(pointer | uintptr_t(type));
|
||||
constexpr void MarkRasterizerCached() noexcept {
|
||||
data_raw.fetch_or(0b111);
|
||||
}
|
||||
|
||||
constexpr void MarkDebug(u64 ptr, u16 block) noexcept {
|
||||
Store(true, PageType::DebugMemory, block, ptr);
|
||||
}
|
||||
|
||||
/// Unpack a pointer from a page info raw representation
|
||||
[[nodiscard]] static uintptr_t ExtractPointer(uintptr_t raw) noexcept {
|
||||
return raw & (~uintptr_t{0} << ATTRIBUTE_BITS);
|
||||
[[nodiscard]] static uintptr_t ExtractPointer(Data raw, bool ignore_marked = false) noexcept {
|
||||
return raw.marked && !ignore_marked ? 0
|
||||
// shift raw.page's fake sign bit to the actual sign bit, then sign extend
|
||||
: ((s64)(raw.page << (64 - 44))) >> (64 - 44 - 12);
|
||||
}
|
||||
|
||||
/// Unpack a page type from a page info raw representation
|
||||
[[nodiscard]] static PageType ExtractType(uintptr_t raw) noexcept {
|
||||
return static_cast<PageType>(raw & ((uintptr_t{1} << ATTRIBUTE_BITS) - 1));
|
||||
[[nodiscard]] static u16 ExtractBlock(Data raw) noexcept {
|
||||
return static_cast<u16>(raw.block | (raw.block2 << 9));
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<uintptr_t> raw;
|
||||
std::atomic<u64> data_raw;
|
||||
static_assert(sizeof(Data) == sizeof(std::atomic<u64>));
|
||||
};
|
||||
|
||||
PageTable();
|
||||
@@ -100,13 +129,8 @@ struct PageTable {
|
||||
|
||||
PageTable(const PageTable&) = delete;
|
||||
PageTable& operator=(const PageTable&) = delete;
|
||||
|
||||
PageTable(PageTable&&) noexcept = default;
|
||||
PageTable& operator=(PageTable&&) noexcept = default;
|
||||
|
||||
bool BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
|
||||
Common::ProcessAddress address) const;
|
||||
bool ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const;
|
||||
PageTable(PageTable&&) noexcept = delete;
|
||||
PageTable& operator=(PageTable&&) noexcept = delete;
|
||||
|
||||
/**
|
||||
* Resizes the page table to be able to accommodate enough pages within
|
||||
@@ -121,30 +145,14 @@ struct PageTable {
|
||||
return current_address_space_width_in_bits;
|
||||
}
|
||||
|
||||
bool GetPhysicalAddress(Common::PhysicalAddress* out_phys_addr,
|
||||
Common::ProcessAddress virt_addr) const {
|
||||
if (virt_addr > (1ULL << this->GetAddressSpaceBits())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*out_phys_addr = entries[virt_addr / page_size].addr + GetInteger(virt_addr);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Vector of memory pointers backing each page. An entry can only be non-null if the
|
||||
/// corresponding attribute element is of type `Memory`.
|
||||
struct PageEntryData {
|
||||
PageInfo ptr;
|
||||
u64 block;
|
||||
u64 addr;
|
||||
u64 padding;
|
||||
};
|
||||
VirtualBuffer<PageEntryData> entries;
|
||||
static_assert(sizeof(PageEntryData) == 32);
|
||||
SparseLargeVector<PageEntryData> entries;
|
||||
static_assert(sizeof(PageEntryData) == 8);
|
||||
|
||||
u8* fastmem_arena{};
|
||||
std::size_t current_address_space_width_in_bits{};
|
||||
std::size_t page_size{};
|
||||
std::size_t current_page_bits{};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* virtual_buffer.cpp */
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <mutex>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/sparse_large_vector.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
#ifdef _WIN32
|
||||
static std::vector<std::pair<u64, u64>> vector_regions {};
|
||||
|
||||
// Workaround for handling non-commited memory accessed by Dynarmic; usually result of an error
|
||||
static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) {
|
||||
DWORD code = info->ExceptionRecord->ExceptionCode;
|
||||
u64 exception_addr = reinterpret_cast<u64>(info->ExceptionRecord->ExceptionAddress);
|
||||
|
||||
if (code != EXCEPTION_ACCESS_VIOLATION) {
|
||||
// Not our problem
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
u64 addr = 0, addr2 = 0;
|
||||
|
||||
for (auto region: vector_regions) {
|
||||
auto addr_shifted = exception_addr >> HostPageBits;
|
||||
if (region.first <= addr_shifted && addr_shifted <= region.second) {
|
||||
addr = addr_shifted;
|
||||
}
|
||||
|
||||
// Page-boundary accesses
|
||||
if (auto addr_ = (exception_addr + 0x40) >> HostPageBits; addr_ != addr_shifted && region.first <= addr_ && addr_ <= region.second) {
|
||||
addr2 = addr_;
|
||||
}
|
||||
|
||||
if (addr != 0 || addr2 != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (addr == 0 && addr2 == 0) {
|
||||
// Not our problem
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
LOG_ERROR(HW_Memory, "Accessing an unallocated region of a SparseLargeVector at {:#x}; this shouldn't happen and is likely a Dynarmic error!", exception_addr);
|
||||
|
||||
// Commit this region
|
||||
if (addr != 0) {
|
||||
if (!CommitVectorPage(addr << HostPageBits, false)) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
}
|
||||
// Commit next region if needed
|
||||
if (addr2 != 0) {
|
||||
if (!CommitVectorPage(addr2 << HostPageBits, false)) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
|
||||
MEMORY_BASIC_INFORMATION info {};
|
||||
auto res = VirtualQuery(reinterpret_cast<void*>(addr), &info, sizeof(info));
|
||||
if (res == 0) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to query large buffer region at {:#x} with error {}, will try committing anyway", addr, GetLastError());
|
||||
} else if (info.State != MEM_RESERVE) {
|
||||
LOG_ERROR(HW_Memory, "Tried to commit an unreserved large buffer region at {:#x} that is not mapped or is already committed (state {:#x})", addr, info.State);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto perm = write ? PAGE_READWRITE : PAGE_READONLY;
|
||||
void* res2 = VirtualAlloc(reinterpret_cast<LPVOID>(addr), HostPageSize, MEM_COMMIT, perm);
|
||||
if (res2 == nullptr) {
|
||||
LOG_ERROR(HW_Memory, "Failed to commit large buffer region at {:#x}, error {}", addr, GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef MAP_NOCORE
|
||||
#define MAP_NOCORE 0
|
||||
#endif
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept {
|
||||
if (auto page = HostPageSize; size % page != 0) {
|
||||
LOG_WARNING(HW_Memory, "Allocating unaligned large vector with size {:#x}; aligning to {} page size", size, page);
|
||||
size = AlignUp(size, page);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// We will never use this memory entirely so instead of committing it up front let's just reserve it and commit each page individually
|
||||
void* base = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE);
|
||||
|
||||
if (base != nullptr) {
|
||||
vector_regions.emplace_back(reinterpret_cast<u64>(base), reinterpret_cast<u64>(base) + size);
|
||||
|
||||
static std::once_flag flag;
|
||||
std::call_once(flag, []() { AddVectoredExceptionHandler(1, FakePageFaultHandler); });
|
||||
} else {
|
||||
// Try committing everything instead??
|
||||
LOG_WARNING(HW_Memory, "Failed to reserve large vector region with error {}, trying to commit instead..", GetLastError());
|
||||
base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
}
|
||||
ASSERT_MSG(base, "Failed to reserve {:#x} sized region with error {}", size, GetLastError());
|
||||
#else
|
||||
void* base = mmap(nullptr, size, PROT_READ, MAP_ANON | MAP_PRIVATE | MAP_NOCORE, -1, 0);
|
||||
if (base == MAP_FAILED)
|
||||
base = nullptr;
|
||||
ASSERT_MSG(base, "Failed to allocate {:#x} sized region with error {}", size, strerror(errno));
|
||||
#endif
|
||||
return base;
|
||||
}
|
||||
|
||||
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
|
||||
if (auto page = HostPageSize; size % page != 0) {
|
||||
size = AlignUp(size, page);
|
||||
}
|
||||
if (!base)
|
||||
return;
|
||||
#ifdef _WIN32
|
||||
ASSERT(VirtualFree(base, 0, MEM_RELEASE));
|
||||
#else
|
||||
ASSERT(munmap(base, size) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -0,0 +1,194 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* virtual_buffer.h */
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
#ifdef _WIN32
|
||||
constexpr u64 HostPageSize = 0x1000;
|
||||
constexpr u64 HostPageBits = 12;
|
||||
constexpr u64 HostPageMask = ~(HostPageSize - 1);
|
||||
bool CommitVectorPage(uintptr_t addr, bool write) noexcept;
|
||||
#else
|
||||
const u64 HostPageSize = sysconf(_SC_PAGESIZE);
|
||||
const u64 HostPageBits = std::countr_zero(HostPageSize);
|
||||
const u64 HostPageMask = ~(HostPageSize - 1);
|
||||
#endif
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept;
|
||||
void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
||||
|
||||
/// A large page-aligned buffer that has optimized memory usage for zero-writes.
|
||||
template <typename T>
|
||||
requires std::is_trivially_copyable_v<T>
|
||||
class SparseLargeVector final {
|
||||
public:
|
||||
constexpr SparseLargeVector() = default;
|
||||
|
||||
explicit SparseLargeVector(std::size_t count) noexcept
|
||||
: alloc_size{count * sizeof(T)}
|
||||
{
|
||||
base_ptr = static_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||
|
||||
// each item in vector holds information for 64 pages
|
||||
auto denom = HostPageSize * 64;
|
||||
committed_pages = std::vector<std::atomic<u64>>((alloc_size + denom - 1) / denom);
|
||||
}
|
||||
|
||||
~SparseLargeVector() noexcept {
|
||||
FreeMemoryPages(base_ptr, alloc_size);
|
||||
}
|
||||
|
||||
SparseLargeVector(const SparseLargeVector&) = delete;
|
||||
SparseLargeVector& operator=(const SparseLargeVector&) = delete;
|
||||
SparseLargeVector(SparseLargeVector&& other) = delete;
|
||||
SparseLargeVector& operator=(SparseLargeVector&& other) = delete;
|
||||
|
||||
void ResizeAndClear(std::size_t count) noexcept {
|
||||
if (auto const new_size = count * sizeof(T); new_size != alloc_size) {
|
||||
FreeMemoryPages(base_ptr, alloc_size);
|
||||
alloc_size = new_size;
|
||||
base_ptr = static_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||
|
||||
auto denom = HostPageSize * 64;
|
||||
committed_pages = std::vector<std::atomic<u64>>((alloc_size + denom - 1) / denom);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the value of the requested index and allocates memory if needed.
|
||||
T& GetAndFault(std::size_t index) noexcept {
|
||||
if (index > alloc_size / sizeof(T)) {
|
||||
UNREACHABLE_MSG("Out of bounds RW access on SparseLargeVector @ {}", index);
|
||||
}
|
||||
|
||||
if (!IsCommittedPage(index)) {
|
||||
CommitPage(index);
|
||||
}
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
/// Returns a reference to the value of the requested index if initialized, or will otherwise return a zero-initialized object.
|
||||
const T& GetOrDefault(std::size_t index) const {
|
||||
#ifdef _WIN32
|
||||
if (!IsCommittedPage(index)) {
|
||||
return *reinterpret_cast<const T*>(&default_val);
|
||||
}
|
||||
#endif
|
||||
// On non-Windows, OS page table should optimize this by pointing to a zero page if unallocated.
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
void Set(std::size_t index, const T& value) noexcept {
|
||||
if (index > alloc_size / sizeof(T)) {
|
||||
LOG_CRITICAL(Common_Memory, "Out of bounds write on SparseLargeVector @ {}", index);
|
||||
return;
|
||||
}
|
||||
if (!IsCommittedPage(index))
|
||||
CommitPage(index);
|
||||
base_ptr[index] = value;
|
||||
}
|
||||
|
||||
void ZeroRegion(std::size_t start, std::size_t end_) noexcept {
|
||||
u64 base = reinterpret_cast<u64>(&base_ptr[start]);
|
||||
const u64 end = reinterpret_cast<u64>(&base_ptr[end_]);
|
||||
|
||||
const u64 end_page = AlignUp(base, HostPageSize);
|
||||
const u64 first_size = (std::min)(end_page, end) - base;
|
||||
|
||||
if (IsCommittedPage(start / sizeof(T))) {
|
||||
std::memset(reinterpret_cast<void*>(base), 0, first_size);
|
||||
}
|
||||
|
||||
if (end <= end_page)
|
||||
return;
|
||||
|
||||
base = end_page;
|
||||
|
||||
for (u64 page = base; page < end; page += HostPageSize) {
|
||||
if (!IsCommittedPage((page - reinterpret_cast<u64>(base_ptr)) / sizeof(T))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::memset(reinterpret_cast<void*>(page), 0, (std::min)( HostPageSize, end - page));
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void CommitRegion(size_t index, size_t end_) {
|
||||
const u64 base = static_cast<u64>(index) * sizeof(T);
|
||||
const u64 end = static_cast<u64>(end_) * sizeof(T);
|
||||
|
||||
for (u64 page = AlignDown(base, HostPageSize); page < end; page += HostPageSize) {
|
||||
if (!IsCommittedPage(page / sizeof(T))) {
|
||||
CommitPage(page / sizeof(T));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr T& GetUnchecked(size_t index) {
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T& operator[](std::size_t index) const noexcept {
|
||||
return GetOrDefault(index);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T* data() const noexcept {
|
||||
return base_ptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::size_t size() const noexcept {
|
||||
return alloc_size / sizeof(T);
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] constexpr bool IsCommittedPage(std::size_t index) const noexcept {
|
||||
if (index > alloc_size / sizeof(T)) {
|
||||
LOG_CRITICAL(Common_Memory, "Out of bounds access on large vector @ {}", index);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto page = (index * sizeof(T)) >> HostPageBits;
|
||||
auto val = committed_pages[page >> 6].load(std::memory_order_acquire);
|
||||
return (val >> (page & 63)) & 1;
|
||||
}
|
||||
|
||||
constexpr void CommitPage(std::size_t index) noexcept {
|
||||
auto page_index = (index * sizeof(T)) >> HostPageBits;
|
||||
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
|
||||
#if defined(_WIN32)
|
||||
CommitVectorPage(page, true);
|
||||
#else
|
||||
mprotect(reinterpret_cast<void*>(page), HostPageSize, PROT_READ | PROT_WRITE);
|
||||
#endif
|
||||
|
||||
committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release);
|
||||
}
|
||||
|
||||
std::size_t alloc_size{};
|
||||
T* base_ptr{};
|
||||
|
||||
std::vector<std::atomic<u64>> committed_pages{};
|
||||
#ifdef _WIN32
|
||||
const std::array<u8, sizeof(T)> default_val{};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,44 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept {
|
||||
#ifdef _WIN32
|
||||
void* base = VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (base == nullptr) {
|
||||
// Probably failing to reserve is less likely than failing to commit
|
||||
base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
}
|
||||
#else
|
||||
void* base = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
if (base == MAP_FAILED)
|
||||
base = nullptr;
|
||||
#endif
|
||||
ASSERT(base);
|
||||
return base;
|
||||
}
|
||||
|
||||
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
|
||||
if (!base)
|
||||
return;
|
||||
#ifdef _WIN32
|
||||
ASSERT(VirtualFree(base, 0, MEM_RELEASE));
|
||||
#else
|
||||
ASSERT(munmap(base, size) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,84 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace Common {
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept;
|
||||
void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
||||
|
||||
template <typename T>
|
||||
class VirtualBuffer final {
|
||||
public:
|
||||
// TODO: Uncomment this and change Common::PageTable::PageInfo to be trivially constructible
|
||||
// using std::atomic_ref once libc++ has support for it
|
||||
// static_assert(
|
||||
// std::is_trivially_constructible_v<T>,
|
||||
// "T must be trivially constructible, as non-trivial constructors will not be executed "
|
||||
// "with the current allocator");
|
||||
|
||||
constexpr VirtualBuffer() = default;
|
||||
explicit VirtualBuffer(std::size_t count) noexcept
|
||||
: alloc_size{count * sizeof(T)}
|
||||
{
|
||||
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||
}
|
||||
|
||||
~VirtualBuffer() noexcept {
|
||||
FreeMemoryPages(base_ptr, alloc_size);
|
||||
}
|
||||
|
||||
VirtualBuffer(const VirtualBuffer&) = delete;
|
||||
VirtualBuffer& operator=(const VirtualBuffer&) = delete;
|
||||
|
||||
VirtualBuffer(VirtualBuffer&& other) noexcept
|
||||
: alloc_size{std::exchange(other.alloc_size, 0)}
|
||||
, base_ptr{std::exchange(other.base_ptr, nullptr)}
|
||||
{}
|
||||
|
||||
VirtualBuffer& operator=(VirtualBuffer&& other) noexcept {
|
||||
alloc_size = std::exchange(other.alloc_size, 0);
|
||||
base_ptr = std::exchange(other.base_ptr, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void resize(std::size_t count) noexcept {
|
||||
if (auto const new_size = count * sizeof(T); new_size != alloc_size) {
|
||||
FreeMemoryPages(base_ptr, alloc_size);
|
||||
alloc_size = new_size;
|
||||
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T& operator[](std::size_t index) const noexcept {
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T& operator[](std::size_t index) noexcept {
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T* data() noexcept {
|
||||
return base_ptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T* data() const noexcept {
|
||||
return base_ptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::size_t size() const noexcept {
|
||||
return alloc_size / sizeof(T);
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t alloc_size{};
|
||||
T* base_ptr{};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -1199,6 +1199,7 @@ else()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_include_directories(core PRIVATE ${OPUS_INCLUDE_DIRS})
|
||||
target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz)
|
||||
|
||||
if (BOOST_NO_HEADERS)
|
||||
|
||||
@@ -172,12 +172,12 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
||||
if (page_table) {
|
||||
constexpr size_t PageBits = 12;
|
||||
constexpr size_t NumPageTableEntries = 1 << (32 - PageBits);
|
||||
constexpr size_t PageLog2Stride = 5;
|
||||
static_assert(1 << PageLog2Stride == sizeof(Common::PageTable::PageEntryData));
|
||||
|
||||
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>(page_table->entries.data());
|
||||
config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
|
||||
config.page_table_log2_stride = PageLog2Stride;
|
||||
// Dynarmic will not write to the page table, const_cast is safe here
|
||||
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>(
|
||||
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
|
||||
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
|
||||
config.page_table_marked_bit = 0;
|
||||
config.absolute_offset_page_table = true;
|
||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
||||
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
|
||||
@@ -188,6 +188,13 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
||||
|
||||
config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt;
|
||||
config.recompile_on_exclusive_fastmem_failure = true;
|
||||
|
||||
if (reinterpret_cast<u64>(m_system.DeviceMemory().buffer.BackingBasePointer() +
|
||||
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
|
||||
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
|
||||
// we have to manually sign extend when our actual pointer is negative.
|
||||
config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
// Multi-process state
|
||||
@@ -420,6 +427,7 @@ void ArmDynarmic32::SignalInterrupt(Kernel::KThread* thread) {
|
||||
}
|
||||
|
||||
void ArmDynarmic32::ClearInstructionCache() {
|
||||
m_cb->last_code_addr = u64(-1);
|
||||
m_jit->ClearCache();
|
||||
}
|
||||
|
||||
|
||||
@@ -211,13 +211,12 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
||||
|
||||
// Memory
|
||||
if (page_table) {
|
||||
constexpr size_t PageLog2Stride = 5;
|
||||
static_assert(1 << PageLog2Stride == sizeof(Common::PageTable::PageEntryData));
|
||||
|
||||
config.page_table = reinterpret_cast<void**>(page_table->entries.data());
|
||||
// Dynarmic will not write to the page table, const_cast is safe here
|
||||
config.page_table = reinterpret_cast<void**>(
|
||||
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
|
||||
config.page_table_address_space_bits = std::uint32_t(address_space_bits);
|
||||
config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
|
||||
config.page_table_log2_stride = PageLog2Stride;
|
||||
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
|
||||
config.page_table_marked_bit = 0;
|
||||
config.silently_mirror_page_table = false;
|
||||
config.absolute_offset_page_table = true;
|
||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
||||
@@ -231,6 +230,13 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
||||
|
||||
config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt;
|
||||
config.recompile_on_exclusive_fastmem_failure = true;
|
||||
|
||||
if (reinterpret_cast<u64>(m_system.DeviceMemory().buffer.BackingBasePointer() +
|
||||
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
|
||||
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
|
||||
// we have to manually sign extend when our actual pointer is negative.
|
||||
config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
// Multi-process state
|
||||
@@ -447,6 +453,7 @@ void ArmDynarmic64::SignalInterrupt(Kernel::KThread* thread) {
|
||||
}
|
||||
|
||||
void ArmDynarmic64::ClearInstructionCache() {
|
||||
m_cb->last_code_addr = u64(-1);
|
||||
m_jit->ClearCache();
|
||||
}
|
||||
|
||||
|
||||
@@ -29,8 +29,11 @@ public:
|
||||
|
||||
template <typename T>
|
||||
Common::PhysicalAddress GetPhysicalAddr(const T* ptr) const {
|
||||
return (reinterpret_cast<uintptr_t>(ptr) -
|
||||
reinterpret_cast<uintptr_t>(buffer.BackingBasePointer())) +
|
||||
return GetPhysicalAddr(reinterpret_cast<uintptr_t>(ptr));
|
||||
}
|
||||
|
||||
Common::PhysicalAddress GetPhysicalAddr(uintptr_t ptr) const {
|
||||
return (ptr - reinterpret_cast<uintptr_t>(buffer.BackingBasePointer())) +
|
||||
DramMemoryMap::Base;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/range_mutex.h"
|
||||
#include "common/scratch_buffer.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
#include "common/sparse_large_vector.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
@@ -178,8 +178,8 @@ private:
|
||||
u32 continuity_tracker;
|
||||
u32 compressed_physical_ptr;
|
||||
};
|
||||
Common::VirtualBuffer<u32> compressed_device_addr;
|
||||
Common::VirtualBuffer<TrackedEntry> tracked_entries;
|
||||
Common::SparseLargeVector<u32> compressed_device_addr;
|
||||
Common::SparseLargeVector<TrackedEntry> tracked_entries;
|
||||
|
||||
// Process memory interfaces
|
||||
|
||||
@@ -200,8 +200,8 @@ private:
|
||||
return std::make_pair(asid, address);
|
||||
}
|
||||
|
||||
void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) {
|
||||
tracked_entries[page_index].cpu_backing_address = address | (asid.id << asid_start_bit);
|
||||
constexpr void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) {
|
||||
tracked_entries.GetUnchecked(page_index).cpu_backing_address = address | (asid.id << asid_start_bit);
|
||||
}
|
||||
|
||||
std::array<TranslationEntry, 4> t_slot{};
|
||||
|
||||
@@ -177,17 +177,6 @@ DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memo
|
||||
{
|
||||
impl = std::make_unique<DeviceMemoryManagerAllocator<Traits>>();
|
||||
cached_pages = std::make_unique<CachedPages>();
|
||||
|
||||
const size_t total_virtual = device_as_size >> Memory::YUZU_PAGEBITS;
|
||||
for (size_t i = 0; i < total_virtual; i++) {
|
||||
tracked_entries[i].compressed_physical_ptr = 0;
|
||||
tracked_entries[i].continuity_tracker = 1;
|
||||
tracked_entries[i].cpu_backing_address = 0;
|
||||
}
|
||||
const size_t total_phys = 1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS);
|
||||
for (size_t i = 0; i < total_phys; i++) {
|
||||
compressed_device_addr[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
@@ -220,26 +209,28 @@ void DeviceMemoryManager<Traits>::Map(DAddr address, VAddr virtual_address, size
|
||||
size_t start_page_d = address >> Memory::YUZU_PAGEBITS;
|
||||
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
|
||||
std::scoped_lock lk(mapping_guard);
|
||||
|
||||
tracked_entries.CommitRegion(start_page_d, start_page_d + num_pages);
|
||||
for (size_t i = 0; i < num_pages; i++) {
|
||||
const VAddr new_vaddress = virtual_address + i * Memory::YUZU_PAGESIZE;
|
||||
auto* ptr = process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress));
|
||||
if (ptr == nullptr) [[unlikely]] {
|
||||
tracked_entries[start_page_d + i].compressed_physical_ptr = 0;
|
||||
tracked_entries.GetUnchecked(start_page_d + i).compressed_physical_ptr = 0;
|
||||
continue;
|
||||
}
|
||||
auto phys_addr = static_cast<u32>(GetRawPhysicalAddr(ptr) >> Memory::YUZU_PAGEBITS) + 1U;
|
||||
tracked_entries[start_page_d + i].compressed_physical_ptr = phys_addr;
|
||||
tracked_entries.GetUnchecked(start_page_d + i).compressed_physical_ptr = phys_addr;
|
||||
InsertCPUBacking(start_page_d + i, new_vaddress, asid);
|
||||
const u32 base_dev = compressed_device_addr[phys_addr - 1U];
|
||||
const u32 new_dev = static_cast<u32>(start_page_d + i);
|
||||
if (base_dev == 0) [[likely]] {
|
||||
compressed_device_addr[phys_addr - 1U] = new_dev;
|
||||
compressed_device_addr.GetAndFault(phys_addr - 1U) = new_dev;
|
||||
continue;
|
||||
}
|
||||
u32 start_id = base_dev & MULTI_MASK;
|
||||
if ((base_dev >> MULTI_FLAG_BITS) == 0) {
|
||||
start_id = impl->multi_dev_address.Register(base_dev);
|
||||
compressed_device_addr[phys_addr - 1U] = MULTI_FLAG | start_id;
|
||||
compressed_device_addr.GetAndFault(phys_addr - 1U) = MULTI_FLAG | start_id;
|
||||
}
|
||||
impl->multi_dev_address.Register(new_dev, start_id);
|
||||
}
|
||||
@@ -255,24 +246,26 @@ void DeviceMemoryManager<Traits>::Unmap(DAddr address, size_t size) {
|
||||
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
|
||||
device_inter->InvalidateRegion(address, size);
|
||||
std::scoped_lock lk(mapping_guard);
|
||||
|
||||
tracked_entries.CommitRegion(start_page_d, start_page_d + num_pages); // should already be committed, but just in case
|
||||
for (size_t i = 0; i < num_pages; i++) {
|
||||
auto phys_addr = tracked_entries[start_page_d + i].compressed_physical_ptr;
|
||||
tracked_entries[start_page_d + i].compressed_physical_ptr = 0;
|
||||
tracked_entries[start_page_d + i].cpu_backing_address = 0;
|
||||
auto& entry = tracked_entries.GetUnchecked(start_page_d + i);
|
||||
auto phys_addr = entry.compressed_physical_ptr;
|
||||
entry.compressed_physical_ptr = 0;
|
||||
entry.cpu_backing_address = 0;
|
||||
if (phys_addr != 0) [[likely]] {
|
||||
const u32 base_dev = compressed_device_addr[phys_addr - 1U];
|
||||
u32& base_dev = compressed_device_addr.GetAndFault(phys_addr - 1U);
|
||||
if ((base_dev >> MULTI_FLAG_BITS) == 0) [[likely]] {
|
||||
compressed_device_addr[phys_addr - 1] = 0;
|
||||
base_dev = 0;
|
||||
continue;
|
||||
}
|
||||
const auto [more_entries, new_start] = impl->multi_dev_address.Unregister(
|
||||
static_cast<u32>(start_page_d + i), base_dev & MULTI_MASK);
|
||||
if (!more_entries) {
|
||||
compressed_device_addr[phys_addr - 1] =
|
||||
impl->multi_dev_address.ReleaseEntry(new_start);
|
||||
base_dev = impl->multi_dev_address.ReleaseEntry(new_start);
|
||||
continue;
|
||||
}
|
||||
compressed_device_addr[phys_addr - 1] = new_start | MULTI_FLAG;
|
||||
base_dev = new_start | MULTI_FLAG;
|
||||
}
|
||||
}
|
||||
t_slot = {};
|
||||
@@ -285,6 +278,8 @@ void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtu
|
||||
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
|
||||
uintptr_t last_ptr = 0;
|
||||
size_t page_count = 1;
|
||||
|
||||
tracked_entries.CommitRegion(start_page_d, start_page_d + num_pages);
|
||||
for (size_t i = num_pages; i > 0; i--) {
|
||||
size_t index = i - 1;
|
||||
const VAddr new_vaddress = virtual_address + index * Memory::YUZU_PAGESIZE;
|
||||
@@ -296,14 +291,14 @@ void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtu
|
||||
page_count = 1;
|
||||
}
|
||||
last_ptr = new_ptr;
|
||||
tracked_entries[start_page_d + index].continuity_tracker = static_cast<u32>(page_count);
|
||||
tracked_entries.GetUnchecked(start_page_d + index).continuity_tracker = static_cast<u32>(page_count) - 1;
|
||||
}
|
||||
}
|
||||
template <typename Traits>
|
||||
u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) {
|
||||
size_t page_index = src_addr >> page_bits;
|
||||
size_t subbits = src_addr & page_mask;
|
||||
if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker) << page_bits) >= size + subbits) {
|
||||
if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker+1) << page_bits) >= size + subbits) {
|
||||
return GetPointer<u8>(src_addr);
|
||||
}
|
||||
return nullptr;
|
||||
@@ -313,7 +308,7 @@ template <typename Traits>
|
||||
const u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) const {
|
||||
size_t page_index = src_addr >> page_bits;
|
||||
size_t subbits = src_addr & page_mask;
|
||||
if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker) << page_bits) >= size + subbits) {
|
||||
if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker+1) << page_bits) >= size + subbits) {
|
||||
return GetPointer<u8>(src_addr);
|
||||
}
|
||||
return nullptr;
|
||||
@@ -383,7 +378,7 @@ void DeviceMemoryManager<Traits>::WalkBlock(DAddr addr, std::size_t size, auto o
|
||||
std::size_t page_index = addr >> Memory::YUZU_PAGEBITS;
|
||||
std::size_t page_offset = addr & Memory::YUZU_PAGEMASK;
|
||||
while (remaining_size) {
|
||||
const size_t next_pages = std::size_t(tracked_entries[page_index].continuity_tracker);
|
||||
const size_t next_pages = std::size_t(tracked_entries[page_index].continuity_tracker+1);
|
||||
const std::size_t copy_amount = (std::min)((next_pages << Memory::YUZU_PAGEBITS) - page_offset, remaining_size);
|
||||
const auto current_vaddr = u64((page_index << Memory::YUZU_PAGEBITS) + page_offset);
|
||||
SCOPE_EXIT{
|
||||
|
||||
@@ -635,6 +635,36 @@ Result KPageTableBase::CheckMemoryState(const KMemoryInfo& info, KMemoryState st
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
bool KPageTableBase::BeginTraversal(const Common::PageTable &impl, TraversalEntry *out_entry, TraversalContext *out_context,
|
||||
Common::ProcessAddress address) const {
|
||||
out_context->next_offset = GetInteger(address);
|
||||
out_context->next_page = GetInteger(address) >> PageBits;
|
||||
|
||||
return ContinueTraversal(impl, out_entry, out_context);
|
||||
}
|
||||
|
||||
bool KPageTableBase::ContinueTraversal(const Common::PageTable &impl, TraversalEntry *out_entry,
|
||||
TraversalContext *context) const {
|
||||
// Setup invalid defaults.
|
||||
out_entry->phys_addr = 0;
|
||||
out_entry->block_size = PageSize;
|
||||
// Validate that we can read the actual entry.
|
||||
if (auto const page = context->next_page; page < impl.entries.size()) {
|
||||
// Validate that the entry is mapped.
|
||||
if (auto const paddr = impl.entries[page].Pointer(true); paddr != 0) {
|
||||
// Populate the results and return true
|
||||
out_entry->phys_addr = GetInteger(m_system.DeviceMemory().GetPhysicalAddr(paddr + context->next_offset));
|
||||
context->next_page += 1;
|
||||
context->next_offset += PageSize;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
context->next_page += 1;
|
||||
context->next_offset += PageSize;
|
||||
// Otherwise return false
|
||||
return false;
|
||||
}
|
||||
|
||||
Result KPageTableBase::CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr,
|
||||
size_t size, KMemoryState state_mask,
|
||||
KMemoryState state, KMemoryPermission perm_mask,
|
||||
@@ -940,7 +970,7 @@ Result KPageTableBase::QueryMappingImpl(KProcessAddress* out, KPhysicalAddress a
|
||||
size_t tot_size = 0;
|
||||
|
||||
next_valid =
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), region_start);
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), region_start);
|
||||
next_entry.block_size =
|
||||
(next_entry.block_size - (GetInteger(region_start) & (next_entry.block_size - 1)));
|
||||
|
||||
@@ -976,7 +1006,7 @@ Result KPageTableBase::QueryMappingImpl(KProcessAddress* out, KPhysicalAddress a
|
||||
break;
|
||||
}
|
||||
|
||||
next_valid = impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
next_valid = ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||
}
|
||||
|
||||
// Check the last entry.
|
||||
@@ -1754,7 +1784,7 @@ Result KPageTableBase::MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_
|
||||
// Begin traversal.
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
R_UNLESS(impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), addr),
|
||||
R_UNLESS(BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), addr),
|
||||
ResultInvalidCurrentMemory);
|
||||
|
||||
// Prepare tracking variables.
|
||||
@@ -1764,7 +1794,7 @@ Result KPageTableBase::MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_
|
||||
|
||||
// Iterate, adding to group as we go.
|
||||
while (tot_size < size) {
|
||||
R_UNLESS(impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)),
|
||||
R_UNLESS(ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)),
|
||||
ResultInvalidCurrentMemory);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
@@ -1828,7 +1858,7 @@ bool KPageTableBase::IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr
|
||||
// Begin traversal.
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
if (!impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), addr)) {
|
||||
if (!BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), addr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1839,7 +1869,7 @@ bool KPageTableBase::IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr
|
||||
|
||||
// Iterate, comparing expected to actual.
|
||||
while (tot_size < size) {
|
||||
if (!impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context))) {
|
||||
if (!ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1896,7 +1926,7 @@ Result KPageTableBase::GetContiguousMemoryRangeWithState(
|
||||
// Begin a traversal.
|
||||
TraversalContext context;
|
||||
TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0};
|
||||
R_UNLESS(impl.BeginTraversal(std::addressof(cur_entry), std::addressof(context), address),
|
||||
R_UNLESS(BeginTraversal(impl, std::addressof(cur_entry), std::addressof(context), address),
|
||||
ResultInvalidCurrentMemory);
|
||||
|
||||
// Traverse until we have enough size or we aren't contiguous any more.
|
||||
@@ -1905,7 +1935,7 @@ Result KPageTableBase::GetContiguousMemoryRangeWithState(
|
||||
for (contig_size =
|
||||
cur_entry.block_size - (GetInteger(phys_address) & (cur_entry.block_size - 1));
|
||||
contig_size < size; contig_size += cur_entry.block_size) {
|
||||
if (!impl.ContinueTraversal(std::addressof(cur_entry), std::addressof(context))) {
|
||||
if (!ContinueTraversal(impl, std::addressof(cur_entry), std::addressof(context))) {
|
||||
break;
|
||||
}
|
||||
if (cur_entry.phys_addr != phys_address + contig_size) {
|
||||
@@ -2334,7 +2364,7 @@ Result KPageTableBase::QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out,
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid =
|
||||
m_impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), virt_addr);
|
||||
BeginTraversal(m_impl, std::addressof(next_entry), std::addressof(context), virt_addr);
|
||||
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
||||
|
||||
// Set tracking variables.
|
||||
@@ -2345,7 +2375,7 @@ Result KPageTableBase::QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out,
|
||||
while (true) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
m_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ContinueTraversal(m_impl, std::addressof(next_entry), std::addressof(context));
|
||||
if (!traverse_valid) {
|
||||
break;
|
||||
}
|
||||
@@ -2567,7 +2597,7 @@ Result KPageTableBase::UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddre
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
ASSERT(
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_address));
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), dst_address));
|
||||
|
||||
// Check that the physical region matches.
|
||||
R_UNLESS(next_entry.phys_addr == phys_addr, ResultInvalidMemoryRegion);
|
||||
@@ -2577,7 +2607,7 @@ Result KPageTableBase::UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddre
|
||||
next_entry.block_size - (GetInteger(phys_addr) & (next_entry.block_size - 1));
|
||||
checked_size < size; checked_size += next_entry.block_size) {
|
||||
// Continue the traversal.
|
||||
ASSERT(impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)));
|
||||
ASSERT(ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)));
|
||||
|
||||
// Check that the physical region matches.
|
||||
R_UNLESS(next_entry.phys_addr == phys_addr + checked_size, ResultInvalidMemoryRegion);
|
||||
@@ -3029,7 +3059,7 @@ Result KPageTableBase::InvalidateProcessDataCache(KProcessAddress address, size_
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid =
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), address);
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), address);
|
||||
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
||||
|
||||
// Prepare tracking variables.
|
||||
@@ -3041,7 +3071,7 @@ Result KPageTableBase::InvalidateProcessDataCache(KProcessAddress address, size_
|
||||
while (tot_size < size) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
@@ -3129,7 +3159,7 @@ Result KPageTableBase::ReadDebugMemory(KProcessAddress dst_address, KProcessAddr
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid =
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_address);
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), src_address);
|
||||
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
||||
|
||||
// Prepare tracking variables.
|
||||
@@ -3167,7 +3197,7 @@ Result KPageTableBase::ReadDebugMemory(KProcessAddress dst_address, KProcessAddr
|
||||
while (tot_size < size) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
@@ -3225,7 +3255,7 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid =
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_address);
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), dst_address);
|
||||
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
||||
|
||||
// Prepare tracking variables.
|
||||
@@ -3267,7 +3297,7 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd
|
||||
while (tot_size < size) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
@@ -3728,7 +3758,7 @@ Result KPageTableBase::CopyMemoryFromLinearToUser(
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid =
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_addr);
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), src_addr);
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
// Prepare tracking variables.
|
||||
@@ -3768,7 +3798,7 @@ Result KPageTableBase::CopyMemoryFromLinearToUser(
|
||||
while (tot_size < size) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
@@ -3822,7 +3852,7 @@ Result KPageTableBase::CopyMemoryFromLinearToKernel(
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid =
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_addr);
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), src_addr);
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
// Prepare tracking variables.
|
||||
@@ -3845,7 +3875,7 @@ Result KPageTableBase::CopyMemoryFromLinearToKernel(
|
||||
while (tot_size < size) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
@@ -3902,7 +3932,7 @@ Result KPageTableBase::CopyMemoryFromUserToLinear(
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid =
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_addr);
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), dst_addr);
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
// Prepare tracking variables.
|
||||
@@ -3941,7 +3971,7 @@ Result KPageTableBase::CopyMemoryFromUserToLinear(
|
||||
while (tot_size < size) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
@@ -3997,7 +4027,7 @@ Result KPageTableBase::CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, si
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid =
|
||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_addr);
|
||||
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), dst_addr);
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
// Prepare tracking variables.
|
||||
@@ -4020,7 +4050,7 @@ Result KPageTableBase::CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, si
|
||||
while (tot_size < size) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
@@ -4089,10 +4119,10 @@ Result KPageTableBase::CopyMemoryFromHeapToHeap(
|
||||
bool traverse_valid;
|
||||
|
||||
// Begin traversal.
|
||||
traverse_valid = src_impl.BeginTraversal(std::addressof(src_next_entry),
|
||||
traverse_valid = BeginTraversal(src_impl, std::addressof(src_next_entry),
|
||||
std::addressof(src_context), src_addr);
|
||||
ASSERT(traverse_valid);
|
||||
traverse_valid = dst_impl.BeginTraversal(std::addressof(dst_next_entry),
|
||||
traverse_valid = BeginTraversal(dst_impl, std::addressof(dst_next_entry),
|
||||
std::addressof(dst_context), dst_addr);
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
@@ -4127,7 +4157,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeap(
|
||||
if (ofs + cur_copy_size != size) {
|
||||
if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) {
|
||||
// Continue the src traversal.
|
||||
traverse_valid = src_impl.ContinueTraversal(std::addressof(src_next_entry),
|
||||
traverse_valid = ContinueTraversal(src_impl, std::addressof(src_next_entry),
|
||||
std::addressof(src_context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
@@ -4138,7 +4168,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeap(
|
||||
if (cur_dst_addr + cur_min_size ==
|
||||
dst_next_entry.phys_addr + dst_next_entry.block_size) {
|
||||
// Continue the dst traversal.
|
||||
traverse_valid = dst_impl.ContinueTraversal(std::addressof(dst_next_entry),
|
||||
traverse_valid = ContinueTraversal(dst_impl, std::addressof(dst_next_entry),
|
||||
std::addressof(dst_context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
@@ -4223,10 +4253,10 @@ Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination(
|
||||
bool traverse_valid;
|
||||
|
||||
// Begin traversal.
|
||||
traverse_valid = src_impl.BeginTraversal(std::addressof(src_next_entry),
|
||||
traverse_valid = BeginTraversal(src_impl, std::addressof(src_next_entry),
|
||||
std::addressof(src_context), src_addr);
|
||||
ASSERT(traverse_valid);
|
||||
traverse_valid = dst_impl.BeginTraversal(std::addressof(dst_next_entry),
|
||||
traverse_valid = BeginTraversal(dst_impl, std::addressof(dst_next_entry),
|
||||
std::addressof(dst_context), dst_addr);
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
@@ -4261,7 +4291,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination(
|
||||
if (ofs + cur_copy_size != size) {
|
||||
if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) {
|
||||
// Continue the src traversal.
|
||||
traverse_valid = src_impl.ContinueTraversal(std::addressof(src_next_entry),
|
||||
traverse_valid = ContinueTraversal(src_impl, std::addressof(src_next_entry),
|
||||
std::addressof(src_context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
@@ -4272,7 +4302,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination(
|
||||
if (cur_dst_addr + cur_min_size ==
|
||||
dst_next_entry.phys_addr + dst_next_entry.block_size) {
|
||||
// Continue the dst traversal.
|
||||
traverse_valid = dst_impl.ContinueTraversal(std::addressof(dst_next_entry),
|
||||
traverse_valid = ContinueTraversal(dst_impl, std::addressof(dst_next_entry),
|
||||
std::addressof(dst_context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
@@ -4547,7 +4577,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
|
||||
// Begin traversal.
|
||||
TraversalContext context;
|
||||
TraversalEntry next_entry;
|
||||
bool traverse_valid = src_impl.BeginTraversal(std::addressof(next_entry),
|
||||
bool traverse_valid = BeginTraversal(src_impl, std::addressof(next_entry),
|
||||
std::addressof(context), aligned_src_start);
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
@@ -4597,7 +4627,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
|
||||
// If the block's size was one page, we may need to continue traversal.
|
||||
if (cur_block_size == 0 && aligned_src_size > PageSize) {
|
||||
traverse_valid =
|
||||
src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ContinueTraversal(src_impl, std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
cur_block_addr = next_entry.phys_addr;
|
||||
@@ -4610,7 +4640,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
|
||||
while (aligned_src_start + tot_block_size < mapping_src_end) {
|
||||
// Continue the traversal.
|
||||
traverse_valid =
|
||||
src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ContinueTraversal(src_impl, std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
// Process the block.
|
||||
@@ -4653,7 +4683,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
|
||||
if (mapped_block_end + cur_block_size < aligned_src_end &&
|
||||
cur_block_size == last_block_size) {
|
||||
traverse_valid =
|
||||
src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
||||
ContinueTraversal(src_impl, std::addressof(next_entry), std::addressof(context));
|
||||
ASSERT(traverse_valid);
|
||||
|
||||
cur_block_addr = next_entry.phys_addr;
|
||||
@@ -5601,7 +5631,7 @@ Result KPageTableBase::UnmapProcessMemory(KProcessAddress dst_address, size_t si
|
||||
ContiguousRangeInfo(KPageTableBase& pt, KProcessAddress address, size_t size)
|
||||
: m_pt(pt), m_remaining_size(size) {
|
||||
// Begin a traversal.
|
||||
ASSERT(m_pt.GetImpl().BeginTraversal(std::addressof(m_entry),
|
||||
ASSERT(m_pt.BeginTraversal(m_pt.GetImpl(), std::addressof(m_entry),
|
||||
std::addressof(m_context), address));
|
||||
|
||||
// Setup tracking fields.
|
||||
@@ -5632,7 +5662,7 @@ Result KPageTableBase::UnmapProcessMemory(KProcessAddress dst_address, size_t si
|
||||
void DetermineContiguousBlockExtents() {
|
||||
// Continue traversing until we're not contiguous, or we have enough.
|
||||
while (m_cur_size < m_remaining_size) {
|
||||
ASSERT(m_pt.GetImpl().ContinueTraversal(std::addressof(m_entry),
|
||||
ASSERT(m_pt.ContinueTraversal(m_pt.GetImpl(), std::addressof(m_entry),
|
||||
std::addressof(m_context)));
|
||||
|
||||
// If we're not contiguous, we're done.
|
||||
|
||||
@@ -370,6 +370,10 @@ private:
|
||||
size_t num_pages, size_t alignment, size_t offset,
|
||||
size_t guard_pages) const;
|
||||
|
||||
bool BeginTraversal(const Common::PageTable& impl, TraversalEntry* out_entry, TraversalContext* out_context,
|
||||
Common::ProcessAddress address) const;
|
||||
bool ContinueTraversal(const Common::PageTable& impl, TraversalEntry* out_entry, TraversalContext* context) const;
|
||||
|
||||
Result CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, size_t size,
|
||||
KMemoryState state_mask, KMemoryState state,
|
||||
KMemoryPermission perm_mask, KMemoryPermission perm,
|
||||
@@ -474,7 +478,14 @@ private:
|
||||
// Validate pre-conditions.
|
||||
ASSERT(this->IsLockedByCurrentThread());
|
||||
|
||||
return this->GetImpl().GetPhysicalAddress(out, virt_addr);
|
||||
if (virt_addr > (1ULL << m_address_space_width)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*out = m_system.DeviceMemory().GetPhysicalAddr(
|
||||
this->GetImpl().entries[GetInteger(virt_addr) >> PageBits].Pointer(true) + GetInteger(virt_addr));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -73,20 +73,16 @@ Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) {
|
||||
R_TRY(m_manager_display_service->CreateManagedLayer(
|
||||
out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()}));
|
||||
|
||||
m_manager_display_service->SetLayerVisibility(m_visible, *out_layer_id);
|
||||
(void)m_display_service->GetContainer()->SetLayerStackMask(*out_layer_id,
|
||||
this->GetLayerStackMask());
|
||||
|
||||
if (m_applet_id != AppletId::Application) {
|
||||
(void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id);
|
||||
if (m_applet_id == AppletId::OverlayDisplay) {
|
||||
(void)m_manager_display_service->SetLayerZIndex(Overlay, *out_layer_id);
|
||||
(void)m_manager_display_service->SetLayerZIndex(-1, *out_layer_id);
|
||||
(void)m_display_service->GetContainer()->SetLayerIsOverlay(*out_layer_id, true);
|
||||
} else {
|
||||
(void)m_manager_display_service->SetLayerZIndex(Foreground, *out_layer_id);
|
||||
(void)m_manager_display_service->SetLayerZIndex(1, *out_layer_id);
|
||||
}
|
||||
}
|
||||
|
||||
m_display_service->GetContainer()->SetLayerZIndex(*out_layer_id, true);
|
||||
(void)m_display_service->GetContainer()->SetLayerZIndex(*out_layer_id, true);
|
||||
m_managed_display_layers.emplace(*out_layer_id);
|
||||
|
||||
R_SUCCEED();
|
||||
@@ -126,16 +122,14 @@ Result DisplayLayerManager::IsSystemBufferSharingEnabled() {
|
||||
|
||||
// Ensure the overlay layer is visible
|
||||
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
|
||||
(void)m_manager_display_service->SetLayerBlending(m_blending_enabled, m_system_shared_layer_id);
|
||||
s32 initial_z = Foreground;
|
||||
m_manager_display_service->SetLayerBlending(m_blending_enabled, m_system_shared_layer_id);
|
||||
s32 initial_z = 1;
|
||||
(void)m_display_service->GetContainer()->SetLayerZIndex(m_system_shared_layer_id, true);
|
||||
if (m_applet_id == AppletId::OverlayDisplay) {
|
||||
initial_z = Overlay;
|
||||
(void)m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id);
|
||||
initial_z = -1;
|
||||
(void)m_display_service->GetContainer()->SetLayerIsOverlay(m_system_shared_layer_id, true);
|
||||
}
|
||||
m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id);
|
||||
m_display_service->GetContainer()->SetLayerZIndex(m_system_shared_layer_id, true);
|
||||
m_managed_display_layers.emplace(m_system_shared_layer_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -142,7 +142,6 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa
|
||||
{10, &IReadOnlyApplicationControlDataInterface::ListApplicationIcon, "ListApplicationIcon"},
|
||||
{13, &IReadOnlyApplicationControlDataInterface::ListApplicationTitle, "ListApplicationTitle"},
|
||||
{19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData3>, "GetApplicationControlData"},
|
||||
{23, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData3>, "GetApplicationControlData"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
+42
-39
@@ -101,8 +101,10 @@ struct Memory::Impl {
|
||||
}
|
||||
|
||||
u64 protect_bytes = 0, protect_begin = 0;
|
||||
|
||||
current_page_table->entries.CommitRegion(vaddr >> YUZU_PAGEBITS, (vaddr + size) >> YUZU_PAGEBITS);
|
||||
for (u64 addr = vaddr; addr < vaddr + size; addr += YUZU_PAGESIZE) {
|
||||
const Common::PageType page_type = current_page_table->entries[addr >> YUZU_PAGEBITS].ptr.Type();
|
||||
const Common::PageType page_type = current_page_table->entries.GetUnchecked(addr >> YUZU_PAGEBITS).Type();
|
||||
switch (page_type) {
|
||||
case Common::PageType::RasterizerCachedMemory:
|
||||
if (protect_bytes > 0) {
|
||||
@@ -123,16 +125,14 @@ struct Memory::Impl {
|
||||
}
|
||||
|
||||
[[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(u64 vaddr) const {
|
||||
Common::PhysicalAddress const paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].addr;
|
||||
if (paddr)
|
||||
return system.DeviceMemory().GetPointer<u8>(paddr + vaddr);
|
||||
if (u64 paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].Pointer(true); paddr)
|
||||
return reinterpret_cast<u8*>(paddr) + vaddr;
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] u8* GetPointerFromDebugMemory(u64 vaddr) const {
|
||||
const Common::PhysicalAddress paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].addr;
|
||||
if (paddr != 0)
|
||||
return system.DeviceMemory().GetPointer<u8>(paddr + vaddr);
|
||||
if (u64 paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].Pointer(true); paddr)
|
||||
return reinterpret_cast<u8*>(paddr) + vaddr;
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -243,10 +243,12 @@ struct Memory::Impl {
|
||||
std::size_t page_index = addr >> YUZU_PAGEBITS;
|
||||
std::size_t page_offset = addr & YUZU_PAGEMASK;
|
||||
bool user_accessible = true;
|
||||
|
||||
current_page_table->entries.CommitRegion(page_index, page_index + (size >> YUZU_PAGEBITS) + 1);
|
||||
while (remaining_size != 0) {
|
||||
const std::size_t copy_amount = (std::min)(std::size_t(YUZU_PAGESIZE) - page_offset, remaining_size);
|
||||
const auto current_vaddr = u64((page_index << YUZU_PAGEBITS) + page_offset);
|
||||
const auto [pointer, type] = current_page_table->entries[page_index].ptr.PointerType();
|
||||
const auto [pointer, type, _] = current_page_table->entries.GetUnchecked(page_index).PointerTypeBlock();
|
||||
switch (type) {
|
||||
case Common::PageType::Unmapped: {
|
||||
user_accessible = false;
|
||||
@@ -297,10 +299,10 @@ struct Memory::Impl {
|
||||
}
|
||||
|
||||
[[nodiscard]] inline const u8* GetSpan(const VAddr addr, const std::size_t size) const noexcept {
|
||||
return (current_page_table->entries[addr >> YUZU_PAGEBITS].block == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].block) ? GetPointerSilent(addr) : nullptr;
|
||||
return (current_page_table->entries[addr >> YUZU_PAGEBITS].Block() == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].Block()) ? GetPointerSilent(addr) : nullptr;
|
||||
}
|
||||
[[nodiscard]] inline u8* GetSpan(const VAddr addr, const std::size_t size) noexcept {
|
||||
return (current_page_table->entries[addr >> YUZU_PAGEBITS].block == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].block) ? GetPointerSilent(addr) : nullptr;
|
||||
return (current_page_table->entries[addr >> YUZU_PAGEBITS].Block() == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].Block()) ? GetPointerSilent(addr) : nullptr;
|
||||
}
|
||||
|
||||
bool WriteBlockImpl(const Common::ProcessAddress addr, const void* buffer, const std::size_t size, bool unsafe) {
|
||||
@@ -404,11 +406,14 @@ struct Memory::Impl {
|
||||
// The region is at a granularity of CPU pages.
|
||||
|
||||
const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1;
|
||||
|
||||
current_page_table->entries.CommitRegion(vaddr >> YUZU_PAGEBITS, (vaddr >> YUZU_PAGEBITS) + num_pages);
|
||||
for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) {
|
||||
const Common::PageType page_type = current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Type();
|
||||
auto& entry = current_page_table->entries.GetUnchecked(vaddr >> YUZU_PAGEBITS);
|
||||
const auto [pointer, type, block] = entry.PointerTypeBlock(true);
|
||||
if (debug) {
|
||||
// Switch page type to debug if now debug
|
||||
switch (page_type) {
|
||||
switch (type) {
|
||||
case Common::PageType::Unmapped:
|
||||
ASSERT(false && "Attempted to mark unmapped pages as debug");
|
||||
break;
|
||||
@@ -417,14 +422,14 @@ struct Memory::Impl {
|
||||
// Page is already marked.
|
||||
break;
|
||||
case Common::PageType::Memory:
|
||||
current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(0, Common::PageType::DebugMemory);
|
||||
entry.MarkDebug(pointer, block);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
} else {
|
||||
// Switch page type to non-debug if now non-debug
|
||||
switch (page_type) {
|
||||
switch (type) {
|
||||
case Common::PageType::Unmapped:
|
||||
ASSERT(false && "Attempted to mark unmapped pages as non-debug");
|
||||
break;
|
||||
@@ -433,8 +438,7 @@ struct Memory::Impl {
|
||||
// Don't mess with already non-debug or rasterizer memory.
|
||||
break;
|
||||
case Common::PageType::DebugMemory: {
|
||||
u8* const pointer = GetPointerFromDebugMemory(vaddr & ~YUZU_PAGEMASK);
|
||||
current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(uintptr_t(pointer) - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory);
|
||||
entry.Store(false, Common::PageType::Memory, block, pointer);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -466,8 +470,10 @@ struct Memory::Impl {
|
||||
// is different). This assumes the specified GPU address region is contiguous as well.
|
||||
|
||||
const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1;
|
||||
current_page_table->entries.CommitRegion(vaddr >> YUZU_PAGEBITS, (vaddr >> YUZU_PAGEBITS) + num_pages);
|
||||
for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) {
|
||||
const Common::PageType page_type= current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Type();
|
||||
auto& entry = current_page_table->entries.GetUnchecked(vaddr >> YUZU_PAGEBITS);
|
||||
const Common::PageType page_type = entry.Type();
|
||||
if (cached) {
|
||||
// Switch page type to cached if now cached
|
||||
switch (page_type) {
|
||||
@@ -477,7 +483,7 @@ struct Memory::Impl {
|
||||
break;
|
||||
case Common::PageType::DebugMemory:
|
||||
case Common::PageType::Memory:
|
||||
current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(0, Common::PageType::RasterizerCachedMemory);
|
||||
entry.MarkRasterizerCached();
|
||||
break;
|
||||
case Common::PageType::RasterizerCachedMemory:
|
||||
// There can be more than one GPU region mapped per CPU region, so it's common
|
||||
@@ -499,13 +505,13 @@ struct Memory::Impl {
|
||||
// that this area is already unmarked as cached.
|
||||
break;
|
||||
case Common::PageType::RasterizerCachedMemory: {
|
||||
if (u8* const pointer = GetPointerFromRasterizerCachedMemory(vaddr & ~YUZU_PAGEMASK); pointer == nullptr) {
|
||||
if (auto [ptr, _, block] = entry.PointerTypeBlock(true); ptr == 0) {
|
||||
// It's possible that this function has been called while updating the
|
||||
// pagetable after unmapping a VMA. In that case the underlying VMA will no
|
||||
// longer exist, and we should just leave the pagetable entry blank.
|
||||
current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(0, Common::PageType::Unmapped);
|
||||
entry.Store(false, Common::PageType::Unmapped, block, 0);
|
||||
} else {
|
||||
current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(uintptr_t(pointer) - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory);
|
||||
entry.Store(false, Common::PageType::Memory, block, ptr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -539,22 +545,18 @@ struct Memory::Impl {
|
||||
ASSERT_MSG(type != Common::PageType::Memory,
|
||||
"Mapping memory page without a pointer @ {:016x}", base * YUZU_PAGESIZE);
|
||||
|
||||
while (base != end) {
|
||||
page_table.entries[base].ptr.Store(0, type);
|
||||
page_table.entries[base].addr = 0;
|
||||
page_table.entries[base].block = 0;
|
||||
base += 1;
|
||||
}
|
||||
page_table.entries.ZeroRegion(base, end);
|
||||
} else {
|
||||
auto orig_base = base;
|
||||
while (base != end) {
|
||||
auto host_ptr = uintptr_t(system.DeviceMemory().GetPointer<u8>(target)) - (base << YUZU_PAGEBITS);
|
||||
auto backing = GetInteger(target) - (base << YUZU_PAGEBITS);
|
||||
page_table.entries[base].ptr.Store(host_ptr, type);
|
||||
page_table.entries[base].addr = backing;
|
||||
page_table.entries[base].block = orig_base << YUZU_PAGEBITS;
|
||||
auto current_block = block_count.fetch_add(1, std::memory_order_relaxed);
|
||||
ASSERT(current_block != 65535);
|
||||
|
||||
ASSERT_MSG(page_table.entries[base].ptr.Pointer(),
|
||||
page_table.entries.CommitRegion(base, end);
|
||||
while (base != end) {
|
||||
auto host_ptr = reinterpret_cast<u64>(system.DeviceMemory().GetPointer<u8>(target)) - (base << YUZU_PAGEBITS);;
|
||||
auto& entry = page_table.entries.GetUnchecked(base);
|
||||
|
||||
entry.Store(false, type, current_block, host_ptr);
|
||||
ASSERT_MSG(page_table.entries[base].Pointer(),
|
||||
"memory mapping base yield a nullptr within the table");
|
||||
|
||||
base += 1;
|
||||
@@ -569,11 +571,11 @@ struct Memory::Impl {
|
||||
vaddr &= 0xffffffffffffULL;
|
||||
if (AddressSpaceContains(*current_page_table, vaddr, 1)) [[likely]] {
|
||||
// Avoid adding any extra logic to this fast-path block
|
||||
const uintptr_t raw_pointer = current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Raw();
|
||||
if (const uintptr_t pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) [[likely]] {
|
||||
const auto raw = current_page_table->entries[vaddr >> YUZU_PAGEBITS].Raw();
|
||||
if (auto pointer = Common::PageTable::PageEntryData::ExtractPointer(raw); pointer) [[likely]] {
|
||||
return reinterpret_cast<u8*>(pointer + vaddr);
|
||||
} else {
|
||||
switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
|
||||
switch (static_cast<Common::PageType>(raw.type)) {
|
||||
case Common::PageType::Memory:
|
||||
ASSERT_MSG(false, "Mapped memory page without a pointer @ {:#016x}", vaddr);
|
||||
return nullptr;
|
||||
@@ -773,6 +775,7 @@ struct Memory::Impl {
|
||||
#else
|
||||
Common::HostMemory* host_buffer{};
|
||||
#endif
|
||||
std::atomic<u16> block_count = 0;
|
||||
};
|
||||
|
||||
Memory::Memory(Core::System& system_) : system{system_} {
|
||||
@@ -811,7 +814,7 @@ bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const {
|
||||
if (page >= page_table.entries.size()) {
|
||||
return false;
|
||||
}
|
||||
const auto [pointer, type] = page_table.entries[page].ptr.PointerType();
|
||||
const auto [pointer, type, _] = page_table.entries[page].PointerTypeBlock();
|
||||
return pointer != 0 || type == Common::PageType::RasterizerCachedMemory ||
|
||||
type == Common::PageType::DebugMemory;
|
||||
}
|
||||
|
||||
@@ -371,8 +371,10 @@ EmitConfig A32AddressSpace::GetEmitConfig() {
|
||||
|
||||
.page_table_pointer = std::bit_cast<u64>(conf.page_table),
|
||||
.page_table_address_space_bits = 32,
|
||||
.page_table_pointer_mask_bits = conf.page_table_pointer_mask_bits,
|
||||
.page_table_pointer_mask = conf.page_table_pointer_mask,
|
||||
.page_table_log2_stride = conf.page_table_log2_stride,
|
||||
.page_table_marked_bit = conf.page_table_marked_bit,
|
||||
.page_table_sign_extension = conf.page_table_sign_extension,
|
||||
.silently_mirror_page_table = true,
|
||||
.absolute_offset_page_table = conf.absolute_offset_page_table,
|
||||
.detect_misaligned_access_via_page_table = conf.detect_misaligned_access_via_page_table,
|
||||
|
||||
@@ -31,7 +31,7 @@ struct Jit::Impl final {
|
||||
, core(conf) {}
|
||||
|
||||
HaltReason Run() {
|
||||
ASSERT(!jit_interface->is_executing);
|
||||
DEBUG_ASSERT(!jit_interface->is_executing);
|
||||
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&halt_reason)));
|
||||
|
||||
jit_interface->is_executing = true;
|
||||
@@ -42,7 +42,7 @@ struct Jit::Impl final {
|
||||
}
|
||||
|
||||
HaltReason Step() {
|
||||
ASSERT(!jit_interface->is_executing);
|
||||
DEBUG_ASSERT(!jit_interface->is_executing);
|
||||
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&halt_reason)));
|
||||
|
||||
jit_interface->is_executing = true;
|
||||
|
||||
@@ -545,8 +545,10 @@ EmitConfig A64AddressSpace::GetEmitConfig() {
|
||||
|
||||
.page_table_pointer = std::bit_cast<u64>(conf.page_table),
|
||||
.page_table_address_space_bits = conf.page_table_address_space_bits,
|
||||
.page_table_pointer_mask_bits = conf.page_table_pointer_mask_bits,
|
||||
.page_table_pointer_mask = conf.page_table_pointer_mask,
|
||||
.page_table_log2_stride = conf.page_table_log2_stride,
|
||||
.page_table_marked_bit = conf.page_table_marked_bit,
|
||||
.page_table_sign_extension = conf.page_table_sign_extension,
|
||||
.silently_mirror_page_table = conf.silently_mirror_page_table,
|
||||
.absolute_offset_page_table = conf.absolute_offset_page_table,
|
||||
.detect_misaligned_access_via_page_table = conf.detect_misaligned_access_via_page_table,
|
||||
|
||||
@@ -31,7 +31,7 @@ struct Jit::Impl final {
|
||||
, core(conf) {}
|
||||
|
||||
HaltReason Run() {
|
||||
ASSERT(!is_executing);
|
||||
DEBUG_ASSERT(!is_executing);
|
||||
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&halt_reason)));
|
||||
is_executing = true;
|
||||
HaltReason hr = core.Run(current_address_space, current_state, &halt_reason);
|
||||
@@ -41,7 +41,7 @@ struct Jit::Impl final {
|
||||
}
|
||||
|
||||
HaltReason Step() {
|
||||
ASSERT(!is_executing);
|
||||
DEBUG_ASSERT(!is_executing);
|
||||
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&halt_reason)));
|
||||
is_executing = true;
|
||||
HaltReason hr = core.Step(current_address_space, current_state, &halt_reason);
|
||||
|
||||
@@ -57,7 +57,7 @@ constexpr RegisterList ToRegList(oaknut::Reg reg) {
|
||||
if (reg.is_vector()) {
|
||||
return RegisterList{1} << (reg.index() + 32);
|
||||
}
|
||||
ASSERT(reg.index() != 31 && "ZR not allowed in reg list");
|
||||
DEBUG_ASSERT(reg.index() != 31 && "ZR not allowed in reg list");
|
||||
if (reg.index() == -1) {
|
||||
return RegisterList{1} << 31;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ AddressSpace::AddressSpace(std::size_t code_cache_size)
|
||||
, code(mem.ptr(), mem.ptr())
|
||||
, fastmem_manager(exception_handler)
|
||||
{
|
||||
ASSERT(code_cache_size <= 128 * 1024 * 1024 && "code_cache_size > 128 MiB not currently supported");
|
||||
DEBUG_ASSERT(code_cache_size <= 128 * 1024 * 1024 && "code_cache_size > 128 MiB not currently supported");
|
||||
|
||||
exception_handler.Register(mem, code_cache_size);
|
||||
exception_handler.SetFastmemCallback([this](u64 host_pc) {
|
||||
@@ -115,9 +115,13 @@ EmittedBlockInfo AddressSpace::Emit(IR::Block block) {
|
||||
|
||||
EmittedBlockInfo block_info = EmitArm64(code, std::move(block), GetEmitConfig(), fastmem_manager);
|
||||
|
||||
ASSERT(block_entries.insert({block.Location(), block_info.entry_point}).second);
|
||||
ASSERT(reverse_block_entries.insert({block_info.entry_point, block.Location()}).second);
|
||||
ASSERT(block_infos.insert({block_info.entry_point, block_info}).second);
|
||||
[[maybe_unused]] bool r = true;
|
||||
r &= block_entries.insert({block.Location(), block_info.entry_point}).second;
|
||||
DEBUG_ASSERT(r);
|
||||
r &= reverse_block_entries.insert({block_info.entry_point, block.Location()}).second;
|
||||
DEBUG_ASSERT(r);
|
||||
r &= block_infos.insert({block_info.entry_point, block_info}).second;
|
||||
DEBUG_ASSERT(r);
|
||||
|
||||
Link(block_info);
|
||||
RelinkForDescriptor(block.Location(), block_info.entry_point);
|
||||
|
||||
@@ -54,7 +54,7 @@ void EmitIR<IR::Opcode::PushRSB>(oaknut::CodeGenerator& code, EmitContext& ctx,
|
||||
}
|
||||
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ASSERT(args[0].IsImmediate());
|
||||
DEBUG_ASSERT(args[0].IsImmediate());
|
||||
const IR::LocationDescriptor target{args[0].GetImmediateU64()};
|
||||
|
||||
code.LDR(Wscratch2, SP, offsetof(StackLayout, rsb_ptr));
|
||||
@@ -71,19 +71,19 @@ void EmitIR<IR::Opcode::PushRSB>(oaknut::CodeGenerator& code, EmitContext& ctx,
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetCarryFromOp>(oaknut::CodeGenerator&, EmitContext& ctx, IR::Inst* inst) {
|
||||
[[maybe_unused]] auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ASSERT(ctx.reg_alloc.WasValueDefined(inst));
|
||||
DEBUG_ASSERT(ctx.reg_alloc.WasValueDefined(inst));
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetOverflowFromOp>(oaknut::CodeGenerator&, EmitContext& ctx, IR::Inst* inst) {
|
||||
[[maybe_unused]] auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ASSERT(ctx.reg_alloc.WasValueDefined(inst));
|
||||
DEBUG_ASSERT(ctx.reg_alloc.WasValueDefined(inst));
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetGEFromOp>(oaknut::CodeGenerator&, EmitContext& ctx, IR::Inst* inst) {
|
||||
[[maybe_unused]] auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ASSERT(ctx.reg_alloc.WasValueDefined(inst));
|
||||
DEBUG_ASSERT(ctx.reg_alloc.WasValueDefined(inst));
|
||||
}
|
||||
|
||||
template<>
|
||||
@@ -149,13 +149,13 @@ void EmitIR<IR::Opcode::GetNZFromOp>(oaknut::CodeGenerator& code, EmitContext& c
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetUpperFromOp>(oaknut::CodeGenerator&, EmitContext& ctx, IR::Inst* inst) {
|
||||
[[maybe_unused]] auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ASSERT(ctx.reg_alloc.WasValueDefined(inst));
|
||||
DEBUG_ASSERT(ctx.reg_alloc.WasValueDefined(inst));
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetLowerFromOp>(oaknut::CodeGenerator&, EmitContext& ctx, IR::Inst* inst) {
|
||||
[[maybe_unused]] auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ASSERT(ctx.reg_alloc.WasValueDefined(inst));
|
||||
DEBUG_ASSERT(ctx.reg_alloc.WasValueDefined(inst));
|
||||
}
|
||||
|
||||
template<>
|
||||
@@ -206,9 +206,9 @@ EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block, const E
|
||||
ebi.entry_point = code.xptr<CodePtr>();
|
||||
|
||||
if (ctx.block.GetCondition() == IR::Cond::AL) {
|
||||
ASSERT(!ctx.block.HasConditionFailedLocation());
|
||||
DEBUG_ASSERT(!ctx.block.HasConditionFailedLocation());
|
||||
} else {
|
||||
ASSERT(ctx.block.HasConditionFailedLocation());
|
||||
DEBUG_ASSERT(ctx.block.HasConditionFailedLocation());
|
||||
oaknut::Label pass;
|
||||
|
||||
pass = conf.emit_cond(code, ctx, ctx.block.GetCondition());
|
||||
|
||||
@@ -128,8 +128,10 @@ struct EmitConfig {
|
||||
// Page table
|
||||
u64 page_table_pointer;
|
||||
std::size_t page_table_address_space_bits;
|
||||
int page_table_pointer_mask_bits;
|
||||
u64 page_table_pointer_mask;
|
||||
std::size_t page_table_log2_stride;
|
||||
std::optional<std::uint8_t> page_table_marked_bit;
|
||||
std::optional<std::uint8_t> page_table_sign_extension;
|
||||
bool silently_mirror_page_table;
|
||||
bool absolute_offset_page_table;
|
||||
u8 detect_misaligned_access_via_page_table;
|
||||
|
||||
@@ -234,7 +234,7 @@ void EmitIR<IR::Opcode::A32GetRegister>(oaknut::CodeGenerator& code, EmitContext
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetExtendedRegister32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
|
||||
ASSERT(A32::IsSingleExtReg(reg));
|
||||
DEBUG_ASSERT(A32::IsSingleExtReg(reg));
|
||||
const size_t index = static_cast<size_t>(reg) - static_cast<size_t>(A32::ExtReg::S0);
|
||||
|
||||
auto Sresult = ctx.reg_alloc.WriteS(inst);
|
||||
@@ -248,7 +248,7 @@ void EmitIR<IR::Opcode::A32GetExtendedRegister32>(oaknut::CodeGenerator& code, E
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetVector>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
|
||||
ASSERT(A32::IsDoubleExtReg(reg) || A32::IsQuadExtReg(reg));
|
||||
DEBUG_ASSERT(A32::IsDoubleExtReg(reg) || A32::IsQuadExtReg(reg));
|
||||
|
||||
if (A32::IsDoubleExtReg(reg)) {
|
||||
const size_t index = static_cast<size_t>(reg) - static_cast<size_t>(A32::ExtReg::D0);
|
||||
@@ -266,7 +266,7 @@ void EmitIR<IR::Opcode::A32GetVector>(oaknut::CodeGenerator& code, EmitContext&
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetExtendedRegister64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
|
||||
ASSERT(A32::IsDoubleExtReg(reg));
|
||||
DEBUG_ASSERT(A32::IsDoubleExtReg(reg));
|
||||
const size_t index = static_cast<size_t>(reg) - static_cast<size_t>(A32::ExtReg::D0);
|
||||
|
||||
auto Dresult = ctx.reg_alloc.WriteD(inst);
|
||||
@@ -294,7 +294,7 @@ void EmitIR<IR::Opcode::A32SetRegister>(oaknut::CodeGenerator& code, EmitContext
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetExtendedRegister32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
|
||||
ASSERT(A32::IsSingleExtReg(reg));
|
||||
DEBUG_ASSERT(A32::IsSingleExtReg(reg));
|
||||
const size_t index = static_cast<size_t>(reg) - static_cast<size_t>(A32::ExtReg::S0);
|
||||
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
@@ -309,7 +309,7 @@ void EmitIR<IR::Opcode::A32SetExtendedRegister32>(oaknut::CodeGenerator& code, E
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetExtendedRegister64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
|
||||
ASSERT(A32::IsDoubleExtReg(reg));
|
||||
DEBUG_ASSERT(A32::IsDoubleExtReg(reg));
|
||||
const size_t index = static_cast<size_t>(reg) - static_cast<size_t>(A32::ExtReg::D0);
|
||||
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
@@ -324,7 +324,7 @@ void EmitIR<IR::Opcode::A32SetExtendedRegister64>(oaknut::CodeGenerator& code, E
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetVector>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
|
||||
ASSERT(A32::IsDoubleExtReg(reg) || A32::IsQuadExtReg(reg));
|
||||
DEBUG_ASSERT(A32::IsDoubleExtReg(reg) || A32::IsQuadExtReg(reg));
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
|
||||
if (A32::IsDoubleExtReg(reg)) {
|
||||
|
||||
@@ -194,8 +194,8 @@ void EmitIR<IR::Opcode::TestBit>(oaknut::CodeGenerator& code, EmitContext& ctx,
|
||||
auto Xresult = ctx.reg_alloc.WriteX(inst);
|
||||
auto Xoperand = ctx.reg_alloc.ReadX(args[0]);
|
||||
RegAlloc::Realize(Xresult, Xoperand);
|
||||
ASSERT(args[1].IsImmediate());
|
||||
ASSERT(args[1].GetImmediateU8() < 64);
|
||||
DEBUG_ASSERT(args[1].IsImmediate());
|
||||
DEBUG_ASSERT(args[1].GetImmediateU8() < 64);
|
||||
|
||||
code.UBFX(Xresult, Xoperand, args[1].GetImmediateU8(), 1);
|
||||
}
|
||||
@@ -893,9 +893,9 @@ static void EmitAddSub(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst*
|
||||
|
||||
if (overflow_inst) {
|
||||
// There is a limited set of circumstances where this is required, so assert for this.
|
||||
ASSERT(!sub);
|
||||
ASSERT(!nzcv_inst);
|
||||
ASSERT(args[2].IsImmediate() && args[2].GetImmediateU1() == false);
|
||||
DEBUG_ASSERT(!sub);
|
||||
DEBUG_ASSERT(!nzcv_inst);
|
||||
DEBUG_ASSERT(args[2].IsImmediate() && args[2].GetImmediateU1() == false);
|
||||
|
||||
auto Rb = ctx.reg_alloc.ReadReg<bitsize>(args[1]);
|
||||
auto Woverflow = ctx.reg_alloc.WriteW(overflow_inst);
|
||||
@@ -1134,7 +1134,7 @@ static void EmitBitOp(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* i
|
||||
if constexpr (!std::is_same_v<EmitFn2, std::nullptr_t>) {
|
||||
const auto nz_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetNZFromOp);
|
||||
const auto nzcv_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetNZCVFromOp);
|
||||
ASSERT(!(nz_inst && nzcv_inst));
|
||||
DEBUG_ASSERT(!(nz_inst && nzcv_inst));
|
||||
const auto flag_inst = nz_inst ? nz_inst : nzcv_inst;
|
||||
|
||||
if (flag_inst) {
|
||||
@@ -1171,7 +1171,7 @@ template<std::size_t bitsize>
|
||||
static void EmitAndNot(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
const auto nz_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetNZFromOp);
|
||||
const auto nzcv_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetNZCVFromOp);
|
||||
ASSERT(!(nz_inst && nzcv_inst));
|
||||
DEBUG_ASSERT(!(nz_inst && nzcv_inst));
|
||||
const auto flag_inst = nz_inst ? nz_inst : nzcv_inst;
|
||||
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
@@ -1402,7 +1402,7 @@ void EmitIR<IR::Opcode::CountLeadingZeros64>(oaknut::CodeGenerator& code, EmitCo
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ExtractRegister32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ASSERT(args[2].IsImmediate());
|
||||
DEBUG_ASSERT(args[2].IsImmediate());
|
||||
|
||||
auto Wresult = ctx.reg_alloc.WriteW(inst);
|
||||
auto Wop1 = ctx.reg_alloc.ReadW(args[0]);
|
||||
@@ -1416,7 +1416,7 @@ void EmitIR<IR::Opcode::ExtractRegister32>(oaknut::CodeGenerator& code, EmitCont
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ExtractRegister64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ASSERT(args[2].IsImmediate());
|
||||
DEBUG_ASSERT(args[2].IsImmediate());
|
||||
|
||||
auto Xresult = ctx.reg_alloc.WriteX(inst);
|
||||
auto Xop1 = ctx.reg_alloc.ReadX(args[0]);
|
||||
@@ -1430,7 +1430,7 @@ void EmitIR<IR::Opcode::ExtractRegister64>(oaknut::CodeGenerator& code, EmitCont
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ReplicateBit32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ASSERT(args[1].IsImmediate());
|
||||
DEBUG_ASSERT(args[1].IsImmediate());
|
||||
|
||||
auto Wresult = ctx.reg_alloc.WriteW(inst);
|
||||
auto Wvalue = ctx.reg_alloc.ReadW(args[0]);
|
||||
@@ -1444,7 +1444,7 @@ void EmitIR<IR::Opcode::ReplicateBit32>(oaknut::CodeGenerator& code, EmitContext
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ReplicateBit64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ASSERT(args[1].IsImmediate());
|
||||
DEBUG_ASSERT(args[1].IsImmediate());
|
||||
|
||||
auto Xresult = ctx.reg_alloc.WriteX(inst);
|
||||
auto Xvalue = ctx.reg_alloc.ReadX(args[0]);
|
||||
|
||||
@@ -68,7 +68,7 @@ static void EmitConvert(oaknut::CodeGenerator&, EmitContext& ctx, IR::Inst* inst
|
||||
RegAlloc::Realize(Vto, Vfrom);
|
||||
ctx.fpsr.Load();
|
||||
|
||||
ASSERT(rounding_mode == ctx.FPCR().RMode());
|
||||
DEBUG_ASSERT(rounding_mode == ctx.FPCR().RMode());
|
||||
|
||||
emit(Vto, Vfrom);
|
||||
}
|
||||
@@ -106,8 +106,8 @@ static void EmitToFixed(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst*
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ASSERT(fbits == 0);
|
||||
ASSERT(bitsize_to != 16);
|
||||
DEBUG_ASSERT(fbits == 0);
|
||||
DEBUG_ASSERT(bitsize_to != 16);
|
||||
if constexpr (is_signed) {
|
||||
switch (rounding_mode) {
|
||||
case FP::RoundingMode::ToNearest_TieEven:
|
||||
@@ -449,7 +449,7 @@ void EmitIR<IR::Opcode::FPRoundInt32>(oaknut::CodeGenerator& code, EmitContext&
|
||||
ctx.fpsr.Load();
|
||||
|
||||
if (exact) {
|
||||
ASSERT(ctx.FPCR().RMode() == rounding_mode);
|
||||
DEBUG_ASSERT(ctx.FPCR().RMode() == rounding_mode);
|
||||
code.FRINTX(Sresult, Soperand);
|
||||
} else {
|
||||
switch (rounding_mode) {
|
||||
@@ -486,7 +486,7 @@ void EmitIR<IR::Opcode::FPRoundInt64>(oaknut::CodeGenerator& code, EmitContext&
|
||||
ctx.fpsr.Load();
|
||||
|
||||
if (exact) {
|
||||
ASSERT(ctx.FPCR().RMode() == rounding_mode);
|
||||
DEBUG_ASSERT(ctx.FPCR().RMode() == rounding_mode);
|
||||
code.FRINTX(Dresult, Doperand);
|
||||
} else {
|
||||
switch (rounding_mode) {
|
||||
|
||||
@@ -273,9 +273,18 @@ std::pair<oaknut::XReg, oaknut::XReg> InlinePageTableEmitVAddrLookup(oaknut::Cod
|
||||
// load x0 = *<(u8*)pagetable + index>
|
||||
code.LDR(Xscratch0, Xpagetable, Xscratch0);
|
||||
|
||||
if (ctx.conf.page_table_pointer_mask_bits != 0) {
|
||||
const u64 mask = u64(~u64(0)) << ctx.conf.page_table_pointer_mask_bits;
|
||||
code.AND(Xscratch0, Xscratch0, mask);
|
||||
if (ctx.conf.page_table_marked_bit) {
|
||||
// check for marked bit
|
||||
code.TBNZ(Xscratch0, *ctx.conf.page_table_marked_bit, *fallback);
|
||||
}
|
||||
|
||||
if (ctx.conf.page_table_pointer_mask != 0) {
|
||||
code.AND(Xscratch0, Xscratch0, ctx.conf.page_table_pointer_mask);
|
||||
}
|
||||
|
||||
// TODO: combine this with page_table_pointer_mask
|
||||
if (ctx.conf.page_table_sign_extension) {
|
||||
code.SBFM(Xscratch0, Xscratch0, 0, *ctx.conf.page_table_sign_extension);
|
||||
}
|
||||
|
||||
code.CBZ(Xscratch0, *fallback);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2022 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
@@ -244,7 +247,7 @@ static void EmitPackedAddSub(oaknut::CodeGenerator& code, EmitContext& ctx, IR::
|
||||
}
|
||||
|
||||
if (ge_inst) {
|
||||
ASSERT(!is_halving);
|
||||
DEBUG_ASSERT(!is_halving);
|
||||
|
||||
auto Vge = ctx.reg_alloc.WriteD(ge_inst);
|
||||
RegAlloc::Realize(Vge);
|
||||
|
||||
@@ -24,7 +24,7 @@ using namespace oaknut::util;
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedAddWithFlag32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
const auto overflow_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp);
|
||||
ASSERT(overflow_inst);
|
||||
DEBUG_ASSERT(overflow_inst);
|
||||
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
auto Wresult = ctx.reg_alloc.WriteW(inst);
|
||||
@@ -44,7 +44,7 @@ void EmitIR<IR::Opcode::SignedSaturatedAddWithFlag32>(oaknut::CodeGenerator& cod
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedSubWithFlag32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
const auto overflow_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp);
|
||||
ASSERT(overflow_inst);
|
||||
DEBUG_ASSERT(overflow_inst);
|
||||
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
auto Wresult = ctx.reg_alloc.WriteW(inst);
|
||||
@@ -67,7 +67,7 @@ void EmitIR<IR::Opcode::SignedSaturation>(oaknut::CodeGenerator& code, EmitConte
|
||||
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
const std::size_t N = args[1].GetImmediateU8();
|
||||
ASSERT(N >= 1 && N <= 32);
|
||||
DEBUG_ASSERT(N >= 1 && N <= 32);
|
||||
|
||||
if (N == 32) {
|
||||
ctx.reg_alloc.DefineAsExisting(inst, args[0]);
|
||||
@@ -113,7 +113,7 @@ void EmitIR<IR::Opcode::UnsignedSaturation>(oaknut::CodeGenerator& code, EmitCon
|
||||
ctx.reg_alloc.SpillFlags();
|
||||
|
||||
const std::size_t N = args[1].GetImmediateU8();
|
||||
ASSERT(N <= 31);
|
||||
DEBUG_ASSERT(N <= 31);
|
||||
const u32 saturated_value = (1u << N) - 1;
|
||||
|
||||
code.MOV(Wscratch0, saturated_value);
|
||||
|
||||
@@ -275,7 +275,7 @@ static void EmitReduce(oaknut::CodeGenerator&, EmitContext& ctx, IR::Inst* inst,
|
||||
template<std::size_t size, typename EmitFn>
|
||||
static void EmitGetElement(oaknut::CodeGenerator&, EmitContext& ctx, IR::Inst* inst, EmitFn emit) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ASSERT(args[1].IsImmediate());
|
||||
DEBUG_ASSERT(args[1].IsImmediate());
|
||||
const u8 index = args[1].GetImmediateU8();
|
||||
|
||||
auto Rresult = ctx.reg_alloc.WriteReg<std::max<std::size_t>(32, size)>(inst);
|
||||
@@ -310,7 +310,7 @@ void EmitIR<IR::Opcode::VectorGetElement64>(oaknut::CodeGenerator& code, EmitCon
|
||||
template<std::size_t size, typename EmitFn>
|
||||
static void EmitSetElement(oaknut::CodeGenerator&, EmitContext& ctx, IR::Inst* inst, EmitFn emit) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ASSERT(args[1].IsImmediate());
|
||||
DEBUG_ASSERT(args[1].IsImmediate());
|
||||
const u8 index = args[1].GetImmediateU8();
|
||||
|
||||
auto Qvector = ctx.reg_alloc.ReadWriteQ(args[0], inst);
|
||||
@@ -650,7 +650,7 @@ void EmitIR<IR::Opcode::VectorExtract>(oaknut::CodeGenerator& code, EmitContext&
|
||||
auto Qa = ctx.reg_alloc.ReadQ(args[0]);
|
||||
auto Qb = ctx.reg_alloc.ReadQ(args[1]);
|
||||
const u8 position = args[2].GetImmediateU8();
|
||||
ASSERT(position % 8 == 0);
|
||||
DEBUG_ASSERT(position % 8 == 0);
|
||||
RegAlloc::Realize(Qresult, Qa, Qb);
|
||||
|
||||
code.EXT(Qresult->B16(), Qa->B16(), Qb->B16(), position / 8);
|
||||
@@ -663,7 +663,7 @@ void EmitIR<IR::Opcode::VectorExtractLower>(oaknut::CodeGenerator& code, EmitCon
|
||||
auto Da = ctx.reg_alloc.ReadD(args[0]);
|
||||
auto Db = ctx.reg_alloc.ReadD(args[1]);
|
||||
const u8 position = args[2].GetImmediateU8();
|
||||
ASSERT(position % 8 == 0);
|
||||
DEBUG_ASSERT(position % 8 == 0);
|
||||
RegAlloc::Realize(Dresult, Da, Db);
|
||||
|
||||
code.EXT(Dresult->B8(), Da->B8(), Db->B8(), position / 8);
|
||||
@@ -958,7 +958,7 @@ void EmitIR<IR::Opcode::VectorMultiply32>(oaknut::CodeGenerator& code, EmitConte
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::VectorMultiply64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(ctx.conf.very_verbose_debugging_output && "VectorMultiply64 is for debugging only");
|
||||
DEBUG_ASSERT(ctx.conf.very_verbose_debugging_output && "VectorMultiply64 is for debugging only");
|
||||
EmitThreeOp(code, ctx, inst, [&](auto& Qresult, auto& Qa, auto& Qb) {
|
||||
code.FMOV(Xscratch0, Qa->toD());
|
||||
code.FMOV(Xscratch1, Qb->toD());
|
||||
@@ -1289,7 +1289,7 @@ void EmitIR<IR::Opcode::VectorReduceAdd64>(oaknut::CodeGenerator& code, EmitCont
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::VectorRotateWholeVectorRight>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitImmShift<8>(code, ctx, inst, [&](auto Vresult, auto Voperand, u8 shift_amount) {
|
||||
ASSERT(shift_amount % 8 == 0);
|
||||
DEBUG_ASSERT(shift_amount % 8 == 0);
|
||||
const u8 ext_imm = (shift_amount % 128) / 8;
|
||||
code.EXT(Vresult, Voperand, Voperand, ext_imm);
|
||||
});
|
||||
@@ -1602,12 +1602,12 @@ void EmitIR<IR::Opcode::VectorSub64>(oaknut::CodeGenerator& code, EmitContext& c
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::VectorTable>(oaknut::CodeGenerator&, EmitContext&, IR::Inst* inst) {
|
||||
// Do nothing. We *want* to hold on to the refcount for our arguments, so VectorTableLookup can use our arguments.
|
||||
ASSERT(inst->UseCount() == 1 && "Table cannot be used multiple times");
|
||||
DEBUG_ASSERT(inst->UseCount() == 1 && "Table cannot be used multiple times");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::VectorTableLookup64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(inst->GetArg(1).GetInst()->GetOpcode() == IR::Opcode::VectorTable);
|
||||
DEBUG_ASSERT(inst->GetArg(1).GetInst()->GetOpcode() == IR::Opcode::VectorTable);
|
||||
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
auto table = ctx.reg_alloc.GetArgumentInfo(inst->GetArg(1).GetInst());
|
||||
@@ -1674,7 +1674,7 @@ void EmitIR<IR::Opcode::VectorTableLookup64>(oaknut::CodeGenerator& code, EmitCo
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::VectorTableLookup128>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(inst->GetArg(1).GetInst()->GetOpcode() == IR::Opcode::VectorTable);
|
||||
DEBUG_ASSERT(inst->GetArg(1).GetInst()->GetOpcode() == IR::Opcode::VectorTable);
|
||||
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
auto table = ctx.reg_alloc.GetArgumentInfo(inst->GetArg(1).GetInst());
|
||||
|
||||
@@ -139,7 +139,7 @@ static void EmitFromFixed(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Ins
|
||||
const u8 fbits = args[1].GetImmediateU8();
|
||||
const FP::RoundingMode rounding_mode = static_cast<FP::RoundingMode>(args[2].GetImmediateU8());
|
||||
const bool fpcr_controlled = args[3].GetImmediateU1();
|
||||
ASSERT(rounding_mode == ctx.FPCR(fpcr_controlled).RMode());
|
||||
DEBUG_ASSERT(rounding_mode == ctx.FPCR(fpcr_controlled).RMode());
|
||||
RegAlloc::Realize(Qto, Qfrom);
|
||||
|
||||
MaybeStandardFPSCRValue(code, ctx, fpcr_controlled, [&] {
|
||||
@@ -199,7 +199,7 @@ void EmitToFixed(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ASSERT(fbits == 0);
|
||||
DEBUG_ASSERT(fbits == 0);
|
||||
if constexpr (is_signed) {
|
||||
switch (rounding_mode) {
|
||||
case FP::RoundingMode::ToNearest_TieEven:
|
||||
@@ -346,7 +346,7 @@ template<>
|
||||
void EmitIR<IR::Opcode::FPVectorFromHalf32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
const auto rounding_mode = static_cast<FP::RoundingMode>(args[1].GetImmediateU8());
|
||||
ASSERT(rounding_mode == FP::RoundingMode::ToNearest_TieEven);
|
||||
DEBUG_ASSERT(rounding_mode == FP::RoundingMode::ToNearest_TieEven);
|
||||
const bool fpcr_controlled = args[2].GetImmediateU1();
|
||||
|
||||
auto Qresult = ctx.reg_alloc.WriteQ(inst);
|
||||
@@ -617,7 +617,7 @@ void EmitIR<IR::Opcode::FPVectorRoundInt32>(oaknut::CodeGenerator& code, EmitCon
|
||||
|
||||
MaybeStandardFPSCRValue(code, ctx, fpcr_controlled, [&] {
|
||||
if (exact) {
|
||||
ASSERT(ctx.FPCR(fpcr_controlled).RMode() == rounding_mode);
|
||||
DEBUG_ASSERT(ctx.FPCR(fpcr_controlled).RMode() == rounding_mode);
|
||||
code.FRINTX(Qresult->S4(), Qoperand->S4());
|
||||
} else {
|
||||
switch (rounding_mode) {
|
||||
@@ -657,7 +657,7 @@ void EmitIR<IR::Opcode::FPVectorRoundInt64>(oaknut::CodeGenerator& code, EmitCon
|
||||
|
||||
MaybeStandardFPSCRValue(code, ctx, fpcr_controlled, [&] {
|
||||
if (exact) {
|
||||
ASSERT(ctx.FPCR(fpcr_controlled).RMode() == rounding_mode);
|
||||
DEBUG_ASSERT(ctx.FPCR(fpcr_controlled).RMode() == rounding_mode);
|
||||
code.FRINTX(Qresult->D2(), Qoperand->D2());
|
||||
} else {
|
||||
switch (rounding_mode) {
|
||||
@@ -743,7 +743,7 @@ template<>
|
||||
void EmitIR<IR::Opcode::FPVectorToHalf32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
const auto rounding_mode = static_cast<FP::RoundingMode>(args[1].GetImmediateU8());
|
||||
ASSERT(rounding_mode == FP::RoundingMode::ToNearest_TieEven);
|
||||
DEBUG_ASSERT(rounding_mode == FP::RoundingMode::ToNearest_TieEven);
|
||||
const bool fpcr_controlled = args[2].GetImmediateU1();
|
||||
|
||||
auto Dresult = ctx.reg_alloc.WriteD(inst);
|
||||
|
||||
@@ -53,19 +53,19 @@ bool Argument::GetImmediateU1() const {
|
||||
|
||||
u8 Argument::GetImmediateU8() const {
|
||||
const u64 imm = value.GetImmediateAsU64();
|
||||
ASSERT(imm < 0x100);
|
||||
DEBUG_ASSERT(imm < 0x100);
|
||||
return u8(imm);
|
||||
}
|
||||
|
||||
u16 Argument::GetImmediateU16() const {
|
||||
const u64 imm = value.GetImmediateAsU64();
|
||||
ASSERT(imm < 0x10000);
|
||||
DEBUG_ASSERT(imm < 0x10000);
|
||||
return u16(imm);
|
||||
}
|
||||
|
||||
u32 Argument::GetImmediateU32() const {
|
||||
const u64 imm = value.GetImmediateAsU64();
|
||||
ASSERT(imm < 0x100000000);
|
||||
DEBUG_ASSERT(imm < 0x100000000);
|
||||
return u32(imm);
|
||||
}
|
||||
|
||||
@@ -74,12 +74,12 @@ u64 Argument::GetImmediateU64() const {
|
||||
}
|
||||
|
||||
IR::Cond Argument::GetImmediateCond() const {
|
||||
ASSERT(IsImmediate() && GetType() == IR::Type::Cond);
|
||||
DEBUG_ASSERT(IsImmediate() && GetType() == IR::Type::Cond);
|
||||
return value.GetCond();
|
||||
}
|
||||
|
||||
IR::AccType Argument::GetImmediateAccType() const {
|
||||
ASSERT(IsImmediate() && GetType() == IR::Type::AccType);
|
||||
DEBUG_ASSERT(IsImmediate() && GetType() == IR::Type::AccType);
|
||||
return value.GetAccType();
|
||||
}
|
||||
|
||||
@@ -92,12 +92,12 @@ bool HostLocInfo::Contains(const IR::Inst* value) const {
|
||||
}
|
||||
|
||||
void HostLocInfo::SetupScratchLocation() {
|
||||
ASSERT(IsCompletelyEmpty());
|
||||
DEBUG_ASSERT(IsCompletelyEmpty());
|
||||
realized = true;
|
||||
}
|
||||
|
||||
void HostLocInfo::SetupLocation(const IR::Inst* value) {
|
||||
ASSERT(IsCompletelyEmpty());
|
||||
DEBUG_ASSERT(IsCompletelyEmpty());
|
||||
values.clear();
|
||||
values.push_back(value);
|
||||
realized = true;
|
||||
@@ -135,7 +135,7 @@ RegAlloc::ArgumentInfo RegAlloc::GetArgumentInfo(IR::Inst* inst) {
|
||||
const IR::Value arg = inst->GetArg(i);
|
||||
ret[i].value = arg;
|
||||
if (!arg.IsImmediate() && !IsValuelessType(arg.GetType())) {
|
||||
ASSERT(ValueLocation(arg.GetInst()) && "argument must already been defined");
|
||||
DEBUG_ASSERT(ValueLocation(arg.GetInst()) && "argument must already been defined");
|
||||
ValueInfo(arg.GetInst()).uses_this_inst++;
|
||||
}
|
||||
}
|
||||
@@ -174,11 +174,11 @@ void RegAlloc::PrepareForCall(std::optional<Argument::copyable_reference> arg0,
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (args[i]) {
|
||||
if (args[i]->get().GetType() == IR::Type::U128) {
|
||||
ASSERT(fprs[nsrn].IsCompletelyEmpty());
|
||||
DEBUG_ASSERT(fprs[nsrn].IsCompletelyEmpty());
|
||||
LoadCopyInto(args[i]->get().value, oaknut::QReg{nsrn});
|
||||
nsrn++;
|
||||
} else {
|
||||
ASSERT(gprs[ngrn].IsCompletelyEmpty());
|
||||
DEBUG_ASSERT(gprs[ngrn].IsCompletelyEmpty());
|
||||
LoadCopyInto(args[i]->get().value, oaknut::XReg{ngrn});
|
||||
ngrn++;
|
||||
}
|
||||
@@ -192,7 +192,7 @@ void RegAlloc::PrepareForCall(std::optional<Argument::copyable_reference> arg0,
|
||||
|
||||
void RegAlloc::DefineAsExisting(IR::Inst* inst, Argument& arg) {
|
||||
defined_insts.insert(inst);
|
||||
ASSERT(!ValueLocation(inst));
|
||||
DEBUG_ASSERT(!ValueLocation(inst));
|
||||
|
||||
if (arg.value.IsImmediate()) {
|
||||
inst->ReplaceUsesWith(arg.value);
|
||||
@@ -206,9 +206,9 @@ void RegAlloc::DefineAsExisting(IR::Inst* inst, Argument& arg) {
|
||||
|
||||
void RegAlloc::DefineAsRegister(IR::Inst* inst, oaknut::Reg reg) {
|
||||
defined_insts.insert(inst);
|
||||
ASSERT(!ValueLocation(inst));
|
||||
DEBUG_ASSERT(!ValueLocation(inst));
|
||||
auto& info = reg.is_vector() ? fprs[reg.index()] : gprs[reg.index()];
|
||||
ASSERT(info.IsCompletelyEmpty());
|
||||
DEBUG_ASSERT(info.IsCompletelyEmpty());
|
||||
info.values.push_back(inst);
|
||||
info.expected_uses += inst->UseCount();
|
||||
}
|
||||
@@ -228,18 +228,18 @@ void RegAlloc::UpdateAllUses() {
|
||||
|
||||
void RegAlloc::AssertAllUnlocked() const {
|
||||
const auto is_unlocked = [](const auto& i) { return !i.locked && !i.realized; };
|
||||
ASSERT(std::all_of(gprs.begin(), gprs.end(), is_unlocked));
|
||||
ASSERT(std::all_of(fprs.begin(), fprs.end(), is_unlocked));
|
||||
ASSERT(is_unlocked(flags));
|
||||
ASSERT(std::all_of(spills.begin(), spills.end(), is_unlocked));
|
||||
DEBUG_ASSERT(std::all_of(gprs.begin(), gprs.end(), is_unlocked));
|
||||
DEBUG_ASSERT(std::all_of(fprs.begin(), fprs.end(), is_unlocked));
|
||||
DEBUG_ASSERT(is_unlocked(flags));
|
||||
DEBUG_ASSERT(std::all_of(spills.begin(), spills.end(), is_unlocked));
|
||||
}
|
||||
|
||||
void RegAlloc::AssertNoMoreUses() const {
|
||||
const auto is_empty = [](const auto& i) { return i.IsCompletelyEmpty(); };
|
||||
ASSERT(std::all_of(gprs.begin(), gprs.end(), is_empty));
|
||||
ASSERT(std::all_of(fprs.begin(), fprs.end(), is_empty));
|
||||
ASSERT(is_empty(flags));
|
||||
ASSERT(std::all_of(spills.begin(), spills.end(), is_empty));
|
||||
DEBUG_ASSERT(std::all_of(gprs.begin(), gprs.end(), is_empty));
|
||||
DEBUG_ASSERT(std::all_of(fprs.begin(), fprs.end(), is_empty));
|
||||
DEBUG_ASSERT(is_empty(flags));
|
||||
DEBUG_ASSERT(std::all_of(spills.begin(), spills.end(), is_empty));
|
||||
}
|
||||
|
||||
void RegAlloc::EmitVerboseDebuggingOutput() {
|
||||
@@ -271,7 +271,7 @@ void RegAlloc::EmitVerboseDebuggingOutput() {
|
||||
|
||||
template<HostLoc::Kind kind>
|
||||
int RegAlloc::GenerateImmediate(const IR::Value& value) {
|
||||
ASSERT(value.GetType() != IR::Type::U1);
|
||||
DEBUG_ASSERT(value.GetType() != IR::Type::U1);
|
||||
if constexpr (kind == HostLoc::Kind::Gpr) {
|
||||
const int new_location_index = AllocateRegister(gprs, gpr_order);
|
||||
SpillGpr(new_location_index);
|
||||
@@ -309,15 +309,15 @@ int RegAlloc::RealizeReadImpl(const IR::Value& value) {
|
||||
}
|
||||
|
||||
const auto current_location = ValueLocation(value.GetInst());
|
||||
ASSERT(current_location);
|
||||
DEBUG_ASSERT(current_location);
|
||||
|
||||
if (current_location->kind == required_kind) {
|
||||
ValueInfo(*current_location).realized = true;
|
||||
return current_location->index;
|
||||
}
|
||||
|
||||
ASSERT(!bool(ValueInfo(*current_location).realized));
|
||||
ASSERT(bool(ValueInfo(*current_location).locked));
|
||||
DEBUG_ASSERT(!bool(ValueInfo(*current_location).realized));
|
||||
DEBUG_ASSERT(bool(ValueInfo(*current_location).locked));
|
||||
|
||||
if constexpr (required_kind == HostLoc::Kind::Gpr) {
|
||||
const int new_location_index = AllocateRegister(gprs, gpr_order);
|
||||
@@ -328,7 +328,7 @@ int RegAlloc::RealizeReadImpl(const IR::Value& value) {
|
||||
UNREACHABLE(); //logic error
|
||||
case HostLoc::Kind::Fpr:
|
||||
code.FMOV(oaknut::XReg{new_location_index}, oaknut::DReg{current_location->index});
|
||||
// ASSERT size fits
|
||||
// DEBUG_ASSERT size fits
|
||||
break;
|
||||
case HostLoc::Kind::Spill:
|
||||
code.LDR(oaknut::XReg{new_location_index}, SP, spill_offset + current_location->index * spill_slot_size);
|
||||
@@ -355,7 +355,7 @@ int RegAlloc::RealizeReadImpl(const IR::Value& value) {
|
||||
code.LDR(oaknut::QReg{new_location_index}, SP, spill_offset + current_location->index * spill_slot_size);
|
||||
break;
|
||||
case HostLoc::Kind::Flags:
|
||||
ASSERT(false && "Moving from flags into fprs is not currently supported");
|
||||
DEBUG_ASSERT(false && "Moving from flags into fprs is not currently supported");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -372,7 +372,7 @@ int RegAlloc::RealizeReadImpl(const IR::Value& value) {
|
||||
template<HostLoc::Kind kind>
|
||||
int RegAlloc::RealizeWriteImpl(const IR::Inst* value) {
|
||||
defined_insts.insert(value);
|
||||
ASSERT(!ValueLocation(value));
|
||||
DEBUG_ASSERT(!ValueLocation(value));
|
||||
|
||||
if constexpr (kind == HostLoc::Kind::Gpr) {
|
||||
const int new_location_index = AllocateRegister(gprs, gpr_order);
|
||||
@@ -407,7 +407,7 @@ int RegAlloc::RealizeReadWriteImpl(const IR::Value& read_value, const IR::Inst*
|
||||
LoadCopyInto(read_value, oaknut::QReg{write_loc});
|
||||
return write_loc;
|
||||
} else if constexpr (kind == HostLoc::Kind::Flags) {
|
||||
ASSERT(false && "Incorrect function for ReadWrite of flags");
|
||||
DEBUG_ASSERT(false && "Incorrect function for ReadWrite of flags");
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -439,7 +439,7 @@ int RegAlloc::AllocateRegister(const std::array<HostLocInfo, 32>& regs, const st
|
||||
}
|
||||
|
||||
void RegAlloc::SpillGpr(int index) {
|
||||
ASSERT(!gprs[index].locked && !gprs[index].realized);
|
||||
DEBUG_ASSERT(!gprs[index].locked && !gprs[index].realized);
|
||||
if (gprs[index].values.empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -449,7 +449,7 @@ void RegAlloc::SpillGpr(int index) {
|
||||
}
|
||||
|
||||
void RegAlloc::SpillFpr(int index) {
|
||||
ASSERT(!fprs[index].locked && !fprs[index].realized);
|
||||
DEBUG_ASSERT(!fprs[index].locked && !fprs[index].realized);
|
||||
if (fprs[index].values.empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -461,7 +461,7 @@ void RegAlloc::SpillFpr(int index) {
|
||||
void RegAlloc::ReadWriteFlags(Argument& read, IR::Inst* write) {
|
||||
defined_insts.insert(write);
|
||||
const auto current_location = ValueLocation(read.value.GetInst());
|
||||
ASSERT(current_location);
|
||||
DEBUG_ASSERT(current_location);
|
||||
|
||||
if (current_location->kind == HostLoc::Kind::Flags) {
|
||||
if (!flags.IsOneRemainingUse()) {
|
||||
@@ -489,7 +489,7 @@ void RegAlloc::ReadWriteFlags(Argument& read, IR::Inst* write) {
|
||||
}
|
||||
|
||||
void RegAlloc::SpillFlags() {
|
||||
ASSERT(!flags.locked && !flags.realized);
|
||||
DEBUG_ASSERT(!flags.locked && !flags.realized);
|
||||
if (flags.values.empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -501,7 +501,7 @@ void RegAlloc::SpillFlags() {
|
||||
|
||||
int RegAlloc::FindFreeSpill() const {
|
||||
const auto iter = std::find_if(spills.begin(), spills.end(), [](const HostLocInfo& info) { return info.values.empty(); });
|
||||
ASSERT(iter != spills.end() && "All spill locations are full");
|
||||
DEBUG_ASSERT(iter != spills.end() && "All spill locations are full");
|
||||
return static_cast<int>(iter - spills.begin());
|
||||
}
|
||||
|
||||
@@ -512,14 +512,14 @@ void RegAlloc::LoadCopyInto(const IR::Value& value, oaknut::XReg reg) {
|
||||
}
|
||||
|
||||
const auto current_location = ValueLocation(value.GetInst());
|
||||
ASSERT(current_location);
|
||||
DEBUG_ASSERT(current_location);
|
||||
switch (current_location->kind) {
|
||||
case HostLoc::Kind::Gpr:
|
||||
code.MOV(reg, oaknut::XReg{current_location->index});
|
||||
break;
|
||||
case HostLoc::Kind::Fpr:
|
||||
code.FMOV(reg, oaknut::DReg{current_location->index});
|
||||
// ASSERT size fits
|
||||
// DEBUG_ASSERT size fits
|
||||
break;
|
||||
case HostLoc::Kind::Spill:
|
||||
code.LDR(reg, SP, spill_offset + current_location->index * spill_slot_size);
|
||||
@@ -538,7 +538,7 @@ void RegAlloc::LoadCopyInto(const IR::Value& value, oaknut::QReg reg) {
|
||||
}
|
||||
|
||||
const auto current_location = ValueLocation(value.GetInst());
|
||||
ASSERT(current_location);
|
||||
DEBUG_ASSERT(current_location);
|
||||
switch (current_location->kind) {
|
||||
case HostLoc::Kind::Gpr:
|
||||
code.FMOV(reg.toD(), oaknut::XReg{current_location->index});
|
||||
|
||||
@@ -87,7 +87,11 @@ private:
|
||||
};
|
||||
|
||||
MachHandler::MachHandler() {
|
||||
#define KCHECK(x) ASSERT((x) == KERN_SUCCESS && "init failure at " #x)
|
||||
#define KCHECK(x) do { \
|
||||
[[maybe_unused]] auto r = (x); \
|
||||
DEBUG_ASSERT(r == KERN_SUCCESS && "init failure at " #x); \
|
||||
} while (0);
|
||||
|
||||
KCHECK(mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &server_port));
|
||||
KCHECK(mach_port_insert_right(mach_task_self(), server_port, server_port, MACH_MSG_TYPE_MAKE_SEND));
|
||||
KCHECK(task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, server_port, EXCEPTION_STATE | MACH_EXCEPTION_CODES, THREAD_STATE));
|
||||
|
||||
@@ -138,7 +138,7 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
|
||||
#elif defined(ARCHITECTURE_loongarch64)
|
||||
CTX_PC = fc.call_pc;
|
||||
#else
|
||||
ASSERT(false);
|
||||
DEBUG_ASSERT(false);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ void A32AddressSpace::Link(EmittedBlockInfo& block_info) {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT(false && "Invalid relocation target");
|
||||
DEBUG_ASSERT(false && "Invalid relocation target");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ struct Jit::Impl final {
|
||||
, current_address_space(conf) {}
|
||||
|
||||
HaltReason Run() {
|
||||
ASSERT(!jit_interface->is_executing);
|
||||
DEBUG_ASSERT(!jit_interface->is_executing);
|
||||
jit_interface->is_executing = true;
|
||||
|
||||
const auto location_descriptor = current_state.GetLocationDescriptor();
|
||||
@@ -38,7 +38,7 @@ struct Jit::Impl final {
|
||||
}
|
||||
|
||||
HaltReason Step() {
|
||||
ASSERT(!jit_interface->is_executing);
|
||||
DEBUG_ASSERT(!jit_interface->is_executing);
|
||||
jit_interface->is_executing = true;
|
||||
|
||||
const auto location_descriptor = A32::LocationDescriptor{current_state.GetLocationDescriptor()}.SetSingleStepping(true);
|
||||
|
||||
@@ -20,7 +20,7 @@ public:
|
||||
explicit CodeBlock(std::size_t size) noexcept
|
||||
: memsize(size) {
|
||||
mem = static_cast<u8*>(mmap(nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0));
|
||||
ASSERT(mem != MAP_FAILED);
|
||||
DEBUG_ASSERT(mem != MAP_FAILED);
|
||||
la_init_assembler(&as, mem, size);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Dynarmic::Backend::LoongArch64 {
|
||||
|
||||
template<IR::Opcode op>
|
||||
void EmitIR(lagoon_assembler_t&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented opcode");
|
||||
DEBUG_ASSERT(false && "Unimplemented opcode");
|
||||
}
|
||||
|
||||
template<>
|
||||
@@ -36,7 +36,7 @@ void EmitIR<IR::Opcode::LogicalShiftLeft32>(lagoon_assembler_t&, EmitContext& ct
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetCarryFromOp>(lagoon_assembler_t&, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(ctx.reg_alloc.IsValueLive(inst));
|
||||
DEBUG_ASSERT(ctx.reg_alloc.IsValueLive(inst));
|
||||
}
|
||||
|
||||
template<>
|
||||
@@ -98,7 +98,7 @@ EmittedBlockInfo EmitLoongArch64(lagoon_assembler_t& as, IR::Block block, const
|
||||
const auto term = block.GetTerminal();
|
||||
const IR::Term::LeafTerminal* leaft_term = std::get_if<IR::Term::LeafTerminal>(&term);
|
||||
const IR::Term::LinkBlock* link_block_term = std::get_if<IR::Term::LinkBlock>(leaft_term);
|
||||
ASSERT(link_block_term);
|
||||
DEBUG_ASSERT(link_block_term);
|
||||
la_load_immediate64(&as, Xscratch0, link_block_term->next.Value());
|
||||
la_st_w(&as, Xscratch0, Xstate, static_cast<int32_t>(offsetof(A32JitState, regs) + sizeof(u32) * 15));
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ template<>
|
||||
void EmitIR<IR::Opcode::A32SetCpsrNZC>(lagoon_assembler_t& as, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
|
||||
ASSERT(!args[0].IsImmediate() && !args[1].IsImmediate());
|
||||
DEBUG_ASSERT(!args[0].IsImmediate() && !args[1].IsImmediate());
|
||||
|
||||
auto Xnz = ctx.reg_alloc.ReadX(args[0]);
|
||||
auto Xc = ctx.reg_alloc.ReadX(args[1]);
|
||||
|
||||
@@ -22,8 +22,8 @@ void EmitIR<IR::Opcode::LogicalShiftLeft32>(lagoon_assembler_t& as, EmitContext&
|
||||
auto& shift_arg = args[1];
|
||||
auto& carry_arg = args[2];
|
||||
|
||||
ASSERT(carry_inst != nullptr);
|
||||
ASSERT(shift_arg.IsImmediate());
|
||||
DEBUG_ASSERT(carry_inst != nullptr);
|
||||
DEBUG_ASSERT(shift_arg.IsImmediate());
|
||||
|
||||
auto Xresult = ctx.reg_alloc.WriteX(inst);
|
||||
auto Xcarry_out = ctx.reg_alloc.WriteX(carry_inst);
|
||||
|
||||
@@ -42,19 +42,19 @@ bool Argument::GetImmediateU1() const {
|
||||
|
||||
u8 Argument::GetImmediateU8() const {
|
||||
const u64 imm = value.GetImmediateAsU64();
|
||||
ASSERT(imm < 0x100);
|
||||
DEBUG_ASSERT(imm < 0x100);
|
||||
return u8(imm);
|
||||
}
|
||||
|
||||
u16 Argument::GetImmediateU16() const {
|
||||
const u64 imm = value.GetImmediateAsU64();
|
||||
ASSERT(imm < 0x10000);
|
||||
DEBUG_ASSERT(imm < 0x10000);
|
||||
return u16(imm);
|
||||
}
|
||||
|
||||
u32 Argument::GetImmediateU32() const {
|
||||
const u64 imm = value.GetImmediateAsU64();
|
||||
ASSERT(imm < 0x100000000);
|
||||
DEBUG_ASSERT(imm < 0x100000000);
|
||||
return u32(imm);
|
||||
}
|
||||
|
||||
@@ -63,12 +63,12 @@ u64 Argument::GetImmediateU64() const {
|
||||
}
|
||||
|
||||
IR::Cond Argument::GetImmediateCond() const {
|
||||
ASSERT(IsImmediate() && GetType() == IR::Type::Cond);
|
||||
DEBUG_ASSERT(IsImmediate() && GetType() == IR::Type::Cond);
|
||||
return value.GetCond();
|
||||
}
|
||||
|
||||
IR::AccType Argument::GetImmediateAccType() const {
|
||||
ASSERT(IsImmediate() && GetType() == IR::Type::AccType);
|
||||
DEBUG_ASSERT(IsImmediate() && GetType() == IR::Type::AccType);
|
||||
return value.GetAccType();
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ bool HostLocInfo::Contains(const IR::Inst* value) const {
|
||||
}
|
||||
|
||||
void HostLocInfo::SetupScratchLocation() {
|
||||
ASSERT(IsCompletelyEmpty());
|
||||
DEBUG_ASSERT(IsCompletelyEmpty());
|
||||
locked = 1;
|
||||
realized = true;
|
||||
}
|
||||
@@ -103,7 +103,7 @@ RegAlloc::ArgumentInfo RegAlloc::GetArgumentInfo(IR::Inst* inst) {
|
||||
const IR::Value arg = inst->GetArg(i);
|
||||
ret[i].value = arg;
|
||||
if (!arg.IsImmediate() && !IsValuelessType(arg.GetType())) {
|
||||
ASSERT(ValueLocation(arg.GetInst()) && "argument must already been defined");
|
||||
DEBUG_ASSERT(ValueLocation(arg.GetInst()) && "argument must already been defined");
|
||||
ValueInfo(arg.GetInst()).uses_this_inst++;
|
||||
}
|
||||
}
|
||||
@@ -121,7 +121,7 @@ void RegAlloc::UpdateAllUses() {
|
||||
}
|
||||
|
||||
void RegAlloc::DefineAsExisting(IR::Inst* inst, Argument& arg) {
|
||||
ASSERT(!ValueLocation(inst));
|
||||
DEBUG_ASSERT(!ValueLocation(inst));
|
||||
|
||||
if (arg.value.IsImmediate()) {
|
||||
inst->ReplaceUsesWith(arg.value);
|
||||
@@ -136,7 +136,7 @@ void RegAlloc::DefineAsExisting(IR::Inst* inst, Argument& arg) {
|
||||
void RegAlloc::AssertNoMoreUses() const {
|
||||
// TODO: Re-enable this assert once all register allocation issues are fixed
|
||||
// const auto is_empty = [](const auto& i) { return i.IsCompletelyEmpty(); };
|
||||
// ASSERT(std::all_of(hostloc_info.begin(), hostloc_info.end(), is_empty));
|
||||
// DEBUG_ASSERT(std::all_of(hostloc_info.begin(), hostloc_info.end(), is_empty));
|
||||
}
|
||||
|
||||
template<HostLoc::Kind kind>
|
||||
@@ -151,7 +151,7 @@ u32 RegAlloc::GenerateImmediate(const IR::Value& value) {
|
||||
|
||||
return new_location_index;
|
||||
} else if constexpr (kind == HostLoc::Kind::Fpr) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -165,15 +165,15 @@ u32 RegAlloc::RealizeReadImpl(const IR::Value& value) {
|
||||
}
|
||||
|
||||
const auto current_location = ValueLocation(value.GetInst());
|
||||
ASSERT(current_location);
|
||||
DEBUG_ASSERT(current_location);
|
||||
|
||||
if (current_location->kind == required_kind) {
|
||||
ValueInfo(*current_location).realized = true;
|
||||
return current_location->index;
|
||||
}
|
||||
|
||||
ASSERT(!ValueInfo(*current_location).realized);
|
||||
ASSERT(!ValueInfo(*current_location).locked);
|
||||
DEBUG_ASSERT(!ValueInfo(*current_location).realized);
|
||||
DEBUG_ASSERT(!ValueInfo(*current_location).locked);
|
||||
|
||||
if constexpr (required_kind == HostLoc::Kind::Gpr) {
|
||||
const u32 new_location_index = AllocateRegister(gpr_order, GprOffset);
|
||||
@@ -236,7 +236,7 @@ u32 RegAlloc::RealizeWriteImpl(const IR::Inst* value, HostLoc::Kind required_kin
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(!ValueLocation(value));
|
||||
DEBUG_ASSERT(!ValueLocation(value));
|
||||
|
||||
const auto setup_location = [&](HostLocInfo& info) {
|
||||
info = {};
|
||||
@@ -277,7 +277,7 @@ u32 RegAlloc::AllocateRegister(const std::vector<u32>& order, size_t base_offset
|
||||
std::copy_if(order.begin(), order.end(), std::back_inserter(candidates), [&](u32 i) {
|
||||
return !hostloc_info[base_offset + i].locked;
|
||||
});
|
||||
ASSERT(!candidates.empty());
|
||||
DEBUG_ASSERT(!candidates.empty());
|
||||
|
||||
u32 best = candidates[0];
|
||||
size_t min_lru = hostloc_info[base_offset + best].lru_counter;
|
||||
@@ -294,7 +294,7 @@ u32 RegAlloc::AllocateRegister(const std::vector<u32>& order, size_t base_offset
|
||||
|
||||
void RegAlloc::SpillGpr(u32 index) {
|
||||
auto& gpr_info = hostloc_info[GprOffset + index];
|
||||
ASSERT(!gpr_info.locked && !gpr_info.realized);
|
||||
DEBUG_ASSERT(!gpr_info.locked && !gpr_info.realized);
|
||||
if (gpr_info.values.empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -306,7 +306,7 @@ void RegAlloc::SpillGpr(u32 index) {
|
||||
|
||||
void RegAlloc::SpillFpr(u32 index) {
|
||||
auto& fpr_info = hostloc_info[FprOffset + index];
|
||||
ASSERT(!fpr_info.locked && !fpr_info.realized);
|
||||
DEBUG_ASSERT(!fpr_info.locked && !fpr_info.realized);
|
||||
if (fpr_info.values.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ void A32AddressSpace::EmitPrelude() {
|
||||
|
||||
void A32AddressSpace::SetCursorPtr(CodePtr ptr) {
|
||||
ptrdiff_t offset = ptr - GetMemPtr<CodePtr>();
|
||||
ASSERT(offset >= 0);
|
||||
DEBUG_ASSERT(offset >= 0);
|
||||
as.RewindBuffer(offset);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ struct Jit::Impl final {
|
||||
, core(conf) {}
|
||||
|
||||
HaltReason Run() {
|
||||
ASSERT(!jit_interface->is_executing);
|
||||
DEBUG_ASSERT(!jit_interface->is_executing);
|
||||
jit_interface->is_executing = true;
|
||||
HaltReason hr = core.Run(current_address_space, current_state, &halt_reason);
|
||||
RequestCacheInvalidation();
|
||||
@@ -40,9 +40,9 @@ struct Jit::Impl final {
|
||||
}
|
||||
|
||||
HaltReason Step() {
|
||||
ASSERT(!jit_interface->is_executing);
|
||||
DEBUG_ASSERT(!jit_interface->is_executing);
|
||||
jit_interface->is_executing = true;
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
RequestCacheInvalidation();
|
||||
jit_interface->is_executing = false;
|
||||
return HaltReason{};
|
||||
|
||||
@@ -28,12 +28,12 @@ struct Jit::Impl final {
|
||||
, jit_interface(jit_interface) {}
|
||||
|
||||
HaltReason Run() {
|
||||
ASSERT(false);
|
||||
DEBUG_ASSERT(false);
|
||||
return HaltReason{};
|
||||
}
|
||||
|
||||
HaltReason Step() {
|
||||
ASSERT(false);
|
||||
DEBUG_ASSERT(false);
|
||||
return HaltReason{};
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ struct Jit::Impl final {
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
ASSERT(!is_executing);
|
||||
DEBUG_ASSERT(!is_executing);
|
||||
//jit_state = {};
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ class CodeBlock {
|
||||
public:
|
||||
explicit CodeBlock(std::size_t size) noexcept : memsize(size) {
|
||||
mem = (u8*)mmap(nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
ASSERT(mem != nullptr);
|
||||
DEBUG_ASSERT(mem != nullptr);
|
||||
}
|
||||
|
||||
~CodeBlock() noexcept {
|
||||
|
||||
@@ -35,39 +35,39 @@ void EmitIR<IR::Opcode::Identity>(biscuit::Assembler&, EmitContext& ctx, IR::Ins
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Breakpoint>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CallHostFunction>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PushRSB>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetCarryFromOp>(biscuit::Assembler&, EmitContext& ctx, IR::Inst* inst) {
|
||||
[[maybe_unused]] auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ASSERT(ctx.reg_alloc.IsValueLive(inst));
|
||||
DEBUG_ASSERT(ctx.reg_alloc.IsValueLive(inst));
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetOverflowFromOp>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetGEFromOp>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetNZCVFromOp>(biscuit::Assembler&, EmitContext& ctx, IR::Inst* inst) {
|
||||
[[maybe_unused]] auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ASSERT(ctx.reg_alloc.IsValueLive(inst));
|
||||
DEBUG_ASSERT(ctx.reg_alloc.IsValueLive(inst));
|
||||
}
|
||||
|
||||
template<>
|
||||
@@ -87,12 +87,12 @@ void EmitIR<IR::Opcode::GetNZFromOp>(biscuit::Assembler& as, EmitContext& ctx, I
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetUpperFromOp>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetLowerFromOp>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
@@ -109,7 +109,7 @@ void EmitIR<IR::Opcode::GetCFlagFromNZCV>(biscuit::Assembler& as, EmitContext& c
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::NZCVFromPackedFlags>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
EmittedBlockInfo EmitRV64(biscuit::Assembler& as, IR::Block block, const EmitConfig& emit_conf) {
|
||||
|
||||
@@ -228,7 +228,7 @@ void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx) {
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetCheckBit>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
@@ -243,17 +243,17 @@ void EmitIR<IR::Opcode::A32GetRegister>(biscuit::Assembler& as, EmitContext& ctx
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetExtendedRegister32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetExtendedRegister64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetVector>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
@@ -272,27 +272,27 @@ void EmitIR<IR::Opcode::A32SetRegister>(biscuit::Assembler& as, EmitContext& ctx
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetExtendedRegister32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetExtendedRegister64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetVector>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetCpsr>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetCpsr>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
@@ -307,17 +307,17 @@ void EmitIR<IR::Opcode::A32SetCpsrNZCV>(biscuit::Assembler& as, EmitContext& ctx
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetCpsrNZCVRaw>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetCpsrNZCVQ>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetCpsrNZ>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
@@ -325,7 +325,7 @@ void EmitIR<IR::Opcode::A32SetCpsrNZC>(biscuit::Assembler& as, EmitContext& ctx,
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
|
||||
// TODO: Add full implementation
|
||||
ASSERT(!args[0].IsImmediate() && !args[1].IsImmediate());
|
||||
DEBUG_ASSERT(!args[0].IsImmediate() && !args[1].IsImmediate());
|
||||
|
||||
auto Xnz = ctx.reg_alloc.ReadX(args[0]);
|
||||
auto Xc = ctx.reg_alloc.ReadX(args[1]);
|
||||
@@ -341,82 +341,82 @@ void EmitIR<IR::Opcode::A32SetCpsrNZC>(biscuit::Assembler& as, EmitContext& ctx,
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetCFlag>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32OrQFlag>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetGEFlags>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetGEFlags>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetGEFlagsCompressed>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32BXWritePC>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32UpdateUpperLocationDescriptor>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CallSupervisor>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExceptionRaised>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32DataSynchronizationBarrier>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32DataMemoryBarrier>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32InstructionSynchronizationBarrier>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetFpscr>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetFpscr>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetFpscrNZCV>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetFpscrNZCV>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
|
||||
@@ -22,37 +22,37 @@ namespace Dynarmic::Backend::RV64 {
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocInternalOperation>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocSendOneWord>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocSendTwoWords>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocGetOneWord>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocGetTwoWords>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocLoadWords>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocStoreWords>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
|
||||
@@ -22,87 +22,87 @@ namespace Dynarmic::Backend::RV64 {
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ClearExclusive>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ReadMemory8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ReadMemory16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ReadMemory32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ReadMemory64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExclusiveReadMemory8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExclusiveReadMemory16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExclusiveReadMemory32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExclusiveReadMemory64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32WriteMemory8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32WriteMemory16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32WriteMemory32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32WriteMemory64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
|
||||
@@ -22,182 +22,182 @@ namespace Dynarmic::Backend::RV64 {
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetCheckBit>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetCFlag>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetNZCVRaw>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetNZCVRaw>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetNZCV>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetW>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetX>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetS>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetD>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetQ>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetSP>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetFPCR>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetFPSR>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetW>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetX>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetS>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetD>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetQ>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetSP>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetFPCR>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetFPSR>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetPC>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64CallSupervisor>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExceptionRaised>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64DataCacheOperationRaised>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64InstructionCacheOperationRaised>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64DataSynchronizationBarrier>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64DataMemoryBarrier>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64InstructionSynchronizationBarrier>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetCNTFRQ>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetCNTPCT>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetCTR>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetDCZID>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetTPIDR>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetTPIDRRO>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetTPIDR>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
|
||||
@@ -22,107 +22,107 @@ namespace Dynarmic::Backend::RV64 {
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ClearExclusive>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ReadMemory8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ReadMemory16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ReadMemory32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ReadMemory64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ReadMemory128>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveReadMemory8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveReadMemory16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveReadMemory32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveReadMemory64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveReadMemory128>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64WriteMemory8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64WriteMemory16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64WriteMemory32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64WriteMemory64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64WriteMemory128>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveWriteMemory8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveWriteMemory16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveWriteMemory32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveWriteMemory64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveWriteMemory128>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
|
||||
@@ -22,82 +22,82 @@ namespace Dynarmic::Backend::RV64 {
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CRC32Castagnoli8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CRC32Castagnoli16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CRC32Castagnoli32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CRC32Castagnoli64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CRC32ISO8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CRC32ISO16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CRC32ISO32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CRC32ISO64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::AESDecryptSingleRound>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::AESEncryptSingleRound>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::AESInverseMixColumns>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::AESMixColumns>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SM4AccessSubstitutionBox>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SHA256Hash>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SHA256MessageSchedule0>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SHA256MessageSchedule1>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
|
||||
@@ -22,67 +22,67 @@ namespace Dynarmic::Backend::RV64 {
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Pack2x32To1x64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Pack2x64To1x128>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LeastSignificantWord>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LeastSignificantHalf>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LeastSignificantByte>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MostSignificantWord>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MostSignificantBit>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::IsZero32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::IsZero64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::TestBit>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ConditionalSelect32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ConditionalSelect64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ConditionalSelectNZCV>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
@@ -95,8 +95,8 @@ void EmitIR<IR::Opcode::LogicalShiftLeft32>(biscuit::Assembler& as, EmitContext&
|
||||
auto& carry_arg = args[2];
|
||||
|
||||
// TODO: Add full implementation
|
||||
ASSERT(carry_inst != nullptr);
|
||||
ASSERT(shift_arg.IsImmediate());
|
||||
DEBUG_ASSERT(carry_inst != nullptr);
|
||||
DEBUG_ASSERT(shift_arg.IsImmediate());
|
||||
|
||||
auto Xresult = ctx.reg_alloc.WriteX(inst);
|
||||
auto Xcarry_out = ctx.reg_alloc.WriteX(carry_inst);
|
||||
@@ -124,7 +124,7 @@ void EmitIR<IR::Opcode::LogicalShiftLeft32>(biscuit::Assembler& as, EmitContext&
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LogicalShiftLeft64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
@@ -136,8 +136,8 @@ void EmitIR<IR::Opcode::LogicalShiftRight32>(biscuit::Assembler& as, EmitContext
|
||||
auto& shift_arg = args[1];
|
||||
|
||||
// TODO: Add full implementation
|
||||
ASSERT(carry_inst == nullptr);
|
||||
ASSERT(shift_arg.IsImmediate());
|
||||
DEBUG_ASSERT(carry_inst == nullptr);
|
||||
DEBUG_ASSERT(shift_arg.IsImmediate());
|
||||
|
||||
const u8 shift = shift_arg.GetImmediateU8();
|
||||
auto Xresult = ctx.reg_alloc.WriteX(inst);
|
||||
@@ -153,72 +153,72 @@ void EmitIR<IR::Opcode::LogicalShiftRight32>(biscuit::Assembler& as, EmitContext
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LogicalShiftRight64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ArithmeticShiftRight32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ArithmeticShiftRight64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::BitRotateRight32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::BitRotateRight64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::RotateRightExtended>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LogicalShiftLeftMasked32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LogicalShiftLeftMasked64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LogicalShiftRightMasked32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LogicalShiftRightMasked64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ArithmeticShiftRightMasked32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ArithmeticShiftRightMasked64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::RotateRightMasked32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::RotateRightMasked64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<size_t bitsize>
|
||||
@@ -264,7 +264,7 @@ static void AddImmWithFlags(biscuit::Assembler& as, biscuit::GPR rd, biscuit::GP
|
||||
as.SLLI(Xscratch1, Xscratch1, 28);
|
||||
as.OR(flags, flags, Xscratch1);
|
||||
} else {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,7 +279,7 @@ static void EmitAddSub(biscuit::Assembler& as, EmitContext& ctx, IR::Inst* inst)
|
||||
auto Xa = ctx.reg_alloc.ReadX(args[0]);
|
||||
|
||||
if (overflow_inst) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
} else if (nzcv_inst) {
|
||||
if (args[1].IsImmediate()) {
|
||||
const u64 imm = args[1].GetImmediateU64();
|
||||
@@ -294,17 +294,17 @@ static void EmitAddSub(biscuit::Assembler& as, EmitContext& ctx, IR::Inst* inst)
|
||||
AddImmWithFlags<bitsize>(as, *Xresult, *Xa, sub ? -imm : imm, *Xflags);
|
||||
}
|
||||
} else {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
} else {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
} else {
|
||||
if (args[1].IsImmediate()) {
|
||||
const u64 imm = args[1].GetImmediateU64();
|
||||
|
||||
if (args[2].IsImmediate()) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
} else {
|
||||
auto Xnzcv = ctx.reg_alloc.ReadX(args[2]);
|
||||
RegAlloc::Realize(Xresult, Xa, Xnzcv);
|
||||
@@ -317,7 +317,7 @@ static void EmitAddSub(biscuit::Assembler& as, EmitContext& ctx, IR::Inst* inst)
|
||||
as.ADDW(Xresult, Xa, Xscratch0);
|
||||
}
|
||||
} else {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -329,7 +329,7 @@ void EmitIR<IR::Opcode::Add32>(biscuit::Assembler& as, EmitContext& ctx, IR::Ins
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Add64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
@@ -339,237 +339,237 @@ void EmitIR<IR::Opcode::Sub32>(biscuit::Assembler& as, EmitContext& ctx, IR::Ins
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Sub64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Mul32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Mul64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedMultiplyHigh64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedMultiplyHigh64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedDiv32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedDiv64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedDiv32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedDiv64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::And32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::And64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::AndNot32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::AndNot64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Eor32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Eor64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Or32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Or64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Not32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Not64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignExtendByteToWord>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignExtendHalfToWord>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignExtendByteToLong>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignExtendHalfToLong>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignExtendWordToLong>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ZeroExtendByteToWord>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ZeroExtendHalfToWord>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ZeroExtendByteToLong>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ZeroExtendHalfToLong>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ZeroExtendWordToLong>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ZeroExtendLongToQuad>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ByteReverseWord>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ByteReverseHalf>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ByteReverseDual>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CountLeadingZeros32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CountLeadingZeros64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ExtractRegister32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ExtractRegister64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ReplicateBit32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ReplicateBit64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MaxSigned32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MaxSigned64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MaxUnsigned32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MaxUnsigned64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MinSigned32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MinSigned64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MinUnsigned32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MinUnsigned64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
|
||||
@@ -22,442 +22,442 @@ namespace Dynarmic::Backend::RV64 {
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPAbs16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPAbs32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPAbs64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPAdd32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPAdd64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPCompare32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPCompare64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDiv32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDiv64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMax32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMax64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMaxNumeric32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMaxNumeric64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMin32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMin64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMinNumeric32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMinNumeric64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMul32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMul64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMulAdd16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMulAdd32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMulAdd64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMulSub16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMulSub32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMulSub64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMulX32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMulX64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPNeg16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPNeg32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPNeg64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipEstimate16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipEstimate32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipEstimate64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipExponent16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipExponent32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipExponent64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipStepFused16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipStepFused32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipStepFused64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRoundInt16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRoundInt32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRoundInt64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRSqrtEstimate16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRSqrtEstimate32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRSqrtEstimate64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRSqrtStepFused16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRSqrtStepFused32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRSqrtStepFused64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSqrt32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSqrt64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSub32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSub64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPHalfToDouble>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPHalfToSingle>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSingleToDouble>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSingleToHalf>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDoubleToHalf>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDoubleToSingle>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDoubleToFixedS16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDoubleToFixedS32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDoubleToFixedS64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDoubleToFixedU16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDoubleToFixedU32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDoubleToFixedU64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPHalfToFixedS16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPHalfToFixedS32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPHalfToFixedS64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPHalfToFixedU16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPHalfToFixedU32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPHalfToFixedU64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSingleToFixedS16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSingleToFixedS32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSingleToFixedS64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSingleToFixedU16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSingleToFixedU32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSingleToFixedU64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedU16ToSingle>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedS16ToSingle>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedU16ToDouble>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedS16ToDouble>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedU32ToSingle>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedS32ToSingle>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedU32ToDouble>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedS32ToDouble>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedU64ToDouble>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedU64ToSingle>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedS64ToDouble>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedS64ToSingle>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
|
||||
@@ -22,172 +22,172 @@ namespace Dynarmic::Backend::RV64 {
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedAddU8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedAddS8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSubU8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSubS8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedAddU16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedAddS16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSubU16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSubS16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedAddSubU16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedAddSubS16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSubAddU16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSubAddS16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingAddU8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingAddS8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingSubU8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingSubS8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingAddU16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingAddS16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingSubU16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingSubS16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingAddSubU16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingAddSubS16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingSubAddU16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingSubAddS16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSaturatedAddU8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSaturatedAddS8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSaturatedSubU8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSaturatedSubS8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSaturatedAddU16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSaturatedAddS16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSaturatedSubU16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSaturatedSubS16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedAbsDiffSumU8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSelect>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
|
||||
@@ -22,112 +22,112 @@ namespace Dynarmic::Backend::RV64 {
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedAddWithFlag32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedSubWithFlag32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturation>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturation>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedAdd8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedAdd16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedAdd32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedAdd64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedDoublingMultiplyReturnHigh16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedDoublingMultiplyReturnHigh32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedSub8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedSub16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedSub32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedSub64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturatedAdd8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturatedAdd16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturatedAdd32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturatedAdd64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturatedSub8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturatedSub16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturatedSub32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturatedSub64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,337 +22,337 @@ namespace Dynarmic::Backend::RV64 {
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorAbs16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorAbs32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorAbs64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorAdd32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorAdd64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorDiv32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorDiv64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorEqual16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorEqual32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorEqual64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorFromHalf32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorFromSignedFixed32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorFromSignedFixed64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorFromUnsignedFixed32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorFromUnsignedFixed64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorGreater32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorGreater64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorGreaterEqual32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorGreaterEqual64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorMax32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorMax64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorMaxNumeric32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorMaxNumeric64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorMin32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorMin64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorMinNumeric32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorMinNumeric64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorMul32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorMul64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorMulAdd16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorMulAdd32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorMulAdd64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorMulX32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorMulX64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorNeg16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorNeg32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorNeg64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorPairedAdd32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorPairedAdd64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorPairedAddLower32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorPairedAddLower64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorRecipEstimate16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorRecipEstimate32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorRecipEstimate64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorRecipStepFused16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorRecipStepFused32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorRecipStepFused64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorRoundInt16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorRoundInt32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorRoundInt64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorRSqrtEstimate16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorRSqrtEstimate32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorRSqrtEstimate64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorRSqrtStepFused16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorRSqrtStepFused32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorRSqrtStepFused64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorSqrt32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorSqrt64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorSub32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorSub64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorToHalf32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorToSignedFixed16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorToSignedFixed32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorToSignedFixed64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorToUnsignedFixed16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorToUnsignedFixed32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorToUnsignedFixed64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
|
||||
@@ -22,82 +22,82 @@ namespace Dynarmic::Backend::RV64 {
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::VectorSignedSaturatedAdd8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::VectorSignedSaturatedAdd16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::VectorSignedSaturatedAdd32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::VectorSignedSaturatedAdd64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::VectorSignedSaturatedSub8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::VectorSignedSaturatedSub16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::VectorSignedSaturatedSub32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::VectorSignedSaturatedSub64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::VectorUnsignedSaturatedAdd8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::VectorUnsignedSaturatedAdd16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::VectorUnsignedSaturatedAdd32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::VectorUnsignedSaturatedAdd64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::VectorUnsignedSaturatedSub8>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::VectorUnsignedSaturatedSub16>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::VectorUnsignedSaturatedSub32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::VectorUnsignedSaturatedSub64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
|
||||
@@ -44,19 +44,19 @@ bool Argument::GetImmediateU1() const {
|
||||
|
||||
u8 Argument::GetImmediateU8() const {
|
||||
const u64 imm = value.GetImmediateAsU64();
|
||||
ASSERT(imm < 0x100);
|
||||
DEBUG_ASSERT(imm < 0x100);
|
||||
return u8(imm);
|
||||
}
|
||||
|
||||
u16 Argument::GetImmediateU16() const {
|
||||
const u64 imm = value.GetImmediateAsU64();
|
||||
ASSERT(imm < 0x10000);
|
||||
DEBUG_ASSERT(imm < 0x10000);
|
||||
return u16(imm);
|
||||
}
|
||||
|
||||
u32 Argument::GetImmediateU32() const {
|
||||
const u64 imm = value.GetImmediateAsU64();
|
||||
ASSERT(imm < 0x100000000);
|
||||
DEBUG_ASSERT(imm < 0x100000000);
|
||||
return u32(imm);
|
||||
}
|
||||
|
||||
@@ -65,12 +65,12 @@ u64 Argument::GetImmediateU64() const {
|
||||
}
|
||||
|
||||
IR::Cond Argument::GetImmediateCond() const {
|
||||
ASSERT(IsImmediate() && GetType() == IR::Type::Cond);
|
||||
DEBUG_ASSERT(IsImmediate() && GetType() == IR::Type::Cond);
|
||||
return value.GetCond();
|
||||
}
|
||||
|
||||
IR::AccType Argument::GetImmediateAccType() const {
|
||||
ASSERT(IsImmediate() && GetType() == IR::Type::AccType);
|
||||
DEBUG_ASSERT(IsImmediate() && GetType() == IR::Type::AccType);
|
||||
return value.GetAccType();
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ bool HostLocInfo::Contains(const IR::Inst* value) const {
|
||||
}
|
||||
|
||||
void HostLocInfo::SetupScratchLocation() {
|
||||
ASSERT(IsCompletelyEmpty());
|
||||
DEBUG_ASSERT(IsCompletelyEmpty());
|
||||
realized = true;
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ RegAlloc::ArgumentInfo RegAlloc::GetArgumentInfo(IR::Inst* inst) {
|
||||
const IR::Value arg = inst->GetArg(i);
|
||||
ret[i].value = arg;
|
||||
if (!arg.IsImmediate() && !IsValuelessType(arg.GetType())) {
|
||||
ASSERT(ValueLocation(arg.GetInst()) && "argument must already been defined");
|
||||
DEBUG_ASSERT(ValueLocation(arg.GetInst()) && "argument must already been defined");
|
||||
ValueInfo(arg.GetInst()).uses_this_inst++;
|
||||
}
|
||||
}
|
||||
@@ -128,7 +128,7 @@ void RegAlloc::UpdateAllUses() {
|
||||
}
|
||||
|
||||
void RegAlloc::DefineAsExisting(IR::Inst* inst, Argument& arg) {
|
||||
ASSERT(!ValueLocation(inst));
|
||||
DEBUG_ASSERT(!ValueLocation(inst));
|
||||
|
||||
if (arg.value.IsImmediate()) {
|
||||
inst->ReplaceUsesWith(arg.value);
|
||||
@@ -142,15 +142,15 @@ void RegAlloc::DefineAsExisting(IR::Inst* inst, Argument& arg) {
|
||||
|
||||
void RegAlloc::AssertNoMoreUses() const {
|
||||
const auto is_empty = [](const auto& i) { return i.IsCompletelyEmpty(); };
|
||||
ASSERT(std::all_of(gprs.begin(), gprs.end(), is_empty));
|
||||
ASSERT(std::all_of(fprs.begin(), fprs.end(), is_empty));
|
||||
ASSERT(std::all_of(spills.begin(), spills.end(), is_empty));
|
||||
DEBUG_ASSERT(std::all_of(gprs.begin(), gprs.end(), is_empty));
|
||||
DEBUG_ASSERT(std::all_of(fprs.begin(), fprs.end(), is_empty));
|
||||
DEBUG_ASSERT(std::all_of(spills.begin(), spills.end(), is_empty));
|
||||
}
|
||||
|
||||
template<HostLoc::Kind kind>
|
||||
u32 RegAlloc::GenerateImmediate(const IR::Value& value) {
|
||||
// TODO
|
||||
// ASSERT(value.GetType() != IR::Type::U1);
|
||||
// DEBUG_ASSERT(value.GetType() != IR::Type::U1);
|
||||
|
||||
if constexpr (kind == HostLoc::Kind::Gpr) {
|
||||
const u32 new_location_index = AllocateRegister(gprs, gpr_order);
|
||||
@@ -161,7 +161,7 @@ u32 RegAlloc::GenerateImmediate(const IR::Value& value) {
|
||||
|
||||
return new_location_index;
|
||||
} else if constexpr (kind == HostLoc::Kind::Fpr) {
|
||||
ASSERT(false && "Unimplemented instruction");
|
||||
DEBUG_ASSERT(false && "Unimplemented instruction");
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -175,15 +175,15 @@ u32 RegAlloc::RealizeReadImpl(const IR::Value& value) {
|
||||
}
|
||||
|
||||
const auto current_location = ValueLocation(value.GetInst());
|
||||
ASSERT(current_location);
|
||||
DEBUG_ASSERT(current_location);
|
||||
|
||||
if (current_location->kind == required_kind) {
|
||||
ValueInfo(*current_location).realized = true;
|
||||
return current_location->index;
|
||||
}
|
||||
|
||||
ASSERT(!ValueInfo(*current_location).realized);
|
||||
ASSERT(!ValueInfo(*current_location).locked);
|
||||
DEBUG_ASSERT(!ValueInfo(*current_location).realized);
|
||||
DEBUG_ASSERT(!ValueInfo(*current_location).locked);
|
||||
|
||||
if constexpr (required_kind == HostLoc::Kind::Gpr) {
|
||||
const u32 new_location_index = AllocateRegister(gprs, gpr_order);
|
||||
@@ -194,7 +194,7 @@ u32 RegAlloc::RealizeReadImpl(const IR::Value& value) {
|
||||
UNREACHABLE(); //logic error
|
||||
case HostLoc::Kind::Fpr:
|
||||
as.FMV_X_D(biscuit::GPR(new_location_index), biscuit::FPR{current_location->index});
|
||||
// ASSERT size fits
|
||||
// DEBUG_ASSERT size fits
|
||||
break;
|
||||
case HostLoc::Kind::Spill:
|
||||
as.LD(biscuit::GPR{new_location_index}, spill_offset + current_location->index * spill_slot_size, biscuit::sp);
|
||||
@@ -229,7 +229,7 @@ u32 RegAlloc::RealizeReadImpl(const IR::Value& value) {
|
||||
|
||||
template<HostLoc::Kind required_kind>
|
||||
u32 RegAlloc::RealizeWriteImpl(const IR::Inst* value) {
|
||||
ASSERT(!ValueLocation(value));
|
||||
DEBUG_ASSERT(!ValueLocation(value));
|
||||
|
||||
const auto setup_location = [&](HostLocInfo& info) {
|
||||
info = {};
|
||||
@@ -274,7 +274,7 @@ u32 RegAlloc::AllocateRegister(const std::array<HostLocInfo, 32>& regs, const st
|
||||
}
|
||||
|
||||
void RegAlloc::SpillGpr(u32 index) {
|
||||
ASSERT(!gprs[index].locked && !gprs[index].realized);
|
||||
DEBUG_ASSERT(!gprs[index].locked && !gprs[index].realized);
|
||||
if (gprs[index].values.empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -284,7 +284,7 @@ void RegAlloc::SpillGpr(u32 index) {
|
||||
}
|
||||
|
||||
void RegAlloc::SpillFpr(u32 index) {
|
||||
ASSERT(!fprs[index].locked && !fprs[index].realized);
|
||||
DEBUG_ASSERT(!fprs[index].locked && !fprs[index].realized);
|
||||
if (fprs[index].values.empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -295,7 +295,7 @@ void RegAlloc::SpillFpr(u32 index) {
|
||||
|
||||
u32 RegAlloc::FindFreeSpill() const {
|
||||
const auto iter = std::find_if(spills.begin(), spills.end(), [](const HostLocInfo& info) { return info.values.empty(); });
|
||||
ASSERT(iter != spills.end() && "All spill locations are full");
|
||||
DEBUG_ASSERT(iter != spills.end() && "All spill locations are full");
|
||||
return static_cast<u32>(iter - spills.begin());
|
||||
}
|
||||
|
||||
|
||||
@@ -209,11 +209,11 @@ void A32EmitX64::InvalidateCacheRanges(const boost::icl::interval_set<u32>& rang
|
||||
|
||||
void A32EmitX64::EmitCondPrelude(const A32EmitContext& ctx) {
|
||||
if (ctx.block.GetCondition() == IR::Cond::AL) {
|
||||
ASSERT(!ctx.block.HasConditionFailedLocation());
|
||||
DEBUG_ASSERT(!ctx.block.HasConditionFailedLocation());
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(ctx.block.HasConditionFailedLocation());
|
||||
DEBUG_ASSERT(ctx.block.HasConditionFailedLocation());
|
||||
|
||||
Xbyak::Label pass = EmitCond(ctx.block.GetCondition());
|
||||
if (conf.enable_cycle_counting) {
|
||||
@@ -311,7 +311,7 @@ void A32EmitX64::EmitA32GetRegister(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
|
||||
void A32EmitX64::EmitA32GetExtendedRegister32(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
|
||||
ASSERT(A32::IsSingleExtReg(reg));
|
||||
DEBUG_ASSERT(A32::IsSingleExtReg(reg));
|
||||
|
||||
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code);
|
||||
code.movss(result, MJitStateExtReg(reg));
|
||||
@@ -320,7 +320,7 @@ void A32EmitX64::EmitA32GetExtendedRegister32(A32EmitContext& ctx, IR::Inst* ins
|
||||
|
||||
void A32EmitX64::EmitA32GetExtendedRegister64(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
|
||||
ASSERT(A32::IsDoubleExtReg(reg));
|
||||
DEBUG_ASSERT(A32::IsDoubleExtReg(reg));
|
||||
|
||||
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code);
|
||||
code.movsd(result, MJitStateExtReg(reg));
|
||||
@@ -329,7 +329,7 @@ void A32EmitX64::EmitA32GetExtendedRegister64(A32EmitContext& ctx, IR::Inst* ins
|
||||
|
||||
void A32EmitX64::EmitA32GetVector(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
|
||||
ASSERT(A32::IsDoubleExtReg(reg) || A32::IsQuadExtReg(reg));
|
||||
DEBUG_ASSERT(A32::IsDoubleExtReg(reg) || A32::IsQuadExtReg(reg));
|
||||
|
||||
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code);
|
||||
if (A32::IsDoubleExtReg(reg)) {
|
||||
@@ -358,7 +358,7 @@ void A32EmitX64::EmitA32SetRegister(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
void A32EmitX64::EmitA32SetExtendedRegister32(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
|
||||
ASSERT(A32::IsSingleExtReg(reg));
|
||||
DEBUG_ASSERT(A32::IsSingleExtReg(reg));
|
||||
|
||||
if (args[1].IsInXmm(ctx.reg_alloc)) {
|
||||
Xbyak::Xmm to_store = ctx.reg_alloc.UseXmm(code, args[1]);
|
||||
@@ -372,7 +372,7 @@ void A32EmitX64::EmitA32SetExtendedRegister32(A32EmitContext& ctx, IR::Inst* ins
|
||||
void A32EmitX64::EmitA32SetExtendedRegister64(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
|
||||
ASSERT(A32::IsDoubleExtReg(reg));
|
||||
DEBUG_ASSERT(A32::IsDoubleExtReg(reg));
|
||||
|
||||
if (args[1].IsInXmm(ctx.reg_alloc)) {
|
||||
const Xbyak::Xmm to_store = ctx.reg_alloc.UseXmm(code, args[1]);
|
||||
@@ -386,7 +386,7 @@ void A32EmitX64::EmitA32SetExtendedRegister64(A32EmitContext& ctx, IR::Inst* ins
|
||||
void A32EmitX64::EmitA32SetVector(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
|
||||
ASSERT(A32::IsDoubleExtReg(reg) || A32::IsQuadExtReg(reg));
|
||||
DEBUG_ASSERT(A32::IsDoubleExtReg(reg) || A32::IsQuadExtReg(reg));
|
||||
|
||||
const Xbyak::Xmm to_store = ctx.reg_alloc.UseXmm(code, args[1]);
|
||||
if (A32::IsDoubleExtReg(reg)) {
|
||||
@@ -647,7 +647,7 @@ void A32EmitX64::EmitA32GetGEFlags(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
|
||||
void A32EmitX64::EmitA32SetGEFlags(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ASSERT(!args[0].IsImmediate());
|
||||
DEBUG_ASSERT(!args[0].IsImmediate());
|
||||
|
||||
if (args[0].IsInXmm(ctx.reg_alloc)) {
|
||||
const Xbyak::Xmm to_store = ctx.reg_alloc.UseXmm(code, args[0]);
|
||||
@@ -788,7 +788,7 @@ void A32EmitX64::EmitA32ExceptionRaised(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
ctx.reg_alloc.EndOfAllocScope();
|
||||
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ASSERT(args[0].IsImmediate() && args[1].IsImmediate());
|
||||
DEBUG_ASSERT(args[0].IsImmediate() && args[1].IsImmediate());
|
||||
const u32 pc = args[0].GetImmediateU32();
|
||||
const u64 exception = args[1].GetImmediateU64();
|
||||
Devirtualize<&A32::UserCallbacks::ExceptionRaised>(conf.callbacks).EmitCall(code, [&](RegList param) {
|
||||
|
||||
@@ -74,7 +74,7 @@ struct Jit::Impl {
|
||||
~Impl() = default;
|
||||
|
||||
HaltReason Run() {
|
||||
ASSERT(!jit_interface->is_executing);
|
||||
DEBUG_ASSERT(!jit_interface->is_executing);
|
||||
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&jit_state.halt_reason)));
|
||||
jit_interface->is_executing = true;
|
||||
const CodePtr current_codeptr = [this] {
|
||||
@@ -94,7 +94,7 @@ struct Jit::Impl {
|
||||
}
|
||||
|
||||
HaltReason Step() {
|
||||
ASSERT(!jit_interface->is_executing);
|
||||
DEBUG_ASSERT(!jit_interface->is_executing);
|
||||
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&jit_state.halt_reason)));
|
||||
jit_interface->is_executing = true;
|
||||
const HaltReason hr = block_of_code.StepCode(&jit_state, GetCurrentSingleStep());
|
||||
@@ -116,7 +116,7 @@ struct Jit::Impl {
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
ASSERT(!jit_interface->is_executing);
|
||||
DEBUG_ASSERT(!jit_interface->is_executing);
|
||||
jit_state = {};
|
||||
}
|
||||
|
||||
|
||||
@@ -501,7 +501,7 @@ void A64EmitX64::EmitA64SetPC(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
void A64EmitX64::EmitA64CallSupervisor(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
ctx.reg_alloc.HostCall(code, nullptr);
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ASSERT(args[0].IsImmediate());
|
||||
DEBUG_ASSERT(args[0].IsImmediate());
|
||||
const u32 imm = args[0].GetImmediateU32();
|
||||
Devirtualize<&A64::UserCallbacks::CallSVC>(conf.callbacks).EmitCall(code, [&](RegList param) {
|
||||
code.mov(param[0], imm);
|
||||
@@ -513,7 +513,7 @@ void A64EmitX64::EmitA64CallSupervisor(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
void A64EmitX64::EmitA64ExceptionRaised(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
ctx.reg_alloc.HostCall(code, nullptr);
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ASSERT(args[0].IsImmediate() && args[1].IsImmediate());
|
||||
DEBUG_ASSERT(args[0].IsImmediate() && args[1].IsImmediate());
|
||||
const u64 pc = args[0].GetImmediateU64();
|
||||
const u64 exception = args[1].GetImmediateU64();
|
||||
Devirtualize<&A64::UserCallbacks::ExceptionRaised>(conf.callbacks).EmitCall(code, [&](RegList param) {
|
||||
|
||||
@@ -66,13 +66,13 @@ public:
|
||||
, emitter(block_of_code, conf, jit)
|
||||
, polyfill_options(GenPolyfillOptions(block_of_code))
|
||||
{
|
||||
ASSERT(conf.page_table_address_space_bits >= 12 && conf.page_table_address_space_bits <= 64);
|
||||
DEBUG_ASSERT(conf.page_table_address_space_bits >= 12 && conf.page_table_address_space_bits <= 64);
|
||||
}
|
||||
|
||||
~Impl() = default;
|
||||
|
||||
HaltReason Run() {
|
||||
ASSERT(!is_executing);
|
||||
DEBUG_ASSERT(!is_executing);
|
||||
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&jit_state.halt_reason)));
|
||||
is_executing = true;
|
||||
// TODO: Check code alignment
|
||||
@@ -92,7 +92,7 @@ public:
|
||||
}
|
||||
|
||||
HaltReason Step() {
|
||||
ASSERT(!is_executing);
|
||||
DEBUG_ASSERT(!is_executing);
|
||||
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&jit_state.halt_reason)));
|
||||
is_executing = true;
|
||||
const HaltReason hr = block_of_code.StepCode(&jit_state, GetCurrentSingleStep());
|
||||
@@ -116,7 +116,7 @@ public:
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
ASSERT(!is_executing);
|
||||
DEBUG_ASSERT(!is_executing);
|
||||
jit_state = {};
|
||||
}
|
||||
|
||||
|
||||
@@ -191,12 +191,12 @@ void BlockOfCode::DisableWriting() {
|
||||
}
|
||||
|
||||
void BlockOfCode::ClearCache() {
|
||||
ASSERT(prelude_complete);
|
||||
DEBUG_ASSERT(prelude_complete);
|
||||
SetCodePtr(code_begin);
|
||||
}
|
||||
|
||||
size_t BlockOfCode::SpaceRemaining() const {
|
||||
ASSERT(prelude_complete);
|
||||
DEBUG_ASSERT(prelude_complete);
|
||||
const u8* current_ptr = getCurr<const u8*>();
|
||||
if (current_ptr >= &top_[maxSize_])
|
||||
return 0;
|
||||
@@ -466,7 +466,7 @@ void BlockOfCode::SetCodePtr(CodePtr code_ptr) {
|
||||
|
||||
void BlockOfCode::EnsurePatchLocationSize(CodePtr begin, size_t size) {
|
||||
size_t current_size = getCurr<const u8*>() - reinterpret_cast<const u8*>(begin);
|
||||
ASSERT(current_size <= size);
|
||||
DEBUG_ASSERT(current_size <= size);
|
||||
nop(size - current_size);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ Xbyak::Address ConstantPool::GetConstant(BlockOfCode& code, const Xbyak::Address
|
||||
const auto constant = ConstantT(lower, upper);
|
||||
auto it = constant_info.find(constant);
|
||||
if (it == constant_info.end()) {
|
||||
ASSERT(insertion_point < pool.size());
|
||||
DEBUG_ASSERT(insertion_point < pool.size());
|
||||
ConstantT& target_constant = pool[insertion_point];
|
||||
target_constant = constant;
|
||||
it = constant_info.insert({constant, &target_constant}).first;
|
||||
|
||||
@@ -148,7 +148,7 @@ void EmitX64::EmitVerboseDebuggingOutput(RegAlloc& reg_alloc) {
|
||||
|
||||
void EmitX64::EmitPushRSB(EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ASSERT(args[0].IsImmediate());
|
||||
DEBUG_ASSERT(args[0].IsImmediate());
|
||||
const u64 unique_hash_of_target = args[0].GetImmediateU64();
|
||||
|
||||
ctx.reg_alloc.ScratchGpr(code, HostLoc::RCX);
|
||||
@@ -288,7 +288,7 @@ void EmitX64::EmitNZCVFromPackedFlags(EmitContext& ctx, IR::Inst* inst) {
|
||||
}
|
||||
|
||||
void EmitX64::EmitAddCycles(size_t cycles) {
|
||||
ASSERT(cycles < (std::numeric_limits<s32>::max)());
|
||||
DEBUG_ASSERT(cycles < (std::numeric_limits<s32>::max)());
|
||||
code.sub(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], static_cast<u32>(cycles));
|
||||
}
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ void EmitX64::EmitIsZero64(EmitContext& ctx, IR::Inst* inst) {
|
||||
void EmitX64::EmitTestBit(EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
const Xbyak::Reg64 result = ctx.reg_alloc.UseScratchGpr(code, args[0]);
|
||||
ASSERT(args[1].IsImmediate());
|
||||
DEBUG_ASSERT(args[1].IsImmediate());
|
||||
// TODO: Flag optimization
|
||||
code.bt(result, args[1].GetImmediateU8());
|
||||
code.setc(result.cvt8());
|
||||
|
||||
@@ -1842,7 +1842,7 @@ void EmitX64::EmitFPFixedS32ToSingle(EmitContext& ctx, IR::Inst* inst) {
|
||||
if (rounding_mode == ctx.FPCR().RMode() || ctx.HasOptimization(OptimizationFlag::Unsafe_IgnoreStandardFPCRValue)) {
|
||||
code.cvtsi2ss(result, from);
|
||||
} else {
|
||||
ASSERT(rounding_mode == FP::RoundingMode::ToNearest_TieEven);
|
||||
DEBUG_ASSERT(rounding_mode == FP::RoundingMode::ToNearest_TieEven);
|
||||
code.EnterStandardASIMD();
|
||||
code.cvtsi2ss(result, from);
|
||||
code.LeaveStandardASIMD();
|
||||
@@ -1878,7 +1878,7 @@ void EmitX64::EmitFPFixedU32ToSingle(EmitContext& ctx, IR::Inst* inst) {
|
||||
if (rounding_mode == ctx.FPCR().RMode() || ctx.HasOptimization(OptimizationFlag::Unsafe_IgnoreStandardFPCRValue)) {
|
||||
op();
|
||||
} else {
|
||||
ASSERT(rounding_mode == FP::RoundingMode::ToNearest_TieEven);
|
||||
DEBUG_ASSERT(rounding_mode == FP::RoundingMode::ToNearest_TieEven);
|
||||
code.EnterStandardASIMD();
|
||||
op();
|
||||
code.LeaveStandardASIMD();
|
||||
@@ -1984,7 +1984,7 @@ void EmitX64::EmitFPFixedS64ToDouble(EmitContext& ctx, IR::Inst* inst) {
|
||||
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code);
|
||||
const size_t fbits = args[1].GetImmediateU8();
|
||||
const FP::RoundingMode rounding_mode = static_cast<FP::RoundingMode>(args[2].GetImmediateU8());
|
||||
ASSERT(rounding_mode == ctx.FPCR().RMode());
|
||||
DEBUG_ASSERT(rounding_mode == ctx.FPCR().RMode());
|
||||
|
||||
code.cvtsi2sd(result, from);
|
||||
|
||||
@@ -2003,7 +2003,7 @@ void EmitX64::EmitFPFixedS64ToSingle(EmitContext& ctx, IR::Inst* inst) {
|
||||
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code);
|
||||
const size_t fbits = args[1].GetImmediateU8();
|
||||
const FP::RoundingMode rounding_mode = static_cast<FP::RoundingMode>(args[2].GetImmediateU8());
|
||||
ASSERT(rounding_mode == ctx.FPCR().RMode());
|
||||
DEBUG_ASSERT(rounding_mode == ctx.FPCR().RMode());
|
||||
|
||||
code.cvtsi2ss(result, from);
|
||||
|
||||
@@ -2022,7 +2022,7 @@ void EmitX64::EmitFPFixedU64ToDouble(EmitContext& ctx, IR::Inst* inst) {
|
||||
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code);
|
||||
const size_t fbits = args[1].GetImmediateU8();
|
||||
const FP::RoundingMode rounding_mode = static_cast<FP::RoundingMode>(args[2].GetImmediateU8());
|
||||
ASSERT(rounding_mode == ctx.FPCR().RMode());
|
||||
DEBUG_ASSERT(rounding_mode == ctx.FPCR().RMode());
|
||||
|
||||
if (code.HasHostFeature(HostFeature::AVX512F)) {
|
||||
code.vcvtusi2sd(result, result, from);
|
||||
@@ -2053,7 +2053,7 @@ void EmitX64::EmitFPFixedU64ToSingle(EmitContext& ctx, IR::Inst* inst) {
|
||||
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code);
|
||||
const size_t fbits = args[1].GetImmediateU8();
|
||||
const FP::RoundingMode rounding_mode = static_cast<FP::RoundingMode>(args[2].GetImmediateU8());
|
||||
ASSERT(rounding_mode == ctx.FPCR().RMode());
|
||||
DEBUG_ASSERT(rounding_mode == ctx.FPCR().RMode());
|
||||
|
||||
if (code.HasHostFeature(HostFeature::AVX512F)) {
|
||||
const Xbyak::Reg64 from = ctx.reg_alloc.UseGpr(code, args[0]);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2022 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
@@ -113,7 +116,7 @@ void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) {
|
||||
});
|
||||
} else {
|
||||
// Use page table
|
||||
ASSERT(conf.page_table);
|
||||
DEBUG_ASSERT(conf.page_table);
|
||||
const auto src_ptr = EmitVAddrLookup(code, ctx, bitsize, *abort, vaddr);
|
||||
EmitReadMemoryMov<bitsize>(code, value_idx, src_ptr, ordered);
|
||||
|
||||
@@ -200,7 +203,7 @@ void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) {
|
||||
});
|
||||
} else {
|
||||
// Use page table
|
||||
ASSERT(conf.page_table);
|
||||
DEBUG_ASSERT(conf.page_table);
|
||||
const auto dest_ptr = EmitVAddrLookup(code, ctx, bitsize, *abort, vaddr);
|
||||
EmitWriteMemoryMov<bitsize>(code, dest_ptr, value_idx, ordered);
|
||||
|
||||
@@ -216,7 +219,7 @@ void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) {
|
||||
|
||||
template<std::size_t bitsize, auto callback>
|
||||
void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(conf.global_monitor != nullptr);
|
||||
DEBUG_ASSERT(conf.global_monitor != nullptr);
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
const bool ordered = IsOrdered(args[2].GetImmediateAccType());
|
||||
|
||||
@@ -267,7 +270,7 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) {
|
||||
|
||||
template<std::size_t bitsize, auto callback>
|
||||
void AxxEmitX64::EmitExclusiveWriteMemory(AxxEmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(conf.global_monitor != nullptr);
|
||||
DEBUG_ASSERT(conf.global_monitor != nullptr);
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
const bool ordered = IsOrdered(args[3].GetImmediateAccType());
|
||||
|
||||
@@ -320,7 +323,7 @@ void AxxEmitX64::EmitExclusiveWriteMemory(AxxEmitContext& ctx, IR::Inst* inst) {
|
||||
|
||||
template<std::size_t bitsize, auto callback>
|
||||
void AxxEmitX64::EmitExclusiveReadMemoryInline(AxxEmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(conf.global_monitor && conf.fastmem_pointer);
|
||||
DEBUG_ASSERT(conf.global_monitor && conf.fastmem_pointer);
|
||||
if (!exception_handler.SupportsFastmem()) {
|
||||
EmitExclusiveReadMemory<bitsize, callback>(ctx, inst);
|
||||
return;
|
||||
@@ -397,7 +400,7 @@ void AxxEmitX64::EmitExclusiveReadMemoryInline(AxxEmitContext& ctx, IR::Inst* in
|
||||
|
||||
template<std::size_t bitsize, auto callback>
|
||||
void AxxEmitX64::EmitExclusiveWriteMemoryInline(AxxEmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(conf.global_monitor && conf.fastmem_pointer);
|
||||
DEBUG_ASSERT(conf.global_monitor && conf.fastmem_pointer);
|
||||
if (!exception_handler.SupportsFastmem()) {
|
||||
EmitExclusiveWriteMemory<bitsize, callback>(ctx, inst);
|
||||
return;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <bit>
|
||||
#include <utility>
|
||||
#include "dynarmic/backend/x64/xbyak.h"
|
||||
|
||||
#include "dynarmic/backend/x64/a32_emit_x64.h"
|
||||
@@ -78,27 +79,40 @@ Xbyak::RegExp EmitVAddrLookup(BlockOfCode& code, EmitContext& ctx, size_t bitsiz
|
||||
template<>
|
||||
[[maybe_unused]] Xbyak::RegExp EmitVAddrLookup<A32EmitContext>(BlockOfCode& code, A32EmitContext& ctx, size_t bitsize, Xbyak::Label& abort, Xbyak::Reg64 vaddr) {
|
||||
const Xbyak::Reg64 page = ctx.reg_alloc.ScratchGpr(code);
|
||||
const Xbyak::Reg32 tmp = ctx.conf.absolute_offset_page_table ? page.cvt32() : ctx.reg_alloc.ScratchGpr(code).cvt32();
|
||||
const Xbyak::Reg64 tmp = ctx.conf.absolute_offset_page_table && ctx.conf.page_table_pointer_mask == 0 ? page : ctx.reg_alloc.ScratchGpr(code);
|
||||
|
||||
EmitDetectMisalignedVAddr(code, ctx, bitsize, abort, vaddr, tmp.cvt64());
|
||||
EmitDetectMisalignedVAddr(code, ctx, bitsize, abort, vaddr, tmp);
|
||||
|
||||
// TODO: This code assumes vaddr has been zext from 32-bits to 64-bits.
|
||||
|
||||
code.mov(tmp, vaddr.cvt32());
|
||||
code.mov(tmp, vaddr);
|
||||
code.shr(tmp, int(page_table_const_bits));
|
||||
code.shl(tmp, int(ctx.conf.page_table_log2_stride));
|
||||
code.mov(page, qword[r14 + tmp.cvt64()]);
|
||||
if (ctx.conf.page_table_pointer_mask_bits == 0) {
|
||||
|
||||
// check for marked bit, use as unmapped if marked
|
||||
if (ctx.conf.page_table_marked_bit) {
|
||||
code.bt(page, *ctx.conf.page_table_marked_bit);
|
||||
code.jc(abort, code.T_NEAR);
|
||||
}
|
||||
// mask away attributes
|
||||
if (ctx.conf.page_table_pointer_mask == 0) {
|
||||
code.test(page, page);
|
||||
} else if (std::in_range<s32>(ctx.conf.page_table_pointer_mask)) {
|
||||
code.and_(page, ctx.conf.page_table_pointer_mask);
|
||||
} else {
|
||||
code.and_(page, ~u32(0) << ctx.conf.page_table_pointer_mask_bits);
|
||||
code.mov(tmp, ctx.conf.page_table_pointer_mask);
|
||||
code.and_(page, tmp);
|
||||
}
|
||||
if (ctx.conf.page_table_sign_extension) {
|
||||
code.shl(page, *ctx.conf.page_table_sign_extension);
|
||||
code.sar(page, *ctx.conf.page_table_sign_extension);
|
||||
}
|
||||
|
||||
code.jz(abort, code.T_NEAR);
|
||||
if (ctx.conf.absolute_offset_page_table) {
|
||||
return page + vaddr;
|
||||
}
|
||||
code.mov(tmp, vaddr.cvt32());
|
||||
code.and_(tmp, static_cast<u32>(page_table_const_mask));
|
||||
code.mov(tmp, vaddr);
|
||||
code.and_(tmp, u32(page_table_const_mask));
|
||||
return page + tmp.cvt64();
|
||||
}
|
||||
|
||||
@@ -108,7 +122,7 @@ template<>
|
||||
const size_t unused_top_bits = 64 - ctx.conf.page_table_address_space_bits;
|
||||
|
||||
const Xbyak::Reg64 page = ctx.reg_alloc.ScratchGpr(code);
|
||||
const Xbyak::Reg64 tmp = ctx.conf.absolute_offset_page_table ? page : ctx.reg_alloc.ScratchGpr(code);
|
||||
const Xbyak::Reg64 tmp = ctx.conf.absolute_offset_page_table && ctx.conf.page_table_pointer_mask == 0 ? page : ctx.reg_alloc.ScratchGpr(code);
|
||||
|
||||
EmitDetectMisalignedVAddr(code, ctx, bitsize, abort, vaddr, tmp);
|
||||
|
||||
@@ -134,7 +148,7 @@ template<>
|
||||
code.and_(tmp, u32((1 << valid_page_index_bits) - 1));
|
||||
}
|
||||
} else {
|
||||
ASSERT(valid_page_index_bits < 32);
|
||||
DEBUG_ASSERT(valid_page_index_bits < 32);
|
||||
code.mov(tmp, vaddr);
|
||||
code.shr(tmp, int(page_table_const_bits));
|
||||
code.test(tmp, u32(-(1 << valid_page_index_bits)));
|
||||
@@ -143,11 +157,26 @@ template<>
|
||||
|
||||
code.shl(tmp, int(ctx.conf.page_table_log2_stride));
|
||||
code.mov(page, qword[r14 + tmp]);
|
||||
if (ctx.conf.page_table_pointer_mask_bits == 0) {
|
||||
code.test(page, page);
|
||||
} else {
|
||||
code.and_(page, ~u32(0) << ctx.conf.page_table_pointer_mask_bits);
|
||||
|
||||
// check for marked bit, use as unmapped if marked
|
||||
if (ctx.conf.page_table_marked_bit) {
|
||||
code.bt(page, *ctx.conf.page_table_marked_bit);
|
||||
code.jc(abort, code.T_NEAR);
|
||||
}
|
||||
// mask away attributes
|
||||
if (ctx.conf.page_table_pointer_mask == 0) {
|
||||
code.test(page, page);
|
||||
} else if (std::in_range<s32>(ctx.conf.page_table_pointer_mask)) {
|
||||
code.and_(page, ctx.conf.page_table_pointer_mask);
|
||||
} else {
|
||||
code.mov(tmp, ctx.conf.page_table_pointer_mask);
|
||||
code.and_(page, tmp);
|
||||
}
|
||||
if (ctx.conf.page_table_sign_extension) {
|
||||
code.shl(page, *ctx.conf.page_table_sign_extension);
|
||||
code.sar(page, *ctx.conf.page_table_sign_extension);
|
||||
}
|
||||
|
||||
code.jz(abort, code.T_NEAR);
|
||||
if (ctx.conf.absolute_offset_page_table) {
|
||||
return page + vaddr;
|
||||
|
||||
@@ -118,7 +118,7 @@ void EmitX64::EmitSignedSaturation(EmitContext& ctx, IR::Inst* inst) {
|
||||
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
const size_t N = args[1].GetImmediateU8();
|
||||
ASSERT(N >= 1 && N <= 32);
|
||||
DEBUG_ASSERT(N >= 1 && N <= 32);
|
||||
|
||||
if (N == 32) {
|
||||
if (overflow_inst) {
|
||||
@@ -167,7 +167,7 @@ void EmitX64::EmitUnsignedSaturation(EmitContext& ctx, IR::Inst* inst) {
|
||||
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
const size_t N = args[1].GetImmediateU8();
|
||||
ASSERT(N <= 31);
|
||||
DEBUG_ASSERT(N <= 31);
|
||||
|
||||
const u32 saturated_value = (1u << N) - 1;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -18,7 +18,7 @@ void EmitX64::EmitSHA256Hash(EmitContext& ctx, IR::Inst* inst) {
|
||||
|
||||
const bool part1 = args[3].GetImmediateU1();
|
||||
|
||||
ASSERT(code.HasHostFeature(HostFeature::SHA));
|
||||
DEBUG_ASSERT(code.HasHostFeature(HostFeature::SHA));
|
||||
|
||||
// 3 2 1 0
|
||||
// x = d c b a
|
||||
@@ -54,7 +54,7 @@ void EmitX64::EmitSHA256Hash(EmitContext& ctx, IR::Inst* inst) {
|
||||
void EmitX64::EmitSHA256MessageSchedule0(EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
|
||||
ASSERT(code.HasHostFeature(HostFeature::SHA));
|
||||
DEBUG_ASSERT(code.HasHostFeature(HostFeature::SHA));
|
||||
|
||||
const Xbyak::Xmm x = ctx.reg_alloc.UseScratchXmm(code, args[0]);
|
||||
const Xbyak::Xmm y = ctx.reg_alloc.UseXmm(code, args[1]);
|
||||
@@ -67,7 +67,7 @@ void EmitX64::EmitSHA256MessageSchedule0(EmitContext& ctx, IR::Inst* inst) {
|
||||
void EmitX64::EmitSHA256MessageSchedule1(EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
|
||||
ASSERT(code.HasHostFeature(HostFeature::SHA));
|
||||
DEBUG_ASSERT(code.HasHostFeature(HostFeature::SHA));
|
||||
|
||||
const Xbyak::Xmm x = ctx.reg_alloc.UseScratchXmm(code, args[0]);
|
||||
const Xbyak::Xmm y = ctx.reg_alloc.UseXmm(code, args[1]);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user