From ae02310bdff82262a1c62bd1ddc457053c1b1a5d Mon Sep 17 00:00:00 2001 From: crueter Date: Sat, 12 Jul 2025 18:05:37 -0400 Subject: [PATCH 01/25] optional multimedia/webengine Signed-off-by: crueter --- .ci/linux/build.sh | 16 ++++++++++++++-- .ci/windows/build.sh | 16 ++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/.ci/linux/build.sh b/.ci/linux/build.sh index 745f8244b7..aa15333ac2 100755 --- a/.ci/linux/build.sh +++ b/.ci/linux/build.sh @@ -60,6 +60,18 @@ if [ "$DEVEL" != "true" ]; then export EXTRA_CMAKE_FLAGS=("${EXTRA_CMAKE_FLAGS[@]}" -DENABLE_QT_UPDATE_CHECKER=ON) fi +if [ "$USE_WEBENGINE" = "true" ]; then + WEBENGINE=ON +else + WEBENGINE=OFF +fi + +if [ "$USE_MULTIMEDIA" = "false" ]; then + MULTIMEDIA=OFF +else + MULTIMEDIA=ON +fi + export EXTRA_CMAKE_FLAGS=("${EXTRA_CMAKE_FLAGS[@]}" $@) mkdir -p build && cd build @@ -74,8 +86,8 @@ cmake .. -G Ninja \ -DYUZU_USE_BUNDLED_SDL2=OFF \ -DYUZU_USE_EXTERNAL_SDL2=ON \ -DYUZU_TESTS=OFF \ - -DYUZU_USE_QT_MULTIMEDIA=ON \ - -DYUZU_USE_QT_WEB_ENGINE=ON \ + -DYUZU_USE_QT_MULTIMEDIA=$MULTIMEDIA \ + -DYUZU_USE_QT_WEB_ENGINE=$WEBENGINE \ -DYUZU_USE_FASTER_LD=ON \ -DYUZU_ENABLE_LTO=ON \ "${EXTRA_CMAKE_FLAGS[@]}" diff --git a/.ci/windows/build.sh b/.ci/windows/build.sh index 4e993b0488..667fd316fa 100644 --- a/.ci/windows/build.sh +++ b/.ci/windows/build.sh @@ -22,6 +22,18 @@ if [ "$WINDEPLOYQT" == "" ]; then exit 1 fi +if [ "$USE_WEBENGINE" = "true" ]; then + WEBENGINE=ON +else + WEBENGINE=OFF +fi + +if [ "$USE_MULTIMEDIA" = "false" ]; then + MULTIMEDIA=OFF +else + MULTIMEDIA=ON +fi + export EXTRA_CMAKE_FLAGS=("${EXTRA_CMAKE_FLAGS[@]}" $@) mkdir -p build && cd build @@ -34,8 +46,8 @@ cmake .. -G Ninja \ -DYUZU_TESTS=OFF \ -DYUZU_CMD=OFF \ -DYUZU_ROOM_STANDALONE=OFF \ - -DYUZU_USE_QT_MULTIMEDIA=ON \ - -DYUZU_USE_QT_WEB_ENGINE=ON \ + -DYUZU_USE_QT_MULTIMEDIA=$MULTIMEDIA \ + -DYUZU_USE_QT_WEB_ENGINE=$WEBENGINE \ -DYUZU_ENABLE_LTO=ON \ "${EXTRA_CMAKE_FLAGS[@]}" From d5c58342e083a275ce4acbfd2b2453c53ab12c3f Mon Sep 17 00:00:00 2001 From: lizzie Date: Sun, 13 Jul 2025 03:25:43 +0200 Subject: [PATCH 02/25] [yuzu_cmd] gdbstub option (#43) Co-authored-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/43 Co-authored-by: lizzie Co-committed-by: lizzie --- src/yuzu_cmd/yuzu.cpp | 55 ++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 005f5bf1db..c1d714be77 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -80,6 +83,7 @@ static void PrintHelp(const char* argv0) { " Nickname, password, address and port for multiplayer\n" "-p, --program Pass following string as arguments to executable\n" "-u, --user Select a specific user profile from 0 to 7\n" + "-d, --debug Run the GDB stub on a port from 1 to 65535\n" "-v, --version Output version information and exit\n"; } @@ -162,24 +166,22 @@ static void OnMessageReceived(const Network::ChatEntry& msg) { } static void OnStatusMessageReceived(const Network::StatusMessageEntry& msg) { - std::string message; - switch (msg.type) { - case Network::IdMemberJoin: - message = fmt::format("{} has joined", msg.nickname); - break; - case Network::IdMemberLeave: - message = fmt::format("{} has left", msg.nickname); - break; - case Network::IdMemberKicked: - message = fmt::format("{} has been kicked", msg.nickname); - break; - case Network::IdMemberBanned: - message = fmt::format("{} has been banned", msg.nickname); - break; - case Network::IdAddressUnbanned: - message = fmt::format("{} has been unbanned", msg.nickname); - break; - } + std::string message = [&]() { + switch (msg.type) { + case Network::IdMemberJoin: + return fmt::format("{} has joined", msg.nickname); + case Network::IdMemberLeave: + return fmt::format("{} has left", msg.nickname); + case Network::IdMemberKicked: + return fmt::format("{} has been kicked", msg.nickname); + case Network::IdMemberBanned: + return fmt::format("{} has been banned", msg.nickname); + case Network::IdAddressUnbanned: + return fmt::format("{} has been unbanned", msg.nickname); + default: + return std::string{}; + } + }(); if (!message.empty()) std::cout << std::endl << "* " << message << std::endl << std::endl; } @@ -209,10 +211,10 @@ int main(int argc, char** argv) { } #endif std::string filepath; - std::optional config_path; + std::optional config_path{}; std::string program_args; - std::optional selected_user; - + std::optional selected_user{}; + std::optional override_gdb_port{}; bool use_multiplayer = false; bool fullscreen = false; std::string nickname{}; @@ -222,6 +224,7 @@ int main(int argc, char** argv) { static struct option long_options[] = { // clang-format off + {"debug", no_argument, 0, 'd'}, {"config", required_argument, 0, 'c'}, {"fullscreen", no_argument, 0, 'f'}, {"help", no_argument, 0, 'h'}, @@ -235,9 +238,12 @@ int main(int argc, char** argv) { }; while (optind < argc) { - int arg = getopt_long(argc, argv, "g:fhvp::c:u:", long_options, &option_index); + int arg = getopt_long(argc, argv, "g:fhvp::c:u:d:", long_options, &option_index); if (arg != -1) { switch (static_cast(arg)) { + case 'd': + override_gdb_port = static_cast(atoi(optarg)); + break; case 'c': config_path = optarg; break; @@ -323,6 +329,11 @@ int main(int argc, char** argv) { Settings::values.current_user = std::clamp(*selected_user, 0, 7); } + if (override_gdb_port.has_value()) { + Settings::values.use_gdbstub = true; + Settings::values.gdbstub_port = *override_gdb_port; + } + #ifdef _WIN32 LocalFree(argv_w); #endif From 55a77973782d1e74c5beec65b3dc5b61ddb37ef4 Mon Sep 17 00:00:00 2001 From: crueter Date: Sun, 13 Jul 2025 03:39:01 +0200 Subject: [PATCH 03/25] [desktop] add options to open root, NAND, SDMC, load, and log dirs (#53) Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/53 --- src/yuzu/configuration/configure_debug.ui | 14 ++++---- src/yuzu/main.cpp | 35 +++++++++++++++++-- src/yuzu/main.h | 6 +++- src/yuzu/main.ui | 42 +++++++++++++++++++---- 4 files changed, 80 insertions(+), 17 deletions(-) diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 155dac412b..5408d485b4 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -157,13 +157,6 @@ Logging - - - - Open Log Location - - - @@ -224,6 +217,13 @@ + + + + Open Log Location + + + diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 13d2373f5d..4ff59291e5 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1674,7 +1674,12 @@ void GMainWindow::ConnectMenuEvents() { connect_menu(ui->action_Configure_Tas, &GMainWindow::OnConfigureTas); // Help - connect_menu(ui->action_Open_yuzu_Folder, &GMainWindow::OnOpenYuzuFolder); + connect_menu(ui->action_Root_Data_Folder, &GMainWindow::OnOpenRootDataFolder); + connect_menu(ui->action_NAND_Folder, &GMainWindow::OnOpenNANDFolder); + connect_menu(ui->action_SDMC_Folder, &GMainWindow::OnOpenSDMCFolder); + connect_menu(ui->action_Mod_Folder, &GMainWindow::OnOpenModFolder); + connect_menu(ui->action_Log_Folder, &GMainWindow::OnOpenLogFolder); + connect_menu(ui->action_Discord, &GMainWindow::OnOpenDiscord); connect_menu(ui->action_Verify_installed_contents, &GMainWindow::OnVerifyInstalledContents); connect_menu(ui->action_Install_Firmware, &GMainWindow::OnInstallFirmware); @@ -4159,11 +4164,35 @@ void GMainWindow::LoadAmiibo(const QString& filename) { } } -void GMainWindow::OnOpenYuzuFolder() { - QDesktopServices::openUrl(QUrl::fromLocalFile( +void GMainWindow::OnOpenRootDataFolder() { + QDesktopServices::openUrl(QUrl( QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::EdenDir)))); } +void GMainWindow::OnOpenNANDFolder() +{ + QDesktopServices::openUrl(QUrl::fromLocalFile( + QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::NANDDir)))); +} + +void GMainWindow::OnOpenSDMCFolder() +{ + QDesktopServices::openUrl(QUrl::fromLocalFile( + QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir)))); +} + +void GMainWindow::OnOpenModFolder() +{ + QDesktopServices::openUrl(QUrl::fromLocalFile( + QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::LoadDir)))); +} + +void GMainWindow::OnOpenLogFolder() +{ + QDesktopServices::openUrl(QUrl::fromLocalFile( + QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::LogDir)))); +} + void GMainWindow::OnVerifyInstalledContents() { // Initialize a progress dialog. QProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, this); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 84588f641a..1f2582098a 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -387,7 +387,11 @@ private slots: void OnToggleAdaptingFilter(); void OnConfigurePerGame(); void OnLoadAmiibo(); - void OnOpenYuzuFolder(); + void OnOpenRootDataFolder(); + void OnOpenNANDFolder(); + void OnOpenSDMCFolder(); + void OnOpenModFolder(); + void OnOpenLogFolder(); void OnVerifyInstalledContents(); void OnInstallFirmware(); void OnInstallDecryptionKeys(); diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 6b19d1f8f5..e35b7afa5a 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -57,6 +57,16 @@ &Recent Files + + + Open &Eden Folders + + + + + + + @@ -66,7 +76,7 @@ - + @@ -388,11 +398,6 @@ &FAQ - - - Open &eden Folder - - false @@ -517,6 +522,31 @@ &Application Menu + + + &Root Data Folder + + + + + &NAND Folder + + + + + &SDMC Folder + + + + + &Mod Folder + + + + + &Log Folder + + From 03351a4f8b463a798d64dcb78899b4cdd9004071 Mon Sep 17 00:00:00 2001 From: crueter Date: Sun, 13 Jul 2025 03:39:19 +0200 Subject: [PATCH 04/25] [frontend] refactor: extract common firmware & key functions (#38) Extracts some firmware version/verification functions into `frontend_common` to reduce duplicate code, especially for the new QML frontend. Additionally adds a check for games that are known to require firmware (e.g. MK8DX) and warns the user if they don't have firmware installed and attempt to run the game. Firmware installation is to be in a separate PR. Signed-off-by: crueter Co-authored-by: Aleksandr Popovich Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/38 --- .../java/org/yuzu/yuzu_emu/NativeLibrary.kt | 48 +- .../org/yuzu/yuzu_emu/adapters/GameAdapter.kt | 72 +- .../fragments/DriverFetcherFragment.kt | 2 - .../fragments/HomeSettingsFragment.kt | 2 +- .../yuzu/yuzu_emu/fragments/SetupFragment.kt | 2 +- .../org/yuzu/yuzu_emu/ui/GamesFragment.kt | 7 +- .../org/yuzu/yuzu_emu/ui/main/MainActivity.kt | 303 ++--- src/android/app/src/main/jni/native.cpp | 43 +- .../app/src/main/res/values-ar/strings.xml | 8 - .../app/src/main/res/values-ckb/strings.xml | 8 - .../app/src/main/res/values-cs/strings.xml | 6 - .../app/src/main/res/values-de/strings.xml | 8 - .../app/src/main/res/values-es/strings.xml | 8 - .../app/src/main/res/values-fa/strings.xml | 8 - .../app/src/main/res/values-fr/strings.xml | 8 - .../app/src/main/res/values-he/strings.xml | 8 - .../app/src/main/res/values-hu/strings.xml | 8 - .../app/src/main/res/values-id/strings.xml | 8 - .../app/src/main/res/values-it/strings.xml | 8 - .../app/src/main/res/values-ja/strings.xml | 8 - .../app/src/main/res/values-ko/strings.xml | 8 - .../app/src/main/res/values-nb/strings.xml | 8 - .../app/src/main/res/values-pl/strings.xml | 8 - .../src/main/res/values-pt-rBR/strings.xml | 8 - .../src/main/res/values-pt-rPT/strings.xml | 8 - .../app/src/main/res/values-ru/strings.xml | 8 - .../app/src/main/res/values-sr/strings.xml | 8 - .../app/src/main/res/values-uk/strings.xml | 8 - .../app/src/main/res/values-vi/strings.xml | 8 - .../src/main/res/values-zh-rCN/strings.xml | 8 - .../src/main/res/values-zh-rTW/strings.xml | 8 - .../app/src/main/res/values/arrays.xml | 14 + .../app/src/main/res/values/strings.xml | 23 +- src/common/android/id_cache.cpp | 1028 +++++++++-------- src/common/android/id_cache.h | 3 + src/frontend_common/CMakeLists.txt | 2 + src/frontend_common/firmware_manager.cpp | 139 +++ src/frontend_common/firmware_manager.h | 148 +++ src/yuzu/main.cpp | 206 ++-- 39 files changed, 1146 insertions(+), 1078 deletions(-) create mode 100644 src/frontend_common/firmware_manager.cpp create mode 100644 src/frontend_common/firmware_manager.h diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt index c7d6d6c33a..c0e5983fc6 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt @@ -15,6 +15,7 @@ import android.view.Surface import android.view.View import android.widget.TextView import androidx.annotation.Keep +import androidx.core.net.toUri import com.google.android.material.dialog.MaterialAlertDialogBuilder import net.swiftzer.semver.SemVer import java.lang.ref.WeakReference @@ -27,6 +28,7 @@ import org.yuzu.yuzu_emu.model.InstallResult import org.yuzu.yuzu_emu.model.Patch import org.yuzu.yuzu_emu.model.GameVerificationResult import org.yuzu.yuzu_emu.network.NetPlayManager +import java.io.File /** * Class which contains methods that interact @@ -102,6 +104,21 @@ object NativeLibrary { FileUtil.getFilename(Uri.parse(path)) } + @Keep + @JvmStatic + fun copyFileToStorage(source: String, destdir: String): Boolean { + return FileUtil.copyUriToInternalStorage( + source.toUri(), + destdir + ) != null + } + + @Keep + @JvmStatic + fun getFileExtension(source: String): String { + return FileUtil.getExtension(source.toUri()) + } + external fun setAppDirectory(directory: String) /** @@ -415,18 +432,29 @@ object NativeLibrary { */ external fun firmwareVersion(): String - fun isFirmwareSupported(): Boolean { - var version: SemVer + /** + * Verifies installed firmware. + * + * @return The result code. + */ + external fun verifyFirmware(): Int - try { - version = SemVer.parse(firmwareVersion()) - } catch (_: Exception) { - return false - } - val max = SemVer(19, 0, 1) + /** + * Check if a game requires firmware to be playable. + * + * @param programId The game's Program ID. + * @return Whether or not the game requires firmware to be playable. + */ + external fun gameRequiresFirmware(programId: String): Boolean - return version <= max - } + /** + * Installs keys from the specified path. + * + * @param path The path to install keys from. + * @param ext What extension the keys should have. + * @return The result code. + */ + external fun installKeys(path: String, ext: String): Int /** * Checks the PatchManager for any addons that are available diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt index 750e8f4729..c4652f55e1 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt @@ -3,11 +3,16 @@ package org.yuzu.yuzu_emu.adapters +import android.content.DialogInterface import android.net.Uri +import android.text.Html +import android.text.method.LinkMovementMethod import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup import android.widget.LinearLayout import android.widget.ImageView +import android.widget.TextView import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.core.content.pm.ShortcutInfoCompat @@ -33,6 +38,10 @@ import org.yuzu.yuzu_emu.utils.GameIconUtils import org.yuzu.yuzu_emu.utils.ViewUtils.marquee import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder import androidx.recyclerview.widget.RecyclerView +import androidx.core.net.toUri +import androidx.core.content.edit +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.yuzu.yuzu_emu.NativeLibrary class GameAdapter(private val activity: AppCompatActivity) : AbstractDiffAdapter(exact = false) { @@ -171,8 +180,9 @@ class GameAdapter(private val activity: AppCompatActivity) : fun onClick(game: Game) { val gameExists = DocumentFile.fromSingleUri( YuzuApplication.appContext, - Uri.parse(game.path) + game.path.toUri() )?.exists() == true + if (!gameExists) { Toast.makeText( YuzuApplication.appContext, @@ -184,29 +194,49 @@ class GameAdapter(private val activity: AppCompatActivity) : return } - val preferences = - PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) - preferences.edit() - .putLong( - game.keyLastPlayedTime, - System.currentTimeMillis() - ) - .apply() - - activity.lifecycleScope.launch { - withContext(Dispatchers.IO) { - val shortcut = - ShortcutInfoCompat.Builder(YuzuApplication.appContext, game.path) - .setShortLabel(game.title) - .setIcon(GameIconUtils.getShortcutIcon(activity, game)) - .setIntent(game.launchIntent) - .build() - ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut) + val launch: () -> Unit = { + val preferences = + PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) + preferences.edit { + putLong( + game.keyLastPlayedTime, + System.currentTimeMillis() + ) } + + activity.lifecycleScope.launch { + withContext(Dispatchers.IO) { + val shortcut = + ShortcutInfoCompat.Builder(YuzuApplication.appContext, game.path) + .setShortLabel(game.title) + .setIcon(GameIconUtils.getShortcutIcon(activity, game)) + .setIntent(game.launchIntent) + .build() + ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut) + } + } + + val action = HomeNavigationDirections.actionGlobalEmulationActivity(game, true) + binding.root.findNavController().navigate(action) } - val action = HomeNavigationDirections.actionGlobalEmulationActivity(game, true) - binding.root.findNavController().navigate(action) + if (NativeLibrary.gameRequiresFirmware(game.programId) && !NativeLibrary.isFirmwareAvailable()) { + MaterialAlertDialogBuilder(activity) + .setTitle(R.string.loader_requires_firmware) + .setMessage( + Html.fromHtml( + activity.getString(R.string.loader_requires_firmware_description), + Html.FROM_HTML_MODE_LEGACY + ) + ) + .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int -> + launch() + } + .setNegativeButton(android.R.string.cancel) { _,_ -> } + .show() + } else { + launch() + } } fun onLongClick(game: Game): Boolean { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverFetcherFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverFetcherFragment.kt index 0c1e39d095..91670b207d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverFetcherFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverFetcherFragment.kt @@ -264,8 +264,6 @@ class DriverFetcherFragment : Fragment() { } releases.add(release) - - println(release.publishTime) } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt index f39d9514b3..5fed99e0b0 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt @@ -137,7 +137,7 @@ class HomeSettingsFragment : Fragment() { binding.root.findNavController() .navigate(R.id.action_homeSettingsFragment_to_appletLauncherFragment) }, - { NativeLibrary.isFirmwareAvailable() && NativeLibrary.isFirmwareSupported() }, + { NativeLibrary.isFirmwareAvailable() }, R.string.applets_error_firmware, R.string.applets_error_description ) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt index 6443067885..61797f75f5 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt @@ -352,7 +352,7 @@ class SetupFragment : Fragment() { val getProdKey = registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> if (result != null) { - mainActivity.processKey(result) + mainActivity.processKey(result, "keys") if (NativeLibrary.areKeysPresent()) { keyCallback.onStepCompleted() } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt index 1a74057569..43b9085f50 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt @@ -47,9 +47,6 @@ import info.debatty.java.stringsimilarity.Jaccard import info.debatty.java.stringsimilarity.JaroWinkler import java.util.Locale import androidx.core.content.edit -import androidx.core.view.updateLayoutParams -import org.yuzu.yuzu_emu.features.settings.model.Settings -import android.view.ViewParent import androidx.core.view.doOnNextLayout class GamesFragment : Fragment() { @@ -151,7 +148,7 @@ class GamesFragment : Fragment() { ) } gamesViewModel.games.collect(viewLifecycleOwner) { - if (it.size > 0) { + if (it.isNotEmpty()) { setAdapter(it) } } @@ -361,7 +358,7 @@ class GamesFragment : Fragment() { popup.setOnMenuItemClickListener { item -> currentFilter = item.itemId - preferences.edit().putInt(PREF_SORT_TYPE, currentFilter).apply() + preferences.edit { putInt(PREF_SORT_TYPE, currentFilter) } filterAndSearch() true } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt index f3800f94e9..f8ba35dbd5 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt @@ -6,6 +6,8 @@ package org.yuzu.yuzu_emu.ui.main import android.content.Intent import android.net.Uri import android.os.Bundle +import android.os.ParcelFileDescriptor +import android.provider.OpenableColumns import android.view.View import android.view.ViewGroup.MarginLayoutParams import android.view.WindowManager @@ -47,6 +49,7 @@ import java.io.BufferedOutputStream import java.util.zip.ZipEntry import java.util.zip.ZipInputStream import androidx.core.content.edit +import androidx.core.net.toFile class MainActivity : AppCompatActivity(), ThemeProvider { private lateinit var binding: ActivityMainBinding @@ -70,10 +73,12 @@ class MainActivity : AppCompatActivity(), ThemeProvider { val granted = permissions.entries.all { it.value } if (granted) { // Permissions were granted. - Toast.makeText(this, R.string.bluetooth_permissions_granted, Toast.LENGTH_SHORT).show() + Toast.makeText(this, R.string.bluetooth_permissions_granted, Toast.LENGTH_SHORT) + .show() } else { // Permissions were denied. - Toast.makeText(this, R.string.bluetooth_permissions_denied, Toast.LENGTH_LONG).show() + Toast.makeText(this, R.string.bluetooth_permissions_denied, Toast.LENGTH_LONG) + .show() } } @@ -94,7 +99,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } } } - + override fun onCreate(savedInstanceState: Bundle?) { val splashScreen = installSplashScreen() splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady } @@ -105,13 +110,13 @@ class MainActivity : AppCompatActivity(), ThemeProvider { NativeLibrary.initMultiplayer() binding = ActivityMainBinding.inflate(layoutInflater) - + setContentView(binding.root) - - + + checkAndRequestBluetoothPermissions() - + if (savedInstanceState != null) { checkedDecryption = savedInstanceState.getBoolean(CHECKED_DECRYPTION) checkedFirmware = savedInstanceState.getBoolean(CHECKED_FIRMWARE) @@ -146,22 +151,16 @@ class MainActivity : AppCompatActivity(), ThemeProvider { binding.statusBarShade.setBackgroundColor( ThemeHelper.getColorWithOpacity( MaterialColors.getColor( - binding.root, - com.google.android.material.R.attr.colorSurface - ), - ThemeHelper.SYSTEM_BAR_ALPHA + binding.root, com.google.android.material.R.attr.colorSurface + ), ThemeHelper.SYSTEM_BAR_ALPHA ) ) - if (InsetsHelper.getSystemGestureType(applicationContext) != - InsetsHelper.GESTURE_NAVIGATION - ) { + if (InsetsHelper.getSystemGestureType(applicationContext) != InsetsHelper.GESTURE_NAVIGATION) { binding.navigationBarShade.setBackgroundColor( ThemeHelper.getColorWithOpacity( MaterialColors.getColor( - binding.root, - com.google.android.material.R.attr.colorSurface - ), - ThemeHelper.SYSTEM_BAR_ALPHA + binding.root, com.google.android.material.R.attr.colorSurface + ), ThemeHelper.SYSTEM_BAR_ALPHA ) ) } @@ -172,9 +171,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { homeViewModel.statusBarShadeVisible.collect(this) { showStatusBarShade(it) } homeViewModel.contentToInstall.collect( - this, - resetState = { homeViewModel.setContentToInstall(null) } - ) { + this, resetState = { homeViewModel.setContentToInstall(null) }) { if (it != null) { installContent(it) } @@ -183,7 +180,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider { if (it) checkKeys() } - homeViewModel.checkFirmware.collect(this, resetState = { homeViewModel.setCheckFirmware(false) }) { + homeViewModel.checkFirmware.collect( + this, resetState = { homeViewModel.setCheckFirmware(false) }) { if (it) checkFirmware() } @@ -203,12 +201,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider { negativeButtonTitleId = R.string.close, showNegativeButton = true, positiveAction = { - PreferenceManager.getDefaultSharedPreferences(applicationContext) - .edit() { - putBoolean(Settings.PREF_SHOULD_SHOW_PRE_ALPHA_WARNING, false) - } - } - ).show(supportFragmentManager, MessageDialogFragment.TAG) + PreferenceManager.getDefaultSharedPreferences(applicationContext).edit() { + putBoolean(Settings.PREF_SHOULD_SHOW_PRE_ALPHA_WARNING, false) + } + }).show(supportFragmentManager, MessageDialogFragment.TAG) } } @@ -228,15 +224,18 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } private fun checkFirmware() { - if (!NativeLibrary.isFirmwareAvailable() || !NativeLibrary.isFirmwareSupported()) { - MessageDialogFragment.newInstance( - titleId = R.string.firmware_missing, - descriptionId = R.string.firmware_missing_description, - helpLinkId = R.string.firmware_missing_help - ).show(supportFragmentManager, MessageDialogFragment.TAG) - } - } + val resultCode: Int = NativeLibrary.verifyFirmware() + if (resultCode == 0) return; + val resultString: String = + resources.getStringArray(R.array.verifyFirmwareResults)[resultCode] + + MessageDialogFragment.newInstance( + titleId = R.string.firmware_invalid, + descriptionString = resultString, + helpLinkId = R.string.firmware_missing_help + ).show(supportFragmentManager, MessageDialogFragment.TAG) + } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) @@ -283,23 +282,22 @@ class MainActivity : AppCompatActivity(), ThemeProvider { super.onResume() } - private fun setInsets() = - ViewCompat.setOnApplyWindowInsetsListener( - binding.root - ) { _: View, windowInsets: WindowInsetsCompat -> - val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) - val mlpStatusShade = binding.statusBarShade.layoutParams as MarginLayoutParams - mlpStatusShade.height = insets.top - binding.statusBarShade.layoutParams = mlpStatusShade + private fun setInsets() = ViewCompat.setOnApplyWindowInsetsListener( + binding.root + ) { _: View, windowInsets: WindowInsetsCompat -> + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) + val mlpStatusShade = binding.statusBarShade.layoutParams as MarginLayoutParams + mlpStatusShade.height = insets.top + binding.statusBarShade.layoutParams = mlpStatusShade - // The only situation where we care to have a nav bar shade is when it's at the bottom - // of the screen where scrolling list elements can go behind it. - val mlpNavShade = binding.navigationBarShade.layoutParams as MarginLayoutParams - mlpNavShade.height = insets.bottom - binding.navigationBarShade.layoutParams = mlpNavShade + // The only situation where we care to have a nav bar shade is when it's at the bottom + // of the screen where scrolling list elements can go behind it. + val mlpNavShade = binding.navigationBarShade.layoutParams as MarginLayoutParams + mlpNavShade.height = insets.bottom + binding.navigationBarShade.layoutParams = mlpNavShade - windowInsets - } + windowInsets + } override fun setTheme(resId: Int) { super.setTheme(resId) @@ -315,17 +313,14 @@ class MainActivity : AppCompatActivity(), ThemeProvider { fun processGamesDir(result: Uri, calledFromGameFragment: Boolean = false) { contentResolver.takePersistableUriPermission( - result, - Intent.FLAG_GRANT_READ_URI_PERMISSION + result, Intent.FLAG_GRANT_READ_URI_PERMISSION ) val uriString = result.toString() val folder = gamesViewModel.folders.value.firstOrNull { it.uriString == uriString } if (folder != null) { Toast.makeText( - applicationContext, - R.string.folder_already_added, - Toast.LENGTH_SHORT + applicationContext, R.string.folder_already_added, Toast.LENGTH_SHORT ).show() return } @@ -334,72 +329,50 @@ class MainActivity : AppCompatActivity(), ThemeProvider { .show(supportFragmentManager, AddGameFolderDialogFragment.TAG) } - val getProdKey = - registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> - if (result != null) { - processKey(result) - } + val getProdKey = registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> + if (result != null) { + processKey(result, "keys") } - - fun processKey(result: Uri): Boolean { - if (FileUtil.getExtension(result) != "keys") { - MessageDialogFragment.newInstance( - this, - titleId = R.string.reading_keys_failure, - descriptionId = R.string.install_prod_keys_failure_extension_description - ).show(supportFragmentManager, MessageDialogFragment.TAG) - return false - } - - contentResolver.takePersistableUriPermission( - result, - Intent.FLAG_GRANT_READ_URI_PERMISSION - ) - - val dstPath = DirectoryInitialization.userDirectory + "/keys/" - if (FileUtil.copyUriToInternalStorage( - result, - dstPath, - "prod.keys" - ) != null - ) { - if (NativeLibrary.reloadKeys()) { - Toast.makeText( - applicationContext, - R.string.install_keys_success, - Toast.LENGTH_SHORT - ).show() - homeViewModel.setCheckKeys(true) - - val firstTimeSetup = PreferenceManager.getDefaultSharedPreferences(applicationContext) - .getBoolean(Settings.PREF_FIRST_APP_LAUNCH, true) - if (!firstTimeSetup) { - homeViewModel.setCheckFirmware(true) - } - - gamesViewModel.reloadGames(true) - return true - } else { - MessageDialogFragment.newInstance( - this, - titleId = R.string.invalid_keys_error, - descriptionId = R.string.install_keys_failure_description, - helpLinkId = R.string.dumping_keys_quickstart_link - ).show(supportFragmentManager, MessageDialogFragment.TAG) - return false - } - } - return false } - val getFirmware = - registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> - if (result == null) { - return@registerForActivityResult - } + val getAmiiboKey = registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> + if (result != null) { + processKey(result, "bin") + } + } + fun processKey(result: Uri, extension: String = "keys") { + contentResolver.takePersistableUriPermission( + result, Intent.FLAG_GRANT_READ_URI_PERMISSION + ) + + val resultCode: Int = NativeLibrary.installKeys(result.toString(), extension); + + if (resultCode == 0) { + Toast.makeText( + applicationContext, R.string.keys_install_success, Toast.LENGTH_SHORT + ).show() + + gamesViewModel.reloadGames(true) + + return + } + + val resultString: String = + resources.getStringArray(R.array.installKeysResults)[resultCode] + + MessageDialogFragment.newInstance( + titleId = R.string.keys_failed, + descriptionString = resultString, + helpLinkId = R.string.keys_missing_help + ).show(supportFragmentManager, MessageDialogFragment.TAG) + } + + val getFirmware = registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> + if (result != null) { processFirmware(result) } + } fun processFirmware(result: Uri, onComplete: (() -> Unit)? = null) { val filterNCA = FilenameFilter { _, dirName -> dirName.endsWith(".nca") } @@ -409,15 +382,12 @@ class MainActivity : AppCompatActivity(), ThemeProvider { val cacheFirmwareDir = File("${cacheDir.path}/registered/") ProgressDialogFragment.newInstance( - this, - R.string.firmware_installing + this, R.string.firmware_installing ) { progressCallback, _ -> var messageToShow: Any try { FileUtil.unzipToInternalStorage( - result.toString(), - cacheFirmwareDir, - progressCallback + result.toString(), cacheFirmwareDir, progressCallback ) val unfilteredNumOfFiles = cacheFirmwareDir.list()?.size ?: -1 val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2 @@ -448,10 +418,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } fun uninstallFirmware() { - val firmwarePath = File(DirectoryInitialization.userDirectory + "/nand/system/Contents/registered/") + val firmwarePath = + File(DirectoryInitialization.userDirectory + "/nand/system/Contents/registered/") ProgressDialogFragment.newInstance( - this, - R.string.firmware_uninstalling + this, R.string.firmware_uninstalling ) { progressCallback, _ -> var messageToShow: Any try { @@ -473,49 +443,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider { messageToShow }.show(supportFragmentManager, ProgressDialogFragment.TAG) } - val getAmiiboKey = - registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> - if (result == null) { - return@registerForActivityResult - } - - if (FileUtil.getExtension(result) != "bin") { - MessageDialogFragment.newInstance( - this, - titleId = R.string.reading_keys_failure, - descriptionId = R.string.install_amiibo_keys_failure_extension_description - ).show(supportFragmentManager, MessageDialogFragment.TAG) - return@registerForActivityResult - } - - contentResolver.takePersistableUriPermission( - result, - Intent.FLAG_GRANT_READ_URI_PERMISSION - ) - - val dstPath = DirectoryInitialization.userDirectory + "/keys/" - if (FileUtil.copyUriToInternalStorage( - result, - dstPath, - "key_retail.bin" - ) != null - ) { - if (NativeLibrary.reloadKeys()) { - Toast.makeText( - applicationContext, - R.string.install_keys_success, - Toast.LENGTH_SHORT - ).show() - } else { - MessageDialogFragment.newInstance( - this, - titleId = R.string.invalid_keys_error, - descriptionId = R.string.install_keys_failure_description, - helpLinkId = R.string.dumping_keys_quickstart_link - ).show(supportFragmentManager, MessageDialogFragment.TAG) - } - } - } val installGameUpdate = registerForActivityResult( ActivityResultContracts.OpenMultipleDocuments() @@ -530,15 +457,12 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } ProgressDialogFragment.newInstance( - this@MainActivity, - R.string.verifying_content, - false + this@MainActivity, R.string.verifying_content, false ) { _, _ -> var updatesMatchProgram = true for (document in documents) { val valid = NativeLibrary.doesUpdateMatchProgram( - addonViewModel.game!!.programId, - document.toString() + addonViewModel.game!!.programId, document.toString() ) if (!valid) { updatesMatchProgram = false @@ -554,16 +478,14 @@ class MainActivity : AppCompatActivity(), ThemeProvider { titleId = R.string.content_install_notice, descriptionId = R.string.content_install_notice_description, positiveAction = { homeViewModel.setContentToInstall(documents) }, - negativeAction = {} - ) + negativeAction = {}) } }.show(supportFragmentManager, ProgressDialogFragment.TAG) } private fun installContent(documents: List) { ProgressDialogFragment.newInstance( - this@MainActivity, - R.string.installing_game_content + this@MainActivity, R.string.installing_game_content ) { progressCallback, messageCallback -> var installSuccess = 0 var installOverwrite = 0 @@ -571,14 +493,11 @@ class MainActivity : AppCompatActivity(), ThemeProvider { var error = 0 documents.forEach { messageCallback.invoke(FileUtil.getFilename(it)) - when ( - InstallResult.from( - NativeLibrary.installFileToNand( - it.toString(), - progressCallback - ) + when (InstallResult.from( + NativeLibrary.installFileToNand( + it.toString(), progressCallback ) - ) { + )) { InstallResult.Success -> { installSuccess += 1 } @@ -599,13 +518,12 @@ class MainActivity : AppCompatActivity(), ThemeProvider { addonViewModel.refreshAddons() - val separator = System.getProperty("line.separator") ?: "\n" + val separator = System.lineSeparator() ?: "\n" val installResult = StringBuilder() if (installSuccess > 0) { installResult.append( getString( - R.string.install_game_content_success_install, - installSuccess + R.string.install_game_content_success_install, installSuccess ) ) installResult.append(separator) @@ -613,8 +531,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { if (installOverwrite > 0) { installResult.append( getString( - R.string.install_game_content_success_overwrite, - installOverwrite + R.string.install_game_content_success_overwrite, installOverwrite ) ) installResult.append(separator) @@ -624,8 +541,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { installResult.append(separator) installResult.append( getString( - R.string.install_game_content_failed_count, - errorTotal + R.string.install_game_content_failed_count, errorTotal ) ) installResult.append(separator) @@ -666,9 +582,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } ProgressDialogFragment.newInstance( - this, - R.string.exporting_user_data, - true + this, R.string.exporting_user_data, true ) { progressCallback, _ -> val zipResult = FileUtil.zipFromInternalStorage( File(DirectoryInitialization.userDirectory!!), @@ -692,8 +606,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } ProgressDialogFragment.newInstance( - this, - R.string.importing_user_data + this, R.string.importing_user_data ) { progressCallback, _ -> val checkStream = ZipInputStream(BufferedInputStream(contentResolver.openInputStream(result))) diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index fe7c0658d4..9fed0b1449 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -60,6 +60,7 @@ #include "core/hle/service/set/system_settings_server.h" #include "core/loader/loader.h" #include "frontend_common/config.h" +#include "frontend_common/firmware_manager.h" #include "hid_core/frontend/emulated_controller.h" #include "hid_core/hid_core.h" #include "hid_core/hid_types.h" @@ -283,6 +284,7 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string : Service::AM::LaunchType::ApplicationInitiated, .program_index = static_cast(program_index), }; + m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath, params); if (m_load_result != Core::SystemResultStatus::Success) { return m_load_result; @@ -764,35 +766,19 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCabinetMode(JNIEnv* env, jclass cl } bool isFirmwarePresent() { - auto bis_system = - EmulationSession::GetInstance().System().GetFileSystemController().GetSystemNANDContents(); - if (!bis_system) { - return false; - } - - // Query an applet to see if it's available - auto applet_nca = - bis_system->GetEntry(0x010000000000100Dull, FileSys::ContentRecordType::Program); - if (!applet_nca) { - return false; - } - return true; + return FirmwareManager::CheckFirmwarePresence(EmulationSession::GetInstance().System()); } jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isFirmwareAvailable(JNIEnv* env, jclass clazz) { return isFirmwarePresent(); } -// TODO(crueter): This check is nonfunctional... jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_firmwareVersion(JNIEnv* env, jclass clazz) { - Service::Set::FirmwareVersionFormat firmware_data{}; - const auto result = Service::Set::GetFirmwareVersionImpl( - firmware_data, EmulationSession::GetInstance().System(), - Service::Set::GetFirmwareVersionType::Version2); + const auto pair = FirmwareManager::GetFirmwareVersion(EmulationSession::GetInstance().System()); + const auto firmware_data = pair.first; + const auto result = pair.second; if (result.IsError() || !isFirmwarePresent()) { - LOG_INFO(Frontend, "Installed firmware: No firmware available"); - return Common::Android::ToJString(env, "N/A"); } @@ -804,6 +790,23 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_firmwareVersion(JNIEnv* env, jclas return Common::Android::ToJString(env, display_version); } +jint Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyFirmware(JNIEnv* env, jclass clazz) { + return static_cast(FirmwareManager::VerifyFirmware(EmulationSession::GetInstance().System())); +} + +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_gameRequiresFirmware(JNIEnv* env, jclass clazz, jstring jprogramId) { + auto program_id = EmulationSession::GetProgramId(env, jprogramId); + + return FirmwareManager::GameRequiresFirmware(program_id); +} + +jint Java_org_yuzu_yuzu_1emu_NativeLibrary_installKeys(JNIEnv* env, jclass clazz, jstring jpath, jstring jext) { + const auto path = Common::Android::GetJString(env, jpath); + const auto ext = Common::Android::GetJString(env, jext); + + return static_cast(FirmwareManager::InstallKeys(path, ext)); +} + jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env, jobject jobj, jstring jpath, jstring jprogramId) { diff --git a/src/android/app/src/main/res/values-ar/strings.xml b/src/android/app/src/main/res/values-ar/strings.xml index 4ea8b30c1e..e100bafe7e 100644 --- a/src/android/app/src/main/res/values-ar/strings.xml +++ b/src/android/app/src/main/res/values-ar/strings.xml @@ -349,13 +349,7 @@ إلغاء تثبيت مفاتيح أميبو مطلوب لاستخدام أميبو في اللعبة - تم تحديد ملف مفاتيح غير صالح - تم تثبيت المفاتيح بنجاح - خطأ في قراءة مفاتيح التشفير - وحاول مرة أخر keys تحقق من أن ملف المفاتيح له امتداد وحاول مرة أخر bin تحقق من أن ملف المفاتيح له امتداد - مفاتيح التشفير غير صالحة - الملف المحدد غير صحيح أو تالف. يرجى إعادة المفاتيح الخاصة بك GPU مدير برنامج تشغيل GPU تثبيت برنامج تشغيل قم بتثبيت برامج تشغيل بديلة للحصول على أداء أو دقة أفضل @@ -422,8 +416,6 @@ فحص المحتوى المثبت بحثًا عن تلف مفاتيح التشفير مفقودة لا يمكن فك تشفير البرنامج الثابت والألعاب - البرنامج الثابت مفقود أو جديد جدًا - بعض الألعاب قد لا تعمل بشكل صحيح. يتطلب إيدن البرنامج الثابت 19.0.1 أو أقل. Qlaunch diff --git a/src/android/app/src/main/res/values-ckb/strings.xml b/src/android/app/src/main/res/values-ckb/strings.xml index af4f2056b3..d13f24d173 100644 --- a/src/android/app/src/main/res/values-ckb/strings.xml +++ b/src/android/app/src/main/res/values-ckb/strings.xml @@ -344,13 +344,7 @@ ڕەتکردنەوە دامەزراندنی کلیلی Amiibo پێویستە بۆ بەکارهێنانی Amiibo لە یاریدا - فایلی کلیلێکی نادروست هەڵبژێردرا - کلیلەکان بە سەرکەوتوویی دامەزران - هەڵە لە خوێندنەوەی کۆدکردنی کلیل - دڵنیابەوە کە فایلی کلیلەکانت درێژکراوەی .keys ی هەیە و دووبارە هەوڵبدەرەوە. دڵنیابە کە فایلی کلیلەکانت درێژکراوەی .bin ی هەیە و دووبارە هەوڵبدەرەوە. - کلیلی کۆدکردنی نادروستە - فایلە هەڵبژێردراوەکە هەڵەیە یان تێکچووە. تکایە دووبارە کلیلەکانت دەربێنەوە. دامەزراندنی وەگەڕخەری GPU دامەزراندنی وەگەڕخەری بەدیل بۆ ئەوەی بە ئەگەرێکی زۆرەوە کارایی باشتر یان وردبینی هەبێت ڕێکخستنە پێشکەوتووەکان @@ -416,8 +410,6 @@ هەموو ناوەڕۆکی دامەزراو پشکنین دەکات بۆ تێکچوون کلیلە کۆدکراوەکان دیار نییە پتەوواڵا و یارییە تاکەکەسییەکان ناتوانرێت کۆد بکرێنەوە - فریموێر بوونی نییە یان زۆر نوێە - هەندێ یاری لەوانەیە باش کار نەکەن. ئێدەن پێویستی بە فریموێری ١٩.٠.١ یان کەمترە. Qlaunch diff --git a/src/android/app/src/main/res/values-cs/strings.xml b/src/android/app/src/main/res/values-cs/strings.xml index c71ea42f75..fe8575512e 100644 --- a/src/android/app/src/main/res/values-cs/strings.xml +++ b/src/android/app/src/main/res/values-cs/strings.xml @@ -333,10 +333,6 @@ Zrušit Instalovat Amiibo klíče Povinné použití Amiibo ve hře - Vybrané klíče jsou neplatné - Klíče úspěšně nainstalovány - Chyba při čtení šifrovacích klíčů - Neplatné šifrovací klíče Správce ovladače GPU Instalovat GPU ovladač Pokročilé nastavení @@ -380,8 +376,6 @@ Kontrola poškození obsahu Chybí šifrovací klíče Firmware a retail hry nelze dešifrovat - Firmware chybí nebo je příliš nový - Některé hry nemusí fungovat správně. Eden vyžaduje firmware 19.0.1 nebo nižší. Qlaunch diff --git a/src/android/app/src/main/res/values-de/strings.xml b/src/android/app/src/main/res/values-de/strings.xml index 06b14f7c15..93881eabb5 100644 --- a/src/android/app/src/main/res/values-de/strings.xml +++ b/src/android/app/src/main/res/values-de/strings.xml @@ -346,13 +346,7 @@ Abbrechen Amiibo-Schlüssel installieren Benötigt um Amiibos im Spiel zu verwenden - Ungültige Schlüsseldatei ausgewählt - Schlüssel erfolgreich installiert - Fehler beim Lesen der Schlüssel - Überprüfen Sie, ob Ihre Schlüsseldatei die Erweiterung \".keys\" hat, und versuchen Sie es erneut. Überprüfen Sie, ob Ihre Schlüsseldatei die Erweiterung \".bin\" hat, und versuchen Sie es erneut. - Ungültige Schlüssel - Die ausgewählte Datei ist falsch oder beschädigt. Bitte kopieren Sie Ihre Schlüssel erneut. GPU-Treiber Verwaltung GPU-Treiber installieren Alternative Treiber für eventuell bessere Leistung oder Genauigkeit installieren @@ -415,8 +409,6 @@ Wirklich fortfahren? Überprüft installierte Inhalte auf Fehler Schlüssel fehlen Firmware und Spiele können nicht entschlüsselt werden - Firmware fehlt oder ist zu neu - Einige Spiele funktionieren möglicherweise nicht richtig. Eden erfordert Firmware 19.0.1 oder älter. Qlaunch diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml index 9cac800975..a55bf4d935 100644 --- a/src/android/app/src/main/res/values-es/strings.xml +++ b/src/android/app/src/main/res/values-es/strings.xml @@ -347,13 +347,7 @@ Cancelar Instalar claves de Amiibo Necesario para usar Amiibos en el juego - Archivo de claves seleccionado no válido - Claves instaladas correctamente - Error al leer las claves de cifrado - Compruebe que el archivo de claves tenga una extensión .keys y pruebe otra vez. Compruebe que el archivo de claves tenga una extensión .bin y pruebe otra vez. - Claves de cifrado no válidas - El archivo seleccionado es incorrecto o está corrupto. Vuelva a redumpear sus claves. Explorador de drivers de GPU Instalar driver de GPU Instale drivers alternativos para obtener un rendimiento o una precisión potencialmente mejores @@ -428,8 +422,6 @@ Comprueba todo el contenido instalado por si hubiese alguno corrupto Faltan las claves de encriptación El firmware y los juegos no se pueden desencriptar - Falta el firmware o es demasiado nuevo - Algunos juegos pueden no funcionar correctamente. Eden requiere firmware 19.0.1 o inferior. Qlaunch diff --git a/src/android/app/src/main/res/values-fa/strings.xml b/src/android/app/src/main/res/values-fa/strings.xml index 48f024322b..99f48c1c9d 100644 --- a/src/android/app/src/main/res/values-fa/strings.xml +++ b/src/android/app/src/main/res/values-fa/strings.xml @@ -347,13 +347,7 @@ لغو کلیدهای Amiibo را نصب کنید برای استفاده از Amiibo در بازی لازم است - فایل کلیدهای نامعتبر انتخاب شد - کلیدها با موفقیت نصب شدند - خطا در خواندن کلیدهای رمزگذاری - بررسی کنید که فایل کلیدهای شما دارای پسوند keys. باشد و دوباره امتحان کنید. بررسی کنید که فایل کلیدهای شما دارای پسوند bin. باشد و دوباره امتحان کنید. - کلیدهای رمزگذاری نامعتبر - فایل انتخابی نادرست یا خراب است. لطفا کلیدهای خود را دوباره استخراج کنید. مدیریت درایور پردازنده گرافیکی نصب درایور پردازنده گرافیکی درایورهای جایگزین را برای عملکرد یا دقت بهتر نصب کنید @@ -426,8 +420,6 @@ تمام محتوای نصب شده را از نظر خرابی بررسی می‌کند کلیدهای رمزگذاری وجود ندارند ثابت‌افزار و بازی‌های فروشگاهی قابل رمزگشایی نیستند - فریمور وجود ندارد یا خیلی جدید است - برخی بازی‌ها ممکن است به درستی کار نکنند. ایدن به فریمور نسخه 19.0.1 یا پایین‌تر نیاز دارد. Qlaunch diff --git a/src/android/app/src/main/res/values-fr/strings.xml b/src/android/app/src/main/res/values-fr/strings.xml index f8ace84f1f..d9783766fe 100644 --- a/src/android/app/src/main/res/values-fr/strings.xml +++ b/src/android/app/src/main/res/values-fr/strings.xml @@ -347,13 +347,7 @@ Annuler Installer les clés Amiibo Nécessaire pour utiliser les Amiibo en jeu - Fichier de clés sélectionné invalide - Clés installées avec succès - Erreur lors de la lecture des clés de chiffrement - Vérifiez que votre fichier de clés a une extension .keys et réessayez. Vérifiez que votre fichier de clés a une extension .bin et réessayez. - Clés de chiffrement invalides - Le fichier sélectionné est incorrect ou corrompu. Veuillez dumper à nouveau vos clés. Gestionnaire de pilotes du GPU Installer le pilote du GPU Installer des pilotes alternatifs pour des performances ou une précision potentiellement meilleures @@ -428,8 +422,6 @@ Vérifie l\'intégrité des contenus installés Les clés de chiffrement sont manquantes. Le firmware et les jeux commerciaux ne peuvent pas être déchiffrés - Firmware manquant ou trop récent - Certains jeux peuvent ne pas fonctionner correctement. Eden nécessite le firmware 19.0.1 ou antérieur. Qlaunch diff --git a/src/android/app/src/main/res/values-he/strings.xml b/src/android/app/src/main/res/values-he/strings.xml index 711b71d3ed..47a01d7dbd 100644 --- a/src/android/app/src/main/res/values-he/strings.xml +++ b/src/android/app/src/main/res/values-he/strings.xml @@ -348,13 +348,7 @@ ביטול התקן מפתחות Amiibo נחוץ כדי להשתמש ב Amiibo במשחק - קובץ מפתחות לא חוקי נבחר - מפתחות הותקנו בהצלחה - שגיאה בקריאת מפתחות ההצפנה - ודא שלקובץ המפתחות שלך יש סיומת של key. ונסה/י שוב. ודא/י שלקובץ המפתחות שלך יש סיומת של bin. ונסה/י שוב. - מפתחות הצפנה לא חוקיים - קבוץ שנבחר מושחת או לא נכון. בבקשה הוצא מחדש את המפתחות שלך. מנהל הדרייברים של המעבד הגרפי התקן דרייבר למעבד הגרפי התקן דרייברים אחרים בשביל סיכוי לביצועים או דיוק גבוההים יותר @@ -427,8 +421,6 @@ בודק תוכן מותקן לשגיאות מפתחות הצפנה חסרים לא ניתן לפענח firmware ומשחקים - קושחה חסרה או חדשה מדי - חלק מהמשחקים עשויים לא לפעול כראוי. Eden דורש קושחה בגרסה 19.0.1 או נמוכה יותר. Qlaunch diff --git a/src/android/app/src/main/res/values-hu/strings.xml b/src/android/app/src/main/res/values-hu/strings.xml index 0bedc72e29..e14af511c6 100644 --- a/src/android/app/src/main/res/values-hu/strings.xml +++ b/src/android/app/src/main/res/values-hu/strings.xml @@ -346,13 +346,7 @@ Mégse Amiibo kulcsok telepítése Amiibo használata szükséges a játékhoz - Érvénytelen titkosítófájlok kiválasztva - Kulcsok sikeresen telepítve - Hiba történt a titkosítókulcsok olvasása során - Győződj meg róla, hogy a titkosító fájlod .keys kiterjesztéssel rendelkezik, majd próbáld újra. Győződj meg róla, hogy a titkosító fájlod .bin kiterjesztéssel rendelkezik, majd próbáld újra. - Érvénytelen titkosítókulcsok - A kiválasztott fájl helytelen, vagy sérült. Állíts össze egy új kulcsot. GPU illesztőprogram-kezelő GPU illesztőprogram telepítése Alternatív illesztőprogramok telepítése az esetlegesen elérhető teljesítmény és pontosság érdekében @@ -424,8 +418,6 @@ A telepített tartalom épségét ellenőrzi Hiányzó titkosítókulcsok A Firmware és a kiskereskedelmi (retail) játékok nem dekódolhatók - Hiányzó vagy túl új firmware - Néhány játék nem fog megfelelően működni. Az Eden 19.0.1 vagy régebbi firmware-t igényel. Qlaunch diff --git a/src/android/app/src/main/res/values-id/strings.xml b/src/android/app/src/main/res/values-id/strings.xml index 542734e451..2d931d2c5d 100644 --- a/src/android/app/src/main/res/values-id/strings.xml +++ b/src/android/app/src/main/res/values-id/strings.xml @@ -347,13 +347,7 @@ Batalkan Install Amiibo keys Diperlukan untuk menggunakan Amiibo di dalam game - Keys yang dipilih invalid - Keys berhasil diinstal - Error saat mengecek enkripsi keys - Pastikan file keys anda memiliki format .keys dan coba lagi. Pastikan file keys anda memiliki format .bin dan coba lagi. - Keys enkripsi tidak valid - File yang dipilih salah atau rusak. Silakan masukkan kembali kunci Anda. Manajer driver GPU Install driver GPU Instal driver lain untuk kinerja atau akurasi yang berpotensi lebih baik @@ -424,8 +418,6 @@ Memeriksa semua konten yang terinstal dari kerusakan Kunci enkripsi hilang Firmware dan game retail tidak dapat didekripsi - Firmware hilang atau terlalu baru - Beberapa game mungkin tidak berfungsi dengan baik. Eden memerlukan firmware 19.0.1 atau lebih rendah. Qlaunch diff --git a/src/android/app/src/main/res/values-it/strings.xml b/src/android/app/src/main/res/values-it/strings.xml index ad5b9f62cf..33a937ca93 100644 --- a/src/android/app/src/main/res/values-it/strings.xml +++ b/src/android/app/src/main/res/values-it/strings.xml @@ -348,13 +348,7 @@ Annulla Installa le chiavi degli Amiibo Necessario per usare gli Amiibo in gioco - Selezionate chiavi non valide - Chiavi installate correttamente - Errore durante la lettura delle chiavi di crittografia - Controlla che le tue chiavi abbiano l\'estensione .keys e prova di nuovo. Controlla che le tue chiavi abbiano l\'estensione .bin e prova di nuovo - Chiavi di crittografia non valide - Il file selezionato è incorretto o corrotto. Per favore riesegui il dump delle tue chiavi. Gestore driver GPU Installa i driver GPU Installa driver alternativi per potenziali prestazioni migliori o accuratezza. @@ -427,8 +421,6 @@ Verifica l\'integrità di tutti i contenuti installati. Chiavi di crittografia mancanti Impossibile decifrare firmware e giochi retail - Firmware mancante o troppo recente - Alcuni giochi potrebbero non funzionare correttamente. Eden richiede il firmware 19.0.1 o precedente. Qlaunch diff --git a/src/android/app/src/main/res/values-ja/strings.xml b/src/android/app/src/main/res/values-ja/strings.xml index b209943135..c8d23cb6b9 100644 --- a/src/android/app/src/main/res/values-ja/strings.xml +++ b/src/android/app/src/main/res/values-ja/strings.xml @@ -346,13 +346,7 @@ キャンセル Amiibo ゲーム内での Amiibo の使用に必要です - 無効なキーファイルです - 正常にインストールされました - 暗号化キーの読み込み失敗 - キーの拡張子が.keysであることを確認し、再度お試しください。 キーの拡張子が.binであることを確認し、再度お試しください。 - 暗号化キーが無効 - ファイルが間違っているか破損しています。キーを再ダンプしてください。 GPUドライバーの管理 GPUドライバー 代替ドライバーをインストールしてパフォーマンスや精度を向上させます @@ -417,8 +411,6 @@ すべてのインストール済みコンテンツの整合性を確認 暗号化キーが不足 ファームウェアと製品版ゲームを復号化できません - ファームウェアがないか、バージョンが新しすぎます - 一部のゲームが正常に動作しない可能性があります。Edenは19.0.1以下のファームウェアが必要です。 Qlaunch diff --git a/src/android/app/src/main/res/values-ko/strings.xml b/src/android/app/src/main/res/values-ko/strings.xml index 595795570f..deb901fc3f 100644 --- a/src/android/app/src/main/res/values-ko/strings.xml +++ b/src/android/app/src/main/res/values-ko/strings.xml @@ -346,13 +346,7 @@ 취소 amiibo 키 설치 게임에서 amiibo 사용 시 필요 - 잘못된 키 파일이 선택됨 - 키 값을 설치했습니다. - 암호화 키 읽기 오류 - 키 파일의 확장자가 .keys인지 확인하고 다시 시도하세요. 키 파일의 확장자가 .bin인지 확인하고 다시 시도하세요. - 암호화 키가 올바르지 않음 - 선택한 파일이 잘못되었거나 손상되었습니다. 키를 다시 덤프하세요. GPU 드라이버 관리자 GPU 드라이버 설치 잠재적인 성능 또는 정확도 개선을 위해 대체 드라이버 설치 @@ -423,8 +417,6 @@ 전체 설치된 콘텐츠의 손상을 확인합니다. 암호화 키를 찾을 수 없음 펌웨어 및 패키지 게임을 해독할 수 없음 - 펌웨어가 없거나 버전이 너무 높습니다 - 일부 게임이 제대로 작동하지 않을 수 있습니다. Eden은 19.0.1 이하 버전의 펌웨어가 필요합니다. Qlaunch diff --git a/src/android/app/src/main/res/values-nb/strings.xml b/src/android/app/src/main/res/values-nb/strings.xml index 82a4601e65..80173a357d 100644 --- a/src/android/app/src/main/res/values-nb/strings.xml +++ b/src/android/app/src/main/res/values-nb/strings.xml @@ -344,13 +344,7 @@ Avbryt Installer Amiibo-nøkler Kreves for å bruke Amiibo i spillet - Ugyldig nøkkelfil valgt - Nøkler vellykket installert - Feil ved lesing av krypteringsnøkler - Kontroller at nøkkelfilen har filtypen .keys, og prøv igjen. Kontroller at nøkkelfilen har filtypen .bin, og prøv igjen. - Ugyldige krypteringsnøkler - Den valgte filen er feil eller ødelagt. Vennligst dump nøklene på nytt. Installer GPU-driver Installer alternative drivere for potensielt bedre ytelse eller nøyaktighet. Avanserte innstillinger @@ -416,8 +410,6 @@ Sjekk for korrupsjon Nøkler mangler Kan ikke dekryptere firmware/spill - Firmware mangler eller er for ny - Noen spill fungerer kanskje ikke skikkelig. Eden krever firmware 19.0.1 eller lavere. Qlaunch diff --git a/src/android/app/src/main/res/values-pl/strings.xml b/src/android/app/src/main/res/values-pl/strings.xml index 394de5fb16..4742f795c8 100644 --- a/src/android/app/src/main/res/values-pl/strings.xml +++ b/src/android/app/src/main/res/values-pl/strings.xml @@ -344,13 +344,7 @@ Anuluj Zainstaluj klucze Amiibo Wymagane aby korzystać z Amiibo w grze - Wybrano niepoprawne klucze - Klucze zainstalowane pomyślnie - Błąd podczas odczytu kluczy - Upewnij się że twoje klucze mają rozszerzenie .keys i spróbuj ponownie. Upewnij się że twoje klucze mają rozszerzenie .bin i spróbuj ponownie. - Niepoprawne klucze - Wybrany plik jest niepoprawny lub uszkodzony. Zrzuć ponownie swoje klucze. Zainstaluj sterownik GPU Użyj alternatywnych sterowników aby potencjalnie zwiększyć wydajność i naprawić błędy Ustawienia zaawansowane @@ -416,8 +410,6 @@ Sprawdza integralność zainstalowanych plików. Brak kluczy Firmware i gry nie mogą być odszyfrowane. - Brak firmware lub zbyt nowa wersja - Niektóre gry mogą nie działać poprawnie. Eden wymaga firmware w wersji 19.0.1 lub starszej. Qlaunch diff --git a/src/android/app/src/main/res/values-pt-rBR/strings.xml b/src/android/app/src/main/res/values-pt-rBR/strings.xml index 46778754ee..a886450e47 100644 --- a/src/android/app/src/main/res/values-pt-rBR/strings.xml +++ b/src/android/app/src/main/res/values-pt-rBR/strings.xml @@ -347,13 +347,7 @@ Cancelar Instalar chaves Amiibo Necessárias para usar Amiibos em um jogo - Arquivo de chaves selecionado inválido - Chaves instaladas com sucesso - Erro ao ler chaves de encriptação - Verifique se seu arquivo de chaves possui a extensão .keys e tente novamente. Verifique se seu arquivo de chaves possui a extensão .bin e tente novamente. - Chaves de encriptação inválidas - O arquivo selecionado está incorreto ou corrompido. Por favor extraia suas chaves novamente. Gerenciador de driver de GPU Instalar driver para GPU Instale drivers alternativos para desempenho ou precisão potencialmente melhores @@ -428,8 +422,6 @@ Verifica todo o conteúdo instalado em busca de dados corrompidos Faltando chaves de encriptação O firmware e jogos comerciais não poderão ser decriptados - Firmware ausente ou muito recente - Alguns jogos podem não funcionar corretamente. O Eden requer firmware 19.0.1 ou inferior. Qlaunch diff --git a/src/android/app/src/main/res/values-pt-rPT/strings.xml b/src/android/app/src/main/res/values-pt-rPT/strings.xml index 2da6518d48..caf7090993 100644 --- a/src/android/app/src/main/res/values-pt-rPT/strings.xml +++ b/src/android/app/src/main/res/values-pt-rPT/strings.xml @@ -347,13 +347,7 @@ Cancelar Instala chaves Amiibo Necessário para usares Amiibo no jogo - Ficheiro de chaves inválido - Chaves instaladas com sucesso - Erro ao ler chaves de encriptação - Verifique se seu arquivo keys possui a extensão .keys e tente novamente. Verifique se seu arquivo keys possui a extensão .bin e tente novamente. - Chaves de encriptação inválidas - O ficheiro selecionado está corrompido. Por favor recarrega as tuas chaves. Gerenciador de driver de GPU Instala driver para GPU Instala drivers alternativos para desempenho ou precisão potencialmente melhores @@ -428,8 +422,6 @@ Verifica todo o conteúdo instalado em busca de dados corrompidos Faltando chaves de encriptação O firmware e jogos comerciais não poderão ser decriptados - Firmware em falta ou demasiado recente - Alguns jogos podem não funcionar corretamente. O Eden requer firmware versão 19.0.1 ou inferior. Qlaunch diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml index b9f146aa84..3d8371324f 100644 --- a/src/android/app/src/main/res/values-ru/strings.xml +++ b/src/android/app/src/main/res/values-ru/strings.xml @@ -346,13 +346,7 @@ Отмена Установить ключи Amiibo Необходимо для использования Amiibo в играх - Выбран неверный файл ключей - Ключи успешно установлены - Ошибка при чтении ключей шифрования - Убедитесь, что файл ключей имеет расширение .keys, и повторите попытку. Убедитесь, что файл ключей имеет расширение .bin, и повторите попытку. - Неверные ключи шифрования - Выбранный файл неверен или поврежден. Пожалуйста, пере-дампите ваши ключи. Менеджер драйверов ГП Установить драйвер ГП Установите альтернативные драйверы для потенциально лучшей производительности и/или точности @@ -430,8 +424,6 @@ Проверяет весь установленный контент на наличие повреждений Отсутствуют ключи шифрования Прошивка и розничные игры не могут быть расшифрованы - Прошивка отсутствует или слишком новая - Некоторые игры могут работать некорректно. Eden требует прошивку версии 19.0.1 или ниже. Qlaunch diff --git a/src/android/app/src/main/res/values-sr/strings.xml b/src/android/app/src/main/res/values-sr/strings.xml index 0217819b68..d133a470b2 100644 --- a/src/android/app/src/main/res/values-sr/strings.xml +++ b/src/android/app/src/main/res/values-sr/strings.xml @@ -298,13 +298,7 @@ Отказати Инсталирајте Амиибо Кеис Потребно је користити Амиибо у игри - Изабрана је неважећа датотека тастера - Кључеви су успешно инсталирани - Грешка приликом чишћења кључева за шифровање - Проверите да датотека кључева има. Екеис Ектенсион и покушајте поново. Проверите да датотека кључева има .бин екстензију и покушајте поново. - Неважеће кључеве за шифровање - Изабрана датотека је нетачна или оштећена. Молим вас да вам умањите кључеве. ГПУ возач фетцхер ГПУ управљач возача Инсталирајте ГПУ драјвер @@ -380,8 +374,6 @@ Шифра о шифрирањима недостају Фирмваре и малопродајне игре не могу се дешифровати - Фирмвер недостаје или је превише нов - Неке игре можда неће радити исправно. Eden захтева фирмвер верзије 19.0.1 или старији. Клаунцх diff --git a/src/android/app/src/main/res/values-uk/strings.xml b/src/android/app/src/main/res/values-uk/strings.xml index c2ab22b0d4..91b600eded 100644 --- a/src/android/app/src/main/res/values-uk/strings.xml +++ b/src/android/app/src/main/res/values-uk/strings.xml @@ -344,12 +344,6 @@ Відміна Встановити ключі Amiibo Необхідно для використання Amiibo в іграх - Вибрано неправильний файл ключів - Ключі успішно встановлено - Помилка під час зчитування ключів шифрування - Переконайтеся, що файл ключів має розширення .keys, і повторіть спробу. - Невірні ключі шифрування - Обраний файл невірний або пошкоджений. Будь ласка, зробіть повторний дамп ваших ключів. Встановити драйвер GPU Встановіть альтернативні драйвери для потенційно кращої продуктивності та/або точності Розширені налаштування @@ -415,8 +409,6 @@ Перевіряє встановлений вміст на наявність помилок. Відсутні ключі Прошивку та роздрібні ігри не вдасться розшифрувати. - Прошивка відсутня або занадто нова - Деякі ігри можуть працювати неправильно. Eden вимагає прошивку версії 19.0.1 або нижче. Qlaunch diff --git a/src/android/app/src/main/res/values-vi/strings.xml b/src/android/app/src/main/res/values-vi/strings.xml index 75a797a386..87de8e80dc 100644 --- a/src/android/app/src/main/res/values-vi/strings.xml +++ b/src/android/app/src/main/res/values-vi/strings.xml @@ -344,13 +344,7 @@ Huỷ Cài đặt Amiibo Cần thiết để dùng Amiibo trong trò chơi - Chìa khóa không hợp lệ - Cài đặt chìa khóa thành công - Lỗi đọc keys mã hóa - Xác minh rằng tệp keys của bạn có đuôi .keys và thử lại. Xác minh rằng tệp keys của bạn có đuôi .bin và thử lại. - Keys mã hoá không hợp lệ - Chọn file sai hoặc bị hỏng. Hãy xuất chìa khóa khác Cài đặt driver GPU Cài đặt driver thay thế để có thể có hiệu suất tốt và chính xác hơn Cài đặt nâng cao @@ -416,8 +410,6 @@ Kiểm tra lỗi nội dung đã cài Thiếu keys mã hóa Không thể giải mã firmware và game - Thiếu firmware hoặc phiên bản quá mới - Một số trò chơi có thể không hoạt động bình thường. Eden yêu cầu firmware phiên bản 19.0.1 hoặc thấp hơn. Qlaunch diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml index 534b2a322d..20f9bdea90 100644 --- a/src/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml @@ -346,13 +346,7 @@ 取消 安装 Amiibo 密钥文件 在遊戏中使用 Amiibo 时必需 - 选择的密钥文件无效 - 密钥文件已成功安装 - 读取加密密钥时出错 - 请确保您的密钥文件扩展名为 .keys 并重试。 请确保您的密钥文件扩展名为 .bin 并重试。 - 无效的加密密钥 - 选择的密钥文件不正确或已损坏。请重新转储密钥文件。 GPU 驱动管理器 安装 GPU 驱动 安装替代的驱动程序以获得更好的性能和精度 @@ -423,8 +417,6 @@ 检查所有安装的内容是否有损坏 密钥缺失 无法解密固件和商业游戏 - 固件缺失或版本过新 - 某些游戏可能无法正常运行。Eden需要19.0.1或更低版本的固件。 Qlaunch diff --git a/src/android/app/src/main/res/values-zh-rTW/strings.xml b/src/android/app/src/main/res/values-zh-rTW/strings.xml index 5c4494c9aa..dcda2d8260 100644 --- a/src/android/app/src/main/res/values-zh-rTW/strings.xml +++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml @@ -350,13 +350,7 @@ 取消 安裝 Amiibo 金鑰 需要在遊戲中使用 Amiibo - 無效的金鑰檔案已選取 - 金鑰已成功安裝 - 讀取加密金鑰時發生錯誤 - 驗證您的金鑰檔案是否具有 .keys 副檔名並再試一次。 驗證您的金鑰檔案是否具有 .bin 副檔名並再試一次。 - 無效的加密金鑰 - 選取的檔案不正確或已損毀,請重新傾印您的金鑰。 GPU 驅動程式管理員 安裝 GPU 驅動程式 安裝替代驅動程式以取得潛在的更佳效能或準確度 @@ -427,8 +421,6 @@ 检查所有安装的内容是否有损坏 密钥缺失 无法解密固件和商业游戏 - 韌體缺失或版本過新 - 某些遊戲可能無法正常運作。Eden需要19.0.1或更低版本的韌體。 Qlaunch diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index d037d2680a..855fd6e769 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -496,4 +496,18 @@ 3 + + "" + @string/error_firmware_missing + @string/error_firmware_corrupted + @string/error_firmware_too_new + + + + "" + "" + @string/error_keys_copy_failed + @string/error_keys_invalid_filename + @string/error_keys_failed_init + diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 602be31027..e611e66c1f 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -304,14 +304,8 @@ Cancel Install Amiibo keys Required to use Amiibo in game - Invalid keys file selected - Keys successfully installed - Error reading encryption keys - Verify your keys file has a .keys extension and try again. Verify your keys file has a .bin extension and try again. - Invalid encryption keys https://yuzu-mirror.github.io/help/quickstart/#dumping-decryption-keys - The selected file is incorrect or corrupt. Please redump your keys. GPU driver fetcher GPU driver manager Install GPU driver @@ -390,10 +384,18 @@ Firmware and retail games cannot be decrypted https://yuzu-mirror.github.io/help/quickstart/#dumping-decryption-keys - Firmware is missing or too new - Some games may not function properly. Eden requires firmware 19.0.1 or below. + Firmware Invalid + Firmware is required to run certain games and use the Home Menu. Eden only works with firmware 19.0.1 and earlier. + Firmware reported as present, but was unable to be read. Check for decryption keys and redump firmware if necessary. + Firmware is too new or could not be read. Eden only works with firmware 19.0.1 and earlier. https://yuzu-mirror.github.io/help/quickstart/#dumping-system-firmware + Failed to Install Keys + Keys successfully installed + One or more keys failed to copy. + Verify your keys file has a .keys extension and try again. + Keys failed to initialize. Check that your dumping tools are up to date and re-dump keys. + Qlaunch Launch applications from the system home screen @@ -754,13 +756,16 @@ Your ROM is encrypted - game cartidges or installed titles.]]> + game cartridges or installed titles.]]> prod.keys file is installed so that games can be decrypted.]]> An error occurred initializing the video core This is usually caused by an incompatible GPU driver. Installing a custom GPU driver may resolve this problem. Unable to load ROM ROM file does not exist + Game Requires Firmware + dump and install firmware, or press "OK" to launch anyways.]]> + Exit emulation Done diff --git a/src/common/android/id_cache.cpp b/src/common/android/id_cache.cpp index 2625e55c35..e0edd006a5 100644 --- a/src/common/android/id_cache.cpp +++ b/src/common/android/id_cache.cpp @@ -15,7 +15,7 @@ #include -static JavaVM* s_java_vm; +static JavaVM *s_java_vm; static jclass s_native_library_class; static jclass s_disk_cache_progress_class; static jclass s_load_callback_stage_class; @@ -26,6 +26,9 @@ static jmethodID s_disk_cache_load_progress; static jmethodID s_on_emulation_started; static jmethodID s_on_emulation_stopped; static jmethodID s_on_program_changed; +static jmethodID s_copy_to_storage; +static jmethodID s_file_exists; +static jmethodID s_file_extension; static jclass s_game_class; static jmethodID s_game_constructor; @@ -102,513 +105,534 @@ static constexpr jint JNI_VERSION = JNI_VERSION_1_6; namespace Common::Android { -JNIEnv* GetEnvForThread() { - thread_local static struct OwnedEnv { - OwnedEnv() { - status = s_java_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); - if (status == JNI_EDETACHED) - s_java_vm->AttachCurrentThread(&env, nullptr); - } - - ~OwnedEnv() { - if (status == JNI_EDETACHED) - s_java_vm->DetachCurrentThread(); - } - - int status; - JNIEnv* env = nullptr; - } owned; - return owned.env; -} - -jclass GetNativeLibraryClass() { - return s_native_library_class; -} - -jclass GetDiskCacheProgressClass() { - return s_disk_cache_progress_class; -} - -jclass GetDiskCacheLoadCallbackStageClass() { - return s_load_callback_stage_class; -} - -jclass GetGameDirClass() { - return s_game_dir_class; -} - -jmethodID GetGameDirConstructor() { - return s_game_dir_constructor; -} - -jmethodID GetExitEmulationActivity() { - return s_exit_emulation_activity; -} - -jmethodID GetDiskCacheLoadProgress() { - return s_disk_cache_load_progress; -} - -jmethodID GetOnEmulationStarted() { - return s_on_emulation_started; -} - -jmethodID GetOnEmulationStopped() { - return s_on_emulation_stopped; -} - -jmethodID GetOnProgramChanged() { - return s_on_program_changed; -} - -jclass GetGameClass() { - return s_game_class; -} - -jmethodID GetGameConstructor() { - return s_game_constructor; -} - -jfieldID GetGameTitleField() { - return s_game_title_field; -} - -jfieldID GetGamePathField() { - return s_game_path_field; -} - -jfieldID GetGameProgramIdField() { - return s_game_program_id_field; -} - -jfieldID GetGameDeveloperField() { - return s_game_developer_field; -} - -jfieldID GetGameVersionField() { - return s_game_version_field; -} - -jfieldID GetGameIsHomebrewField() { - return s_game_is_homebrew_field; -} - -jclass GetStringClass() { - return s_string_class; -} - -jclass GetPairClass() { - return s_pair_class; -} - -jmethodID GetPairConstructor() { - return s_pair_constructor; -} - -jfieldID GetPairFirstField() { - return s_pair_first_field; -} - -jfieldID GetPairSecondField() { - return s_pair_second_field; -} - -jclass GetOverlayControlDataClass() { - return s_overlay_control_data_class; -} - -jmethodID GetOverlayControlDataConstructor() { - return s_overlay_control_data_constructor; -} - -jfieldID GetOverlayControlDataIdField() { - return s_overlay_control_data_id_field; -} - -jfieldID GetOverlayControlDataEnabledField() { - return s_overlay_control_data_enabled_field; -} - -jfieldID GetOverlayControlDataLandscapePositionField() { - return s_overlay_control_data_landscape_position_field; -} - -jfieldID GetOverlayControlDataPortraitPositionField() { - return s_overlay_control_data_portrait_position_field; -} - -jfieldID GetOverlayControlDataFoldablePositionField() { - return s_overlay_control_data_foldable_position_field; -} - -jclass GetPatchClass() { - return s_patch_class; -} - -jmethodID GetPatchConstructor() { - return s_patch_constructor; -} - -jfieldID GetPatchEnabledField() { - return s_patch_enabled_field; -} - -jfieldID GetPatchNameField() { - return s_patch_name_field; -} - -jfieldID GetPatchVersionField() { - return s_patch_version_field; -} - -jfieldID GetPatchTypeField() { - return s_patch_type_field; -} - -jfieldID GetPatchProgramIdField() { - return s_patch_program_id_field; -} - -jfieldID GetPatchTitleIdField() { - return s_patch_title_id_field; -} - -jclass GetDoubleClass() { - return s_double_class; -} - -jmethodID GetDoubleConstructor() { - return s_double_constructor; -} - -jfieldID GetDoubleValueField() { - return s_double_value_field; -} - -jclass GetIntegerClass() { - return s_integer_class; -} - -jmethodID GetIntegerConstructor() { - return s_integer_constructor; -} - -jfieldID GetIntegerValueField() { - return s_integer_value_field; -} - -jclass GetBooleanClass() { - return s_boolean_class; -} - -jmethodID GetBooleanConstructor() { - return s_boolean_constructor; -} - -jfieldID GetBooleanValueField() { - return s_boolean_value_field; -} - -jclass GetPlayerInputClass() { - return s_player_input_class; -} - -jmethodID GetPlayerInputConstructor() { - return s_player_input_constructor; -} - -jfieldID GetPlayerInputConnectedField() { - return s_player_input_connected_field; -} - -jfieldID GetPlayerInputButtonsField() { - return s_player_input_buttons_field; -} - -jfieldID GetPlayerInputAnalogsField() { - return s_player_input_analogs_field; -} - -jfieldID GetPlayerInputMotionsField() { - return s_player_input_motions_field; -} - -jfieldID GetPlayerInputVibrationEnabledField() { - return s_player_input_vibration_enabled_field; -} - -jfieldID GetPlayerInputVibrationStrengthField() { - return s_player_input_vibration_strength_field; -} - -jfieldID GetPlayerInputBodyColorLeftField() { - return s_player_input_body_color_left_field; -} - -jfieldID GetPlayerInputBodyColorRightField() { - return s_player_input_body_color_right_field; -} - -jfieldID GetPlayerInputButtonColorLeftField() { - return s_player_input_button_color_left_field; -} - -jfieldID GetPlayerInputButtonColorRightField() { - return s_player_input_button_color_right_field; -} - -jfieldID GetPlayerInputProfileNameField() { - return s_player_input_profile_name_field; -} - -jfieldID GetPlayerInputUseSystemVibratorField() { - return s_player_input_use_system_vibrator_field; -} - -jclass GetYuzuInputDeviceInterface() { - return s_yuzu_input_device_interface; -} - -jmethodID GetYuzuDeviceGetName() { - return s_yuzu_input_device_get_name; -} - -jmethodID GetYuzuDeviceGetGUID() { - return s_yuzu_input_device_get_guid; -} - -jmethodID GetYuzuDeviceGetPort() { - return s_yuzu_input_device_get_port; -} - -jmethodID GetYuzuDeviceGetSupportsVibration() { - return s_yuzu_input_device_get_supports_vibration; -} - -jmethodID GetYuzuDeviceVibrate() { - return s_yuzu_input_device_vibrate; -} - -jmethodID GetYuzuDeviceGetAxes() { - return s_yuzu_input_device_get_axes; -} - -jmethodID GetYuzuDeviceHasKeys() { - return s_yuzu_input_device_has_keys; -} - -jmethodID GetAddNetPlayMessage() { - return s_add_netplay_message; -} - -jmethodID ClearChat() { - return s_clear_chat; -} - -#ifdef __cplusplus -extern "C" { -#endif - -jint JNI_OnLoad(JavaVM* vm, void* reserved) { - s_java_vm = vm; - - JNIEnv* env; - if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION) != JNI_OK) - return JNI_ERR; - - // Initialize Java classes - const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary"); - s_native_library_class = reinterpret_cast(env->NewGlobalRef(native_library_class)); - s_disk_cache_progress_class = reinterpret_cast(env->NewGlobalRef( - env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress"))); - s_load_callback_stage_class = reinterpret_cast(env->NewGlobalRef(env->FindClass( - "org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage"))); - - const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir"); - s_game_dir_class = reinterpret_cast(env->NewGlobalRef(game_dir_class)); - s_game_dir_constructor = env->GetMethodID(game_dir_class, "", "(Ljava/lang/String;Z)V"); - env->DeleteLocalRef(game_dir_class); - - // Initialize methods - s_exit_emulation_activity = - env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V"); - s_disk_cache_load_progress = - env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V"); - s_on_emulation_started = - env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V"); - s_on_emulation_stopped = - env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V"); - s_on_program_changed = - env->GetStaticMethodID(s_native_library_class, "onProgramChanged", "(I)V"); - - const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game"); - s_game_class = reinterpret_cast(env->NewGlobalRef(game_class)); - s_game_constructor = env->GetMethodID(game_class, "", - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/" - "String;Ljava/lang/String;Ljava/lang/String;Z)V"); - s_game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;"); - s_game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;"); - s_game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;"); - s_game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;"); - s_game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;"); - s_game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z"); - env->DeleteLocalRef(game_class); - - const jclass string_class = env->FindClass("java/lang/String"); - s_string_class = reinterpret_cast(env->NewGlobalRef(string_class)); - env->DeleteLocalRef(string_class); - - const jclass pair_class = env->FindClass("kotlin/Pair"); - s_pair_class = reinterpret_cast(env->NewGlobalRef(pair_class)); - s_pair_constructor = - env->GetMethodID(pair_class, "", "(Ljava/lang/Object;Ljava/lang/Object;)V"); - s_pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;"); - s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;"); - env->DeleteLocalRef(pair_class); - - const jclass overlay_control_data_class = - env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData"); - s_overlay_control_data_class = - reinterpret_cast(env->NewGlobalRef(overlay_control_data_class)); - s_overlay_control_data_constructor = - env->GetMethodID(overlay_control_data_class, "", - "(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;)V"); - s_overlay_control_data_id_field = - env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;"); - s_overlay_control_data_enabled_field = - env->GetFieldID(overlay_control_data_class, "enabled", "Z"); - s_overlay_control_data_landscape_position_field = - env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;"); - s_overlay_control_data_portrait_position_field = - env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;"); - s_overlay_control_data_foldable_position_field = - env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;"); - env->DeleteLocalRef(overlay_control_data_class); - - const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch"); - s_patch_class = reinterpret_cast(env->NewGlobalRef(patch_class)); - s_patch_constructor = env->GetMethodID( - patch_class, "", - "(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V"); - s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z"); - s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;"); - s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;"); - s_patch_type_field = env->GetFieldID(patch_class, "type", "I"); - s_patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;"); - s_patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;"); - env->DeleteLocalRef(patch_class); - - const jclass double_class = env->FindClass("java/lang/Double"); - s_double_class = reinterpret_cast(env->NewGlobalRef(double_class)); - s_double_constructor = env->GetMethodID(double_class, "", "(D)V"); - s_double_value_field = env->GetFieldID(double_class, "value", "D"); - env->DeleteLocalRef(double_class); - - const jclass int_class = env->FindClass("java/lang/Integer"); - s_integer_class = reinterpret_cast(env->NewGlobalRef(int_class)); - s_integer_constructor = env->GetMethodID(int_class, "", "(I)V"); - s_integer_value_field = env->GetFieldID(int_class, "value", "I"); - env->DeleteLocalRef(int_class); - - const jclass boolean_class = env->FindClass("java/lang/Boolean"); - s_boolean_class = reinterpret_cast(env->NewGlobalRef(boolean_class)); - s_boolean_constructor = env->GetMethodID(boolean_class, "", "(Z)V"); - s_boolean_value_field = env->GetFieldID(boolean_class, "value", "Z"); - env->DeleteLocalRef(boolean_class); - - const jclass player_input_class = - env->FindClass("org/yuzu/yuzu_emu/features/input/model/PlayerInput"); - s_player_input_class = reinterpret_cast(env->NewGlobalRef(player_input_class)); - s_player_input_constructor = env->GetMethodID( - player_input_class, "", - "(Z[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;ZIJJJJLjava/lang/String;Z)V"); - s_player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z"); - s_player_input_buttons_field = - env->GetFieldID(player_input_class, "buttons", "[Ljava/lang/String;"); - s_player_input_analogs_field = - env->GetFieldID(player_input_class, "analogs", "[Ljava/lang/String;"); - s_player_input_motions_field = - env->GetFieldID(player_input_class, "motions", "[Ljava/lang/String;"); - s_player_input_vibration_enabled_field = - env->GetFieldID(player_input_class, "vibrationEnabled", "Z"); - s_player_input_vibration_strength_field = - env->GetFieldID(player_input_class, "vibrationStrength", "I"); - s_player_input_body_color_left_field = - env->GetFieldID(player_input_class, "bodyColorLeft", "J"); - s_player_input_body_color_right_field = - env->GetFieldID(player_input_class, "bodyColorRight", "J"); - s_player_input_button_color_left_field = - env->GetFieldID(player_input_class, "buttonColorLeft", "J"); - s_player_input_button_color_right_field = - env->GetFieldID(player_input_class, "buttonColorRight", "J"); - s_player_input_profile_name_field = - env->GetFieldID(player_input_class, "profileName", "Ljava/lang/String;"); - s_player_input_use_system_vibrator_field = - env->GetFieldID(player_input_class, "useSystemVibrator", "Z"); - env->DeleteLocalRef(player_input_class); - - const jclass yuzu_input_device_interface = - env->FindClass("org/yuzu/yuzu_emu/features/input/YuzuInputDevice"); - s_yuzu_input_device_interface = - reinterpret_cast(env->NewGlobalRef(yuzu_input_device_interface)); - s_yuzu_input_device_get_name = - env->GetMethodID(yuzu_input_device_interface, "getName", "()Ljava/lang/String;"); - s_yuzu_input_device_get_guid = - env->GetMethodID(yuzu_input_device_interface, "getGUID", "()Ljava/lang/String;"); - s_yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort", "()I"); - s_yuzu_input_device_get_supports_vibration = - env->GetMethodID(yuzu_input_device_interface, "getSupportsVibration", "()Z"); - s_yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate", "(F)V"); - s_yuzu_input_device_get_axes = - env->GetMethodID(yuzu_input_device_interface, "getAxes", "()[Ljava/lang/Integer;"); - s_yuzu_input_device_has_keys = - env->GetMethodID(yuzu_input_device_interface, "hasKeys", "([I)[Z"); - env->DeleteLocalRef(yuzu_input_device_interface); - s_add_netplay_message = env->GetStaticMethodID(s_native_library_class, "addNetPlayMessage", - "(ILjava/lang/String;)V"); - s_clear_chat = env->GetStaticMethodID(s_native_library_class, "clearChat", "()V"); - - - // Initialize Android Storage - Common::FS::Android::RegisterCallbacks(env, s_native_library_class); - - // Initialize applets - Common::Android::SoftwareKeyboard::InitJNI(env); - - return JNI_VERSION; -} - -void JNI_OnUnload(JavaVM* vm, void* reserved) { - JNIEnv* env; - if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION) != JNI_OK) { - return; + JNIEnv *GetEnvForThread() { + thread_local static struct OwnedEnv { + OwnedEnv() { + status = s_java_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); + if (status == JNI_EDETACHED) + s_java_vm->AttachCurrentThread(&env, nullptr); + } + + ~OwnedEnv() { + if (status == JNI_EDETACHED) + s_java_vm->DetachCurrentThread(); + } + + int status; + JNIEnv *env = nullptr; + } owned; + return owned.env; } - // UnInitialize Android Storage - Common::FS::Android::UnRegisterCallbacks(); - env->DeleteGlobalRef(s_native_library_class); - env->DeleteGlobalRef(s_disk_cache_progress_class); - env->DeleteGlobalRef(s_load_callback_stage_class); - env->DeleteGlobalRef(s_game_dir_class); - env->DeleteGlobalRef(s_game_class); - env->DeleteGlobalRef(s_string_class); - env->DeleteGlobalRef(s_pair_class); - env->DeleteGlobalRef(s_overlay_control_data_class); - env->DeleteGlobalRef(s_patch_class); - env->DeleteGlobalRef(s_double_class); - env->DeleteGlobalRef(s_integer_class); - env->DeleteGlobalRef(s_boolean_class); - env->DeleteGlobalRef(s_player_input_class); - env->DeleteGlobalRef(s_yuzu_input_device_interface); + jclass GetNativeLibraryClass() { + return s_native_library_class; + } - // UnInitialize applets - SoftwareKeyboard::CleanupJNI(env); + jclass GetDiskCacheProgressClass() { + return s_disk_cache_progress_class; + } - AndroidMultiplayer::NetworkShutdown(); -} + jclass GetDiskCacheLoadCallbackStageClass() { + return s_load_callback_stage_class; + } + + jclass GetGameDirClass() { + return s_game_dir_class; + } + + jmethodID GetGameDirConstructor() { + return s_game_dir_constructor; + } + + jmethodID GetExitEmulationActivity() { + return s_exit_emulation_activity; + } + + jmethodID GetDiskCacheLoadProgress() { + return s_disk_cache_load_progress; + } + + jmethodID GetCopyToStorage() { + return s_copy_to_storage; + } + + jmethodID GetFileExists() { + return s_file_exists; + } + + jmethodID GetFileExtension() { + return s_file_extension; + } + + jmethodID GetOnEmulationStarted() { + return s_on_emulation_started; + } + + jmethodID GetOnEmulationStopped() { + return s_on_emulation_stopped; + } + + jmethodID GetOnProgramChanged() { + return s_on_program_changed; + } + + jclass GetGameClass() { + return s_game_class; + } + + jmethodID GetGameConstructor() { + return s_game_constructor; + } + + jfieldID GetGameTitleField() { + return s_game_title_field; + } + + jfieldID GetGamePathField() { + return s_game_path_field; + } + + jfieldID GetGameProgramIdField() { + return s_game_program_id_field; + } + + jfieldID GetGameDeveloperField() { + return s_game_developer_field; + } + + jfieldID GetGameVersionField() { + return s_game_version_field; + } + + jfieldID GetGameIsHomebrewField() { + return s_game_is_homebrew_field; + } + + jclass GetStringClass() { + return s_string_class; + } + + jclass GetPairClass() { + return s_pair_class; + } + + jmethodID GetPairConstructor() { + return s_pair_constructor; + } + + jfieldID GetPairFirstField() { + return s_pair_first_field; + } + + jfieldID GetPairSecondField() { + return s_pair_second_field; + } + + jclass GetOverlayControlDataClass() { + return s_overlay_control_data_class; + } + + jmethodID GetOverlayControlDataConstructor() { + return s_overlay_control_data_constructor; + } + + jfieldID GetOverlayControlDataIdField() { + return s_overlay_control_data_id_field; + } + + jfieldID GetOverlayControlDataEnabledField() { + return s_overlay_control_data_enabled_field; + } + + jfieldID GetOverlayControlDataLandscapePositionField() { + return s_overlay_control_data_landscape_position_field; + } + + jfieldID GetOverlayControlDataPortraitPositionField() { + return s_overlay_control_data_portrait_position_field; + } + + jfieldID GetOverlayControlDataFoldablePositionField() { + return s_overlay_control_data_foldable_position_field; + } + + jclass GetPatchClass() { + return s_patch_class; + } + + jmethodID GetPatchConstructor() { + return s_patch_constructor; + } + + jfieldID GetPatchEnabledField() { + return s_patch_enabled_field; + } + + jfieldID GetPatchNameField() { + return s_patch_name_field; + } + + jfieldID GetPatchVersionField() { + return s_patch_version_field; + } + + jfieldID GetPatchTypeField() { + return s_patch_type_field; + } + + jfieldID GetPatchProgramIdField() { + return s_patch_program_id_field; + } + + jfieldID GetPatchTitleIdField() { + return s_patch_title_id_field; + } + + jclass GetDoubleClass() { + return s_double_class; + } + + jmethodID GetDoubleConstructor() { + return s_double_constructor; + } + + jfieldID GetDoubleValueField() { + return s_double_value_field; + } + + jclass GetIntegerClass() { + return s_integer_class; + } + + jmethodID GetIntegerConstructor() { + return s_integer_constructor; + } + + jfieldID GetIntegerValueField() { + return s_integer_value_field; + } + + jclass GetBooleanClass() { + return s_boolean_class; + } + + jmethodID GetBooleanConstructor() { + return s_boolean_constructor; + } + + jfieldID GetBooleanValueField() { + return s_boolean_value_field; + } + + jclass GetPlayerInputClass() { + return s_player_input_class; + } + + jmethodID GetPlayerInputConstructor() { + return s_player_input_constructor; + } + + jfieldID GetPlayerInputConnectedField() { + return s_player_input_connected_field; + } + + jfieldID GetPlayerInputButtonsField() { + return s_player_input_buttons_field; + } + + jfieldID GetPlayerInputAnalogsField() { + return s_player_input_analogs_field; + } + + jfieldID GetPlayerInputMotionsField() { + return s_player_input_motions_field; + } + + jfieldID GetPlayerInputVibrationEnabledField() { + return s_player_input_vibration_enabled_field; + } + + jfieldID GetPlayerInputVibrationStrengthField() { + return s_player_input_vibration_strength_field; + } + + jfieldID GetPlayerInputBodyColorLeftField() { + return s_player_input_body_color_left_field; + } + + jfieldID GetPlayerInputBodyColorRightField() { + return s_player_input_body_color_right_field; + } + + jfieldID GetPlayerInputButtonColorLeftField() { + return s_player_input_button_color_left_field; + } + + jfieldID GetPlayerInputButtonColorRightField() { + return s_player_input_button_color_right_field; + } + + jfieldID GetPlayerInputProfileNameField() { + return s_player_input_profile_name_field; + } + + jfieldID GetPlayerInputUseSystemVibratorField() { + return s_player_input_use_system_vibrator_field; + } + + jclass GetYuzuInputDeviceInterface() { + return s_yuzu_input_device_interface; + } + + jmethodID GetYuzuDeviceGetName() { + return s_yuzu_input_device_get_name; + } + + jmethodID GetYuzuDeviceGetGUID() { + return s_yuzu_input_device_get_guid; + } + + jmethodID GetYuzuDeviceGetPort() { + return s_yuzu_input_device_get_port; + } + + jmethodID GetYuzuDeviceGetSupportsVibration() { + return s_yuzu_input_device_get_supports_vibration; + } + + jmethodID GetYuzuDeviceVibrate() { + return s_yuzu_input_device_vibrate; + } + + jmethodID GetYuzuDeviceGetAxes() { + return s_yuzu_input_device_get_axes; + } + + jmethodID GetYuzuDeviceHasKeys() { + return s_yuzu_input_device_has_keys; + } + + jmethodID GetAddNetPlayMessage() { + return s_add_netplay_message; + } + + jmethodID ClearChat() { + return s_clear_chat; + } #ifdef __cplusplus -} + extern "C" { +#endif + + jint JNI_OnLoad(JavaVM *vm, void *reserved) { + s_java_vm = vm; + + JNIEnv *env; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION) != JNI_OK) + return JNI_ERR; + + // Initialize Java classes + const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary"); + s_native_library_class = reinterpret_cast(env->NewGlobalRef(native_library_class)); + s_disk_cache_progress_class = reinterpret_cast(env->NewGlobalRef( + env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress"))); + s_load_callback_stage_class = reinterpret_cast(env->NewGlobalRef(env->FindClass( + "org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage"))); + + const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir"); + s_game_dir_class = reinterpret_cast(env->NewGlobalRef(game_dir_class)); + s_game_dir_constructor = env->GetMethodID(game_dir_class, "", + "(Ljava/lang/String;Z)V"); + env->DeleteLocalRef(game_dir_class); + + // Initialize methods + s_exit_emulation_activity = + env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V"); + s_disk_cache_load_progress = + env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V"); + s_copy_to_storage = env->GetStaticMethodID(s_native_library_class, "copyFileToStorage", + "(Ljava/lang/String;Ljava/lang/String;)Z"); + s_file_exists = env->GetStaticMethodID(s_native_library_class, "exists", + "(Ljava/lang/String;)Z"); + s_file_extension = env->GetStaticMethodID(s_native_library_class, "getFileExtension", + "(Ljava/lang/String;)Ljava/lang/String;"); + s_on_emulation_started = + env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V"); + s_on_emulation_stopped = + env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V"); + s_on_program_changed = + env->GetStaticMethodID(s_native_library_class, "onProgramChanged", "(I)V"); + + const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game"); + s_game_class = reinterpret_cast(env->NewGlobalRef(game_class)); + s_game_constructor = env->GetMethodID(game_class, "", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/" + "String;Ljava/lang/String;Ljava/lang/String;Z)V"); + s_game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;"); + s_game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;"); + s_game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;"); + s_game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;"); + s_game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;"); + s_game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z"); + env->DeleteLocalRef(game_class); + + const jclass string_class = env->FindClass("java/lang/String"); + s_string_class = reinterpret_cast(env->NewGlobalRef(string_class)); + env->DeleteLocalRef(string_class); + + const jclass pair_class = env->FindClass("kotlin/Pair"); + s_pair_class = reinterpret_cast(env->NewGlobalRef(pair_class)); + s_pair_constructor = + env->GetMethodID(pair_class, "", "(Ljava/lang/Object;Ljava/lang/Object;)V"); + s_pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;"); + s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;"); + env->DeleteLocalRef(pair_class); + + const jclass overlay_control_data_class = + env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData"); + s_overlay_control_data_class = + reinterpret_cast(env->NewGlobalRef(overlay_control_data_class)); + s_overlay_control_data_constructor = + env->GetMethodID(overlay_control_data_class, "", + "(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;)V"); + s_overlay_control_data_id_field = + env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;"); + s_overlay_control_data_enabled_field = + env->GetFieldID(overlay_control_data_class, "enabled", "Z"); + s_overlay_control_data_landscape_position_field = + env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;"); + s_overlay_control_data_portrait_position_field = + env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;"); + s_overlay_control_data_foldable_position_field = + env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;"); + env->DeleteLocalRef(overlay_control_data_class); + + const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch"); + s_patch_class = reinterpret_cast(env->NewGlobalRef(patch_class)); + s_patch_constructor = env->GetMethodID( + patch_class, "", + "(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V"); + s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z"); + s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;"); + s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;"); + s_patch_type_field = env->GetFieldID(patch_class, "type", "I"); + s_patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;"); + s_patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;"); + env->DeleteLocalRef(patch_class); + + const jclass double_class = env->FindClass("java/lang/Double"); + s_double_class = reinterpret_cast(env->NewGlobalRef(double_class)); + s_double_constructor = env->GetMethodID(double_class, "", "(D)V"); + s_double_value_field = env->GetFieldID(double_class, "value", "D"); + env->DeleteLocalRef(double_class); + + const jclass int_class = env->FindClass("java/lang/Integer"); + s_integer_class = reinterpret_cast(env->NewGlobalRef(int_class)); + s_integer_constructor = env->GetMethodID(int_class, "", "(I)V"); + s_integer_value_field = env->GetFieldID(int_class, "value", "I"); + env->DeleteLocalRef(int_class); + + const jclass boolean_class = env->FindClass("java/lang/Boolean"); + s_boolean_class = reinterpret_cast(env->NewGlobalRef(boolean_class)); + s_boolean_constructor = env->GetMethodID(boolean_class, "", "(Z)V"); + s_boolean_value_field = env->GetFieldID(boolean_class, "value", "Z"); + env->DeleteLocalRef(boolean_class); + + const jclass player_input_class = + env->FindClass("org/yuzu/yuzu_emu/features/input/model/PlayerInput"); + s_player_input_class = reinterpret_cast(env->NewGlobalRef(player_input_class)); + s_player_input_constructor = env->GetMethodID( + player_input_class, "", + "(Z[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;ZIJJJJLjava/lang/String;Z)V"); + s_player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z"); + s_player_input_buttons_field = + env->GetFieldID(player_input_class, "buttons", "[Ljava/lang/String;"); + s_player_input_analogs_field = + env->GetFieldID(player_input_class, "analogs", "[Ljava/lang/String;"); + s_player_input_motions_field = + env->GetFieldID(player_input_class, "motions", "[Ljava/lang/String;"); + s_player_input_vibration_enabled_field = + env->GetFieldID(player_input_class, "vibrationEnabled", "Z"); + s_player_input_vibration_strength_field = + env->GetFieldID(player_input_class, "vibrationStrength", "I"); + s_player_input_body_color_left_field = + env->GetFieldID(player_input_class, "bodyColorLeft", "J"); + s_player_input_body_color_right_field = + env->GetFieldID(player_input_class, "bodyColorRight", "J"); + s_player_input_button_color_left_field = + env->GetFieldID(player_input_class, "buttonColorLeft", "J"); + s_player_input_button_color_right_field = + env->GetFieldID(player_input_class, "buttonColorRight", "J"); + s_player_input_profile_name_field = + env->GetFieldID(player_input_class, "profileName", "Ljava/lang/String;"); + s_player_input_use_system_vibrator_field = + env->GetFieldID(player_input_class, "useSystemVibrator", "Z"); + env->DeleteLocalRef(player_input_class); + + const jclass yuzu_input_device_interface = + env->FindClass("org/yuzu/yuzu_emu/features/input/YuzuInputDevice"); + s_yuzu_input_device_interface = + reinterpret_cast(env->NewGlobalRef(yuzu_input_device_interface)); + s_yuzu_input_device_get_name = + env->GetMethodID(yuzu_input_device_interface, "getName", "()Ljava/lang/String;"); + s_yuzu_input_device_get_guid = + env->GetMethodID(yuzu_input_device_interface, "getGUID", "()Ljava/lang/String;"); + s_yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort", + "()I"); + s_yuzu_input_device_get_supports_vibration = + env->GetMethodID(yuzu_input_device_interface, "getSupportsVibration", "()Z"); + s_yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate", + "(F)V"); + s_yuzu_input_device_get_axes = + env->GetMethodID(yuzu_input_device_interface, "getAxes", "()[Ljava/lang/Integer;"); + s_yuzu_input_device_has_keys = + env->GetMethodID(yuzu_input_device_interface, "hasKeys", "([I)[Z"); + env->DeleteLocalRef(yuzu_input_device_interface); + s_add_netplay_message = env->GetStaticMethodID(s_native_library_class, "addNetPlayMessage", + "(ILjava/lang/String;)V"); + s_clear_chat = env->GetStaticMethodID(s_native_library_class, "clearChat", "()V"); + + + // Initialize Android Storage + Common::FS::Android::RegisterCallbacks(env, s_native_library_class); + + // Initialize applets + Common::Android::SoftwareKeyboard::InitJNI(env); + + return JNI_VERSION; + } + + void JNI_OnUnload(JavaVM *vm, void *reserved) { + JNIEnv *env; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION) != JNI_OK) { + return; + } + + // UnInitialize Android Storage + Common::FS::Android::UnRegisterCallbacks(); + env->DeleteGlobalRef(s_native_library_class); + env->DeleteGlobalRef(s_disk_cache_progress_class); + env->DeleteGlobalRef(s_load_callback_stage_class); + env->DeleteGlobalRef(s_game_dir_class); + env->DeleteGlobalRef(s_game_class); + env->DeleteGlobalRef(s_string_class); + env->DeleteGlobalRef(s_pair_class); + env->DeleteGlobalRef(s_overlay_control_data_class); + env->DeleteGlobalRef(s_patch_class); + env->DeleteGlobalRef(s_double_class); + env->DeleteGlobalRef(s_integer_class); + env->DeleteGlobalRef(s_boolean_class); + env->DeleteGlobalRef(s_player_input_class); + env->DeleteGlobalRef(s_yuzu_input_device_interface); + + // UnInitialize applets + SoftwareKeyboard::CleanupJNI(env); + + AndroidMultiplayer::NetworkShutdown(); + } + +#ifdef __cplusplus + } #endif } // namespace Common::Android diff --git a/src/common/android/id_cache.h b/src/common/android/id_cache.h index cbfbf36be8..c56ffcf5c6 100644 --- a/src/common/android/id_cache.h +++ b/src/common/android/id_cache.h @@ -39,6 +39,9 @@ jclass GetDiskCacheLoadCallbackStageClass(); jclass GetGameDirClass(); jmethodID GetGameDirConstructor(); jmethodID GetDiskCacheLoadProgress(); +jmethodID GetCopyToStorage(); +jmethodID GetFileExists(); +jmethodID GetFileExtension(); jmethodID GetExitEmulationActivity(); jmethodID GetOnEmulationStarted(); diff --git a/src/frontend_common/CMakeLists.txt b/src/frontend_common/CMakeLists.txt index 94d8cc4c3b..70e142bb0c 100644 --- a/src/frontend_common/CMakeLists.txt +++ b/src/frontend_common/CMakeLists.txt @@ -5,6 +5,8 @@ add_library(frontend_common STATIC config.cpp config.h content_manager.h + firmware_manager.h + firmware_manager.cpp ) create_target_directory_groups(frontend_common) diff --git a/src/frontend_common/firmware_manager.cpp b/src/frontend_common/firmware_manager.cpp new file mode 100644 index 0000000000..f1022579be --- /dev/null +++ b/src/frontend_common/firmware_manager.cpp @@ -0,0 +1,139 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "firmware_manager.h" +#include +#include + +#include "common/fs/fs.h" +#include "common/fs/path_util.h" + +#include "common/logging/backend.h" + +#include "core/crypto/key_manager.h" +#include "frontend_common/content_manager.h" + +#ifdef ANDROID +#include +#include +#include +#endif + +FirmwareManager::KeyInstallResult +FirmwareManager::InstallKeys(std::string location, std::string extension) { + LOG_INFO(Frontend, "Installing key files from {}", location); + + const auto keys_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::KeysDir); + +#ifdef ANDROID + JNIEnv *env = Common::Android::GetEnvForThread(); + + jstring jsrc = Common::Android::ToJString(env, location); + + jclass native = Common::Android::GetNativeLibraryClass(); + jmethodID getExtension = Common::Android::GetFileExtension(); + + jstring jext = static_cast(env->CallStaticObjectMethod( + native, + getExtension, + jsrc + )); + + std::string ext = Common::Android::GetJString(env, jext); + + if (ext != extension) { + return ErrorWrongFilename; + } + + jmethodID copyToStorage = Common::Android::GetCopyToStorage(); + jstring jdest = Common::Android::ToJString(env, keys_dir.string()); + + jboolean copyResult = env->CallStaticBooleanMethod( + native, + copyToStorage, + jsrc, + jdest + ); + + if (!copyResult) { + return ErrorFailedCopy; + } +#else + if (!location.ends_with(extension)) { + return ErrorWrongFilename; + } + + bool prod_keys_found = false; + + const std::filesystem::path prod_key_path = location; + const std::filesystem::path key_source_path = prod_key_path.parent_path(); + + if (!Common::FS::IsDir(key_source_path)) { + return InvalidDir; + } + + std::vector source_key_files; + + if (Common::FS::Exists(prod_key_path)) { + prod_keys_found = true; + source_key_files.emplace_back(prod_key_path); + } + + if (Common::FS::Exists(key_source_path / "title.keys")) { + source_key_files.emplace_back(key_source_path / "title.keys"); + } + + if (Common::FS::Exists(key_source_path / "key_retail.bin")) { + source_key_files.emplace_back(key_source_path / "key_retail.bin"); + } + + if (source_key_files.empty() || !prod_keys_found) { + return ErrorWrongFilename; + } + + for (const auto &key_file : source_key_files) { + std::filesystem::path destination_key_file = keys_dir / key_file.filename(); + if (!std::filesystem::copy_file(key_file, + destination_key_file, + std::filesystem::copy_options::overwrite_existing)) { + LOG_ERROR(Frontend, + "Failed to copy file {} to {}", + key_file.string(), + destination_key_file.string()); + return ErrorFailedCopy; + } + } +#endif + + // Reinitialize the key manager + Core::Crypto::KeyManager::Instance().ReloadKeys(); + + if (ContentManager::AreKeysPresent()) { + return Success; + } + + // Let the frontend handle everything else + return ErrorFailedInit; +} + +FirmwareManager::FirmwareCheckResult FirmwareManager::VerifyFirmware(Core::System &system) { + if (!CheckFirmwarePresence(system)) { + return ErrorFirmwareMissing; + } else { + const auto pair = GetFirmwareVersion(system); + const auto firmware_data = pair.first; + const auto result = pair.second; + + if (result.IsError()) { + LOG_INFO(Frontend, "Unable to read firmware"); + return ErrorFirmwareCorrupted; + } + + // TODO: update this whenever newer firmware is properly supported + if (firmware_data.major > 19) { + return ErrorFirmwareTooNew; + } + } + + return FirmwareGood; +} diff --git a/src/frontend_common/firmware_manager.h b/src/frontend_common/firmware_manager.h new file mode 100644 index 0000000000..20f3b41478 --- /dev/null +++ b/src/frontend_common/firmware_manager.h @@ -0,0 +1,148 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef FIRMWARE_MANAGER_H +#define FIRMWARE_MANAGER_H + +#include "common/common_types.h" +#include "core/core.h" +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/registered_cache.h" +#include "core/hle/service/filesystem/filesystem.h" +#include +#include +#include +#include + +#include "core/hle/service/set/settings_types.h" +#include "core/hle/service/set/system_settings_server.h" +#include "core/hle/result.h" + +namespace FirmwareManager { + +static constexpr std::array KEY_INSTALL_RESULT_STRINGS = { + "Decryption Keys were successfully installed", + "Unable to read key directory, aborting", + "One or more keys failed to copy.", + "Verify your keys file has a .keys extension and try again.", + "Decryption Keys failed to initialize. Check that your dumping tools are up to date and " + "re-dump keys.", +}; + +static constexpr std::array FIRMWARE_REQUIRED_GAMES = { + 0x0100152000022000ULL, // MK8DX +}; + +enum KeyInstallResult { + Success, + InvalidDir, + ErrorFailedCopy, + ErrorWrongFilename, + ErrorFailedInit, +}; + +/** + * @brief Installs any arbitrary set of keys for the emulator. + * @param location Where the keys are located. + * @param expected_extension What extension the file should have. + * @return A result code for the operation. + */ +KeyInstallResult InstallKeys(std::string location, std::string expected_extension); + +/** + * \brief Get a string representation of a result from InstallKeys. + * \param result The result code. + * \return A string representation of the passed result code. + */ +inline constexpr const char *GetKeyInstallResultString(KeyInstallResult result) +{ + return KEY_INSTALL_RESULT_STRINGS.at(static_cast(result)); +} + +/** + * \brief Check if the specified program requires firmware to run properly. + * It is the responsibility of the frontend to properly expose this to the user. + * \param program_id The program ID to check. + * \return Whether or not the program requires firmware to run properly. + */ +inline constexpr bool GameRequiresFirmware(u64 program_id) +{ + return std::find(FIRMWARE_REQUIRED_GAMES.begin(), FIRMWARE_REQUIRED_GAMES.end(), program_id) + != FIRMWARE_REQUIRED_GAMES.end(); +} + + +enum FirmwareCheckResult { + FirmwareGood, + ErrorFirmwareMissing, + ErrorFirmwareCorrupted, + ErrorFirmwareTooNew, +}; + +static constexpr std::array FIRMWARE_CHECK_STRINGS = { + "", + "Firmware missing. Firmware is required to run certain games and use the Home Menu. " + "Eden only works with firmware 19.0.1 and earlier.", + "Firmware reported as present, but was unable to be read. Check for decryption keys and " + "redump firmware if necessary.", + "Firmware is too new or could not be read. Eden only works with firmware 19.0.1 and earlier.", +}; + +/** + * \brief Checks for installed firmware within the system. + * \param system The system to check for firmware. + * \return Whether or not the system has installed firmware. + */ +inline bool CheckFirmwarePresence(Core::System &system) +{ + constexpr u64 MiiEditId = static_cast(Service::AM::AppletProgramId::MiiEdit); + + auto bis_system = system.GetFileSystemController().GetSystemNANDContents(); + if (!bis_system) { + return false; + } + + auto mii_applet_nca = bis_system->GetEntry(MiiEditId, FileSys::ContentRecordType::Program); + + if (!mii_applet_nca) { + return false; + } + + return true; +} + +/** + * \brief Verifies if firmware is properly installed and is in the correct version range. + * \param system The system to check firmware on. + * \return A result code defining the status of the system's firmware. + */ +FirmwareCheckResult VerifyFirmware(Core::System &system); + +/** + * \brief Get a string representation of a result from CheckFirmwareVersion. + * \param result The result code. + * \return A string representation of the passed result code. + */ +inline constexpr const char *GetFirmwareCheckString(FirmwareCheckResult result) +{ + return FIRMWARE_CHECK_STRINGS.at(static_cast(result)); +} + +/** + * @brief Get the currently installed firmware version. + * @param system The system to check firmware on. + * @return A pair of the firmware version format and result code. + */ +inline std::pair GetFirmwareVersion(Core::System &system) +{ + Service::Set::FirmwareVersionFormat firmware_data{}; + const auto result + = Service::Set::GetFirmwareVersionImpl(firmware_data, + system, + Service::Set::GetFirmwareVersionType::Version2); + + return {firmware_data, result}; +} +} + +#endif // FIRMWARE_MANAGER_H diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 4ff59291e5..081f08cf52 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -10,6 +10,7 @@ #include "core/hle/service/am/applet_manager.h" #include "core/loader/nca.h" #include "core/tools/renderdoc.h" +#include "frontend_common/firmware_manager.h" #ifdef __APPLE__ #include // for chdir @@ -211,7 +212,7 @@ enum class CalloutFlag : uint32_t { * Some games perform worse or straight-up don't work with updates, * so this tracks which games are bad in this regard. */ -static const QList bad_update_games{ +constexpr std::array bad_update_games{ 0x0100F2C0115B6000 // Tears of the Kingdom }; @@ -1889,46 +1890,61 @@ bool GMainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletPa QSettings settings; QStringList currentIgnored = settings.value("ignoredBadUpdates", {}).toStringList(); - for (const u64 id : bad_update_games) { - const bool ignored = currentIgnored.contains(QString::number(id)); + if (std::find(bad_update_games.begin(), bad_update_games.end(), params.program_id) != bad_update_games.end() + && !currentIgnored.contains(QString::number(params.program_id))) { + QMessageBox *msg = new QMessageBox(this); + msg->setWindowTitle(tr("Game Updates Warning")); + msg->setIcon(QMessageBox::Warning); + msg->setText(tr("The game you are trying to launch is known to have performance or booting " + "issues when updates are applied. Please try increasing the memory layout to " + "6GB or 8GB if any issues occur.

Press \"OK\" to continue launching, or " + "\"Cancel\" to cancel the launch.")); - if (params.program_id == id && !ignored) { - QMessageBox *msg = new QMessageBox(this); - msg->setWindowTitle(tr("Game Updates Warning")); - msg->setIcon(QMessageBox::Warning); - msg->setText(tr("The game you are trying to launch is known to have performance or booting " - "issues when updates are applied. Please try increasing the memory layout to " - "6GB or 8GB if any issues occur.

Press \"OK\" to continue launching, or " - "\"Cancel\" to cancel the launch.")); + msg->setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); - // TODO: TMP: Recommends more memory for TotK. + QCheckBox *dontShowAgain = new QCheckBox(msg); + dontShowAgain->setText(tr("Don't show again for this game")); + msg->setCheckBox(dontShowAgain); - msg->setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + int result = msg->exec(); - QCheckBox *dontShowAgain = new QCheckBox(msg); - dontShowAgain->setText(tr("Don't show again for this game")); - msg->setCheckBox(dontShowAgain); + // wtf + QMessageBox::ButtonRole role = msg->buttonRole(msg->button((QMessageBox::StandardButton) result)); - int result = msg->exec(); - - // wtf - QMessageBox::ButtonRole role = msg->buttonRole(msg->button((QMessageBox::StandardButton) result)); - - switch (role) { - - case QMessageBox::RejectRole: - return false; - - case QMessageBox::AcceptRole: - default: - if (dontShowAgain->isChecked()) { - currentIgnored << QString::number(params.program_id); - - settings.setValue("ignoredBadUpdates", currentIgnored); - settings.sync(); - } - break; + switch (role) { + case QMessageBox::RejectRole: + return false; + case QMessageBox::AcceptRole: + default: + if (dontShowAgain->isChecked()) { + currentIgnored << QString::number(params.program_id); + settings.setValue("ignoredBadUpdates", currentIgnored); + settings.sync(); } + break; + } + } + + if (FirmwareManager::GameRequiresFirmware(params.program_id) && !FirmwareManager::CheckFirmwarePresence(*system)) { + QMessageBox *msg = new QMessageBox(this); + msg->setWindowTitle(tr("Game Requires Firmware")); + msg->setIcon(QMessageBox::Warning); + msg->setText(tr("The game you are trying to launch requires firmware to boot or to get past the " + "opening menu. Please " + "dump and install firmware, or press \"OK\" to launch anyways.")); + + msg->setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + + int exec_result = msg->exec(); + + QMessageBox::ButtonRole role = msg->buttonRole(msg->button((QMessageBox::StandardButton) exec_result)); + + switch (role) { + case QMessageBox::RejectRole: + return false; + case QMessageBox::AcceptRole: + default: + break; } } @@ -1945,7 +1961,7 @@ bool GMainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletPa UISettings::values.callout_flags = UISettings::values.callout_flags.GetValue() | static_cast(CalloutFlag::DRDDeprecation); QMessageBox::warning( - this, tr("Warning Outdated Game Format"), + this, tr("Warning: Outdated Game Format"), tr("You are using the deconstructed ROM directory format for this game, which is an " "outdated format that has been superseded by others such as NCA, NAX, XCI, or " "NSP. Deconstructed ROM directories lack icons, metadata, and update " @@ -1968,7 +1984,7 @@ bool GMainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletPa "This is usually caused by outdated GPU drivers, including integrated ones. " "Please see the log for more details. " "For more information on accessing the log, please see the following page: " - "" + "" "How to Upload the Log File. ")); break; default: @@ -4322,8 +4338,8 @@ void GMainWindow::OnInstallFirmware() { progress.close(); QMessageBox::warning( this, tr("Firmware install failed"), - tr("Firmware installation cancelled, firmware may be in bad state, " - "restart eden or re-install firmware.")); + tr("Firmware installation cancelled, firmware may be in a bad state or corrupted. " + "Restart Eden or re-install firmware.")); return; } } @@ -4368,72 +4384,27 @@ void GMainWindow::OnInstallDecryptionKeys() { } const QString key_source_location = QFileDialog::getOpenFileName( - this, tr("Select Dumped Keys Location"), {}, QStringLiteral("prod.keys (prod.keys)"), {}, + this, tr("Select Dumped Keys Location"), {}, QStringLiteral("Decryption Keys (*.keys)"), {}, QFileDialog::ReadOnly); if (key_source_location.isEmpty()) { return; } - // Verify that it contains prod.keys, title.keys and optionally, key_retail.bin - LOG_INFO(Frontend, "Installing key files from {}", key_source_location.toStdString()); + FirmwareManager::KeyInstallResult result = FirmwareManager::InstallKeys(key_source_location.toStdString(), "keys"); - const std::filesystem::path prod_key_path = key_source_location.toStdString(); - const std::filesystem::path key_source_path = prod_key_path.parent_path(); - if (!Common::FS::IsDir(key_source_path)) { - return; - } - - bool prod_keys_found = false; - std::vector source_key_files; - - if (Common::FS::Exists(prod_key_path)) { - prod_keys_found = true; - source_key_files.emplace_back(prod_key_path); - } - - if (Common::FS::Exists(key_source_path / "title.keys")) { - source_key_files.emplace_back(key_source_path / "title.keys"); - } - - if (Common::FS::Exists(key_source_path / "key_retail.bin")) { - source_key_files.emplace_back(key_source_path / "key_retail.bin"); - } - - // There should be at least prod.keys. - if (source_key_files.empty() || !prod_keys_found) { - QMessageBox::warning(this, tr("Decryption Keys install failed"), - tr("prod.keys is a required decryption key file.")); - return; - } - - const auto yuzu_keys_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::KeysDir); - for (auto key_file : source_key_files) { - std::filesystem::path destination_key_file = yuzu_keys_dir / key_file.filename(); - if (!std::filesystem::copy_file(key_file, destination_key_file, - std::filesystem::copy_options::overwrite_existing)) { - LOG_ERROR(Frontend, "Failed to copy file {} to {}", key_file.string(), - destination_key_file.string()); - QMessageBox::critical(this, tr("Decryption Keys install failed"), - tr("One or more keys failed to copy.")); - return; - } - } - - // Reinitialize the key manager, re-read the vfs (for update/dlc files), - // and re-populate the game list in the UI if the user has already added - // game folders. - Core::Crypto::KeyManager::Instance().ReloadKeys(); system->GetFileSystemController().CreateFactories(*vfs); game_list->PopulateAsync(UISettings::values.game_dirs); - if (ContentManager::AreKeysPresent()) { + switch (result) { + case FirmwareManager::KeyInstallResult::Success: QMessageBox::information(this, tr("Decryption Keys install succeeded"), tr("Decryption Keys were successfully installed")); - } else { + break; + default: QMessageBox::critical( this, tr("Decryption Keys install failed"), - tr("Decryption Keys failed to initialize. Check that your dumping tools are " - "up to date and re-dump keys.")); + tr(FirmwareManager::GetKeyInstallResultString(result))); + break; } OnCheckFirmwareDecryption(); @@ -5103,52 +5074,27 @@ void GMainWindow::OnCheckFirmwareDecryption() { void GMainWindow::OnCheckFirmware() { - if (!CheckFirmwarePresence()) { + auto result = FirmwareManager::VerifyFirmware(*system.get()); + + switch (result) { + case FirmwareManager::FirmwareGood: + break; + default: QMessageBox::warning( - this, tr("Firmware Missing"), - tr("Firmware missing. Firmware is required to run certain games and use the Home Menu.\n" - "Eden only works with firmware 19.0.1 and earlier.")); - } else { - Service::Set::FirmwareVersionFormat firmware_data{}; - const auto result = Service::Set::GetFirmwareVersionImpl( - firmware_data, *system, Service::Set::GetFirmwareVersionType::Version2); - - if (result.IsError()) { - LOG_INFO(Frontend, "Unable to read firmware"); - QMessageBox::warning( - this, tr("Firmware Corrupted"), - tr("Firmware reported as present, but was unable to be read. Check for decryption keys and redump firmware if necessary.")); - return; - } - - if (firmware_data.major > 19) { - QMessageBox::warning( - this, tr("Firmware Too New"), - tr("Firmware is too new. Eden only works with firmware 19.0.1 and earlier.")); - } + this, tr("Firmware Read Error"), + tr(FirmwareManager::GetFirmwareCheckString(result))); + break; } } bool GMainWindow::CheckFirmwarePresence() { - constexpr u64 MiiEditId = static_cast(Service::AM::AppletProgramId::MiiEdit); - - auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); - if (!bis_system) { - return false; - } - - auto mii_applet_nca = bis_system->GetEntry(MiiEditId, FileSys::ContentRecordType::Program); - if (!mii_applet_nca) { - return false; - } - - return true; + return FirmwareManager::CheckFirmwarePresence(*system.get()); } void GMainWindow::SetFirmwareVersion() { - Service::Set::FirmwareVersionFormat firmware_data{}; - const auto result = Service::Set::GetFirmwareVersionImpl( - firmware_data, *system, Service::Set::GetFirmwareVersionType::Version2); + const auto pair = FirmwareManager::GetFirmwareVersion(*system.get()); + const auto firmware_data = pair.first; + const auto result = pair.second; if (result.IsError() || !CheckFirmwarePresence()) { LOG_INFO(Frontend, "Installed firmware: No firmware available"); From 5091759a47182fd85e12ca58abcb57d6f4ca1ea1 Mon Sep 17 00:00:00 2001 From: lizzie Date: Sun, 13 Jul 2025 03:40:48 +0200 Subject: [PATCH 05/25] [vk, opengl] defer checks to topmost call (avoid unnecessary call) (#40) Co-authored-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/40 Co-authored-by: lizzie Co-committed-by: lizzie --- src/core/memory.cpp | 69 +++++++++++-------- src/video_core/gpu.h | 5 +- .../renderer_opengl/gl_rasterizer.cpp | 46 ++++++------- .../renderer_vulkan/vk_rasterizer.cpp | 7 +- 4 files changed, 66 insertions(+), 61 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index b033858bf8..34539cc650 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2015 Citra Emulator Project // SPDX-FileCopyrightText: 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -868,35 +871,35 @@ struct Memory::Impl { [[nodiscard]] u8* GetPointerImpl(u64 vaddr, auto on_unmapped, auto on_rasterizer) const { // AARCH64 masks the upper 16 bit of all memory accesses vaddr = vaddr & 0xffffffffffffULL; - if (!AddressSpaceContains(*current_page_table, vaddr, 1)) [[unlikely]] { on_unmapped(); return nullptr; + } else { + // Avoid adding any extra logic to this fast-path block + const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Raw(); + if (const uintptr_t pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { + return reinterpret_cast(pointer + vaddr); + } else { + switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) { + case Common::PageType::Unmapped: + on_unmapped(); + return nullptr; + case Common::PageType::Memory: + ASSERT_MSG(false, "Mapped memory page without a pointer @ 0x{:016X}", vaddr); + return nullptr; + case Common::PageType::DebugMemory: + return GetPointerFromDebugMemory(vaddr); + case Common::PageType::RasterizerCachedMemory: { + u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; + on_rasterizer(); + return host_ptr; + } + default: + UNREACHABLE(); + } + return nullptr; + } } - - // Avoid adding any extra logic to this fast-path block - const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Raw(); - if (const uintptr_t pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { - return reinterpret_cast(pointer + vaddr); - } - switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) { - case Common::PageType::Unmapped: - on_unmapped(); - return nullptr; - case Common::PageType::Memory: - ASSERT_MSG(false, "Mapped memory page without a pointer @ 0x{:016X}", vaddr); - return nullptr; - case Common::PageType::DebugMemory: - return GetPointerFromDebugMemory(vaddr); - case Common::PageType::RasterizerCachedMemory: { - u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; - on_rasterizer(); - return host_ptr; - } - default: - UNREACHABLE(); - } - return nullptr; } [[nodiscard]] u8* GetPointer(const Common::ProcessAddress vaddr) const { @@ -1149,13 +1152,19 @@ struct Memory::Impl { gpu_device_memory->ApplyOpOnPointer(p, scratch_buffers[core], [&](DAddr address) { auto& current_area = rasterizer_write_areas[core]; PAddr subaddress = address >> YUZU_PAGEBITS; - bool do_collection = current_area.last_address == subaddress; - if (!do_collection) [[unlikely]] { - do_collection = system.GPU().OnCPUWrite(address, size); - if (!do_collection) { + // Performance note: + // It may not be a good idea to assume accesses are within the same subaddress (i.e same page) + // It is often the case the games like to access wildly different addresses. Hence why I propose + // we should let the compiler just do it's thing... + if (current_area.last_address != subaddress) { + // Short circuit the need to check for address/size + auto const do_collection = (address != 0 && size != 0) + && system.GPU().OnCPUWrite(address, size); + if (do_collection) { + current_area.last_address = subaddress; + } else { return; } - current_area.last_address = subaddress; } gpu_dirty_managers[core].Collect(address, size); }); diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 8a06adad79..538c4da85a 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -244,7 +247,7 @@ public: void InvalidateRegion(DAddr addr, u64 size); /// Notify rasterizer that CPU is trying to write this area. It returns true if the area is - /// sensible, false otherwise + /// sensible, false otherwise, addr and size must be a valid combination bool OnCPUWrite(DAddr addr, u64 size); /// Notify rasterizer that any caches of the specified region should be flushed and invalidated diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 19f586518f..4fb193293b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2015 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -565,22 +568,17 @@ void RasterizerOpenGL::InvalidateRegion(DAddr addr, u64 size, VideoCommon::Cache bool RasterizerOpenGL::OnCPUWrite(DAddr addr, u64 size) { MICROPROFILE_SCOPE(OpenGL_CacheManagement); - if (addr == 0 || size == 0) { - return false; - } - + DEBUG_ASSERT(addr != 0 || size != 0); { std::scoped_lock lock{buffer_cache.mutex}; if (buffer_cache.OnCPUWrite(addr, size)) { return true; } } - { std::scoped_lock lock{texture_cache.mutex}; texture_cache.WriteMemory(addr, size); } - shader_cache.InvalidateRegion(addr, size); return false; } @@ -1204,24 +1202,24 @@ void RasterizerOpenGL::SyncLogicOpState() { } flags[Dirty::LogicOp] = false; - auto& regs = maxwell3d->regs; - - if (device.IsAmd()) { - using namespace Tegra::Engines; - struct In { - const Maxwell3D::Regs::VertexAttribute::Type d; - In(Maxwell3D::Regs::VertexAttribute::Type n) : d(n) {} - bool operator()(Maxwell3D::Regs::VertexAttribute n) const { - return n.type == d; - } - }; - - bool has_float = - std::any_of(regs.vertex_attrib_format.begin(), regs.vertex_attrib_format.end(), - In(Maxwell3D::Regs::VertexAttribute::Type::Float)); - regs.logic_op.enable = static_cast(!has_float); - } - + auto& regs = maxwell3d->regs; + + if (device.IsAmd()) { + using namespace Tegra::Engines; + struct In { + const Maxwell3D::Regs::VertexAttribute::Type d; + In(Maxwell3D::Regs::VertexAttribute::Type n) : d(n) {} + bool operator()(Maxwell3D::Regs::VertexAttribute n) const { + return n.type == d; + } + }; + + bool has_float = + std::any_of(regs.vertex_attrib_format.begin(), regs.vertex_attrib_format.end(), + In(Maxwell3D::Regs::VertexAttribute::Type::Float)); + regs.logic_op.enable = static_cast(!has_float); + } + if (regs.logic_op.enable) { glEnable(GL_COLOR_LOGIC_OP); glLogicOp(MaxwellToGL::LogicOp(regs.logic_op.op)); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 66f4668026..a2492f1d74 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -636,22 +636,17 @@ void RasterizerVulkan::InnerInvalidation(std::span Date: Sun, 13 Jul 2025 01:14:09 -0400 Subject: [PATCH 06/25] [android] Fix key install and revert to old icon_bg `312b3d4743ad734ac3234385f1edaa94d5b69440` Signed-off-by: crueter --- .../org/yuzu/yuzu_emu/ui/main/MainActivity.kt | 2 + .../app/src/main/res/drawable/ic_icon_bg.xml | 1675 ++++++++--------- src/frontend_common/firmware_manager.cpp | 2 +- 3 files changed, 740 insertions(+), 939 deletions(-) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt index f8ba35dbd5..6d9a2002f5 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt @@ -349,6 +349,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider { val resultCode: Int = NativeLibrary.installKeys(result.toString(), extension); if (resultCode == 0) { + // TODO(crueter): It may be worth it to switch some of these Toasts to snackbars, + // since most of it is foreground-only anyways. Toast.makeText( applicationContext, R.string.keys_install_success, Toast.LENGTH_SHORT ).show() diff --git a/src/android/app/src/main/res/drawable/ic_icon_bg.xml b/src/android/app/src/main/res/drawable/ic_icon_bg.xml index b409a61ea2..df62dde92e 100644 --- a/src/android/app/src/main/res/drawable/ic_icon_bg.xml +++ b/src/android/app/src/main/res/drawable/ic_icon_bg.xml @@ -1,952 +1,751 @@ - - - - - - - - - + + + + android:pathData="M0,0h512v512h-512z" + android:fillColor="#ffffff"/> + android:pathData="M0,0h512v512h-512z" + android:fillColor="#1C1C1C"/> + android:pathData="M208.16,7H159.88C155.54,7 152,10.54 152,14.88V92.16C152,96.54 155.54,100.04 159.88,100.04H208.12C212.5,100.04 216,96.5 216,92.16V14.88C216.04,10.54 212.5,7 208.16,7Z" + android:strokeAlpha="0.7" + android:fillColor="#282828" + android:fillAlpha="0.7"/> + android:pathData="M208.8,89.73H158.44C156.65,89.73 155.18,88.26 155.18,86.47V17.02C155.18,15.23 156.65,13.76 158.44,13.76H208.84C210.63,13.76 212.1,15.23 212.1,17.02V86.51C212.06,88.26 210.59,89.73 208.8,89.73Z" + android:strokeAlpha="0.7" + android:fillColor="#1C1C1C" + android:fillAlpha="0.7"/> + android:pathData="M194.16,14.16H173.08V12.93C173.08,12.29 173.6,11.77 174.24,11.77H193.01C193.65,11.77 194.16,12.29 194.16,12.93V14.16Z" + android:strokeAlpha="0.7" + android:fillColor="#1C1C1C" + android:fillAlpha="0.7"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:pathData="M183.86,97.29L177.93,92.92H189.79L183.86,97.29Z" + android:strokeAlpha="0.7" + android:fillColor="#1C1C1C" + android:fillAlpha="0.7"/> - - + android:pathData="M424.16,7H375.88C371.54,7 368,10.54 368,14.88V92.16C368,96.54 371.54,100.04 375.88,100.04H424.12C428.5,100.04 432,96.5 432,92.16V14.88C432.04,10.54 428.5,7 424.16,7Z" + android:strokeAlpha="0.7" + android:fillColor="#282828" + android:fillAlpha="0.7"/> - - + android:pathData="M424.8,89.73H374.44C372.65,89.73 371.18,88.26 371.18,86.47V17.02C371.18,15.23 372.65,13.76 374.44,13.76H424.84C426.63,13.76 428.1,15.23 428.1,17.02V86.51C428.06,88.26 426.59,89.73 424.8,89.73Z" + android:strokeAlpha="0.7" + android:fillColor="#1C1C1C" + android:fillAlpha="0.7"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:pathData="M410.16,14.16H389.08V12.93C389.08,12.29 389.6,11.77 390.23,11.77H409.01C409.65,11.77 410.16,12.29 410.16,12.93V14.16Z" + android:strokeAlpha="0.7" + android:fillColor="#1C1C1C" + android:fillAlpha="0.7"/> + android:pathData="M399.86,97.29L393.93,92.92H405.79L399.86,97.29Z" + android:strokeAlpha="0.7" + android:fillColor="#1C1C1C" + android:fillAlpha="0.7"/> - - - - + android:pathData="M352.16,109H303.88C299.54,109 296,112.54 296,116.88V194.16C296,198.54 299.54,202.04 303.88,202.04H352.12C356.5,202.04 360,198.5 360,194.16V116.88C360.04,112.54 356.5,109 352.16,109Z" + android:strokeAlpha="0.7" + android:fillColor="#282828" + android:fillAlpha="0.7"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + diff --git a/src/frontend_common/firmware_manager.cpp b/src/frontend_common/firmware_manager.cpp index f1022579be..a180693996 100644 --- a/src/frontend_common/firmware_manager.cpp +++ b/src/frontend_common/firmware_manager.cpp @@ -46,7 +46,7 @@ FirmwareManager::InstallKeys(std::string location, std::string extension) { } jmethodID copyToStorage = Common::Android::GetCopyToStorage(); - jstring jdest = Common::Android::ToJString(env, keys_dir.string()); + jstring jdest = Common::Android::ToJString(env, keys_dir.string() + "/"); jboolean copyResult = env->CallStaticBooleanMethod( native, From c47f6615d3bda7b3c7d305701ec8af38333aeebf Mon Sep 17 00:00:00 2001 From: Ghost <> Date: Sun, 13 Jul 2025 19:27:39 +0200 Subject: [PATCH 07/25] [vk, opengl] Prevent GPU draw call if CBUF binding fails (cbuf0 error handling) (#2) Add defensive checks to cancel draw calls early if any graphics storage buffer (CBUF) fails to bind properly. - Modified BindGraphicsStorageBuffer to return false on invalid buffer ID., - ConfigureImpl (both OpenGL and Vulkan) now propagates binding failure., - Pipeline::Configure returns false if CBUF binding fails., - PrepareDraw cancels rendering if pipeline configuration fails., This avoids undefined GPU behavior, draw corruption, or crashes caused by uninitialized or invalid constant buffer (CBUF0) access, particularly in games with faulty or missing shader bindings. Eden Collaborator: Authored-by: CamilleLaVey Signed-off-by: Bix Co-authored-by: Bix <114880614+Bixbr@users.noreply.github.com> Co-authored-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2 Co-authored-by: Ghost <> Co-committed-by: Ghost <> --- src/video_core/buffer_cache/buffer_cache.h | 3 ++- .../buffer_cache/buffer_cache_base.h | 2 +- .../renderer_opengl/gl_graphics_pipeline.cpp | 9 +++++++-- .../renderer_opengl/gl_graphics_pipeline.h | 13 ++++++++----- .../renderer_opengl/gl_rasterizer.cpp | 3 ++- .../renderer_vulkan/vk_graphics_pipeline.cpp | 19 ++++++++++++++----- .../renderer_vulkan/vk_graphics_pipeline.h | 13 ++++++++----- .../renderer_vulkan/vk_rasterizer.cpp | 3 ++- 8 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 0ce2abc627..a6e87a3583 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -416,7 +416,7 @@ void BufferCache

::UnbindGraphicsStorageBuffers(size_t stage) { } template -void BufferCache

::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, +bool BufferCache

::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset, bool is_written) { channel_state->enabled_storage_buffers[stage] |= 1U << ssbo_index; channel_state->written_storage_buffers[stage] |= (is_written ? 1U : 0U) << ssbo_index; @@ -425,6 +425,7 @@ void BufferCache

::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, const GPUVAddr ssbo_addr = cbufs.const_buffers[cbuf_index].address + cbuf_offset; channel_state->storage_buffers[stage][ssbo_index] = StorageBufferBinding(ssbo_addr, cbuf_index, is_written); + return (channel_state->storage_buffers[stage][ssbo_index].buffer_id != NULL_BUFFER_ID); } template diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index d45f595ea8..1c7320dcc1 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -235,7 +235,7 @@ public: void UnbindGraphicsStorageBuffers(size_t stage); - void BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset, + bool BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset, bool is_written); void UnbindGraphicsTextureBuffers(size_t stage); diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp index af0a453ee7..b4417de703 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -128,7 +131,7 @@ bool Passes(const std::array& stage_infos, u32 enabled_mask) { return true; } -using ConfigureFuncPtr = void (*)(GraphicsPipeline*, bool); +using ConfigureFuncPtr = bool (*)(GraphicsPipeline*, bool); template ConfigureFuncPtr FindSpec(const std::array& stage_infos, u32 enabled_mask) { @@ -275,7 +278,7 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c } template -void GraphicsPipeline::ConfigureImpl(bool is_indexed) { +bool GraphicsPipeline::ConfigureImpl(bool is_indexed) { std::array views; std::array samplers; size_t views_index{}; @@ -556,6 +559,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { if (image_binding != 0) { glBindImageTextures(0, image_binding, images.data()); } + + return true; } void GraphicsPipeline::ConfigureTransformFeedbackImpl() const { diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h index 2f70c1ae9c..66ab677919 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -80,8 +83,8 @@ public: const std::array& infos, const GraphicsPipelineKey& key_, bool force_context_flush = false); - void Configure(bool is_indexed) { - configure_func(this, is_indexed); + bool Configure(bool is_indexed) { + return configure_func(this, is_indexed); } void ConfigureTransformFeedback() const { @@ -107,7 +110,7 @@ public: template static auto MakeConfigureSpecFunc() { return [](GraphicsPipeline* pipeline, bool is_indexed) { - pipeline->ConfigureImpl(is_indexed); + return pipeline->ConfigureImpl(is_indexed); }; } @@ -118,7 +121,7 @@ public: private: template - void ConfigureImpl(bool is_indexed); + bool ConfigureImpl(bool is_indexed); void ConfigureTransformFeedbackImpl() const; @@ -134,7 +137,7 @@ private: StateTracker& state_tracker; const GraphicsPipelineKey key; - void (*configure_func)(GraphicsPipeline*, bool){}; + bool (*configure_func)(GraphicsPipeline*, bool){}; std::array source_programs; std::array assembly_programs; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 4fb193293b..131b7463e0 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -250,7 +250,8 @@ void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) { program_manager.LocalMemoryWarmup(); } pipeline->SetEngine(maxwell3d, gpu_memory); - pipeline->Configure(is_indexed); + if (!pipeline->Configure(is_indexed)) + return; SyncState(); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index e7cec364b6..eb757d68f5 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -175,7 +178,7 @@ bool Passes(const std::array& modules, return true; } -using ConfigureFuncPtr = void (*)(GraphicsPipeline*, bool); +using ConfigureFuncPtr = bool (*)(GraphicsPipeline*, bool); template ConfigureFuncPtr FindSpec(const std::array& modules, @@ -302,7 +305,7 @@ void GraphicsPipeline::AddTransition(GraphicsPipeline* transition) { } template -void GraphicsPipeline::ConfigureImpl(bool is_indexed) { +bool GraphicsPipeline::ConfigureImpl(bool is_indexed) { std::array views; std::array samplers; size_t sampler_index{}; @@ -321,8 +324,9 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { size_t ssbo_index{}; for (const auto& desc : info.storage_buffers_descriptors) { ASSERT(desc.count == 1); - buffer_cache.BindGraphicsStorageBuffer(stage, ssbo_index, desc.cbuf_index, - desc.cbuf_offset, desc.is_written); + if (!buffer_cache.BindGraphicsStorageBuffer(stage, ssbo_index, desc.cbuf_index, + desc.cbuf_offset, desc.is_written)) + return false; ++ssbo_index; } } @@ -382,6 +386,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { add_image(desc, desc.is_written); } } + + return true; }}; if constexpr (Spec::enabled_stages[0]) { config_stage(0); @@ -396,7 +402,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { config_stage(3); } if constexpr (Spec::enabled_stages[4]) { - config_stage(4); + if (!config_stage(4)) + return false; } texture_cache.FillGraphicsImageViews(std::span(views.data(), view_index)); @@ -490,6 +497,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { texture_cache.UpdateRenderTargets(false); texture_cache.CheckFeedbackLoop(views); ConfigureDraw(rescaling, render_area); + + return true; } void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling, diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 99e56e9ad8..7e9dbb583a 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -86,8 +89,8 @@ public: void AddTransition(GraphicsPipeline* transition); - void Configure(bool is_indexed) { - configure_func(this, is_indexed); + bool Configure(bool is_indexed) { + return configure_func(this, is_indexed); } [[nodiscard]] GraphicsPipeline* Next(const GraphicsPipelineCacheKey& current_key) noexcept { @@ -105,7 +108,7 @@ public: template static auto MakeConfigureSpecFunc() { - return [](GraphicsPipeline* pl, bool is_indexed) { pl->ConfigureImpl(is_indexed); }; + return [](GraphicsPipeline* pl, bool is_indexed) { return pl->ConfigureImpl(is_indexed); }; } void SetEngine(Tegra::Engines::Maxwell3D* maxwell3d_, Tegra::MemoryManager* gpu_memory_) { @@ -115,7 +118,7 @@ public: private: template - void ConfigureImpl(bool is_indexed); + bool ConfigureImpl(bool is_indexed); void ConfigureDraw(const RescalingPushConstant& rescaling, const RenderAreaPushConstant& render_are); @@ -134,7 +137,7 @@ private: Scheduler& scheduler; GuestDescriptorQueue& guest_descriptor_queue; - void (*configure_func)(GraphicsPipeline*, bool){}; + bool (*configure_func)(GraphicsPipeline*, bool){}; std::vector transition_keys; std::vector transitions; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index a2492f1d74..0243693049 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -226,7 +226,8 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) { std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; // update engine as channel may be different. pipeline->SetEngine(maxwell3d, gpu_memory); - pipeline->Configure(is_indexed); + if (!pipeline->Configure(is_indexed)) + return; UpdateDynamicStates(); From a0a208db578b8d77579573d437ad251029db1635 Mon Sep 17 00:00:00 2001 From: SDK Chan Date: Sun, 13 Jul 2025 20:58:04 +0200 Subject: [PATCH 08/25] [cmake] Fix misplaced comment (#54) A member of the community notified me that I misplaced a comment so, I corrected it. Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/54 Co-authored-by: SDK Chan Co-committed-by: SDK Chan --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1efa94e45a..1b8f60089d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,10 +29,10 @@ elseif (TARGET SDL2::SDL2-shared) endif() # Set bundled sdl2/qt as dependent options. +# On Linux system SDL2 is likely to be lacking HIDAPI support which have drawbacks but is needed for SDL motion option(ENABLE_SDL2 "Enable the SDL2 frontend" ON) CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" ON "ENABLE_SDL2;MSVC" OFF) if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") - # On Linux system SDL2 is likely to be lacking HIDAPI support which have drawbacks but is needed for SDL motion CMAKE_DEPENDENT_OPTION(YUZU_USE_EXTERNAL_SDL2 "Compile external SDL2" OFF "ENABLE_SDL2;NOT MSVC" OFF) else() CMAKE_DEPENDENT_OPTION(YUZU_USE_EXTERNAL_SDL2 "Compile external SDL2" ON "ENABLE_SDL2;NOT MSVC" OFF) From fe4f5a3860fcb2941f2dabe1327c7bd2eb229ca6 Mon Sep 17 00:00:00 2001 From: Ghost <> Date: Sun, 13 Jul 2025 23:03:26 +0200 Subject: [PATCH 09/25] [dynarmic] lea over mov and other stuff (#24) Co-authored-by: Esther1024 Co-authored-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/24 Co-authored-by: Ghost <> Co-committed-by: Ghost <> --- .gitignore | 1 + externals/dynarmic/CMakeLists.txt | 6 +- .../dynarmic/src/dynarmic/CMakeLists.txt | 105 - .../dynarmic/src/dynarmic/backend/arm64/abi.h | 3 +- .../src/dynarmic/backend/riscv64/code_block.h | 13 +- .../src/dynarmic/backend/x64/a32_emit_x64.cpp | 47 +- .../src/dynarmic/backend/x64/a64_emit_x64.cpp | 7 +- .../backend/x64/a64_emit_x64_memory.cpp | 16 +- .../dynarmic/backend/x64/block_of_code.cpp | 9 +- .../src/dynarmic/backend/x64/emit_x64.cpp | 9 +- .../backend/x64/emit_x64_data_processing.cpp | 1 - .../backend/x64/emit_x64_floating_point.cpp | 80 +- .../dynarmic/backend/x64/emit_x64_memory.h | 7 +- .../x64/emit_x64_vector_floating_point.cpp | 39 +- .../x64/emit_x64_vector_saturation.cpp | 3 + .../backend/x64/exception_handler_windows.cpp | 2 +- .../dynarmic/frontend/A32/a32_ir_emitter.h | 12 +- .../src/dynarmic/frontend/A32/decoder/arm.h | 11 +- .../frontend/A32/translate/a32_translate.cpp | 48 + .../A32/translate/impl/a32_translate_impl.cpp | 4 + .../A32/translate/impl/a32_translate_impl.h | 2 +- .../A32/translate/impl/asimd_three_regs.cpp | 6 +- .../translate/impl/asimd_two_regs_misc.cpp | 30 +- .../translate/impl/asimd_two_regs_shift.cpp | 36 +- .../frontend/A32/translate/impl/common.h | 31 + .../frontend/A32/translate/impl/extension.cpp | 6 +- .../frontend/A32/translate/impl/saturated.cpp | 9 +- ...data_processing_plain_binary_immediate.cpp | 8 +- .../impl/thumb32_data_processing_register.cpp | 5 +- .../A32/translate/impl/thumb32_load_byte.cpp | 8 +- .../translate/impl/thumb32_load_halfword.cpp | 9 +- .../impl/thumb32_load_store_dual.cpp | 4 +- .../impl/thumb32_load_store_multiple.cpp | 4 +- .../A32/translate/impl/thumb32_load_word.cpp | 4 +- .../A32/translate/impl/thumb32_parallel.cpp | 8 +- .../dynarmic/frontend/A64/a64_ir_emitter.cpp | 254 -- .../dynarmic/frontend/A64/a64_ir_emitter.h | 313 +- .../src/dynarmic/frontend/A64/decoder/a64.h | 16 +- .../frontend/A64/translate/a64_translate.cpp | 61 + .../translate/impl/simd_scalar_pairwise.cpp | 20 +- .../impl/simd_scalar_shift_by_immediate.cpp | 58 +- .../translate/impl/simd_scalar_three_same.cpp | 10 +- .../impl/simd_scalar_two_register_misc.cpp | 52 +- .../impl/simd_scalar_x_indexed_element.cpp | 8 +- .../impl/simd_shift_by_immediate.cpp | 98 +- .../translate/impl/simd_three_different.cpp | 54 +- .../A64/translate/impl/simd_three_same.cpp | 140 +- .../translate/impl/simd_two_register_misc.cpp | 92 +- .../impl/simd_vector_x_indexed_element.cpp | 80 +- .../src/dynarmic/interface/A32/arch_version.h | 4 +- .../src/dynarmic/interface/A32/config.h | 85 +- .../src/dynarmic/interface/A64/config.h | 137 +- .../dynarmic/src/dynarmic/ir/ir_emitter.cpp | 2872 --------------- .../dynarmic/src/dynarmic/ir/ir_emitter.h | 3176 +++++++++++++++-- 54 files changed, 3874 insertions(+), 4249 deletions(-) create mode 100644 externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/common.h diff --git a/.gitignore b/.gitignore index 7484a368e6..9aaf549512 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ CMakeLists.txt.user* # Visual Studio CMake settings CMakeSettings.json +.cache/ # OSX global filetypes # Created by Finder or Spotlight in directories for various OS functionality (indexing, etc) diff --git a/externals/dynarmic/CMakeLists.txt b/externals/dynarmic/CMakeLists.txt index efb5edf165..3db8d8077b 100644 --- a/externals/dynarmic/CMakeLists.txt +++ b/externals/dynarmic/CMakeLists.txt @@ -98,8 +98,7 @@ else() -Wextra -Wcast-qual -pedantic - -Wno-missing-braces - -Wstack-usage=4096) + -Wno-missing-braces) if (ARCHITECTURE STREQUAL "x86_64") list(APPEND DYNARMIC_CXX_FLAGS -mtune=core2) @@ -123,12 +122,15 @@ else() # GCC knows that the variable is actually a Reg64. isMEM() will never return true for a # Reg64, but GCC doesn't know that. list(APPEND DYNARMIC_CXX_FLAGS -Wno-array-bounds) + list(APPEND DYNARMIC_CXX_FLAGS -Wstack-usage=4096) endif() if (CMAKE_CXX_COMPILER_ID MATCHES "[Cc]lang") # Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6. # And this in turns limits the size of a std::array. list(APPEND DYNARMIC_CXX_FLAGS -fbracket-depth=1024) + # Clang mistakenly blames CMake for using unused arguments during compilation + list(APPEND DYNARMIC_CXX_FLAGS -Wno-unused-command-line-argument) endif() endif() diff --git a/externals/dynarmic/src/dynarmic/CMakeLists.txt b/externals/dynarmic/src/dynarmic/CMakeLists.txt index 9227951fcc..a43c9eae10 100644 --- a/externals/dynarmic/src/dynarmic/CMakeLists.txt +++ b/externals/dynarmic/src/dynarmic/CMakeLists.txt @@ -125,52 +125,6 @@ if ("A32" IN_LIST DYNARMIC_FRONTENDS) frontend/A32/translate/a32_translate.h frontend/A32/translate/conditional_state.cpp frontend/A32/translate/conditional_state.h - frontend/A32/translate/impl/a32_branch.cpp - frontend/A32/translate/impl/a32_crc32.cpp - frontend/A32/translate/impl/a32_exception_generating.cpp - frontend/A32/translate/impl/a32_translate_impl.cpp - frontend/A32/translate/impl/a32_translate_impl.h - frontend/A32/translate/impl/asimd_load_store_structures.cpp - frontend/A32/translate/impl/asimd_misc.cpp - frontend/A32/translate/impl/asimd_one_reg_modified_immediate.cpp - frontend/A32/translate/impl/asimd_three_regs.cpp - frontend/A32/translate/impl/asimd_two_regs_misc.cpp - frontend/A32/translate/impl/asimd_two_regs_scalar.cpp - frontend/A32/translate/impl/asimd_two_regs_shift.cpp - frontend/A32/translate/impl/barrier.cpp - frontend/A32/translate/impl/coprocessor.cpp - frontend/A32/translate/impl/data_processing.cpp - frontend/A32/translate/impl/divide.cpp - frontend/A32/translate/impl/extension.cpp - frontend/A32/translate/impl/hint.cpp - frontend/A32/translate/impl/load_store.cpp - frontend/A32/translate/impl/misc.cpp - frontend/A32/translate/impl/multiply.cpp - frontend/A32/translate/impl/packing.cpp - frontend/A32/translate/impl/parallel.cpp - frontend/A32/translate/impl/reversal.cpp - frontend/A32/translate/impl/saturated.cpp - frontend/A32/translate/impl/status_register_access.cpp - frontend/A32/translate/impl/synchronization.cpp - frontend/A32/translate/impl/thumb16.cpp - frontend/A32/translate/impl/thumb32_branch.cpp - frontend/A32/translate/impl/thumb32_control.cpp - frontend/A32/translate/impl/thumb32_coprocessor.cpp - frontend/A32/translate/impl/thumb32_data_processing_modified_immediate.cpp - frontend/A32/translate/impl/thumb32_data_processing_plain_binary_immediate.cpp - frontend/A32/translate/impl/thumb32_data_processing_register.cpp - frontend/A32/translate/impl/thumb32_data_processing_shifted_register.cpp - frontend/A32/translate/impl/thumb32_load_byte.cpp - frontend/A32/translate/impl/thumb32_load_halfword.cpp - frontend/A32/translate/impl/thumb32_load_store_dual.cpp - frontend/A32/translate/impl/thumb32_load_store_multiple.cpp - frontend/A32/translate/impl/thumb32_load_word.cpp - frontend/A32/translate/impl/thumb32_long_multiply.cpp - frontend/A32/translate/impl/thumb32_misc.cpp - frontend/A32/translate/impl/thumb32_multiply.cpp - frontend/A32/translate/impl/thumb32_parallel.cpp - frontend/A32/translate/impl/thumb32_store_single_data_item.cpp - frontend/A32/translate/impl/vfp.cpp frontend/A32/translate/translate_arm.cpp frontend/A32/translate/translate_thumb.cpp interface/A32/a32.h @@ -194,65 +148,6 @@ if ("A64" IN_LIST DYNARMIC_FRONTENDS) frontend/A64/decoder/a64.inc frontend/A64/translate/a64_translate.cpp frontend/A64/translate/a64_translate.h - frontend/A64/translate/impl/a64_branch.cpp - frontend/A64/translate/impl/a64_exception_generating.cpp - frontend/A64/translate/impl/data_processing_addsub.cpp - frontend/A64/translate/impl/data_processing_bitfield.cpp - frontend/A64/translate/impl/data_processing_conditional_compare.cpp - frontend/A64/translate/impl/data_processing_conditional_select.cpp - frontend/A64/translate/impl/data_processing_crc32.cpp - frontend/A64/translate/impl/data_processing_logical.cpp - frontend/A64/translate/impl/data_processing_multiply.cpp - frontend/A64/translate/impl/data_processing_pcrel.cpp - frontend/A64/translate/impl/data_processing_register.cpp - frontend/A64/translate/impl/data_processing_shift.cpp - frontend/A64/translate/impl/floating_point_compare.cpp - frontend/A64/translate/impl/floating_point_conditional_compare.cpp - frontend/A64/translate/impl/floating_point_conditional_select.cpp - frontend/A64/translate/impl/floating_point_conversion_fixed_point.cpp - frontend/A64/translate/impl/floating_point_conversion_integer.cpp - frontend/A64/translate/impl/floating_point_data_processing_one_register.cpp - frontend/A64/translate/impl/floating_point_data_processing_three_register.cpp - frontend/A64/translate/impl/floating_point_data_processing_two_register.cpp - frontend/A64/translate/impl/impl.cpp - frontend/A64/translate/impl/impl.h - frontend/A64/translate/impl/load_store_exclusive.cpp - frontend/A64/translate/impl/load_store_load_literal.cpp - frontend/A64/translate/impl/load_store_multiple_structures.cpp - frontend/A64/translate/impl/load_store_no_allocate_pair.cpp - frontend/A64/translate/impl/load_store_register_immediate.cpp - frontend/A64/translate/impl/load_store_register_pair.cpp - frontend/A64/translate/impl/load_store_register_register_offset.cpp - frontend/A64/translate/impl/load_store_register_unprivileged.cpp - frontend/A64/translate/impl/load_store_single_structure.cpp - frontend/A64/translate/impl/move_wide.cpp - frontend/A64/translate/impl/simd_across_lanes.cpp - frontend/A64/translate/impl/simd_aes.cpp - frontend/A64/translate/impl/simd_copy.cpp - frontend/A64/translate/impl/simd_crypto_four_register.cpp - frontend/A64/translate/impl/simd_crypto_three_register.cpp - frontend/A64/translate/impl/simd_extract.cpp - frontend/A64/translate/impl/simd_modified_immediate.cpp - frontend/A64/translate/impl/simd_permute.cpp - frontend/A64/translate/impl/simd_scalar_pairwise.cpp - frontend/A64/translate/impl/simd_scalar_shift_by_immediate.cpp - frontend/A64/translate/impl/simd_scalar_three_same.cpp - frontend/A64/translate/impl/simd_scalar_two_register_misc.cpp - frontend/A64/translate/impl/simd_scalar_x_indexed_element.cpp - frontend/A64/translate/impl/simd_sha.cpp - frontend/A64/translate/impl/simd_sha512.cpp - frontend/A64/translate/impl/simd_shift_by_immediate.cpp - frontend/A64/translate/impl/simd_table_lookup.cpp - frontend/A64/translate/impl/simd_three_different.cpp - frontend/A64/translate/impl/simd_three_same.cpp - frontend/A64/translate/impl/simd_three_same_extra.cpp - frontend/A64/translate/impl/simd_two_register_misc.cpp - frontend/A64/translate/impl/simd_vector_x_indexed_element.cpp - frontend/A64/translate/impl/sys_dc.cpp - frontend/A64/translate/impl/sys_ic.cpp - frontend/A64/translate/impl/system.cpp - frontend/A64/translate/impl/system_flag_format.cpp - frontend/A64/translate/impl/system_flag_manipulation.cpp interface/A64/a64.h interface/A64/config.h ir/opt/a64_callback_config_pass.cpp diff --git a/externals/dynarmic/src/dynarmic/backend/arm64/abi.h b/externals/dynarmic/src/dynarmic/backend/arm64/abi.h index 609b06cd22..d3d5de963a 100644 --- a/externals/dynarmic/src/dynarmic/backend/arm64/abi.h +++ b/externals/dynarmic/src/dynarmic/backend/arm64/abi.h @@ -59,13 +59,12 @@ constexpr RegisterList ToRegList(oaknut::Reg reg) { } if (reg.index() == 31) { - throw std::out_of_range("ZR not allowed in reg list"); + ASSERT_FALSE("ZR not allowed in reg list"); } if (reg.index() == -1) { return RegisterList{1} << 31; } - return RegisterList{1} << reg.index(); } diff --git a/externals/dynarmic/src/dynarmic/backend/riscv64/code_block.h b/externals/dynarmic/src/dynarmic/backend/riscv64/code_block.h index 6ac014a51a..8f98fdf01f 100644 --- a/externals/dynarmic/src/dynarmic/backend/riscv64/code_block.h +++ b/externals/dynarmic/src/dynarmic/backend/riscv64/code_block.h @@ -14,29 +14,26 @@ namespace Dynarmic::Backend::RV64 { class CodeBlock { public: - explicit CodeBlock(std::size_t size) - : memsize(size) { + 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); - if (mem == nullptr) - throw std::bad_alloc{}; + ASSERT_FALSE("out of memory"); } - ~CodeBlock() { + ~CodeBlock() noexcept { if (mem == nullptr) return; - munmap(mem, memsize); } template - T ptr() const { + T ptr() const noexcept { static_assert(std::is_pointer_v || std::is_same_v || std::is_same_v); return reinterpret_cast(mem); } protected: - u8* mem; + u8* mem = nullptr; size_t memsize = 0; }; } // namespace Dynarmic::Backend::RV64 diff --git a/externals/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp b/externals/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp index ce38a52c73..740625c982 100644 --- a/externals/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp +++ b/externals/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp @@ -124,35 +124,36 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) { EmitCondPrelude(ctx); - for (auto iter = block.begin(); iter != block.end(); ++iter) { - IR::Inst* inst = &*iter; - - // Call the relevant Emit* member function. - switch (inst->GetOpcode()) { -#define OPCODE(name, type, ...) \ - case IR::Opcode::name: \ - A32EmitX64::Emit##name(ctx, inst); \ - break; -#define A32OPC(name, type, ...) \ - case IR::Opcode::A32##name: \ - A32EmitX64::EmitA32##name(ctx, inst); \ - break; + auto const loop_all_inst = [this, &block, &ctx](auto const func) { + for (auto iter = block.begin(); iter != block.end(); ++iter) [[likely]] { + auto* inst = &*iter; + // Call the relevant Emit* member function. + switch (inst->GetOpcode()) { +#define OPCODE(name, type, ...) \ + case IR::Opcode::name: \ + A32EmitX64::Emit##name(ctx, inst); \ + break; +#define A32OPC(name, type, ...) \ + case IR::Opcode::A32##name: \ + A32EmitX64::EmitA32##name(ctx, inst);\ + break; #define A64OPC(...) #include "dynarmic/ir/opcodes.inc" #undef OPCODE #undef A32OPC #undef A64OPC - - default: - ASSERT_FALSE("Invalid opcode: {}", inst->GetOpcode()); - break; + default: [[unlikely]] ASSERT_FALSE("Invalid opcode: {}", inst->GetOpcode()); + } + reg_alloc.EndOfAllocScope(); + func(reg_alloc); } - - reg_alloc.EndOfAllocScope(); - - if (conf.very_verbose_debugging_output) { + }; + if (!conf.very_verbose_debugging_output) [[likely]] { + loop_all_inst([](auto&) { /*noop*/ }); + } else [[unlikely]] { + loop_all_inst([this](auto& reg_alloc) { EmitVerboseDebuggingOutput(reg_alloc); - } + }); } reg_alloc.AssertNoMoreUses(); @@ -229,7 +230,7 @@ void A32EmitX64::GenTerminalHandlers() { terminal_handler_pop_rsb_hint = code.getCurr(); calculate_location_descriptor(); code.mov(eax, dword[r15 + offsetof(A32JitState, rsb_ptr)]); - code.sub(eax, 1); + code.dec(eax); code.and_(eax, u32(A32JitState::RSBPtrMask)); code.mov(dword[r15 + offsetof(A32JitState, rsb_ptr)], eax); code.cmp(rbx, qword[r15 + offsetof(A32JitState, rsb_location_descriptors) + rax * sizeof(u64)]); diff --git a/externals/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp b/externals/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp index ad84e0ecc0..4d7bb0d7b1 100644 --- a/externals/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp +++ b/externals/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp @@ -198,18 +198,19 @@ void A64EmitX64::GenTerminalHandlers() { code.or_(rbx, rcx); }; - Xbyak::Label fast_dispatch_cache_miss, rsb_cache_miss; + Xbyak::Label fast_dispatch_cache_miss; + Xbyak::Label rsb_cache_miss; code.align(); terminal_handler_pop_rsb_hint = code.getCurr(); calculate_location_descriptor(); code.mov(eax, dword[r15 + offsetof(A64JitState, rsb_ptr)]); - code.sub(eax, 1); + code.dec(eax); code.and_(eax, u32(A64JitState::RSBPtrMask)); code.mov(dword[r15 + offsetof(A64JitState, rsb_ptr)], eax); code.cmp(rbx, qword[r15 + offsetof(A64JitState, rsb_location_descriptors) + rax * sizeof(u64)]); if (conf.HasOptimization(OptimizationFlag::FastDispatch)) { - code.jne(rsb_cache_miss); + code.jne(rsb_cache_miss, code.T_NEAR); } else { code.jne(code.GetReturnFromRunCodeAddress()); } diff --git a/externals/dynarmic/src/dynarmic/backend/x64/a64_emit_x64_memory.cpp b/externals/dynarmic/src/dynarmic/backend/x64/a64_emit_x64_memory.cpp index 450b16d000..fe7dfa011f 100644 --- a/externals/dynarmic/src/dynarmic/backend/x64/a64_emit_x64_memory.cpp +++ b/externals/dynarmic/src/dynarmic/backend/x64/a64_emit_x64_memory.cpp @@ -33,13 +33,13 @@ void A64EmitX64::GenMemory128Accessors() { #ifdef _WIN32 Devirtualize<&A64::UserCallbacks::MemoryRead128>(conf.callbacks).EmitCallWithReturnPointer(code, [&](Xbyak::Reg64 return_value_ptr, [[maybe_unused]] RegList args) { code.mov(code.ABI_PARAM3, code.ABI_PARAM2); - code.sub(rsp, 8 + 16 + ABI_SHADOW_SPACE); + code.lea(rsp, ptr[rsp - (8 + 16 + ABI_SHADOW_SPACE)]); code.lea(return_value_ptr, ptr[rsp + ABI_SHADOW_SPACE]); }); code.movups(xmm1, xword[code.ABI_RETURN]); code.add(rsp, 8 + 16 + ABI_SHADOW_SPACE); #else - code.sub(rsp, 8); + code.lea(rsp, ptr[rsp - 8]); Devirtualize<&A64::UserCallbacks::MemoryRead128>(conf.callbacks).EmitCall(code); if (code.HasHostFeature(HostFeature::SSE41)) { code.movq(xmm1, code.ABI_RETURN); @@ -57,13 +57,13 @@ void A64EmitX64::GenMemory128Accessors() { code.align(); memory_write_128 = code.getCurr(); #ifdef _WIN32 - code.sub(rsp, 8 + 16 + ABI_SHADOW_SPACE); + code.lea(rsp, ptr[rsp - (8 + 16 + ABI_SHADOW_SPACE)]); code.lea(code.ABI_PARAM3, ptr[rsp + ABI_SHADOW_SPACE]); code.movaps(xword[code.ABI_PARAM3], xmm1); Devirtualize<&A64::UserCallbacks::MemoryWrite128>(conf.callbacks).EmitCall(code); code.add(rsp, 8 + 16 + ABI_SHADOW_SPACE); #else - code.sub(rsp, 8); + code.lea(rsp, ptr[rsp - 8]); if (code.HasHostFeature(HostFeature::SSE41)) { code.movq(code.ABI_PARAM3, xmm1); code.pextrq(code.ABI_PARAM4, xmm1, 1); @@ -81,7 +81,7 @@ void A64EmitX64::GenMemory128Accessors() { code.align(); memory_exclusive_write_128 = code.getCurr(); #ifdef _WIN32 - code.sub(rsp, 8 + 32 + ABI_SHADOW_SPACE); + code.lea(rsp, ptr[rsp - (8 + 32 + ABI_SHADOW_SPACE)]); code.lea(code.ABI_PARAM3, ptr[rsp + ABI_SHADOW_SPACE]); code.lea(code.ABI_PARAM4, ptr[rsp + ABI_SHADOW_SPACE + 16]); code.movaps(xword[code.ABI_PARAM3], xmm1); @@ -89,7 +89,7 @@ void A64EmitX64::GenMemory128Accessors() { Devirtualize<&A64::UserCallbacks::MemoryWriteExclusive128>(conf.callbacks).EmitCall(code); code.add(rsp, 8 + 32 + ABI_SHADOW_SPACE); #else - code.sub(rsp, 8); + code.lea(rsp, ptr[rsp - 8]); if (code.HasHostFeature(HostFeature::SSE41)) { code.movq(code.ABI_PARAM3, xmm1); code.pextrq(code.ABI_PARAM4, xmm1, 1); @@ -131,8 +131,8 @@ void A64EmitX64::GenFastmemFallbacks() { {64, Devirtualize<&A64::UserCallbacks::MemoryWriteExclusive64>(conf.callbacks)}, }}; - for (bool ordered : {false, true}) { - for (int vaddr_idx : idxes) { + for (auto const ordered : {false, true}) { + for (auto const vaddr_idx : idxes) { if (vaddr_idx == 4 || vaddr_idx == 15) { continue; } diff --git a/externals/dynarmic/src/dynarmic/backend/x64/block_of_code.cpp b/externals/dynarmic/src/dynarmic/backend/x64/block_of_code.cpp index 22d9868fc5..e5fb25573b 100644 --- a/externals/dynarmic/src/dynarmic/backend/x64/block_of_code.cpp +++ b/externals/dynarmic/src/dynarmic/backend/x64/block_of_code.cpp @@ -63,7 +63,8 @@ public: uint8_t* alloc(size_t size) override { void* p = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE); if (p == nullptr) { - throw Xbyak::Error(Xbyak::ERR_CANT_ALLOC); + using Xbyak::Error; + XBYAK_THROW(Xbyak::ERR_CANT_ALLOC); } return static_cast(p); } @@ -95,7 +96,8 @@ public: void* p = mmap(nullptr, size, PROT_READ | PROT_WRITE, mode, -1, 0); if (p == MAP_FAILED) { - throw Xbyak::Error(Xbyak::ERR_CANT_ALLOC); + using Xbyak::Error; + XBYAK_THROW(Xbyak::ERR_CANT_ALLOC); } std::memcpy(p, &size, sizeof(size_t)); return static_cast(p) + DYNARMIC_PAGE_SIZE; @@ -514,7 +516,8 @@ size_t BlockOfCode::GetTotalCodeSize() const { void* BlockOfCode::AllocateFromCodeSpace(size_t alloc_size) { if (size_ + alloc_size >= maxSize_) { - throw Xbyak::Error(Xbyak::ERR_CODE_IS_TOO_BIG); + using Xbyak::Error; + XBYAK_THROW(Xbyak::ERR_CODE_IS_TOO_BIG); } EnsureMemoryCommitted(alloc_size); diff --git a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp index 8bd9102d0d..b9a705813f 100644 --- a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp +++ b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp @@ -104,7 +104,7 @@ void EmitX64::PushRSBHelper(Xbyak::Reg64 loc_desc_reg, Xbyak::Reg64 index_reg, I } void EmitX64::EmitVerboseDebuggingOutput(RegAlloc& reg_alloc) { - code.sub(rsp, sizeof(RegisterData)); + code.lea(rsp, ptr[rsp - sizeof(RegisterData)]); code.stmxcsr(dword[rsp + offsetof(RegisterData, mxcsr)]); for (int i = 0; i < 16; i++) { if (rsp.getIdx() == i) { @@ -223,7 +223,7 @@ void EmitX64::EmitGetNZCVFromOp(EmitContext& ctx, IR::Inst* inst) { const Xbyak::Reg value = ctx.reg_alloc.UseGpr(args[0]).changeBit(bitsize); code.test(value, value); code.lahf(); - code.mov(al, 0); + code.xor_(al, al); ctx.reg_alloc.DefineValue(inst, nzcv); } @@ -270,7 +270,6 @@ void EmitX64::EmitNZCVFromPackedFlags(EmitContext& ctx, IR::Inst* inst) { code.shr(nzcv, 28); code.imul(nzcv, nzcv, NZCV::to_x64_multiplier); code.and_(nzcv, NZCV::x64_mask); - ctx.reg_alloc.DefineValue(inst, nzcv); } } @@ -331,10 +330,8 @@ Xbyak::Label EmitX64::EmitCond(IR::Cond cond) { code.jle(pass); break; default: - ASSERT_MSG(false, "Unknown cond {}", static_cast(cond)); - break; + UNREACHABLE(); } - return pass; } diff --git a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_data_processing.cpp b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_data_processing.cpp index 98197c2db3..cb1afdec9e 100644 --- a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_data_processing.cpp +++ b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_data_processing.cpp @@ -992,7 +992,6 @@ static void EmitAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst, int bit code.seto(overflow); ctx.reg_alloc.DefineValue(overflow_inst, overflow); } - ctx.reg_alloc.DefineValue(inst, result); } diff --git a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_floating_point.cpp b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_floating_point.cpp index 182c887538..aeb4ceac3c 100644 --- a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_floating_point.cpp +++ b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_floating_point.cpp @@ -33,6 +33,23 @@ #include "dynarmic/ir/basic_block.h" #include "dynarmic/ir/microinstruction.h" +#define FCODE(NAME) \ + [&code](auto... args) { \ + if constexpr (fsize == 32) { \ + code.NAME##s(args...); \ + } else { \ + code.NAME##d(args...); \ + } \ + } +#define ICODE(NAME) \ + [&code](auto... args) { \ + if constexpr (fsize == 32) { \ + code.NAME##d(args...); \ + } else { \ + code.NAME##q(args...); \ + } \ + } + namespace Dynarmic::Backend::X64 { using namespace Xbyak::util; @@ -60,23 +77,6 @@ constexpr u64 f64_max_s32 = 0x41dfffffffc00000u; // 2147483647 as a double constexpr u64 f64_max_u32 = 0x41efffffffe00000u; // 4294967295 as a double constexpr u64 f64_max_s64_lim = 0x43e0000000000000u; // 2^63 as a double (actual maximum unrepresentable) -#define FCODE(NAME) \ - [&code](auto... args) { \ - if constexpr (fsize == 32) { \ - code.NAME##s(args...); \ - } else { \ - code.NAME##d(args...); \ - } \ - } -#define ICODE(NAME) \ - [&code](auto... args) { \ - if constexpr (fsize == 32) { \ - code.NAME##d(args...); \ - } else { \ - code.NAME##q(args...); \ - } \ - } - template void ForceDenormalsToZero(BlockOfCode& code, std::initializer_list to_daz) { if (code.HasHostFeature(HostFeature::AVX512_OrthoFloat)) { @@ -473,7 +473,7 @@ static void EmitFPMinMax(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) { } template -static void EmitFPMinMaxNumeric(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) { +static inline void EmitFPMinMaxNumeric(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) noexcept { using FPT = mcl::unsigned_integer_of_size; constexpr FPT default_nan = FP::FPInfo::DefaultNaN(); @@ -701,15 +701,14 @@ static void EmitFPMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) { // x64 rounds before flushing to zero // AArch64 rounds after flushing to zero // This difference of behaviour is noticable if something would round to a smallest normalized number - - code.sub(rsp, 8); + code.lea(rsp, ptr[rsp - 8]); ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx())); code.movq(code.ABI_PARAM1, operand1); code.movq(code.ABI_PARAM2, operand2); code.movq(code.ABI_PARAM3, operand3); code.mov(code.ABI_PARAM4.cvt32(), ctx.FPCR().Value()); #ifdef _WIN32 - code.sub(rsp, 16 + ABI_SHADOW_SPACE); + code.lea(rsp, ptr[rsp - (16 + ABI_SHADOW_SPACE)]); code.lea(rax, code.ptr[code.r15 + code.GetJitStateInfo().offsetof_fpsr_exc]); code.mov(qword[rsp + ABI_SHADOW_SPACE], rax); code.CallFunction(fallback_fn); @@ -735,13 +734,13 @@ static void EmitFPMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) { code.vmovaps(xmm0, code.Const(xword, FP::FPInfo::mantissa_msb)); FCODE(ucomis)(operand2, operand3); - code.jp(has_nan); + code.jp(has_nan, code.T_NEAR); FCODE(ucomis)(operand1, operand1); - code.jnp(indeterminate); + code.jnp(indeterminate, code.T_NEAR); // AArch64 specifically emits a default NaN for the case when the addend is a QNaN and the two other arguments are {inf, zero} code.ptest(operand1, xmm0); - code.jz(op1_snan); + code.jz(op1_snan, code.T_NEAR); FCODE(vmuls)(xmm0, operand2, operand3); // check if {op2, op3} are {inf, zero}/{zero, inf} FCODE(ucomis)(xmm0, xmm0); code.jnp(*end); @@ -753,10 +752,10 @@ static void EmitFPMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) { code.L(has_nan); FCODE(ucomis)(operand1, operand1); - code.jnp(op1_done); + code.jnp(op1_done, code.T_NEAR); code.movaps(result, operand1); // this is done because of NaN behavior of vfmadd231s (priority of op2, op3, op1) code.ptest(operand1, xmm0); - code.jnz(op1_done); + code.jnz(op1_done, code.T_NEAR); code.L(op1_snan); code.vorps(result, operand1, xmm0); code.jmp(*end); @@ -774,9 +773,9 @@ static void EmitFPMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) { code.L(op2_done); FCODE(ucomis)(operand3, operand3); - code.jnp(op3_done); + code.jnp(op3_done, code.T_NEAR); code.ptest(operand3, xmm0); - code.jnz(op3_done); + code.jnz(op3_done, code.T_NEAR); code.vorps(result, operand3, xmm0); code.jmp(*end); code.L(op3_done); @@ -1019,7 +1018,7 @@ static void EmitFPRecipStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst* ctx.deferred_emits.emplace_back([=, &code, &ctx] { code.L(*fallback); - code.sub(rsp, 8); + code.lea(rsp, ptr[rsp - 8]); ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx())); code.movq(code.ABI_PARAM1, operand1); code.movq(code.ABI_PARAM2, operand2); @@ -1204,9 +1203,9 @@ static void EmitFPRSqrtEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* i } // a > 0 && a < 0x00800000; - code.sub(tmp, 1); + code.dec(tmp); code.cmp(tmp, 0x007FFFFF); - code.jb(fallback); + code.jb(fallback, code.T_NEAR); //within -127,128 needs_fallback = true; } @@ -1235,17 +1234,17 @@ static void EmitFPRSqrtEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* i code.ucomisd(value, result); if (ctx.FPCR().DN()) { - code.jc(default_nan); - code.je(zero); + code.jc(default_nan, code.T_NEAR); + code.je(zero, code.T_NEAR); } else { - code.jp(nan); - code.je(zero); - code.jc(default_nan); + code.jp(nan, code.T_NEAR); + code.je(zero, code.T_NEAR); + code.jc(default_nan, code.T_NEAR); } if (!ctx.FPCR().FZ()) { needs_fallback = true; - code.jmp(fallback); + code.jmp(fallback, code.T_NEAR); } else { // result = 0 code.jmp(*end, code.T_NEAR); @@ -1278,7 +1277,7 @@ static void EmitFPRSqrtEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* i code.L(fallback); if (needs_fallback) { - code.sub(rsp, 8); + code.lea(rsp, ptr[rsp - 8]); ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx())); code.movq(code.ABI_PARAM1, operand); code.mov(code.ABI_PARAM2.cvt32(), ctx.FPCR().Value()); @@ -1361,7 +1360,7 @@ static void EmitFPRSqrtStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst* ctx.deferred_emits.emplace_back([=, &code, &ctx] { code.L(*fallback); - code.sub(rsp, 8); + code.lea(rsp, ptr[rsp - 8]); ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx())); code.movq(code.ABI_PARAM1, operand1); code.movq(code.ABI_PARAM2, operand2); @@ -2132,3 +2131,6 @@ void EmitX64::EmitFPFixedU64ToSingle(EmitContext& ctx, IR::Inst* inst) { ctx.reg_alloc.DefineValue(inst, result); } } // namespace Dynarmic::Backend::X64 + +#undef FCODE +#undef ICODE diff --git a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.h b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.h index c99980d617..b25b33101c 100644 --- a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.h +++ b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.h @@ -161,8 +161,7 @@ template<> template<> [[maybe_unused]] Xbyak::RegExp EmitFastmemVAddr(BlockOfCode& code, A64EmitContext& ctx, Xbyak::Label& abort, Xbyak::Reg64 vaddr, bool& require_abort_handling, std::optional tmp) { - const size_t unused_top_bits = 64 - ctx.conf.fastmem_address_space_bits; - + auto const unused_top_bits = 64 - ctx.conf.fastmem_address_space_bits; if (unused_top_bits == 0) { return r13 + vaddr; } else if (ctx.conf.silently_mirror_fastmem) { @@ -306,7 +305,7 @@ const void* EmitWriteMemoryMov(BlockOfCode& code, const Xbyak::RegExp& addr, int code.L(loop); code.lock(); code.cmpxchg16b(xword[addr]); - code.jnz(loop); + code.jnz(loop, code.T_NEAR); break; } default: @@ -373,7 +372,7 @@ void EmitExclusiveTestAndClear(BlockOfCode& code, const UserConfig& conf, Xbyak: Xbyak::Label ok; code.mov(pointer, mcl::bit_cast(GetExclusiveMonitorAddressPointer(conf.global_monitor, processor_index))); code.cmp(qword[pointer], vaddr); - code.jne(ok); + code.jne(ok, code.T_NEAR); code.mov(qword[pointer], tmp); code.L(ok); } diff --git a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_vector_floating_point.cpp b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_vector_floating_point.cpp index b8aa3eb653..88d0786b03 100644 --- a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_vector_floating_point.cpp +++ b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_vector_floating_point.cpp @@ -33,13 +33,6 @@ #include "dynarmic/ir/basic_block.h" #include "dynarmic/ir/microinstruction.h" -namespace Dynarmic::Backend::X64 { - -using namespace Xbyak::util; -namespace mp = mcl::mp; - -namespace { - #define FCODE(NAME) \ [&code](auto... args) { \ if constexpr (fsize == 32) { \ @@ -57,6 +50,13 @@ namespace { } \ } +namespace Dynarmic::Backend::X64 { + +using namespace Xbyak::util; +namespace mp = mcl::mp; + +namespace { + template void MaybeStandardFPSCRValue(BlockOfCode& code, EmitContext& ctx, bool fpcr_controlled, Lambda lambda) { const bool switch_mxcsr = ctx.FPCR(fpcr_controlled) != ctx.FPCR(); @@ -122,11 +122,11 @@ void HandleNaNs(BlockOfCode& code, EmitContext& ctx, bool fpcr_controlled, std:: const Xbyak::Xmm result = xmms[0]; - code.sub(rsp, 8); + code.lea(rsp, ptr[rsp - 8]); ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx())); const size_t stack_space = xmms.size() * 16; - code.sub(rsp, static_cast(stack_space + ABI_SHADOW_SPACE)); + code.lea(rsp, ptr[rsp - static_cast(stack_space + ABI_SHADOW_SPACE)]); for (size_t i = 0; i < xmms.size(); ++i) { code.movaps(xword[rsp + ABI_SHADOW_SPACE + i * 16], xmms[i]); } @@ -443,7 +443,7 @@ void EmitTwoOpFallbackWithoutRegAlloc(BlockOfCode& code, EmitContext& ctx, Xbyak const u32 fpcr = ctx.FPCR(fpcr_controlled).Value(); constexpr u32 stack_space = 2 * 16; - code.sub(rsp, stack_space + ABI_SHADOW_SPACE); + code.lea(rsp, ptr[rsp - (stack_space + ABI_SHADOW_SPACE)]); code.lea(code.ABI_PARAM1, ptr[rsp + ABI_SHADOW_SPACE + 0 * 16]); code.lea(code.ABI_PARAM2, ptr[rsp + ABI_SHADOW_SPACE + 1 * 16]); code.mov(code.ABI_PARAM3.cvt32(), fpcr); @@ -479,7 +479,7 @@ void EmitThreeOpFallbackWithoutRegAlloc(BlockOfCode& code, EmitContext& ctx, Xby #ifdef _WIN32 constexpr u32 stack_space = 4 * 16; - code.sub(rsp, stack_space + ABI_SHADOW_SPACE); + code.lea(rsp, ptr[rsp - (stack_space + ABI_SHADOW_SPACE)]); code.lea(code.ABI_PARAM1, ptr[rsp + ABI_SHADOW_SPACE + 1 * 16]); code.lea(code.ABI_PARAM2, ptr[rsp + ABI_SHADOW_SPACE + 2 * 16]); code.lea(code.ABI_PARAM3, ptr[rsp + ABI_SHADOW_SPACE + 3 * 16]); @@ -488,7 +488,7 @@ void EmitThreeOpFallbackWithoutRegAlloc(BlockOfCode& code, EmitContext& ctx, Xby code.mov(qword[rsp + ABI_SHADOW_SPACE + 0], rax); #else constexpr u32 stack_space = 3 * 16; - code.sub(rsp, stack_space + ABI_SHADOW_SPACE); + code.lea(rsp, ptr[rsp - (stack_space + ABI_SHADOW_SPACE)]); code.lea(code.ABI_PARAM1, ptr[rsp + ABI_SHADOW_SPACE + 0 * 16]); code.lea(code.ABI_PARAM2, ptr[rsp + ABI_SHADOW_SPACE + 1 * 16]); code.lea(code.ABI_PARAM3, ptr[rsp + ABI_SHADOW_SPACE + 2 * 16]); @@ -536,7 +536,7 @@ void EmitFourOpFallbackWithoutRegAlloc(BlockOfCode& code, EmitContext& ctx, Xbya #ifdef _WIN32 constexpr u32 stack_space = 5 * 16; - code.sub(rsp, stack_space + ABI_SHADOW_SPACE); + code.lea(rsp, ptr[rsp - (stack_space + ABI_SHADOW_SPACE)]); code.lea(code.ABI_PARAM1, ptr[rsp + ABI_SHADOW_SPACE + 1 * 16]); code.lea(code.ABI_PARAM2, ptr[rsp + ABI_SHADOW_SPACE + 2 * 16]); code.lea(code.ABI_PARAM3, ptr[rsp + ABI_SHADOW_SPACE + 3 * 16]); @@ -546,7 +546,7 @@ void EmitFourOpFallbackWithoutRegAlloc(BlockOfCode& code, EmitContext& ctx, Xbya code.mov(qword[rsp + ABI_SHADOW_SPACE + 8], rax); #else constexpr u32 stack_space = 4 * 16; - code.sub(rsp, stack_space + ABI_SHADOW_SPACE); + code.lea(rsp, ptr[rsp - (stack_space + ABI_SHADOW_SPACE)]); code.lea(code.ABI_PARAM1, ptr[rsp + ABI_SHADOW_SPACE + 0 * 16]); code.lea(code.ABI_PARAM2, ptr[rsp + ABI_SHADOW_SPACE + 1 * 16]); code.lea(code.ABI_PARAM3, ptr[rsp + ABI_SHADOW_SPACE + 2 * 16]); @@ -1371,7 +1371,7 @@ void EmitFPVectorMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) { ctx.deferred_emits.emplace_back([=, &code, &ctx] { code.L(*fallback); - code.sub(rsp, 8); + code.lea(rsp, ptr[rsp - 8]); ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx())); if (needs_rounding_correction && needs_nan_correction) { EmitFourOpFallbackWithoutRegAlloc(code, ctx, result, xmm_a, xmm_b, xmm_c, EmitFPVectorMulAddFallback, fpcr_controlled); @@ -1635,7 +1635,7 @@ static void EmitRecipStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst* in ctx.deferred_emits.emplace_back([=, &code, &ctx] { code.L(*fallback); - code.sub(rsp, 8); + code.lea(rsp, ptr[rsp - 8]); ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx())); EmitThreeOpFallbackWithoutRegAlloc(code, ctx, result, operand1, operand2, fallback_fn, fpcr_controlled); ABI_PopCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx())); @@ -1812,7 +1812,7 @@ static void EmitRSqrtEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* ins ctx.deferred_emits.emplace_back([=, &code, &ctx] { code.L(*bad_values); - code.sub(rsp, 8); + code.lea(rsp, ptr[rsp - 8]); ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx())); EmitTwoOpFallbackWithoutRegAlloc(code, ctx, result, operand, fallback_fn, fpcr_controlled); ABI_PopCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx())); @@ -1898,7 +1898,7 @@ static void EmitRSqrtStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst* in ctx.deferred_emits.emplace_back([=, &code, &ctx] { code.L(*fallback); - code.sub(rsp, 8); + code.lea(rsp, ptr[rsp - 8]); ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx())); EmitThreeOpFallbackWithoutRegAlloc(code, ctx, result, operand1, operand2, fallback_fn, fpcr_controlled); ABI_PopCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx())); @@ -2180,3 +2180,6 @@ void EmitX64::EmitFPVectorToUnsignedFixed64(EmitContext& ctx, IR::Inst* inst) { } } // namespace Dynarmic::Backend::X64 + +#undef FCODE +#undef ICODE diff --git a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_vector_saturation.cpp b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_vector_saturation.cpp index fb30549fb0..5bab9c93f4 100644 --- a/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_vector_saturation.cpp +++ b/externals/dynarmic/src/dynarmic/backend/x64/emit_x64_vector_saturation.cpp @@ -338,3 +338,6 @@ void EmitX64::EmitVectorUnsignedSaturatedSub64(EmitContext& ctx, IR::Inst* inst) } } // namespace Dynarmic::Backend::X64 + +#undef FCODE +#undef ICODE diff --git a/externals/dynarmic/src/dynarmic/backend/x64/exception_handler_windows.cpp b/externals/dynarmic/src/dynarmic/backend/x64/exception_handler_windows.cpp index a7f964337a..633e1aac9d 100644 --- a/externals/dynarmic/src/dynarmic/backend/x64/exception_handler_windows.cpp +++ b/externals/dynarmic/src/dynarmic/backend/x64/exception_handler_windows.cpp @@ -186,7 +186,7 @@ struct ExceptionHandler::Impl final { code.cmp(code.rax, static_cast(code.GetTotalCodeSize())); code.ja(exception_handler_without_cb); - code.sub(code.rsp, 8); + code.lea(code.rsp, code.ptr[code.rsp - 8]); code.mov(code.ABI_PARAM1, mcl::bit_cast(&cb)); code.mov(code.ABI_PARAM2, code.ABI_PARAM3); code.CallLambda( diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/a32_ir_emitter.h b/externals/dynarmic/src/dynarmic/frontend/A32/a32_ir_emitter.h index 9fde4f8775..38160f96d4 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/a32_ir_emitter.h +++ b/externals/dynarmic/src/dynarmic/frontend/A32/a32_ir_emitter.h @@ -15,7 +15,7 @@ namespace Dynarmic::A32 { -enum class ArchVersion; +enum class ArchVersion : std::uint8_t; enum class CoprocReg; enum class Exception; enum class ExtReg; @@ -27,12 +27,11 @@ enum class Reg; * The user of this class updates `current_location` as appropriate. */ class IREmitter : public IR::IREmitter { + IR::U64 ImmCurrentLocationDescriptor(); public: IREmitter(IR::Block& block, LocationDescriptor descriptor, ArchVersion arch_version) : IR::IREmitter(block), current_location(descriptor), arch_version(arch_version) {} - - LocationDescriptor current_location; - + size_t ArchVersion() const; u32 PC() const; @@ -107,10 +106,9 @@ public: IR::U64 CoprocGetTwoWords(size_t coproc_no, bool two, size_t opc, CoprocReg CRm); void CoprocLoadWords(size_t coproc_no, bool two, bool long_transfer, CoprocReg CRd, const IR::U32& address, bool has_option, u8 option); void CoprocStoreWords(size_t coproc_no, bool two, bool long_transfer, CoprocReg CRd, const IR::U32& address, bool has_option, u8 option); - -private: +public: + LocationDescriptor current_location; enum ArchVersion arch_version; - IR::U64 ImmCurrentLocationDescriptor(); }; } // namespace Dynarmic::A32 diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/decoder/arm.h b/externals/dynarmic/src/dynarmic/frontend/A32/decoder/arm.h index 16ae52e13a..e4cf4a2865 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/decoder/arm.h +++ b/externals/dynarmic/src/dynarmic/frontend/A32/decoder/arm.h @@ -33,13 +33,11 @@ inline size_t ToFastLookupIndexArm(u32 instruction) { } // namespace detail template -ArmDecodeTable GetArmDecodeTable() { +constexpr ArmDecodeTable GetArmDecodeTable() { std::vector> list = { - #define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(ArmMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)), #include "./arm.inc" #undef INST - }; // If a matcher has more bits in its mask it is more specific, so it should come first. @@ -62,9 +60,10 @@ ArmDecodeTable GetArmDecodeTable() { template std::optional>> DecodeArm(u32 instruction) { - static const auto table = GetArmDecodeTable(); - - const auto matches_instruction = [instruction](const auto& matcher) { return matcher.Matches(instruction); }; + alignas(64) static const auto table = GetArmDecodeTable(); + const auto matches_instruction = [instruction](const auto& matcher) { + return matcher.Matches(instruction); + }; const auto& subtable = table[detail::ToFastLookupIndexArm(instruction)]; auto iter = std::find_if(subtable.begin(), subtable.end(), matches_instruction); diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/a32_translate.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/a32_translate.cpp index 97a7f11adf..2e69927ace 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/a32_translate.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/a32_translate.cpp @@ -25,3 +25,51 @@ bool TranslateSingleInstruction(IR::Block& block, LocationDescriptor descriptor, } } // namespace Dynarmic::A32 + +// ls -l | awk '{print "#include \"dynarmic/frontend/A32/translate/impl/" $9 "\""}' +#include "dynarmic/frontend/A32/translate/impl/a32_branch.cpp" +#include "dynarmic/frontend/A32/translate/impl/a32_crc32.cpp" +#include "dynarmic/frontend/A32/translate/impl/a32_exception_generating.cpp" +#include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.cpp" +//#include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" +#include "dynarmic/frontend/A32/translate/impl/asimd_load_store_structures.cpp" +#include "dynarmic/frontend/A32/translate/impl/asimd_misc.cpp" +#include "dynarmic/frontend/A32/translate/impl/asimd_one_reg_modified_immediate.cpp" +#include "dynarmic/frontend/A32/translate/impl/asimd_three_regs.cpp" +#include "dynarmic/frontend/A32/translate/impl/asimd_two_regs_misc.cpp" +#include "dynarmic/frontend/A32/translate/impl/asimd_two_regs_scalar.cpp" +#include "dynarmic/frontend/A32/translate/impl/asimd_two_regs_shift.cpp" +#include "dynarmic/frontend/A32/translate/impl/barrier.cpp" +#include "dynarmic/frontend/A32/translate/impl/coprocessor.cpp" +#include "dynarmic/frontend/A32/translate/impl/data_processing.cpp" +#include "dynarmic/frontend/A32/translate/impl/divide.cpp" +#include "dynarmic/frontend/A32/translate/impl/extension.cpp" +#include "dynarmic/frontend/A32/translate/impl/hint.cpp" +#include "dynarmic/frontend/A32/translate/impl/load_store.cpp" +#include "dynarmic/frontend/A32/translate/impl/misc.cpp" +#include "dynarmic/frontend/A32/translate/impl/multiply.cpp" +#include "dynarmic/frontend/A32/translate/impl/packing.cpp" +#include "dynarmic/frontend/A32/translate/impl/parallel.cpp" +#include "dynarmic/frontend/A32/translate/impl/reversal.cpp" +#include "dynarmic/frontend/A32/translate/impl/saturated.cpp" +#include "dynarmic/frontend/A32/translate/impl/status_register_access.cpp" +#include "dynarmic/frontend/A32/translate/impl/synchronization.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb16.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_branch.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_control.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_coprocessor.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_data_processing_modified_immediate.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_data_processing_plain_binary_immediate.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_data_processing_register.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_data_processing_shifted_register.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_load_byte.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_load_halfword.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_load_store_dual.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_load_store_multiple.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_load_word.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_long_multiply.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_misc.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_multiply.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_parallel.cpp" +#include "dynarmic/frontend/A32/translate/impl/thumb32_store_single_data_item.cpp" +#include "dynarmic/frontend/A32/translate/impl/vfp.cpp" diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/a32_translate_impl.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/a32_translate_impl.cpp index 276f8384e7..c83529f343 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/a32_translate_impl.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/a32_translate_impl.cpp @@ -11,6 +11,10 @@ namespace Dynarmic::A32 { +bool TranslatorVisitor::arm_NOP() { + return true; +} + bool TranslatorVisitor::ArmConditionPassed(Cond cond) { return IsConditionPassed(*this, cond); } diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/a32_translate_impl.h b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/a32_translate_impl.h index 44ac24503c..d33069c6e0 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/a32_translate_impl.h +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/a32_translate_impl.h @@ -258,7 +258,7 @@ struct TranslatorVisitor final { bool arm_CLZ(Cond cond, Reg d, Reg m); bool arm_MOVT(Cond cond, Imm<4> imm4, Reg d, Imm<12> imm12); bool arm_MOVW(Cond cond, Imm<4> imm4, Reg d, Imm<12> imm12); - bool arm_NOP() { return true; } + bool arm_NOP(); bool arm_RBIT(Cond cond, Reg d, Reg m); bool arm_SBFX(Cond cond, Imm<5> widthm1, Reg d, Imm<5> lsb, Reg n); bool arm_SEL(Cond cond, Reg n, Reg d, Reg m); diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_three_regs.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_three_regs.cpp index a69f39bfb6..da8f43f2fb 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_three_regs.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_three_regs.cpp @@ -6,6 +6,7 @@ #include #include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" +#include "dynarmic/frontend/A32/translate/impl/common.h" namespace Dynarmic::A32 { namespace { @@ -17,11 +18,6 @@ enum class Comparison { AbsoluteGT, }; -enum class AccumulateBehavior { - None, - Accumulate, -}; - enum class WidenBehaviour { Second, Both, diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_two_regs_misc.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_two_regs_misc.cpp index 62b9af55a5..ddae1f420b 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_two_regs_misc.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_two_regs_misc.cpp @@ -8,10 +8,11 @@ #include #include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" +#include "dynarmic/frontend/A32/translate/impl/common.h" namespace Dynarmic::A32 { namespace { -enum class Comparison { +enum class ComparisonATRM { EQ, GE, GT, @@ -19,7 +20,7 @@ enum class Comparison { LT, }; -bool CompareWithZero(TranslatorVisitor& v, bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm, Comparison type) { +bool CompareWithZero(TranslatorVisitor& v, bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm, ComparisonATRM type) { if (sz == 0b11 || (F && sz != 0b10)) { return v.UndefinedInstruction(); } @@ -36,15 +37,15 @@ bool CompareWithZero(TranslatorVisitor& v, bool D, size_t sz, size_t Vd, bool F, if (F) { switch (type) { - case Comparison::EQ: + case ComparisonATRM::EQ: return v.ir.FPVectorEqual(32, reg_m, zero, false); - case Comparison::GE: + case ComparisonATRM::GE: return v.ir.FPVectorGreaterEqual(32, reg_m, zero, false); - case Comparison::GT: + case ComparisonATRM::GT: return v.ir.FPVectorGreater(32, reg_m, zero, false); - case Comparison::LE: + case ComparisonATRM::LE: return v.ir.FPVectorGreaterEqual(32, zero, reg_m, false); - case Comparison::LT: + case ComparisonATRM::LT: return v.ir.FPVectorGreater(32, zero, reg_m, false); } @@ -67,11 +68,6 @@ bool CompareWithZero(TranslatorVisitor& v, bool D, size_t sz, size_t Vd, bool F, return true; } -enum class AccumulateBehavior { - None, - Accumulate, -}; - bool PairedAddOperation(TranslatorVisitor& v, bool D, size_t sz, size_t Vd, bool op, bool Q, bool M, size_t Vm, AccumulateBehavior accumulate) { if (sz == 0b11) { return v.UndefinedInstruction(); @@ -385,23 +381,23 @@ bool TranslatorVisitor::asimd_VQNEG(bool D, size_t sz, size_t Vd, bool Q, bool M } bool TranslatorVisitor::asimd_VCGT_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) { - return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, Comparison::GT); + return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, ComparisonATRM::GT); } bool TranslatorVisitor::asimd_VCGE_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) { - return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, Comparison::GE); + return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, ComparisonATRM::GE); } bool TranslatorVisitor::asimd_VCEQ_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) { - return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, Comparison::EQ); + return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, ComparisonATRM::EQ); } bool TranslatorVisitor::asimd_VCLE_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) { - return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, Comparison::LE); + return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, ComparisonATRM::LE); } bool TranslatorVisitor::asimd_VCLT_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) { - return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, Comparison::LT); + return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, ComparisonATRM::LT); } bool TranslatorVisitor::asimd_VABS(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) { diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_two_regs_shift.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_two_regs_shift.cpp index b7300f91e4..cbdfea04ce 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_two_regs_shift.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/asimd_two_regs_shift.cpp @@ -16,7 +16,7 @@ enum class Accumulating { Accumulate }; -enum class Rounding { +enum class RoundingATRS { None, Round, }; @@ -32,7 +32,7 @@ enum class Signedness { Unsigned }; -IR::U128 PerformRoundingCorrection(TranslatorVisitor& v, size_t esize, u64 round_value, IR::U128 original, IR::U128 shifted) { +IR::U128 PerformRoundingATRSCorrection(TranslatorVisitor& v, size_t esize, u64 round_value, IR::U128 original, IR::U128 shifted) { const auto round_const = v.ir.VectorBroadcast(esize, v.I(esize, round_value)); const auto round_correction = v.ir.VectorEqual(esize, v.ir.VectorAnd(original, round_const), round_const); return v.ir.VectorSub(esize, shifted, round_correction); @@ -58,7 +58,7 @@ std::pair ElementSizeAndShiftAmount(bool right_shift, bool L, si } } -bool ShiftRight(TranslatorVisitor& v, bool U, bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm, Accumulating accumulate, Rounding rounding) { +bool ShiftRight(TranslatorVisitor& v, bool U, bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm, Accumulating accumulate, RoundingATRS RoundingATRS) { if (!L && mcl::bit::get_bits<3, 5>(imm6) == 0) { return v.DecodeError(); } @@ -75,9 +75,9 @@ bool ShiftRight(TranslatorVisitor& v, bool U, bool D, size_t imm6, size_t Vd, bo auto result = U ? v.ir.VectorLogicalShiftRight(esize, reg_m, static_cast(shift_amount)) : v.ir.VectorArithmeticShiftRight(esize, reg_m, static_cast(shift_amount)); - if (rounding == Rounding::Round) { + if (RoundingATRS == RoundingATRS::Round) { const u64 round_value = 1ULL << (shift_amount - 1); - result = PerformRoundingCorrection(v, esize, round_value, reg_m, result); + result = PerformRoundingATRSCorrection(v, esize, round_value, reg_m, result); } if (accumulate == Accumulating::Accumulate) { @@ -89,7 +89,7 @@ bool ShiftRight(TranslatorVisitor& v, bool U, bool D, size_t imm6, size_t Vd, bo return true; } -bool ShiftRightNarrowing(TranslatorVisitor& v, bool D, size_t imm6, size_t Vd, bool M, size_t Vm, Rounding rounding, Narrowing narrowing, Signedness signedness) { +bool ShiftRightNarrowing(TranslatorVisitor& v, bool D, size_t imm6, size_t Vd, bool M, size_t Vm, RoundingATRS RoundingATRS, Narrowing narrowing, Signedness signedness) { if (mcl::bit::get_bits<3, 5>(imm6) == 0) { return v.DecodeError(); } @@ -113,9 +113,9 @@ bool ShiftRightNarrowing(TranslatorVisitor& v, bool D, size_t imm6, size_t Vd, b return v.ir.VectorLogicalShiftRight(source_esize, reg_m, shift_amount); }(); - if (rounding == Rounding::Round) { + if (RoundingATRS == RoundingATRS::Round) { const u64 round_value = 1ULL << (shift_amount - 1); - wide_result = PerformRoundingCorrection(v, source_esize, round_value, reg_m, wide_result); + wide_result = PerformRoundingATRSCorrection(v, source_esize, round_value, reg_m, wide_result); } const auto result = [&] { @@ -141,22 +141,22 @@ bool ShiftRightNarrowing(TranslatorVisitor& v, bool D, size_t imm6, size_t Vd, b bool TranslatorVisitor::asimd_SHR(bool U, bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm) { return ShiftRight(*this, U, D, imm6, Vd, L, Q, M, Vm, - Accumulating::None, Rounding::None); + Accumulating::None, RoundingATRS::None); } bool TranslatorVisitor::asimd_SRA(bool U, bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm) { return ShiftRight(*this, U, D, imm6, Vd, L, Q, M, Vm, - Accumulating::Accumulate, Rounding::None); + Accumulating::Accumulate, RoundingATRS::None); } bool TranslatorVisitor::asimd_VRSHR(bool U, bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm) { return ShiftRight(*this, U, D, imm6, Vd, L, Q, M, Vm, - Accumulating::None, Rounding::Round); + Accumulating::None, RoundingATRS::Round); } bool TranslatorVisitor::asimd_VRSRA(bool U, bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm) { return ShiftRight(*this, U, D, imm6, Vd, L, Q, M, Vm, - Accumulating::Accumulate, Rounding::Round); + Accumulating::Accumulate, RoundingATRS::Round); } bool TranslatorVisitor::asimd_VSRI(bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm) { @@ -271,32 +271,32 @@ bool TranslatorVisitor::asimd_VSHL(bool D, size_t imm6, size_t Vd, bool L, bool bool TranslatorVisitor::asimd_VSHRN(bool D, size_t imm6, size_t Vd, bool M, size_t Vm) { return ShiftRightNarrowing(*this, D, imm6, Vd, M, Vm, - Rounding::None, Narrowing::Truncation, Signedness::Unsigned); + RoundingATRS::None, Narrowing::Truncation, Signedness::Unsigned); } bool TranslatorVisitor::asimd_VRSHRN(bool D, size_t imm6, size_t Vd, bool M, size_t Vm) { return ShiftRightNarrowing(*this, D, imm6, Vd, M, Vm, - Rounding::Round, Narrowing::Truncation, Signedness::Unsigned); + RoundingATRS::Round, Narrowing::Truncation, Signedness::Unsigned); } bool TranslatorVisitor::asimd_VQRSHRUN(bool D, size_t imm6, size_t Vd, bool M, size_t Vm) { return ShiftRightNarrowing(*this, D, imm6, Vd, M, Vm, - Rounding::Round, Narrowing::SaturateToUnsigned, Signedness::Signed); + RoundingATRS::Round, Narrowing::SaturateToUnsigned, Signedness::Signed); } bool TranslatorVisitor::asimd_VQSHRUN(bool D, size_t imm6, size_t Vd, bool M, size_t Vm) { return ShiftRightNarrowing(*this, D, imm6, Vd, M, Vm, - Rounding::None, Narrowing::SaturateToUnsigned, Signedness::Signed); + RoundingATRS::None, Narrowing::SaturateToUnsigned, Signedness::Signed); } bool TranslatorVisitor::asimd_VQSHRN(bool U, bool D, size_t imm6, size_t Vd, bool M, size_t Vm) { return ShiftRightNarrowing(*this, D, imm6, Vd, M, Vm, - Rounding::None, U ? Narrowing::SaturateToUnsigned : Narrowing::SaturateToSigned, U ? Signedness::Unsigned : Signedness::Signed); + RoundingATRS::None, U ? Narrowing::SaturateToUnsigned : Narrowing::SaturateToSigned, U ? Signedness::Unsigned : Signedness::Signed); } bool TranslatorVisitor::asimd_VQRSHRN(bool U, bool D, size_t imm6, size_t Vd, bool M, size_t Vm) { return ShiftRightNarrowing(*this, D, imm6, Vd, M, Vm, - Rounding::Round, U ? Narrowing::SaturateToUnsigned : Narrowing::SaturateToSigned, U ? Signedness::Unsigned : Signedness::Signed); + RoundingATRS::Round, U ? Narrowing::SaturateToUnsigned : Narrowing::SaturateToSigned, U ? Signedness::Unsigned : Signedness::Signed); } bool TranslatorVisitor::asimd_VSHLL(bool U, bool D, size_t imm6, size_t Vd, bool M, size_t Vm) { diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/common.h b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/common.h new file mode 100644 index 0000000000..39b2c4355c --- /dev/null +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/common.h @@ -0,0 +1,31 @@ +#pragma once + +#include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" + +namespace Dynarmic::A32 { + +static inline IR::U32 Pack2x16To1x32(A32::IREmitter& ir, IR::U32 lo, IR::U32 hi) noexcept { + return ir.Or(ir.And(lo, ir.Imm32(0xFFFF)), ir.LogicalShiftLeft(hi, ir.Imm8(16), ir.Imm1(0)).result); +} + +static inline IR::U16 MostSignificantHalf(A32::IREmitter& ir, IR::U32 value) noexcept { + return ir.LeastSignificantHalf(ir.LogicalShiftRight(value, ir.Imm8(16), ir.Imm1(0)).result); +} + +static inline IR::U32 Rotate(A32::IREmitter& ir, Reg m, SignExtendRotation rotate) noexcept { + const u8 rotate_by = static_cast(static_cast(rotate) * 8); + return ir.RotateRight(ir.GetRegister(m), ir.Imm8(rotate_by), ir.Imm1(0)).result; +} + +static inline bool ITBlockCheck(const A32::IREmitter& ir) noexcept { + return ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock(); +} + +using ExtensionFunctionU16 = IR::U32 (IREmitter::*)(const IR::U16&); + +enum class AccumulateBehavior { + None, + Accumulate, +}; + +} diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/extension.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/extension.cpp index 3ee6a670f0..518f8b944e 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/extension.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/extension.cpp @@ -4,14 +4,10 @@ */ #include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" +#include "dynarmic/frontend/A32/translate/impl/common.h" namespace Dynarmic::A32 { -static IR::U32 Rotate(A32::IREmitter& ir, Reg m, SignExtendRotation rotate) { - const u8 rotate_by = static_cast(static_cast(rotate) * 8); - return ir.RotateRight(ir.GetRegister(m), ir.Imm8(rotate_by), ir.Imm1(0)).result; -} - // SXTAB , , {, } bool TranslatorVisitor::arm_SXTAB(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) { if (d == Reg::PC || m == Reg::PC) { diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/saturated.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/saturated.cpp index 41db115044..2371e54ae7 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/saturated.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/saturated.cpp @@ -4,17 +4,10 @@ */ #include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" +#include "dynarmic/frontend/A32/translate/impl/common.h" namespace Dynarmic::A32 { -static IR::U32 Pack2x16To1x32(A32::IREmitter& ir, IR::U32 lo, IR::U32 hi) { - return ir.Or(ir.And(lo, ir.Imm32(0xFFFF)), ir.LogicalShiftLeft(hi, ir.Imm8(16), ir.Imm1(0)).result); -} - -static IR::U16 MostSignificantHalf(A32::IREmitter& ir, IR::U32 value) { - return ir.LeastSignificantHalf(ir.LogicalShiftRight(value, ir.Imm8(16), ir.Imm1(0)).result); -} - // Saturation instructions // SSAT , #, {, } diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_plain_binary_immediate.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_plain_binary_immediate.cpp index 1a6767dbee..14a3fef2d1 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_plain_binary_immediate.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_plain_binary_immediate.cpp @@ -7,15 +7,9 @@ #include #include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" +#include "dynarmic/frontend/A32/translate/impl/common.h" namespace Dynarmic::A32 { -static IR::U32 Pack2x16To1x32(A32::IREmitter& ir, IR::U32 lo, IR::U32 hi) { - return ir.Or(ir.And(lo, ir.Imm32(0xFFFF)), ir.LogicalShiftLeft(hi, ir.Imm8(16), ir.Imm1(0)).result); -} - -static IR::U16 MostSignificantHalf(A32::IREmitter& ir, IR::U32 value) { - return ir.LeastSignificantHalf(ir.LogicalShiftRight(value, ir.Imm8(16), ir.Imm1(0)).result); -} using SaturationFunction = IR::ResultAndOverflow (IREmitter::*)(const IR::U32&, size_t); diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_register.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_register.cpp index 1dda532d92..1498ac6c7b 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_register.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_data_processing_register.cpp @@ -4,13 +4,10 @@ */ #include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" +#include "dynarmic/frontend/A32/translate/impl/common.h" namespace Dynarmic::A32 { namespace { -IR::U32 Rotate(A32::IREmitter& ir, Reg m, SignExtendRotation rotate) { - const u8 rotate_by = static_cast(static_cast(rotate) * 8); - return ir.RotateRight(ir.GetRegister(m), ir.Imm8(rotate_by), ir.Imm1(0)).result; -} using ShiftFunction = IR::ResultAndCarry (IREmitter::*)(const IR::U32&, const IR::U8&, const IR::U1&); diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_byte.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_byte.cpp index 42b2ebf4aa..d309d42d66 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_byte.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_byte.cpp @@ -25,9 +25,9 @@ static bool PLIHandler(TranslatorVisitor& v) { return v.RaiseException(Exception::PreloadInstruction); } -using ExtensionFunction = IR::U32 (IREmitter::*)(const IR::U8&); +using ExtensionFunctionU8 = IR::U32 (IREmitter::*)(const IR::U8&); -static bool LoadByteLiteral(TranslatorVisitor& v, bool U, Reg t, Imm<12> imm12, ExtensionFunction ext_fn) { +static bool LoadByteLiteral(TranslatorVisitor& v, bool U, Reg t, Imm<12> imm12, ExtensionFunctionU8 ext_fn) { const u32 imm32 = imm12.ZeroExtend(); const u32 base = v.ir.AlignPC(4); const u32 address = U ? (base + imm32) : (base - imm32); @@ -37,7 +37,7 @@ static bool LoadByteLiteral(TranslatorVisitor& v, bool U, Reg t, Imm<12> imm12, return true; } -static bool LoadByteRegister(TranslatorVisitor& v, Reg n, Reg t, Imm<2> imm2, Reg m, ExtensionFunction ext_fn) { +static bool LoadByteRegister(TranslatorVisitor& v, Reg n, Reg t, Imm<2> imm2, Reg m, ExtensionFunctionU8 ext_fn) { if (m == Reg::PC) { return v.UnpredictableInstruction(); } @@ -52,7 +52,7 @@ static bool LoadByteRegister(TranslatorVisitor& v, Reg n, Reg t, Imm<2> imm2, Re return true; } -static bool LoadByteImmediate(TranslatorVisitor& v, Reg n, Reg t, bool P, bool U, bool W, Imm<12> imm12, ExtensionFunction ext_fn) { +static bool LoadByteImmediate(TranslatorVisitor& v, Reg n, Reg t, bool P, bool U, bool W, Imm<12> imm12, ExtensionFunctionU8 ext_fn) { const u32 imm32 = imm12.ZeroExtend(); const IR::U32 reg_n = v.ir.GetRegister(n); const IR::U32 offset_address = U ? v.ir.Add(reg_n, v.ir.Imm32(imm32)) diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_halfword.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_halfword.cpp index 5b9f1639af..d8a043e553 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_halfword.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_halfword.cpp @@ -4,12 +4,11 @@ */ #include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" +#include "dynarmic/frontend/A32/translate/impl/common.h" namespace Dynarmic::A32 { -using ExtensionFunction = IR::U32 (IREmitter::*)(const IR::U16&); - -static bool LoadHalfLiteral(TranslatorVisitor& v, bool U, Reg t, Imm<12> imm12, ExtensionFunction ext_fn) { +static bool LoadHalfLiteral(TranslatorVisitor& v, bool U, Reg t, Imm<12> imm12, ExtensionFunctionU16 ext_fn) { const auto imm32 = imm12.ZeroExtend(); const auto base = v.ir.AlignPC(4); const auto address = U ? (base + imm32) : (base - imm32); @@ -19,7 +18,7 @@ static bool LoadHalfLiteral(TranslatorVisitor& v, bool U, Reg t, Imm<12> imm12, return true; } -static bool LoadHalfRegister(TranslatorVisitor& v, Reg n, Reg t, Imm<2> imm2, Reg m, ExtensionFunction ext_fn) { +static bool LoadHalfRegister(TranslatorVisitor& v, Reg n, Reg t, Imm<2> imm2, Reg m, ExtensionFunctionU16 ext_fn) { if (m == Reg::PC) { return v.UnpredictableInstruction(); } @@ -34,7 +33,7 @@ static bool LoadHalfRegister(TranslatorVisitor& v, Reg n, Reg t, Imm<2> imm2, Re return true; } -static bool LoadHalfImmediate(TranslatorVisitor& v, Reg n, Reg t, bool P, bool U, bool W, Imm<12> imm12, ExtensionFunction ext_fn) { +static bool LoadHalfImmediate(TranslatorVisitor& v, Reg n, Reg t, bool P, bool U, bool W, Imm<12> imm12, ExtensionFunctionU16 ext_fn) { const u32 imm32 = imm12.ZeroExtend(); const IR::U32 reg_n = v.ir.GetRegister(n); const IR::U32 offset_address = U ? v.ir.Add(reg_n, v.ir.Imm32(imm32)) diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_store_dual.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_store_dual.cpp index 17d4285c23..eb574d773c 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_store_dual.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_store_dual.cpp @@ -6,11 +6,9 @@ #include #include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" +#include "dynarmic/frontend/A32/translate/impl/common.h" namespace Dynarmic::A32 { -static bool ITBlockCheck(const A32::IREmitter& ir) { - return ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock(); -} static bool TableBranch(TranslatorVisitor& v, Reg n, Reg m, bool half) { if (m == Reg::PC) { diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_store_multiple.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_store_multiple.cpp index 2bc782b973..d446fbf3dd 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_store_multiple.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_store_multiple.cpp @@ -6,11 +6,9 @@ #include #include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" +#include "dynarmic/frontend/A32/translate/impl/common.h" namespace Dynarmic::A32 { -static bool ITBlockCheck(const A32::IREmitter& ir) { - return ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock(); -} static bool LDMHelper(A32::IREmitter& ir, bool W, Reg n, u32 list, const IR::U32& start_address, const IR::U32& writeback_address) { auto address = start_address; diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_word.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_word.cpp index b92e27fc66..b7556a8caa 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_word.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_load_word.cpp @@ -4,11 +4,9 @@ */ #include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" +#include "dynarmic/frontend/A32/translate/impl/common.h" namespace Dynarmic::A32 { -static bool ITBlockCheck(const A32::IREmitter& ir) { - return ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock(); -} bool TranslatorVisitor::thumb32_LDR_lit(bool U, Reg t, Imm<12> imm12) { if (t == Reg::PC && ITBlockCheck(ir)) { diff --git a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_parallel.cpp b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_parallel.cpp index 654940967d..64d57e917d 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_parallel.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_parallel.cpp @@ -4,15 +4,9 @@ */ #include "dynarmic/frontend/A32/translate/impl/a32_translate_impl.h" +#include "dynarmic/frontend/A32/translate/impl/common.h" namespace Dynarmic::A32 { -static IR::U32 Pack2x16To1x32(A32::IREmitter& ir, IR::U32 lo, IR::U32 hi) { - return ir.Or(ir.And(lo, ir.Imm32(0xFFFF)), ir.LogicalShiftLeft(hi, ir.Imm8(16), ir.Imm1(0)).result); -} - -static IR::U16 MostSignificantHalf(A32::IREmitter& ir, IR::U32 value) { - return ir.LeastSignificantHalf(ir.LogicalShiftRight(value, ir.Imm8(16), ir.Imm1(0)).result); -} bool TranslatorVisitor::thumb32_SADD8(Reg n, Reg d, Reg m) { if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/a64_ir_emitter.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/a64_ir_emitter.cpp index 3f5a70bdc0..68f536c8d5 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/a64_ir_emitter.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/a64_ir_emitter.cpp @@ -5,261 +5,7 @@ #include "dynarmic/frontend/A64/a64_ir_emitter.h" -#include - -#include "dynarmic/ir/opcodes.h" - namespace Dynarmic::A64 { -using Opcode = IR::Opcode; - -u64 IREmitter::PC() const { - return current_location->PC(); -} - -u64 IREmitter::AlignPC(size_t alignment) const { - const u64 pc = PC(); - return static_cast(pc - pc % alignment); -} - -void IREmitter::SetCheckBit(const IR::U1& value) { - Inst(Opcode::A64SetCheckBit, value); -} - -IR::U1 IREmitter::GetCFlag() { - return Inst(Opcode::A64GetCFlag); -} - -IR::U32 IREmitter::GetNZCVRaw() { - return Inst(Opcode::A64GetNZCVRaw); -} - -void IREmitter::SetNZCVRaw(IR::U32 value) { - Inst(Opcode::A64SetNZCVRaw, value); -} - -void IREmitter::SetNZCV(const IR::NZCV& nzcv) { - Inst(Opcode::A64SetNZCV, nzcv); -} - -void IREmitter::CallSupervisor(u32 imm) { - Inst(Opcode::A64CallSupervisor, Imm32(imm)); -} - -void IREmitter::ExceptionRaised(Exception exception) { - Inst(Opcode::A64ExceptionRaised, Imm64(PC()), Imm64(static_cast(exception))); -} - -void IREmitter::DataCacheOperationRaised(DataCacheOperation op, const IR::U64& value) { - Inst(Opcode::A64DataCacheOperationRaised, ImmCurrentLocationDescriptor(), Imm64(static_cast(op)), value); -} - -void IREmitter::InstructionCacheOperationRaised(InstructionCacheOperation op, const IR::U64& value) { - Inst(Opcode::A64InstructionCacheOperationRaised, Imm64(static_cast(op)), value); -} - -void IREmitter::DataSynchronizationBarrier() { - Inst(Opcode::A64DataSynchronizationBarrier); -} - -void IREmitter::DataMemoryBarrier() { - Inst(Opcode::A64DataMemoryBarrier); -} - -void IREmitter::InstructionSynchronizationBarrier() { - Inst(Opcode::A64InstructionSynchronizationBarrier); -} - -IR::U32 IREmitter::GetCNTFRQ() { - return Inst(Opcode::A64GetCNTFRQ); -} - -IR::U64 IREmitter::GetCNTPCT() { - return Inst(Opcode::A64GetCNTPCT); -} - -IR::U32 IREmitter::GetCTR() { - return Inst(Opcode::A64GetCTR); -} - -IR::U32 IREmitter::GetDCZID() { - return Inst(Opcode::A64GetDCZID); -} - -IR::U64 IREmitter::GetTPIDR() { - return Inst(Opcode::A64GetTPIDR); -} - -void IREmitter::SetTPIDR(const IR::U64& value) { - Inst(Opcode::A64SetTPIDR, value); -} - -IR::U64 IREmitter::GetTPIDRRO() { - return Inst(Opcode::A64GetTPIDRRO); -} - -void IREmitter::ClearExclusive() { - Inst(Opcode::A64ClearExclusive); -} - -IR::U8 IREmitter::ReadMemory8(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ReadMemory8, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); -} - -IR::U16 IREmitter::ReadMemory16(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ReadMemory16, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); -} - -IR::U32 IREmitter::ReadMemory32(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ReadMemory32, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); -} - -IR::U64 IREmitter::ReadMemory64(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ReadMemory64, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); -} - -IR::U128 IREmitter::ReadMemory128(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ReadMemory128, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); -} - -IR::U8 IREmitter::ExclusiveReadMemory8(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveReadMemory8, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); -} - -IR::U16 IREmitter::ExclusiveReadMemory16(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveReadMemory16, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); -} - -IR::U32 IREmitter::ExclusiveReadMemory32(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveReadMemory32, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); -} - -IR::U64 IREmitter::ExclusiveReadMemory64(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveReadMemory64, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); -} - -IR::U128 IREmitter::ExclusiveReadMemory128(const IR::U64& vaddr, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveReadMemory128, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); -} - -void IREmitter::WriteMemory8(const IR::U64& vaddr, const IR::U8& value, IR::AccType acc_type) { - Inst(Opcode::A64WriteMemory8, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); -} - -void IREmitter::WriteMemory16(const IR::U64& vaddr, const IR::U16& value, IR::AccType acc_type) { - Inst(Opcode::A64WriteMemory16, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); -} - -void IREmitter::WriteMemory32(const IR::U64& vaddr, const IR::U32& value, IR::AccType acc_type) { - Inst(Opcode::A64WriteMemory32, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); -} - -void IREmitter::WriteMemory64(const IR::U64& vaddr, const IR::U64& value, IR::AccType acc_type) { - Inst(Opcode::A64WriteMemory64, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); -} - -void IREmitter::WriteMemory128(const IR::U64& vaddr, const IR::U128& value, IR::AccType acc_type) { - Inst(Opcode::A64WriteMemory128, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); -} - -IR::U32 IREmitter::ExclusiveWriteMemory8(const IR::U64& vaddr, const IR::U8& value, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveWriteMemory8, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); -} - -IR::U32 IREmitter::ExclusiveWriteMemory16(const IR::U64& vaddr, const IR::U16& value, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveWriteMemory16, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); -} - -IR::U32 IREmitter::ExclusiveWriteMemory32(const IR::U64& vaddr, const IR::U32& value, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveWriteMemory32, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); -} - -IR::U32 IREmitter::ExclusiveWriteMemory64(const IR::U64& vaddr, const IR::U64& value, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveWriteMemory64, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); -} - -IR::U32 IREmitter::ExclusiveWriteMemory128(const IR::U64& vaddr, const IR::U128& value, IR::AccType acc_type) { - return Inst(Opcode::A64ExclusiveWriteMemory128, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); -} - -IR::U32 IREmitter::GetW(Reg reg) { - if (reg == Reg::ZR) - return Imm32(0); - return Inst(Opcode::A64GetW, IR::Value(reg)); -} - -IR::U64 IREmitter::GetX(Reg reg) { - if (reg == Reg::ZR) - return Imm64(0); - return Inst(Opcode::A64GetX, IR::Value(reg)); -} - -IR::U128 IREmitter::GetS(Vec vec) { - return Inst(Opcode::A64GetS, IR::Value(vec)); -} - -IR::U128 IREmitter::GetD(Vec vec) { - return Inst(Opcode::A64GetD, IR::Value(vec)); -} - -IR::U128 IREmitter::GetQ(Vec vec) { - return Inst(Opcode::A64GetQ, IR::Value(vec)); -} - -IR::U64 IREmitter::GetSP() { - return Inst(Opcode::A64GetSP); -} - -IR::U32 IREmitter::GetFPCR() { - return Inst(Opcode::A64GetFPCR); -} - -IR::U32 IREmitter::GetFPSR() { - return Inst(Opcode::A64GetFPSR); -} - -void IREmitter::SetW(const Reg reg, const IR::U32& value) { - if (reg == Reg::ZR) - return; - Inst(Opcode::A64SetW, IR::Value(reg), value); -} - -void IREmitter::SetX(const Reg reg, const IR::U64& value) { - if (reg == Reg::ZR) - return; - Inst(Opcode::A64SetX, IR::Value(reg), value); -} - -void IREmitter::SetS(const Vec vec, const IR::U128& value) { - Inst(Opcode::A64SetS, IR::Value(vec), value); -} - -void IREmitter::SetD(const Vec vec, const IR::U128& value) { - Inst(Opcode::A64SetD, IR::Value(vec), value); -} - -void IREmitter::SetQ(const Vec vec, const IR::U128& value) { - Inst(Opcode::A64SetQ, IR::Value(vec), value); -} - -void IREmitter::SetSP(const IR::U64& value) { - Inst(Opcode::A64SetSP, value); -} - -void IREmitter::SetFPCR(const IR::U32& value) { - Inst(Opcode::A64SetFPCR, value); -} - -void IREmitter::SetFPSR(const IR::U32& value) { - Inst(Opcode::A64SetFPSR, value); -} - -void IREmitter::SetPC(const IR::U64& value) { - Inst(Opcode::A64SetPC, value); -} - -IR::U64 IREmitter::ImmCurrentLocationDescriptor() { - return Imm64(IR::LocationDescriptor{*current_location}.Value()); -} } // namespace Dynarmic::A64 diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/a64_ir_emitter.h b/externals/dynarmic/src/dynarmic/frontend/A64/a64_ir_emitter.h index 7fc8bea7c4..3bf633a3da 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/a64_ir_emitter.h +++ b/externals/dynarmic/src/dynarmic/frontend/A64/a64_ir_emitter.h @@ -8,12 +8,14 @@ #include #include +#include #include "dynarmic/frontend/A64/a64_location_descriptor.h" #include "dynarmic/frontend/A64/a64_types.h" #include "dynarmic/interface/A64/config.h" #include "dynarmic/ir/ir_emitter.h" #include "dynarmic/ir/value.h" +#include "dynarmic/ir/opcodes.h" namespace Dynarmic::A64 { @@ -24,79 +26,262 @@ namespace Dynarmic::A64 { */ class IREmitter : public IR::IREmitter { public: - explicit IREmitter(IR::Block& block) - : IR::IREmitter(block) {} - explicit IREmitter(IR::Block& block, LocationDescriptor descriptor) - : IR::IREmitter(block), current_location(descriptor) {} + explicit IREmitter(IR::Block& block) : IR::IREmitter(block) {} + explicit IREmitter(IR::Block& block, LocationDescriptor descriptor) : IR::IREmitter(block), current_location(descriptor) {} std::optional current_location; - u64 PC() const; - u64 AlignPC(size_t alignment) const; + using Opcode = IR::Opcode; - void SetCheckBit(const IR::U1& value); - IR::U1 GetCFlag(); - IR::U32 GetNZCVRaw(); - void SetNZCVRaw(IR::U32 value); - void SetNZCV(const IR::NZCV& nzcv); + u64 PC() const noexcept { + return current_location->PC(); + } - void CallSupervisor(u32 imm); - void ExceptionRaised(Exception exception); - void DataCacheOperationRaised(DataCacheOperation op, const IR::U64& value); - void InstructionCacheOperationRaised(InstructionCacheOperation op, const IR::U64& value); - void DataSynchronizationBarrier(); - void DataMemoryBarrier(); - void InstructionSynchronizationBarrier(); - IR::U32 GetCNTFRQ(); - IR::U64 GetCNTPCT(); // TODO: Ensure sub-basic-block cycle counts are updated before this. - IR::U32 GetCTR(); - IR::U32 GetDCZID(); - IR::U64 GetTPIDR(); - IR::U64 GetTPIDRRO(); - void SetTPIDR(const IR::U64& value); + u64 AlignPC(size_t alignment) const noexcept { + const u64 pc = PC(); + return static_cast(pc - pc % alignment); + } - void ClearExclusive(); - IR::U8 ReadMemory8(const IR::U64& vaddr, IR::AccType acc_type); - IR::U16 ReadMemory16(const IR::U64& vaddr, IR::AccType acc_type); - IR::U32 ReadMemory32(const IR::U64& vaddr, IR::AccType acc_type); - IR::U64 ReadMemory64(const IR::U64& vaddr, IR::AccType acc_type); - IR::U128 ReadMemory128(const IR::U64& vaddr, IR::AccType acc_type); - IR::U8 ExclusiveReadMemory8(const IR::U64& vaddr, IR::AccType acc_type); - IR::U16 ExclusiveReadMemory16(const IR::U64& vaddr, IR::AccType acc_type); - IR::U32 ExclusiveReadMemory32(const IR::U64& vaddr, IR::AccType acc_type); - IR::U64 ExclusiveReadMemory64(const IR::U64& vaddr, IR::AccType acc_type); - IR::U128 ExclusiveReadMemory128(const IR::U64& vaddr, IR::AccType acc_type); - void WriteMemory8(const IR::U64& vaddr, const IR::U8& value, IR::AccType acc_type); - void WriteMemory16(const IR::U64& vaddr, const IR::U16& value, IR::AccType acc_type); - void WriteMemory32(const IR::U64& vaddr, const IR::U32& value, IR::AccType acc_type); - void WriteMemory64(const IR::U64& vaddr, const IR::U64& value, IR::AccType acc_type); - void WriteMemory128(const IR::U64& vaddr, const IR::U128& value, IR::AccType acc_type); - IR::U32 ExclusiveWriteMemory8(const IR::U64& vaddr, const IR::U8& value, IR::AccType acc_type); - IR::U32 ExclusiveWriteMemory16(const IR::U64& vaddr, const IR::U16& value, IR::AccType acc_type); - IR::U32 ExclusiveWriteMemory32(const IR::U64& vaddr, const IR::U32& value, IR::AccType acc_type); - IR::U32 ExclusiveWriteMemory64(const IR::U64& vaddr, const IR::U64& value, IR::AccType acc_type); - IR::U32 ExclusiveWriteMemory128(const IR::U64& vaddr, const IR::U128& value, IR::AccType acc_type); + void SetCheckBit(const IR::U1& value) noexcept { + Inst(Opcode::A64SetCheckBit, value); + } - IR::U32 GetW(Reg source_reg); - IR::U64 GetX(Reg source_reg); - IR::U128 GetS(Vec source_vec); - IR::U128 GetD(Vec source_vec); - IR::U128 GetQ(Vec source_vec); - IR::U64 GetSP(); - IR::U32 GetFPCR(); - IR::U32 GetFPSR(); - void SetW(Reg dest_reg, const IR::U32& value); - void SetX(Reg dest_reg, const IR::U64& value); - void SetS(Vec dest_vec, const IR::U128& value); - void SetD(Vec dest_vec, const IR::U128& value); - void SetQ(Vec dest_vec, const IR::U128& value); - void SetSP(const IR::U64& value); - void SetFPCR(const IR::U32& value); - void SetFPSR(const IR::U32& value); - void SetPC(const IR::U64& value); + IR::U1 GetCFlag() noexcept { + return Inst(Opcode::A64GetCFlag); + } + + IR::U32 GetNZCVRaw() noexcept { + return Inst(Opcode::A64GetNZCVRaw); + } + + void SetNZCVRaw(IR::U32 value) noexcept { + Inst(Opcode::A64SetNZCVRaw, value); + } + + void SetNZCV(const IR::NZCV& nzcv) noexcept { + Inst(Opcode::A64SetNZCV, nzcv); + } + + void CallSupervisor(u32 imm) noexcept { + Inst(Opcode::A64CallSupervisor, Imm32(imm)); + } + + void ExceptionRaised(Exception exception) noexcept { + Inst(Opcode::A64ExceptionRaised, Imm64(PC()), Imm64(static_cast(exception))); + } + + void DataCacheOperationRaised(DataCacheOperation op, const IR::U64& value) noexcept { + Inst(Opcode::A64DataCacheOperationRaised, ImmCurrentLocationDescriptor(), Imm64(static_cast(op)), value); + } + + void InstructionCacheOperationRaised(InstructionCacheOperation op, const IR::U64& value) noexcept { + Inst(Opcode::A64InstructionCacheOperationRaised, Imm64(static_cast(op)), value); + } + + void DataSynchronizationBarrier() noexcept { + Inst(Opcode::A64DataSynchronizationBarrier); + } + + void DataMemoryBarrier() noexcept { + Inst(Opcode::A64DataMemoryBarrier); + } + + void InstructionSynchronizationBarrier() noexcept { + Inst(Opcode::A64InstructionSynchronizationBarrier); + } + + IR::U32 GetCNTFRQ() noexcept { + return Inst(Opcode::A64GetCNTFRQ); + } + + IR::U64 GetCNTPCT() noexcept { + return Inst(Opcode::A64GetCNTPCT); + } + + IR::U32 GetCTR() noexcept { + return Inst(Opcode::A64GetCTR); + } + + IR::U32 GetDCZID() noexcept { + return Inst(Opcode::A64GetDCZID); + } + + IR::U64 GetTPIDR() noexcept { + return Inst(Opcode::A64GetTPIDR); + } + + void SetTPIDR(const IR::U64& value) noexcept { + Inst(Opcode::A64SetTPIDR, value); + } + + IR::U64 GetTPIDRRO() noexcept { + return Inst(Opcode::A64GetTPIDRRO); + } + + void ClearExclusive() noexcept { + Inst(Opcode::A64ClearExclusive); + } + + IR::U8 ReadMemory8(const IR::U64& vaddr, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ReadMemory8, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); + } + + IR::U16 ReadMemory16(const IR::U64& vaddr, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ReadMemory16, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); + } + + IR::U32 ReadMemory32(const IR::U64& vaddr, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ReadMemory32, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); + } + + IR::U64 ReadMemory64(const IR::U64& vaddr, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ReadMemory64, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); + } + + IR::U128 ReadMemory128(const IR::U64& vaddr, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ReadMemory128, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); + } + + IR::U8 ExclusiveReadMemory8(const IR::U64& vaddr, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ExclusiveReadMemory8, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); + } + + IR::U16 ExclusiveReadMemory16(const IR::U64& vaddr, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ExclusiveReadMemory16, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); + } + + IR::U32 ExclusiveReadMemory32(const IR::U64& vaddr, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ExclusiveReadMemory32, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); + } + + IR::U64 ExclusiveReadMemory64(const IR::U64& vaddr, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ExclusiveReadMemory64, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); + } + + IR::U128 ExclusiveReadMemory128(const IR::U64& vaddr, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ExclusiveReadMemory128, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type}); + } + + void WriteMemory8(const IR::U64& vaddr, const IR::U8& value, IR::AccType acc_type) noexcept { + Inst(Opcode::A64WriteMemory8, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); + } + + void WriteMemory16(const IR::U64& vaddr, const IR::U16& value, IR::AccType acc_type) noexcept { + Inst(Opcode::A64WriteMemory16, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); + } + + void WriteMemory32(const IR::U64& vaddr, const IR::U32& value, IR::AccType acc_type) noexcept { + Inst(Opcode::A64WriteMemory32, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); + } + + void WriteMemory64(const IR::U64& vaddr, const IR::U64& value, IR::AccType acc_type) noexcept { + Inst(Opcode::A64WriteMemory64, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); + } + + void WriteMemory128(const IR::U64& vaddr, const IR::U128& value, IR::AccType acc_type) noexcept { + Inst(Opcode::A64WriteMemory128, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); + } + + IR::U32 ExclusiveWriteMemory8(const IR::U64& vaddr, const IR::U8& value, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ExclusiveWriteMemory8, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); + } + + IR::U32 ExclusiveWriteMemory16(const IR::U64& vaddr, const IR::U16& value, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ExclusiveWriteMemory16, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); + } + + IR::U32 ExclusiveWriteMemory32(const IR::U64& vaddr, const IR::U32& value, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ExclusiveWriteMemory32, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); + } + + IR::U32 ExclusiveWriteMemory64(const IR::U64& vaddr, const IR::U64& value, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ExclusiveWriteMemory64, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); + } + + IR::U32 ExclusiveWriteMemory128(const IR::U64& vaddr, const IR::U128& value, IR::AccType acc_type) noexcept { + return Inst(Opcode::A64ExclusiveWriteMemory128, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type}); + } + + IR::U32 GetW(Reg reg) noexcept { + if (reg == Reg::ZR) + return Imm32(0); + return Inst(Opcode::A64GetW, IR::Value(reg)); + } + + IR::U64 GetX(Reg reg) noexcept { + if (reg == Reg::ZR) + return Imm64(0); + return Inst(Opcode::A64GetX, IR::Value(reg)); + } + + IR::U128 GetS(Vec vec) noexcept { + return Inst(Opcode::A64GetS, IR::Value(vec)); + } + + IR::U128 GetD(Vec vec) noexcept { + return Inst(Opcode::A64GetD, IR::Value(vec)); + } + + IR::U128 GetQ(Vec vec) noexcept { + return Inst(Opcode::A64GetQ, IR::Value(vec)); + } + + IR::U64 GetSP() noexcept { + return Inst(Opcode::A64GetSP); + } + + IR::U32 GetFPCR() noexcept { + return Inst(Opcode::A64GetFPCR); + } + + IR::U32 GetFPSR() noexcept { + return Inst(Opcode::A64GetFPSR); + } + + void SetW(const Reg reg, const IR::U32& value) noexcept { + if (reg == Reg::ZR) + return; + Inst(Opcode::A64SetW, IR::Value(reg), value); + } + + void SetX(const Reg reg, const IR::U64& value) noexcept { + if (reg == Reg::ZR) + return; + Inst(Opcode::A64SetX, IR::Value(reg), value); + } + + void SetS(const Vec vec, const IR::U128& value) noexcept { + Inst(Opcode::A64SetS, IR::Value(vec), value); + } + + void SetD(const Vec vec, const IR::U128& value) noexcept { + Inst(Opcode::A64SetD, IR::Value(vec), value); + } + + void SetQ(const Vec vec, const IR::U128& value) noexcept { + Inst(Opcode::A64SetQ, IR::Value(vec), value); + } + + void SetSP(const IR::U64& value) noexcept { + Inst(Opcode::A64SetSP, value); + } + + void SetFPCR(const IR::U32& value) noexcept { + Inst(Opcode::A64SetFPCR, value); + } + + void SetFPSR(const IR::U32& value) noexcept { + Inst(Opcode::A64SetFPSR, value); + } + + void SetPC(const IR::U64& value) noexcept { + Inst(Opcode::A64SetPC, value); + } private: - IR::U64 ImmCurrentLocationDescriptor(); + IR::U64 ImmCurrentLocationDescriptor() noexcept { + return Imm64(IR::LocationDescriptor{*current_location}.Value()); + } }; } // namespace Dynarmic::A64 diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/decoder/a64.h b/externals/dynarmic/src/dynarmic/frontend/A64/decoder/a64.h index f264893502..e807490d16 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/decoder/a64.h +++ b/externals/dynarmic/src/dynarmic/frontend/A64/decoder/a64.h @@ -33,27 +33,26 @@ inline size_t ToFastLookupIndex(u32 instruction) { } // namespace detail template -DecodeTable GetDecodeTable() { +constexpr DecodeTable GetDecodeTable() { std::vector> list = { #define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)), #include "./a64.inc" #undef INST }; + // If a matcher has more bits in its mask it is more specific, so it should come first. std::stable_sort(list.begin(), list.end(), [](const auto& matcher1, const auto& matcher2) { // If a matcher has more bits in its mask it is more specific, so it should come first. return mcl::bit::count_ones(matcher1.GetMask()) > mcl::bit::count_ones(matcher2.GetMask()); }); // Exceptions to the above rule of thumb. - const std::set comes_first{ - "MOVI, MVNI, ORR, BIC (vector, immediate)", - "FMOV (vector, immediate)", - "Unallocated SIMD modified immediate", - }; - std::stable_partition(list.begin(), list.end(), [&](const auto& matcher) { - return comes_first.count(matcher.GetName()) > 0; + return std::set{ + "MOVI, MVNI, ORR, BIC (vector, immediate)", + "FMOV (vector, immediate)", + "Unallocated SIMD modified immediate", + }.count(matcher.GetName()) > 0; }); DecodeTable table{}; @@ -75,7 +74,6 @@ std::optional>> Decode(u32 instruction) const auto matches_instruction = [instruction](const auto& matcher) { return matcher.Matches(instruction); }; - const auto& subtable = table[detail::ToFastLookupIndex(instruction)]; auto iter = std::find_if(subtable.begin(), subtable.end(), matches_instruction); return iter != subtable.end() ? std::optional>>(*iter) : std::nullopt; diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/translate/a64_translate.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/translate/a64_translate.cpp index 05996aeb64..352c2e6ae2 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/translate/a64_translate.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/translate/a64_translate.cpp @@ -67,3 +67,64 @@ bool TranslateSingleInstruction(IR::Block& block, LocationDescriptor descriptor, } } // namespace Dynarmic::A64 + +// ls -l | awk '{print "#include \"dynarmic/frontend/A64/translate/impl/" $9 "\""}' +#include "dynarmic/frontend/A64/translate/impl/a64_branch.cpp" +#include "dynarmic/frontend/A64/translate/impl/a64_exception_generating.cpp" +#include "dynarmic/frontend/A64/translate/impl/data_processing_addsub.cpp" +#include "dynarmic/frontend/A64/translate/impl/data_processing_bitfield.cpp" +#include "dynarmic/frontend/A64/translate/impl/data_processing_conditional_compare.cpp" +#include "dynarmic/frontend/A64/translate/impl/data_processing_conditional_select.cpp" +#include "dynarmic/frontend/A64/translate/impl/data_processing_crc32.cpp" +#include "dynarmic/frontend/A64/translate/impl/data_processing_logical.cpp" +#include "dynarmic/frontend/A64/translate/impl/data_processing_multiply.cpp" +#include "dynarmic/frontend/A64/translate/impl/data_processing_pcrel.cpp" +#include "dynarmic/frontend/A64/translate/impl/data_processing_register.cpp" +#include "dynarmic/frontend/A64/translate/impl/data_processing_shift.cpp" +#include "dynarmic/frontend/A64/translate/impl/floating_point_compare.cpp" +#include "dynarmic/frontend/A64/translate/impl/floating_point_conditional_compare.cpp" +#include "dynarmic/frontend/A64/translate/impl/floating_point_conditional_select.cpp" +#include "dynarmic/frontend/A64/translate/impl/floating_point_conversion_fixed_point.cpp" +#include "dynarmic/frontend/A64/translate/impl/floating_point_conversion_integer.cpp" +#include "dynarmic/frontend/A64/translate/impl/floating_point_data_processing_one_register.cpp" +#include "dynarmic/frontend/A64/translate/impl/floating_point_data_processing_three_register.cpp" +#include "dynarmic/frontend/A64/translate/impl/floating_point_data_processing_two_register.cpp" +#include "dynarmic/frontend/A64/translate/impl/impl.cpp" +#include "dynarmic/frontend/A64/translate/impl/impl.h" +#include "dynarmic/frontend/A64/translate/impl/load_store_exclusive.cpp" +#include "dynarmic/frontend/A64/translate/impl/load_store_load_literal.cpp" +#include "dynarmic/frontend/A64/translate/impl/load_store_multiple_structures.cpp" +#include "dynarmic/frontend/A64/translate/impl/load_store_no_allocate_pair.cpp" +#include "dynarmic/frontend/A64/translate/impl/load_store_register_immediate.cpp" +#include "dynarmic/frontend/A64/translate/impl/load_store_register_pair.cpp" +#include "dynarmic/frontend/A64/translate/impl/load_store_register_register_offset.cpp" +#include "dynarmic/frontend/A64/translate/impl/load_store_register_unprivileged.cpp" +#include "dynarmic/frontend/A64/translate/impl/load_store_single_structure.cpp" +#include "dynarmic/frontend/A64/translate/impl/move_wide.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_across_lanes.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_aes.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_copy.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_crypto_four_register.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_crypto_three_register.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_extract.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_modified_immediate.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_permute.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_scalar_pairwise.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_scalar_shift_by_immediate.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_scalar_three_same.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_scalar_two_register_misc.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_scalar_x_indexed_element.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_sha512.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_sha.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_shift_by_immediate.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_table_lookup.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_three_different.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_three_same.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_three_same_extra.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_two_register_misc.cpp" +#include "dynarmic/frontend/A64/translate/impl/simd_vector_x_indexed_element.cpp" +#include "dynarmic/frontend/A64/translate/impl/sys_dc.cpp" +#include "dynarmic/frontend/A64/translate/impl/sys_ic.cpp" +#include "dynarmic/frontend/A64/translate/impl/system.cpp" +#include "dynarmic/frontend/A64/translate/impl/system_flag_format.cpp" +#include "dynarmic/frontend/A64/translate/impl/system_flag_manipulation.cpp" diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_pairwise.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_pairwise.cpp index 63615b0f9a..e3b8d7502c 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_pairwise.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_pairwise.cpp @@ -7,14 +7,14 @@ namespace Dynarmic::A64 { namespace { -enum class MinMaxOperation { +enum class MinMaxOperationSSPW { Max, MaxNumeric, Min, MinNumeric, }; -bool FPPairwiseMinMax(TranslatorVisitor& v, bool sz, Vec Vn, Vec Vd, MinMaxOperation operation) { +bool FPPairwiseMinMax(TranslatorVisitor& v, bool sz, Vec Vn, Vec Vd, MinMaxOperationSSPW operation) { const size_t esize = sz ? 64 : 32; const IR::U128 operand = v.V(128, Vn); @@ -22,13 +22,13 @@ bool FPPairwiseMinMax(TranslatorVisitor& v, bool sz, Vec Vn, Vec Vd, MinMaxOpera const IR::U32U64 element2 = v.ir.VectorGetElement(esize, operand, 1); const IR::U32U64 result = [&] { switch (operation) { - case MinMaxOperation::Max: + case MinMaxOperationSSPW::Max: return v.ir.FPMax(element1, element2); - case MinMaxOperation::MaxNumeric: + case MinMaxOperationSSPW::MaxNumeric: return v.ir.FPMaxNumeric(element1, element2); - case MinMaxOperation::Min: + case MinMaxOperationSSPW::Min: return v.ir.FPMin(element1, element2); - case MinMaxOperation::MinNumeric: + case MinMaxOperationSSPW::MinNumeric: return v.ir.FPMinNumeric(element1, element2); default: UNREACHABLE(); @@ -63,18 +63,18 @@ bool TranslatorVisitor::FADDP_pair_2(bool size, Vec Vn, Vec Vd) { } bool TranslatorVisitor::FMAXNMP_pair_2(bool sz, Vec Vn, Vec Vd) { - return FPPairwiseMinMax(*this, sz, Vn, Vd, MinMaxOperation::MaxNumeric); + return FPPairwiseMinMax(*this, sz, Vn, Vd, MinMaxOperationSSPW::MaxNumeric); } bool TranslatorVisitor::FMAXP_pair_2(bool sz, Vec Vn, Vec Vd) { - return FPPairwiseMinMax(*this, sz, Vn, Vd, MinMaxOperation::Max); + return FPPairwiseMinMax(*this, sz, Vn, Vd, MinMaxOperationSSPW::Max); } bool TranslatorVisitor::FMINNMP_pair_2(bool sz, Vec Vn, Vec Vd) { - return FPPairwiseMinMax(*this, sz, Vn, Vd, MinMaxOperation::MinNumeric); + return FPPairwiseMinMax(*this, sz, Vn, Vd, MinMaxOperationSSPW::MinNumeric); } bool TranslatorVisitor::FMINP_pair_2(bool sz, Vec Vn, Vec Vd) { - return FPPairwiseMinMax(*this, sz, Vn, Vd, MinMaxOperation::Min); + return FPPairwiseMinMax(*this, sz, Vn, Vd, MinMaxOperationSSPW::Min); } } // namespace Dynarmic::A64 diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_shift_by_immediate.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_shift_by_immediate.cpp index a0570edc7e..5d60cb31c3 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_shift_by_immediate.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_shift_by_immediate.cpp @@ -27,7 +27,7 @@ enum class ShiftExtraBehavior { Accumulate, }; -enum class Signedness { +enum class SignednessSSSBI { Signed, Unsigned, }; @@ -63,7 +63,7 @@ bool SaturatingShiftLeft(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, return true; } -bool ShiftRight(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, ShiftExtraBehavior behavior, Signedness signedness) { +bool ShiftRight(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, ShiftExtraBehavior behavior, SignednessSSSBI SignednessSSSBI) { if (!immh.Bit<3>()) { return v.ReservedValue(); } @@ -73,7 +73,7 @@ bool ShiftRight(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, const IR::U64 operand = v.V_scalar(esize, Vn); IR::U64 result = [&]() -> IR::U64 { - if (signedness == Signedness::Signed) { + if (SignednessSSSBI == SignednessSSSBI::Signed) { return v.ir.ArithmeticShiftRight(operand, v.ir.Imm8(shift_amount)); } return v.ir.LogicalShiftRight(operand, v.ir.Imm8(shift_amount)); @@ -88,7 +88,7 @@ bool ShiftRight(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, return true; } -bool RoundingShiftRight(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, ShiftExtraBehavior behavior, Signedness signedness) { +bool RoundingShiftRight(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, ShiftExtraBehavior behavior, SignednessSSSBI SignednessSSSBI) { if (!immh.Bit<3>()) { return v.ReservedValue(); } @@ -100,7 +100,7 @@ bool RoundingShiftRight(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, const IR::U64 round_bit = v.ir.LogicalShiftRight(v.ir.LogicalShiftLeft(operand, v.ir.Imm8(64 - shift_amount)), v.ir.Imm8(63)); const IR::U64 result = [&] { const IR::U64 shifted = [&]() -> IR::U64 { - if (signedness == Signedness::Signed) { + if (SignednessSSSBI == SignednessSSSBI::Signed) { return v.ir.ArithmeticShiftRight(operand, v.ir.Imm8(shift_amount)); } return v.ir.LogicalShiftRight(operand, v.ir.Imm8(shift_amount)); @@ -163,7 +163,7 @@ bool ShiftAndInsert(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec return true; } -bool ShiftRightNarrowing(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, Narrowing narrowing, Signedness signedness) { +bool ShiftRightNarrowing(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, Narrowing narrowing, SignednessSSSBI SignednessSSSBI) { if (immh == 0b0000) { return v.ReservedValue(); } @@ -179,7 +179,7 @@ bool ShiftRightNarrowing(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, const IR::U128 operand = v.ir.ZeroExtendToQuad(v.ir.VectorGetElement(source_esize, v.V(128, Vn), 0)); IR::U128 wide_result = [&] { - if (signedness == Signedness::Signed) { + if (SignednessSSSBI == SignednessSSSBI::Signed) { return v.ir.VectorArithmeticShiftRight(source_esize, operand, shift_amount); } return v.ir.VectorLogicalShiftRight(source_esize, operand, shift_amount); @@ -190,12 +190,12 @@ bool ShiftRightNarrowing(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, case Narrowing::Truncation: return v.ir.VectorNarrow(source_esize, wide_result); case Narrowing::SaturateToUnsigned: - if (signedness == Signedness::Signed) { + if (SignednessSSSBI == SignednessSSSBI::Signed) { return v.ir.VectorSignedSaturatedNarrowToUnsigned(source_esize, wide_result); } return v.ir.VectorUnsignedSaturatedNarrow(source_esize, wide_result); case Narrowing::SaturateToSigned: - ASSERT(signedness == Signedness::Signed); + ASSERT(SignednessSSSBI == SignednessSSSBI::Signed); return v.ir.VectorSignedSaturatedNarrowToSigned(source_esize, wide_result); } UNREACHABLE(); @@ -206,7 +206,7 @@ bool ShiftRightNarrowing(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, return true; } -bool ScalarFPConvertWithRound(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, Signedness sign, FloatConversionDirection direction, FP::RoundingMode rounding_mode) { +bool ScalarFPConvertWithRound(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, SignednessSSSBI sign, FloatConversionDirection direction, FP::RoundingMode rounding_mode) { const u32 immh_value = immh.ZeroExtend(); if ((immh_value & 0b1110) == 0b0000) { @@ -227,23 +227,23 @@ bool ScalarFPConvertWithRound(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Ve switch (direction) { case FloatConversionDirection::FloatToFixed: if (esize == 64) { - return sign == Signedness::Signed + return sign == SignednessSSSBI::Signed ? v.ir.FPToFixedS64(operand, fbits, rounding_mode) : v.ir.FPToFixedU64(operand, fbits, rounding_mode); } - return sign == Signedness::Signed + return sign == SignednessSSSBI::Signed ? v.ir.FPToFixedS32(operand, fbits, rounding_mode) : v.ir.FPToFixedU32(operand, fbits, rounding_mode); case FloatConversionDirection::FixedToFloat: if (esize == 64) { - return sign == Signedness::Signed + return sign == SignednessSSSBI::Signed ? v.ir.FPSignedFixedToDouble(operand, fbits, rounding_mode) : v.ir.FPUnsignedFixedToDouble(operand, fbits, rounding_mode); } - return sign == Signedness::Signed + return sign == SignednessSSSBI::Signed ? v.ir.FPSignedFixedToSingle(operand, fbits, rounding_mode) : v.ir.FPUnsignedFixedToSingle(operand, fbits, rounding_mode); } @@ -257,19 +257,19 @@ bool ScalarFPConvertWithRound(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Ve } // Anonymous namespace bool TranslatorVisitor::FCVTZS_fix_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, immh, immb, Vn, Vd, Signedness::Signed, FloatConversionDirection::FloatToFixed, FP::RoundingMode::TowardsZero); + return ScalarFPConvertWithRound(*this, immh, immb, Vn, Vd, SignednessSSSBI::Signed, FloatConversionDirection::FloatToFixed, FP::RoundingMode::TowardsZero); } bool TranslatorVisitor::FCVTZU_fix_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, immh, immb, Vn, Vd, Signedness::Unsigned, FloatConversionDirection::FloatToFixed, FP::RoundingMode::TowardsZero); + return ScalarFPConvertWithRound(*this, immh, immb, Vn, Vd, SignednessSSSBI::Unsigned, FloatConversionDirection::FloatToFixed, FP::RoundingMode::TowardsZero); } bool TranslatorVisitor::SCVTF_fix_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, immh, immb, Vn, Vd, Signedness::Signed, FloatConversionDirection::FixedToFloat, ir.current_location->FPCR().RMode()); + return ScalarFPConvertWithRound(*this, immh, immb, Vn, Vd, SignednessSSSBI::Signed, FloatConversionDirection::FixedToFloat, ir.current_location->FPCR().RMode()); } bool TranslatorVisitor::UCVTF_fix_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, immh, immb, Vn, Vd, Signedness::Unsigned, FloatConversionDirection::FixedToFloat, ir.current_location->FPCR().RMode()); + return ScalarFPConvertWithRound(*this, immh, immb, Vn, Vd, SignednessSSSBI::Unsigned, FloatConversionDirection::FixedToFloat, ir.current_location->FPCR().RMode()); } bool TranslatorVisitor::SLI_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { @@ -289,27 +289,27 @@ bool TranslatorVisitor::SQSHLU_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { } bool TranslatorVisitor::SQSHRN_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRightNarrowing(*this, immh, immb, Vn, Vd, Narrowing::SaturateToSigned, Signedness::Signed); + return ShiftRightNarrowing(*this, immh, immb, Vn, Vd, Narrowing::SaturateToSigned, SignednessSSSBI::Signed); } bool TranslatorVisitor::SQSHRUN_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRightNarrowing(*this, immh, immb, Vn, Vd, Narrowing::SaturateToUnsigned, Signedness::Signed); + return ShiftRightNarrowing(*this, immh, immb, Vn, Vd, Narrowing::SaturateToUnsigned, SignednessSSSBI::Signed); } bool TranslatorVisitor::SRSHR_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return RoundingShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::None, Signedness::Signed); + return RoundingShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::None, SignednessSSSBI::Signed); } bool TranslatorVisitor::SRSRA_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return RoundingShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::Accumulate, Signedness::Signed); + return RoundingShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::Accumulate, SignednessSSSBI::Signed); } bool TranslatorVisitor::SSHR_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::None, Signedness::Signed); + return ShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::None, SignednessSSSBI::Signed); } bool TranslatorVisitor::SSRA_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::Accumulate, Signedness::Signed); + return ShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::Accumulate, SignednessSSSBI::Signed); } bool TranslatorVisitor::SHL_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { @@ -332,23 +332,23 @@ bool TranslatorVisitor::UQSHL_imm_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { } bool TranslatorVisitor::UQSHRN_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRightNarrowing(*this, immh, immb, Vn, Vd, Narrowing::SaturateToUnsigned, Signedness::Unsigned); + return ShiftRightNarrowing(*this, immh, immb, Vn, Vd, Narrowing::SaturateToUnsigned, SignednessSSSBI::Unsigned); } bool TranslatorVisitor::URSHR_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return RoundingShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::None, Signedness::Unsigned); + return RoundingShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::None, SignednessSSSBI::Unsigned); } bool TranslatorVisitor::URSRA_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return RoundingShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::Accumulate, Signedness::Unsigned); + return RoundingShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::Accumulate, SignednessSSSBI::Unsigned); } bool TranslatorVisitor::USHR_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::None, Signedness::Unsigned); + return ShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::None, SignednessSSSBI::Unsigned); } bool TranslatorVisitor::USRA_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::Accumulate, Signedness::Unsigned); + return ShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::Accumulate, SignednessSSSBI::Unsigned); } } // namespace Dynarmic::A64 diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_three_same.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_three_same.cpp index fb9ae9d141..d551605bda 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_three_same.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_three_same.cpp @@ -26,12 +26,12 @@ enum class ComparisonVariant { Zero, }; -enum class Signedness { +enum class SignednessSSTS { Signed, Unsigned, }; -bool RoundingShiftLeft(TranslatorVisitor& v, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, Signedness sign) { +bool RoundingShiftLeft(TranslatorVisitor& v, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, SignednessSSTS sign) { if (size != 0b11) { return v.ReservedValue(); } @@ -39,7 +39,7 @@ bool RoundingShiftLeft(TranslatorVisitor& v, Imm<2> size, Vec Vm, Vec Vn, Vec Vd const IR::U128 operand1 = v.V(64, Vn); const IR::U128 operand2 = v.V(64, Vm); const IR::U128 result = [&] { - if (sign == Signedness::Signed) { + if (sign == SignednessSSTS::Signed) { return v.ir.VectorRoundingShiftLeftSigned(64, operand1, operand2); } @@ -369,7 +369,7 @@ bool TranslatorVisitor::SQSHL_reg_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::SRSHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return RoundingShiftLeft(*this, size, Vm, Vn, Vd, Signedness::Signed); + return RoundingShiftLeft(*this, size, Vm, Vn, Vd, SignednessSSTS::Signed); } bool TranslatorVisitor::SSHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { @@ -411,7 +411,7 @@ bool TranslatorVisitor::UQSHL_reg_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::URSHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return RoundingShiftLeft(*this, size, Vm, Vn, Vd, Signedness::Unsigned); + return RoundingShiftLeft(*this, size, Vm, Vn, Vd, SignednessSSTS::Unsigned); } bool TranslatorVisitor::USHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_two_register_misc.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_two_register_misc.cpp index 2289f5cbd1..0fc37f538f 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_two_register_misc.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_two_register_misc.cpp @@ -7,7 +7,7 @@ namespace Dynarmic::A64 { namespace { -enum class ComparisonType { +enum class ComparisonTypeSSTRM { EQ, GE, GT, @@ -15,12 +15,12 @@ enum class ComparisonType { LT }; -enum class Signedness { +enum class SignednessSSTRM { Signed, Unsigned }; -bool ScalarFPCompareAgainstZero(TranslatorVisitor& v, bool sz, Vec Vn, Vec Vd, ComparisonType type) { +bool ScalarFPCompareAgainstZero(TranslatorVisitor& v, bool sz, Vec Vn, Vec Vd, ComparisonTypeSSTRM type) { const size_t esize = sz ? 64 : 32; const size_t datasize = esize; @@ -28,15 +28,15 @@ bool ScalarFPCompareAgainstZero(TranslatorVisitor& v, bool sz, Vec Vn, Vec Vd, C const IR::U128 zero = v.ir.ZeroVector(); const IR::U128 result = [&] { switch (type) { - case ComparisonType::EQ: + case ComparisonTypeSSTRM::EQ: return v.ir.FPVectorEqual(esize, operand, zero); - case ComparisonType::GE: + case ComparisonTypeSSTRM::GE: return v.ir.FPVectorGreaterEqual(esize, operand, zero); - case ComparisonType::GT: + case ComparisonTypeSSTRM::GT: return v.ir.FPVectorGreater(esize, operand, zero); - case ComparisonType::LE: + case ComparisonTypeSSTRM::LE: return v.ir.FPVectorGreaterEqual(esize, zero, operand); - case ComparisonType::LT: + case ComparisonTypeSSTRM::LT: return v.ir.FPVectorGreater(esize, zero, operand); } @@ -47,18 +47,18 @@ bool ScalarFPCompareAgainstZero(TranslatorVisitor& v, bool sz, Vec Vn, Vec Vd, C return true; } -bool ScalarFPConvertWithRound(TranslatorVisitor& v, bool sz, Vec Vn, Vec Vd, FP::RoundingMode rmode, Signedness sign) { +bool ScalarFPConvertWithRound(TranslatorVisitor& v, bool sz, Vec Vn, Vec Vd, FP::RoundingMode rmode, SignednessSSTRM sign) { const size_t esize = sz ? 64 : 32; const IR::U32U64 operand = v.V_scalar(esize, Vn); const IR::U32U64 result = [&]() -> IR::U32U64 { if (sz) { - return sign == Signedness::Signed + return sign == SignednessSSTRM::Signed ? v.ir.FPToFixedS64(operand, 0, rmode) : v.ir.FPToFixedU64(operand, 0, rmode); } - return sign == Signedness::Signed + return sign == SignednessSSTRM::Signed ? v.ir.FPToFixedS32(operand, 0, rmode) : v.ir.FPToFixedU32(operand, 0, rmode); }(); @@ -107,55 +107,55 @@ bool TranslatorVisitor::FCMEQ_zero_1(Vec Vn, Vec Vd) { } bool TranslatorVisitor::FCMEQ_zero_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonType::EQ); + return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonTypeSSTRM::EQ); } bool TranslatorVisitor::FCMGE_zero_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonType::GE); + return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonTypeSSTRM::GE); } bool TranslatorVisitor::FCMGT_zero_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonType::GT); + return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonTypeSSTRM::GT); } bool TranslatorVisitor::FCMLE_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonType::LE); + return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonTypeSSTRM::LE); } bool TranslatorVisitor::FCMLT_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonType::LT); + return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonTypeSSTRM::LT); } bool TranslatorVisitor::FCVTAS_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::ToNearest_TieAwayFromZero, Signedness::Signed); + return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::ToNearest_TieAwayFromZero, SignednessSSTRM::Signed); } bool TranslatorVisitor::FCVTAU_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::ToNearest_TieAwayFromZero, Signedness::Unsigned); + return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::ToNearest_TieAwayFromZero, SignednessSSTRM::Unsigned); } bool TranslatorVisitor::FCVTMS_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsMinusInfinity, Signedness::Signed); + return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsMinusInfinity, SignednessSSTRM::Signed); } bool TranslatorVisitor::FCVTMU_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsMinusInfinity, Signedness::Unsigned); + return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsMinusInfinity, SignednessSSTRM::Unsigned); } bool TranslatorVisitor::FCVTNS_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::ToNearest_TieEven, Signedness::Signed); + return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::ToNearest_TieEven, SignednessSSTRM::Signed); } bool TranslatorVisitor::FCVTNU_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::ToNearest_TieEven, Signedness::Unsigned); + return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::ToNearest_TieEven, SignednessSSTRM::Unsigned); } bool TranslatorVisitor::FCVTPS_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsPlusInfinity, Signedness::Signed); + return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsPlusInfinity, SignednessSSTRM::Signed); } bool TranslatorVisitor::FCVTPU_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsPlusInfinity, Signedness::Unsigned); + return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsPlusInfinity, SignednessSSTRM::Unsigned); } bool TranslatorVisitor::FCVTXN_1(bool sz, Vec Vn, Vec Vd) { @@ -171,11 +171,11 @@ bool TranslatorVisitor::FCVTXN_1(bool sz, Vec Vn, Vec Vd) { } bool TranslatorVisitor::FCVTZS_int_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsZero, Signedness::Signed); + return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsZero, SignednessSSTRM::Signed); } bool TranslatorVisitor::FCVTZU_int_2(bool sz, Vec Vn, Vec Vd) { - return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsZero, Signedness::Unsigned); + return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsZero, SignednessSSTRM::Unsigned); } bool TranslatorVisitor::FRECPE_1(Vec Vn, Vec Vd) { diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_x_indexed_element.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_x_indexed_element.cpp index dbbc4ce12c..7a5d9847d7 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_x_indexed_element.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_scalar_x_indexed_element.cpp @@ -9,7 +9,7 @@ namespace Dynarmic::A64 { namespace { -std::pair Combine(Imm<2> size, Imm<1> H, Imm<1> L, Imm<1> M, Imm<4> Vmlo) { +std::pair CombineScalar(Imm<2> size, Imm<1> H, Imm<1> L, Imm<1> M, Imm<4> Vmlo) { if (size == 0b01) { return {concatenate(H, L, M).ZeroExtend(), Vmlo.ZeroExtend()}; } @@ -122,7 +122,7 @@ bool TranslatorVisitor::SQDMULH_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vm } const size_t esize = 8 << size.ZeroExtend(); - const auto [index, Vm] = Combine(size, H, L, M, Vmlo); + const auto [index, Vm] = CombineScalar(size, H, L, M, Vmlo); const IR::UAny operand1 = V_scalar(esize, Vn); const IR::UAny operand2 = ir.VectorGetElement(esize, V(128, Vm), index); @@ -137,7 +137,7 @@ bool TranslatorVisitor::SQRDMULH_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> V } const size_t esize = 8 << size.ZeroExtend(); - const auto [index, Vm] = Combine(size, H, L, M, Vmlo); + const auto [index, Vm] = CombineScalar(size, H, L, M, Vmlo); const IR::U128 operand1 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(128, Vn), 0)); const IR::U128 operand2 = V(128, Vm); @@ -154,7 +154,7 @@ bool TranslatorVisitor::SQDMULL_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vm } const size_t esize = 8 << size.ZeroExtend(); - const auto [index, Vm] = Combine(size, H, L, M, Vmlo); + const auto [index, Vm] = CombineScalar(size, H, L, M, Vmlo); const IR::U128 operand1 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(128, Vn), 0)); const IR::U128 operand2 = V(128, Vm); diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_shift_by_immediate.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_shift_by_immediate.cpp index 41b0952249..559721a22a 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_shift_by_immediate.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_shift_by_immediate.cpp @@ -20,24 +20,24 @@ enum class Accumulating { Accumulate }; -enum class Signedness { +enum class SignednessSSBI { Signed, Unsigned }; -enum class Narrowing { +enum class NarrowingSSBI { Truncation, SaturateToUnsigned, SaturateToSigned, }; -enum class SaturatingShiftLeftType { +enum class SaturatingShiftLeftTypeSSBI { Signed, Unsigned, SignedWithUnsignedSaturation, }; -enum class FloatConversionDirection { +enum class FloatConversionDirectionSSBI { FixedToFloat, FloatToFixed, }; @@ -48,7 +48,7 @@ IR::U128 PerformRoundingCorrection(TranslatorVisitor& v, size_t esize, u64 round return v.ir.VectorSub(esize, shifted, round_correction); } -bool ShiftRight(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, Rounding rounding, Accumulating accumulating, Signedness signedness) { +bool ShiftRight(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, Rounding rounding, Accumulating accumulating, SignednessSSBI SignednessSSBI) { if (immh == 0b0000) { return v.DecodeError(); } @@ -65,7 +65,7 @@ bool ShiftRight(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, const IR::U128 operand = v.V(datasize, Vn); IR::U128 result = [&] { - if (signedness == Signedness::Signed) { + if (SignednessSSBI == SignednessSSBI::Signed) { return v.ir.VectorArithmeticShiftRight(esize, operand, shift_amount); } return v.ir.VectorLogicalShiftRight(esize, operand, shift_amount); @@ -85,7 +85,7 @@ bool ShiftRight(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, return true; } -bool ShiftRightNarrowing(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, Rounding rounding, Narrowing narrowing, Signedness signedness) { +bool ShiftRightNarrowingSSBI(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, Rounding rounding, NarrowingSSBI NarrowingSSBI, SignednessSSBI SignednessSSBI) { if (immh == 0b0000) { return v.DecodeError(); } @@ -103,7 +103,7 @@ bool ShiftRightNarrowing(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, const IR::U128 operand = v.V(128, Vn); IR::U128 wide_result = [&] { - if (signedness == Signedness::Signed) { + if (SignednessSSBI == SignednessSSBI::Signed) { return v.ir.VectorArithmeticShiftRight(source_esize, operand, shift_amount); } return v.ir.VectorLogicalShiftRight(source_esize, operand, shift_amount); @@ -115,16 +115,16 @@ bool ShiftRightNarrowing(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, } const IR::U128 result = [&] { - switch (narrowing) { - case Narrowing::Truncation: + switch (NarrowingSSBI) { + case NarrowingSSBI::Truncation: return v.ir.VectorNarrow(source_esize, wide_result); - case Narrowing::SaturateToUnsigned: - if (signedness == Signedness::Signed) { + case NarrowingSSBI::SaturateToUnsigned: + if (SignednessSSBI == SignednessSSBI::Signed) { return v.ir.VectorSignedSaturatedNarrowToUnsigned(source_esize, wide_result); } return v.ir.VectorUnsignedSaturatedNarrow(source_esize, wide_result); - case Narrowing::SaturateToSigned: - ASSERT(signedness == Signedness::Signed); + case NarrowingSSBI::SaturateToSigned: + ASSERT(SignednessSSBI == SignednessSSBI::Signed); return v.ir.VectorSignedSaturatedNarrowToSigned(source_esize, wide_result); } UNREACHABLE(); @@ -134,7 +134,7 @@ bool ShiftRightNarrowing(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, return true; } -bool ShiftLeftLong(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, Signedness signedness) { +bool ShiftLeftLong(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, SignednessSSBI SignednessSSBI) { if (immh == 0b0000) { return v.DecodeError(); } @@ -151,7 +151,7 @@ bool ShiftLeftLong(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec V const IR::U128 operand = v.Vpart(datasize, Vn, part); const IR::U128 expanded_operand = [&] { - if (signedness == Signedness::Signed) { + if (SignednessSSBI == SignednessSSBI::Signed) { return v.ir.VectorSignExtend(esize, operand); } return v.ir.VectorZeroExtend(esize, operand); @@ -162,7 +162,7 @@ bool ShiftLeftLong(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec V return true; } -bool SaturatingShiftLeft(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, SaturatingShiftLeftType type) { +bool SaturatingShiftLeft(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, SaturatingShiftLeftTypeSSBI type) { if (!Q && immh.Bit<3>()) { return v.ReservedValue(); } @@ -174,11 +174,11 @@ bool SaturatingShiftLeft(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, const IR::U128 operand = v.V(datasize, Vn); const IR::U128 shift_vec = v.ir.VectorBroadcast(esize, v.I(esize, shift)); const IR::U128 result = [&] { - if (type == SaturatingShiftLeftType::Signed) { + if (type == SaturatingShiftLeftTypeSSBI::Signed) { return v.ir.VectorSignedSaturatedShiftLeft(esize, operand, shift_vec); } - if (type == SaturatingShiftLeftType::Unsigned) { + if (type == SaturatingShiftLeftTypeSSBI::Unsigned) { return v.ir.VectorUnsignedSaturatedShiftLeft(esize, operand, shift_vec); } @@ -189,7 +189,7 @@ bool SaturatingShiftLeft(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, return true; } -bool ConvertFloat(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, Signedness signedness, FloatConversionDirection direction, FP::RoundingMode rounding_mode) { +bool ConvertFloat(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, SignednessSSBI SignednessSSBI, FloatConversionDirectionSSBI direction, FP::RoundingMode rounding_mode) { if (immh == 0b0000) { return v.DecodeError(); } @@ -210,12 +210,12 @@ bool ConvertFloat(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn const IR::U128 operand = v.V(datasize, Vn); const IR::U128 result = [&] { switch (direction) { - case FloatConversionDirection::FixedToFloat: - return signedness == Signedness::Signed + case FloatConversionDirectionSSBI::FixedToFloat: + return SignednessSSBI == SignednessSSBI::Signed ? v.ir.FPVectorFromSignedFixed(esize, operand, fbits, rounding_mode) : v.ir.FPVectorFromUnsignedFixed(esize, operand, fbits, rounding_mode); - case FloatConversionDirection::FloatToFixed: - return signedness == Signedness::Signed + case FloatConversionDirectionSSBI::FloatToFixed: + return SignednessSSBI == SignednessSSBI::Signed ? v.ir.FPVectorToSignedFixed(esize, operand, fbits, rounding_mode) : v.ir.FPVectorToUnsignedFixed(esize, operand, fbits, rounding_mode); } @@ -229,19 +229,19 @@ bool ConvertFloat(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn } // Anonymous namespace bool TranslatorVisitor::SSHR_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::None, Accumulating::None, Signedness::Signed); + return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::None, Accumulating::None, SignednessSSBI::Signed); } bool TranslatorVisitor::SRSHR_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Accumulating::None, Signedness::Signed); + return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Accumulating::None, SignednessSSBI::Signed); } bool TranslatorVisitor::SRSRA_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Accumulating::Accumulate, Signedness::Signed); + return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Accumulating::Accumulate, SignednessSSBI::Signed); } bool TranslatorVisitor::SSRA_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::None, Accumulating::Accumulate, Signedness::Signed); + return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::None, Accumulating::Accumulate, SignednessSSBI::Signed); } bool TranslatorVisitor::SHL_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { @@ -264,71 +264,71 @@ bool TranslatorVisitor::SHL_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) } bool TranslatorVisitor::SHRN(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::None, Narrowing::Truncation, Signedness::Unsigned); + return ShiftRightNarrowingSSBI(*this, Q, immh, immb, Vn, Vd, Rounding::None, NarrowingSSBI::Truncation, SignednessSSBI::Unsigned); } bool TranslatorVisitor::RSHRN(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Narrowing::Truncation, Signedness::Unsigned); + return ShiftRightNarrowingSSBI(*this, Q, immh, immb, Vn, Vd, Rounding::Round, NarrowingSSBI::Truncation, SignednessSSBI::Unsigned); } bool TranslatorVisitor::SQSHL_imm_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return SaturatingShiftLeft(*this, Q, immh, immb, Vn, Vd, SaturatingShiftLeftType::Signed); + return SaturatingShiftLeft(*this, Q, immh, immb, Vn, Vd, SaturatingShiftLeftTypeSSBI::Signed); } bool TranslatorVisitor::SQSHLU_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return SaturatingShiftLeft(*this, Q, immh, immb, Vn, Vd, SaturatingShiftLeftType::SignedWithUnsignedSaturation); + return SaturatingShiftLeft(*this, Q, immh, immb, Vn, Vd, SaturatingShiftLeftTypeSSBI::SignedWithUnsignedSaturation); } bool TranslatorVisitor::SQSHRN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::None, Narrowing::SaturateToSigned, Signedness::Signed); + return ShiftRightNarrowingSSBI(*this, Q, immh, immb, Vn, Vd, Rounding::None, NarrowingSSBI::SaturateToSigned, SignednessSSBI::Signed); } bool TranslatorVisitor::SQRSHRN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Narrowing::SaturateToSigned, Signedness::Signed); + return ShiftRightNarrowingSSBI(*this, Q, immh, immb, Vn, Vd, Rounding::Round, NarrowingSSBI::SaturateToSigned, SignednessSSBI::Signed); } bool TranslatorVisitor::SQSHRUN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::None, Narrowing::SaturateToUnsigned, Signedness::Signed); + return ShiftRightNarrowingSSBI(*this, Q, immh, immb, Vn, Vd, Rounding::None, NarrowingSSBI::SaturateToUnsigned, SignednessSSBI::Signed); } bool TranslatorVisitor::SQRSHRUN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Narrowing::SaturateToUnsigned, Signedness::Signed); + return ShiftRightNarrowingSSBI(*this, Q, immh, immb, Vn, Vd, Rounding::Round, NarrowingSSBI::SaturateToUnsigned, SignednessSSBI::Signed); } bool TranslatorVisitor::UQSHL_imm_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return SaturatingShiftLeft(*this, Q, immh, immb, Vn, Vd, SaturatingShiftLeftType::Unsigned); + return SaturatingShiftLeft(*this, Q, immh, immb, Vn, Vd, SaturatingShiftLeftTypeSSBI::Unsigned); } bool TranslatorVisitor::UQSHRN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::None, Narrowing::SaturateToUnsigned, Signedness::Unsigned); + return ShiftRightNarrowingSSBI(*this, Q, immh, immb, Vn, Vd, Rounding::None, NarrowingSSBI::SaturateToUnsigned, SignednessSSBI::Unsigned); } bool TranslatorVisitor::UQRSHRN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Narrowing::SaturateToUnsigned, Signedness::Unsigned); + return ShiftRightNarrowingSSBI(*this, Q, immh, immb, Vn, Vd, Rounding::Round, NarrowingSSBI::SaturateToUnsigned, SignednessSSBI::Unsigned); } bool TranslatorVisitor::SSHLL(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftLeftLong(*this, Q, immh, immb, Vn, Vd, Signedness::Signed); + return ShiftLeftLong(*this, Q, immh, immb, Vn, Vd, SignednessSSBI::Signed); } bool TranslatorVisitor::URSHR_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Accumulating::None, Signedness::Unsigned); + return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Accumulating::None, SignednessSSBI::Unsigned); } bool TranslatorVisitor::URSRA_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Accumulating::Accumulate, Signedness::Unsigned); + return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Accumulating::Accumulate, SignednessSSBI::Unsigned); } bool TranslatorVisitor::USHR_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::None, Accumulating::None, Signedness::Unsigned); + return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::None, Accumulating::None, SignednessSSBI::Unsigned); } bool TranslatorVisitor::USRA_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::None, Accumulating::Accumulate, Signedness::Unsigned); + return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::None, Accumulating::Accumulate, SignednessSSBI::Unsigned); } bool TranslatorVisitor::USHLL(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ShiftLeftLong(*this, Q, immh, immb, Vn, Vd, Signedness::Unsigned); + return ShiftLeftLong(*this, Q, immh, immb, Vn, Vd, SignednessSSBI::Unsigned); } bool TranslatorVisitor::SRI_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { @@ -384,19 +384,19 @@ bool TranslatorVisitor::SLI_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) } bool TranslatorVisitor::SCVTF_fix_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ConvertFloat(*this, Q, immh, immb, Vn, Vd, Signedness::Signed, FloatConversionDirection::FixedToFloat, ir.current_location->FPCR().RMode()); + return ConvertFloat(*this, Q, immh, immb, Vn, Vd, SignednessSSBI::Signed, FloatConversionDirectionSSBI::FixedToFloat, ir.current_location->FPCR().RMode()); } bool TranslatorVisitor::UCVTF_fix_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ConvertFloat(*this, Q, immh, immb, Vn, Vd, Signedness::Unsigned, FloatConversionDirection::FixedToFloat, ir.current_location->FPCR().RMode()); + return ConvertFloat(*this, Q, immh, immb, Vn, Vd, SignednessSSBI::Unsigned, FloatConversionDirectionSSBI::FixedToFloat, ir.current_location->FPCR().RMode()); } bool TranslatorVisitor::FCVTZS_fix_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ConvertFloat(*this, Q, immh, immb, Vn, Vd, Signedness::Signed, FloatConversionDirection::FloatToFixed, FP::RoundingMode::TowardsZero); + return ConvertFloat(*this, Q, immh, immb, Vn, Vd, SignednessSSBI::Signed, FloatConversionDirectionSSBI::FloatToFixed, FP::RoundingMode::TowardsZero); } bool TranslatorVisitor::FCVTZU_fix_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) { - return ConvertFloat(*this, Q, immh, immb, Vn, Vd, Signedness::Unsigned, FloatConversionDirection::FloatToFixed, FP::RoundingMode::TowardsZero); + return ConvertFloat(*this, Q, immh, immb, Vn, Vd, SignednessSSBI::Unsigned, FloatConversionDirectionSSBI::FloatToFixed, FP::RoundingMode::TowardsZero); } } // namespace Dynarmic::A64 diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_three_different.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_three_different.cpp index 8cc677b652..8f460665da 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_three_different.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_three_different.cpp @@ -12,12 +12,12 @@ enum class AbsoluteDifferenceBehavior { Accumulate }; -enum class Signedness { +enum class SignednessSTD { Signed, Unsigned }; -bool AbsoluteDifferenceLong(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, AbsoluteDifferenceBehavior behavior, Signedness sign) { +bool AbsoluteDifferenceLong(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, AbsoluteDifferenceBehavior behavior, SignednessSTD sign) { if (size == 0b11) { return v.ReservedValue(); } @@ -27,7 +27,7 @@ bool AbsoluteDifferenceLong(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, V const IR::U128 operand1 = v.ir.VectorZeroExtend(esize, v.Vpart(datasize, Vn, Q)); const IR::U128 operand2 = v.ir.VectorZeroExtend(esize, v.Vpart(datasize, Vm, Q)); - IR::U128 result = sign == Signedness::Signed ? v.ir.VectorSignedAbsoluteDifference(esize, operand1, operand2) + IR::U128 result = sign == SignednessSTD::Signed ? v.ir.VectorSignedAbsoluteDifference(esize, operand1, operand2) : v.ir.VectorUnsignedAbsoluteDifference(esize, operand1, operand2); if (behavior == AbsoluteDifferenceBehavior::Accumulate) { @@ -45,7 +45,7 @@ enum class MultiplyLongBehavior { Subtract }; -bool MultiplyLong(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, MultiplyLongBehavior behavior, Signedness sign) { +bool MultiplyLong(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, MultiplyLongBehavior behavior, SignednessSTD sign) { if (size == 0b11) { return v.ReservedValue(); } @@ -59,7 +59,7 @@ bool MultiplyLong(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec const auto reg_n = v.Vpart(datasize, Vn, Q); const auto reg_m = v.Vpart(datasize, Vm, Q); - return sign == Signedness::Signed + return sign == SignednessSTD::Signed ? v.ir.VectorMultiplySignedWiden(esize, reg_n, reg_m) : v.ir.VectorMultiplyUnsignedWiden(esize, reg_n, reg_m); }(); @@ -81,7 +81,7 @@ enum class LongOperationBehavior { Subtraction }; -bool LongOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, LongOperationBehavior behavior, Signedness sign) { +bool LongOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, LongOperationBehavior behavior, SignednessSTD sign) { if (size == 0b11) { return v.ReservedValue(); } @@ -92,7 +92,7 @@ bool LongOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Ve const auto get_operand = [&](Vec vec) { const IR::U128 tmp = v.Vpart(64, vec, part); - if (sign == Signedness::Signed) { + if (sign == SignednessSTD::Signed) { return v.ir.VectorSignExtend(esize, tmp); } @@ -118,7 +118,7 @@ enum class WideOperationBehavior { Subtraction }; -bool WideOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, WideOperationBehavior behavior, Signedness sign) { +bool WideOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, WideOperationBehavior behavior, SignednessSTD sign) { if (size == 0b11) { return v.ReservedValue(); } @@ -130,7 +130,7 @@ bool WideOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Ve const IR::U128 operand2 = [&] { const IR::U128 tmp = v.Vpart(64, Vm, part); - if (sign == Signedness::Signed) { + if (sign == SignednessSTD::Signed) { return v.ir.VectorSignExtend(esize, tmp); } @@ -166,75 +166,75 @@ bool TranslatorVisitor::PMULL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::SABAL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return AbsoluteDifferenceLong(*this, Q, size, Vm, Vn, Vd, AbsoluteDifferenceBehavior::Accumulate, Signedness::Signed); + return AbsoluteDifferenceLong(*this, Q, size, Vm, Vn, Vd, AbsoluteDifferenceBehavior::Accumulate, SignednessSTD::Signed); } bool TranslatorVisitor::SABDL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return AbsoluteDifferenceLong(*this, Q, size, Vm, Vn, Vd, AbsoluteDifferenceBehavior::None, Signedness::Signed); + return AbsoluteDifferenceLong(*this, Q, size, Vm, Vn, Vd, AbsoluteDifferenceBehavior::None, SignednessSTD::Signed); } bool TranslatorVisitor::SADDL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return LongOperation(*this, Q, size, Vm, Vn, Vd, LongOperationBehavior::Addition, Signedness::Signed); + return LongOperation(*this, Q, size, Vm, Vn, Vd, LongOperationBehavior::Addition, SignednessSTD::Signed); } bool TranslatorVisitor::SADDW(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return WideOperation(*this, Q, size, Vm, Vn, Vd, WideOperationBehavior::Addition, Signedness::Signed); + return WideOperation(*this, Q, size, Vm, Vn, Vd, WideOperationBehavior::Addition, SignednessSTD::Signed); } bool TranslatorVisitor::SMLAL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::Accumulate, Signedness::Signed); + return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::Accumulate, SignednessSTD::Signed); } bool TranslatorVisitor::SMLSL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::Subtract, Signedness::Signed); + return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::Subtract, SignednessSTD::Signed); } bool TranslatorVisitor::SMULL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::None, Signedness::Signed); + return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::None, SignednessSTD::Signed); } bool TranslatorVisitor::SSUBW(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return WideOperation(*this, Q, size, Vm, Vn, Vd, WideOperationBehavior::Subtraction, Signedness::Signed); + return WideOperation(*this, Q, size, Vm, Vn, Vd, WideOperationBehavior::Subtraction, SignednessSTD::Signed); } bool TranslatorVisitor::SSUBL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return LongOperation(*this, Q, size, Vm, Vn, Vd, LongOperationBehavior::Subtraction, Signedness::Signed); + return LongOperation(*this, Q, size, Vm, Vn, Vd, LongOperationBehavior::Subtraction, SignednessSTD::Signed); } bool TranslatorVisitor::UADDL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return LongOperation(*this, Q, size, Vm, Vn, Vd, LongOperationBehavior::Addition, Signedness::Unsigned); + return LongOperation(*this, Q, size, Vm, Vn, Vd, LongOperationBehavior::Addition, SignednessSTD::Unsigned); } bool TranslatorVisitor::UABAL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return AbsoluteDifferenceLong(*this, Q, size, Vm, Vn, Vd, AbsoluteDifferenceBehavior::Accumulate, Signedness::Unsigned); + return AbsoluteDifferenceLong(*this, Q, size, Vm, Vn, Vd, AbsoluteDifferenceBehavior::Accumulate, SignednessSTD::Unsigned); } bool TranslatorVisitor::UABDL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return AbsoluteDifferenceLong(*this, Q, size, Vm, Vn, Vd, AbsoluteDifferenceBehavior::None, Signedness::Unsigned); + return AbsoluteDifferenceLong(*this, Q, size, Vm, Vn, Vd, AbsoluteDifferenceBehavior::None, SignednessSTD::Unsigned); } bool TranslatorVisitor::UADDW(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return WideOperation(*this, Q, size, Vm, Vn, Vd, WideOperationBehavior::Addition, Signedness::Unsigned); + return WideOperation(*this, Q, size, Vm, Vn, Vd, WideOperationBehavior::Addition, SignednessSTD::Unsigned); } bool TranslatorVisitor::UMLAL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::Accumulate, Signedness::Unsigned); + return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::Accumulate, SignednessSTD::Unsigned); } bool TranslatorVisitor::UMLSL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::Subtract, Signedness::Unsigned); + return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::Subtract, SignednessSTD::Unsigned); } bool TranslatorVisitor::UMULL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::None, Signedness::Unsigned); + return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::None, SignednessSTD::Unsigned); } bool TranslatorVisitor::USUBW(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return WideOperation(*this, Q, size, Vm, Vn, Vd, WideOperationBehavior::Subtraction, Signedness::Unsigned); + return WideOperation(*this, Q, size, Vm, Vn, Vd, WideOperationBehavior::Subtraction, SignednessSTD::Unsigned); } bool TranslatorVisitor::USUBL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return LongOperation(*this, Q, size, Vm, Vn, Vd, LongOperationBehavior::Subtraction, Signedness::Unsigned); + return LongOperation(*this, Q, size, Vm, Vn, Vd, LongOperationBehavior::Subtraction, SignednessSTD::Unsigned); } bool TranslatorVisitor::SQDMULL_vec_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_three_same.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_three_same.cpp index 5c8bf13aeb..1cfc2ced78 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_three_same.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_three_same.cpp @@ -12,12 +12,12 @@ enum class Operation { Subtract, }; -enum class ExtraBehavior { +enum class ExtraBehaviorSTS { None, Round }; -bool HighNarrowingOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, Operation op, ExtraBehavior behavior) { +bool HighNarrowingOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, Operation op, ExtraBehaviorSTS behavior) { if (size == 0b11) { return v.ReservedValue(); } @@ -35,7 +35,7 @@ bool HighNarrowingOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, V return v.ir.VectorSub(doubled_esize, operand1, operand2); }(); - if (behavior == ExtraBehavior::Round) { + if (behavior == ExtraBehaviorSTS::Round) { const u64 round_const = 1ULL << (esize - 1); const IR::U128 round_operand = v.ir.VectorBroadcast(doubled_esize, v.I(doubled_esize, round_const)); wide = v.ir.VectorAdd(doubled_esize, wide, round_operand); @@ -48,12 +48,12 @@ bool HighNarrowingOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, V return true; } -enum class AbsDiffExtraBehavior { +enum class AbsDiffExtraBehaviorSTS { None, Accumulate }; -bool SignedAbsoluteDifference(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, AbsDiffExtraBehavior behavior) { +bool SignedAbsoluteDifference(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, AbsDiffExtraBehaviorSTS behavior) { if (size == 0b11) { return v.ReservedValue(); } @@ -66,7 +66,7 @@ bool SignedAbsoluteDifference(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, const IR::U128 result = [&] { const IR::U128 tmp = v.ir.VectorSignedAbsoluteDifference(esize, operand1, operand2); - if (behavior == AbsDiffExtraBehavior::Accumulate) { + if (behavior == AbsDiffExtraBehaviorSTS::Accumulate) { const IR::U128 d = v.V(datasize, Vd); return v.ir.VectorAdd(esize, d, tmp); } @@ -78,12 +78,12 @@ bool SignedAbsoluteDifference(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, return true; } -enum class Signedness { +enum class SignednessSTS { Signed, Unsigned }; -bool RoundingHalvingAdd(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, Signedness sign) { +bool RoundingHalvingAdd(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, SignednessSTS sign) { if (size == 0b11) { return v.ReservedValue(); } @@ -93,14 +93,14 @@ bool RoundingHalvingAdd(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec V const IR::U128 operand1 = v.V(datasize, Vm); const IR::U128 operand2 = v.V(datasize, Vn); - const IR::U128 result = sign == Signedness::Signed ? v.ir.VectorRoundingHalvingAddSigned(esize, operand1, operand2) + const IR::U128 result = sign == SignednessSTS::Signed ? v.ir.VectorRoundingHalvingAddSigned(esize, operand1, operand2) : v.ir.VectorRoundingHalvingAddUnsigned(esize, operand1, operand2); v.V(datasize, Vd, result); return true; } -bool RoundingShiftLeft(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, Signedness sign) { +bool RoundingShiftLeft(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, SignednessSTS sign) { if (size == 0b11 && !Q) { return v.ReservedValue(); } @@ -111,7 +111,7 @@ bool RoundingShiftLeft(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn const IR::U128 operand1 = v.V(datasize, Vn); const IR::U128 operand2 = v.V(datasize, Vm); const IR::U128 result = [&] { - if (sign == Signedness::Signed) { + if (sign == SignednessSTS::Signed) { return v.ir.VectorRoundingShiftLeftSigned(esize, operand1, operand2); } @@ -122,7 +122,7 @@ bool RoundingShiftLeft(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn return true; } -enum class ComparisonType { +enum class ComparisonTypeSTS { EQ, GE, AbsoluteGE, @@ -130,7 +130,7 @@ enum class ComparisonType { AbsoluteGT }; -bool FPCompareRegister(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd, ComparisonType type) { +bool FPCompareRegister(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd, ComparisonTypeSTS type) { if (sz && !Q) { return v.ReservedValue(); } @@ -142,17 +142,17 @@ bool FPCompareRegister(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec Vn, Ve const IR::U128 operand2 = v.V(datasize, Vm); const IR::U128 result = [&] { switch (type) { - case ComparisonType::EQ: + case ComparisonTypeSTS::EQ: return v.ir.FPVectorEqual(esize, operand1, operand2); - case ComparisonType::GE: + case ComparisonTypeSTS::GE: return v.ir.FPVectorGreaterEqual(esize, operand1, operand2); - case ComparisonType::AbsoluteGE: + case ComparisonTypeSTS::AbsoluteGE: return v.ir.FPVectorGreaterEqual(esize, v.ir.FPVectorAbs(esize, operand1), v.ir.FPVectorAbs(esize, operand2)); - case ComparisonType::GT: + case ComparisonTypeSTS::GT: return v.ir.FPVectorGreater(esize, operand1, operand2); - case ComparisonType::AbsoluteGT: + case ComparisonTypeSTS::AbsoluteGT: return v.ir.FPVectorGreater(esize, v.ir.FPVectorAbs(esize, operand1), v.ir.FPVectorAbs(esize, operand2)); @@ -165,12 +165,12 @@ bool FPCompareRegister(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec Vn, Ve return true; } -enum class MinMaxOperation { +enum class MinMaxOperationSTS { Min, Max, }; -bool VectorMinMaxOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, MinMaxOperation operation, Signedness sign) { +bool VectorMinMaxOperationSTS(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, MinMaxOperationSTS operation, SignednessSTS sign) { if (size == 0b11) { return v.ReservedValue(); } @@ -182,14 +182,14 @@ bool VectorMinMaxOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Ve const IR::U128 operand2 = v.V(datasize, Vm); const IR::U128 result = [&] { switch (operation) { - case MinMaxOperation::Max: - if (sign == Signedness::Signed) { + case MinMaxOperationSTS::Max: + if (sign == SignednessSTS::Signed) { return v.ir.VectorMaxSigned(esize, operand1, operand2); } return v.ir.VectorMaxUnsigned(esize, operand1, operand2); - case MinMaxOperation::Min: - if (sign == Signedness::Signed) { + case MinMaxOperationSTS::Min: + if (sign == SignednessSTS::Signed) { return v.ir.VectorMinSigned(esize, operand1, operand2); } return v.ir.VectorMinUnsigned(esize, operand1, operand2); @@ -203,7 +203,7 @@ bool VectorMinMaxOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Ve return true; } -bool FPMinMaxOperation(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd, MinMaxOperation operation) { +bool FPMinMaxOperationSTS(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd, MinMaxOperationSTS operation) { if (sz && !Q) { return v.ReservedValue(); } @@ -214,7 +214,7 @@ bool FPMinMaxOperation(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec Vn, Ve const IR::U128 operand1 = v.V(datasize, Vn); const IR::U128 operand2 = v.V(datasize, Vm); const IR::U128 result = [&] { - if (operation == MinMaxOperation::Min) { + if (operation == MinMaxOperationSTS::Min) { return v.ir.FPVectorMin(esize, operand1, operand2); } @@ -225,7 +225,7 @@ bool FPMinMaxOperation(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec Vn, Ve return true; } -bool FPMinMaxNumericOperation(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd, MinMaxOperation operation) { +bool FPMinMaxNumericOperation(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd, MinMaxOperationSTS operation) { if (sz && !Q) { return v.ReservedValue(); } @@ -236,7 +236,7 @@ bool FPMinMaxNumericOperation(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec const IR::U128 operand1 = v.V(datasize, Vn); const IR::U128 operand2 = v.V(datasize, Vm); const IR::U128 result = [&] { - if (operation == MinMaxOperation::Min) { + if (operation == MinMaxOperationSTS::Min) { return v.ir.FPVectorMinNumeric(esize, operand1, operand2); } @@ -247,7 +247,7 @@ bool FPMinMaxNumericOperation(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec return true; } -bool PairedMinMaxOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, MinMaxOperation operation, Signedness sign) { +bool PairedMinMaxOperationSTS(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, MinMaxOperationSTS operation, SignednessSTS sign) { if (size == 0b11) { return v.ReservedValue(); } @@ -259,14 +259,14 @@ bool PairedMinMaxOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Ve const IR::U128 operand2 = v.V(datasize, Vm); IR::U128 result = [&] { switch (operation) { - case MinMaxOperation::Max: - if (sign == Signedness::Signed) { + case MinMaxOperationSTS::Max: + if (sign == SignednessSTS::Signed) { return Q ? v.ir.VectorPairedMaxSigned(esize, operand1, operand2) : v.ir.VectorPairedMaxSignedLower(esize, operand1, operand2); } return Q ? v.ir.VectorPairedMaxUnsigned(esize, operand1, operand2) : v.ir.VectorPairedMaxUnsignedLower(esize, operand1, operand2); - case MinMaxOperation::Min: - if (sign == Signedness::Signed) { + case MinMaxOperationSTS::Min: + if (sign == SignednessSTS::Signed) { return Q ? v.ir.VectorPairedMinSigned(esize, operand1, operand2) : v.ir.VectorPairedMinSignedLower(esize, operand1, operand2); } return Q ? v.ir.VectorPairedMinUnsigned(esize, operand1, operand2) : v.ir.VectorPairedMinUnsignedLower(esize, operand1, operand2); @@ -311,7 +311,7 @@ bool FPPairedMinMax(TranslatorVisitor& v, bool Q, bool sz, Vec Vm, Vec Vn, Vec V return true; } -bool SaturatingArithmeticOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, Operation op, Signedness sign) { +bool SaturatingArithmeticOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, Operation op, SignednessSTS sign) { if (size == 0b11 && !Q) { return v.ReservedValue(); } @@ -323,7 +323,7 @@ bool SaturatingArithmeticOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Ve const IR::U128 operand2 = v.V(datasize, Vm); const IR::U128 result = [&] { - if (sign == Signedness::Signed) { + if (sign == SignednessSTS::Signed) { if (op == Operation::Add) { return v.ir.VectorSignedSaturatedAdd(esize, operand1, operand2); } @@ -342,7 +342,7 @@ bool SaturatingArithmeticOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Ve return true; } -bool SaturatingShiftLeft(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, Signedness sign) { +bool SaturatingShiftLeft(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, SignednessSTS sign) { if (size == 0b11 && !Q) { return v.ReservedValue(); } @@ -353,7 +353,7 @@ bool SaturatingShiftLeft(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec const IR::U128 operand1 = v.V(datasize, Vn); const IR::U128 operand2 = v.V(datasize, Vm); const IR::U128 result = [&] { - if (sign == Signedness::Signed) { + if (sign == SignednessSTS::Signed) { return v.ir.VectorSignedSaturatedShiftLeft(esize, operand1, operand2); } @@ -401,27 +401,27 @@ bool TranslatorVisitor::CMGE_reg_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) } bool TranslatorVisitor::SABA(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return SignedAbsoluteDifference(*this, Q, size, Vm, Vn, Vd, AbsDiffExtraBehavior::Accumulate); + return SignedAbsoluteDifference(*this, Q, size, Vm, Vn, Vd, AbsDiffExtraBehaviorSTS::Accumulate); } bool TranslatorVisitor::SABD(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return SignedAbsoluteDifference(*this, Q, size, Vm, Vn, Vd, AbsDiffExtraBehavior::None); + return SignedAbsoluteDifference(*this, Q, size, Vm, Vn, Vd, AbsDiffExtraBehaviorSTS::None); } bool TranslatorVisitor::SMAX(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return VectorMinMaxOperation(*this, Q, size, Vm, Vn, Vd, MinMaxOperation::Max, Signedness::Signed); + return VectorMinMaxOperationSTS(*this, Q, size, Vm, Vn, Vd, MinMaxOperationSTS::Max, SignednessSTS::Signed); } bool TranslatorVisitor::SMAXP(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return PairedMinMaxOperation(*this, Q, size, Vm, Vn, Vd, MinMaxOperation::Max, Signedness::Signed); + return PairedMinMaxOperationSTS(*this, Q, size, Vm, Vn, Vd, MinMaxOperationSTS::Max, SignednessSTS::Signed); } bool TranslatorVisitor::SMIN(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return VectorMinMaxOperation(*this, Q, size, Vm, Vn, Vd, MinMaxOperation::Min, Signedness::Signed); + return VectorMinMaxOperationSTS(*this, Q, size, Vm, Vn, Vd, MinMaxOperationSTS::Min, SignednessSTS::Signed); } bool TranslatorVisitor::SMINP(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return PairedMinMaxOperation(*this, Q, size, Vm, Vn, Vd, MinMaxOperation::Min, Signedness::Signed); + return PairedMinMaxOperationSTS(*this, Q, size, Vm, Vn, Vd, MinMaxOperationSTS::Min, SignednessSTS::Signed); } bool TranslatorVisitor::SQDMULH_vec_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { @@ -506,19 +506,19 @@ bool TranslatorVisitor::MUL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::ADDHN(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return HighNarrowingOperation(*this, Q, size, Vm, Vn, Vd, Operation::Add, ExtraBehavior::None); + return HighNarrowingOperation(*this, Q, size, Vm, Vn, Vd, Operation::Add, ExtraBehaviorSTS::None); } bool TranslatorVisitor::RADDHN(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return HighNarrowingOperation(*this, Q, size, Vm, Vn, Vd, Operation::Add, ExtraBehavior::Round); + return HighNarrowingOperation(*this, Q, size, Vm, Vn, Vd, Operation::Add, ExtraBehaviorSTS::Round); } bool TranslatorVisitor::SUBHN(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return HighNarrowingOperation(*this, Q, size, Vm, Vn, Vd, Operation::Subtract, ExtraBehavior::None); + return HighNarrowingOperation(*this, Q, size, Vm, Vn, Vd, Operation::Subtract, ExtraBehaviorSTS::None); } bool TranslatorVisitor::RSUBHN(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return HighNarrowingOperation(*this, Q, size, Vm, Vn, Vd, Operation::Subtract, ExtraBehavior::Round); + return HighNarrowingOperation(*this, Q, size, Vm, Vn, Vd, Operation::Subtract, ExtraBehaviorSTS::Round); } bool TranslatorVisitor::SHADD(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { @@ -554,15 +554,15 @@ bool TranslatorVisitor::SHSUB(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::SQADD_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return SaturatingArithmeticOperation(*this, Q, size, Vm, Vn, Vd, Operation::Add, Signedness::Signed); + return SaturatingArithmeticOperation(*this, Q, size, Vm, Vn, Vd, Operation::Add, SignednessSTS::Signed); } bool TranslatorVisitor::SQSUB_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return SaturatingArithmeticOperation(*this, Q, size, Vm, Vn, Vd, Operation::Subtract, Signedness::Signed); + return SaturatingArithmeticOperation(*this, Q, size, Vm, Vn, Vd, Operation::Subtract, SignednessSTS::Signed); } bool TranslatorVisitor::SRHADD(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return RoundingHalvingAdd(*this, Q, size, Vm, Vn, Vd, Signedness::Signed); + return RoundingHalvingAdd(*this, Q, size, Vm, Vn, Vd, SignednessSTS::Signed); } bool TranslatorVisitor::UHADD(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { @@ -598,15 +598,15 @@ bool TranslatorVisitor::UHSUB(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::UQADD_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return SaturatingArithmeticOperation(*this, Q, size, Vm, Vn, Vd, Operation::Add, Signedness::Unsigned); + return SaturatingArithmeticOperation(*this, Q, size, Vm, Vn, Vd, Operation::Add, SignednessSTS::Unsigned); } bool TranslatorVisitor::UQSUB_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return SaturatingArithmeticOperation(*this, Q, size, Vm, Vn, Vd, Operation::Subtract, Signedness::Unsigned); + return SaturatingArithmeticOperation(*this, Q, size, Vm, Vn, Vd, Operation::Subtract, SignednessSTS::Unsigned); } bool TranslatorVisitor::URHADD(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return RoundingHalvingAdd(*this, Q, size, Vm, Vn, Vd, Signedness::Unsigned); + return RoundingHalvingAdd(*this, Q, size, Vm, Vn, Vd, SignednessSTS::Unsigned); } bool TranslatorVisitor::ADDP_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { @@ -642,11 +642,11 @@ bool TranslatorVisitor::FABD_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::FACGE_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { - return FPCompareRegister(*this, Q, sz, Vm, Vn, Vd, ComparisonType::AbsoluteGE); + return FPCompareRegister(*this, Q, sz, Vm, Vn, Vd, ComparisonTypeSTS::AbsoluteGE); } bool TranslatorVisitor::FACGT_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { - return FPCompareRegister(*this, Q, sz, Vm, Vn, Vd, ComparisonType::AbsoluteGT); + return FPCompareRegister(*this, Q, sz, Vm, Vn, Vd, ComparisonTypeSTS::AbsoluteGT); } bool TranslatorVisitor::FADD_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { @@ -737,15 +737,15 @@ bool TranslatorVisitor::FCMEQ_reg_3(bool Q, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::FCMEQ_reg_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { - return FPCompareRegister(*this, Q, sz, Vm, Vn, Vd, ComparisonType::EQ); + return FPCompareRegister(*this, Q, sz, Vm, Vn, Vd, ComparisonTypeSTS::EQ); } bool TranslatorVisitor::FCMGE_reg_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { - return FPCompareRegister(*this, Q, sz, Vm, Vn, Vd, ComparisonType::GE); + return FPCompareRegister(*this, Q, sz, Vm, Vn, Vd, ComparisonTypeSTS::GE); } bool TranslatorVisitor::FCMGT_reg_4(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { - return FPCompareRegister(*this, Q, sz, Vm, Vn, Vd, ComparisonType::GT); + return FPCompareRegister(*this, Q, sz, Vm, Vn, Vd, ComparisonTypeSTS::GT); } bool TranslatorVisitor::AND_asimd(bool Q, Vec Vm, Vec Vn, Vec Vd) { @@ -827,11 +827,11 @@ bool TranslatorVisitor::CMTST_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::SQSHL_reg_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return SaturatingShiftLeft(*this, Q, size, Vm, Vn, Vd, Signedness::Signed); + return SaturatingShiftLeft(*this, Q, size, Vm, Vn, Vd, SignednessSTS::Signed); } bool TranslatorVisitor::SRSHL_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return RoundingShiftLeft(*this, Q, size, Vm, Vn, Vd, Signedness::Signed); + return RoundingShiftLeft(*this, Q, size, Vm, Vn, Vd, SignednessSTS::Signed); } bool TranslatorVisitor::SSHL_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { @@ -851,11 +851,11 @@ bool TranslatorVisitor::SSHL_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::UQSHL_reg_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return SaturatingShiftLeft(*this, Q, size, Vm, Vn, Vd, Signedness::Unsigned); + return SaturatingShiftLeft(*this, Q, size, Vm, Vn, Vd, SignednessSTS::Unsigned); } bool TranslatorVisitor::URSHL_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return RoundingShiftLeft(*this, Q, size, Vm, Vn, Vd, Signedness::Unsigned); + return RoundingShiftLeft(*this, Q, size, Vm, Vn, Vd, SignednessSTS::Unsigned); } bool TranslatorVisitor::USHL_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { @@ -875,11 +875,11 @@ bool TranslatorVisitor::USHL_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::UMAX(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return VectorMinMaxOperation(*this, Q, size, Vm, Vn, Vd, MinMaxOperation::Max, Signedness::Unsigned); + return VectorMinMaxOperationSTS(*this, Q, size, Vm, Vn, Vd, MinMaxOperationSTS::Max, SignednessSTS::Unsigned); } bool TranslatorVisitor::UMAXP(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return PairedMinMaxOperation(*this, Q, size, Vm, Vn, Vd, MinMaxOperation::Max, Signedness::Unsigned); + return PairedMinMaxOperationSTS(*this, Q, size, Vm, Vn, Vd, MinMaxOperationSTS::Max, SignednessSTS::Unsigned); } bool TranslatorVisitor::UABA(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { @@ -918,11 +918,11 @@ bool TranslatorVisitor::UABD(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::UMIN(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return VectorMinMaxOperation(*this, Q, size, Vm, Vn, Vd, MinMaxOperation::Min, Signedness::Unsigned); + return VectorMinMaxOperationSTS(*this, Q, size, Vm, Vn, Vd, MinMaxOperationSTS::Min, SignednessSTS::Unsigned); } bool TranslatorVisitor::UMINP(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) { - return PairedMinMaxOperation(*this, Q, size, Vm, Vn, Vd, MinMaxOperation::Min, Signedness::Unsigned); + return PairedMinMaxOperationSTS(*this, Q, size, Vm, Vn, Vd, MinMaxOperationSTS::Min, SignednessSTS::Unsigned); } bool TranslatorVisitor::FSUB_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { @@ -1104,11 +1104,11 @@ bool TranslatorVisitor::EOR_asimd(bool Q, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::FMAX_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { - return FPMinMaxOperation(*this, Q, sz, Vm, Vn, Vd, MinMaxOperation::Max); + return FPMinMaxOperationSTS(*this, Q, sz, Vm, Vn, Vd, MinMaxOperationSTS::Max); } bool TranslatorVisitor::FMAXNM_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { - return FPMinMaxNumericOperation(*this, Q, sz, Vm, Vn, Vd, MinMaxOperation::Max); + return FPMinMaxNumericOperation(*this, Q, sz, Vm, Vn, Vd, MinMaxOperationSTS::Max); } bool TranslatorVisitor::FMAXNMP_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { @@ -1120,11 +1120,11 @@ bool TranslatorVisitor::FMAXP_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { } bool TranslatorVisitor::FMIN_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { - return FPMinMaxOperation(*this, Q, sz, Vm, Vn, Vd, MinMaxOperation::Min); + return FPMinMaxOperationSTS(*this, Q, sz, Vm, Vn, Vd, MinMaxOperationSTS::Min); } bool TranslatorVisitor::FMINNM_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { - return FPMinMaxNumericOperation(*this, Q, sz, Vm, Vn, Vd, MinMaxOperation::Min); + return FPMinMaxNumericOperation(*this, Q, sz, Vm, Vn, Vd, MinMaxOperationSTS::Min); } bool TranslatorVisitor::FMINNMP_vec_2(bool Q, bool sz, Vec Vm, Vec Vn, Vec Vd) { diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_two_register_misc.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_two_register_misc.cpp index ca3e3b9591..80a8e531a7 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_two_register_misc.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_two_register_misc.cpp @@ -8,7 +8,7 @@ namespace Dynarmic::A64 { namespace { -enum class ComparisonType { +enum class ComparisonTypeSTRM { EQ, GE, GT, @@ -16,7 +16,7 @@ enum class ComparisonType { LT, }; -bool CompareAgainstZero(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vn, Vec Vd, ComparisonType type) { +bool CompareAgainstZero(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vn, Vec Vd, ComparisonTypeSTRM type) { if (size == 0b11 && !Q) { return v.ReservedValue(); } @@ -28,15 +28,15 @@ bool CompareAgainstZero(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vn, Vec V const IR::U128 zero = v.ir.ZeroVector(); IR::U128 result = [&] { switch (type) { - case ComparisonType::EQ: + case ComparisonTypeSTRM::EQ: return v.ir.VectorEqual(esize, operand, zero); - case ComparisonType::GE: + case ComparisonTypeSTRM::GE: return v.ir.VectorGreaterEqualSigned(esize, operand, zero); - case ComparisonType::GT: + case ComparisonTypeSTRM::GT: return v.ir.VectorGreaterSigned(esize, operand, zero); - case ComparisonType::LE: + case ComparisonTypeSTRM::LE: return v.ir.VectorLessEqualSigned(esize, operand, zero); - case ComparisonType::LT: + case ComparisonTypeSTRM::LT: default: return v.ir.VectorLessSigned(esize, operand, zero); } @@ -50,7 +50,7 @@ bool CompareAgainstZero(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vn, Vec V return true; } -bool FPCompareAgainstZero(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd, ComparisonType type) { +bool FPCompareAgainstZero(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd, ComparisonTypeSTRM type) { if (sz && !Q) { return v.ReservedValue(); } @@ -62,15 +62,15 @@ bool FPCompareAgainstZero(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd, const IR::U128 zero = v.ir.ZeroVector(); const IR::U128 result = [&] { switch (type) { - case ComparisonType::EQ: + case ComparisonTypeSTRM::EQ: return v.ir.FPVectorEqual(esize, operand, zero); - case ComparisonType::GE: + case ComparisonTypeSTRM::GE: return v.ir.FPVectorGreaterEqual(esize, operand, zero); - case ComparisonType::GT: + case ComparisonTypeSTRM::GT: return v.ir.FPVectorGreater(esize, operand, zero); - case ComparisonType::LE: + case ComparisonTypeSTRM::LE: return v.ir.FPVectorGreaterEqual(esize, zero, operand); - case ComparisonType::LT: + case ComparisonTypeSTRM::LT: return v.ir.FPVectorGreater(esize, zero, operand); } @@ -81,12 +81,12 @@ bool FPCompareAgainstZero(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd, return true; } -enum class Signedness { +enum class SignednessSTRM { Signed, Unsigned }; -bool IntegerConvertToFloat(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd, Signedness signedness) { +bool IntegerConvertToFloat(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd, SignednessSTRM SignednessSTRM) { if (sz && !Q) { return v.ReservedValue(); } @@ -96,7 +96,7 @@ bool IntegerConvertToFloat(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd const FP::RoundingMode rounding_mode = v.ir.current_location->FPCR().RMode(); const IR::U128 operand = v.V(datasize, Vn); - const IR::U128 result = signedness == Signedness::Signed + const IR::U128 result = SignednessSTRM == SignednessSTRM::Signed ? v.ir.FPVectorFromSignedFixed(esize, operand, 0, rounding_mode) : v.ir.FPVectorFromUnsignedFixed(esize, operand, 0, rounding_mode); @@ -104,7 +104,7 @@ bool IntegerConvertToFloat(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd return true; } -bool FloatConvertToInteger(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd, Signedness signedness, FP::RoundingMode rounding_mode) { +bool FloatConvertToInteger(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd, SignednessSTRM SignednessSTRM, FP::RoundingMode rounding_mode) { if (sz && !Q) { return v.ReservedValue(); } @@ -113,7 +113,7 @@ bool FloatConvertToInteger(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd const size_t esize = sz ? 64 : 32; const IR::U128 operand = v.V(datasize, Vn); - const IR::U128 result = signedness == Signedness::Signed + const IR::U128 result = SignednessSTRM == SignednessSTRM::Signed ? v.ir.FPVectorToSignedFixed(esize, operand, 0, rounding_mode) : v.ir.FPVectorToUnsignedFixed(esize, operand, 0, rounding_mode); @@ -168,7 +168,7 @@ enum class PairedAddLongExtraBehavior { Accumulate, }; -bool PairedAddLong(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vn, Vec Vd, Signedness sign, PairedAddLongExtraBehavior behavior) { +bool PairedAddLong(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vn, Vec Vd, SignednessSTRM sign, PairedAddLongExtraBehavior behavior) { if (size == 0b11) { return v.ReservedValue(); } @@ -178,7 +178,7 @@ bool PairedAddLong(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vn, Vec Vd, Si const IR::U128 operand = v.V(datasize, Vn); IR::U128 result = [&] { - if (sign == Signedness::Signed) { + if (sign == SignednessSTRM::Signed) { return v.ir.VectorPairedAddSignedWiden(esize, operand); } @@ -254,23 +254,23 @@ bool TranslatorVisitor::CNT(bool Q, Imm<2> size, Vec Vn, Vec Vd) { } bool TranslatorVisitor::CMGE_zero_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { - return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonType::GE); + return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonTypeSTRM::GE); } bool TranslatorVisitor::CMGT_zero_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { - return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonType::GT); + return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonTypeSTRM::GT); } bool TranslatorVisitor::CMEQ_zero_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { - return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonType::EQ); + return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonTypeSTRM::EQ); } bool TranslatorVisitor::CMLE_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { - return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonType::LE); + return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonTypeSTRM::LE); } bool TranslatorVisitor::CMLT_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { - return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonType::LT); + return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonTypeSTRM::LT); } bool TranslatorVisitor::ABS_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { @@ -341,23 +341,23 @@ bool TranslatorVisitor::FCMEQ_zero_3(bool Q, Vec Vn, Vec Vd) { } bool TranslatorVisitor::FCMEQ_zero_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonType::EQ); + return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonTypeSTRM::EQ); } bool TranslatorVisitor::FCMGE_zero_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonType::GE); + return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonTypeSTRM::GE); } bool TranslatorVisitor::FCMGT_zero_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonType::GT); + return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonTypeSTRM::GT); } bool TranslatorVisitor::FCMLE_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonType::LE); + return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonTypeSTRM::LE); } bool TranslatorVisitor::FCMLT_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonType::LT); + return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonTypeSTRM::LT); } bool TranslatorVisitor::FCVTL(bool Q, bool sz, Vec Vn, Vec Vd) { @@ -411,19 +411,19 @@ bool TranslatorVisitor::FCVTN(bool Q, bool sz, Vec Vn, Vec Vd) { } bool TranslatorVisitor::FCVTNS_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Signed, FP::RoundingMode::ToNearest_TieEven); + return FloatConvertToInteger(*this, Q, sz, Vn, Vd, SignednessSTRM::Signed, FP::RoundingMode::ToNearest_TieEven); } bool TranslatorVisitor::FCVTMS_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Signed, FP::RoundingMode::TowardsMinusInfinity); + return FloatConvertToInteger(*this, Q, sz, Vn, Vd, SignednessSTRM::Signed, FP::RoundingMode::TowardsMinusInfinity); } bool TranslatorVisitor::FCVTAS_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Signed, FP::RoundingMode::ToNearest_TieAwayFromZero); + return FloatConvertToInteger(*this, Q, sz, Vn, Vd, SignednessSTRM::Signed, FP::RoundingMode::ToNearest_TieAwayFromZero); } bool TranslatorVisitor::FCVTPS_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Signed, FP::RoundingMode::TowardsPlusInfinity); + return FloatConvertToInteger(*this, Q, sz, Vn, Vd, SignednessSTRM::Signed, FP::RoundingMode::TowardsPlusInfinity); } bool TranslatorVisitor::FCVTXN_2(bool Q, bool sz, Vec Vn, Vec Vd) { @@ -447,27 +447,27 @@ bool TranslatorVisitor::FCVTXN_2(bool Q, bool sz, Vec Vn, Vec Vd) { } bool TranslatorVisitor::FCVTZS_int_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Signed, FP::RoundingMode::TowardsZero); + return FloatConvertToInteger(*this, Q, sz, Vn, Vd, SignednessSTRM::Signed, FP::RoundingMode::TowardsZero); } bool TranslatorVisitor::FCVTNU_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Unsigned, FP::RoundingMode::ToNearest_TieEven); + return FloatConvertToInteger(*this, Q, sz, Vn, Vd, SignednessSTRM::Unsigned, FP::RoundingMode::ToNearest_TieEven); } bool TranslatorVisitor::FCVTMU_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Unsigned, FP::RoundingMode::TowardsMinusInfinity); + return FloatConvertToInteger(*this, Q, sz, Vn, Vd, SignednessSTRM::Unsigned, FP::RoundingMode::TowardsMinusInfinity); } bool TranslatorVisitor::FCVTAU_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Unsigned, FP::RoundingMode::ToNearest_TieAwayFromZero); + return FloatConvertToInteger(*this, Q, sz, Vn, Vd, SignednessSTRM::Unsigned, FP::RoundingMode::ToNearest_TieAwayFromZero); } bool TranslatorVisitor::FCVTPU_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Unsigned, FP::RoundingMode::TowardsPlusInfinity); + return FloatConvertToInteger(*this, Q, sz, Vn, Vd, SignednessSTRM::Unsigned, FP::RoundingMode::TowardsPlusInfinity); } bool TranslatorVisitor::FCVTZU_int_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Unsigned, FP::RoundingMode::TowardsZero); + return FloatConvertToInteger(*this, Q, sz, Vn, Vd, SignednessSTRM::Unsigned, FP::RoundingMode::TowardsZero); } bool TranslatorVisitor::FRINTN_1(bool Q, Vec Vn, Vec Vd) { @@ -780,19 +780,19 @@ bool TranslatorVisitor::USQADD_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) { } bool TranslatorVisitor::SADALP(bool Q, Imm<2> size, Vec Vn, Vec Vd) { - return PairedAddLong(*this, Q, size, Vn, Vd, Signedness::Signed, PairedAddLongExtraBehavior::Accumulate); + return PairedAddLong(*this, Q, size, Vn, Vd, SignednessSTRM::Signed, PairedAddLongExtraBehavior::Accumulate); } bool TranslatorVisitor::SADDLP(bool Q, Imm<2> size, Vec Vn, Vec Vd) { - return PairedAddLong(*this, Q, size, Vn, Vd, Signedness::Signed, PairedAddLongExtraBehavior::None); + return PairedAddLong(*this, Q, size, Vn, Vd, SignednessSTRM::Signed, PairedAddLongExtraBehavior::None); } bool TranslatorVisitor::UADALP(bool Q, Imm<2> size, Vec Vn, Vec Vd) { - return PairedAddLong(*this, Q, size, Vn, Vd, Signedness::Unsigned, PairedAddLongExtraBehavior::Accumulate); + return PairedAddLong(*this, Q, size, Vn, Vd, SignednessSTRM::Unsigned, PairedAddLongExtraBehavior::Accumulate); } bool TranslatorVisitor::UADDLP(bool Q, Imm<2> size, Vec Vn, Vec Vd) { - return PairedAddLong(*this, Q, size, Vn, Vd, Signedness::Unsigned, PairedAddLongExtraBehavior::None); + return PairedAddLong(*this, Q, size, Vn, Vd, SignednessSTRM::Unsigned, PairedAddLongExtraBehavior::None); } bool TranslatorVisitor::URECPE(bool Q, bool sz, Vec Vn, Vec Vd) { @@ -824,11 +824,11 @@ bool TranslatorVisitor::URSQRTE(bool Q, bool sz, Vec Vn, Vec Vd) { } bool TranslatorVisitor::SCVTF_int_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return IntegerConvertToFloat(*this, Q, sz, Vn, Vd, Signedness::Signed); + return IntegerConvertToFloat(*this, Q, sz, Vn, Vd, SignednessSTRM::Signed); } bool TranslatorVisitor::UCVTF_int_4(bool Q, bool sz, Vec Vn, Vec Vd) { - return IntegerConvertToFloat(*this, Q, sz, Vn, Vd, Signedness::Unsigned); + return IntegerConvertToFloat(*this, Q, sz, Vn, Vd, SignednessSTRM::Unsigned); } bool TranslatorVisitor::SHLL(bool Q, Imm<2> size, Vec Vn, Vec Vd) { diff --git a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_vector_x_indexed_element.cpp b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_vector_x_indexed_element.cpp index 07234fc61b..d23b5fc144 100644 --- a/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_vector_x_indexed_element.cpp +++ b/externals/dynarmic/src/dynarmic/frontend/A64/translate/impl/simd_vector_x_indexed_element.cpp @@ -11,7 +11,7 @@ namespace Dynarmic::A64 { namespace { -std::pair Combine(Imm<2> size, Imm<1> H, Imm<1> L, Imm<1> M, Imm<4> Vmlo) { +std::pair CombineVector(Imm<2> size, Imm<1> H, Imm<1> L, Imm<1> M, Imm<4> Vmlo) { if (size == 0b01) { return {concatenate(H, L, M).ZeroExtend(), Vmlo.ZeroExtend()}; } @@ -19,19 +19,19 @@ std::pair Combine(Imm<2> size, Imm<1> H, Imm<1> L, Imm<1> M, Imm<4> return {concatenate(H, L).ZeroExtend(), concatenate(M, Vmlo).ZeroExtend()}; } -enum class ExtraBehavior { +enum class ExtraBehaviorSVXIE { None, Extended, Accumulate, Subtract, }; -bool MultiplyByElement(TranslatorVisitor& v, bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd, ExtraBehavior extra_behavior) { +bool MultiplyByElement(TranslatorVisitor& v, bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd, ExtraBehaviorSVXIE extra_behavior) { if (size != 0b01 && size != 0b10) { return v.ReservedValue(); } - const auto [index, Vm] = Combine(size, H, L, M, Vmlo); + const auto [index, Vm] = CombineVector(size, H, L, M, Vmlo); const size_t idxdsize = H == 1 ? 128 : 64; const size_t esize = 8 << size.ZeroExtend(); const size_t datasize = Q ? 128 : 64; @@ -41,9 +41,9 @@ bool MultiplyByElement(TranslatorVisitor& v, bool Q, Imm<2> size, Imm<1> L, Imm< const IR::U128 operand3 = v.V(datasize, Vd); IR::U128 result = v.ir.VectorMultiply(esize, operand1, operand2); - if (extra_behavior == ExtraBehavior::Accumulate) { + if (extra_behavior == ExtraBehaviorSVXIE::Accumulate) { result = v.ir.VectorAdd(esize, operand3, result); - } else if (extra_behavior == ExtraBehavior::Subtract) { + } else if (extra_behavior == ExtraBehaviorSVXIE::Subtract) { result = v.ir.VectorSub(esize, operand3, result); } @@ -51,7 +51,7 @@ bool MultiplyByElement(TranslatorVisitor& v, bool Q, Imm<2> size, Imm<1> L, Imm< return true; } -bool FPMultiplyByElement(TranslatorVisitor& v, bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd, ExtraBehavior extra_behavior) { +bool FPMultiplyByElement(TranslatorVisitor& v, bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd, ExtraBehaviorSVXIE extra_behavior) { if (sz && L == 1) { return v.ReservedValue(); } @@ -71,13 +71,13 @@ bool FPMultiplyByElement(TranslatorVisitor& v, bool Q, bool sz, Imm<1> L, Imm<1> const IR::U128 result = [&] { switch (extra_behavior) { - case ExtraBehavior::None: + case ExtraBehaviorSVXIE::None: return v.ir.FPVectorMul(esize, operand1, operand2); - case ExtraBehavior::Extended: + case ExtraBehaviorSVXIE::Extended: return v.ir.FPVectorMulX(esize, operand1, operand2); - case ExtraBehavior::Accumulate: + case ExtraBehaviorSVXIE::Accumulate: return v.ir.FPVectorMulAdd(esize, operand3, operand1, operand2); - case ExtraBehavior::Subtract: + case ExtraBehaviorSVXIE::Subtract: return v.ir.FPVectorMulAdd(esize, operand3, v.ir.FPVectorNeg(esize, operand1), operand2); } UNREACHABLE(); @@ -86,7 +86,7 @@ bool FPMultiplyByElement(TranslatorVisitor& v, bool Q, bool sz, Imm<1> L, Imm<1> return true; } -bool FPMultiplyByElementHalfPrecision(TranslatorVisitor& v, bool Q, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd, ExtraBehavior extra_behavior) { +bool FPMultiplyByElementHalfPrecision(TranslatorVisitor& v, bool Q, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd, ExtraBehaviorSVXIE extra_behavior) { const size_t idxdsize = H == 1 ? 128 : 64; const size_t index = concatenate(H, L, M).ZeroExtend(); const Vec Vm = Vmlo.ZeroExtend(); @@ -101,13 +101,13 @@ bool FPMultiplyByElementHalfPrecision(TranslatorVisitor& v, bool Q, Imm<1> L, Im // regular multiplies and extended multiplies. const IR::U128 result = [&] { switch (extra_behavior) { - case ExtraBehavior::None: + case ExtraBehaviorSVXIE::None: break; - case ExtraBehavior::Extended: + case ExtraBehaviorSVXIE::Extended: break; - case ExtraBehavior::Accumulate: + case ExtraBehaviorSVXIE::Accumulate: return v.ir.FPVectorMulAdd(esize, operand3, operand1, operand2); - case ExtraBehavior::Subtract: + case ExtraBehaviorSVXIE::Subtract: return v.ir.FPVectorMulAdd(esize, operand3, v.ir.FPVectorNeg(esize, operand1), operand2); } UNREACHABLE(); @@ -151,12 +151,12 @@ bool DotProduct(TranslatorVisitor& v, bool Q, Imm<2> size, Imm<1> L, Imm<1> M, I return true; } -enum class Signedness { +enum class SignednessSVXIE { Signed, Unsigned }; -bool MultiplyLong(TranslatorVisitor& v, bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd, ExtraBehavior extra_behavior, Signedness sign) { +bool MultiplyLong(TranslatorVisitor& v, bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd, ExtraBehaviorSVXIE extra_behavior, SignednessSVXIE sign) { if (size == 0b00 || size == 0b11) { return v.ReservedValue(); } @@ -164,23 +164,23 @@ bool MultiplyLong(TranslatorVisitor& v, bool Q, Imm<2> size, Imm<1> L, Imm<1> M, const size_t idxsize = H == 1 ? 128 : 64; const size_t esize = 8 << size.ZeroExtend(); const size_t datasize = 64; - const auto [index, Vm] = Combine(size, H, L, M, Vmlo); + const auto [index, Vm] = CombineVector(size, H, L, M, Vmlo); const IR::U128 operand1 = v.Vpart(datasize, Vn, Q); const IR::U128 operand2 = v.V(idxsize, Vm); const IR::U128 index_vector = v.ir.VectorBroadcastElement(esize, operand2, index); const IR::U128 result = [&] { - const IR::U128 product = sign == Signedness::Signed + const IR::U128 product = sign == SignednessSVXIE::Signed ? v.ir.VectorMultiplySignedWiden(esize, operand1, index_vector) : v.ir.VectorMultiplyUnsignedWiden(esize, operand1, index_vector); - if (extra_behavior == ExtraBehavior::None) { + if (extra_behavior == ExtraBehaviorSVXIE::None) { return product; } const IR::U128 operand3 = v.V(2 * datasize, Vd); - if (extra_behavior == ExtraBehavior::Accumulate) { + if (extra_behavior == ExtraBehaviorSVXIE::Accumulate) { return v.ir.VectorAdd(2 * esize, operand3, product); } @@ -193,15 +193,15 @@ bool MultiplyLong(TranslatorVisitor& v, bool Q, Imm<2> size, Imm<1> L, Imm<1> M, } // Anonymous namespace bool TranslatorVisitor::MLA_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return MultiplyByElement(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Accumulate); + return MultiplyByElement(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::Accumulate); } bool TranslatorVisitor::MLS_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return MultiplyByElement(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Subtract); + return MultiplyByElement(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::Subtract); } bool TranslatorVisitor::MUL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return MultiplyByElement(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::None); + return MultiplyByElement(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::None); } bool TranslatorVisitor::FCMLA_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<2> rot, Imm<1> H, Vec Vn, Vec Vd) { @@ -292,39 +292,39 @@ bool TranslatorVisitor::FCMLA_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4 } bool TranslatorVisitor::FMLA_elt_3(bool Q, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return FPMultiplyByElementHalfPrecision(*this, Q, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Accumulate); + return FPMultiplyByElementHalfPrecision(*this, Q, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::Accumulate); } bool TranslatorVisitor::FMLA_elt_4(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return FPMultiplyByElement(*this, Q, sz, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Accumulate); + return FPMultiplyByElement(*this, Q, sz, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::Accumulate); } bool TranslatorVisitor::FMLS_elt_3(bool Q, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return FPMultiplyByElementHalfPrecision(*this, Q, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Subtract); + return FPMultiplyByElementHalfPrecision(*this, Q, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::Subtract); } bool TranslatorVisitor::FMLS_elt_4(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return FPMultiplyByElement(*this, Q, sz, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Subtract); + return FPMultiplyByElement(*this, Q, sz, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::Subtract); } bool TranslatorVisitor::FMUL_elt_4(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return FPMultiplyByElement(*this, Q, sz, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::None); + return FPMultiplyByElement(*this, Q, sz, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::None); } bool TranslatorVisitor::FMULX_elt_4(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return FPMultiplyByElement(*this, Q, sz, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Extended); + return FPMultiplyByElement(*this, Q, sz, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::Extended); } bool TranslatorVisitor::SMLAL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Accumulate, Signedness::Signed); + return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::Accumulate, SignednessSVXIE::Signed); } bool TranslatorVisitor::SMLSL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Subtract, Signedness::Signed); + return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::Subtract, SignednessSVXIE::Signed); } bool TranslatorVisitor::SMULL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::None, Signedness::Signed); + return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::None, SignednessSVXIE::Signed); } bool TranslatorVisitor::SQDMULL_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { @@ -336,7 +336,7 @@ bool TranslatorVisitor::SQDMULL_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, I const size_t idxsize = H == 1 ? 128 : 64; const size_t esize = 8 << size.ZeroExtend(); const size_t datasize = 64; - const auto [index, Vm] = Combine(size, H, L, M, Vmlo); + const auto [index, Vm] = CombineVector(size, H, L, M, Vmlo); const IR::U128 operand1 = Vpart(datasize, Vn, part); const IR::U128 operand2 = V(idxsize, Vm); @@ -355,7 +355,7 @@ bool TranslatorVisitor::SQDMULH_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, I const size_t idxsize = H == 1 ? 128 : 64; const size_t esize = 8 << size.ZeroExtend(); const size_t datasize = Q ? 128 : 64; - const auto [index, Vm] = Combine(size, H, L, M, Vmlo); + const auto [index, Vm] = CombineVector(size, H, L, M, Vmlo); const IR::U128 operand1 = V(datasize, Vn); const IR::U128 operand2 = V(idxsize, Vm); @@ -374,7 +374,7 @@ bool TranslatorVisitor::SQRDMULH_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, const size_t idxsize = H == 1 ? 128 : 64; const size_t esize = 8 << size.ZeroExtend(); const size_t datasize = Q ? 128 : 64; - const auto [index, Vm] = Combine(size, H, L, M, Vmlo); + const auto [index, Vm] = CombineVector(size, H, L, M, Vmlo); const IR::U128 operand1 = V(datasize, Vn); const IR::U128 operand2 = V(idxsize, Vm); @@ -394,15 +394,15 @@ bool TranslatorVisitor::UDOT_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> } bool TranslatorVisitor::UMLAL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Accumulate, Signedness::Unsigned); + return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::Accumulate, SignednessSVXIE::Unsigned); } bool TranslatorVisitor::UMLSL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Subtract, Signedness::Unsigned); + return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::Subtract, SignednessSVXIE::Unsigned); } bool TranslatorVisitor::UMULL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) { - return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::None, Signedness::Unsigned); + return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehaviorSVXIE::None, SignednessSVXIE::Unsigned); } } // namespace Dynarmic::A64 diff --git a/externals/dynarmic/src/dynarmic/interface/A32/arch_version.h b/externals/dynarmic/src/dynarmic/interface/A32/arch_version.h index 240e40ee4c..209bc594f2 100644 --- a/externals/dynarmic/src/dynarmic/interface/A32/arch_version.h +++ b/externals/dynarmic/src/dynarmic/interface/A32/arch_version.h @@ -5,10 +5,12 @@ #pragma once +#include + namespace Dynarmic { namespace A32 { -enum class ArchVersion { +enum class ArchVersion : std::uint8_t { v3, v4, v4T, diff --git a/externals/dynarmic/src/dynarmic/interface/A32/config.h b/externals/dynarmic/src/dynarmic/interface/A32/config.h index 360df06e2a..033967dc00 100644 --- a/externals/dynarmic/src/dynarmic/interface/A32/config.h +++ b/externals/dynarmic/src/dynarmic/interface/A32/config.h @@ -120,14 +120,32 @@ struct UserCallbacks : public TranslateCallbacks { }; struct UserConfig { + bool HasOptimization(OptimizationFlag f) const { + if (!unsafe_optimizations) { + f &= all_safe_optimizations; + } + return (f & optimizations) != no_optimizations; + } + UserCallbacks* callbacks; - size_t processor_id = 0; ExclusiveMonitor* global_monitor = nullptr; - /// Select the architecture version to use. - /// There are minor behavioural differences between versions. - ArchVersion arch_version = ArchVersion::v8; + // Page Table + // The page table is used for faster memory access. If an entry in the table is nullptr, + // the JIT will fallback to calling the MemoryRead*/MemoryWrite* callbacks. + static constexpr std::size_t PAGE_BITS = 12; + static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - PAGE_BITS); + std::array* page_table = nullptr; + + /// Coprocessors + std::array, 16> coprocessors{}; + + /// Fastmem Pointer + /// This should point to the beginning of a 4GB address space which is in arranged just like + /// what you wish for emulated memory to be. If the host page faults on an address, the JIT + /// will fallback to calling the MemoryRead*/MemoryWrite* callbacks. + std::optional fastmem_pointer = std::nullopt; /// This selects other optimizations than can't otherwise be disabled by setting other /// configuration options. This includes: @@ -137,12 +155,29 @@ struct UserConfig { /// This is intended to be used for debugging. OptimizationFlag optimizations = all_safe_optimizations; - bool HasOptimization(OptimizationFlag f) const { - if (!unsafe_optimizations) { - f &= all_safe_optimizations; - } - return (f & optimizations) != no_optimizations; - } + /// Minimum size is about 8MiB. Maximum size is about 128MiB (arm64 host) or 2GiB (x64 host). + /// Maximum size is limited by the maximum length of a x86_64 / arm64 jump. + std::uint32_t code_cache_size = 128 * 1024 * 1024; // bytes + + /// Processor ID + std::uint32_t processor_id = 0; + + /// Masks out the first N bits in host pointers from the page table. + /// The intention behind this is to allow users of Dynarmic to pack attributes in the + /// same integer and update the pointer attribute pair atomically. + /// If the configured value is 3, all pointers will be forcefully aligned to 8 bytes. + std::int32_t page_table_pointer_mask_bits = 0; + + /// Select the architecture version to use. + /// There are minor behavioural differences between versions. + ArchVersion arch_version = ArchVersion::v8; + + /// Determines if we should detect memory accesses via page_table that straddle are + /// misaligned. Accesses that straddle page boundaries will fallback to the relevant + /// memory callback. + /// This value should be the required access sizes this applies to ORed together. + /// To detect any access, use: 8 | 16 | 32 | 64. + std::uint8_t detect_misaligned_access_via_page_table = 0; /// This enables unsafe optimizations that reduce emulation accuracy in favour of speed. /// For safety, in order to enable unsafe optimizations you have to set BOTH this flag @@ -150,12 +185,6 @@ struct UserConfig { /// The prefered and tested mode for this library is with unsafe optimizations disabled. bool unsafe_optimizations = false; - // Page Table - // The page table is used for faster memory access. If an entry in the table is nullptr, - // the JIT will fallback to calling the MemoryRead*/MemoryWrite* callbacks. - static constexpr std::size_t PAGE_BITS = 12; - static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - PAGE_BITS); - std::array* page_table = nullptr; /// Determines if the pointer in the page_table shall be offseted locally or globally. /// 'false' will access page_table[addr >> bits][addr & mask] /// 'true' will access page_table[addr >> bits][addr] @@ -163,26 +192,11 @@ struct UserConfig { /// So there might be wrongly faulted pages which maps to nullptr. /// This can be avoided by carefully allocating the memory region. bool absolute_offset_page_table = false; - /// Masks out the first N bits in host pointers from the page table. - /// The intention behind this is to allow users of Dynarmic to pack attributes in the - /// same integer and update the pointer attribute pair atomically. - /// If the configured value is 3, all pointers will be forcefully aligned to 8 bytes. - int page_table_pointer_mask_bits = 0; - /// Determines if we should detect memory accesses via page_table that straddle are - /// misaligned. Accesses that straddle page boundaries will fallback to the relevant - /// memory callback. - /// This value should be the required access sizes this applies to ORed together. - /// To detect any access, use: 8 | 16 | 32 | 64. - std::uint8_t detect_misaligned_access_via_page_table = 0; + /// Determines if the above option only triggers when the misalignment straddles a /// page boundary. bool only_detect_misalignment_via_page_table_on_page_boundary = false; - // Fastmem Pointer - // This should point to the beginning of a 4GB address space which is in arranged just like - // what you wish for emulated memory to be. If the host page faults on an address, the JIT - // will fallback to calling the MemoryRead*/MemoryWrite* callbacks. - std::optional fastmem_pointer = std::nullopt; /// Determines if instructions that pagefault should cause recompilation of that block /// with fastmem disabled. /// Recompiled code will use the page_table if this is available, otherwise memory @@ -198,9 +212,6 @@ struct UserConfig { /// callbacks. bool recompile_on_exclusive_fastmem_failure = true; - // Coprocessors - std::array, 16> coprocessors{}; - /// When set to true, UserCallbacks::InstructionSynchronizationBarrierRaised will be /// called when an ISB instruction is executed. /// When set to false, ISB will be treated as a NOP instruction. @@ -234,10 +245,6 @@ struct UserConfig { /// in unusual behavior. bool always_little_endian = false; - // Minimum size is about 8MiB. Maximum size is about 128MiB (arm64 host) or 2GiB (x64 host). - // Maximum size is limited by the maximum length of a x86_64 / arm64 jump. - size_t code_cache_size = 128 * 1024 * 1024; // bytes - /// Internal use only bool very_verbose_debugging_output = false; }; diff --git a/externals/dynarmic/src/dynarmic/interface/A64/config.h b/externals/dynarmic/src/dynarmic/interface/A64/config.h index c8ed623eb4..3563c0b2f4 100644 --- a/externals/dynarmic/src/dynarmic/interface/A64/config.h +++ b/externals/dynarmic/src/dynarmic/interface/A64/config.h @@ -136,11 +136,30 @@ struct UserCallbacks { }; struct UserConfig { + /// Fastmem Pointer + /// This should point to the beginning of a 2^page_table_address_space_bits bytes + /// address space which is in arranged just like what you wish for emulated memory to + /// be. If the host page faults on an address, the JIT will fallback to calling the + /// MemoryRead*/MemoryWrite* callbacks. + std::optional fastmem_pointer = std::nullopt; + UserCallbacks* callbacks; - size_t processor_id = 0; ExclusiveMonitor* global_monitor = nullptr; + /// Pointer to where TPIDRRO_EL0 is stored. This pointer will be inserted into + /// emitted code. + const std::uint64_t* tpidrro_el0 = nullptr; + + /// Pointer to where TPIDR_EL0 is stored. This pointer will be inserted into + /// emitted code. + std::uint64_t* tpidr_el0 = nullptr; + + /// Pointer to the page table which we can use for direct page table access. + /// If an entry in page_table is null, the relevant memory callback will be called. + /// If page_table is nullptr, all memory accesses hit the memory callbacks. + void** page_table = nullptr; + /// This selects other optimizations than can't otherwise be disabled by setting other /// configuration options. This includes: /// - IR optimizations @@ -149,12 +168,50 @@ struct UserConfig { /// This is intended to be used for debugging. OptimizationFlag optimizations = all_safe_optimizations; - bool HasOptimization(OptimizationFlag f) const { - if (!unsafe_optimizations) { - f &= all_safe_optimizations; - } - return (f & optimizations) != no_optimizations; - } + /// Declares how many valid address bits are there in virtual addresses. + /// Determines the size of page_table. Valid values are between 12 and 64 inclusive. + /// This is only used if page_table is not nullptr. + std::uint32_t page_table_address_space_bits = 36; + + /// Masks out the first N bits in host pointers from the page table. + /// The intention behind this is to allow users of Dynarmic to pack attributes in the + /// same integer and update the pointer attribute pair atomically. + /// If the configured value is 3, all pointers will be forcefully aligned to 8 bytes. + std::int32_t page_table_pointer_mask_bits = 0; + + /// Counter-timer frequency register. The value of the register is not interpreted by + /// dynarmic. + std::uint32_t cntfrq_el0 = 600000000; + + /// CTR_EL0<27:24> is log2 of the cache writeback granule in words. + /// CTR_EL0<23:20> is log2 of the exclusives reservation granule in words. + /// CTR_EL0<19:16> is log2 of the smallest data/unified cacheline in words. + /// CTR_EL0<15:14> is the level 1 instruction cache policy. + /// CTR_EL0<3:0> is log2 of the smallest instruction cacheline in words. + std::uint32_t ctr_el0 = 0x8444c004; + + /// DCZID_EL0<3:0> is log2 of the block size in words + /// DCZID_EL0<4> is 0 if the DC ZVA instruction is permitted. + std::uint32_t dczid_el0 = 4; + + /// Declares how many valid address bits are there in virtual addresses. + /// Determines the size of fastmem arena. Valid values are between 12 and 64 inclusive. + /// This is only used if fastmem_pointer is set. + std::uint32_t fastmem_address_space_bits = 36; + + // Minimum size is about 8MiB. Maximum size is about 128MiB (arm64 host) or 2GiB (x64 host). + // Maximum size is limited by the maximum length of a x86_64 / arm64 jump. + std::uint32_t code_cache_size = 128 * 1024 * 1024; // bytes + + /// Determines if we should detect memory accesses via page_table that straddle are + /// misaligned. Accesses that straddle page boundaries will fallback to the relevant + /// memory callback. + /// This value should be the required access sizes this applies to ORed together. + /// To detect any access, use: 8 | 16 | 32 | 64 | 128. + std::uint8_t detect_misaligned_access_via_page_table = 0; + + /// Processor ID + std::uint8_t processor_id = 0; /// This enables unsafe optimizations that reduce emulation accuracy in favour of speed. /// For safety, in order to enable unsafe optimizations you have to set BOTH this flag @@ -177,48 +234,13 @@ struct UserConfig { /// instruction is executed. bool hook_hint_instructions = false; - /// Counter-timer frequency register. The value of the register is not interpreted by - /// dynarmic. - std::uint32_t cntfrq_el0 = 600000000; - - /// CTR_EL0<27:24> is log2 of the cache writeback granule in words. - /// CTR_EL0<23:20> is log2 of the exclusives reservation granule in words. - /// CTR_EL0<19:16> is log2 of the smallest data/unified cacheline in words. - /// CTR_EL0<15:14> is the level 1 instruction cache policy. - /// CTR_EL0<3:0> is log2 of the smallest instruction cacheline in words. - std::uint32_t ctr_el0 = 0x8444c004; - - /// DCZID_EL0<3:0> is log2 of the block size in words - /// DCZID_EL0<4> is 0 if the DC ZVA instruction is permitted. - std::uint32_t dczid_el0 = 4; - - /// Pointer to where TPIDRRO_EL0 is stored. This pointer will be inserted into - /// emitted code. - const std::uint64_t* tpidrro_el0 = nullptr; - - /// Pointer to where TPIDR_EL0 is stored. This pointer will be inserted into - /// emitted code. - std::uint64_t* tpidr_el0 = nullptr; - - /// Pointer to the page table which we can use for direct page table access. - /// If an entry in page_table is null, the relevant memory callback will be called. - /// If page_table is nullptr, all memory accesses hit the memory callbacks. - void** page_table = nullptr; - /// Declares how many valid address bits are there in virtual addresses. - /// Determines the size of page_table. Valid values are between 12 and 64 inclusive. - /// This is only used if page_table is not nullptr. - size_t page_table_address_space_bits = 36; - /// Masks out the first N bits in host pointers from the page table. - /// The intention behind this is to allow users of Dynarmic to pack attributes in the - /// same integer and update the pointer attribute pair atomically. - /// If the configured value is 3, all pointers will be forcefully aligned to 8 bytes. - int page_table_pointer_mask_bits = 0; /// Determines what happens if the guest accesses an entry that is off the end of the /// page table. If true, Dynarmic will silently mirror page_table's address space. If /// false, accessing memory outside of page_table bounds will result in a call to the /// relevant memory callback. /// This is only used if page_table is not nullptr. bool silently_mirror_page_table = true; + /// Determines if the pointer in the page_table shall be offseted locally or globally. /// 'false' will access page_table[addr >> bits][addr & mask] /// 'true' will access page_table[addr >> bits][addr] @@ -226,31 +248,17 @@ struct UserConfig { /// So there might be wrongly faulted pages which maps to nullptr. /// This can be avoided by carefully allocating the memory region. bool absolute_offset_page_table = false; - /// Determines if we should detect memory accesses via page_table that straddle are - /// misaligned. Accesses that straddle page boundaries will fallback to the relevant - /// memory callback. - /// This value should be the required access sizes this applies to ORed together. - /// To detect any access, use: 8 | 16 | 32 | 64 | 128. - std::uint8_t detect_misaligned_access_via_page_table = 0; + /// Determines if the above option only triggers when the misalignment straddles a /// page boundary. bool only_detect_misalignment_via_page_table_on_page_boundary = false; - /// Fastmem Pointer - /// This should point to the beginning of a 2^page_table_address_space_bits bytes - /// address space which is in arranged just like what you wish for emulated memory to - /// be. If the host page faults on an address, the JIT will fallback to calling the - /// MemoryRead*/MemoryWrite* callbacks. - std::optional fastmem_pointer = std::nullopt; /// Determines if instructions that pagefault should cause recompilation of that block /// with fastmem disabled. /// Recompiled code will use the page_table if this is available, otherwise memory /// accesses will hit the memory callbacks. bool recompile_on_fastmem_failure = true; - /// Declares how many valid address bits are there in virtual addresses. - /// Determines the size of fastmem arena. Valid values are between 12 and 64 inclusive. - /// This is only used if fastmem_pointer is set. - size_t fastmem_address_space_bits = 36; + /// Determines what happens if the guest accesses an entry that is off the end of the /// fastmem arena. If true, Dynarmic will silently mirror fastmem's address space. If /// false, accessing memory outside of fastmem bounds will result in a call to the @@ -285,12 +293,15 @@ struct UserConfig { /// AddTicks and GetTicksRemaining are never called, and no cycle counting is done. bool enable_cycle_counting = true; - // Minimum size is about 8MiB. Maximum size is about 128MiB (arm64 host) or 2GiB (x64 host). - // Maximum size is limited by the maximum length of a x86_64 / arm64 jump. - size_t code_cache_size = 128 * 1024 * 1024; // bytes - /// Internal use only bool very_verbose_debugging_output = false; + + inline bool HasOptimization(OptimizationFlag f) const { + if (!unsafe_optimizations) { + f &= all_safe_optimizations; + } + return (f & optimizations) != no_optimizations; + } }; } // namespace A64 diff --git a/externals/dynarmic/src/dynarmic/ir/ir_emitter.cpp b/externals/dynarmic/src/dynarmic/ir/ir_emitter.cpp index fc4f69b3e0..3734aae4d5 100644 --- a/externals/dynarmic/src/dynarmic/ir/ir_emitter.cpp +++ b/externals/dynarmic/src/dynarmic/ir/ir_emitter.cpp @@ -14,2877 +14,5 @@ namespace Dynarmic::IR { -U1 IREmitter::Imm1(bool imm1) const { - return U1(Value(imm1)); -} - -U8 IREmitter::Imm8(u8 imm8) const { - return U8(Value(imm8)); -} - -U16 IREmitter::Imm16(u16 imm16) const { - return U16(Value(imm16)); -} - -U32 IREmitter::Imm32(u32 imm32) const { - return U32(Value(imm32)); -} - -U64 IREmitter::Imm64(u64 imm64) const { - return U64(Value(imm64)); -} - -void IREmitter::PushRSB(const LocationDescriptor& return_location) { - Inst(Opcode::PushRSB, IR::Value(return_location.Value())); -} - -U64 IREmitter::Pack2x32To1x64(const U32& lo, const U32& hi) { - return Inst(Opcode::Pack2x32To1x64, lo, hi); -} - -U128 IREmitter::Pack2x64To1x128(const U64& lo, const U64& hi) { - return Inst(Opcode::Pack2x64To1x128, lo, hi); -} - -UAny IREmitter::LeastSignificant(size_t bitsize, const U32U64& value) { - switch (bitsize) { - case 8: - return LeastSignificantByte(value); - case 16: - return LeastSignificantHalf(value); - case 32: - if (value.GetType() == Type::U32) { - return value; - } - return LeastSignificantWord(value); - case 64: - ASSERT(value.GetType() == Type::U64); - return value; - } - ASSERT_FALSE("Invalid bitsize"); -} - -U32 IREmitter::LeastSignificantWord(const U64& value) { - return Inst(Opcode::LeastSignificantWord, value); -} - -U16 IREmitter::LeastSignificantHalf(U32U64 value) { - if (value.GetType() == Type::U64) { - value = LeastSignificantWord(value); - } - return Inst(Opcode::LeastSignificantHalf, value); -} - -U8 IREmitter::LeastSignificantByte(U32U64 value) { - if (value.GetType() == Type::U64) { - value = LeastSignificantWord(value); - } - return Inst(Opcode::LeastSignificantByte, value); -} - -ResultAndCarry IREmitter::MostSignificantWord(const U64& value) { - const auto result = Inst(Opcode::MostSignificantWord, value); - const auto carry_out = Inst(Opcode::GetCarryFromOp, result); - return {result, carry_out}; -} - -U1 IREmitter::MostSignificantBit(const U32& value) { - return Inst(Opcode::MostSignificantBit, value); -} - -U1 IREmitter::IsZero(const U32& value) { - return Inst(Opcode::IsZero32, value); -} - -U1 IREmitter::IsZero(const U64& value) { - return Inst(Opcode::IsZero64, value); -} - -U1 IREmitter::IsZero(const U32U64& value) { - if (value.GetType() == Type::U32) { - return Inst(Opcode::IsZero32, value); - } else { - return Inst(Opcode::IsZero64, value); - } -} - -U1 IREmitter::TestBit(const U32U64& value, const U8& bit) { - if (value.GetType() == Type::U32) { - return Inst(Opcode::TestBit, IndeterminateExtendToLong(value), bit); - } else { - return Inst(Opcode::TestBit, value, bit); - } -} - -U32 IREmitter::ConditionalSelect(Cond cond, const U32& a, const U32& b) { - return Inst(Opcode::ConditionalSelect32, Value{cond}, a, b); -} - -U64 IREmitter::ConditionalSelect(Cond cond, const U64& a, const U64& b) { - return Inst(Opcode::ConditionalSelect64, Value{cond}, a, b); -} - -NZCV IREmitter::ConditionalSelect(Cond cond, const NZCV& a, const NZCV& b) { - return Inst(Opcode::ConditionalSelectNZCV, Value{cond}, a, b); -} - -U32U64 IREmitter::ConditionalSelect(Cond cond, const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - if (a.GetType() == Type::U32) { - return Inst(Opcode::ConditionalSelect32, Value{cond}, a, b); - } else { - return Inst(Opcode::ConditionalSelect64, Value{cond}, a, b); - } -} - -U1 IREmitter::GetCFlagFromNZCV(const NZCV& nzcv) { - return Inst(Opcode::GetCFlagFromNZCV, nzcv); -} - -NZCV IREmitter::NZCVFromPackedFlags(const U32& a) { - return Inst(Opcode::NZCVFromPackedFlags, a); -} - -NZCV IREmitter::NZCVFrom(const Value& value) { - return Inst(Opcode::GetNZCVFromOp, value); -} - -ResultAndCarry IREmitter::LogicalShiftLeft(const U32& value_in, const U8& shift_amount, const U1& carry_in) { - const auto result = Inst(Opcode::LogicalShiftLeft32, value_in, shift_amount, carry_in); - const auto carry_out = Inst(Opcode::GetCarryFromOp, result); - return {result, carry_out}; -} - -ResultAndCarry IREmitter::LogicalShiftRight(const U32& value_in, const U8& shift_amount, const U1& carry_in) { - const auto result = Inst(Opcode::LogicalShiftRight32, value_in, shift_amount, carry_in); - const auto carry_out = Inst(Opcode::GetCarryFromOp, result); - return {result, carry_out}; -} - -ResultAndCarry IREmitter::ArithmeticShiftRight(const U32& value_in, const U8& shift_amount, const U1& carry_in) { - const auto result = Inst(Opcode::ArithmeticShiftRight32, value_in, shift_amount, carry_in); - const auto carry_out = Inst(Opcode::GetCarryFromOp, result); - return {result, carry_out}; -} - -ResultAndCarry IREmitter::RotateRight(const U32& value_in, const U8& shift_amount, const U1& carry_in) { - const auto result = Inst(Opcode::RotateRight32, value_in, shift_amount, carry_in); - const auto carry_out = Inst(Opcode::GetCarryFromOp, result); - return {result, carry_out}; -} - -ResultAndCarry IREmitter::RotateRightExtended(const U32& value_in, const U1& carry_in) { - const auto result = Inst(Opcode::RotateRightExtended, value_in, carry_in); - const auto carry_out = Inst(Opcode::GetCarryFromOp, result); - return {result, carry_out}; -} - -U32U64 IREmitter::LogicalShiftLeft(const U32U64& value_in, const U8& shift_amount) { - if (value_in.GetType() == Type::U32) { - return Inst(Opcode::LogicalShiftLeft32, value_in, shift_amount, Imm1(0)); - } else { - return Inst(Opcode::LogicalShiftLeft64, value_in, shift_amount); - } -} - -U32U64 IREmitter::LogicalShiftRight(const U32U64& value_in, const U8& shift_amount) { - if (value_in.GetType() == Type::U32) { - return Inst(Opcode::LogicalShiftRight32, value_in, shift_amount, Imm1(0)); - } else { - return Inst(Opcode::LogicalShiftRight64, value_in, shift_amount); - } -} - -U32U64 IREmitter::ArithmeticShiftRight(const U32U64& value_in, const U8& shift_amount) { - if (value_in.GetType() == Type::U32) { - return Inst(Opcode::ArithmeticShiftRight32, value_in, shift_amount, Imm1(0)); - } else { - return Inst(Opcode::ArithmeticShiftRight64, value_in, shift_amount); - } -} - -U32U64 IREmitter::RotateRight(const U32U64& value_in, const U8& shift_amount) { - if (value_in.GetType() == Type::U32) { - return Inst(Opcode::RotateRight32, value_in, shift_amount, Imm1(0)); - } else { - return Inst(Opcode::RotateRight64, value_in, shift_amount); - } -} - -U32U64 IREmitter::LogicalShiftLeftMasked(const U32U64& value_in, const U32U64& shift_amount) { - ASSERT(value_in.GetType() == shift_amount.GetType()); - if (value_in.GetType() == Type::U32) { - return Inst(Opcode::LogicalShiftLeftMasked32, value_in, shift_amount); - } else { - return Inst(Opcode::LogicalShiftLeftMasked64, value_in, shift_amount); - } -} - -U32U64 IREmitter::LogicalShiftRightMasked(const U32U64& value_in, const U32U64& shift_amount) { - ASSERT(value_in.GetType() == shift_amount.GetType()); - if (value_in.GetType() == Type::U32) { - return Inst(Opcode::LogicalShiftRightMasked32, value_in, shift_amount); - } else { - return Inst(Opcode::LogicalShiftRightMasked64, value_in, shift_amount); - } -} - -U32U64 IREmitter::ArithmeticShiftRightMasked(const U32U64& value_in, const U32U64& shift_amount) { - ASSERT(value_in.GetType() == shift_amount.GetType()); - if (value_in.GetType() == Type::U32) { - return Inst(Opcode::ArithmeticShiftRightMasked32, value_in, shift_amount); - } else { - return Inst(Opcode::ArithmeticShiftRightMasked64, value_in, shift_amount); - } -} - -U32U64 IREmitter::RotateRightMasked(const U32U64& value_in, const U32U64& shift_amount) { - ASSERT(value_in.GetType() == shift_amount.GetType()); - if (value_in.GetType() == Type::U32) { - return Inst(Opcode::RotateRightMasked32, value_in, shift_amount); - } else { - return Inst(Opcode::RotateRightMasked64, value_in, shift_amount); - } -} - -U32U64 IREmitter::AddWithCarry(const U32U64& a, const U32U64& b, const U1& carry_in) { - ASSERT(a.GetType() == b.GetType()); - if (a.GetType() == Type::U32) { - return Inst(Opcode::Add32, a, b, carry_in); - } else { - return Inst(Opcode::Add64, a, b, carry_in); - } -} - -U32U64 IREmitter::Add(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - if (a.GetType() == Type::U32) { - return Inst(Opcode::Add32, a, b, Imm1(0)); - } else { - return Inst(Opcode::Add64, a, b, Imm1(0)); - } -} - -U32U64 IREmitter::SubWithCarry(const U32U64& a, const U32U64& b, const U1& carry_in) { - ASSERT(a.GetType() == b.GetType()); - if (a.GetType() == Type::U32) { - return Inst(Opcode::Sub32, a, b, carry_in); - } else { - return Inst(Opcode::Sub64, a, b, carry_in); - } -} - -U32U64 IREmitter::Sub(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - if (a.GetType() == Type::U32) { - return Inst(Opcode::Sub32, a, b, Imm1(1)); - } else { - return Inst(Opcode::Sub64, a, b, Imm1(1)); - } -} - -U32U64 IREmitter::Mul(const U32U64& a, const U32U64& b) { - if (a.GetType() == Type::U32) { - return Inst(Opcode::Mul32, a, b); - } - - return Inst(Opcode::Mul64, a, b); -} - -U64 IREmitter::UnsignedMultiplyHigh(const U64& a, const U64& b) { - return Inst(Opcode::UnsignedMultiplyHigh64, a, b); -} - -U64 IREmitter::SignedMultiplyHigh(const U64& a, const U64& b) { - return Inst(Opcode::SignedMultiplyHigh64, a, b); -} - -U32U64 IREmitter::UnsignedDiv(const U32U64& a, const U32U64& b) { - if (a.GetType() == Type::U32) { - return Inst(Opcode::UnsignedDiv32, a, b); - } - - return Inst(Opcode::UnsignedDiv64, a, b); -} - -U32U64 IREmitter::SignedDiv(const U32U64& a, const U32U64& b) { - if (a.GetType() == Type::U32) { - return Inst(Opcode::SignedDiv32, a, b); - } - - return Inst(Opcode::SignedDiv64, a, b); -} - -U32U64 IREmitter::And(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - if (a.GetType() == Type::U32) { - return Inst(Opcode::And32, a, b); - } else { - return Inst(Opcode::And64, a, b); - } -} - -U32U64 IREmitter::AndNot(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - if (a.GetType() == Type::U32) { - return Inst(Opcode::AndNot32, a, b); - } else { - return Inst(Opcode::AndNot64, a, b); - } -} - -U32U64 IREmitter::Eor(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - if (a.GetType() == Type::U32) { - return Inst(Opcode::Eor32, a, b); - } else { - return Inst(Opcode::Eor64, a, b); - } -} - -U32U64 IREmitter::Or(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - if (a.GetType() == Type::U32) { - return Inst(Opcode::Or32, a, b); - } else { - return Inst(Opcode::Or64, a, b); - } -} - -U32U64 IREmitter::Not(const U32U64& a) { - if (a.GetType() == Type::U32) { - return Inst(Opcode::Not32, a); - } else { - return Inst(Opcode::Not64, a); - } -} - -U64 IREmitter::SignExtendToLong(const UAny& a) { - switch (a.GetType()) { - case Type::U8: - return Inst(Opcode::SignExtendByteToLong, a); - case Type::U16: - return Inst(Opcode::SignExtendHalfToLong, a); - case Type::U32: - return Inst(Opcode::SignExtendWordToLong, a); - case Type::U64: - return U64(a); - default: - UNREACHABLE(); - } -} - -U32 IREmitter::SignExtendToWord(const UAny& a) { - switch (a.GetType()) { - case Type::U8: - return Inst(Opcode::SignExtendByteToWord, a); - case Type::U16: - return Inst(Opcode::SignExtendHalfToWord, a); - case Type::U32: - return U32(a); - case Type::U64: - return Inst(Opcode::LeastSignificantWord, a); - default: - UNREACHABLE(); - } -} - -U64 IREmitter::SignExtendWordToLong(const U32& a) { - return Inst(Opcode::SignExtendWordToLong, a); -} - -U32 IREmitter::SignExtendHalfToWord(const U16& a) { - return Inst(Opcode::SignExtendHalfToWord, a); -} - -U32 IREmitter::SignExtendByteToWord(const U8& a) { - return Inst(Opcode::SignExtendByteToWord, a); -} - -U64 IREmitter::ZeroExtendToLong(const UAny& a) { - switch (a.GetType()) { - case Type::U8: - return Inst(Opcode::ZeroExtendByteToLong, a); - case Type::U16: - return Inst(Opcode::ZeroExtendHalfToLong, a); - case Type::U32: - return Inst(Opcode::ZeroExtendWordToLong, a); - case Type::U64: - return U64(a); - default: - UNREACHABLE(); - } -} - -U32 IREmitter::ZeroExtendToWord(const UAny& a) { - switch (a.GetType()) { - case Type::U8: - return Inst(Opcode::ZeroExtendByteToWord, a); - case Type::U16: - return Inst(Opcode::ZeroExtendHalfToWord, a); - case Type::U32: - return U32(a); - case Type::U64: - return Inst(Opcode::LeastSignificantWord, a); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::ZeroExtendToQuad(const UAny& a) { - return Inst(Opcode::ZeroExtendLongToQuad, ZeroExtendToLong(a)); -} - -U64 IREmitter::ZeroExtendWordToLong(const U32& a) { - return Inst(Opcode::ZeroExtendWordToLong, a); -} - -U32 IREmitter::ZeroExtendHalfToWord(const U16& a) { - return Inst(Opcode::ZeroExtendHalfToWord, a); -} - -U32 IREmitter::ZeroExtendByteToWord(const U8& a) { - return Inst(Opcode::ZeroExtendByteToWord, a); -} - -U32 IREmitter::IndeterminateExtendToWord(const UAny& a) { - // TODO: Implement properly - return ZeroExtendToWord(a); -} - -U64 IREmitter::IndeterminateExtendToLong(const UAny& a) { - // TODO: Implement properly - return ZeroExtendToLong(a); -} - -U32 IREmitter::ByteReverseWord(const U32& a) { - return Inst(Opcode::ByteReverseWord, a); -} - -U16 IREmitter::ByteReverseHalf(const U16& a) { - return Inst(Opcode::ByteReverseHalf, a); -} - -U64 IREmitter::ByteReverseDual(const U64& a) { - return Inst(Opcode::ByteReverseDual, a); -} - -U32U64 IREmitter::CountLeadingZeros(const U32U64& a) { - if (a.GetType() == IR::Type::U32) { - return Inst(Opcode::CountLeadingZeros32, a); - } - - return Inst(Opcode::CountLeadingZeros64, a); -} - -U32U64 IREmitter::ExtractRegister(const U32U64& a, const U32U64& b, const U8& lsb) { - if (a.GetType() == IR::Type::U32) { - return Inst(Opcode::ExtractRegister32, a, b, lsb); - } - - return Inst(Opcode::ExtractRegister64, a, b, lsb); -} - -U32U64 IREmitter::ReplicateBit(const U32U64& a, u8 bit) { - if (a.GetType() == IR::Type::U32) { - ASSERT(bit < 32); - return Inst(Opcode::ReplicateBit32, a, Imm8(bit)); - } - - ASSERT(bit < 64); - return Inst(Opcode::ReplicateBit64, a, Imm8(bit)); -} - -U32U64 IREmitter::MaxSigned(const U32U64& a, const U32U64& b) { - if (a.GetType() == IR::Type::U32) { - return Inst(Opcode::MaxSigned32, a, b); - } - - return Inst(Opcode::MaxSigned64, a, b); -} - -U32U64 IREmitter::MaxUnsigned(const U32U64& a, const U32U64& b) { - if (a.GetType() == IR::Type::U32) { - return Inst(Opcode::MaxUnsigned32, a, b); - } - - return Inst(Opcode::MaxUnsigned64, a, b); -} - -U32U64 IREmitter::MinSigned(const U32U64& a, const U32U64& b) { - if (a.GetType() == IR::Type::U32) { - return Inst(Opcode::MinSigned32, a, b); - } - - return Inst(Opcode::MinSigned64, a, b); -} - -U32U64 IREmitter::MinUnsigned(const U32U64& a, const U32U64& b) { - if (a.GetType() == IR::Type::U32) { - return Inst(Opcode::MinUnsigned32, a, b); - } - - return Inst(Opcode::MinUnsigned64, a, b); -} - -ResultAndOverflow IREmitter::SignedSaturatedAddWithFlag(const U32& a, const U32& b) { - const auto result = Inst(Opcode::SignedSaturatedAddWithFlag32, a, b); - const auto overflow = Inst(Opcode::GetOverflowFromOp, result); - return {result, overflow}; -} - -ResultAndOverflow IREmitter::SignedSaturatedSubWithFlag(const U32& a, const U32& b) { - const auto result = Inst(Opcode::SignedSaturatedSubWithFlag32, a, b); - const auto overflow = Inst(Opcode::GetOverflowFromOp, result); - return {result, overflow}; -} - -ResultAndOverflow IREmitter::SignedSaturation(const U32& a, size_t bit_size_to_saturate_to) { - ASSERT(bit_size_to_saturate_to >= 1 && bit_size_to_saturate_to <= 32); - const auto result = Inst(Opcode::SignedSaturation, a, Imm8(static_cast(bit_size_to_saturate_to))); - const auto overflow = Inst(Opcode::GetOverflowFromOp, result); - return {result, overflow}; -} - -ResultAndOverflow IREmitter::UnsignedSaturation(const U32& a, size_t bit_size_to_saturate_to) { - ASSERT(bit_size_to_saturate_to <= 31); - const auto result = Inst(Opcode::UnsignedSaturation, a, Imm8(static_cast(bit_size_to_saturate_to))); - const auto overflow = Inst(Opcode::GetOverflowFromOp, result); - return {result, overflow}; -} - -UAny IREmitter::SignedSaturatedAdd(const UAny& a, const UAny& b) { - ASSERT(a.GetType() == b.GetType()); - const auto result = [&]() -> IR::UAny { - switch (a.GetType()) { - case IR::Type::U8: - return Inst(Opcode::SignedSaturatedAdd8, a, b); - case IR::Type::U16: - return Inst(Opcode::SignedSaturatedAdd16, a, b); - case IR::Type::U32: - return Inst(Opcode::SignedSaturatedAdd32, a, b); - case IR::Type::U64: - return Inst(Opcode::SignedSaturatedAdd64, a, b); - default: - return IR::UAny{}; - } - }(); - return result; -} - -UAny IREmitter::SignedSaturatedDoublingMultiplyReturnHigh(const UAny& a, const UAny& b) { - ASSERT(a.GetType() == b.GetType()); - const auto result = [&]() -> IR::UAny { - switch (a.GetType()) { - case IR::Type::U16: - return Inst(Opcode::SignedSaturatedDoublingMultiplyReturnHigh16, a, b); - case IR::Type::U32: - return Inst(Opcode::SignedSaturatedDoublingMultiplyReturnHigh32, a, b); - default: - UNREACHABLE(); - } - }(); - return result; -} - -UAny IREmitter::SignedSaturatedSub(const UAny& a, const UAny& b) { - ASSERT(a.GetType() == b.GetType()); - const auto result = [&]() -> IR::UAny { - switch (a.GetType()) { - case IR::Type::U8: - return Inst(Opcode::SignedSaturatedSub8, a, b); - case IR::Type::U16: - return Inst(Opcode::SignedSaturatedSub16, a, b); - case IR::Type::U32: - return Inst(Opcode::SignedSaturatedSub32, a, b); - case IR::Type::U64: - return Inst(Opcode::SignedSaturatedSub64, a, b); - default: - return IR::UAny{}; - } - }(); - return result; -} - -UAny IREmitter::UnsignedSaturatedAdd(const UAny& a, const UAny& b) { - ASSERT(a.GetType() == b.GetType()); - const auto result = [&]() -> IR::UAny { - switch (a.GetType()) { - case IR::Type::U8: - return Inst(Opcode::UnsignedSaturatedAdd8, a, b); - case IR::Type::U16: - return Inst(Opcode::UnsignedSaturatedAdd16, a, b); - case IR::Type::U32: - return Inst(Opcode::UnsignedSaturatedAdd32, a, b); - case IR::Type::U64: - return Inst(Opcode::UnsignedSaturatedAdd64, a, b); - default: - return IR::UAny{}; - } - }(); - return result; -} - -UAny IREmitter::UnsignedSaturatedSub(const UAny& a, const UAny& b) { - ASSERT(a.GetType() == b.GetType()); - const auto result = [&]() -> IR::UAny { - switch (a.GetType()) { - case IR::Type::U8: - return Inst(Opcode::UnsignedSaturatedSub8, a, b); - case IR::Type::U16: - return Inst(Opcode::UnsignedSaturatedSub16, a, b); - case IR::Type::U32: - return Inst(Opcode::UnsignedSaturatedSub32, a, b); - case IR::Type::U64: - return Inst(Opcode::UnsignedSaturatedSub64, a, b); - default: - return IR::UAny{}; - } - }(); - return result; -} - -U128 IREmitter::VectorSignedSaturatedAdd(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorSignedSaturatedAdd8, a, b); - case 16: - return Inst(Opcode::VectorSignedSaturatedAdd16, a, b); - case 32: - return Inst(Opcode::VectorSignedSaturatedAdd32, a, b); - case 64: - return Inst(Opcode::VectorSignedSaturatedAdd64, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorSignedSaturatedSub(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorSignedSaturatedSub8, a, b); - case 16: - return Inst(Opcode::VectorSignedSaturatedSub16, a, b); - case 32: - return Inst(Opcode::VectorSignedSaturatedSub32, a, b); - case 64: - return Inst(Opcode::VectorSignedSaturatedSub64, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorUnsignedSaturatedAdd(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorUnsignedSaturatedAdd8, a, b); - case 16: - return Inst(Opcode::VectorUnsignedSaturatedAdd16, a, b); - case 32: - return Inst(Opcode::VectorUnsignedSaturatedAdd32, a, b); - case 64: - return Inst(Opcode::VectorUnsignedSaturatedAdd64, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorUnsignedSaturatedSub(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorUnsignedSaturatedSub8, a, b); - case 16: - return Inst(Opcode::VectorUnsignedSaturatedSub16, a, b); - case 32: - return Inst(Opcode::VectorUnsignedSaturatedSub32, a, b); - case 64: - return Inst(Opcode::VectorUnsignedSaturatedSub64, a, b); - default: - UNREACHABLE(); - } -} - -ResultAndGE IREmitter::PackedAddU8(const U32& a, const U32& b) { - const auto result = Inst(Opcode::PackedAddU8, a, b); - const auto ge = Inst(Opcode::GetGEFromOp, result); - return {result, ge}; -} - -ResultAndGE IREmitter::PackedAddS8(const U32& a, const U32& b) { - const auto result = Inst(Opcode::PackedAddS8, a, b); - const auto ge = Inst(Opcode::GetGEFromOp, result); - return {result, ge}; -} - -ResultAndGE IREmitter::PackedAddU16(const U32& a, const U32& b) { - const auto result = Inst(Opcode::PackedAddU16, a, b); - const auto ge = Inst(Opcode::GetGEFromOp, result); - return {result, ge}; -} - -ResultAndGE IREmitter::PackedAddS16(const U32& a, const U32& b) { - const auto result = Inst(Opcode::PackedAddS16, a, b); - const auto ge = Inst(Opcode::GetGEFromOp, result); - return {result, ge}; -} - -ResultAndGE IREmitter::PackedSubU8(const U32& a, const U32& b) { - const auto result = Inst(Opcode::PackedSubU8, a, b); - const auto ge = Inst(Opcode::GetGEFromOp, result); - return {result, ge}; -} - -ResultAndGE IREmitter::PackedSubS8(const U32& a, const U32& b) { - const auto result = Inst(Opcode::PackedSubS8, a, b); - const auto ge = Inst(Opcode::GetGEFromOp, result); - return {result, ge}; -} - -ResultAndGE IREmitter::PackedSubU16(const U32& a, const U32& b) { - const auto result = Inst(Opcode::PackedSubU16, a, b); - const auto ge = Inst(Opcode::GetGEFromOp, result); - return {result, ge}; -} - -ResultAndGE IREmitter::PackedSubS16(const U32& a, const U32& b) { - const auto result = Inst(Opcode::PackedSubS16, a, b); - const auto ge = Inst(Opcode::GetGEFromOp, result); - return {result, ge}; -} - -ResultAndGE IREmitter::PackedAddSubU16(const U32& a, const U32& b) { - const auto result = Inst(Opcode::PackedAddSubU16, a, b); - const auto ge = Inst(Opcode::GetGEFromOp, result); - return {result, ge}; -} - -ResultAndGE IREmitter::PackedAddSubS16(const U32& a, const U32& b) { - const auto result = Inst(Opcode::PackedAddSubS16, a, b); - const auto ge = Inst(Opcode::GetGEFromOp, result); - return {result, ge}; -} - -ResultAndGE IREmitter::PackedSubAddU16(const U32& a, const U32& b) { - const auto result = Inst(Opcode::PackedSubAddU16, a, b); - const auto ge = Inst(Opcode::GetGEFromOp, result); - return {result, ge}; -} - -ResultAndGE IREmitter::PackedSubAddS16(const U32& a, const U32& b) { - const auto result = Inst(Opcode::PackedSubAddS16, a, b); - const auto ge = Inst(Opcode::GetGEFromOp, result); - return {result, ge}; -} - -U32 IREmitter::PackedHalvingAddU8(const U32& a, const U32& b) { - return Inst(Opcode::PackedHalvingAddU8, a, b); -} - -U32 IREmitter::PackedHalvingAddS8(const U32& a, const U32& b) { - return Inst(Opcode::PackedHalvingAddS8, a, b); -} - -U32 IREmitter::PackedHalvingSubU8(const U32& a, const U32& b) { - return Inst(Opcode::PackedHalvingSubU8, a, b); -} - -U32 IREmitter::PackedHalvingSubS8(const U32& a, const U32& b) { - return Inst(Opcode::PackedHalvingSubS8, a, b); -} - -U32 IREmitter::PackedHalvingAddU16(const U32& a, const U32& b) { - return Inst(Opcode::PackedHalvingAddU16, a, b); -} - -U32 IREmitter::PackedHalvingAddS16(const U32& a, const U32& b) { - return Inst(Opcode::PackedHalvingAddS16, a, b); -} - -U32 IREmitter::PackedHalvingSubU16(const U32& a, const U32& b) { - return Inst(Opcode::PackedHalvingSubU16, a, b); -} - -U32 IREmitter::PackedHalvingSubS16(const U32& a, const U32& b) { - return Inst(Opcode::PackedHalvingSubS16, a, b); -} - -U32 IREmitter::PackedHalvingAddSubU16(const U32& a, const U32& b) { - return Inst(Opcode::PackedHalvingAddSubU16, a, b); -} - -U32 IREmitter::PackedHalvingAddSubS16(const U32& a, const U32& b) { - return Inst(Opcode::PackedHalvingAddSubS16, a, b); -} - -U32 IREmitter::PackedHalvingSubAddU16(const U32& a, const U32& b) { - return Inst(Opcode::PackedHalvingSubAddU16, a, b); -} - -U32 IREmitter::PackedHalvingSubAddS16(const U32& a, const U32& b) { - return Inst(Opcode::PackedHalvingSubAddS16, a, b); -} - -U32 IREmitter::PackedSaturatedAddU8(const U32& a, const U32& b) { - return Inst(Opcode::PackedSaturatedAddU8, a, b); -} - -U32 IREmitter::PackedSaturatedAddS8(const U32& a, const U32& b) { - return Inst(Opcode::PackedSaturatedAddS8, a, b); -} - -U32 IREmitter::PackedSaturatedSubU8(const U32& a, const U32& b) { - return Inst(Opcode::PackedSaturatedSubU8, a, b); -} - -U32 IREmitter::PackedSaturatedSubS8(const U32& a, const U32& b) { - return Inst(Opcode::PackedSaturatedSubS8, a, b); -} - -U32 IREmitter::PackedSaturatedAddU16(const U32& a, const U32& b) { - return Inst(Opcode::PackedSaturatedAddU16, a, b); -} - -U32 IREmitter::PackedSaturatedAddS16(const U32& a, const U32& b) { - return Inst(Opcode::PackedSaturatedAddS16, a, b); -} - -U32 IREmitter::PackedSaturatedSubU16(const U32& a, const U32& b) { - return Inst(Opcode::PackedSaturatedSubU16, a, b); -} - -U32 IREmitter::PackedSaturatedSubS16(const U32& a, const U32& b) { - return Inst(Opcode::PackedSaturatedSubS16, a, b); -} - -U32 IREmitter::PackedAbsDiffSumU8(const U32& a, const U32& b) { - return Inst(Opcode::PackedAbsDiffSumU8, a, b); -} - -U32 IREmitter::PackedSelect(const U32& ge, const U32& a, const U32& b) { - return Inst(Opcode::PackedSelect, ge, a, b); -} - -U32 IREmitter::CRC32Castagnoli8(const U32& a, const U32& b) { - return Inst(Opcode::CRC32Castagnoli8, a, b); -} - -U32 IREmitter::CRC32Castagnoli16(const U32& a, const U32& b) { - return Inst(Opcode::CRC32Castagnoli16, a, b); -} - -U32 IREmitter::CRC32Castagnoli32(const U32& a, const U32& b) { - return Inst(Opcode::CRC32Castagnoli32, a, b); -} - -U32 IREmitter::CRC32Castagnoli64(const U32& a, const U64& b) { - return Inst(Opcode::CRC32Castagnoli64, a, b); -} - -U32 IREmitter::CRC32ISO8(const U32& a, const U32& b) { - return Inst(Opcode::CRC32ISO8, a, b); -} - -U32 IREmitter::CRC32ISO16(const U32& a, const U32& b) { - return Inst(Opcode::CRC32ISO16, a, b); -} - -U32 IREmitter::CRC32ISO32(const U32& a, const U32& b) { - return Inst(Opcode::CRC32ISO32, a, b); -} - -U32 IREmitter::CRC32ISO64(const U32& a, const U64& b) { - return Inst(Opcode::CRC32ISO64, a, b); -} - -U128 IREmitter::AESDecryptSingleRound(const U128& a) { - return Inst(Opcode::AESDecryptSingleRound, a); -} - -U128 IREmitter::AESEncryptSingleRound(const U128& a) { - return Inst(Opcode::AESEncryptSingleRound, a); -} - -U128 IREmitter::AESInverseMixColumns(const U128& a) { - return Inst(Opcode::AESInverseMixColumns, a); -} - -U128 IREmitter::AESMixColumns(const U128& a) { - return Inst(Opcode::AESMixColumns, a); -} - -U8 IREmitter::SM4AccessSubstitutionBox(const U8& a) { - return Inst(Opcode::SM4AccessSubstitutionBox, a); -} - -U128 IREmitter::SHA256Hash(const U128& x, const U128& y, const U128& w, bool part1) { - return Inst(Opcode::SHA256Hash, x, y, w, Imm1(part1)); -} - -U128 IREmitter::SHA256MessageSchedule0(const U128& x, const U128& y) { - return Inst(Opcode::SHA256MessageSchedule0, x, y); -} - -U128 IREmitter::SHA256MessageSchedule1(const U128& x, const U128& y, const U128& z) { - return Inst(Opcode::SHA256MessageSchedule1, x, y, z); -} - -UAny IREmitter::VectorGetElement(size_t esize, const U128& a, size_t index) { - ASSERT_MSG(esize * index < 128, "Invalid index"); - switch (esize) { - case 8: - return Inst(Opcode::VectorGetElement8, a, Imm8(static_cast(index))); - case 16: - return Inst(Opcode::VectorGetElement16, a, Imm8(static_cast(index))); - case 32: - return Inst(Opcode::VectorGetElement32, a, Imm8(static_cast(index))); - case 64: - return Inst(Opcode::VectorGetElement64, a, Imm8(static_cast(index))); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorSetElement(size_t esize, const U128& a, size_t index, const IR::UAny& elem) { - ASSERT_MSG(esize * index < 128, "Invalid index"); - switch (esize) { - case 8: - return Inst(Opcode::VectorSetElement8, a, Imm8(static_cast(index)), elem); - case 16: - return Inst(Opcode::VectorSetElement16, a, Imm8(static_cast(index)), elem); - case 32: - return Inst(Opcode::VectorSetElement32, a, Imm8(static_cast(index)), elem); - case 64: - return Inst(Opcode::VectorSetElement64, a, Imm8(static_cast(index)), elem); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorAbs(size_t esize, const U128& a) { - switch (esize) { - case 8: - return Inst(Opcode::VectorAbs8, a); - case 16: - return Inst(Opcode::VectorAbs16, a); - case 32: - return Inst(Opcode::VectorAbs32, a); - case 64: - return Inst(Opcode::VectorAbs64, a); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorAdd(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorAdd8, a, b); - case 16: - return Inst(Opcode::VectorAdd16, a, b); - case 32: - return Inst(Opcode::VectorAdd32, a, b); - case 64: - return Inst(Opcode::VectorAdd64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorAnd(const U128& a, const U128& b) { - return Inst(Opcode::VectorAnd, a, b); -} - -U128 IREmitter::VectorAndNot(const U128& a, const U128& b) { - return Inst(Opcode::VectorAndNot, a, b); -} - -U128 IREmitter::VectorArithmeticShiftRight(size_t esize, const U128& a, u8 shift_amount) { - switch (esize) { - case 8: - return Inst(Opcode::VectorArithmeticShiftRight8, a, Imm8(shift_amount)); - case 16: - return Inst(Opcode::VectorArithmeticShiftRight16, a, Imm8(shift_amount)); - case 32: - return Inst(Opcode::VectorArithmeticShiftRight32, a, Imm8(shift_amount)); - case 64: - return Inst(Opcode::VectorArithmeticShiftRight64, a, Imm8(shift_amount)); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorArithmeticVShift(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorArithmeticVShift8, a, b); - case 16: - return Inst(Opcode::VectorArithmeticVShift16, a, b); - case 32: - return Inst(Opcode::VectorArithmeticVShift32, a, b); - case 64: - return Inst(Opcode::VectorArithmeticVShift64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorBroadcastLower(size_t esize, const UAny& a) { - switch (esize) { - case 8: - return Inst(Opcode::VectorBroadcastLower8, U8(a)); - case 16: - return Inst(Opcode::VectorBroadcastLower16, U16(a)); - case 32: - return Inst(Opcode::VectorBroadcastLower32, U32(a)); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorBroadcast(size_t esize, const UAny& a) { - switch (esize) { - case 8: - return Inst(Opcode::VectorBroadcast8, U8(a)); - case 16: - return Inst(Opcode::VectorBroadcast16, U16(a)); - case 32: - return Inst(Opcode::VectorBroadcast32, U32(a)); - case 64: - return Inst(Opcode::VectorBroadcast64, U64(a)); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorBroadcastElementLower(size_t esize, const U128& a, size_t index) { - ASSERT_MSG(esize * index < 128, "Invalid index"); - switch (esize) { - case 8: - return Inst(Opcode::VectorBroadcastElementLower8, a, u8(index)); - case 16: - return Inst(Opcode::VectorBroadcastElementLower16, a, u8(index)); - case 32: - return Inst(Opcode::VectorBroadcastElementLower32, a, u8(index)); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorBroadcastElement(size_t esize, const U128& a, size_t index) { - ASSERT_MSG(esize * index < 128, "Invalid index"); - switch (esize) { - case 8: - return Inst(Opcode::VectorBroadcastElement8, a, u8(index)); - case 16: - return Inst(Opcode::VectorBroadcastElement16, a, u8(index)); - case 32: - return Inst(Opcode::VectorBroadcastElement32, a, u8(index)); - case 64: - return Inst(Opcode::VectorBroadcastElement64, a, u8(index)); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorCountLeadingZeros(size_t esize, const U128& a) { - switch (esize) { - case 8: - return Inst(Opcode::VectorCountLeadingZeros8, a); - case 16: - return Inst(Opcode::VectorCountLeadingZeros16, a); - case 32: - return Inst(Opcode::VectorCountLeadingZeros32, a); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorDeinterleaveEven(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorDeinterleaveEven8, a, b); - case 16: - return Inst(Opcode::VectorDeinterleaveEven16, a, b); - case 32: - return Inst(Opcode::VectorDeinterleaveEven32, a, b); - case 64: - return Inst(Opcode::VectorDeinterleaveEven64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorDeinterleaveOdd(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorDeinterleaveOdd8, a, b); - case 16: - return Inst(Opcode::VectorDeinterleaveOdd16, a, b); - case 32: - return Inst(Opcode::VectorDeinterleaveOdd32, a, b); - case 64: - return Inst(Opcode::VectorDeinterleaveOdd64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorDeinterleaveEvenLower(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorDeinterleaveEvenLower8, a, b); - case 16: - return Inst(Opcode::VectorDeinterleaveEvenLower16, a, b); - case 32: - return Inst(Opcode::VectorDeinterleaveEvenLower32, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorDeinterleaveOddLower(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorDeinterleaveOddLower8, a, b); - case 16: - return Inst(Opcode::VectorDeinterleaveOddLower16, a, b); - case 32: - return Inst(Opcode::VectorDeinterleaveOddLower32, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorEor(const U128& a, const U128& b) { - return Inst(Opcode::VectorEor, a, b); -} - -U128 IREmitter::VectorEqual(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorEqual8, a, b); - case 16: - return Inst(Opcode::VectorEqual16, a, b); - case 32: - return Inst(Opcode::VectorEqual32, a, b); - case 64: - return Inst(Opcode::VectorEqual64, a, b); - case 128: - return Inst(Opcode::VectorEqual128, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorExtract(const U128& a, const U128& b, size_t position) { - ASSERT(position <= 128); - return Inst(Opcode::VectorExtract, a, b, Imm8(static_cast(position))); -} - -U128 IREmitter::VectorExtractLower(const U128& a, const U128& b, size_t position) { - ASSERT(position <= 64); - return Inst(Opcode::VectorExtractLower, a, b, Imm8(static_cast(position))); -} - -U128 IREmitter::VectorGreaterSigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorGreaterS8, a, b); - case 16: - return Inst(Opcode::VectorGreaterS16, a, b); - case 32: - return Inst(Opcode::VectorGreaterS32, a, b); - case 64: - return Inst(Opcode::VectorGreaterS64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorGreaterEqualSigned(size_t esize, const U128& a, const U128& b) { - return VectorOr(VectorGreaterSigned(esize, a, b), VectorEqual(esize, a, b)); -} - -U128 IREmitter::VectorGreaterEqualUnsigned(size_t esize, const U128& a, const U128& b) { - return VectorEqual(esize, VectorMaxUnsigned(esize, a, b), a); -} - -U128 IREmitter::VectorGreaterUnsigned(size_t esize, const U128& a, const U128& b) { - return VectorNot(VectorEqual(esize, VectorMinUnsigned(esize, a, b), a)); -} - -U128 IREmitter::VectorHalvingAddSigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorHalvingAddS8, a, b); - case 16: - return Inst(Opcode::VectorHalvingAddS16, a, b); - case 32: - return Inst(Opcode::VectorHalvingAddS32, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorHalvingAddUnsigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorHalvingAddU8, a, b); - case 16: - return Inst(Opcode::VectorHalvingAddU16, a, b); - case 32: - return Inst(Opcode::VectorHalvingAddU32, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorHalvingSubSigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorHalvingSubS8, a, b); - case 16: - return Inst(Opcode::VectorHalvingSubS16, a, b); - case 32: - return Inst(Opcode::VectorHalvingSubS32, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorHalvingSubUnsigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorHalvingSubU8, a, b); - case 16: - return Inst(Opcode::VectorHalvingSubU16, a, b); - case 32: - return Inst(Opcode::VectorHalvingSubU32, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorInterleaveLower(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorInterleaveLower8, a, b); - case 16: - return Inst(Opcode::VectorInterleaveLower16, a, b); - case 32: - return Inst(Opcode::VectorInterleaveLower32, a, b); - case 64: - return Inst(Opcode::VectorInterleaveLower64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorInterleaveUpper(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorInterleaveUpper8, a, b); - case 16: - return Inst(Opcode::VectorInterleaveUpper16, a, b); - case 32: - return Inst(Opcode::VectorInterleaveUpper32, a, b); - case 64: - return Inst(Opcode::VectorInterleaveUpper64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorLessEqualSigned(size_t esize, const U128& a, const U128& b) { - return VectorNot(VectorGreaterSigned(esize, a, b)); -} - -U128 IREmitter::VectorLessEqualUnsigned(size_t esize, const U128& a, const U128& b) { - return VectorEqual(esize, VectorMinUnsigned(esize, a, b), a); -} - -U128 IREmitter::VectorLessSigned(size_t esize, const U128& a, const U128& b) { - return VectorNot(VectorOr(VectorGreaterSigned(esize, a, b), VectorEqual(esize, a, b))); -} - -U128 IREmitter::VectorLessUnsigned(size_t esize, const U128& a, const U128& b) { - return VectorNot(VectorEqual(esize, VectorMaxUnsigned(esize, a, b), a)); -} - -U128 IREmitter::VectorLogicalShiftLeft(size_t esize, const U128& a, u8 shift_amount) { - switch (esize) { - case 8: - return Inst(Opcode::VectorLogicalShiftLeft8, a, Imm8(shift_amount)); - case 16: - return Inst(Opcode::VectorLogicalShiftLeft16, a, Imm8(shift_amount)); - case 32: - return Inst(Opcode::VectorLogicalShiftLeft32, a, Imm8(shift_amount)); - case 64: - return Inst(Opcode::VectorLogicalShiftLeft64, a, Imm8(shift_amount)); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorLogicalShiftRight(size_t esize, const U128& a, u8 shift_amount) { - switch (esize) { - case 8: - return Inst(Opcode::VectorLogicalShiftRight8, a, Imm8(shift_amount)); - case 16: - return Inst(Opcode::VectorLogicalShiftRight16, a, Imm8(shift_amount)); - case 32: - return Inst(Opcode::VectorLogicalShiftRight32, a, Imm8(shift_amount)); - case 64: - return Inst(Opcode::VectorLogicalShiftRight64, a, Imm8(shift_amount)); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorLogicalVShift(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorLogicalVShift8, a, b); - case 16: - return Inst(Opcode::VectorLogicalVShift16, a, b); - case 32: - return Inst(Opcode::VectorLogicalVShift32, a, b); - case 64: - return Inst(Opcode::VectorLogicalVShift64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorMaxSigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorMaxS8, a, b); - case 16: - return Inst(Opcode::VectorMaxS16, a, b); - case 32: - return Inst(Opcode::VectorMaxS32, a, b); - case 64: - return Inst(Opcode::VectorMaxS64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorMaxUnsigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorMaxU8, a, b); - case 16: - return Inst(Opcode::VectorMaxU16, a, b); - case 32: - return Inst(Opcode::VectorMaxU32, a, b); - case 64: - return Inst(Opcode::VectorMaxU64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorMinSigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorMinS8, a, b); - case 16: - return Inst(Opcode::VectorMinS16, a, b); - case 32: - return Inst(Opcode::VectorMinS32, a, b); - case 64: - return Inst(Opcode::VectorMinS64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorMinUnsigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorMinU8, a, b); - case 16: - return Inst(Opcode::VectorMinU16, a, b); - case 32: - return Inst(Opcode::VectorMinU32, a, b); - case 64: - return Inst(Opcode::VectorMinU64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorMultiply(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorMultiply8, a, b); - case 16: - return Inst(Opcode::VectorMultiply16, a, b); - case 32: - return Inst(Opcode::VectorMultiply32, a, b); - case 64: - return Inst(Opcode::VectorMultiply64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorMultiplySignedWiden(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorMultiplySignedWiden8, a, b); - case 16: - return Inst(Opcode::VectorMultiplySignedWiden16, a, b); - case 32: - return Inst(Opcode::VectorMultiplySignedWiden32, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorMultiplyUnsignedWiden(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorMultiplyUnsignedWiden8, a, b); - case 16: - return Inst(Opcode::VectorMultiplyUnsignedWiden16, a, b); - case 32: - return Inst(Opcode::VectorMultiplyUnsignedWiden32, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorNarrow(size_t original_esize, const U128& a) { - switch (original_esize) { - case 16: - return Inst(Opcode::VectorNarrow16, a); - case 32: - return Inst(Opcode::VectorNarrow32, a); - case 64: - return Inst(Opcode::VectorNarrow64, a); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorNot(const U128& a) { - return Inst(Opcode::VectorNot, a); -} - -U128 IREmitter::VectorOr(const U128& a, const U128& b) { - return Inst(Opcode::VectorOr, a, b); -} - -U128 IREmitter::VectorPairedAdd(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorPairedAdd8, a, b); - case 16: - return Inst(Opcode::VectorPairedAdd16, a, b); - case 32: - return Inst(Opcode::VectorPairedAdd32, a, b); - case 64: - return Inst(Opcode::VectorPairedAdd64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorPairedAddLower(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorPairedAddLower8, a, b); - case 16: - return Inst(Opcode::VectorPairedAddLower16, a, b); - case 32: - return Inst(Opcode::VectorPairedAddLower32, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorPairedAddSignedWiden(size_t original_esize, const U128& a) { - switch (original_esize) { - case 8: - return Inst(Opcode::VectorPairedAddSignedWiden8, a); - case 16: - return Inst(Opcode::VectorPairedAddSignedWiden16, a); - case 32: - return Inst(Opcode::VectorPairedAddSignedWiden32, a); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorPairedAddUnsignedWiden(size_t original_esize, const U128& a) { - switch (original_esize) { - case 8: - return Inst(Opcode::VectorPairedAddUnsignedWiden8, a); - case 16: - return Inst(Opcode::VectorPairedAddUnsignedWiden16, a); - case 32: - return Inst(Opcode::VectorPairedAddUnsignedWiden32, a); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorPairedMaxSigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorPairedMaxS8, a, b); - case 16: - return Inst(Opcode::VectorPairedMaxS16, a, b); - case 32: - return Inst(Opcode::VectorPairedMaxS32, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorPairedMaxUnsigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorPairedMaxU8, a, b); - case 16: - return Inst(Opcode::VectorPairedMaxU16, a, b); - case 32: - return Inst(Opcode::VectorPairedMaxU32, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorPairedMinSigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorPairedMinS8, a, b); - case 16: - return Inst(Opcode::VectorPairedMinS16, a, b); - case 32: - return Inst(Opcode::VectorPairedMinS32, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorPairedMinUnsigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorPairedMinU8, a, b); - case 16: - return Inst(Opcode::VectorPairedMinU16, a, b); - case 32: - return Inst(Opcode::VectorPairedMinU32, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorPairedMaxSignedLower(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorPairedMaxLowerS8, a, b); - case 16: - return Inst(Opcode::VectorPairedMaxLowerS16, a, b); - case 32: - return Inst(Opcode::VectorPairedMaxLowerS32, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorPairedMaxUnsignedLower(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorPairedMaxLowerU8, a, b); - case 16: - return Inst(Opcode::VectorPairedMaxLowerU16, a, b); - case 32: - return Inst(Opcode::VectorPairedMaxLowerU32, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorPairedMinSignedLower(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorPairedMinLowerS8, a, b); - case 16: - return Inst(Opcode::VectorPairedMinLowerS16, a, b); - case 32: - return Inst(Opcode::VectorPairedMinLowerS32, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorPairedMinUnsignedLower(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorPairedMinLowerU8, a, b); - case 16: - return Inst(Opcode::VectorPairedMinLowerU16, a, b); - case 32: - return Inst(Opcode::VectorPairedMinLowerU32, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorPolynomialMultiply(const U128& a, const U128& b) { - return Inst(Opcode::VectorPolynomialMultiply8, a, b); -} - -U128 IREmitter::VectorPolynomialMultiplyLong(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorPolynomialMultiplyLong8, a, b); - case 64: - return Inst(Opcode::VectorPolynomialMultiplyLong64, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorPopulationCount(const U128& a) { - return Inst(Opcode::VectorPopulationCount, a); -} - -U128 IREmitter::VectorReverseBits(const U128& a) { - return Inst(Opcode::VectorReverseBits, a); -} - -U128 IREmitter::VectorReverseElementsInHalfGroups(size_t esize, const U128& a) { - switch (esize) { - case 8: - return Inst(Opcode::VectorReverseElementsInHalfGroups8, a); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorReverseElementsInWordGroups(size_t esize, const U128& a) { - switch (esize) { - case 8: - return Inst(Opcode::VectorReverseElementsInWordGroups8, a); - case 16: - return Inst(Opcode::VectorReverseElementsInWordGroups16, a); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorReverseElementsInLongGroups(size_t esize, const U128& a) { - switch (esize) { - case 8: - return Inst(Opcode::VectorReverseElementsInLongGroups8, a); - case 16: - return Inst(Opcode::VectorReverseElementsInLongGroups16, a); - case 32: - return Inst(Opcode::VectorReverseElementsInLongGroups32, a); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorReduceAdd(size_t esize, const U128& a) { - switch (esize) { - case 8: - return Inst(Opcode::VectorReduceAdd8, a); - case 16: - return Inst(Opcode::VectorReduceAdd16, a); - case 32: - return Inst(Opcode::VectorReduceAdd32, a); - case 64: - return Inst(Opcode::VectorReduceAdd64, a); - } - - UNREACHABLE(); -} - -U128 IREmitter::VectorRotateLeft(size_t esize, const U128& a, u8 amount) { - ASSERT(amount < esize); - - if (amount == 0) { - return a; - } - - return VectorOr(VectorLogicalShiftLeft(esize, a, amount), - VectorLogicalShiftRight(esize, a, static_cast(esize - amount))); -} - -U128 IREmitter::VectorRotateRight(size_t esize, const U128& a, u8 amount) { - ASSERT(amount < esize); - - if (amount == 0) { - return a; - } - - return VectorOr(VectorLogicalShiftRight(esize, a, amount), - VectorLogicalShiftLeft(esize, a, static_cast(esize - amount))); -} - -U128 IREmitter::VectorRotateWholeVectorRight(const U128& a, u8 amount) { - ASSERT(amount % 32 == 0); - return Inst(Opcode::VectorRotateWholeVectorRight, a, Imm8(amount)); -} - -U128 IREmitter::VectorRoundingHalvingAddSigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorRoundingHalvingAddS8, a, b); - case 16: - return Inst(Opcode::VectorRoundingHalvingAddS16, a, b); - case 32: - return Inst(Opcode::VectorRoundingHalvingAddS32, a, b); - } - - UNREACHABLE(); -} - -U128 IREmitter::VectorRoundingHalvingAddUnsigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorRoundingHalvingAddU8, a, b); - case 16: - return Inst(Opcode::VectorRoundingHalvingAddU16, a, b); - case 32: - return Inst(Opcode::VectorRoundingHalvingAddU32, a, b); - } - - UNREACHABLE(); -} - -U128 IREmitter::VectorRoundingShiftLeftSigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorRoundingShiftLeftS8, a, b); - case 16: - return Inst(Opcode::VectorRoundingShiftLeftS16, a, b); - case 32: - return Inst(Opcode::VectorRoundingShiftLeftS32, a, b); - case 64: - return Inst(Opcode::VectorRoundingShiftLeftS64, a, b); - } - - UNREACHABLE(); -} - -U128 IREmitter::VectorRoundingShiftLeftUnsigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorRoundingShiftLeftU8, a, b); - case 16: - return Inst(Opcode::VectorRoundingShiftLeftU16, a, b); - case 32: - return Inst(Opcode::VectorRoundingShiftLeftU32, a, b); - case 64: - return Inst(Opcode::VectorRoundingShiftLeftU64, a, b); - } - - UNREACHABLE(); -} - -U128 IREmitter::VectorSignExtend(size_t original_esize, const U128& a) { - switch (original_esize) { - case 8: - return Inst(Opcode::VectorSignExtend8, a); - case 16: - return Inst(Opcode::VectorSignExtend16, a); - case 32: - return Inst(Opcode::VectorSignExtend32, a); - case 64: - return Inst(Opcode::VectorSignExtend64, a); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorSignedAbsoluteDifference(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorSignedAbsoluteDifference8, a, b); - case 16: - return Inst(Opcode::VectorSignedAbsoluteDifference16, a, b); - case 32: - return Inst(Opcode::VectorSignedAbsoluteDifference32, a, b); - } - UNREACHABLE(); -} - -UpperAndLower IREmitter::VectorSignedMultiply(size_t esize, const U128& a, const U128& b) { - const Value multiply = [&] { - switch (esize) { - case 16: - return Inst(Opcode::VectorSignedMultiply16, a, b); - case 32: - return Inst(Opcode::VectorSignedMultiply32, a, b); - } - UNREACHABLE(); - }(); - - return { - Inst(Opcode::GetUpperFromOp, multiply), - Inst(Opcode::GetLowerFromOp, multiply), - }; -} - -U128 IREmitter::VectorSignedSaturatedAbs(size_t esize, const U128& a) { - switch (esize) { - case 8: - return Inst(Opcode::VectorSignedSaturatedAbs8, a); - case 16: - return Inst(Opcode::VectorSignedSaturatedAbs16, a); - case 32: - return Inst(Opcode::VectorSignedSaturatedAbs32, a); - case 64: - return Inst(Opcode::VectorSignedSaturatedAbs64, a); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorSignedSaturatedAccumulateUnsigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorSignedSaturatedAccumulateUnsigned8, a, b); - case 16: - return Inst(Opcode::VectorSignedSaturatedAccumulateUnsigned16, a, b); - case 32: - return Inst(Opcode::VectorSignedSaturatedAccumulateUnsigned32, a, b); - case 64: - return Inst(Opcode::VectorSignedSaturatedAccumulateUnsigned64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorSignedSaturatedDoublingMultiplyHigh(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 16: - return Inst(Opcode::VectorSignedSaturatedDoublingMultiplyHigh16, a, b); - case 32: - return Inst(Opcode::VectorSignedSaturatedDoublingMultiplyHigh32, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorSignedSaturatedDoublingMultiplyHighRounding(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 16: - return Inst(Opcode::VectorSignedSaturatedDoublingMultiplyHighRounding16, a, b); - case 32: - return Inst(Opcode::VectorSignedSaturatedDoublingMultiplyHighRounding32, a, b); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::VectorSignedSaturatedDoublingMultiplyLong(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 16: - return Inst(Opcode::VectorSignedSaturatedDoublingMultiplyLong16, a, b); - case 32: - return Inst(Opcode::VectorSignedSaturatedDoublingMultiplyLong32, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorSignedSaturatedNarrowToSigned(size_t original_esize, const U128& a) { - switch (original_esize) { - case 16: - return Inst(Opcode::VectorSignedSaturatedNarrowToSigned16, a); - case 32: - return Inst(Opcode::VectorSignedSaturatedNarrowToSigned32, a); - case 64: - return Inst(Opcode::VectorSignedSaturatedNarrowToSigned64, a); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorSignedSaturatedNarrowToUnsigned(size_t original_esize, const U128& a) { - switch (original_esize) { - case 16: - return Inst(Opcode::VectorSignedSaturatedNarrowToUnsigned16, a); - case 32: - return Inst(Opcode::VectorSignedSaturatedNarrowToUnsigned32, a); - case 64: - return Inst(Opcode::VectorSignedSaturatedNarrowToUnsigned64, a); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorSignedSaturatedNeg(size_t esize, const U128& a) { - switch (esize) { - case 8: - return Inst(Opcode::VectorSignedSaturatedNeg8, a); - case 16: - return Inst(Opcode::VectorSignedSaturatedNeg16, a); - case 32: - return Inst(Opcode::VectorSignedSaturatedNeg32, a); - case 64: - return Inst(Opcode::VectorSignedSaturatedNeg64, a); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorSignedSaturatedShiftLeft(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorSignedSaturatedShiftLeft8, a, b); - case 16: - return Inst(Opcode::VectorSignedSaturatedShiftLeft16, a, b); - case 32: - return Inst(Opcode::VectorSignedSaturatedShiftLeft32, a, b); - case 64: - return Inst(Opcode::VectorSignedSaturatedShiftLeft64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorSignedSaturatedShiftLeftUnsigned(size_t esize, const U128& a, u8 shift_amount) { - ASSERT(shift_amount < esize); - switch (esize) { - case 8: - return Inst(Opcode::VectorSignedSaturatedShiftLeftUnsigned8, a, Imm8(shift_amount)); - case 16: - return Inst(Opcode::VectorSignedSaturatedShiftLeftUnsigned16, a, Imm8(shift_amount)); - case 32: - return Inst(Opcode::VectorSignedSaturatedShiftLeftUnsigned32, a, Imm8(shift_amount)); - case 64: - return Inst(Opcode::VectorSignedSaturatedShiftLeftUnsigned64, a, Imm8(shift_amount)); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorSub(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorSub8, a, b); - case 16: - return Inst(Opcode::VectorSub16, a, b); - case 32: - return Inst(Opcode::VectorSub32, a, b); - case 64: - return Inst(Opcode::VectorSub64, a, b); - } - UNREACHABLE(); -} - -Table IREmitter::VectorTable(std::vector values) { - ASSERT(values.size() >= 1 && values.size() <= 4); - values.resize(4); - return Inst(Opcode::VectorTable, values[0], values[1], values[2], values[3]); -} - -Table IREmitter::VectorTable(std::vector values) { - ASSERT(values.size() >= 1 && values.size() <= 4); - values.resize(4); - return Inst
(Opcode::VectorTable, values[0], values[1], values[2], values[3]); -} - -U64 IREmitter::VectorTableLookup(const U64& defaults, const Table& table, const U64& indices) { - ASSERT(table.GetInst()->GetArg(0).GetType() == Type::U64); - return Inst(Opcode::VectorTableLookup64, defaults, table, indices); -} - -U128 IREmitter::VectorTableLookup(const U128& defaults, const Table& table, const U128& indices) { - ASSERT(table.GetInst()->GetArg(0).GetType() == Type::U128); - return Inst(Opcode::VectorTableLookup128, defaults, table, indices); -} - -U128 IREmitter::VectorTranspose(size_t esize, const U128& a, const U128& b, bool part) { - switch (esize) { - case 8: - return Inst(Opcode::VectorTranspose8, a, b, Imm1(part)); - case 16: - return Inst(Opcode::VectorTranspose16, a, b, Imm1(part)); - case 32: - return Inst(Opcode::VectorTranspose32, a, b, Imm1(part)); - case 64: - return Inst(Opcode::VectorTranspose64, a, b, Imm1(part)); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorUnsignedAbsoluteDifference(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorUnsignedAbsoluteDifference8, a, b); - case 16: - return Inst(Opcode::VectorUnsignedAbsoluteDifference16, a, b); - case 32: - return Inst(Opcode::VectorUnsignedAbsoluteDifference32, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorUnsignedRecipEstimate(const U128& a) { - return Inst(Opcode::VectorUnsignedRecipEstimate, a); -} - -U128 IREmitter::VectorUnsignedRecipSqrtEstimate(const U128& a) { - return Inst(Opcode::VectorUnsignedRecipSqrtEstimate, a); -} - -U128 IREmitter::VectorUnsignedSaturatedAccumulateSigned(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorUnsignedSaturatedAccumulateSigned8, a, b); - case 16: - return Inst(Opcode::VectorUnsignedSaturatedAccumulateSigned16, a, b); - case 32: - return Inst(Opcode::VectorUnsignedSaturatedAccumulateSigned32, a, b); - case 64: - return Inst(Opcode::VectorUnsignedSaturatedAccumulateSigned64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorUnsignedSaturatedNarrow(size_t esize, const U128& a) { - switch (esize) { - case 16: - return Inst(Opcode::VectorUnsignedSaturatedNarrow16, a); - case 32: - return Inst(Opcode::VectorUnsignedSaturatedNarrow32, a); - case 64: - return Inst(Opcode::VectorUnsignedSaturatedNarrow64, a); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorUnsignedSaturatedShiftLeft(size_t esize, const U128& a, const U128& b) { - switch (esize) { - case 8: - return Inst(Opcode::VectorUnsignedSaturatedShiftLeft8, a, b); - case 16: - return Inst(Opcode::VectorUnsignedSaturatedShiftLeft16, a, b); - case 32: - return Inst(Opcode::VectorUnsignedSaturatedShiftLeft32, a, b); - case 64: - return Inst(Opcode::VectorUnsignedSaturatedShiftLeft64, a, b); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorZeroExtend(size_t original_esize, const U128& a) { - switch (original_esize) { - case 8: - return Inst(Opcode::VectorZeroExtend8, a); - case 16: - return Inst(Opcode::VectorZeroExtend16, a); - case 32: - return Inst(Opcode::VectorZeroExtend32, a); - case 64: - return Inst(Opcode::VectorZeroExtend64, a); - } - UNREACHABLE(); -} - -U128 IREmitter::VectorZeroUpper(const U128& a) { - return Inst(Opcode::VectorZeroUpper, a); -} - -U128 IREmitter::ZeroVector() { - return Inst(Opcode::ZeroVector); -} - -U16U32U64 IREmitter::FPAbs(const U16U32U64& a) { - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPAbs16, a); - case Type::U32: - return Inst(Opcode::FPAbs32, a); - case Type::U64: - return Inst(Opcode::FPAbs64, a); - default: - UNREACHABLE(); - } -} - -U32U64 IREmitter::FPAdd(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U32: - return Inst(Opcode::FPAdd32, a, b); - case Type::U64: - return Inst(Opcode::FPAdd64, a, b); - default: - UNREACHABLE(); - } -} - -NZCV IREmitter::FPCompare(const U32U64& a, const U32U64& b, bool exc_on_qnan) { - ASSERT(a.GetType() == b.GetType()); - - const IR::U1 exc_on_qnan_imm = Imm1(exc_on_qnan); - - switch (a.GetType()) { - case Type::U32: - return Inst(Opcode::FPCompare32, a, b, exc_on_qnan_imm); - case Type::U64: - return Inst(Opcode::FPCompare64, a, b, exc_on_qnan_imm); - default: - UNREACHABLE(); - } -} - -U32U64 IREmitter::FPDiv(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U32: - return Inst(Opcode::FPDiv32, a, b); - case Type::U64: - return Inst(Opcode::FPDiv64, a, b); - default: - UNREACHABLE(); - } -} - -U32U64 IREmitter::FPMax(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U32: - return Inst(Opcode::FPMax32, a, b); - case Type::U64: - return Inst(Opcode::FPMax64, a, b); - default: - UNREACHABLE(); - } -} - -U32U64 IREmitter::FPMaxNumeric(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U32: - return Inst(Opcode::FPMaxNumeric32, a, b); - case Type::U64: - return Inst(Opcode::FPMaxNumeric64, a, b); - default: - UNREACHABLE(); - } -} - -U32U64 IREmitter::FPMin(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U32: - return Inst(Opcode::FPMin32, a, b); - case Type::U64: - return Inst(Opcode::FPMin64, a, b); - default: - UNREACHABLE(); - } -} - -U32U64 IREmitter::FPMinNumeric(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U32: - return Inst(Opcode::FPMinNumeric32, a, b); - case Type::U64: - return Inst(Opcode::FPMinNumeric64, a, b); - default: - UNREACHABLE(); - } -} - -U32U64 IREmitter::FPMul(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U32: - return Inst(Opcode::FPMul32, a, b); - case Type::U64: - return Inst(Opcode::FPMul64, a, b); - default: - UNREACHABLE(); - } -} - -U16U32U64 IREmitter::FPMulAdd(const U16U32U64& a, const U16U32U64& b, const U16U32U64& c) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPMulAdd16, a, b, c); - case Type::U32: - return Inst(Opcode::FPMulAdd32, a, b, c); - case Type::U64: - return Inst(Opcode::FPMulAdd64, a, b, c); - default: - UNREACHABLE(); - } -} - -U16U32U64 IREmitter::FPMulSub(const U16U32U64& a, const U16U32U64& b, const U16U32U64& c) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPMulSub16, a, b, c); - case Type::U32: - return Inst(Opcode::FPMulSub32, a, b, c); - case Type::U64: - return Inst(Opcode::FPMulSub64, a, b, c); - default: - UNREACHABLE(); - } -} - -U32U64 IREmitter::FPMulX(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U32: - return Inst(Opcode::FPMulX32, a, b); - case Type::U64: - return Inst(Opcode::FPMulX64, a, b); - default: - UNREACHABLE(); - } -} - -U16U32U64 IREmitter::FPNeg(const U16U32U64& a) { - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPNeg16, a); - case Type::U32: - return Inst(Opcode::FPNeg32, a); - case Type::U64: - return Inst(Opcode::FPNeg64, a); - default: - UNREACHABLE(); - } -} - -U16U32U64 IREmitter::FPRecipEstimate(const U16U32U64& a) { - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPRecipEstimate16, a); - case Type::U32: - return Inst(Opcode::FPRecipEstimate32, a); - case Type::U64: - return Inst(Opcode::FPRecipEstimate64, a); - default: - UNREACHABLE(); - } -} - -U16U32U64 IREmitter::FPRecipExponent(const U16U32U64& a) { - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPRecipExponent16, a); - case Type::U32: - return Inst(Opcode::FPRecipExponent32, a); - case Type::U64: - return Inst(Opcode::FPRecipExponent64, a); - default: - UNREACHABLE(); - } -} - -U16U32U64 IREmitter::FPRecipStepFused(const U16U32U64& a, const U16U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPRecipStepFused16, a, b); - case Type::U32: - return Inst(Opcode::FPRecipStepFused32, a, b); - case Type::U64: - return Inst(Opcode::FPRecipStepFused64, a, b); - default: - UNREACHABLE(); - } -} - -U16U32U64 IREmitter::FPRoundInt(const U16U32U64& a, FP::RoundingMode rounding, bool exact) { - const u8 rounding_value = static_cast(rounding); - const IR::U1 exact_imm = Imm1(exact); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPRoundInt16, a, rounding_value, exact_imm); - case Type::U32: - return Inst(Opcode::FPRoundInt32, a, rounding_value, exact_imm); - case Type::U64: - return Inst(Opcode::FPRoundInt64, a, rounding_value, exact_imm); - default: - UNREACHABLE(); - } -} - -U16U32U64 IREmitter::FPRSqrtEstimate(const U16U32U64& a) { - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPRSqrtEstimate16, a); - case Type::U32: - return Inst(Opcode::FPRSqrtEstimate32, a); - case Type::U64: - return Inst(Opcode::FPRSqrtEstimate64, a); - default: - UNREACHABLE(); - } -} - -U16U32U64 IREmitter::FPRSqrtStepFused(const U16U32U64& a, const U16U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPRSqrtStepFused16, a, b); - case Type::U32: - return Inst(Opcode::FPRSqrtStepFused32, a, b); - case Type::U64: - return Inst(Opcode::FPRSqrtStepFused64, a, b); - default: - UNREACHABLE(); - } -} - -U32U64 IREmitter::FPSqrt(const U32U64& a) { - switch (a.GetType()) { - case Type::U32: - return Inst(Opcode::FPSqrt32, a); - case Type::U64: - return Inst(Opcode::FPSqrt64, a); - default: - UNREACHABLE(); - } -} - -U32U64 IREmitter::FPSub(const U32U64& a, const U32U64& b) { - ASSERT(a.GetType() == b.GetType()); - - switch (a.GetType()) { - case Type::U32: - return Inst(Opcode::FPSub32, a, b); - case Type::U64: - return Inst(Opcode::FPSub64, a, b); - default: - UNREACHABLE(); - } -} - -U16 IREmitter::FPDoubleToHalf(const U64& a, FP::RoundingMode rounding) { - return Inst(Opcode::FPDoubleToHalf, a, Imm8(static_cast(rounding))); -} - -U32 IREmitter::FPDoubleToSingle(const U64& a, FP::RoundingMode rounding) { - return Inst(Opcode::FPDoubleToSingle, a, Imm8(static_cast(rounding))); -} - -U64 IREmitter::FPHalfToDouble(const U16& a, FP::RoundingMode rounding) { - return Inst(Opcode::FPHalfToDouble, a, Imm8(static_cast(rounding))); -} - -U32 IREmitter::FPHalfToSingle(const U16& a, FP::RoundingMode rounding) { - return Inst(Opcode::FPHalfToSingle, a, Imm8(static_cast(rounding))); -} - -U64 IREmitter::FPSingleToDouble(const U32& a, FP::RoundingMode rounding) { - return Inst(Opcode::FPSingleToDouble, a, Imm8(static_cast(rounding))); -} - -U16 IREmitter::FPSingleToHalf(const U32& a, FP::RoundingMode rounding) { - return Inst(Opcode::FPSingleToHalf, a, Imm8(static_cast(rounding))); -} - -U16 IREmitter::FPToFixedS16(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { - ASSERT(fbits <= 16); - - const U8 fbits_imm = Imm8(static_cast(fbits)); - const U8 rounding_imm = Imm8(static_cast(rounding)); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPHalfToFixedS16, a, fbits_imm, rounding_imm); - case Type::U32: - return Inst(Opcode::FPSingleToFixedS16, a, fbits_imm, rounding_imm); - case Type::U64: - return Inst(Opcode::FPDoubleToFixedS16, a, fbits_imm, rounding_imm); - default: - UNREACHABLE(); - } -} - -U32 IREmitter::FPToFixedS32(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { - ASSERT(fbits <= 32); - - const U8 fbits_imm = Imm8(static_cast(fbits)); - const U8 rounding_imm = Imm8(static_cast(rounding)); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPHalfToFixedS32, a, fbits_imm, rounding_imm); - case Type::U32: - return Inst(Opcode::FPSingleToFixedS32, a, fbits_imm, rounding_imm); - case Type::U64: - return Inst(Opcode::FPDoubleToFixedS32, a, fbits_imm, rounding_imm); - default: - UNREACHABLE(); - } -} - -U64 IREmitter::FPToFixedS64(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { - ASSERT(fbits <= 64); - - const U8 fbits_imm = Imm8(static_cast(fbits)); - const U8 rounding_imm = Imm8(static_cast(rounding)); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPHalfToFixedS64, a, fbits_imm, rounding_imm); - case Type::U32: - return Inst(Opcode::FPSingleToFixedS64, a, fbits_imm, rounding_imm); - case Type::U64: - return Inst(Opcode::FPDoubleToFixedS64, a, fbits_imm, rounding_imm); - default: - UNREACHABLE(); - } -} - -U16 IREmitter::FPToFixedU16(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { - ASSERT(fbits <= 16); - - const U8 fbits_imm = Imm8(static_cast(fbits)); - const U8 rounding_imm = Imm8(static_cast(rounding)); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPHalfToFixedU16, a, fbits_imm, rounding_imm); - case Type::U32: - return Inst(Opcode::FPSingleToFixedU16, a, fbits_imm, rounding_imm); - case Type::U64: - return Inst(Opcode::FPDoubleToFixedU16, a, fbits_imm, rounding_imm); - default: - UNREACHABLE(); - } -} - -U32 IREmitter::FPToFixedU32(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { - ASSERT(fbits <= 32); - - const U8 fbits_imm = Imm8(static_cast(fbits)); - const U8 rounding_imm = Imm8(static_cast(rounding)); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPHalfToFixedU32, a, fbits_imm, rounding_imm); - case Type::U32: - return Inst(Opcode::FPSingleToFixedU32, a, fbits_imm, rounding_imm); - case Type::U64: - return Inst(Opcode::FPDoubleToFixedU32, a, fbits_imm, rounding_imm); - default: - UNREACHABLE(); - } -} - -U64 IREmitter::FPToFixedU64(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { - ASSERT(fbits <= 64); - - const U8 fbits_imm = Imm8(static_cast(fbits)); - const U8 rounding_imm = Imm8(static_cast(rounding)); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPHalfToFixedU64, a, fbits_imm, rounding_imm); - case Type::U32: - return Inst(Opcode::FPSingleToFixedU64, a, fbits_imm, rounding_imm); - case Type::U64: - return Inst(Opcode::FPDoubleToFixedU64, a, fbits_imm, rounding_imm); - default: - UNREACHABLE(); - } -} - -U32 IREmitter::FPSignedFixedToSingle(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { - ASSERT(fbits <= (a.GetType() == Type::U16 ? 16 : (a.GetType() == Type::U32 ? 32 : 64))); - - const IR::U8 fbits_imm = Imm8(static_cast(fbits)); - const IR::U8 rounding_imm = Imm8(static_cast(rounding)); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPFixedS16ToSingle, a, fbits_imm, rounding_imm); - case Type::U32: - return Inst(Opcode::FPFixedS32ToSingle, a, fbits_imm, rounding_imm); - case Type::U64: - return Inst(Opcode::FPFixedS64ToSingle, a, fbits_imm, rounding_imm); - default: - UNREACHABLE(); - } -} - -U32 IREmitter::FPUnsignedFixedToSingle(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { - ASSERT(fbits <= (a.GetType() == Type::U16 ? 16 : (a.GetType() == Type::U32 ? 32 : 64))); - - const IR::U8 fbits_imm = Imm8(static_cast(fbits)); - const IR::U8 rounding_imm = Imm8(static_cast(rounding)); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPFixedU16ToSingle, a, fbits_imm, rounding_imm); - case Type::U32: - return Inst(Opcode::FPFixedU32ToSingle, a, fbits_imm, rounding_imm); - case Type::U64: - return Inst(Opcode::FPFixedU64ToSingle, a, fbits_imm, rounding_imm); - default: - UNREACHABLE(); - } -} - -U64 IREmitter::FPSignedFixedToDouble(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { - ASSERT(fbits <= (a.GetType() == Type::U16 ? 16 : (a.GetType() == Type::U32 ? 32 : 64))); - - const IR::U8 fbits_imm = Imm8(static_cast(fbits)); - const IR::U8 rounding_imm = Imm8(static_cast(rounding)); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPFixedS16ToDouble, a, fbits_imm, rounding_imm); - case Type::U32: - return Inst(Opcode::FPFixedS32ToDouble, a, fbits_imm, rounding_imm); - case Type::U64: - return Inst(Opcode::FPFixedS64ToDouble, a, fbits_imm, rounding_imm); - default: - UNREACHABLE(); - } -} - -U64 IREmitter::FPUnsignedFixedToDouble(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { - ASSERT(fbits <= (a.GetType() == Type::U16 ? 16 : (a.GetType() == Type::U32 ? 32 : 64))); - - const IR::U8 fbits_imm = Imm8(static_cast(fbits)); - const IR::U8 rounding_imm = Imm8(static_cast(rounding)); - - switch (a.GetType()) { - case Type::U16: - return Inst(Opcode::FPFixedU16ToDouble, a, fbits_imm, rounding_imm); - case Type::U32: - return Inst(Opcode::FPFixedU32ToDouble, a, fbits_imm, rounding_imm); - case Type::U64: - return Inst(Opcode::FPFixedU64ToDouble, a, fbits_imm, rounding_imm); - default: - UNREACHABLE(); - } -} - -U128 IREmitter::FPVectorAbs(size_t esize, const U128& a) { - switch (esize) { - case 16: - return Inst(Opcode::FPVectorAbs16, a); - case 32: - return Inst(Opcode::FPVectorAbs32, a); - case 64: - return Inst(Opcode::FPVectorAbs64, a); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorAdd(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorAdd32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorAdd64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorDiv(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorDiv32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorDiv64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorEqual(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 16: - return Inst(Opcode::FPVectorEqual16, a, b, Imm1(fpcr_controlled)); - case 32: - return Inst(Opcode::FPVectorEqual32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorEqual64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorFromHalf(size_t esize, const U128& a, FP::RoundingMode rounding, bool fpcr_controlled) { - ASSERT(esize == 32); - return Inst(Opcode::FPVectorFromHalf32, a, Imm8(static_cast(rounding)), Imm1(fpcr_controlled)); -} - -U128 IREmitter::FPVectorFromSignedFixed(size_t esize, const U128& a, size_t fbits, FP::RoundingMode rounding, bool fpcr_controlled) { - ASSERT(fbits <= esize); - switch (esize) { - case 32: - return Inst(Opcode::FPVectorFromSignedFixed32, a, Imm8(static_cast(fbits)), Imm8(static_cast(rounding)), Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorFromSignedFixed64, a, Imm8(static_cast(fbits)), Imm8(static_cast(rounding)), Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorFromUnsignedFixed(size_t esize, const U128& a, size_t fbits, FP::RoundingMode rounding, bool fpcr_controlled) { - ASSERT(fbits <= esize); - switch (esize) { - case 32: - return Inst(Opcode::FPVectorFromUnsignedFixed32, a, Imm8(static_cast(fbits)), Imm8(static_cast(rounding)), Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorFromUnsignedFixed64, a, Imm8(static_cast(fbits)), Imm8(static_cast(rounding)), Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorGreater(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorGreater32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorGreater64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorGreaterEqual(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorGreaterEqual32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorGreaterEqual64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorMax(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorMax32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorMax64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorMaxNumeric(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorMaxNumeric32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorMaxNumeric64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorMin(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorMin32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorMin64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorMinNumeric(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorMinNumeric32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorMinNumeric64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorMul(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorMul32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorMul64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorMulAdd(size_t esize, const U128& a, const U128& b, const U128& c, bool fpcr_controlled) { - switch (esize) { - case 16: - return Inst(Opcode::FPVectorMulAdd16, a, b, c, Imm1(fpcr_controlled)); - case 32: - return Inst(Opcode::FPVectorMulAdd32, a, b, c, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorMulAdd64, a, b, c, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorMulX(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorMulX32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorMulX64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorNeg(size_t esize, const U128& a) { - switch (esize) { - case 16: - return Inst(Opcode::FPVectorNeg16, a); - case 32: - return Inst(Opcode::FPVectorNeg32, a); - case 64: - return Inst(Opcode::FPVectorNeg64, a); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorPairedAdd(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorPairedAdd32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorPairedAdd64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorPairedAddLower(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorPairedAddLower32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorPairedAddLower64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorRecipEstimate(size_t esize, const U128& a, bool fpcr_controlled) { - switch (esize) { - case 16: - return Inst(Opcode::FPVectorRecipEstimate16, a, Imm1(fpcr_controlled)); - case 32: - return Inst(Opcode::FPVectorRecipEstimate32, a, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorRecipEstimate64, a, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorRecipStepFused(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 16: - return Inst(Opcode::FPVectorRecipStepFused16, a, b, Imm1(fpcr_controlled)); - case 32: - return Inst(Opcode::FPVectorRecipStepFused32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorRecipStepFused64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorRoundInt(size_t esize, const U128& operand, FP::RoundingMode rounding, bool exact, bool fpcr_controlled) { - const IR::U8 rounding_imm = Imm8(static_cast(rounding)); - const IR::U1 exact_imm = Imm1(exact); - - switch (esize) { - case 16: - return Inst(Opcode::FPVectorRoundInt16, operand, rounding_imm, exact_imm, Imm1(fpcr_controlled)); - case 32: - return Inst(Opcode::FPVectorRoundInt32, operand, rounding_imm, exact_imm, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorRoundInt64, operand, rounding_imm, exact_imm, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorRSqrtEstimate(size_t esize, const U128& a, bool fpcr_controlled) { - switch (esize) { - case 16: - return Inst(Opcode::FPVectorRSqrtEstimate16, a, Imm1(fpcr_controlled)); - case 32: - return Inst(Opcode::FPVectorRSqrtEstimate32, a, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorRSqrtEstimate64, a, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorRSqrtStepFused(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 16: - return Inst(Opcode::FPVectorRSqrtStepFused16, a, b, Imm1(fpcr_controlled)); - case 32: - return Inst(Opcode::FPVectorRSqrtStepFused32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorRSqrtStepFused64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorSqrt(size_t esize, const U128& a, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorSqrt32, a, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorSqrt64, a, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorSub(size_t esize, const U128& a, const U128& b, bool fpcr_controlled) { - switch (esize) { - case 32: - return Inst(Opcode::FPVectorSub32, a, b, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorSub64, a, b, Imm1(fpcr_controlled)); - } - UNREACHABLE(); -} - -U128 IREmitter::FPVectorToHalf(size_t esize, const U128& a, FP::RoundingMode rounding, bool fpcr_controlled) { - ASSERT(esize == 32); - return Inst(Opcode::FPVectorToHalf32, a, Imm8(static_cast(rounding)), Imm1(fpcr_controlled)); -} - -U128 IREmitter::FPVectorToSignedFixed(size_t esize, const U128& a, size_t fbits, FP::RoundingMode rounding, bool fpcr_controlled) { - ASSERT(fbits <= esize); - - const U8 fbits_imm = Imm8(static_cast(fbits)); - const U8 rounding_imm = Imm8(static_cast(rounding)); - - switch (esize) { - case 16: - return Inst(Opcode::FPVectorToSignedFixed16, a, fbits_imm, rounding_imm, Imm1(fpcr_controlled)); - case 32: - return Inst(Opcode::FPVectorToSignedFixed32, a, fbits_imm, rounding_imm, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorToSignedFixed64, a, fbits_imm, rounding_imm, Imm1(fpcr_controlled)); - } - - UNREACHABLE(); -} - -U128 IREmitter::FPVectorToUnsignedFixed(size_t esize, const U128& a, size_t fbits, FP::RoundingMode rounding, bool fpcr_controlled) { - ASSERT(fbits <= esize); - - const U8 fbits_imm = Imm8(static_cast(fbits)); - const U8 rounding_imm = Imm8(static_cast(rounding)); - - switch (esize) { - case 16: - return Inst(Opcode::FPVectorToUnsignedFixed16, a, fbits_imm, rounding_imm, Imm1(fpcr_controlled)); - case 32: - return Inst(Opcode::FPVectorToUnsignedFixed32, a, fbits_imm, rounding_imm, Imm1(fpcr_controlled)); - case 64: - return Inst(Opcode::FPVectorToUnsignedFixed64, a, fbits_imm, rounding_imm, Imm1(fpcr_controlled)); - } - - UNREACHABLE(); -} - -void IREmitter::Breakpoint() { - Inst(Opcode::Breakpoint); -} - -void IREmitter::CallHostFunction(void (*fn)(void)) { - Inst(Opcode::CallHostFunction, Imm64(mcl::bit_cast(fn)), Value{}, Value{}, Value{}); -} - -void IREmitter::CallHostFunction(void (*fn)(u64), const U64& arg1) { - Inst(Opcode::CallHostFunction, Imm64(mcl::bit_cast(fn)), arg1, Value{}, Value{}); -} - -void IREmitter::CallHostFunction(void (*fn)(u64, u64), const U64& arg1, const U64& arg2) { - Inst(Opcode::CallHostFunction, Imm64(mcl::bit_cast(fn)), arg1, arg2, Value{}); -} - -void IREmitter::CallHostFunction(void (*fn)(u64, u64, u64), const U64& arg1, const U64& arg2, const U64& arg3) { - Inst(Opcode::CallHostFunction, Imm64(mcl::bit_cast(fn)), arg1, arg2, arg3); -} - -void IREmitter::SetTerm(const Terminal& terminal) { - block.SetTerminal(terminal); -} } // namespace Dynarmic::IR diff --git a/externals/dynarmic/src/dynarmic/ir/ir_emitter.h b/externals/dynarmic/src/dynarmic/ir/ir_emitter.h index d37df24572..23cfb47498 100644 --- a/externals/dynarmic/src/dynarmic/ir/ir_emitter.h +++ b/externals/dynarmic/src/dynarmic/ir/ir_emitter.h @@ -5,8 +5,13 @@ #pragma once -#include +#include +#include +#include +#include + +#include "dynarmic/ir/opcodes.h" #include "dynarmic/ir/acc_type.h" #include "dynarmic/ir/basic_block.h" #include "dynarmic/ir/location_descriptor.h" @@ -69,337 +74,2878 @@ public: Block& block; - U1 Imm1(bool value) const; - U8 Imm8(u8 value) const; - U16 Imm16(u16 value) const; - U32 Imm32(u32 value) const; - U64 Imm64(u64 value) const; + U1 Imm1(bool imm1) const { + return U1(Value(imm1)); + } - void PushRSB(const LocationDescriptor& return_location); + U8 Imm8(u8 imm8) const { + return U8(Value(imm8)); + } - U64 Pack2x32To1x64(const U32& lo, const U32& hi); - U128 Pack2x64To1x128(const U64& lo, const U64& hi); - UAny LeastSignificant(size_t bitsize, const U32U64& value); - U32 LeastSignificantWord(const U64& value); - U16 LeastSignificantHalf(U32U64 value); - U8 LeastSignificantByte(U32U64 value); - ResultAndCarry MostSignificantWord(const U64& value); - U1 MostSignificantBit(const U32& value); - U1 IsZero(const U32& value); - U1 IsZero(const U64& value); - U1 IsZero(const U32U64& value); - U1 TestBit(const U32U64& value, const U8& bit); - U32 ConditionalSelect(Cond cond, const U32& a, const U32& b); - U64 ConditionalSelect(Cond cond, const U64& a, const U64& b); - NZCV ConditionalSelect(Cond cond, const NZCV& a, const NZCV& b); - U32U64 ConditionalSelect(Cond cond, const U32U64& a, const U32U64& b); + U16 Imm16(u16 imm16) const { + return U16(Value(imm16)); + } - U1 GetCFlagFromNZCV(const NZCV& nzcv); - NZCV NZCVFromPackedFlags(const U32& a); - // This pseudo-instruction may only be added to instructions that support it. - NZCV NZCVFrom(const Value& value); + U32 Imm32(u32 imm32) const { + return U32(Value(imm32)); + } - ResultAndCarry LogicalShiftLeft(const U32& value_in, const U8& shift_amount, const U1& carry_in); - ResultAndCarry LogicalShiftRight(const U32& value_in, const U8& shift_amount, const U1& carry_in); - ResultAndCarry ArithmeticShiftRight(const U32& value_in, const U8& shift_amount, const U1& carry_in); - ResultAndCarry RotateRight(const U32& value_in, const U8& shift_amount, const U1& carry_in); - U32U64 LogicalShiftLeft(const U32U64& value_in, const U8& shift_amount); - U32U64 LogicalShiftRight(const U32U64& value_in, const U8& shift_amount); - U32U64 ArithmeticShiftRight(const U32U64& value_in, const U8& shift_amount); - U32U64 RotateRight(const U32U64& value_in, const U8& shift_amount); - U32U64 LogicalShiftLeftMasked(const U32U64& value_in, const U32U64& shift_amount); - U32U64 LogicalShiftRightMasked(const U32U64& value_in, const U32U64& shift_amount); - U32U64 ArithmeticShiftRightMasked(const U32U64& value_in, const U32U64& shift_amount); - U32U64 RotateRightMasked(const U32U64& value_in, const U32U64& shift_amount); - ResultAndCarry RotateRightExtended(const U32& value_in, const U1& carry_in); - U32U64 AddWithCarry(const U32U64& a, const U32U64& b, const U1& carry_in); - U32U64 SubWithCarry(const U32U64& a, const U32U64& b, const U1& carry_in); - U32U64 Add(const U32U64& a, const U32U64& b); - U32U64 Sub(const U32U64& a, const U32U64& b); - U32U64 Mul(const U32U64& a, const U32U64& b); - U64 UnsignedMultiplyHigh(const U64& a, const U64& b); - U64 SignedMultiplyHigh(const U64& a, const U64& b); - U32U64 UnsignedDiv(const U32U64& a, const U32U64& b); - U32U64 SignedDiv(const U32U64& a, const U32U64& b); - U32U64 And(const U32U64& a, const U32U64& b); - U32U64 AndNot(const U32U64& a, const U32U64& b); - U32U64 Eor(const U32U64& a, const U32U64& b); - U32U64 Or(const U32U64& a, const U32U64& b); - U32U64 Not(const U32U64& a); - U32 SignExtendToWord(const UAny& a); - U64 SignExtendToLong(const UAny& a); - U32 SignExtendByteToWord(const U8& a); - U32 SignExtendHalfToWord(const U16& a); - U64 SignExtendWordToLong(const U32& a); - U32 ZeroExtendToWord(const UAny& a); - U64 ZeroExtendToLong(const UAny& a); - U128 ZeroExtendToQuad(const UAny& a); - U32 ZeroExtendByteToWord(const U8& a); - U32 ZeroExtendHalfToWord(const U16& a); - U64 ZeroExtendWordToLong(const U32& a); - U32 IndeterminateExtendToWord(const UAny& a); - U64 IndeterminateExtendToLong(const UAny& a); - U32 ByteReverseWord(const U32& a); - U16 ByteReverseHalf(const U16& a); - U64 ByteReverseDual(const U64& a); - U32U64 CountLeadingZeros(const U32U64& a); - U32U64 ExtractRegister(const U32U64& a, const U32U64& b, const U8& lsb); - U32U64 ReplicateBit(const U32U64& a, u8 bit); - U32U64 MaxSigned(const U32U64& a, const U32U64& b); - U32U64 MaxUnsigned(const U32U64& a, const U32U64& b); - U32U64 MinSigned(const U32U64& a, const U32U64& b); - U32U64 MinUnsigned(const U32U64& a, const U32U64& b); + U64 Imm64(u64 imm64) const { + return U64(Value(imm64)); + } - ResultAndOverflow SignedSaturatedAddWithFlag(const U32& a, const U32& b); - ResultAndOverflow SignedSaturatedSubWithFlag(const U32& a, const U32& b); - ResultAndOverflow SignedSaturation(const U32& a, size_t bit_size_to_saturate_to); - ResultAndOverflow UnsignedSaturation(const U32& a, size_t bit_size_to_saturate_to); + void PushRSB(const LocationDescriptor& return_location) { + Inst(Opcode::PushRSB, IR::Value(return_location.Value())); + } - UAny SignedSaturatedAdd(const UAny& a, const UAny& b); - UAny SignedSaturatedDoublingMultiplyReturnHigh(const UAny& a, const UAny& b); - UAny SignedSaturatedSub(const UAny& a, const UAny& b); - UAny UnsignedSaturatedAdd(const UAny& a, const UAny& b); - UAny UnsignedSaturatedSub(const UAny& a, const UAny& b); + U64 Pack2x32To1x64(const U32& lo, const U32& hi) { + return Inst(Opcode::Pack2x32To1x64, lo, hi); + } - U128 VectorSignedSaturatedAdd(size_t esize, const U128& a, const U128& b); - U128 VectorSignedSaturatedSub(size_t esize, const U128& a, const U128& b); - U128 VectorUnsignedSaturatedAdd(size_t esize, const U128& a, const U128& b); - U128 VectorUnsignedSaturatedSub(size_t esize, const U128& a, const U128& b); + U128 Pack2x64To1x128(const U64& lo, const U64& hi) { + return Inst(Opcode::Pack2x64To1x128, lo, hi); + } - ResultAndGE PackedAddU8(const U32& a, const U32& b); - ResultAndGE PackedAddS8(const U32& a, const U32& b); - ResultAndGE PackedAddU16(const U32& a, const U32& b); - ResultAndGE PackedAddS16(const U32& a, const U32& b); - ResultAndGE PackedSubU8(const U32& a, const U32& b); - ResultAndGE PackedSubS8(const U32& a, const U32& b); - ResultAndGE PackedSubU16(const U32& a, const U32& b); - ResultAndGE PackedSubS16(const U32& a, const U32& b); - ResultAndGE PackedAddSubU16(const U32& a, const U32& b); - ResultAndGE PackedAddSubS16(const U32& a, const U32& b); - ResultAndGE PackedSubAddU16(const U32& a, const U32& b); - ResultAndGE PackedSubAddS16(const U32& a, const U32& b); - U32 PackedHalvingAddU8(const U32& a, const U32& b); - U32 PackedHalvingAddS8(const U32& a, const U32& b); - U32 PackedHalvingSubU8(const U32& a, const U32& b); - U32 PackedHalvingSubS8(const U32& a, const U32& b); - U32 PackedHalvingAddU16(const U32& a, const U32& b); - U32 PackedHalvingAddS16(const U32& a, const U32& b); - U32 PackedHalvingSubU16(const U32& a, const U32& b); - U32 PackedHalvingSubS16(const U32& a, const U32& b); - U32 PackedHalvingAddSubU16(const U32& a, const U32& b); - U32 PackedHalvingAddSubS16(const U32& a, const U32& b); - U32 PackedHalvingSubAddU16(const U32& a, const U32& b); - U32 PackedHalvingSubAddS16(const U32& a, const U32& b); - U32 PackedSaturatedAddU8(const U32& a, const U32& b); - U32 PackedSaturatedAddS8(const U32& a, const U32& b); - U32 PackedSaturatedSubU8(const U32& a, const U32& b); - U32 PackedSaturatedSubS8(const U32& a, const U32& b); - U32 PackedSaturatedAddU16(const U32& a, const U32& b); - U32 PackedSaturatedAddS16(const U32& a, const U32& b); - U32 PackedSaturatedSubU16(const U32& a, const U32& b); - U32 PackedSaturatedSubS16(const U32& a, const U32& b); - U32 PackedAbsDiffSumU8(const U32& a, const U32& b); - U32 PackedSelect(const U32& ge, const U32& a, const U32& b); + UAny LeastSignificant(size_t bitsize, const U32U64& value) { + switch (bitsize) { + case 8: + return LeastSignificantByte(value); + case 16: + return LeastSignificantHalf(value); + case 32: + if (value.GetType() == Type::U32) { + return value; + } + return LeastSignificantWord(value); + case 64: + ASSERT(value.GetType() == Type::U64); + return value; + } + ASSERT_FALSE("Invalid bitsize"); + } - U32 CRC32Castagnoli8(const U32& a, const U32& b); - U32 CRC32Castagnoli16(const U32& a, const U32& b); - U32 CRC32Castagnoli32(const U32& a, const U32& b); - U32 CRC32Castagnoli64(const U32& a, const U64& b); - U32 CRC32ISO8(const U32& a, const U32& b); - U32 CRC32ISO16(const U32& a, const U32& b); - U32 CRC32ISO32(const U32& a, const U32& b); - U32 CRC32ISO64(const U32& a, const U64& b); + U32 LeastSignificantWord(const U64& value) { + return Inst(Opcode::LeastSignificantWord, value); + } - U128 AESDecryptSingleRound(const U128& a); - U128 AESEncryptSingleRound(const U128& a); - U128 AESInverseMixColumns(const U128& a); - U128 AESMixColumns(const U128& a); + U16 LeastSignificantHalf(U32U64 value) { + if (value.GetType() == Type::U64) { + value = LeastSignificantWord(value); + } + return Inst(Opcode::LeastSignificantHalf, value); + } - U8 SM4AccessSubstitutionBox(const U8& a); + U8 LeastSignificantByte(U32U64 value) { + if (value.GetType() == Type::U64) { + value = LeastSignificantWord(value); + } + return Inst(Opcode::LeastSignificantByte, value); + } - U128 SHA256Hash(const U128& x, const U128& y, const U128& w, bool part1); - U128 SHA256MessageSchedule0(const U128& x, const U128& y); - U128 SHA256MessageSchedule1(const U128& x, const U128& y, const U128& z); + ResultAndCarry MostSignificantWord(const U64& value) { + const auto result = Inst(Opcode::MostSignificantWord, value); + const auto carry_out = Inst(Opcode::GetCarryFromOp, result); + return {result, carry_out}; + } - UAny VectorGetElement(size_t esize, const U128& a, size_t index); - U128 VectorSetElement(size_t esize, const U128& a, size_t index, const UAny& elem); - U128 VectorAbs(size_t esize, const U128& a); - U128 VectorAdd(size_t esize, const U128& a, const U128& b); - U128 VectorAnd(const U128& a, const U128& b); - U128 VectorAndNot(const U128& a, const U128& b); - U128 VectorArithmeticShiftRight(size_t esize, const U128& a, u8 shift_amount); - U128 VectorArithmeticVShift(size_t esize, const U128& a, const U128& b); - U128 VectorBroadcast(size_t esize, const UAny& a); - U128 VectorBroadcastLower(size_t esize, const UAny& a); - U128 VectorBroadcastElement(size_t esize, const U128& a, size_t index); - U128 VectorBroadcastElementLower(size_t esize, const U128& a, size_t index); - U128 VectorCountLeadingZeros(size_t esize, const U128& a); - U128 VectorEor(const U128& a, const U128& b); - U128 VectorDeinterleaveEven(size_t esize, const U128& a, const U128& b); - U128 VectorDeinterleaveEvenLower(size_t esize, const U128& a, const U128& b); - U128 VectorDeinterleaveOdd(size_t esize, const U128& a, const U128& b); - U128 VectorDeinterleaveOddLower(size_t esize, const U128& a, const U128& b); - U128 VectorEqual(size_t esize, const U128& a, const U128& b); - U128 VectorExtract(const U128& a, const U128& b, size_t position); - U128 VectorExtractLower(const U128& a, const U128& b, size_t position); - U128 VectorGreaterEqualSigned(size_t esize, const U128& a, const U128& b); - U128 VectorGreaterEqualUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorGreaterSigned(size_t esize, const U128& a, const U128& b); - U128 VectorGreaterUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorHalvingAddSigned(size_t esize, const U128& a, const U128& b); - U128 VectorHalvingAddUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorHalvingSubSigned(size_t esize, const U128& a, const U128& b); - U128 VectorHalvingSubUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorInterleaveLower(size_t esize, const U128& a, const U128& b); - U128 VectorInterleaveUpper(size_t esize, const U128& a, const U128& b); - U128 VectorLessEqualSigned(size_t esize, const U128& a, const U128& b); - U128 VectorLessEqualUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorLessSigned(size_t esize, const U128& a, const U128& b); - U128 VectorLessUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorLogicalShiftLeft(size_t esize, const U128& a, u8 shift_amount); - U128 VectorLogicalShiftRight(size_t esize, const U128& a, u8 shift_amount); - U128 VectorLogicalVShift(size_t esize, const U128& a, const U128& b); - U128 VectorMaxSigned(size_t esize, const U128& a, const U128& b); - U128 VectorMaxUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorMinSigned(size_t esize, const U128& a, const U128& b); - U128 VectorMinUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorMultiply(size_t esize, const U128& a, const U128& b); - U128 VectorMultiplySignedWiden(size_t esize, const U128& a, const U128& b); - U128 VectorMultiplyUnsignedWiden(size_t esize, const U128& a, const U128& b); - U128 VectorNarrow(size_t original_esize, const U128& a); - U128 VectorNot(const U128& a); - U128 VectorOr(const U128& a, const U128& b); - U128 VectorPairedAdd(size_t esize, const U128& a, const U128& b); - U128 VectorPairedAddLower(size_t esize, const U128& a, const U128& b); - U128 VectorPairedAddSignedWiden(size_t original_esize, const U128& a); - U128 VectorPairedAddUnsignedWiden(size_t original_esize, const U128& a); - U128 VectorPairedMaxSigned(size_t esize, const U128& a, const U128& b); - U128 VectorPairedMaxUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorPairedMinSigned(size_t esize, const U128& a, const U128& b); - U128 VectorPairedMinUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorPairedMaxSignedLower(size_t esize, const U128& a, const U128& b); - U128 VectorPairedMaxUnsignedLower(size_t esize, const U128& a, const U128& b); - U128 VectorPairedMinSignedLower(size_t esize, const U128& a, const U128& b); - U128 VectorPairedMinUnsignedLower(size_t esize, const U128& a, const U128& b); - U128 VectorPolynomialMultiply(const U128& a, const U128& b); - U128 VectorPolynomialMultiplyLong(size_t esize, const U128& a, const U128& b); - U128 VectorPopulationCount(const U128& a); - U128 VectorReverseBits(const U128& a); - U128 VectorReverseElementsInHalfGroups(size_t esize, const U128& a); - U128 VectorReverseElementsInWordGroups(size_t esize, const U128& a); - U128 VectorReverseElementsInLongGroups(size_t esize, const U128& a); - U128 VectorReduceAdd(size_t esize, const U128& a); - U128 VectorRotateLeft(size_t esize, const U128& a, u8 amount); - U128 VectorRotateRight(size_t esize, const U128& a, u8 amount); - U128 VectorRotateWholeVectorRight(const U128& a, u8 amount); - U128 VectorRoundingHalvingAddSigned(size_t esize, const U128& a, const U128& b); - U128 VectorRoundingHalvingAddUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorRoundingShiftLeftSigned(size_t esize, const U128& a, const U128& b); - U128 VectorRoundingShiftLeftUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorSignExtend(size_t original_esize, const U128& a); - U128 VectorSignedAbsoluteDifference(size_t esize, const U128& a, const U128& b); - UpperAndLower VectorSignedMultiply(size_t esize, const U128& a, const U128& b); - U128 VectorSignedSaturatedAbs(size_t esize, const U128& a); - U128 VectorSignedSaturatedAccumulateUnsigned(size_t esize, const U128& a, const U128& b); - U128 VectorSignedSaturatedDoublingMultiplyHigh(size_t esize, const U128& a, const U128& b); - U128 VectorSignedSaturatedDoublingMultiplyHighRounding(size_t esize, const U128& a, const U128& b); - U128 VectorSignedSaturatedDoublingMultiplyLong(size_t esize, const U128& a, const U128& b); - U128 VectorSignedSaturatedNarrowToSigned(size_t original_esize, const U128& a); - U128 VectorSignedSaturatedNarrowToUnsigned(size_t original_esize, const U128& a); - U128 VectorSignedSaturatedNeg(size_t esize, const U128& a); - U128 VectorSignedSaturatedShiftLeft(size_t esize, const U128& a, const U128& b); - U128 VectorSignedSaturatedShiftLeftUnsigned(size_t esize, const U128& a, u8 shift_amount); - U128 VectorSub(size_t esize, const U128& a, const U128& b); - Table VectorTable(std::vector values); - Table VectorTable(std::vector values); - U64 VectorTableLookup(const U64& defaults, const Table& table, const U64& indices); - U128 VectorTableLookup(const U128& defaults, const Table& table, const U128& indices); - U128 VectorTranspose(size_t esize, const U128& a, const U128& b, bool part); - U128 VectorUnsignedAbsoluteDifference(size_t esize, const U128& a, const U128& b); - U128 VectorUnsignedRecipEstimate(const U128& a); - U128 VectorUnsignedRecipSqrtEstimate(const U128& a); - U128 VectorUnsignedSaturatedAccumulateSigned(size_t esize, const U128& a, const U128& b); - U128 VectorUnsignedSaturatedNarrow(size_t esize, const U128& a); - U128 VectorUnsignedSaturatedShiftLeft(size_t esize, const U128& a, const U128& b); - U128 VectorZeroExtend(size_t original_esize, const U128& a); - U128 VectorZeroUpper(const U128& a); - U128 ZeroVector(); + U1 MostSignificantBit(const U32& value) { + return Inst(Opcode::MostSignificantBit, value); + } - U16U32U64 FPAbs(const U16U32U64& a); - U32U64 FPAdd(const U32U64& a, const U32U64& b); - NZCV FPCompare(const U32U64& a, const U32U64& b, bool exc_on_qnan); - U32U64 FPDiv(const U32U64& a, const U32U64& b); - U32U64 FPMax(const U32U64& a, const U32U64& b); - U32U64 FPMaxNumeric(const U32U64& a, const U32U64& b); - U32U64 FPMin(const U32U64& a, const U32U64& b); - U32U64 FPMinNumeric(const U32U64& a, const U32U64& b); - U32U64 FPMul(const U32U64& a, const U32U64& b); - U16U32U64 FPMulAdd(const U16U32U64& addend, const U16U32U64& op1, const U16U32U64& op2); - U16U32U64 FPMulSub(const U16U32U64& minuend, const U16U32U64& op1, const U16U32U64& op2); - U32U64 FPMulX(const U32U64& a, const U32U64& b); - U16U32U64 FPNeg(const U16U32U64& a); - U16U32U64 FPRecipEstimate(const U16U32U64& a); - U16U32U64 FPRecipExponent(const U16U32U64& a); - U16U32U64 FPRecipStepFused(const U16U32U64& a, const U16U32U64& b); - U16U32U64 FPRoundInt(const U16U32U64& a, FP::RoundingMode rounding, bool exact); - U16U32U64 FPRSqrtEstimate(const U16U32U64& a); - U16U32U64 FPRSqrtStepFused(const U16U32U64& a, const U16U32U64& b); - U32U64 FPSqrt(const U32U64& a); - U32U64 FPSub(const U32U64& a, const U32U64& b); - U16 FPDoubleToHalf(const U64& a, FP::RoundingMode rounding); - U32 FPDoubleToSingle(const U64& a, FP::RoundingMode rounding); - U64 FPHalfToDouble(const U16& a, FP::RoundingMode rounding); - U32 FPHalfToSingle(const U16& a, FP::RoundingMode rounding); - U16 FPSingleToHalf(const U32& a, FP::RoundingMode rounding); - U64 FPSingleToDouble(const U32& a, FP::RoundingMode rounding); - U16 FPToFixedS16(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding); - U32 FPToFixedS32(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding); - U64 FPToFixedS64(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding); - U16 FPToFixedU16(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding); - U32 FPToFixedU32(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding); - U64 FPToFixedU64(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding); - U32 FPSignedFixedToSingle(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding); - U32 FPUnsignedFixedToSingle(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding); - U64 FPSignedFixedToDouble(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding); - U64 FPUnsignedFixedToDouble(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding); + U1 IsZero(const U32& value) { + return Inst(Opcode::IsZero32, value); + } - U128 FPVectorAbs(size_t esize, const U128& a); - U128 FPVectorAdd(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorDiv(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorEqual(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorFromHalf(size_t esize, const U128& a, FP::RoundingMode rounding, bool fpcr_controlled = true); - U128 FPVectorFromSignedFixed(size_t esize, const U128& a, size_t fbits, FP::RoundingMode rounding, bool fpcr_controlled = true); - U128 FPVectorFromUnsignedFixed(size_t esize, const U128& a, size_t fbits, FP::RoundingMode rounding, bool fpcr_controlled = true); - U128 FPVectorGreater(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorGreaterEqual(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorMax(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorMaxNumeric(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorMin(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorMinNumeric(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorMul(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorMulAdd(size_t esize, const U128& addend, const U128& op1, const U128& op2, bool fpcr_controlled = true); - U128 FPVectorMulX(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorNeg(size_t esize, const U128& a); - U128 FPVectorPairedAdd(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorPairedAddLower(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorRecipEstimate(size_t esize, const U128& a, bool fpcr_controlled = true); - U128 FPVectorRecipStepFused(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorRoundInt(size_t esize, const U128& operand, FP::RoundingMode rounding, bool exact, bool fpcr_controlled = true); - U128 FPVectorRSqrtEstimate(size_t esize, const U128& a, bool fpcr_controlled = true); - U128 FPVectorRSqrtStepFused(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorSqrt(size_t esize, const U128& a, bool fpcr_controlled = true); - U128 FPVectorSub(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true); - U128 FPVectorToHalf(size_t esize, const U128& a, FP::RoundingMode rounding, bool fpcr_controlled = true); - U128 FPVectorToSignedFixed(size_t esize, const U128& a, size_t fbits, FP::RoundingMode rounding, bool fpcr_controlled = true); - U128 FPVectorToUnsignedFixed(size_t esize, const U128& a, size_t fbits, FP::RoundingMode rounding, bool fpcr_controlled = true); + U1 IsZero(const U64& value) { + return Inst(Opcode::IsZero64, value); + } - void Breakpoint(); - void CallHostFunction(void (*fn)(void)); - void CallHostFunction(void (*fn)(u64), const U64& arg1); - void CallHostFunction(void (*fn)(u64, u64), const U64& arg1, const U64& arg2); - void CallHostFunction(void (*fn)(u64, u64, u64), const U64& arg1, const U64& arg2, const U64& arg3); + U1 IsZero(const U32U64& value) { + if (value.GetType() == Type::U32) { + return Inst(Opcode::IsZero32, value); + } else { + return Inst(Opcode::IsZero64, value); + } + } - void SetTerm(const Terminal& terminal); + U1 TestBit(const U32U64& value, const U8& bit) { + if (value.GetType() == Type::U32) { + return Inst(Opcode::TestBit, IndeterminateExtendToLong(value), bit); + } else { + return Inst(Opcode::TestBit, value, bit); + } + } + + U32 ConditionalSelect(Cond cond, const U32& a, const U32& b) { + return Inst(Opcode::ConditionalSelect32, Value{cond}, a, b); + } + + U64 ConditionalSelect(Cond cond, const U64& a, const U64& b) { + return Inst(Opcode::ConditionalSelect64, Value{cond}, a, b); + } + + NZCV ConditionalSelect(Cond cond, const NZCV& a, const NZCV& b) { + return Inst(Opcode::ConditionalSelectNZCV, Value{cond}, a, b); + } + + U32U64 ConditionalSelect(Cond cond, const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + if (a.GetType() == Type::U32) { + return Inst(Opcode::ConditionalSelect32, Value{cond}, a, b); + } else { + return Inst(Opcode::ConditionalSelect64, Value{cond}, a, b); + } + } + + U1 GetCFlagFromNZCV(const NZCV& nzcv) { + return Inst(Opcode::GetCFlagFromNZCV, nzcv); + } + + NZCV NZCVFromPackedFlags(const U32& a) { + return Inst(Opcode::NZCVFromPackedFlags, a); + } + + NZCV NZCVFrom(const Value& value) { + return Inst(Opcode::GetNZCVFromOp, value); + } + + ResultAndCarry LogicalShiftLeft(const U32& value_in, const U8& shift_amount, const U1& carry_in) { + const auto result = Inst(Opcode::LogicalShiftLeft32, value_in, shift_amount, carry_in); + const auto carry_out = Inst(Opcode::GetCarryFromOp, result); + return {result, carry_out}; + } + + ResultAndCarry LogicalShiftRight(const U32& value_in, const U8& shift_amount, const U1& carry_in) { + const auto result = Inst(Opcode::LogicalShiftRight32, value_in, shift_amount, carry_in); + const auto carry_out = Inst(Opcode::GetCarryFromOp, result); + return {result, carry_out}; + } + + ResultAndCarry ArithmeticShiftRight(const U32& value_in, const U8& shift_amount, const U1& carry_in) { + const auto result = Inst(Opcode::ArithmeticShiftRight32, value_in, shift_amount, carry_in); + const auto carry_out = Inst(Opcode::GetCarryFromOp, result); + return {result, carry_out}; + } + + ResultAndCarry RotateRight(const U32& value_in, const U8& shift_amount, const U1& carry_in) { + const auto result = Inst(Opcode::RotateRight32, value_in, shift_amount, carry_in); + const auto carry_out = Inst(Opcode::GetCarryFromOp, result); + return {result, carry_out}; + } + + ResultAndCarry RotateRightExtended(const U32& value_in, const U1& carry_in) { + const auto result = Inst(Opcode::RotateRightExtended, value_in, carry_in); + const auto carry_out = Inst(Opcode::GetCarryFromOp, result); + return {result, carry_out}; + } + + U32U64 LogicalShiftLeft(const U32U64& value_in, const U8& shift_amount) { + if (value_in.GetType() == Type::U32) { + return Inst(Opcode::LogicalShiftLeft32, value_in, shift_amount, Imm1(0)); + } else { + return Inst(Opcode::LogicalShiftLeft64, value_in, shift_amount); + } + } + + U32U64 LogicalShiftRight(const U32U64& value_in, const U8& shift_amount) { + if (value_in.GetType() == Type::U32) { + return Inst(Opcode::LogicalShiftRight32, value_in, shift_amount, Imm1(0)); + } else { + return Inst(Opcode::LogicalShiftRight64, value_in, shift_amount); + } + } + + U32U64 ArithmeticShiftRight(const U32U64& value_in, const U8& shift_amount) { + if (value_in.GetType() == Type::U32) { + return Inst(Opcode::ArithmeticShiftRight32, value_in, shift_amount, Imm1(0)); + } else { + return Inst(Opcode::ArithmeticShiftRight64, value_in, shift_amount); + } + } + + U32U64 RotateRight(const U32U64& value_in, const U8& shift_amount) { + if (value_in.GetType() == Type::U32) { + return Inst(Opcode::RotateRight32, value_in, shift_amount, Imm1(0)); + } else { + return Inst(Opcode::RotateRight64, value_in, shift_amount); + } + } + + U32U64 LogicalShiftLeftMasked(const U32U64& value_in, const U32U64& shift_amount) { + ASSERT(value_in.GetType() == shift_amount.GetType()); + if (value_in.GetType() == Type::U32) { + return Inst(Opcode::LogicalShiftLeftMasked32, value_in, shift_amount); + } else { + return Inst(Opcode::LogicalShiftLeftMasked64, value_in, shift_amount); + } + } + + U32U64 LogicalShiftRightMasked(const U32U64& value_in, const U32U64& shift_amount) { + ASSERT(value_in.GetType() == shift_amount.GetType()); + if (value_in.GetType() == Type::U32) { + return Inst(Opcode::LogicalShiftRightMasked32, value_in, shift_amount); + } else { + return Inst(Opcode::LogicalShiftRightMasked64, value_in, shift_amount); + } + } + + U32U64 ArithmeticShiftRightMasked(const U32U64& value_in, const U32U64& shift_amount) { + ASSERT(value_in.GetType() == shift_amount.GetType()); + if (value_in.GetType() == Type::U32) { + return Inst(Opcode::ArithmeticShiftRightMasked32, value_in, shift_amount); + } else { + return Inst(Opcode::ArithmeticShiftRightMasked64, value_in, shift_amount); + } + } + + U32U64 RotateRightMasked(const U32U64& value_in, const U32U64& shift_amount) { + ASSERT(value_in.GetType() == shift_amount.GetType()); + if (value_in.GetType() == Type::U32) { + return Inst(Opcode::RotateRightMasked32, value_in, shift_amount); + } else { + return Inst(Opcode::RotateRightMasked64, value_in, shift_amount); + } + } + + U32U64 AddWithCarry(const U32U64& a, const U32U64& b, const U1& carry_in) { + ASSERT(a.GetType() == b.GetType()); + if (a.GetType() == Type::U32) { + return Inst(Opcode::Add32, a, b, carry_in); + } else { + return Inst(Opcode::Add64, a, b, carry_in); + } + } + + U32U64 Add(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + if (a.GetType() == Type::U32) { + return Inst(Opcode::Add32, a, b, Imm1(0)); + } else { + return Inst(Opcode::Add64, a, b, Imm1(0)); + } + } + + U32U64 SubWithCarry(const U32U64& a, const U32U64& b, const U1& carry_in) { + ASSERT(a.GetType() == b.GetType()); + if (a.GetType() == Type::U32) { + return Inst(Opcode::Sub32, a, b, carry_in); + } else { + return Inst(Opcode::Sub64, a, b, carry_in); + } + } + + U32U64 Sub(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + if (a.GetType() == Type::U32) { + return Inst(Opcode::Sub32, a, b, Imm1(1)); + } else { + return Inst(Opcode::Sub64, a, b, Imm1(1)); + } + } + + U32U64 Mul(const U32U64& a, const U32U64& b) { + if (a.GetType() == Type::U32) { + return Inst(Opcode::Mul32, a, b); + } + + return Inst(Opcode::Mul64, a, b); + } + + U64 UnsignedMultiplyHigh(const U64& a, const U64& b) { + return Inst(Opcode::UnsignedMultiplyHigh64, a, b); + } + + U64 SignedMultiplyHigh(const U64& a, const U64& b) { + return Inst(Opcode::SignedMultiplyHigh64, a, b); + } + + U32U64 UnsignedDiv(const U32U64& a, const U32U64& b) { + if (a.GetType() == Type::U32) { + return Inst(Opcode::UnsignedDiv32, a, b); + } + + return Inst(Opcode::UnsignedDiv64, a, b); + } + + U32U64 SignedDiv(const U32U64& a, const U32U64& b) { + if (a.GetType() == Type::U32) { + return Inst(Opcode::SignedDiv32, a, b); + } + + return Inst(Opcode::SignedDiv64, a, b); + } + + U32U64 And(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + if (a.GetType() == Type::U32) { + return Inst(Opcode::And32, a, b); + } else { + return Inst(Opcode::And64, a, b); + } + } + + U32U64 AndNot(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + if (a.GetType() == Type::U32) { + return Inst(Opcode::AndNot32, a, b); + } else { + return Inst(Opcode::AndNot64, a, b); + } + } + + U32U64 Eor(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + if (a.GetType() == Type::U32) { + return Inst(Opcode::Eor32, a, b); + } else { + return Inst(Opcode::Eor64, a, b); + } + } + + U32U64 Or(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + if (a.GetType() == Type::U32) { + return Inst(Opcode::Or32, a, b); + } else { + return Inst(Opcode::Or64, a, b); + } + } + + U32U64 Not(const U32U64& a) { + if (a.GetType() == Type::U32) { + return Inst(Opcode::Not32, a); + } else { + return Inst(Opcode::Not64, a); + } + } + + U64 SignExtendToLong(const UAny& a) { + switch (a.GetType()) { + case Type::U8: + return Inst(Opcode::SignExtendByteToLong, a); + case Type::U16: + return Inst(Opcode::SignExtendHalfToLong, a); + case Type::U32: + return Inst(Opcode::SignExtendWordToLong, a); + case Type::U64: + return U64(a); + default: + UNREACHABLE(); + } + } + + U32 SignExtendToWord(const UAny& a) { + switch (a.GetType()) { + case Type::U8: + return Inst(Opcode::SignExtendByteToWord, a); + case Type::U16: + return Inst(Opcode::SignExtendHalfToWord, a); + case Type::U32: + return U32(a); + case Type::U64: + return Inst(Opcode::LeastSignificantWord, a); + default: + UNREACHABLE(); + } + } + + U64 SignExtendWordToLong(const U32& a) { + return Inst(Opcode::SignExtendWordToLong, a); + } + + U32 SignExtendHalfToWord(const U16& a) { + return Inst(Opcode::SignExtendHalfToWord, a); + } + + U32 SignExtendByteToWord(const U8& a) { + return Inst(Opcode::SignExtendByteToWord, a); + } + + U64 ZeroExtendToLong(const UAny& a) { + switch (a.GetType()) { + case Type::U8: + return Inst(Opcode::ZeroExtendByteToLong, a); + case Type::U16: + return Inst(Opcode::ZeroExtendHalfToLong, a); + case Type::U32: + return Inst(Opcode::ZeroExtendWordToLong, a); + case Type::U64: + return U64(a); + default: + UNREACHABLE(); + } + } + + U32 ZeroExtendToWord(const UAny& a) { + switch (a.GetType()) { + case Type::U8: + return Inst(Opcode::ZeroExtendByteToWord, a); + case Type::U16: + return Inst(Opcode::ZeroExtendHalfToWord, a); + case Type::U32: + return U32(a); + case Type::U64: + return Inst(Opcode::LeastSignificantWord, a); + default: + UNREACHABLE(); + } + } + + U128 ZeroExtendToQuad(const UAny& a) { + return Inst(Opcode::ZeroExtendLongToQuad, ZeroExtendToLong(a)); + } + + U64 ZeroExtendWordToLong(const U32& a) { + return Inst(Opcode::ZeroExtendWordToLong, a); + } + + U32 ZeroExtendHalfToWord(const U16& a) { + return Inst(Opcode::ZeroExtendHalfToWord, a); + } + + U32 ZeroExtendByteToWord(const U8& a) { + return Inst(Opcode::ZeroExtendByteToWord, a); + } + + U32 IndeterminateExtendToWord(const UAny& a) { + // TODO: Implement properly + return ZeroExtendToWord(a); + } + + U64 IndeterminateExtendToLong(const UAny& a) { + // TODO: Implement properly + return ZeroExtendToLong(a); + } + + U32 ByteReverseWord(const U32& a) { + return Inst(Opcode::ByteReverseWord, a); + } + + U16 ByteReverseHalf(const U16& a) { + return Inst(Opcode::ByteReverseHalf, a); + } + + U64 ByteReverseDual(const U64& a) { + return Inst(Opcode::ByteReverseDual, a); + } + + U32U64 CountLeadingZeros(const U32U64& a) { + if (a.GetType() == IR::Type::U32) { + return Inst(Opcode::CountLeadingZeros32, a); + } + + return Inst(Opcode::CountLeadingZeros64, a); + } + + U32U64 ExtractRegister(const U32U64& a, const U32U64& b, const U8& lsb) { + if (a.GetType() == IR::Type::U32) { + return Inst(Opcode::ExtractRegister32, a, b, lsb); + } + + return Inst(Opcode::ExtractRegister64, a, b, lsb); + } + + U32U64 ReplicateBit(const U32U64& a, u8 bit) { + if (a.GetType() == IR::Type::U32) { + ASSERT(bit < 32); + return Inst(Opcode::ReplicateBit32, a, Imm8(bit)); + } + + ASSERT(bit < 64); + return Inst(Opcode::ReplicateBit64, a, Imm8(bit)); + } + + U32U64 MaxSigned(const U32U64& a, const U32U64& b) { + if (a.GetType() == IR::Type::U32) { + return Inst(Opcode::MaxSigned32, a, b); + } + + return Inst(Opcode::MaxSigned64, a, b); + } + + U32U64 MaxUnsigned(const U32U64& a, const U32U64& b) { + if (a.GetType() == IR::Type::U32) { + return Inst(Opcode::MaxUnsigned32, a, b); + } + + return Inst(Opcode::MaxUnsigned64, a, b); + } + + U32U64 MinSigned(const U32U64& a, const U32U64& b) { + if (a.GetType() == IR::Type::U32) { + return Inst(Opcode::MinSigned32, a, b); + } + + return Inst(Opcode::MinSigned64, a, b); + } + + U32U64 MinUnsigned(const U32U64& a, const U32U64& b) { + if (a.GetType() == IR::Type::U32) { + return Inst(Opcode::MinUnsigned32, a, b); + } + + return Inst(Opcode::MinUnsigned64, a, b); + } + + ResultAndOverflow SignedSaturatedAddWithFlag(const U32& a, const U32& b) { + const auto result = Inst(Opcode::SignedSaturatedAddWithFlag32, a, b); + const auto overflow = Inst(Opcode::GetOverflowFromOp, result); + return {result, overflow}; + } + + ResultAndOverflow SignedSaturatedSubWithFlag(const U32& a, const U32& b) { + const auto result = Inst(Opcode::SignedSaturatedSubWithFlag32, a, b); + const auto overflow = Inst(Opcode::GetOverflowFromOp, result); + return {result, overflow}; + } + + ResultAndOverflow SignedSaturation(const U32& a, size_t bit_size_to_saturate_to) { + ASSERT(bit_size_to_saturate_to >= 1 && bit_size_to_saturate_to <= 32); + const auto result = Inst(Opcode::SignedSaturation, a, Imm8(static_cast(bit_size_to_saturate_to))); + const auto overflow = Inst(Opcode::GetOverflowFromOp, result); + return {result, overflow}; + } + + ResultAndOverflow UnsignedSaturation(const U32& a, size_t bit_size_to_saturate_to) { + ASSERT(bit_size_to_saturate_to <= 31); + const auto result = Inst(Opcode::UnsignedSaturation, a, Imm8(static_cast(bit_size_to_saturate_to))); + const auto overflow = Inst(Opcode::GetOverflowFromOp, result); + return {result, overflow}; + } + + UAny SignedSaturatedAdd(const UAny& a, const UAny& b) { + ASSERT(a.GetType() == b.GetType()); + const auto result = [&]() -> IR::UAny { + switch (a.GetType()) { + case IR::Type::U8: + return Inst(Opcode::SignedSaturatedAdd8, a, b); + case IR::Type::U16: + return Inst(Opcode::SignedSaturatedAdd16, a, b); + case IR::Type::U32: + return Inst(Opcode::SignedSaturatedAdd32, a, b); + case IR::Type::U64: + return Inst(Opcode::SignedSaturatedAdd64, a, b); + default: + return IR::UAny{}; + } + }(); + return result; + } + + UAny SignedSaturatedDoublingMultiplyReturnHigh(const UAny& a, const UAny& b) { + ASSERT(a.GetType() == b.GetType()); + const auto result = [&]() -> IR::UAny { + switch (a.GetType()) { + case IR::Type::U16: + return Inst(Opcode::SignedSaturatedDoublingMultiplyReturnHigh16, a, b); + case IR::Type::U32: + return Inst(Opcode::SignedSaturatedDoublingMultiplyReturnHigh32, a, b); + default: + UNREACHABLE(); + } + }(); + return result; + } + + UAny SignedSaturatedSub(const UAny& a, const UAny& b) { + ASSERT(a.GetType() == b.GetType()); + const auto result = [&]() -> IR::UAny { + switch (a.GetType()) { + case IR::Type::U8: + return Inst(Opcode::SignedSaturatedSub8, a, b); + case IR::Type::U16: + return Inst(Opcode::SignedSaturatedSub16, a, b); + case IR::Type::U32: + return Inst(Opcode::SignedSaturatedSub32, a, b); + case IR::Type::U64: + return Inst(Opcode::SignedSaturatedSub64, a, b); + default: + return IR::UAny{}; + } + }(); + return result; + } + + UAny UnsignedSaturatedAdd(const UAny& a, const UAny& b) { + ASSERT(a.GetType() == b.GetType()); + const auto result = [&]() -> IR::UAny { + switch (a.GetType()) { + case IR::Type::U8: + return Inst(Opcode::UnsignedSaturatedAdd8, a, b); + case IR::Type::U16: + return Inst(Opcode::UnsignedSaturatedAdd16, a, b); + case IR::Type::U32: + return Inst(Opcode::UnsignedSaturatedAdd32, a, b); + case IR::Type::U64: + return Inst(Opcode::UnsignedSaturatedAdd64, a, b); + default: + return IR::UAny{}; + } + }(); + return result; + } + + UAny UnsignedSaturatedSub(const UAny& a, const UAny& b) { + ASSERT(a.GetType() == b.GetType()); + const auto result = [&]() -> IR::UAny { + switch (a.GetType()) { + case IR::Type::U8: + return Inst(Opcode::UnsignedSaturatedSub8, a, b); + case IR::Type::U16: + return Inst(Opcode::UnsignedSaturatedSub16, a, b); + case IR::Type::U32: + return Inst(Opcode::UnsignedSaturatedSub32, a, b); + case IR::Type::U64: + return Inst(Opcode::UnsignedSaturatedSub64, a, b); + default: + return IR::UAny{}; + } + }(); + return result; + } + + U128 VectorSignedSaturatedAdd(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorSignedSaturatedAdd8, a, b); + case 16: + return Inst(Opcode::VectorSignedSaturatedAdd16, a, b); + case 32: + return Inst(Opcode::VectorSignedSaturatedAdd32, a, b); + case 64: + return Inst(Opcode::VectorSignedSaturatedAdd64, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorSignedSaturatedSub(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorSignedSaturatedSub8, a, b); + case 16: + return Inst(Opcode::VectorSignedSaturatedSub16, a, b); + case 32: + return Inst(Opcode::VectorSignedSaturatedSub32, a, b); + case 64: + return Inst(Opcode::VectorSignedSaturatedSub64, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorUnsignedSaturatedAdd(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorUnsignedSaturatedAdd8, a, b); + case 16: + return Inst(Opcode::VectorUnsignedSaturatedAdd16, a, b); + case 32: + return Inst(Opcode::VectorUnsignedSaturatedAdd32, a, b); + case 64: + return Inst(Opcode::VectorUnsignedSaturatedAdd64, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorUnsignedSaturatedSub(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorUnsignedSaturatedSub8, a, b); + case 16: + return Inst(Opcode::VectorUnsignedSaturatedSub16, a, b); + case 32: + return Inst(Opcode::VectorUnsignedSaturatedSub32, a, b); + case 64: + return Inst(Opcode::VectorUnsignedSaturatedSub64, a, b); + default: + UNREACHABLE(); + } + } + + ResultAndGE PackedAddU8(const U32& a, const U32& b) { + const auto result = Inst(Opcode::PackedAddU8, a, b); + const auto ge = Inst(Opcode::GetGEFromOp, result); + return {result, ge}; + } + + ResultAndGE PackedAddS8(const U32& a, const U32& b) { + const auto result = Inst(Opcode::PackedAddS8, a, b); + const auto ge = Inst(Opcode::GetGEFromOp, result); + return {result, ge}; + } + + ResultAndGE PackedAddU16(const U32& a, const U32& b) { + const auto result = Inst(Opcode::PackedAddU16, a, b); + const auto ge = Inst(Opcode::GetGEFromOp, result); + return {result, ge}; + } + + ResultAndGE PackedAddS16(const U32& a, const U32& b) { + const auto result = Inst(Opcode::PackedAddS16, a, b); + const auto ge = Inst(Opcode::GetGEFromOp, result); + return {result, ge}; + } + + ResultAndGE PackedSubU8(const U32& a, const U32& b) { + const auto result = Inst(Opcode::PackedSubU8, a, b); + const auto ge = Inst(Opcode::GetGEFromOp, result); + return {result, ge}; + } + + ResultAndGE PackedSubS8(const U32& a, const U32& b) { + const auto result = Inst(Opcode::PackedSubS8, a, b); + const auto ge = Inst(Opcode::GetGEFromOp, result); + return {result, ge}; + } + + ResultAndGE PackedSubU16(const U32& a, const U32& b) { + const auto result = Inst(Opcode::PackedSubU16, a, b); + const auto ge = Inst(Opcode::GetGEFromOp, result); + return {result, ge}; + } + + ResultAndGE PackedSubS16(const U32& a, const U32& b) { + const auto result = Inst(Opcode::PackedSubS16, a, b); + const auto ge = Inst(Opcode::GetGEFromOp, result); + return {result, ge}; + } + + ResultAndGE PackedAddSubU16(const U32& a, const U32& b) { + const auto result = Inst(Opcode::PackedAddSubU16, a, b); + const auto ge = Inst(Opcode::GetGEFromOp, result); + return {result, ge}; + } + + ResultAndGE PackedAddSubS16(const U32& a, const U32& b) { + const auto result = Inst(Opcode::PackedAddSubS16, a, b); + const auto ge = Inst(Opcode::GetGEFromOp, result); + return {result, ge}; + } + + ResultAndGE PackedSubAddU16(const U32& a, const U32& b) { + const auto result = Inst(Opcode::PackedSubAddU16, a, b); + const auto ge = Inst(Opcode::GetGEFromOp, result); + return {result, ge}; + } + + ResultAndGE PackedSubAddS16(const U32& a, const U32& b) { + const auto result = Inst(Opcode::PackedSubAddS16, a, b); + const auto ge = Inst(Opcode::GetGEFromOp, result); + return {result, ge}; + } + + U32 PackedHalvingAddU8(const U32& a, const U32& b) { + return Inst(Opcode::PackedHalvingAddU8, a, b); + } + + U32 PackedHalvingAddS8(const U32& a, const U32& b) { + return Inst(Opcode::PackedHalvingAddS8, a, b); + } + + U32 PackedHalvingSubU8(const U32& a, const U32& b) { + return Inst(Opcode::PackedHalvingSubU8, a, b); + } + + U32 PackedHalvingSubS8(const U32& a, const U32& b) { + return Inst(Opcode::PackedHalvingSubS8, a, b); + } + + U32 PackedHalvingAddU16(const U32& a, const U32& b) { + return Inst(Opcode::PackedHalvingAddU16, a, b); + } + + U32 PackedHalvingAddS16(const U32& a, const U32& b) { + return Inst(Opcode::PackedHalvingAddS16, a, b); + } + + U32 PackedHalvingSubU16(const U32& a, const U32& b) { + return Inst(Opcode::PackedHalvingSubU16, a, b); + } + + U32 PackedHalvingSubS16(const U32& a, const U32& b) { + return Inst(Opcode::PackedHalvingSubS16, a, b); + } + + U32 PackedHalvingAddSubU16(const U32& a, const U32& b) { + return Inst(Opcode::PackedHalvingAddSubU16, a, b); + } + + U32 PackedHalvingAddSubS16(const U32& a, const U32& b) { + return Inst(Opcode::PackedHalvingAddSubS16, a, b); + } + + U32 PackedHalvingSubAddU16(const U32& a, const U32& b) { + return Inst(Opcode::PackedHalvingSubAddU16, a, b); + } + + U32 PackedHalvingSubAddS16(const U32& a, const U32& b) { + return Inst(Opcode::PackedHalvingSubAddS16, a, b); + } + + U32 PackedSaturatedAddU8(const U32& a, const U32& b) { + return Inst(Opcode::PackedSaturatedAddU8, a, b); + } + + U32 PackedSaturatedAddS8(const U32& a, const U32& b) { + return Inst(Opcode::PackedSaturatedAddS8, a, b); + } + + U32 PackedSaturatedSubU8(const U32& a, const U32& b) { + return Inst(Opcode::PackedSaturatedSubU8, a, b); + } + + U32 PackedSaturatedSubS8(const U32& a, const U32& b) { + return Inst(Opcode::PackedSaturatedSubS8, a, b); + } + + U32 PackedSaturatedAddU16(const U32& a, const U32& b) { + return Inst(Opcode::PackedSaturatedAddU16, a, b); + } + + U32 PackedSaturatedAddS16(const U32& a, const U32& b) { + return Inst(Opcode::PackedSaturatedAddS16, a, b); + } + + U32 PackedSaturatedSubU16(const U32& a, const U32& b) { + return Inst(Opcode::PackedSaturatedSubU16, a, b); + } + + U32 PackedSaturatedSubS16(const U32& a, const U32& b) { + return Inst(Opcode::PackedSaturatedSubS16, a, b); + } + + U32 PackedAbsDiffSumU8(const U32& a, const U32& b) { + return Inst(Opcode::PackedAbsDiffSumU8, a, b); + } + + U32 PackedSelect(const U32& ge, const U32& a, const U32& b) { + return Inst(Opcode::PackedSelect, ge, a, b); + } + + U32 CRC32Castagnoli8(const U32& a, const U32& b) { + return Inst(Opcode::CRC32Castagnoli8, a, b); + } + + U32 CRC32Castagnoli16(const U32& a, const U32& b) { + return Inst(Opcode::CRC32Castagnoli16, a, b); + } + + U32 CRC32Castagnoli32(const U32& a, const U32& b) { + return Inst(Opcode::CRC32Castagnoli32, a, b); + } + + U32 CRC32Castagnoli64(const U32& a, const U64& b) { + return Inst(Opcode::CRC32Castagnoli64, a, b); + } + + U32 CRC32ISO8(const U32& a, const U32& b) { + return Inst(Opcode::CRC32ISO8, a, b); + } + + U32 CRC32ISO16(const U32& a, const U32& b) { + return Inst(Opcode::CRC32ISO16, a, b); + } + + U32 CRC32ISO32(const U32& a, const U32& b) { + return Inst(Opcode::CRC32ISO32, a, b); + } + + U32 CRC32ISO64(const U32& a, const U64& b) { + return Inst(Opcode::CRC32ISO64, a, b); + } + + U128 AESDecryptSingleRound(const U128& a) { + return Inst(Opcode::AESDecryptSingleRound, a); + } + + U128 AESEncryptSingleRound(const U128& a) { + return Inst(Opcode::AESEncryptSingleRound, a); + } + + U128 AESInverseMixColumns(const U128& a) { + return Inst(Opcode::AESInverseMixColumns, a); + } + + U128 AESMixColumns(const U128& a) { + return Inst(Opcode::AESMixColumns, a); + } + + U8 SM4AccessSubstitutionBox(const U8& a) { + return Inst(Opcode::SM4AccessSubstitutionBox, a); + } + + U128 SHA256Hash(const U128& x, const U128& y, const U128& w, bool part1) { + return Inst(Opcode::SHA256Hash, x, y, w, Imm1(part1)); + } + + U128 SHA256MessageSchedule0(const U128& x, const U128& y) { + return Inst(Opcode::SHA256MessageSchedule0, x, y); + } + + U128 SHA256MessageSchedule1(const U128& x, const U128& y, const U128& z) { + return Inst(Opcode::SHA256MessageSchedule1, x, y, z); + } + + UAny VectorGetElement(size_t esize, const U128& a, size_t index) { + ASSERT_MSG(esize * index < 128, "Invalid index"); + switch (esize) { + case 8: + return Inst(Opcode::VectorGetElement8, a, Imm8(static_cast(index))); + case 16: + return Inst(Opcode::VectorGetElement16, a, Imm8(static_cast(index))); + case 32: + return Inst(Opcode::VectorGetElement32, a, Imm8(static_cast(index))); + case 64: + return Inst(Opcode::VectorGetElement64, a, Imm8(static_cast(index))); + default: + UNREACHABLE(); + } + } + + U128 VectorSetElement(size_t esize, const U128& a, size_t index, const IR::UAny& elem) { + ASSERT_MSG(esize * index < 128, "Invalid index"); + switch (esize) { + case 8: + return Inst(Opcode::VectorSetElement8, a, Imm8(static_cast(index)), elem); + case 16: + return Inst(Opcode::VectorSetElement16, a, Imm8(static_cast(index)), elem); + case 32: + return Inst(Opcode::VectorSetElement32, a, Imm8(static_cast(index)), elem); + case 64: + return Inst(Opcode::VectorSetElement64, a, Imm8(static_cast(index)), elem); + default: + UNREACHABLE(); + } + } + + U128 VectorAbs(size_t esize, const U128& a) { + switch (esize) { + case 8: + return Inst(Opcode::VectorAbs8, a); + case 16: + return Inst(Opcode::VectorAbs16, a); + case 32: + return Inst(Opcode::VectorAbs32, a); + case 64: + return Inst(Opcode::VectorAbs64, a); + } + UNREACHABLE(); + } + + U128 VectorAdd(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorAdd8, a, b); + case 16: + return Inst(Opcode::VectorAdd16, a, b); + case 32: + return Inst(Opcode::VectorAdd32, a, b); + case 64: + return Inst(Opcode::VectorAdd64, a, b); + } + UNREACHABLE(); + } + + U128 VectorAnd(const U128& a, const U128& b) { + return Inst(Opcode::VectorAnd, a, b); + } + + U128 VectorAndNot(const U128& a, const U128& b) { + return Inst(Opcode::VectorAndNot, a, b); + } + + U128 VectorArithmeticShiftRight(size_t esize, const U128& a, u8 shift_amount) { + switch (esize) { + case 8: + return Inst(Opcode::VectorArithmeticShiftRight8, a, Imm8(shift_amount)); + case 16: + return Inst(Opcode::VectorArithmeticShiftRight16, a, Imm8(shift_amount)); + case 32: + return Inst(Opcode::VectorArithmeticShiftRight32, a, Imm8(shift_amount)); + case 64: + return Inst(Opcode::VectorArithmeticShiftRight64, a, Imm8(shift_amount)); + } + UNREACHABLE(); + } + + U128 VectorArithmeticVShift(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorArithmeticVShift8, a, b); + case 16: + return Inst(Opcode::VectorArithmeticVShift16, a, b); + case 32: + return Inst(Opcode::VectorArithmeticVShift32, a, b); + case 64: + return Inst(Opcode::VectorArithmeticVShift64, a, b); + } + UNREACHABLE(); + } + + U128 VectorBroadcastLower(size_t esize, const UAny& a) { + switch (esize) { + case 8: + return Inst(Opcode::VectorBroadcastLower8, U8(a)); + case 16: + return Inst(Opcode::VectorBroadcastLower16, U16(a)); + case 32: + return Inst(Opcode::VectorBroadcastLower32, U32(a)); + } + UNREACHABLE(); + } + + U128 VectorBroadcast(size_t esize, const UAny& a) { + switch (esize) { + case 8: + return Inst(Opcode::VectorBroadcast8, U8(a)); + case 16: + return Inst(Opcode::VectorBroadcast16, U16(a)); + case 32: + return Inst(Opcode::VectorBroadcast32, U32(a)); + case 64: + return Inst(Opcode::VectorBroadcast64, U64(a)); + } + UNREACHABLE(); + } + + U128 VectorBroadcastElementLower(size_t esize, const U128& a, size_t index) { + ASSERT_MSG(esize * index < 128, "Invalid index"); + switch (esize) { + case 8: + return Inst(Opcode::VectorBroadcastElementLower8, a, u8(index)); + case 16: + return Inst(Opcode::VectorBroadcastElementLower16, a, u8(index)); + case 32: + return Inst(Opcode::VectorBroadcastElementLower32, a, u8(index)); + } + UNREACHABLE(); + } + + U128 VectorBroadcastElement(size_t esize, const U128& a, size_t index) { + ASSERT_MSG(esize * index < 128, "Invalid index"); + switch (esize) { + case 8: + return Inst(Opcode::VectorBroadcastElement8, a, u8(index)); + case 16: + return Inst(Opcode::VectorBroadcastElement16, a, u8(index)); + case 32: + return Inst(Opcode::VectorBroadcastElement32, a, u8(index)); + case 64: + return Inst(Opcode::VectorBroadcastElement64, a, u8(index)); + } + UNREACHABLE(); + } + + U128 VectorCountLeadingZeros(size_t esize, const U128& a) { + switch (esize) { + case 8: + return Inst(Opcode::VectorCountLeadingZeros8, a); + case 16: + return Inst(Opcode::VectorCountLeadingZeros16, a); + case 32: + return Inst(Opcode::VectorCountLeadingZeros32, a); + } + UNREACHABLE(); + } + + U128 VectorDeinterleaveEven(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorDeinterleaveEven8, a, b); + case 16: + return Inst(Opcode::VectorDeinterleaveEven16, a, b); + case 32: + return Inst(Opcode::VectorDeinterleaveEven32, a, b); + case 64: + return Inst(Opcode::VectorDeinterleaveEven64, a, b); + } + UNREACHABLE(); + } + + U128 VectorDeinterleaveOdd(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorDeinterleaveOdd8, a, b); + case 16: + return Inst(Opcode::VectorDeinterleaveOdd16, a, b); + case 32: + return Inst(Opcode::VectorDeinterleaveOdd32, a, b); + case 64: + return Inst(Opcode::VectorDeinterleaveOdd64, a, b); + } + UNREACHABLE(); + } + + U128 VectorDeinterleaveEvenLower(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorDeinterleaveEvenLower8, a, b); + case 16: + return Inst(Opcode::VectorDeinterleaveEvenLower16, a, b); + case 32: + return Inst(Opcode::VectorDeinterleaveEvenLower32, a, b); + } + UNREACHABLE(); + } + + U128 VectorDeinterleaveOddLower(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorDeinterleaveOddLower8, a, b); + case 16: + return Inst(Opcode::VectorDeinterleaveOddLower16, a, b); + case 32: + return Inst(Opcode::VectorDeinterleaveOddLower32, a, b); + } + UNREACHABLE(); + } + + U128 VectorEor(const U128& a, const U128& b) { + return Inst(Opcode::VectorEor, a, b); + } + + U128 VectorEqual(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorEqual8, a, b); + case 16: + return Inst(Opcode::VectorEqual16, a, b); + case 32: + return Inst(Opcode::VectorEqual32, a, b); + case 64: + return Inst(Opcode::VectorEqual64, a, b); + case 128: + return Inst(Opcode::VectorEqual128, a, b); + } + UNREACHABLE(); + } + + U128 VectorExtract(const U128& a, const U128& b, size_t position) { + ASSERT(position <= 128); + return Inst(Opcode::VectorExtract, a, b, Imm8(static_cast(position))); + } + + U128 VectorExtractLower(const U128& a, const U128& b, size_t position) { + ASSERT(position <= 64); + return Inst(Opcode::VectorExtractLower, a, b, Imm8(static_cast(position))); + } + + U128 VectorGreaterSigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorGreaterS8, a, b); + case 16: + return Inst(Opcode::VectorGreaterS16, a, b); + case 32: + return Inst(Opcode::VectorGreaterS32, a, b); + case 64: + return Inst(Opcode::VectorGreaterS64, a, b); + } + UNREACHABLE(); + } + + U128 VectorGreaterEqualSigned(size_t esize, const U128& a, const U128& b) { + return VectorOr(VectorGreaterSigned(esize, a, b), VectorEqual(esize, a, b)); + } + + U128 VectorGreaterEqualUnsigned(size_t esize, const U128& a, const U128& b) { + return VectorEqual(esize, VectorMaxUnsigned(esize, a, b), a); + } + + U128 VectorGreaterUnsigned(size_t esize, const U128& a, const U128& b) { + return VectorNot(VectorEqual(esize, VectorMinUnsigned(esize, a, b), a)); + } + + U128 VectorHalvingAddSigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorHalvingAddS8, a, b); + case 16: + return Inst(Opcode::VectorHalvingAddS16, a, b); + case 32: + return Inst(Opcode::VectorHalvingAddS32, a, b); + } + UNREACHABLE(); + } + + U128 VectorHalvingAddUnsigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorHalvingAddU8, a, b); + case 16: + return Inst(Opcode::VectorHalvingAddU16, a, b); + case 32: + return Inst(Opcode::VectorHalvingAddU32, a, b); + } + UNREACHABLE(); + } + + U128 VectorHalvingSubSigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorHalvingSubS8, a, b); + case 16: + return Inst(Opcode::VectorHalvingSubS16, a, b); + case 32: + return Inst(Opcode::VectorHalvingSubS32, a, b); + } + UNREACHABLE(); + } + + U128 VectorHalvingSubUnsigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorHalvingSubU8, a, b); + case 16: + return Inst(Opcode::VectorHalvingSubU16, a, b); + case 32: + return Inst(Opcode::VectorHalvingSubU32, a, b); + } + UNREACHABLE(); + } + + U128 VectorInterleaveLower(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorInterleaveLower8, a, b); + case 16: + return Inst(Opcode::VectorInterleaveLower16, a, b); + case 32: + return Inst(Opcode::VectorInterleaveLower32, a, b); + case 64: + return Inst(Opcode::VectorInterleaveLower64, a, b); + } + UNREACHABLE(); + } + + U128 VectorInterleaveUpper(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorInterleaveUpper8, a, b); + case 16: + return Inst(Opcode::VectorInterleaveUpper16, a, b); + case 32: + return Inst(Opcode::VectorInterleaveUpper32, a, b); + case 64: + return Inst(Opcode::VectorInterleaveUpper64, a, b); + } + UNREACHABLE(); + } + + U128 VectorLessEqualSigned(size_t esize, const U128& a, const U128& b) { + return VectorNot(VectorGreaterSigned(esize, a, b)); + } + + U128 VectorLessEqualUnsigned(size_t esize, const U128& a, const U128& b) { + return VectorEqual(esize, VectorMinUnsigned(esize, a, b), a); + } + + U128 VectorLessSigned(size_t esize, const U128& a, const U128& b) { + return VectorNot(VectorOr(VectorGreaterSigned(esize, a, b), VectorEqual(esize, a, b))); + } + + U128 VectorLessUnsigned(size_t esize, const U128& a, const U128& b) { + return VectorNot(VectorEqual(esize, VectorMaxUnsigned(esize, a, b), a)); + } + + U128 VectorLogicalShiftLeft(size_t esize, const U128& a, u8 shift_amount) { + switch (esize) { + case 8: + return Inst(Opcode::VectorLogicalShiftLeft8, a, Imm8(shift_amount)); + case 16: + return Inst(Opcode::VectorLogicalShiftLeft16, a, Imm8(shift_amount)); + case 32: + return Inst(Opcode::VectorLogicalShiftLeft32, a, Imm8(shift_amount)); + case 64: + return Inst(Opcode::VectorLogicalShiftLeft64, a, Imm8(shift_amount)); + } + UNREACHABLE(); + } + + U128 VectorLogicalShiftRight(size_t esize, const U128& a, u8 shift_amount) { + switch (esize) { + case 8: + return Inst(Opcode::VectorLogicalShiftRight8, a, Imm8(shift_amount)); + case 16: + return Inst(Opcode::VectorLogicalShiftRight16, a, Imm8(shift_amount)); + case 32: + return Inst(Opcode::VectorLogicalShiftRight32, a, Imm8(shift_amount)); + case 64: + return Inst(Opcode::VectorLogicalShiftRight64, a, Imm8(shift_amount)); + } + UNREACHABLE(); + } + + U128 VectorLogicalVShift(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorLogicalVShift8, a, b); + case 16: + return Inst(Opcode::VectorLogicalVShift16, a, b); + case 32: + return Inst(Opcode::VectorLogicalVShift32, a, b); + case 64: + return Inst(Opcode::VectorLogicalVShift64, a, b); + } + UNREACHABLE(); + } + + U128 VectorMaxSigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorMaxS8, a, b); + case 16: + return Inst(Opcode::VectorMaxS16, a, b); + case 32: + return Inst(Opcode::VectorMaxS32, a, b); + case 64: + return Inst(Opcode::VectorMaxS64, a, b); + } + UNREACHABLE(); + } + + U128 VectorMaxUnsigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorMaxU8, a, b); + case 16: + return Inst(Opcode::VectorMaxU16, a, b); + case 32: + return Inst(Opcode::VectorMaxU32, a, b); + case 64: + return Inst(Opcode::VectorMaxU64, a, b); + } + UNREACHABLE(); + } + + U128 VectorMinSigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorMinS8, a, b); + case 16: + return Inst(Opcode::VectorMinS16, a, b); + case 32: + return Inst(Opcode::VectorMinS32, a, b); + case 64: + return Inst(Opcode::VectorMinS64, a, b); + } + UNREACHABLE(); + } + + U128 VectorMinUnsigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorMinU8, a, b); + case 16: + return Inst(Opcode::VectorMinU16, a, b); + case 32: + return Inst(Opcode::VectorMinU32, a, b); + case 64: + return Inst(Opcode::VectorMinU64, a, b); + } + UNREACHABLE(); + } + + U128 VectorMultiply(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorMultiply8, a, b); + case 16: + return Inst(Opcode::VectorMultiply16, a, b); + case 32: + return Inst(Opcode::VectorMultiply32, a, b); + case 64: + return Inst(Opcode::VectorMultiply64, a, b); + } + UNREACHABLE(); + } + + U128 VectorMultiplySignedWiden(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorMultiplySignedWiden8, a, b); + case 16: + return Inst(Opcode::VectorMultiplySignedWiden16, a, b); + case 32: + return Inst(Opcode::VectorMultiplySignedWiden32, a, b); + } + UNREACHABLE(); + } + + U128 VectorMultiplyUnsignedWiden(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorMultiplyUnsignedWiden8, a, b); + case 16: + return Inst(Opcode::VectorMultiplyUnsignedWiden16, a, b); + case 32: + return Inst(Opcode::VectorMultiplyUnsignedWiden32, a, b); + } + UNREACHABLE(); + } + + U128 VectorNarrow(size_t original_esize, const U128& a) { + switch (original_esize) { + case 16: + return Inst(Opcode::VectorNarrow16, a); + case 32: + return Inst(Opcode::VectorNarrow32, a); + case 64: + return Inst(Opcode::VectorNarrow64, a); + } + UNREACHABLE(); + } + + U128 VectorNot(const U128& a) { + return Inst(Opcode::VectorNot, a); + } + + U128 VectorOr(const U128& a, const U128& b) { + return Inst(Opcode::VectorOr, a, b); + } + + U128 VectorPairedAdd(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorPairedAdd8, a, b); + case 16: + return Inst(Opcode::VectorPairedAdd16, a, b); + case 32: + return Inst(Opcode::VectorPairedAdd32, a, b); + case 64: + return Inst(Opcode::VectorPairedAdd64, a, b); + } + UNREACHABLE(); + } + + U128 VectorPairedAddLower(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorPairedAddLower8, a, b); + case 16: + return Inst(Opcode::VectorPairedAddLower16, a, b); + case 32: + return Inst(Opcode::VectorPairedAddLower32, a, b); + } + UNREACHABLE(); + } + + U128 VectorPairedAddSignedWiden(size_t original_esize, const U128& a) { + switch (original_esize) { + case 8: + return Inst(Opcode::VectorPairedAddSignedWiden8, a); + case 16: + return Inst(Opcode::VectorPairedAddSignedWiden16, a); + case 32: + return Inst(Opcode::VectorPairedAddSignedWiden32, a); + } + UNREACHABLE(); + } + + U128 VectorPairedAddUnsignedWiden(size_t original_esize, const U128& a) { + switch (original_esize) { + case 8: + return Inst(Opcode::VectorPairedAddUnsignedWiden8, a); + case 16: + return Inst(Opcode::VectorPairedAddUnsignedWiden16, a); + case 32: + return Inst(Opcode::VectorPairedAddUnsignedWiden32, a); + } + UNREACHABLE(); + } + + U128 VectorPairedMaxSigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorPairedMaxS8, a, b); + case 16: + return Inst(Opcode::VectorPairedMaxS16, a, b); + case 32: + return Inst(Opcode::VectorPairedMaxS32, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorPairedMaxUnsigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorPairedMaxU8, a, b); + case 16: + return Inst(Opcode::VectorPairedMaxU16, a, b); + case 32: + return Inst(Opcode::VectorPairedMaxU32, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorPairedMinSigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorPairedMinS8, a, b); + case 16: + return Inst(Opcode::VectorPairedMinS16, a, b); + case 32: + return Inst(Opcode::VectorPairedMinS32, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorPairedMinUnsigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorPairedMinU8, a, b); + case 16: + return Inst(Opcode::VectorPairedMinU16, a, b); + case 32: + return Inst(Opcode::VectorPairedMinU32, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorPairedMaxSignedLower(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorPairedMaxLowerS8, a, b); + case 16: + return Inst(Opcode::VectorPairedMaxLowerS16, a, b); + case 32: + return Inst(Opcode::VectorPairedMaxLowerS32, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorPairedMaxUnsignedLower(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorPairedMaxLowerU8, a, b); + case 16: + return Inst(Opcode::VectorPairedMaxLowerU16, a, b); + case 32: + return Inst(Opcode::VectorPairedMaxLowerU32, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorPairedMinSignedLower(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorPairedMinLowerS8, a, b); + case 16: + return Inst(Opcode::VectorPairedMinLowerS16, a, b); + case 32: + return Inst(Opcode::VectorPairedMinLowerS32, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorPairedMinUnsignedLower(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorPairedMinLowerU8, a, b); + case 16: + return Inst(Opcode::VectorPairedMinLowerU16, a, b); + case 32: + return Inst(Opcode::VectorPairedMinLowerU32, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorPolynomialMultiply(const U128& a, const U128& b) { + return Inst(Opcode::VectorPolynomialMultiply8, a, b); + } + + U128 VectorPolynomialMultiplyLong(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorPolynomialMultiplyLong8, a, b); + case 64: + return Inst(Opcode::VectorPolynomialMultiplyLong64, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorPopulationCount(const U128& a) { + return Inst(Opcode::VectorPopulationCount, a); + } + + U128 VectorReverseBits(const U128& a) { + return Inst(Opcode::VectorReverseBits, a); + } + + U128 VectorReverseElementsInHalfGroups(size_t esize, const U128& a) { + switch (esize) { + case 8: + return Inst(Opcode::VectorReverseElementsInHalfGroups8, a); + default: + UNREACHABLE(); + } + } + + U128 VectorReverseElementsInWordGroups(size_t esize, const U128& a) { + switch (esize) { + case 8: + return Inst(Opcode::VectorReverseElementsInWordGroups8, a); + case 16: + return Inst(Opcode::VectorReverseElementsInWordGroups16, a); + default: + UNREACHABLE(); + } + } + + U128 VectorReverseElementsInLongGroups(size_t esize, const U128& a) { + switch (esize) { + case 8: + return Inst(Opcode::VectorReverseElementsInLongGroups8, a); + case 16: + return Inst(Opcode::VectorReverseElementsInLongGroups16, a); + case 32: + return Inst(Opcode::VectorReverseElementsInLongGroups32, a); + default: + UNREACHABLE(); + } + } + + U128 VectorReduceAdd(size_t esize, const U128& a) { + switch (esize) { + case 8: + return Inst(Opcode::VectorReduceAdd8, a); + case 16: + return Inst(Opcode::VectorReduceAdd16, a); + case 32: + return Inst(Opcode::VectorReduceAdd32, a); + case 64: + return Inst(Opcode::VectorReduceAdd64, a); + } + + UNREACHABLE(); + } + + U128 VectorRotateLeft(size_t esize, const U128& a, u8 amount) { + ASSERT(amount < esize); + + if (amount == 0) { + return a; + } + + return VectorOr(VectorLogicalShiftLeft(esize, a, amount), + VectorLogicalShiftRight(esize, a, static_cast(esize - amount))); + } + + U128 VectorRotateRight(size_t esize, const U128& a, u8 amount) { + ASSERT(amount < esize); + + if (amount == 0) { + return a; + } + + return VectorOr(VectorLogicalShiftRight(esize, a, amount), + VectorLogicalShiftLeft(esize, a, static_cast(esize - amount))); + } + + U128 VectorRotateWholeVectorRight(const U128& a, u8 amount) { + ASSERT(amount % 32 == 0); + return Inst(Opcode::VectorRotateWholeVectorRight, a, Imm8(amount)); + } + + U128 VectorRoundingHalvingAddSigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorRoundingHalvingAddS8, a, b); + case 16: + return Inst(Opcode::VectorRoundingHalvingAddS16, a, b); + case 32: + return Inst(Opcode::VectorRoundingHalvingAddS32, a, b); + } + + UNREACHABLE(); + } + + U128 VectorRoundingHalvingAddUnsigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorRoundingHalvingAddU8, a, b); + case 16: + return Inst(Opcode::VectorRoundingHalvingAddU16, a, b); + case 32: + return Inst(Opcode::VectorRoundingHalvingAddU32, a, b); + } + + UNREACHABLE(); + } + + U128 VectorRoundingShiftLeftSigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorRoundingShiftLeftS8, a, b); + case 16: + return Inst(Opcode::VectorRoundingShiftLeftS16, a, b); + case 32: + return Inst(Opcode::VectorRoundingShiftLeftS32, a, b); + case 64: + return Inst(Opcode::VectorRoundingShiftLeftS64, a, b); + } + + UNREACHABLE(); + } + + U128 VectorRoundingShiftLeftUnsigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorRoundingShiftLeftU8, a, b); + case 16: + return Inst(Opcode::VectorRoundingShiftLeftU16, a, b); + case 32: + return Inst(Opcode::VectorRoundingShiftLeftU32, a, b); + case 64: + return Inst(Opcode::VectorRoundingShiftLeftU64, a, b); + } + + UNREACHABLE(); + } + + U128 VectorSignExtend(size_t original_esize, const U128& a) { + switch (original_esize) { + case 8: + return Inst(Opcode::VectorSignExtend8, a); + case 16: + return Inst(Opcode::VectorSignExtend16, a); + case 32: + return Inst(Opcode::VectorSignExtend32, a); + case 64: + return Inst(Opcode::VectorSignExtend64, a); + } + UNREACHABLE(); + } + + U128 VectorSignedAbsoluteDifference(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorSignedAbsoluteDifference8, a, b); + case 16: + return Inst(Opcode::VectorSignedAbsoluteDifference16, a, b); + case 32: + return Inst(Opcode::VectorSignedAbsoluteDifference32, a, b); + } + UNREACHABLE(); + } + + UpperAndLower VectorSignedMultiply(size_t esize, const U128& a, const U128& b) { + const Value multiply = [&] { + switch (esize) { + case 16: + return Inst(Opcode::VectorSignedMultiply16, a, b); + case 32: + return Inst(Opcode::VectorSignedMultiply32, a, b); + } + UNREACHABLE(); + }(); + + return { + Inst(Opcode::GetUpperFromOp, multiply), + Inst(Opcode::GetLowerFromOp, multiply), + }; + } + + U128 VectorSignedSaturatedAbs(size_t esize, const U128& a) { + switch (esize) { + case 8: + return Inst(Opcode::VectorSignedSaturatedAbs8, a); + case 16: + return Inst(Opcode::VectorSignedSaturatedAbs16, a); + case 32: + return Inst(Opcode::VectorSignedSaturatedAbs32, a); + case 64: + return Inst(Opcode::VectorSignedSaturatedAbs64, a); + } + UNREACHABLE(); + } + + U128 VectorSignedSaturatedAccumulateUnsigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorSignedSaturatedAccumulateUnsigned8, a, b); + case 16: + return Inst(Opcode::VectorSignedSaturatedAccumulateUnsigned16, a, b); + case 32: + return Inst(Opcode::VectorSignedSaturatedAccumulateUnsigned32, a, b); + case 64: + return Inst(Opcode::VectorSignedSaturatedAccumulateUnsigned64, a, b); + } + UNREACHABLE(); + } + + U128 VectorSignedSaturatedDoublingMultiplyHigh(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 16: + return Inst(Opcode::VectorSignedSaturatedDoublingMultiplyHigh16, a, b); + case 32: + return Inst(Opcode::VectorSignedSaturatedDoublingMultiplyHigh32, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorSignedSaturatedDoublingMultiplyHighRounding(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 16: + return Inst(Opcode::VectorSignedSaturatedDoublingMultiplyHighRounding16, a, b); + case 32: + return Inst(Opcode::VectorSignedSaturatedDoublingMultiplyHighRounding32, a, b); + default: + UNREACHABLE(); + } + } + + U128 VectorSignedSaturatedDoublingMultiplyLong(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 16: + return Inst(Opcode::VectorSignedSaturatedDoublingMultiplyLong16, a, b); + case 32: + return Inst(Opcode::VectorSignedSaturatedDoublingMultiplyLong32, a, b); + } + UNREACHABLE(); + } + + U128 VectorSignedSaturatedNarrowToSigned(size_t original_esize, const U128& a) { + switch (original_esize) { + case 16: + return Inst(Opcode::VectorSignedSaturatedNarrowToSigned16, a); + case 32: + return Inst(Opcode::VectorSignedSaturatedNarrowToSigned32, a); + case 64: + return Inst(Opcode::VectorSignedSaturatedNarrowToSigned64, a); + } + UNREACHABLE(); + } + + U128 VectorSignedSaturatedNarrowToUnsigned(size_t original_esize, const U128& a) { + switch (original_esize) { + case 16: + return Inst(Opcode::VectorSignedSaturatedNarrowToUnsigned16, a); + case 32: + return Inst(Opcode::VectorSignedSaturatedNarrowToUnsigned32, a); + case 64: + return Inst(Opcode::VectorSignedSaturatedNarrowToUnsigned64, a); + } + UNREACHABLE(); + } + + U128 VectorSignedSaturatedNeg(size_t esize, const U128& a) { + switch (esize) { + case 8: + return Inst(Opcode::VectorSignedSaturatedNeg8, a); + case 16: + return Inst(Opcode::VectorSignedSaturatedNeg16, a); + case 32: + return Inst(Opcode::VectorSignedSaturatedNeg32, a); + case 64: + return Inst(Opcode::VectorSignedSaturatedNeg64, a); + } + UNREACHABLE(); + } + + U128 VectorSignedSaturatedShiftLeft(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorSignedSaturatedShiftLeft8, a, b); + case 16: + return Inst(Opcode::VectorSignedSaturatedShiftLeft16, a, b); + case 32: + return Inst(Opcode::VectorSignedSaturatedShiftLeft32, a, b); + case 64: + return Inst(Opcode::VectorSignedSaturatedShiftLeft64, a, b); + } + UNREACHABLE(); + } + + U128 VectorSignedSaturatedShiftLeftUnsigned(size_t esize, const U128& a, u8 shift_amount) { + ASSERT(shift_amount < esize); + switch (esize) { + case 8: + return Inst(Opcode::VectorSignedSaturatedShiftLeftUnsigned8, a, Imm8(shift_amount)); + case 16: + return Inst(Opcode::VectorSignedSaturatedShiftLeftUnsigned16, a, Imm8(shift_amount)); + case 32: + return Inst(Opcode::VectorSignedSaturatedShiftLeftUnsigned32, a, Imm8(shift_amount)); + case 64: + return Inst(Opcode::VectorSignedSaturatedShiftLeftUnsigned64, a, Imm8(shift_amount)); + } + UNREACHABLE(); + } + + U128 VectorSub(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorSub8, a, b); + case 16: + return Inst(Opcode::VectorSub16, a, b); + case 32: + return Inst(Opcode::VectorSub32, a, b); + case 64: + return Inst(Opcode::VectorSub64, a, b); + } + UNREACHABLE(); + } + + Table VectorTable(std::vector values) { + ASSERT(values.size() >= 1 && values.size() <= 4); + values.resize(4); + return Inst
(Opcode::VectorTable, values[0], values[1], values[2], values[3]); + } + + Table VectorTable(std::vector values) { + ASSERT(values.size() >= 1 && values.size() <= 4); + values.resize(4); + return Inst
(Opcode::VectorTable, values[0], values[1], values[2], values[3]); + } + + U64 VectorTableLookup(const U64& defaults, const Table& table, const U64& indices) { + ASSERT(table.GetInst()->GetArg(0).GetType() == Type::U64); + return Inst(Opcode::VectorTableLookup64, defaults, table, indices); + } + + U128 VectorTableLookup(const U128& defaults, const Table& table, const U128& indices) { + ASSERT(table.GetInst()->GetArg(0).GetType() == Type::U128); + return Inst(Opcode::VectorTableLookup128, defaults, table, indices); + } + + U128 VectorTranspose(size_t esize, const U128& a, const U128& b, bool part) { + switch (esize) { + case 8: + return Inst(Opcode::VectorTranspose8, a, b, Imm1(part)); + case 16: + return Inst(Opcode::VectorTranspose16, a, b, Imm1(part)); + case 32: + return Inst(Opcode::VectorTranspose32, a, b, Imm1(part)); + case 64: + return Inst(Opcode::VectorTranspose64, a, b, Imm1(part)); + } + UNREACHABLE(); + } + + U128 VectorUnsignedAbsoluteDifference(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorUnsignedAbsoluteDifference8, a, b); + case 16: + return Inst(Opcode::VectorUnsignedAbsoluteDifference16, a, b); + case 32: + return Inst(Opcode::VectorUnsignedAbsoluteDifference32, a, b); + } + UNREACHABLE(); + } + + U128 VectorUnsignedRecipEstimate(const U128& a) { + return Inst(Opcode::VectorUnsignedRecipEstimate, a); + } + + U128 VectorUnsignedRecipSqrtEstimate(const U128& a) { + return Inst(Opcode::VectorUnsignedRecipSqrtEstimate, a); + } + + U128 VectorUnsignedSaturatedAccumulateSigned(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorUnsignedSaturatedAccumulateSigned8, a, b); + case 16: + return Inst(Opcode::VectorUnsignedSaturatedAccumulateSigned16, a, b); + case 32: + return Inst(Opcode::VectorUnsignedSaturatedAccumulateSigned32, a, b); + case 64: + return Inst(Opcode::VectorUnsignedSaturatedAccumulateSigned64, a, b); + } + UNREACHABLE(); + } + + U128 VectorUnsignedSaturatedNarrow(size_t esize, const U128& a) { + switch (esize) { + case 16: + return Inst(Opcode::VectorUnsignedSaturatedNarrow16, a); + case 32: + return Inst(Opcode::VectorUnsignedSaturatedNarrow32, a); + case 64: + return Inst(Opcode::VectorUnsignedSaturatedNarrow64, a); + } + UNREACHABLE(); + } + + U128 VectorUnsignedSaturatedShiftLeft(size_t esize, const U128& a, const U128& b) { + switch (esize) { + case 8: + return Inst(Opcode::VectorUnsignedSaturatedShiftLeft8, a, b); + case 16: + return Inst(Opcode::VectorUnsignedSaturatedShiftLeft16, a, b); + case 32: + return Inst(Opcode::VectorUnsignedSaturatedShiftLeft32, a, b); + case 64: + return Inst(Opcode::VectorUnsignedSaturatedShiftLeft64, a, b); + } + UNREACHABLE(); + } + + U128 VectorZeroExtend(size_t original_esize, const U128& a) { + switch (original_esize) { + case 8: + return Inst(Opcode::VectorZeroExtend8, a); + case 16: + return Inst(Opcode::VectorZeroExtend16, a); + case 32: + return Inst(Opcode::VectorZeroExtend32, a); + case 64: + return Inst(Opcode::VectorZeroExtend64, a); + } + UNREACHABLE(); + } + + U128 VectorZeroUpper(const U128& a) { + return Inst(Opcode::VectorZeroUpper, a); + } + + U128 ZeroVector() { + return Inst(Opcode::ZeroVector); + } + + U16U32U64 FPAbs(const U16U32U64& a) { + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPAbs16, a); + case Type::U32: + return Inst(Opcode::FPAbs32, a); + case Type::U64: + return Inst(Opcode::FPAbs64, a); + default: + UNREACHABLE(); + } + } + + U32U64 FPAdd(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U32: + return Inst(Opcode::FPAdd32, a, b); + case Type::U64: + return Inst(Opcode::FPAdd64, a, b); + default: + UNREACHABLE(); + } + } + + NZCV FPCompare(const U32U64& a, const U32U64& b, bool exc_on_qnan) { + ASSERT(a.GetType() == b.GetType()); + + const IR::U1 exc_on_qnan_imm = Imm1(exc_on_qnan); + + switch (a.GetType()) { + case Type::U32: + return Inst(Opcode::FPCompare32, a, b, exc_on_qnan_imm); + case Type::U64: + return Inst(Opcode::FPCompare64, a, b, exc_on_qnan_imm); + default: + UNREACHABLE(); + } + } + + U32U64 FPDiv(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U32: + return Inst(Opcode::FPDiv32, a, b); + case Type::U64: + return Inst(Opcode::FPDiv64, a, b); + default: + UNREACHABLE(); + } + } + + U32U64 FPMax(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U32: + return Inst(Opcode::FPMax32, a, b); + case Type::U64: + return Inst(Opcode::FPMax64, a, b); + default: + UNREACHABLE(); + } + } + + U32U64 FPMaxNumeric(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U32: + return Inst(Opcode::FPMaxNumeric32, a, b); + case Type::U64: + return Inst(Opcode::FPMaxNumeric64, a, b); + default: + UNREACHABLE(); + } + } + + U32U64 FPMin(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U32: + return Inst(Opcode::FPMin32, a, b); + case Type::U64: + return Inst(Opcode::FPMin64, a, b); + default: + UNREACHABLE(); + } + } + + U32U64 FPMinNumeric(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U32: + return Inst(Opcode::FPMinNumeric32, a, b); + case Type::U64: + return Inst(Opcode::FPMinNumeric64, a, b); + default: + UNREACHABLE(); + } + } + + U32U64 FPMul(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U32: + return Inst(Opcode::FPMul32, a, b); + case Type::U64: + return Inst(Opcode::FPMul64, a, b); + default: + UNREACHABLE(); + } + } + + U16U32U64 FPMulAdd(const U16U32U64& a, const U16U32U64& b, const U16U32U64& c) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPMulAdd16, a, b, c); + case Type::U32: + return Inst(Opcode::FPMulAdd32, a, b, c); + case Type::U64: + return Inst(Opcode::FPMulAdd64, a, b, c); + default: + UNREACHABLE(); + } + } + + U16U32U64 FPMulSub(const U16U32U64& a, const U16U32U64& b, const U16U32U64& c) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPMulSub16, a, b, c); + case Type::U32: + return Inst(Opcode::FPMulSub32, a, b, c); + case Type::U64: + return Inst(Opcode::FPMulSub64, a, b, c); + default: + UNREACHABLE(); + } + } + + U32U64 FPMulX(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U32: + return Inst(Opcode::FPMulX32, a, b); + case Type::U64: + return Inst(Opcode::FPMulX64, a, b); + default: + UNREACHABLE(); + } + } + + U16U32U64 FPNeg(const U16U32U64& a) { + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPNeg16, a); + case Type::U32: + return Inst(Opcode::FPNeg32, a); + case Type::U64: + return Inst(Opcode::FPNeg64, a); + default: + UNREACHABLE(); + } + } + + U16U32U64 FPRecipEstimate(const U16U32U64& a) { + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPRecipEstimate16, a); + case Type::U32: + return Inst(Opcode::FPRecipEstimate32, a); + case Type::U64: + return Inst(Opcode::FPRecipEstimate64, a); + default: + UNREACHABLE(); + } + } + + U16U32U64 FPRecipExponent(const U16U32U64& a) { + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPRecipExponent16, a); + case Type::U32: + return Inst(Opcode::FPRecipExponent32, a); + case Type::U64: + return Inst(Opcode::FPRecipExponent64, a); + default: + UNREACHABLE(); + } + } + + U16U32U64 FPRecipStepFused(const U16U32U64& a, const U16U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPRecipStepFused16, a, b); + case Type::U32: + return Inst(Opcode::FPRecipStepFused32, a, b); + case Type::U64: + return Inst(Opcode::FPRecipStepFused64, a, b); + default: + UNREACHABLE(); + } + } + + U16U32U64 FPRoundInt(const U16U32U64& a, FP::RoundingMode rounding, bool exact) { + const u8 rounding_value = static_cast(rounding); + const IR::U1 exact_imm = Imm1(exact); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPRoundInt16, a, rounding_value, exact_imm); + case Type::U32: + return Inst(Opcode::FPRoundInt32, a, rounding_value, exact_imm); + case Type::U64: + return Inst(Opcode::FPRoundInt64, a, rounding_value, exact_imm); + default: + UNREACHABLE(); + } + } + + U16U32U64 FPRSqrtEstimate(const U16U32U64& a) { + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPRSqrtEstimate16, a); + case Type::U32: + return Inst(Opcode::FPRSqrtEstimate32, a); + case Type::U64: + return Inst(Opcode::FPRSqrtEstimate64, a); + default: + UNREACHABLE(); + } + } + + U16U32U64 FPRSqrtStepFused(const U16U32U64& a, const U16U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPRSqrtStepFused16, a, b); + case Type::U32: + return Inst(Opcode::FPRSqrtStepFused32, a, b); + case Type::U64: + return Inst(Opcode::FPRSqrtStepFused64, a, b); + default: + UNREACHABLE(); + } + } + + U32U64 FPSqrt(const U32U64& a) { + switch (a.GetType()) { + case Type::U32: + return Inst(Opcode::FPSqrt32, a); + case Type::U64: + return Inst(Opcode::FPSqrt64, a); + default: + UNREACHABLE(); + } + } + + U32U64 FPSub(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + + switch (a.GetType()) { + case Type::U32: + return Inst(Opcode::FPSub32, a, b); + case Type::U64: + return Inst(Opcode::FPSub64, a, b); + default: + UNREACHABLE(); + } + } + + U16 FPDoubleToHalf(const U64& a, FP::RoundingMode rounding) { + return Inst(Opcode::FPDoubleToHalf, a, Imm8(static_cast(rounding))); + } + + U32 FPDoubleToSingle(const U64& a, FP::RoundingMode rounding) { + return Inst(Opcode::FPDoubleToSingle, a, Imm8(static_cast(rounding))); + } + + U64 FPHalfToDouble(const U16& a, FP::RoundingMode rounding) { + return Inst(Opcode::FPHalfToDouble, a, Imm8(static_cast(rounding))); + } + + U32 FPHalfToSingle(const U16& a, FP::RoundingMode rounding) { + return Inst(Opcode::FPHalfToSingle, a, Imm8(static_cast(rounding))); + } + + U64 FPSingleToDouble(const U32& a, FP::RoundingMode rounding) { + return Inst(Opcode::FPSingleToDouble, a, Imm8(static_cast(rounding))); + } + + U16 FPSingleToHalf(const U32& a, FP::RoundingMode rounding) { + return Inst(Opcode::FPSingleToHalf, a, Imm8(static_cast(rounding))); + } + + U16 FPToFixedS16(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { + ASSERT(fbits <= 16); + + const U8 fbits_imm = Imm8(static_cast(fbits)); + const U8 rounding_imm = Imm8(static_cast(rounding)); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPHalfToFixedS16, a, fbits_imm, rounding_imm); + case Type::U32: + return Inst(Opcode::FPSingleToFixedS16, a, fbits_imm, rounding_imm); + case Type::U64: + return Inst(Opcode::FPDoubleToFixedS16, a, fbits_imm, rounding_imm); + default: + UNREACHABLE(); + } + } + + U32 FPToFixedS32(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { + ASSERT(fbits <= 32); + + const U8 fbits_imm = Imm8(static_cast(fbits)); + const U8 rounding_imm = Imm8(static_cast(rounding)); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPHalfToFixedS32, a, fbits_imm, rounding_imm); + case Type::U32: + return Inst(Opcode::FPSingleToFixedS32, a, fbits_imm, rounding_imm); + case Type::U64: + return Inst(Opcode::FPDoubleToFixedS32, a, fbits_imm, rounding_imm); + default: + UNREACHABLE(); + } + } + + U64 FPToFixedS64(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { + ASSERT(fbits <= 64); + + const U8 fbits_imm = Imm8(static_cast(fbits)); + const U8 rounding_imm = Imm8(static_cast(rounding)); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPHalfToFixedS64, a, fbits_imm, rounding_imm); + case Type::U32: + return Inst(Opcode::FPSingleToFixedS64, a, fbits_imm, rounding_imm); + case Type::U64: + return Inst(Opcode::FPDoubleToFixedS64, a, fbits_imm, rounding_imm); + default: + UNREACHABLE(); + } + } + + U16 FPToFixedU16(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { + ASSERT(fbits <= 16); + + const U8 fbits_imm = Imm8(static_cast(fbits)); + const U8 rounding_imm = Imm8(static_cast(rounding)); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPHalfToFixedU16, a, fbits_imm, rounding_imm); + case Type::U32: + return Inst(Opcode::FPSingleToFixedU16, a, fbits_imm, rounding_imm); + case Type::U64: + return Inst(Opcode::FPDoubleToFixedU16, a, fbits_imm, rounding_imm); + default: + UNREACHABLE(); + } + } + + U32 FPToFixedU32(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { + ASSERT(fbits <= 32); + + const U8 fbits_imm = Imm8(static_cast(fbits)); + const U8 rounding_imm = Imm8(static_cast(rounding)); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPHalfToFixedU32, a, fbits_imm, rounding_imm); + case Type::U32: + return Inst(Opcode::FPSingleToFixedU32, a, fbits_imm, rounding_imm); + case Type::U64: + return Inst(Opcode::FPDoubleToFixedU32, a, fbits_imm, rounding_imm); + default: + UNREACHABLE(); + } + } + + U64 FPToFixedU64(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { + ASSERT(fbits <= 64); + + const U8 fbits_imm = Imm8(static_cast(fbits)); + const U8 rounding_imm = Imm8(static_cast(rounding)); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPHalfToFixedU64, a, fbits_imm, rounding_imm); + case Type::U32: + return Inst(Opcode::FPSingleToFixedU64, a, fbits_imm, rounding_imm); + case Type::U64: + return Inst(Opcode::FPDoubleToFixedU64, a, fbits_imm, rounding_imm); + default: + UNREACHABLE(); + } + } + + U32 FPSignedFixedToSingle(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { + ASSERT(fbits <= (a.GetType() == Type::U16 ? 16 : (a.GetType() == Type::U32 ? 32 : 64))); + + const IR::U8 fbits_imm = Imm8(static_cast(fbits)); + const IR::U8 rounding_imm = Imm8(static_cast(rounding)); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPFixedS16ToSingle, a, fbits_imm, rounding_imm); + case Type::U32: + return Inst(Opcode::FPFixedS32ToSingle, a, fbits_imm, rounding_imm); + case Type::U64: + return Inst(Opcode::FPFixedS64ToSingle, a, fbits_imm, rounding_imm); + default: + UNREACHABLE(); + } + } + + U32 FPUnsignedFixedToSingle(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { + ASSERT(fbits <= (a.GetType() == Type::U16 ? 16 : (a.GetType() == Type::U32 ? 32 : 64))); + + const IR::U8 fbits_imm = Imm8(static_cast(fbits)); + const IR::U8 rounding_imm = Imm8(static_cast(rounding)); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPFixedU16ToSingle, a, fbits_imm, rounding_imm); + case Type::U32: + return Inst(Opcode::FPFixedU32ToSingle, a, fbits_imm, rounding_imm); + case Type::U64: + return Inst(Opcode::FPFixedU64ToSingle, a, fbits_imm, rounding_imm); + default: + UNREACHABLE(); + } + } + + U64 FPSignedFixedToDouble(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { + ASSERT(fbits <= (a.GetType() == Type::U16 ? 16 : (a.GetType() == Type::U32 ? 32 : 64))); + + const IR::U8 fbits_imm = Imm8(static_cast(fbits)); + const IR::U8 rounding_imm = Imm8(static_cast(rounding)); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPFixedS16ToDouble, a, fbits_imm, rounding_imm); + case Type::U32: + return Inst(Opcode::FPFixedS32ToDouble, a, fbits_imm, rounding_imm); + case Type::U64: + return Inst(Opcode::FPFixedS64ToDouble, a, fbits_imm, rounding_imm); + default: + UNREACHABLE(); + } + } + + U64 FPUnsignedFixedToDouble(const U16U32U64& a, size_t fbits, FP::RoundingMode rounding) { + ASSERT(fbits <= (a.GetType() == Type::U16 ? 16 : (a.GetType() == Type::U32 ? 32 : 64))); + + const IR::U8 fbits_imm = Imm8(static_cast(fbits)); + const IR::U8 rounding_imm = Imm8(static_cast(rounding)); + + switch (a.GetType()) { + case Type::U16: + return Inst(Opcode::FPFixedU16ToDouble, a, fbits_imm, rounding_imm); + case Type::U32: + return Inst(Opcode::FPFixedU32ToDouble, a, fbits_imm, rounding_imm); + case Type::U64: + return Inst(Opcode::FPFixedU64ToDouble, a, fbits_imm, rounding_imm); + default: + UNREACHABLE(); + } + } + + U128 FPVectorAbs(size_t esize, const U128& a) { + switch (esize) { + case 16: + return Inst(Opcode::FPVectorAbs16, a); + case 32: + return Inst(Opcode::FPVectorAbs32, a); + case 64: + return Inst(Opcode::FPVectorAbs64, a); + } + UNREACHABLE(); + } + + U128 FPVectorAdd(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorAdd32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorAdd64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorDiv(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorDiv32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorDiv64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorEqual(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 16: + return Inst(Opcode::FPVectorEqual16, a, b, Imm1(fpcr_controlled)); + case 32: + return Inst(Opcode::FPVectorEqual32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorEqual64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorFromHalf(size_t esize, const U128& a, FP::RoundingMode rounding, bool fpcr_controlled = true) { + ASSERT(esize == 32); + return Inst(Opcode::FPVectorFromHalf32, a, Imm8(static_cast(rounding)), Imm1(fpcr_controlled)); + } + + U128 FPVectorFromSignedFixed(size_t esize, const U128& a, size_t fbits, FP::RoundingMode rounding, bool fpcr_controlled = true) { + ASSERT(fbits <= esize); + switch (esize) { + case 32: + return Inst(Opcode::FPVectorFromSignedFixed32, a, Imm8(static_cast(fbits)), Imm8(static_cast(rounding)), Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorFromSignedFixed64, a, Imm8(static_cast(fbits)), Imm8(static_cast(rounding)), Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorFromUnsignedFixed(size_t esize, const U128& a, size_t fbits, FP::RoundingMode rounding, bool fpcr_controlled = true) { + ASSERT(fbits <= esize); + switch (esize) { + case 32: + return Inst(Opcode::FPVectorFromUnsignedFixed32, a, Imm8(static_cast(fbits)), Imm8(static_cast(rounding)), Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorFromUnsignedFixed64, a, Imm8(static_cast(fbits)), Imm8(static_cast(rounding)), Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorGreater(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorGreater32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorGreater64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorGreaterEqual(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorGreaterEqual32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorGreaterEqual64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorMax(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorMax32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorMax64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorMaxNumeric(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorMaxNumeric32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorMaxNumeric64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorMin(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorMin32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorMin64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorMinNumeric(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorMinNumeric32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorMinNumeric64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorMul(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorMul32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorMul64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorMulAdd(size_t esize, const U128& a, const U128& b, const U128& c, bool fpcr_controlled = true) { + switch (esize) { + case 16: + return Inst(Opcode::FPVectorMulAdd16, a, b, c, Imm1(fpcr_controlled)); + case 32: + return Inst(Opcode::FPVectorMulAdd32, a, b, c, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorMulAdd64, a, b, c, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorMulX(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorMulX32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorMulX64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorNeg(size_t esize, const U128& a) { + switch (esize) { + case 16: + return Inst(Opcode::FPVectorNeg16, a); + case 32: + return Inst(Opcode::FPVectorNeg32, a); + case 64: + return Inst(Opcode::FPVectorNeg64, a); + } + UNREACHABLE(); + } + + U128 FPVectorPairedAdd(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorPairedAdd32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorPairedAdd64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorPairedAddLower(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorPairedAddLower32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorPairedAddLower64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorRecipEstimate(size_t esize, const U128& a, bool fpcr_controlled = true) { + switch (esize) { + case 16: + return Inst(Opcode::FPVectorRecipEstimate16, a, Imm1(fpcr_controlled)); + case 32: + return Inst(Opcode::FPVectorRecipEstimate32, a, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorRecipEstimate64, a, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorRecipStepFused(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 16: + return Inst(Opcode::FPVectorRecipStepFused16, a, b, Imm1(fpcr_controlled)); + case 32: + return Inst(Opcode::FPVectorRecipStepFused32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorRecipStepFused64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorRoundInt(size_t esize, const U128& operand, FP::RoundingMode rounding, bool exact, bool fpcr_controlled = true) { + const IR::U8 rounding_imm = Imm8(static_cast(rounding)); + const IR::U1 exact_imm = Imm1(exact); + + switch (esize) { + case 16: + return Inst(Opcode::FPVectorRoundInt16, operand, rounding_imm, exact_imm, Imm1(fpcr_controlled)); + case 32: + return Inst(Opcode::FPVectorRoundInt32, operand, rounding_imm, exact_imm, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorRoundInt64, operand, rounding_imm, exact_imm, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorRSqrtEstimate(size_t esize, const U128& a, bool fpcr_controlled = true) { + switch (esize) { + case 16: + return Inst(Opcode::FPVectorRSqrtEstimate16, a, Imm1(fpcr_controlled)); + case 32: + return Inst(Opcode::FPVectorRSqrtEstimate32, a, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorRSqrtEstimate64, a, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorRSqrtStepFused(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 16: + return Inst(Opcode::FPVectorRSqrtStepFused16, a, b, Imm1(fpcr_controlled)); + case 32: + return Inst(Opcode::FPVectorRSqrtStepFused32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorRSqrtStepFused64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorSqrt(size_t esize, const U128& a, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorSqrt32, a, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorSqrt64, a, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorSub(size_t esize, const U128& a, const U128& b, bool fpcr_controlled = true) { + switch (esize) { + case 32: + return Inst(Opcode::FPVectorSub32, a, b, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorSub64, a, b, Imm1(fpcr_controlled)); + } + UNREACHABLE(); + } + + U128 FPVectorToHalf(size_t esize, const U128& a, FP::RoundingMode rounding, bool fpcr_controlled = true) { + ASSERT(esize == 32); + return Inst(Opcode::FPVectorToHalf32, a, Imm8(static_cast(rounding)), Imm1(fpcr_controlled)); + } + + U128 FPVectorToSignedFixed(size_t esize, const U128& a, size_t fbits, FP::RoundingMode rounding, bool fpcr_controlled = true) { + ASSERT(fbits <= esize); + + const U8 fbits_imm = Imm8(static_cast(fbits)); + const U8 rounding_imm = Imm8(static_cast(rounding)); + + switch (esize) { + case 16: + return Inst(Opcode::FPVectorToSignedFixed16, a, fbits_imm, rounding_imm, Imm1(fpcr_controlled)); + case 32: + return Inst(Opcode::FPVectorToSignedFixed32, a, fbits_imm, rounding_imm, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorToSignedFixed64, a, fbits_imm, rounding_imm, Imm1(fpcr_controlled)); + } + + UNREACHABLE(); + } + + U128 FPVectorToUnsignedFixed(size_t esize, const U128& a, size_t fbits, FP::RoundingMode rounding, bool fpcr_controlled = true) { + ASSERT(fbits <= esize); + + const U8 fbits_imm = Imm8(static_cast(fbits)); + const U8 rounding_imm = Imm8(static_cast(rounding)); + + switch (esize) { + case 16: + return Inst(Opcode::FPVectorToUnsignedFixed16, a, fbits_imm, rounding_imm, Imm1(fpcr_controlled)); + case 32: + return Inst(Opcode::FPVectorToUnsignedFixed32, a, fbits_imm, rounding_imm, Imm1(fpcr_controlled)); + case 64: + return Inst(Opcode::FPVectorToUnsignedFixed64, a, fbits_imm, rounding_imm, Imm1(fpcr_controlled)); + } + + UNREACHABLE(); + } + + void Breakpoint() { + Inst(Opcode::Breakpoint); + } + + void CallHostFunction(void (*fn)(void)) { + Inst(Opcode::CallHostFunction, Imm64(mcl::bit_cast(fn)), Value{}, Value{}, Value{}); + } + + void CallHostFunction(void (*fn)(u64), const U64& arg1) { + Inst(Opcode::CallHostFunction, Imm64(mcl::bit_cast(fn)), arg1, Value{}, Value{}); + } + + void CallHostFunction(void (*fn)(u64, u64), const U64& arg1, const U64& arg2) { + Inst(Opcode::CallHostFunction, Imm64(mcl::bit_cast(fn)), arg1, arg2, Value{}); + } + + void CallHostFunction(void (*fn)(u64, u64, u64), const U64& arg1, const U64& arg2, const U64& arg3) { + Inst(Opcode::CallHostFunction, Imm64(mcl::bit_cast(fn)), arg1, arg2, arg3); + } + + void SetTerm(const Terminal& terminal) { + block.SetTerminal(terminal); + } void SetInsertionPointBefore(IR::Inst* new_insertion_point) { insertion_point = IR::Block::iterator{*new_insertion_point}; From 7b23cd0df4c2ecb13c3b032906950495b187c940 Mon Sep 17 00:00:00 2001 From: lizzie Date: Mon, 14 Jul 2025 00:54:00 +0200 Subject: [PATCH 10/25] [dynarmic] fix userconfig casting warn (#55) Co-authored-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/55 Co-authored-by: lizzie Co-committed-by: lizzie --- .../dynarmic/src/dynarmic/interface/A32/config.h | 9 ++++++--- src/core/arm/dynarmic/arm_dynarmic_32.cpp | 11 +++++++---- src/core/arm/dynarmic/arm_dynarmic_64.cpp | 15 +++++++++------ 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/externals/dynarmic/src/dynarmic/interface/A32/config.h b/externals/dynarmic/src/dynarmic/interface/A32/config.h index 033967dc00..11fe2236a2 100644 --- a/externals/dynarmic/src/dynarmic/interface/A32/config.h +++ b/externals/dynarmic/src/dynarmic/interface/A32/config.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + /* This file is part of the dynarmic project. * Copyright (c) 2016 MerryMage * SPDX-License-Identifier: 0BSD @@ -159,9 +162,6 @@ struct UserConfig { /// Maximum size is limited by the maximum length of a x86_64 / arm64 jump. std::uint32_t code_cache_size = 128 * 1024 * 1024; // bytes - /// Processor ID - std::uint32_t processor_id = 0; - /// Masks out the first N bits in host pointers from the page table. /// The intention behind this is to allow users of Dynarmic to pack attributes in the /// same integer and update the pointer attribute pair atomically. @@ -172,6 +172,9 @@ struct UserConfig { /// There are minor behavioural differences between versions. ArchVersion arch_version = ArchVersion::v8; + /// Processor ID + std::uint8_t processor_id = 0; + /// Determines if we should detect memory accesses via page_table that straddle are /// misaligned. Accesses that straddle page boundaries will fallback to the relevant /// memory callback. diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index d21aa5aacf..afbf178349 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 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 @@ -201,7 +204,7 @@ std::shared_ptr ArmDynarmic32::MakeJit(Common::PageTable* pa } // Multi-process state - config.processor_id = m_core_index; + config.processor_id = std::uint8_t(m_core_index); config.global_monitor = &m_exclusive_monitor.monitor; // Timing @@ -210,9 +213,9 @@ std::shared_ptr ArmDynarmic32::MakeJit(Common::PageTable* pa // Code cache size #ifdef ARCHITECTURE_arm64 - config.code_cache_size = 128_MiB; + config.code_cache_size = std::uint32_t(128_MiB); #else - config.code_cache_size = 512_MiB; + config.code_cache_size = std::uint32_t(512_MiB); #endif // Allow memory fault handling to work @@ -223,7 +226,7 @@ std::shared_ptr ArmDynarmic32::MakeJit(Common::PageTable* pa // null_jit if (!page_table) { // Don't waste too much memory on null_jit - config.code_cache_size = 8_MiB; + config.code_cache_size = std::uint32_t(8_MiB); } // Safe optimizations diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index c251482182..99a80644ad 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -232,7 +235,7 @@ std::shared_ptr ArmDynarmic64::MakeJit(Common::PageTable* pa // Memory if (page_table) { config.page_table = reinterpret_cast(page_table->pointers.data()); - config.page_table_address_space_bits = address_space_bits; + config.page_table_address_space_bits = std::uint32_t(address_space_bits); config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS; config.silently_mirror_page_table = false; config.absolute_offset_page_table = true; @@ -242,7 +245,7 @@ std::shared_ptr ArmDynarmic64::MakeJit(Common::PageTable* pa config.fastmem_pointer = page_table->fastmem_arena ? std::optional{reinterpret_cast(page_table->fastmem_arena)} : std::nullopt; - config.fastmem_address_space_bits = address_space_bits; + config.fastmem_address_space_bits = std::uint32_t(address_space_bits); config.silently_mirror_fastmem = false; config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt; @@ -250,7 +253,7 @@ std::shared_ptr ArmDynarmic64::MakeJit(Common::PageTable* pa } // Multi-process state - config.processor_id = m_core_index; + config.processor_id = std::uint8_t(m_core_index); config.global_monitor = &m_exclusive_monitor.monitor; // System registers @@ -269,9 +272,9 @@ std::shared_ptr ArmDynarmic64::MakeJit(Common::PageTable* pa // Code cache size #ifdef ARCHITECTURE_arm64 - config.code_cache_size = 128_MiB; + config.code_cache_size = std::uint32_t(128_MiB); #else - config.code_cache_size = 512_MiB; + config.code_cache_size = std::uint32_t(512_MiB); #endif // Allow memory fault handling to work @@ -282,7 +285,7 @@ std::shared_ptr ArmDynarmic64::MakeJit(Common::PageTable* pa // null_jit if (!page_table) { // Don't waste too much memory on null_jit - config.code_cache_size = 8_MiB; + config.code_cache_size = std::uint32_t(8_MiB); } // Safe optimizations From f99488fe3e4c0eb783b5fa8aeff42f753c38a0e9 Mon Sep 17 00:00:00 2001 From: crueter Date: Mon, 14 Jul 2025 01:29:57 +0200 Subject: [PATCH 11/25] [desktop] feat: install firmware from ZIP (#52) Closes #12 Adds a menu option to install firmware from a packed ZIP. This PR additionally lays the groundwork to add data import/export via ZIP. In the future, a qt_common subproject should be added to handle common Qt tasks such as this. Furthermore, to decrease dependency complexity, this also introduces CPM, a wrapper around FetchContent. In theory, this should also lay the groundwork for #8 as well. Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/52 --- .ci/patch/0001-quazip-strict.patch | 80 ++++++++++++++++++ .gitignore | 1 - CMakeModules/CPM.cmake | 24 ++++++ src/yuzu/CMakeLists.txt | 21 +++++ src/yuzu/main.cpp | 128 +++++++++++++++++++++++------ src/yuzu/main.h | 3 + src/yuzu/main.ui | 24 ++++-- tools/update-cpm.sh | 3 + 8 files changed, 252 insertions(+), 32 deletions(-) create mode 100644 .ci/patch/0001-quazip-strict.patch create mode 100644 CMakeModules/CPM.cmake create mode 100755 tools/update-cpm.sh diff --git a/.ci/patch/0001-quazip-strict.patch b/.ci/patch/0001-quazip-strict.patch new file mode 100644 index 0000000000..8283497230 --- /dev/null +++ b/.ci/patch/0001-quazip-strict.patch @@ -0,0 +1,80 @@ +diff --git a/quazip/quazipdir.cpp b/quazip/quazipdir.cpp +index d43f1c1..eb24bf1 100644 +--- a/quazip/quazipdir.cpp ++++ b/quazip/quazipdir.cpp +@@ -293,8 +293,8 @@ bool QuaZipDirComparator::operator()(const QuaZipFileInfo64 &info1, + } + + template +-bool QuaZipDirPrivate::entryInfoList(QStringList nameFilters, +- QDir::Filters filter, QDir::SortFlags sort, TFileInfoList &result) const ++bool QuaZipDirPrivate::entryInfoList(QStringList _nameFilters, ++ QDir::Filters _filter, QDir::SortFlags sort, TFileInfoList &result) const + { + QString basePath = simplePath(); + if (!basePath.isEmpty()) +@@ -305,12 +305,12 @@ bool QuaZipDirPrivate::entryInfoList(QStringList nameFilters, + if (!zip->goToFirstFile()) { + return zip->getZipError() == UNZ_OK; + } +- QDir::Filters fltr = filter; ++ QDir::Filters fltr = _filter; + if (fltr == QDir::NoFilter) + fltr = this->filter; + if (fltr == QDir::NoFilter) + fltr = QDir::AllEntries; +- QStringList nmfltr = nameFilters; ++ QStringList nmfltr = _nameFilters; + if (nmfltr.isEmpty()) + nmfltr = this->nameFilters; + QSet dirsFound; +diff --git a/quazip/quazipfile.cpp b/quazip/quazipfile.cpp +index 4a5f2f9..f7865f5 100644 +--- a/quazip/quazipfile.cpp ++++ b/quazip/quazipfile.cpp +@@ -241,14 +241,14 @@ void QuaZipFile::setFileName(const QString& fileName, QuaZip::CaseSensitivity cs + p->caseSensitivity=cs; + } + +-void QuaZipFilePrivate::setZipError(int zipError) const ++void QuaZipFilePrivate::setZipError(int _zipError) const + { + QuaZipFilePrivate *fakeThis = const_cast(this); // non-const +- fakeThis->zipError=zipError; +- if(zipError==UNZ_OK) ++ fakeThis->zipError = _zipError; ++ if(_zipError == UNZ_OK) + q->setErrorString(QString()); + else +- q->setErrorString(QuaZipFile::tr("ZIP/UNZIP API error %1").arg(zipError)); ++ q->setErrorString(QuaZipFile::tr("ZIP/UNZIP API error %1").arg(_zipError)); + } + + bool QuaZipFile::open(OpenMode mode) +diff --git a/quazip/unzip.c b/quazip/unzip.c +index a39365d..ee7b487 100644 +--- a/quazip/unzip.c ++++ b/quazip/unzip.c +@@ -1054,7 +1054,7 @@ local int unz64local_GetCurrentFileInfoInternal (unzFile file, + /* ZIP64 extra fields */ + if (headerId == 0x0001) + { +- uLong uL; ++ uLong _uL; + + if(file_info.uncompressed_size == (ZPOS64_T)0xFFFFFFFFu) + { +@@ -1078,7 +1078,7 @@ local int unz64local_GetCurrentFileInfoInternal (unzFile file, + if(file_info.disk_num_start == 0xFFFFFFFFu) + { + /* Disk Start Number */ +- if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) ++ if (unz64local_getLong(&s->z_filefunc, s->filestream, &_uL) != UNZ_OK) + err=UNZ_ERRNO; + } + +@@ -2151,3 +2151,4 @@ int ZEXPORT unzClearFlags(unzFile file, unsigned flags) + s->flags &= ~flags; + return UNZ_OK; + } ++ diff --git a/.gitignore b/.gitignore index 9aaf549512..4417d3f132 100644 --- a/.gitignore +++ b/.gitignore @@ -52,4 +52,3 @@ Thumbs.db eden-windows-msvc artifacts *.AppImage* -*.patch diff --git a/CMakeModules/CPM.cmake b/CMakeModules/CPM.cmake new file mode 100644 index 0000000000..84748734ce --- /dev/null +++ b/CMakeModules/CPM.cmake @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: MIT +# +# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors + +set(CPM_DOWNLOAD_VERSION 0.42.0) +set(CPM_HASH_SUM "2020b4fc42dba44817983e06342e682ecfc3d2f484a581f11cc5731fbe4dce8a") + +if(CPM_SOURCE_CACHE) + set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +elseif(DEFINED ENV{CPM_SOURCE_CACHE}) + set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +else() + set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +endif() + +# Expand relative path. This is important if the provided path contains a tilde (~) +get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE) + +file(DOWNLOAD + https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake + ${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM} +) + +include(${CPM_DOWNLOAD_LOCATION}) diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 0dc6f562b8..b5125b19a3 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -494,4 +494,25 @@ if (YUZU_ROOM) target_link_libraries(yuzu PRIVATE yuzu-room) endif() +# Extra deps +set(BUILD_SHARED_LIBS OFF) + +include(CPM) +set(CPM_SOURCE_CACHE ${CMAKE_SOURCE_DIR}/.cache/cpm) + +CPMAddPackage( + URI "gh:stachenov/quazip@1.5" + PATCHES + ${CMAKE_SOURCE_DIR}/.ci/patch/0001-quazip-strict.patch +) + +if (NOT MSVC) + target_compile_options(QuaZip PRIVATE + -Wno-error=shadow + -Wno-error=missing-declarations + ) +endif() + +target_link_libraries(yuzu PRIVATE QuaZip::QuaZip) + create_target_directory_groups(yuzu) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 081f08cf52..634c11bdce 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -12,6 +12,8 @@ #include "core/tools/renderdoc.h" #include "frontend_common/firmware_manager.h" +#include + #ifdef __APPLE__ #include // for chdir #endif @@ -1683,7 +1685,8 @@ void GMainWindow::ConnectMenuEvents() { connect_menu(ui->action_Discord, &GMainWindow::OnOpenDiscord); connect_menu(ui->action_Verify_installed_contents, &GMainWindow::OnVerifyInstalledContents); - connect_menu(ui->action_Install_Firmware, &GMainWindow::OnInstallFirmware); + connect_menu(ui->action_Firmware_From_Folder, &GMainWindow::OnInstallFirmware); + connect_menu(ui->action_Firmware_From_ZIP, &GMainWindow::OnInstallFirmwareFromZIP); connect_menu(ui->action_Install_Keys, &GMainWindow::OnInstallDecryptionKeys); connect_menu(ui->action_About, &GMainWindow::OnAbout); } @@ -1714,7 +1717,8 @@ void GMainWindow::UpdateMenuState() { action->setEnabled(emulation_running); } - ui->action_Install_Firmware->setEnabled(!emulation_running); + ui->action_Firmware_From_Folder->setEnabled(!emulation_running); + ui->action_Firmware_From_ZIP->setEnabled(!emulation_running); ui->action_Install_Keys->setEnabled(!emulation_running); for (QAction* action : applet_actions) { @@ -4239,26 +4243,8 @@ void GMainWindow::OnVerifyInstalledContents() { } } -void GMainWindow::OnInstallFirmware() { - // Don't do this while emulation is running, that'd probably be a bad idea. - if (emu_thread != nullptr && emu_thread->IsRunning()) { - return; - } - - // Check for installed keys, error out, suggest restart? - if (!ContentManager::AreKeysPresent()) { - QMessageBox::information( - this, tr("Keys not installed"), - tr("Install decryption keys and restart eden before attempting to install firmware.")); - return; - } - - const QString firmware_source_location = QFileDialog::getExistingDirectory( - this, tr("Select Dumped Firmware Source Location"), {}, QFileDialog::ShowDirsOnly); - if (firmware_source_location.isEmpty()) { - return; - } - +void GMainWindow::InstallFirmware(const QString &location, bool recursive) +{ QProgressDialog progress(tr("Installing Firmware..."), tr("Cancel"), 0, 100, this); progress.setWindowModality(Qt::WindowModal); progress.setMinimumDuration(100); @@ -4272,11 +4258,11 @@ void GMainWindow::OnInstallFirmware() { return progress.wasCanceled(); }; - LOG_INFO(Frontend, "Installing firmware from {}", firmware_source_location.toStdString()); + LOG_INFO(Frontend, "Installing firmware from {}", location.toStdString()); // Check for a reasonable number of .nca files (don't hardcode them, just see if there's some in // there.) - std::filesystem::path firmware_source_path = firmware_source_location.toStdString(); + std::filesystem::path firmware_source_path = location.toStdString(); if (!Common::FS::IsDir(firmware_source_path)) { progress.close(); return; @@ -4294,7 +4280,12 @@ void GMainWindow::OnInstallFirmware() { QtProgressCallback(100, 10); - Common::FS::IterateDirEntries(firmware_source_path, callback, Common::FS::DirEntryFilter::File); + if (recursive) { + Common::FS::IterateDirEntriesRecursively(firmware_source_path, callback, Common::FS::DirEntryFilter::File); + } else { + Common::FS::IterateDirEntries(firmware_source_path, callback, Common::FS::DirEntryFilter::File); + } + if (out.size() <= 0) { progress.close(); QMessageBox::warning(this, tr("Firmware install failed"), @@ -4377,6 +4368,93 @@ void GMainWindow::OnInstallFirmware() { OnCheckFirmware(); } +void GMainWindow::OnInstallFirmware() { + // Don't do this while emulation is running, that'd probably be a bad idea. + if (emu_thread != nullptr && emu_thread->IsRunning()) { + return; + } + + // Check for installed keys, error out, suggest restart? + if (!ContentManager::AreKeysPresent()) { + QMessageBox::information( + this, tr("Keys not installed"), + tr("Install decryption keys and restart Eden before attempting to install firmware.")); + return; + } + + const QString firmware_source_location = QFileDialog::getExistingDirectory( + this, tr("Select Dumped Firmware Source Location"), {}, QFileDialog::ShowDirsOnly); + if (firmware_source_location.isEmpty()) { + return; + } + + InstallFirmware(firmware_source_location); +} + +void GMainWindow::OnInstallFirmwareFromZIP() +{ + // Don't do this while emulation is running, that'd probably be a bad idea. + if (emu_thread != nullptr && emu_thread->IsRunning()) { + return; + } + + // Check for installed keys, error out, suggest restart? + if (!ContentManager::AreKeysPresent()) { + QMessageBox::information( + this, tr("Keys not installed"), + tr("Install decryption keys and restart Eden before attempting to install firmware.")); + return; + } + + const QString firmware_zip_location = QFileDialog::getOpenFileName( + this, tr("Select Dumped Firmware ZIP"), {}, tr("Zipped Archives (*.zip)")); + if (firmware_zip_location.isEmpty()) { + return; + } + + namespace fs = std::filesystem; + fs::path tmp{std::filesystem::temp_directory_path()}; + + if (!std::filesystem::create_directories(tmp / "eden" / "firmware")) { + goto unzipFailed; + } + + { + tmp /= "eden"; + tmp /= "firmware"; + + QString qCacheDir = QString::fromStdString(tmp.string()); + + QFile zip(firmware_zip_location); + + QStringList result = JlCompress::extractDir(&zip, qCacheDir); + if (result.isEmpty()) { + goto unzipFailed; + } + + // In this case, it has to be done recursively, since sometimes people + // will pack it into a subdirectory after dumping + InstallFirmware(qCacheDir, true); + + std::error_code ec; + std::filesystem::remove_all(tmp, ec); + + if (ec) { + QMessageBox::warning(this, tr("Firmware cleanup failed"), + tr("Failed to clean up extracted firmware cache.\n" + "Check write permissions in the system temp directory and try again.\nOS reported error: %1") + .arg(ec.message())); + } + + return; + } +unzipFailed: + QMessageBox::critical(this, tr("Firmware unzip failed"), + tr("Check write permissions in the system temp directory and try again.")); + return; + +} + void GMainWindow::OnInstallDecryptionKeys() { // Don't do this while emulation is running. if (emu_thread != nullptr && emu_thread->IsRunning()) { diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 1f2582098a..7e7c00ec0b 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -394,6 +394,7 @@ private slots: void OnOpenLogFolder(); void OnVerifyInstalledContents(); void OnInstallFirmware(); + void OnInstallFirmwareFromZIP(); void OnInstallDecryptionKeys(); void OnAbout(); void OnToggleFilterBar(); @@ -614,6 +615,8 @@ private: std::string arguments, const bool needs_title); + void InstallFirmware(const QString& location, bool recursive = false); + protected: void dropEvent(QDropEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override; diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index e35b7afa5a..970f8d2901 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -182,8 +182,15 @@ + + + Install Firmware + + + + - + @@ -484,11 +491,6 @@ Open &Controller Menu - - - Install Firmware - - Install Decryption Keys @@ -545,6 +547,16 @@ &Log Folder + + + + + From Folder + + + + + From ZIP diff --git a/tools/update-cpm.sh b/tools/update-cpm.sh new file mode 100755 index 0000000000..30e400209d --- /dev/null +++ b/tools/update-cpm.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +wget -O CMakeModules/CPM.cmake https://github.com/cpm-cmake/CPM.cmake/releases/latest/download/get_cpm.cmake From 492903cc7a6c21fb83bfd61e13ea71c785376d4c Mon Sep 17 00:00:00 2001 From: Aleksandr Popovich Date: Sun, 13 Jul 2025 20:00:38 -0400 Subject: [PATCH 12/25] [cmake] force quazip to use qt 6 Signed-off-by: Aleksandr Popovich --- src/yuzu/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index b5125b19a3..88f25aa42e 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -500,6 +500,7 @@ set(BUILD_SHARED_LIBS OFF) include(CPM) set(CPM_SOURCE_CACHE ${CMAKE_SOURCE_DIR}/.cache/cpm) +set(QUAZIP_QT_MAJOR_VERSION 6) CPMAddPackage( URI "gh:stachenov/quazip@1.5" PATCHES From 2be7df287a65d9185dbfc2f3b68cf09775efd874 Mon Sep 17 00:00:00 2001 From: Aleksandr Popovich Date: Mon, 14 Jul 2025 02:18:33 +0200 Subject: [PATCH 13/25] [android] Fix crash caused by unreferenced driver (#58) Previously, if the user selected a per-game driver and that driver was deleted from the global menu, it would cause a crash, it was because of a mismatch between FileNotFoundException and NoSuchFileException. To avoid the inconsistency I just made the check for if a file exists or not to be separate. Signed-off-by: Aleksandr Popovich Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/58 Co-authored-by: Aleksandr Popovich Co-committed-by: Aleksandr Popovich --- .../main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt index 81943e9235..99f7fd81fe 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -177,6 +180,10 @@ object GpuDriverHelper { * @return A non-null [GpuDriverMetadata] instance that may have null members */ fun getMetadataFromZip(driver: File): GpuDriverMetadata { + if (!driver.exists()) { + return GpuDriverMetadata() + } + try { ZipFile(driver).use { zf -> val entries = zf.entries() From be59b4f15f3b59ccbf4b8ad0e40e8cb16de3bcc1 Mon Sep 17 00:00:00 2001 From: crueter Date: Mon, 14 Jul 2025 04:48:39 +0200 Subject: [PATCH 14/25] [cmake] Patch QuaZip for windows fix (#60) Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/60 --- .ci/patch/0002-quazip-fetchcontent.patch | 13 +++++++++++++ src/yuzu/CMakeLists.txt | 1 + 2 files changed, 14 insertions(+) create mode 100644 .ci/patch/0002-quazip-fetchcontent.patch diff --git a/.ci/patch/0002-quazip-fetchcontent.patch b/.ci/patch/0002-quazip-fetchcontent.patch new file mode 100644 index 0000000000..3554b7dbb6 --- /dev/null +++ b/.ci/patch/0002-quazip-fetchcontent.patch @@ -0,0 +1,13 @@ +diff --git a/cmake/clone-repo.cmake b/cmake/clone-repo.cmake +index 2ffb4b2..77974dc 100644 +--- a/cmake/clone-repo.cmake ++++ b/cmake/clone-repo.cmake +@@ -26,7 +26,7 @@ macro(clone_repo name url) + FetchContent_GetProperties(${name} POPULATED ${name_lower}_POPULATED) + + if(NOT ${name_lower}_POPULATED) +- FetchContent_Populate(${name}) ++ FetchContent_MakeAvailable(${name}) + endif() + + set(${name_upper}_SOURCE_DIR ${${name_lower}_SOURCE_DIR}) diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 88f25aa42e..cf96b6e876 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -505,6 +505,7 @@ CPMAddPackage( URI "gh:stachenov/quazip@1.5" PATCHES ${CMAKE_SOURCE_DIR}/.ci/patch/0001-quazip-strict.patch + ${CMAKE_SOURCE_DIR}/.ci/patch/0002-quazip-fetchcontent.patch ) if (NOT MSVC) From 2e092010e62d2810efc3dbe7b4d4b3d3cf873cdc Mon Sep 17 00:00:00 2001 From: crueter Date: Mon, 14 Jul 2025 06:10:25 +0200 Subject: [PATCH 15/25] [cmake] Disable bzip2 requirement for quazip (#63) caused windows builds to fail Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/63 Co-authored-by: crueter Co-committed-by: crueter --- src/yuzu/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index cf96b6e876..09738b9e03 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -499,8 +499,11 @@ set(BUILD_SHARED_LIBS OFF) include(CPM) set(CPM_SOURCE_CACHE ${CMAKE_SOURCE_DIR}/.cache/cpm) +set(CPM_USE_LOCAL_PACKAGES ON) set(QUAZIP_QT_MAJOR_VERSION 6) +set(QUAZIP_BZIP2 OFF) + CPMAddPackage( URI "gh:stachenov/quazip@1.5" PATCHES From a8564a09b7b2aac98a0319d40f8e5bc6984f8f1d Mon Sep 17 00:00:00 2001 From: SDK-Chan Date: Mon, 14 Jul 2025 22:30:07 +0200 Subject: [PATCH 16/25] [host1x] FreeBSD: Fix random crashes due to CUDA/VAAPI check sideeffects (#64) FreeBSD doesn't support NVDEC, CUDA, and partially supports VAAPI (mostly for firefox). Implementing VAAPI for other use cases would be a little bit complicated so, I chose to switch it off for FreeBSD. This PR ensures that FFmpeg will always default to software decoding on FreeBSD, but should remain the same functionalities for other OS's. The results are slight CPU increases while decoding in software mode, but still neglectable and they don't really harm performance. Co-authored-by: MaranBr Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/64 Co-authored-by: SDK-Chan Co-committed-by: SDK-Chan --- src/video_core/host1x/ffmpeg/ffmpeg.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.cpp b/src/video_core/host1x/ffmpeg/ffmpeg.cpp index 9b718f2591..5321c8c98c 100644 --- a/src/video_core/host1x/ffmpeg/ffmpeg.cpp +++ b/src/video_core/host1x/ffmpeg/ffmpeg.cpp @@ -26,13 +26,14 @@ namespace { constexpr AVPixelFormat PreferredGpuFormat = AV_PIX_FMT_NV12; constexpr AVPixelFormat PreferredCpuFormat = AV_PIX_FMT_YUV420P; constexpr std::array PreferredGpuDecoders = { - AV_HWDEVICE_TYPE_CUDA, #ifdef _WIN32 + AV_HWDEVICE_TYPE_CUDA, AV_HWDEVICE_TYPE_D3D11VA, AV_HWDEVICE_TYPE_DXVA2, +#elif defined(__FreeBSD__) + AV_HWDEVICE_TYPE_VDPAU, #elif defined(__unix__) AV_HWDEVICE_TYPE_VAAPI, - AV_HWDEVICE_TYPE_VDPAU, #endif AV_HWDEVICE_TYPE_VULKAN, }; From e9ca3f4c069b4ed4386d7086bc676c83e4334adc Mon Sep 17 00:00:00 2001 From: MaranBr Date: Mon, 14 Jul 2025 22:30:54 +0200 Subject: [PATCH 17/25] [host1x] Fix FFmpeg crash on Linux (#37) This fixes the FFmpeg crash on Linux / Steam Deck. Credit to Maufeat for AVERROR_EOF check. Co-authored-by: MaranBr Co-authored-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/37 Co-authored-by: MaranBr Co-committed-by: MaranBr --- .ci/linux/build.sh | 13 +++++++++---- .ci/windows/build.sh | 6 +++++- src/video_core/host1x/ffmpeg/ffmpeg.cpp | 6 ++---- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/.ci/linux/build.sh b/.ci/linux/build.sh index aa15333ac2..093b30a7ea 100755 --- a/.ci/linux/build.sh +++ b/.ci/linux/build.sh @@ -36,15 +36,16 @@ case "$1" in ARCH=armv9 ARCH_FLAGS="-march=armv9-a -mtune=generic -w" ;; + *) + echo "Invalid target $1 specified, must be one of amd64, steamdeck, allyx, rog-ally, legacy, aarch64, armv9" + exit 1 + ;; esac export ARCH_FLAGS="$ARCH_FLAGS -O3" -NPROC="$2" if [ -z "$NPROC" ]; then NPROC="$(nproc)" -else - shift fi if [ "$1" != "" ]; then shift; fi @@ -72,11 +73,15 @@ else MULTIMEDIA=ON fi +if [ -z "$BUILD_TYPE" ]; then + export BUILD_TYPE="Release" +fi + export EXTRA_CMAKE_FLAGS=("${EXTRA_CMAKE_FLAGS[@]}" $@) mkdir -p build && cd build cmake .. -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ -DENABLE_QT_TRANSLATION=ON \ -DUSE_DISCORD_PRESENCE=ON \ -DCMAKE_CXX_FLAGS="$ARCH_FLAGS" \ diff --git a/.ci/windows/build.sh b/.ci/windows/build.sh index 667fd316fa..d0c697655a 100644 --- a/.ci/windows/build.sh +++ b/.ci/windows/build.sh @@ -17,6 +17,10 @@ else export EXTRA_CMAKE_FLAGS=("${EXTRA_CMAKE_FLAGS[@]}" -DYUZU_USE_BUNDLED_QT=OFF) fi +if [ -z "$BUILD_TYPE" ]; then + export BUILD_TYPE="Release" +fi + if [ "$WINDEPLOYQT" == "" ]; then echo "You must supply the WINDEPLOYQT environment variable." exit 1 @@ -38,7 +42,7 @@ export EXTRA_CMAKE_FLAGS=("${EXTRA_CMAKE_FLAGS[@]}" $@) mkdir -p build && cd build cmake .. -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ -DENABLE_QT_TRANSLATION=ON \ -DUSE_DISCORD_PRESENCE=ON \ -DYUZU_USE_BUNDLED_SDL2=OFF \ diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.cpp b/src/video_core/host1x/ffmpeg/ffmpeg.cpp index 5321c8c98c..d6eff2bdd7 100644 --- a/src/video_core/host1x/ffmpeg/ffmpeg.cpp +++ b/src/video_core/host1x/ffmpeg/ffmpeg.cpp @@ -216,18 +216,16 @@ bool DecoderContext::OpenContext(const Decoder& decoder) { bool DecoderContext::SendPacket(const Packet& packet) { m_temp_frame = std::make_shared(); - - if (const int ret = avcodec_send_packet(m_codec_context, packet.GetPacket()); ret < 0) { + if (const int ret = avcodec_send_packet(m_codec_context, packet.GetPacket()); ret < 0 && ret != AVERROR_EOF) { LOG_ERROR(HW_GPU, "avcodec_send_packet error: {}", AVError(ret)); return false; } - return true; } std::shared_ptr DecoderContext::ReceiveFrame() { auto ReceiveImpl = [&](AVFrame* frame) -> bool { - if (const int ret = avcodec_receive_frame(m_codec_context, frame); ret < 0) { + if (const int ret = avcodec_receive_frame(m_codec_context, frame); ret < 0 && ret != AVERROR_EOF) { LOG_ERROR(HW_GPU, "avcodec_receive_frame error: {}", AVError(ret)); return false; } From d7574b2878d07dd2def968093c4596b85a961f99 Mon Sep 17 00:00:00 2001 From: crueter Date: Mon, 14 Jul 2025 20:51:20 -0400 Subject: [PATCH 18/25] [android] Update app icon background thx antabaka Signed-off-by: crueter --- externals/sirit/CMakeLists.txt | 2 +- .../app/src/main/res/drawable/ic_icon_bg.png | Bin 0 -> 25025 bytes .../app/src/main/res/drawable/ic_icon_bg.xml | 751 ------------------ .../src/main/res/drawable/ic_icon_bg_orig.png | Bin 0 -> 199682 bytes 4 files changed, 1 insertion(+), 752 deletions(-) create mode 100644 src/android/app/src/main/res/drawable/ic_icon_bg.png delete mode 100644 src/android/app/src/main/res/drawable/ic_icon_bg.xml create mode 100644 src/android/app/src/main/res/drawable/ic_icon_bg_orig.png diff --git a/externals/sirit/CMakeLists.txt b/externals/sirit/CMakeLists.txt index d98a8b5ba5..782ce8f660 100644 --- a/externals/sirit/CMakeLists.txt +++ b/externals/sirit/CMakeLists.txt @@ -1,6 +1,6 @@ # This file has been adapted from dynarmic -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.12) project(sirit CXX) # Determine if we're built as a subproject (using add_subdirectory) diff --git a/src/android/app/src/main/res/drawable/ic_icon_bg.png b/src/android/app/src/main/res/drawable/ic_icon_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..30b29a32d89a07ff887e556e9db024112dd3f718 GIT binary patch literal 25025 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/android/app/src/main/res/drawable/ic_icon_bg_orig.png b/src/android/app/src/main/res/drawable/ic_icon_bg_orig.png new file mode 100644 index 0000000000000000000000000000000000000000..9ebc6ce17c6517140a3e4e672e56ba96a30fa715 GIT binary patch literal 199682 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ȵ+YKgZEvo$;Z1J!su)VDJsXypc@DqQn#VYJ6O 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Ð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%0AxX2da7!o-fCk%q3j=`lFxD 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 literal 0 HcmV?d00001 From a538126eb7a0ce1e3457922a49ecc2406b8d30b1 Mon Sep 17 00:00:00 2001 From: crueter Date: Tue, 15 Jul 2025 22:24:40 +0200 Subject: [PATCH 19/25] [cmake, desktop] Fix <6.9 build error and quazip fetching (#67) Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/67 --- CMakeLists.txt | 2 +- src/yuzu/CMakeLists.txt | 24 +----------------------- src/yuzu/externals/CMakeLists.txt | 31 +++++++++++++++++++++++++++++++ src/yuzu/main.cpp | 2 +- 4 files changed, 34 insertions(+), 25 deletions(-) create mode 100644 src/yuzu/externals/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b8f60089d..f8e8516dbc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,7 +60,7 @@ endif() if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") option(YUZU_USE_EXTERNAL_VULKAN_HEADERS "Use Vulkan-Headers from externals" OFF) else() - option(YUZU_USE_EXTERNAL_VULKAN_HEADERS "Use Vulkan-Headers from externals" ON) + option(YUZU_USE_EXTERNAL_VULKAN_HEADERS "Use Vulkan-Headers from externals" ON) endif() if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 09738b9e03..bab6a6c4f5 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -495,29 +495,7 @@ if (YUZU_ROOM) endif() # Extra deps -set(BUILD_SHARED_LIBS OFF) - -include(CPM) -set(CPM_SOURCE_CACHE ${CMAKE_SOURCE_DIR}/.cache/cpm) -set(CPM_USE_LOCAL_PACKAGES ON) - -set(QUAZIP_QT_MAJOR_VERSION 6) -set(QUAZIP_BZIP2 OFF) - -CPMAddPackage( - URI "gh:stachenov/quazip@1.5" - PATCHES - ${CMAKE_SOURCE_DIR}/.ci/patch/0001-quazip-strict.patch - ${CMAKE_SOURCE_DIR}/.ci/patch/0002-quazip-fetchcontent.patch -) - -if (NOT MSVC) - target_compile_options(QuaZip PRIVATE - -Wno-error=shadow - -Wno-error=missing-declarations - ) -endif() - +add_subdirectory(externals) target_link_libraries(yuzu PRIVATE QuaZip::QuaZip) create_target_directory_groups(yuzu) diff --git a/src/yuzu/externals/CMakeLists.txt b/src/yuzu/externals/CMakeLists.txt new file mode 100644 index 0000000000..ac17308e09 --- /dev/null +++ b/src/yuzu/externals/CMakeLists.txt @@ -0,0 +1,31 @@ +# Disable tests in all externals supporting the standard option name +set(BUILD_TESTING OFF) + +# Build only static externals +set(BUILD_SHARED_LIBS OFF) + +# QuaZip +set(QUAZIP_QT_MAJOR_VERSION 6) +set(QUAZIP_BZIP2 OFF) + +include(CPM) +set(CPM_SOURCE_CACHE ${CMAKE_SOURCE_DIR}/.cache/cpm) +set(CPM_USE_LOCAL_PACKAGES ON) + +CPMAddPackage( + NAME QuaZip-Qt6 + VERSION 1.3 + GIT_REPOSITORY "https://github.com/stachenov/quazip.git" + GIT_TAG v1.5 + PATCHES + ${CMAKE_SOURCE_DIR}/.ci/patch/0001-quazip-strict.patch + ${CMAKE_SOURCE_DIR}/.ci/patch/0002-quazip-fetchcontent.patch +) + +if (NOT MSVC AND NOT "QuaZip-Qt6" IN_LIST CPM_PACKAGES) + message(STATUS "QUAZIP DIR: ${CPM_PACKAGES}") + target_compile_options(QuaZip PRIVATE + -Wno-error=shadow + -Wno-error=missing-declarations + ) +endif() diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 634c11bdce..6a575cfa87 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -4443,7 +4443,7 @@ void GMainWindow::OnInstallFirmwareFromZIP() QMessageBox::warning(this, tr("Firmware cleanup failed"), tr("Failed to clean up extracted firmware cache.\n" "Check write permissions in the system temp directory and try again.\nOS reported error: %1") - .arg(ec.message())); + .arg(QString::fromStdString(ec.message()))); } return; From 108daeeb397fa23ab02ccef93460bd6bc68e4ba9 Mon Sep 17 00:00:00 2001 From: crueter Date: Wed, 16 Jul 2025 23:17:34 +0200 Subject: [PATCH 20/25] [cmake] Fix QuaZip once and for all (#71) (and core5compat) Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/71 --- .ci/patch/0002-quazip-fetchcontent.patch | 13 - .../quazip/0001-strict.patch | 0 .patch/quazip/0002-oldstyle.patch | 26 ++ .patch/quazip/0003-predecls.patch | 19 + .patch/quazip/0004-qt6-only.patch | 400 ++++++++++++++++++ CMakeLists.txt | 12 +- CMakeModules/CopyYuzuQt6Deps.cmake | 1 + CMakeModules/DownloadExternals.cmake | 20 +- src/yuzu/CMakeLists.txt | 6 +- src/yuzu/externals/CMakeLists.txt | 23 +- 10 files changed, 482 insertions(+), 38 deletions(-) delete mode 100644 .ci/patch/0002-quazip-fetchcontent.patch rename .ci/patch/0001-quazip-strict.patch => .patch/quazip/0001-strict.patch (100%) create mode 100644 .patch/quazip/0002-oldstyle.patch create mode 100644 .patch/quazip/0003-predecls.patch create mode 100644 .patch/quazip/0004-qt6-only.patch diff --git a/.ci/patch/0002-quazip-fetchcontent.patch b/.ci/patch/0002-quazip-fetchcontent.patch deleted file mode 100644 index 3554b7dbb6..0000000000 --- a/.ci/patch/0002-quazip-fetchcontent.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/cmake/clone-repo.cmake b/cmake/clone-repo.cmake -index 2ffb4b2..77974dc 100644 ---- a/cmake/clone-repo.cmake -+++ b/cmake/clone-repo.cmake -@@ -26,7 +26,7 @@ macro(clone_repo name url) - FetchContent_GetProperties(${name} POPULATED ${name_lower}_POPULATED) - - if(NOT ${name_lower}_POPULATED) -- FetchContent_Populate(${name}) -+ FetchContent_MakeAvailable(${name}) - endif() - - set(${name_upper}_SOURCE_DIR ${${name_lower}_SOURCE_DIR}) diff --git a/.ci/patch/0001-quazip-strict.patch b/.patch/quazip/0001-strict.patch similarity index 100% rename from .ci/patch/0001-quazip-strict.patch rename to .patch/quazip/0001-strict.patch diff --git a/.patch/quazip/0002-oldstyle.patch b/.patch/quazip/0002-oldstyle.patch new file mode 100644 index 0000000000..2694128f04 --- /dev/null +++ b/.patch/quazip/0002-oldstyle.patch @@ -0,0 +1,26 @@ +diff --git a/quazip/minizip_crypt.h b/quazip/minizip_crypt.h +index 2e833f7..ea9d277 100644 +--- a/quazip/minizip_crypt.h ++++ b/quazip/minizip_crypt.h +@@ -90,13 +90,14 @@ static void init_keys(const char* passwd,unsigned long* pkeys,const z_crc_t FAR + # define ZCR_SEED2 3141592654UL /* use PI as default pattern */ + # endif + +-static int crypthead(passwd, buf, bufSize, pkeys, pcrc_32_tab, crcForCrypting) +- const char *passwd; /* password string */ +- unsigned char *buf; /* where to write header */ +- int bufSize; +- unsigned long* pkeys; +- const z_crc_t FAR * pcrc_32_tab; +- unsigned long crcForCrypting; ++static int crypthead( ++ const char *passwd, /* password string */ ++ unsigned char *buf, /* where to write header */ ++ int bufSize, ++ unsigned long* pkeys, ++ const z_crc_t FAR * pcrc_32_tab, ++ unsigned long crcForCrypting ++) + { + int n; /* index in random header */ + int t; /* temporary */ diff --git a/.patch/quazip/0003-predecls.patch b/.patch/quazip/0003-predecls.patch new file mode 100644 index 0000000000..ec3414c82a --- /dev/null +++ b/.patch/quazip/0003-predecls.patch @@ -0,0 +1,19 @@ +diff --git a/quazip/zip.c b/quazip/zip.c +index 7788b88..f4e21aa 100644 +--- a/quazip/zip.c ++++ b/quazip/zip.c +@@ -645,6 +645,14 @@ local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib + return relativeOffset; + } + ++// compilers hate this ONE SIMPLE TRICK! ++static int LoadCentralDirectoryRecord(zip64_internal* pziinit); ++static int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local, uLong version_to_extract); ++static int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip); ++static int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip); ++static int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip); ++static int Write_GlobalComment(zip64_internal* zi, const char* global_comment); ++ + int LoadCentralDirectoryRecord(zip64_internal* pziinit) + { + int err=ZIP_OK; diff --git a/.patch/quazip/0004-qt6-only.patch b/.patch/quazip/0004-qt6-only.patch new file mode 100644 index 0000000000..8906df2472 --- /dev/null +++ b/.patch/quazip/0004-qt6-only.patch @@ -0,0 +1,400 @@ +"Debloats" QuaZip by removing some unneeded stuff (Qt <6, bzip2, emscripten...) + +This is completely optional. + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index b376fb2..4aac4ec 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -3,64 +3,16 @@ cmake_minimum_required(VERSION 3.15...3.18) + + project(QuaZip VERSION 1.5) + +-include(cmake/clone-repo.cmake) +- + set(QUAZIP_LIB_VERSION ${QuaZip_VERSION}) + set(QUAZIP_LIB_SOVERSION 1.5.0) + +-if(EMSCRIPTEN) +- #option(ZLIB_INCLUDE "Path to include dir" "") +- #option(ZLIB_LIBRARY "Path to library dir" "") +- option(BUILD_SHARED_LIBS "" OFF) +- option(QUAZIP_INSTALL "" OFF) +- option(QUAZIP_USE_QT_ZLIB "" OFF) +- option(QUAZIP_ENABLE_TESTS "Build QuaZip tests" OFF) +-else() +- option(BUILD_SHARED_LIBS "" ON) +- option(QUAZIP_INSTALL "" ON) +- option(QUAZIP_USE_QT_ZLIB "" OFF) +- option(QUAZIP_ENABLE_TESTS "Build QuaZip tests" OFF) +-endif() ++option(BUILD_SHARED_LIBS "" ON) ++option(QUAZIP_INSTALL "" ON) ++option(QUAZIP_ENABLE_TESTS "Build QuaZip tests" OFF) + + OPTION(ZLIB_CONST "Sets ZLIB_CONST preprocessor definition" OFF) + +-# Make BZIP2 optional +-option(QUAZIP_BZIP2 "Enables BZIP2 compression" ON) +-option(QUAZIP_BZIP2_STDIO "Output BZIP2 errors to stdio" ON) +- +-option(QUAZIP_FETCH_LIBS "Enables fetching third-party libraries if not found" ${WIN32}) +-option(QUAZIP_FORCE_FETCH_LIBS "Enables fetching third-party libraries always" OFF) +- +-if (QUAZIP_USE_QT_ZLIB AND BUILD_SHARED_LIBS) +- message(FATAL_ERROR "Using BUILD_SHARED_LIBS=ON together with QUAZIP_USE_QT_ZLIB=ON is not supported." ) +-endif() +- +-# Set the default value of `${QUAZIP_QT_MAJOR_VERSION}`. +-# We search quietly for Qt6, Qt5 and Qt4 in that order. +-# Qt6 and Qt5 provide config files for CMake. +-# Qt4 relies on `FindQt4.cmake`. +-find_package( +- QT NAMES Qt6 Qt5 +- QUIET COMPONENTS Core +-) +-if (NOT QT_FOUND) +- find_package(Qt4 QUIET COMPONENTS QtCore) +- if (Qt4_FOUND) +- set(QT_VERSION_MAJOR 4) +- else() +- # If neither 6, 5 nor 4 are found, we default to 5. +- # The setup will fail further down. +- set(QT_VERSION_MAJOR 5) +- endif() +-endif() +- +-set(QUAZIP_QT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (4, 5 or 6), defaults to ${QT_VERSION_MAJOR}") +- +-if (QUAZIP_QT_MAJOR_VERSION EQUAL 6) +- set(CMAKE_CXX_STANDARD 17) +-else() +- set(CMAKE_CXX_STANDARD 14) +-endif() ++set(CMAKE_CXX_STANDARD 17) + + if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE RELEASE) +@@ -77,92 +29,17 @@ set(QUAZIP_LIB_TARGET_NAME QuaZip) + set(QUAZIP_DIR_NAME QuaZip-Qt${QUAZIP_QT_MAJOR_VERSION}-${QUAZIP_LIB_VERSION}) + set(QUAZIP_PACKAGE_NAME QuaZip-Qt${QUAZIP_QT_MAJOR_VERSION}) + +-message(STATUS "QUAZIP_QT_MAJOR_VERSION set to ${QUAZIP_QT_MAJOR_VERSION}") +-message(STATUS "CMAKE_CXX_STANDARD set to ${CMAKE_CXX_STANDARD}") +- +-if(QUAZIP_QT_MAJOR_VERSION EQUAL 6) +- find_package(Qt6 REQUIRED COMPONENTS Core Core5Compat +- OPTIONAL_COMPONENTS Network Test) +- message(STATUS "Found Qt version ${Qt6_VERSION} at ${Qt6_DIR}") +- set(QUAZIP_QT_ZLIB_COMPONENT BundledZLIB) +- set(QUAZIP_QT_ZLIB_HEADER_COMPONENT ZlibPrivate) +- set(QUAZIP_LIB_LIBRARIES Qt6::Core Qt6::Core5Compat) +- set(QUAZIP_TEST_QT_LIBRARIES Qt6::Core Qt6::Core5Compat Qt6::Network Qt6::Test) +- set(QUAZIP_PKGCONFIG_REQUIRES "zlib, Qt6Core") +-elseif(QUAZIP_QT_MAJOR_VERSION EQUAL 5) +- find_package(Qt5 REQUIRED COMPONENTS Core +- OPTIONAL_COMPONENTS Network Test) +- message(STATUS "Found Qt version ${Qt5_VERSION} at ${Qt5_DIR}") +- set(QUAZIP_QT_ZLIB_COMPONENT Zlib) +- set(QUAZIP_LIB_LIBRARIES Qt5::Core) +- set(QUAZIP_TEST_QT_LIBRARIES Qt5::Core Qt5::Network Qt5::Test) +- set(QUAZIP_PKGCONFIG_REQUIRES "zlib, Qt5Core") +-elseif(QUAZIP_QT_MAJOR_VERSION EQUAL 4) +- find_package(Qt4 4.5.0 REQUIRED COMPONENTS QtCore +- OPTIONAL_COMPONENTS QtNetwork QtTest) +- set(QUAZIP_QT_ZLIB_COMPONENT Zlib) +- set(QUAZIP_LIB_LIBRARIES Qt4::QtCore) +- set(QUAZIP_TEST_QT_LIBRARIES Qt4::QtCore Qt4::QtNetwork Qt4::QtTest) +- set(QUAZIP_PKGCONFIG_REQUIRES "zlib, QtCore") +-else() +- message(FATAL_ERROR "Qt version ${QUAZIP_QT_MAJOR_VERSION} is not supported") +-endif() +- +-message(STATUS "Using Qt version ${QUAZIP_QT_MAJOR_VERSION}") +- +-set(QUAZIP_QT_ZLIB_USED OFF) +-if(QUAZIP_USE_QT_ZLIB) +- find_package(Qt${QUAZIP_QT_MAJOR_VERSION} OPTIONAL_COMPONENTS ${QUAZIP_QT_ZLIB_COMPONENT}) +- set(QUAZIP_QT_ZLIB_COMPONENT_FOUND Qt${QUAZIP_QT_MAJOR_VERSION}${QUAZIP_QT_ZLIB_COMPONENT}_FOUND) +- if (DEFINED QUAZIP_QT_ZLIB_HEADER_COMPONENT) +- find_package(Qt${QUAZIP_QT_MAJOR_VERSION} OPTIONAL_COMPONENTS ${QUAZIP_QT_ZLIB_HEADER_COMPONENT}) +- set(QUAZIP_QT_ZLIB_HEADER_COMPONENT_FOUND Qt${QUAZIP_QT_MAJOR_VERSION}${QUAZIP_QT_ZLIB_HEADER_COMPONENT}_FOUND) +- else() +- set(QUAZIP_QT_ZLIB_HEADER_COMPONENT_FOUND ON) +- endif() +- if(QUAZIP_QT_ZLIB_COMPONENT_FOUND AND QUAZIP_QT_ZLIB_HEADER_COMPONENT_FOUND) +- message(STATUS "Qt component ${QUAZIP_QT_ZLIB_COMPONENT} found") +- set(QUAZIP_LIB_LIBRARIES ${QUAZIP_LIB_LIBRARIES} Qt${QUAZIP_QT_MAJOR_VERSION}::${QUAZIP_QT_ZLIB_COMPONENT}) +- if(DEFINED QUAZIP_QT_ZLIB_HEADER_COMPONENT) +- message(STATUS "Qt component ${QUAZIP_QT_ZLIB_HEADER_COMPONENT} found") +- set(QUAZIP_LIB_LIBRARIES ${QUAZIP_LIB_LIBRARIES} Qt${QUAZIP_QT_MAJOR_VERSION}::${QUAZIP_QT_ZLIB_HEADER_COMPONENT}) +- endif() +- set(QUAZIP_QT_ZLIB_USED ON) +- else() +- message(FATAL_ERROR "QUAZIP_USE_QT_ZLIB was set but bundled zlib was not found. Terminating to prevent accidental linking to system libraries.") +- endif() +-endif() +- +-if(QUAZIP_QT_ZLIB_USED AND QUAZIP_QT_ZLIB_COMPONENT STREQUAL BundledZLIB) +- # Qt's new BundledZLIB uses z-prefix in zlib +- add_compile_definitions(Z_PREFIX) +-endif() +- +-if(NOT QUAZIP_QT_ZLIB_USED) +- +- if(EMSCRIPTEN) +- if(NOT DEFINED ZLIB_LIBRARY) +- message(WARNING "ZLIB_LIBRARY is not set") +- endif() ++find_package(Qt6 REQUIRED COMPONENTS Core Core5Compat ++ OPTIONAL_COMPONENTS Network Test) ++message(STATUS "Found Qt version ${Qt6_VERSION} at ${Qt6_DIR}") ++set(QUAZIP_QT_ZLIB_COMPONENT BundledZLIB) ++set(QUAZIP_QT_ZLIB_HEADER_COMPONENT ZlibPrivate) ++set(QUAZIP_LIB_LIBRARIES Qt6::Core Qt6::Core5Compat) ++set(QUAZIP_TEST_QT_LIBRARIES Qt6::Core Qt6::Core5Compat Qt6::Network Qt6::Test) ++set(QUAZIP_PKGCONFIG_REQUIRES "zlib, Qt6Core") + +- if(NOT DEFINED ZLIB_INCLUDE) +- message(WARNING "ZLIB_INCLUDE is not set") +- else() +- include_directories(${ZLIB_INCLUDE}) +- endif() +- +- if(NOT DEFINED ZCONF_INCLUDE) +- message(WARNING "ZCONF_INCLUDE is not set") +- else() +- include_directories(${ZCONF_INCLUDE}) +- endif() +- +- set(QUAZIP_LIB_LIBRARIES ${QUAZIP_LIB_LIBRARIES} ${ZLIB_LIBRARY}) +- else() +- find_package(ZLIB REQUIRED) +- set(QUAZIP_LIB_LIBRARIES ${QUAZIP_LIB_LIBRARIES} ZLIB::ZLIB) +- endif() +-endif() ++find_package(ZLIB REQUIRED) ++set(QUAZIP_LIB_LIBRARIES ${QUAZIP_LIB_LIBRARIES} ZLIB::ZLIB) + + if (ZLIB_CONST) + add_compile_definitions(ZLIB_CONST) +@@ -173,65 +50,4 @@ set(QUAZIP_INC) + set(QUAZIP_LIB) + set(QUAZIP_LBD) + +-if(QUAZIP_BZIP2) +- # Check if bzip2 is present +- set(QUAZIP_BZIP2 ON) +- +- if(NOT QUAZIP_FORCE_FETCH_LIBS) +- find_package(BZip2 QUIET) +- endif() +- +- if(BZIP2_FOUND AND NOT QUAZIP_FORCE_FETCH_LIBS) +- message(STATUS "Using BZIP2 ${BZIP2_VERSION_STRING}") +- +- list(APPEND QUAZIP_INC ${BZIP2_INCLUDE_DIRS}) +- list(APPEND QUAZIP_LIB ${BZIP2_LIBRARIES}) +- list(APPEND QUAZIP_LBD ${BZIP2_LIBRARY_DIRS}) +- +- set(PC_PRIVATE_LIBS "${PC_PRIVATE_LIBS} -lbzip2") +- elseif(QUAZIP_FETCH_LIBS) +- clone_repo(bzip2 https://sourceware.org/git/bzip2.git) +- +- # BZip2 repository does not support cmake so we have to create +- # the bzip2 library ourselves +- set(BZIP2_SRC +- ${BZIP2_SOURCE_DIR}/blocksort.c +- ${BZIP2_SOURCE_DIR}/bzlib.c +- ${BZIP2_SOURCE_DIR}/compress.c +- ${BZIP2_SOURCE_DIR}/crctable.c +- ${BZIP2_SOURCE_DIR}/decompress.c +- ${BZIP2_SOURCE_DIR}/huffman.c +- ${BZIP2_SOURCE_DIR}/randtable.c) +- +- set(BZIP2_HDR +- ${BZIP2_SOURCE_DIR}/bzlib.h +- ${BZIP2_SOURCE_DIR}/bzlib_private.h) +- +- add_library(bzip2 STATIC ${BZIP2_SRC} ${BZIP2_HDR}) +- +- if(NOT QUAZIP_BZIP2_STDIO) +- target_compile_definitions(bzip2 PRIVATE -DBZ_NO_STDIO) +- endif() +- +- list(APPEND QUAZIP_DEP bzip2) +- list(APPEND QUAZIP_LIB bzip2) +- list(APPEND QUAZIP_INC ${BZIP2_SOURCE_DIR}) +- else() +- message(STATUS "BZip2 library not found") +- +- set(QUAZIP_BZIP2 OFF) +- endif() +- +- if(QUAZIP_BZIP2) +- find_package(BZip2) +- add_compile_definitions(HAVE_BZIP2) +- endif() +-endif() +- + add_subdirectory(quazip) +- +-if(QUAZIP_ENABLE_TESTS) +- message(STATUS "Building QuaZip tests") +- enable_testing() +- add_subdirectory(qztest) +-endif() +diff --git a/quazip/CMakeLists.txt b/quazip/CMakeLists.txt +index 6cfdf4e..66bc4cb 100644 +--- a/quazip/CMakeLists.txt ++++ b/quazip/CMakeLists.txt +@@ -46,10 +46,6 @@ set(QUAZIP_INCLUDE_PATH ${QUAZIP_DIR_NAME}/quazip) + set(QUAZIP_INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake) + set(QUAZIP_PKGCONFIG_NAME quazip${QuaZip_VERSION_MAJOR}-qt${QUAZIP_QT_MAJOR_VERSION}) + +-if(EMSCRIPTEN) +- set(BUILD_SHARED_LIBS OFF) +-endif() +- + add_library(${QUAZIP_LIB_TARGET_NAME} ${QUAZIP_SOURCES}) + add_library(QuaZip::QuaZip ALIAS ${QUAZIP_LIB_TARGET_NAME}) + +diff --git a/quazip/quazip_qt_compat.h b/quazip/quazip_qt_compat.h +index 0dde011..41f9dd1 100644 +--- a/quazip/quazip_qt_compat.h ++++ b/quazip/quazip_qt_compat.h +@@ -14,16 +14,11 @@ + + // Legacy encodings are still everywhere, but the Qt team decided we + // don't need them anymore and moved them out of Core in Qt 6. +-#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +-# include +-#else +-# include +-#endif ++#include + + // QSaveFile terribly breaks the is-a idiom (Liskov substitution principle): + // QSaveFile is-a QIODevice, but it makes close() private and aborts + // if you call it through the base class. Hence this ugly hack: +-#if (QT_VERSION >= 0x050100) + #include + inline bool quazip_close(QIODevice *device) { + QSaveFile *file = qobject_cast(device); +@@ -34,74 +29,35 @@ inline bool quazip_close(QIODevice *device) { + device->close(); + return true; + } +-#else +-inline bool quazip_close(QIODevice *device) { +- device->close(); +- return true; +-} +-#endif + +-// this is yet another stupid move and deprecation +-#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + using Qt::SkipEmptyParts; +-#else +-#include +-const auto SkipEmptyParts = QString::SplitBehavior::SkipEmptyParts; +-#endif + + // and yet another... (why didn't they just make qSort delegate to std::sort?) + #include +-#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)) + #include + template + inline void quazip_sort(T begin, T end, C comparator) { + std::sort(begin, end, comparator); + } +-#else +-#include +-template +-inline void quazip_sort(T begin, T end, C comparator) { +- qSort(begin, end, comparator); +-} +-#endif + + // this is a stupid rename... + #include + #include +-#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + inline QDateTime quazip_ctime(const QFileInfo &fi) { + return fi.birthTime(); + } +-#else +-inline QDateTime quazip_ctime(const QFileInfo &fi) { +- return fi.created(); +-} +-#endif + + // this is just a slightly better alternative + #include +-#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + inline bool quazip_is_symlink(const QFileInfo &fi) { + return fi.isSymbolicLink(); + } +-#else +-inline bool quazip_is_symlink(const QFileInfo &fi) { +- // also detects *.lnk on Windows, but better than nothing +- return fi.isSymLink(); +-} +-#endif + + // I'm not even sure what this one is, but nevertheless + #include +-#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) + inline QString quazip_symlink_target(const QFileInfo &fi) { + return fi.symLinkTarget(); + } +-#else +-inline QString quazip_symlink_target(const QFileInfo &fi) { +- return fi.readLink(); // What's the difference? I've no idea. +-} +-#endif + + // deprecation + #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) +@@ -125,40 +81,19 @@ inline QDateTime quazip_since_epoch_ntfs() { + + // this is not a deprecation but an improvement, for a change + #include +-#if (QT_VERSION >= 0x040700) + inline quint64 quazip_ntfs_ticks(const QDateTime &time, int fineTicks) { + QDateTime base = quazip_since_epoch_ntfs(); + return base.msecsTo(time) * 10000 + fineTicks; + } +-#else +-inline quint64 quazip_ntfs_ticks(const QDateTime &time, int fineTicks) { +- QDateTime base = quazip_since_epoch_ntfs(); +- QDateTime utc = time.toUTC(); +- return (static_cast(base.date().daysTo(utc.date())) +- * Q_INT64_C(86400000) +- + static_cast(base.time().msecsTo(utc.time()))) +- * Q_INT64_C(10000) + fineTicks; +-} +-#endif + + // yet another improvement... + #include +-#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) // Yay! Finally a way to get time as qint64! + inline qint64 quazip_to_time64_t(const QDateTime &time) { + return time.toSecsSinceEpoch(); + } +-#else +-inline qint64 quazip_to_time64_t(const QDateTime &time) { +- return static_cast(time.toTime_t()); // 32 bits only, but better than nothing +-} +-#endif + + #include +-// and another stupid move +-#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + const auto quazip_endl = Qt::endl; +-#else +-const auto quazip_endl = endl; +-#endif + + #endif // QUAZIP_QT_COMPAT_H ++ diff --git a/CMakeLists.txt b/CMakeLists.txt index f8e8516dbc..2621822d4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,6 +79,8 @@ option(YUZU_USE_QT_MULTIMEDIA "Use QtMultimedia for Camera" OFF) option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF) +set(YUZU_QT_MIRROR "" CACHE STRING "What mirror to use for downloading the bundled Qt libraries") + option(ENABLE_CUBEB "Enables the cubeb audio backend" ON) option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) @@ -465,12 +467,10 @@ if (ENABLE_QT) list(APPEND CMAKE_PREFIX_PATH "${Qt6_DIR}") endif() - # QT6 Multimedia pulls in unneeded audio systems (ALSA, Pulseaudio) for FreeBSD - # ALSA is the default sound system on Linux, but FreeBSD uses OSS which works well enough - if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") - find_package(Qt6 REQUIRED COMPONENTS Widgets Concurrent) - else() - find_package(Qt6 REQUIRED COMPONENTS Widgets Multimedia Concurrent) + find_package(Qt6 REQUIRED COMPONENTS Widgets Concurrent Core5Compat) + + if (YUZU_USE_QT_MULTIMEDIA) + find_package(Qt6 REQUIRED COMPONENTS Multimedia) endif() if (UNIX AND NOT APPLE) diff --git a/CMakeModules/CopyYuzuQt6Deps.cmake b/CMakeModules/CopyYuzuQt6Deps.cmake index 39f88cbc19..93c4fd0170 100644 --- a/CMakeModules/CopyYuzuQt6Deps.cmake +++ b/CMakeModules/CopyYuzuQt6Deps.cmake @@ -20,6 +20,7 @@ function(copy_yuzu_Qt6_deps target_dir) if (MSVC) windows_copy_files(${target_dir} ${Qt6_DLL_DIR} ${DLL_DEST} Qt6Core$<$:d>.* + Qt6Core5Compat$<$:d>.* Qt6Gui$<$:d>.* Qt6Widgets$<$:d>.* Qt6Network$<$:d>.* diff --git a/CMakeModules/DownloadExternals.cmake b/CMakeModules/DownloadExternals.cmake index a82d1d72a3..b354ccfbe3 100644 --- a/CMakeModules/DownloadExternals.cmake +++ b/CMakeModules/DownloadExternals.cmake @@ -94,7 +94,7 @@ function(determine_qt_parameters target host_out type_out arch_out arch_path_out else() set(host "linux") set(type "desktop") - set(arch "gcc_64") + set(arch "linux_gcc_64") set(arch_path "linux") endif() @@ -133,12 +133,26 @@ function(download_qt_configuration prefix_out target host type arch arch_path ba set(install_args ${install_args} install-tool --outputdir ${base_path} ${host} desktop ${target}) else() set(prefix "${base_path}/${target}/${arch_path}") - set(install_args ${install_args} install-qt --outputdir ${base_path} ${host} ${type} ${target} ${arch} -m qt3d qt5compat qtactiveqt qtcharts qtconnectivity qtdatavis3d qtgraphs qtgrpc qthttpserver qtimageformats qtlanguageserver qtlocation qtlottie qtmultimedia qtnetworkauth qtpdf qtpositioning qtquick3d qtquick3dphysics qtquickeffectmaker qtquicktimeline qtremoteobjects qtscxml qtsensors qtserialbus qtserialport qtshadertools qtspeech qtvirtualkeyboard qtwebchannel qtwebengine qtwebsockets qtwebview) + set(install_args ${install_args} install-qt --outputdir ${base_path} ${host} ${type} ${target} ${arch} -m qt5compat) + + if (YUZU_USE_QT_MULTIMEDIA) + set(install_args ${install_args} qtmultimedia) + endif() + + if (YUZU_USE_QT_WEB_ENGINE) + set(install_args ${install_args} qtpositioning qtwebchannel qtwebengine) + endif() + + if (NOT ${YUZU_QT_MIRROR} STREQUAL "") + message(STATUS "Using Qt mirror ${YUZU_QT_MIRROR}") + set(install_args ${install_args} -b ${YUZU_QT_MIRROR}) + endif() endif() + message(STATUS "Install Args ${install_args}") if (NOT EXISTS "${prefix}") message(STATUS "Downloading Qt binaries for ${target}:${host}:${type}:${arch}:${arch_path}") - set(AQT_PREBUILD_BASE_URL "https://github.com/miurahr/aqtinstall/releases/download/v3.2.1") + set(AQT_PREBUILD_BASE_URL "https://github.com/miurahr/aqtinstall/releases/download/v3.3.0") if (WIN32) set(aqt_path "${base_path}/aqt.exe") if (NOT EXISTS "${aqt_path}") diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index bab6a6c4f5..e0733fbe92 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -462,8 +462,8 @@ if (WIN32 AND NOT YUZU_USE_BUNDLED_QT AND QT_VERSION VERSION_GREATER_EQUAL 6) endif() if (YUZU_USE_BUNDLED_QT) - include(CopyYuzuQt6Deps) - copy_yuzu_Qt6_deps(yuzu) + include(CopyYuzuQt6Deps) + copy_yuzu_Qt6_deps(yuzu) endif() if (ENABLE_SDL2) @@ -496,6 +496,6 @@ endif() # Extra deps add_subdirectory(externals) -target_link_libraries(yuzu PRIVATE QuaZip::QuaZip) +target_link_libraries(yuzu PRIVATE QuaZip::QuaZip Qt6::Core5Compat) create_target_directory_groups(yuzu) diff --git a/src/yuzu/externals/CMakeLists.txt b/src/yuzu/externals/CMakeLists.txt index ac17308e09..bd63f4e23b 100644 --- a/src/yuzu/externals/CMakeLists.txt +++ b/src/yuzu/externals/CMakeLists.txt @@ -5,9 +5,6 @@ set(BUILD_TESTING OFF) set(BUILD_SHARED_LIBS OFF) # QuaZip -set(QUAZIP_QT_MAJOR_VERSION 6) -set(QUAZIP_BZIP2 OFF) - include(CPM) set(CPM_SOURCE_CACHE ${CMAKE_SOURCE_DIR}/.cache/cpm) set(CPM_USE_LOCAL_PACKAGES ON) @@ -18,14 +15,14 @@ CPMAddPackage( GIT_REPOSITORY "https://github.com/stachenov/quazip.git" GIT_TAG v1.5 PATCHES - ${CMAKE_SOURCE_DIR}/.ci/patch/0001-quazip-strict.patch - ${CMAKE_SOURCE_DIR}/.ci/patch/0002-quazip-fetchcontent.patch -) + ${CMAKE_SOURCE_DIR}/.patch/quazip/0001-strict.patch + ${CMAKE_SOURCE_DIR}/.patch/quazip/0002-oldstyle.patch + ${CMAKE_SOURCE_DIR}/.patch/quazip/0003-predecls.patch + ${CMAKE_SOURCE_DIR}/.patch/quazip/0004-qt6-only.patch -if (NOT MSVC AND NOT "QuaZip-Qt6" IN_LIST CPM_PACKAGES) - message(STATUS "QUAZIP DIR: ${CPM_PACKAGES}") - target_compile_options(QuaZip PRIVATE - -Wno-error=shadow - -Wno-error=missing-declarations - ) -endif() + # thanks to 0004-qt6-only.patch, this isn't needed, + # but we keep it since the patch is "technically" optional + OPTIONS + "QUAZIP_QT_MAJOR_VERSION 6" + "QUAZIP_BZIP2 OFF" +) From 2aab37b516c67c31dfcb0e6bfe2e721ce1d52248 Mon Sep 17 00:00:00 2001 From: crueter Date: Thu, 17 Jul 2025 21:00:00 +0200 Subject: [PATCH 21/25] [cmake] QuaZip: The Finale (#74) Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/74 --- CMakeLists.txt | 2 +- CMakeModules/CopyYuzuQt6Deps.cmake | 1 - CMakeModules/DownloadExternals.cmake | 2 +- src/yuzu/CMakeLists.txt | 2 +- src/yuzu/externals/CMakeLists.txt | 15 ++------------- 5 files changed, 5 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2621822d4f..124a5ac80a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -467,7 +467,7 @@ if (ENABLE_QT) list(APPEND CMAKE_PREFIX_PATH "${Qt6_DIR}") endif() - find_package(Qt6 REQUIRED COMPONENTS Widgets Concurrent Core5Compat) + find_package(Qt6 REQUIRED COMPONENTS Widgets Concurrent) if (YUZU_USE_QT_MULTIMEDIA) find_package(Qt6 REQUIRED COMPONENTS Multimedia) diff --git a/CMakeModules/CopyYuzuQt6Deps.cmake b/CMakeModules/CopyYuzuQt6Deps.cmake index 93c4fd0170..39f88cbc19 100644 --- a/CMakeModules/CopyYuzuQt6Deps.cmake +++ b/CMakeModules/CopyYuzuQt6Deps.cmake @@ -20,7 +20,6 @@ function(copy_yuzu_Qt6_deps target_dir) if (MSVC) windows_copy_files(${target_dir} ${Qt6_DLL_DIR} ${DLL_DEST} Qt6Core$<$:d>.* - Qt6Core5Compat$<$:d>.* Qt6Gui$<$:d>.* Qt6Widgets$<$:d>.* Qt6Network$<$:d>.* diff --git a/CMakeModules/DownloadExternals.cmake b/CMakeModules/DownloadExternals.cmake index b354ccfbe3..e65827290d 100644 --- a/CMakeModules/DownloadExternals.cmake +++ b/CMakeModules/DownloadExternals.cmake @@ -133,7 +133,7 @@ function(download_qt_configuration prefix_out target host type arch arch_path ba set(install_args ${install_args} install-tool --outputdir ${base_path} ${host} desktop ${target}) else() set(prefix "${base_path}/${target}/${arch_path}") - set(install_args ${install_args} install-qt --outputdir ${base_path} ${host} ${type} ${target} ${arch} -m qt5compat) + set(install_args ${install_args} install-qt --outputdir ${base_path} ${host} ${type} ${target} ${arch} -m qtbase) if (YUZU_USE_QT_MULTIMEDIA) set(install_args ${install_args} qtmultimedia) diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index e0733fbe92..34f2ba455a 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -496,6 +496,6 @@ endif() # Extra deps add_subdirectory(externals) -target_link_libraries(yuzu PRIVATE QuaZip::QuaZip Qt6::Core5Compat) +target_link_libraries(yuzu PRIVATE QuaZip::QuaZip) create_target_directory_groups(yuzu) diff --git a/src/yuzu/externals/CMakeLists.txt b/src/yuzu/externals/CMakeLists.txt index bd63f4e23b..d606e27108 100644 --- a/src/yuzu/externals/CMakeLists.txt +++ b/src/yuzu/externals/CMakeLists.txt @@ -12,17 +12,6 @@ set(CPM_USE_LOCAL_PACKAGES ON) CPMAddPackage( NAME QuaZip-Qt6 VERSION 1.3 - GIT_REPOSITORY "https://github.com/stachenov/quazip.git" - GIT_TAG v1.5 - PATCHES - ${CMAKE_SOURCE_DIR}/.patch/quazip/0001-strict.patch - ${CMAKE_SOURCE_DIR}/.patch/quazip/0002-oldstyle.patch - ${CMAKE_SOURCE_DIR}/.patch/quazip/0003-predecls.patch - ${CMAKE_SOURCE_DIR}/.patch/quazip/0004-qt6-only.patch - - # thanks to 0004-qt6-only.patch, this isn't needed, - # but we keep it since the patch is "technically" optional - OPTIONS - "QUAZIP_QT_MAJOR_VERSION 6" - "QUAZIP_BZIP2 OFF" + GIT_REPOSITORY "https://github.com/crueter/quazip-qt6.git" + GIT_TAG v1.5-qt6 ) From fc8d7004d253194d075899de75cc4bc6ae2bb959 Mon Sep 17 00:00:00 2001 From: Bix Date: Sat, 12 Jul 2025 20:14:34 +0200 Subject: [PATCH 22/25] Added "Legacy " to App name on android. Signed-off-by: Bix From ac89ffae9f72a844ccc3897bbbb3ffb41d820194 Mon Sep 17 00:00:00 2001 From: Bix Date: Sat, 12 Jul 2025 20:15:19 +0200 Subject: [PATCH 23/25] revert cd394fc40f3847878e3646c07fa78a13df4cce30 revert revert [android] Snapdragon 865 patches (#23) revert [android] Snapdragon 865 patches (#23) Co-authored-by: Aleksandr Popovich Reviewed-on: https://git.bixed.xyz/Bix/eden/pulls/23 Reverted due to heavy performance hits on Android with higher specifications, will be adjusted to be included in a specific build for older A6XX devices, as 855, 860, 865, 870, meanwhile it does fix critical issues with certain games crashing due to memory and VRAM usage, hits performance on SoC that can do it without this special flags. --- src/common/microprofile.h | 20 +++++++++++++++++-- src/core/arm/nce/arm_nce.cpp | 2 +- src/core/hle/service/sockets/sfdnsres.cpp | 15 +++++++++++--- .../maxwell/translate/impl/warp_shuffle.cpp | 17 +++++++++++++++- src/video_core/buffer_cache/buffer_cache.h | 10 ++++++++-- .../buffer_cache/buffer_cache_base.h | 18 ++++++++++++++++- src/video_core/host1x/host1x.cpp | 5 +++++ src/video_core/host1x/host1x.h | 5 +++++ .../texture_cache/texture_cache_base.h | 11 ++++++++++ .../vulkan_common/vulkan_memory_allocator.cpp | 5 ++++- 10 files changed, 97 insertions(+), 11 deletions(-) diff --git a/src/common/microprofile.h b/src/common/microprofile.h index 56ef0a2dcf..25bf362500 100644 --- a/src/common/microprofile.h +++ b/src/common/microprofile.h @@ -1,11 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2015 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once -// Uncomment this to disable microprofile. This will get you cleaner profiles when using +// Use this to disable microprofile. This will get you cleaner profiles when using // external sampling profilers like "Very Sleepy", and will improve performance somewhat. -// #define MICROPROFILE_ENABLED 0 +#ifdef ANDROID +#define MICROPROFILE_ENABLED 0 +#define MICROPROFILEUI_ENABLED 0 +#define MicroProfileOnThreadExit() do{}while(0) +#define MICROPROFILE_TOKEN(x) 0 +#define MicroProfileEnter(x) 0 +#define MicroProfileLeave(x, y) ignore_all(x, y) +#endif // Customized Citra settings. // This file wraps the MicroProfile header so that these are consistent everywhere. @@ -19,6 +29,12 @@ typedef void* HANDLE; #endif +#include +template +void ignore_all(Args&&... args) { + (static_cast(std::ignore = args), ...); +} + #include #define MP_RGB(r, g, b) ((r) << 16 | (g) << 8 | (b) << 0) diff --git a/src/core/arm/nce/arm_nce.cpp b/src/core/arm/nce/arm_nce.cpp index 877e8ac3c7..0e0d72fc8a 100644 --- a/src/core/arm/nce/arm_nce.cpp +++ b/src/core/arm/nce/arm_nce.cpp @@ -227,7 +227,7 @@ HaltReason ArmNce::RunThread(Kernel::KThread* thread) { if (auto it = post_handlers.find(m_guest_ctx.pc); it != post_handlers.end()) { hr = ReturnToRunCodeByTrampoline(thread_params, &m_guest_ctx, it->second); } else { - hr = ReturnToRunCodeByExceptionLevelChange(m_thread_id, thread_params); + hr = ReturnToRunCodeByExceptionLevelChange(m_thread_id, thread_params); // Android: Use "process handle SIGUSR2 -n true -p true -s false" (and SIGURG) in LLDB when debugging } // Critical section for thread cleanup diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp index b07bd3e58e..d9f01a65b8 100644 --- a/src/core/hle/service/sockets/sfdnsres.cpp +++ b/src/core/hle/service/sockets/sfdnsres.cpp @@ -53,6 +53,16 @@ enum class NetDbError : s32 { NoData = 4, }; +const std::vector blockedDomains = {"srv.nintendo.net", "battle.net", + "microsoft.com", "mojang.com", + "xboxlive.com", "minecraftservices.com"}; + +static bool IsBlockedHost(const std::string& host) { + return std::any_of( + blockedDomains.begin(), blockedDomains.end(), + [&host](const std::string& domain) { return host.find(domain) != std::string::npos; }); +} + static NetDbError GetAddrInfoErrorToNetDbError(GetAddrInfoError result) { // These combinations have been verified on console (but are not // exhaustive). @@ -154,7 +164,7 @@ static std::pair GetHostByNameRequestImpl(HLERequestConte // For now, ignore options, which are in input buffer 1 for GetHostByNameRequestWithOptions. // Prevent resolution of Nintendo servers - if (host.find("srv.nintendo.net") != std::string::npos) { + if (IsBlockedHost(host)) { LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host); return {0, GetAddrInfoError::AGAIN}; } @@ -271,7 +281,7 @@ static std::pair GetAddrInfoRequestImpl(HLERequestContext const std::string host = Common::StringFromBuffer(host_buffer); // Prevent resolution of Nintendo servers - if (host.find("srv.nintendo.net") != std::string::npos) { + if (IsBlockedHost(host)) { LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host); return {0, GetAddrInfoError::AGAIN}; } @@ -359,5 +369,4 @@ void SFDNSRES::ResolverSetOptionRequest(HLERequestContext& ctx) { rb.Push(ResultSuccess); rb.Push(0); // bsd errno } - } // namespace Service::Sockets diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/warp_shuffle.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/warp_shuffle.cpp index 972eec8276..b904821619 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/warp_shuffle.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/warp_shuffle.cpp @@ -7,6 +7,7 @@ #include "common/bit_field.h" #include "common/common_types.h" #include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" +#include namespace Shader::Maxwell { namespace { @@ -36,6 +37,17 @@ enum class ShuffleMode : u64 { } } +bool IsKONA() { + std::ifstream machineFile("/sys/devices/soc0/machine"); + if (machineFile.is_open()) { + std::string line; + std::getline(machineFile, line); + if (line == "KONA") + return true; + } + return false; +} + void Shuffle(TranslatorVisitor& v, u64 insn, const IR::U32& index, const IR::U32& mask) { union { u64 insn; @@ -47,7 +59,10 @@ void Shuffle(TranslatorVisitor& v, u64 insn, const IR::U32& index, const IR::U32 const IR::U32 result{ShuffleOperation(v.ir, v.X(shfl.src_reg), index, mask, shfl.mode)}; v.ir.SetPred(shfl.pred, v.ir.GetInBoundsFromOp(result)); - v.X(shfl.dest_reg, result); + if (IsKONA()) + v.X(shfl.dest_reg, v.ir.Imm32(0xffffffff)); // This fixes the freeze for Retroid / Snapdragon SD865 + else + v.X(shfl.dest_reg, result); } } // Anonymous namespace diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index a6e87a3583..8591378b1c 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -26,7 +26,9 @@ BufferCache

::BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, R void(slot_buffers.insert(runtime, NullBufferParams{})); gpu_modified_ranges.Clear(); inline_buffer_id = NULL_BUFFER_ID; - +#ifdef ANDROID + immediately_free = (Settings::values.vram_usage_mode.GetValue() == Settings::VramUsageMode::Aggressive); +#endif if (!runtime.CanReportMemoryUsage()) { minimum_memory = DEFAULT_EXPECTED_MEMORY; critical_memory = DEFAULT_CRITICAL_MEMORY; @@ -1384,6 +1386,8 @@ void BufferCache

::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, }); new_buffer.MarkUsage(copies[0].dst_offset, copies[0].size); runtime.CopyBuffer(new_buffer, overlap, copies, true); + if (immediately_free) + runtime.Finish(); DeleteBuffer(overlap_id, true); } @@ -1675,7 +1679,9 @@ void BufferCache

::DeleteBuffer(BufferId buffer_id, bool do_not_mark) { } Unregister(buffer_id); - delayed_destruction_ring.Push(std::move(slot_buffers[buffer_id])); + + if (!do_not_mark || !immediately_free) + delayed_destruction_ring.Push(std::move(slot_buffers[buffer_id])); slot_buffers.erase(buffer_id); if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index 1c7320dcc1..fbdf6e858b 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -159,7 +159,11 @@ template class BufferCache : public VideoCommon::ChannelSetupCaches { // Page size for caching purposes. // This is unrelated to the CPU page size and it can be changed as it seems optimal. +#ifdef ANDROID + static constexpr u32 CACHING_PAGEBITS = 12; +#else static constexpr u32 CACHING_PAGEBITS = 16; +#endif static constexpr u64 CACHING_PAGESIZE = u64{1} << CACHING_PAGEBITS; static constexpr bool IS_OPENGL = P::IS_OPENGL; @@ -173,9 +177,15 @@ class BufferCache : public VideoCommon::ChannelSetupCaches slot_buffers; - DelayedDestructionRing delayed_destruction_ring; +#ifdef ANDROID + static constexpr size_t TICKS_TO_DESTROY = 6; +#else + static constexpr size_t TICKS_TO_DESTROY = 8; +#endif + DelayedDestructionRing delayed_destruction_ring; const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect{}; @@ -483,6 +498,7 @@ private: u64 minimum_memory = 0; u64 critical_memory = 0; BufferId inline_buffer_id; + bool immediately_free = false; std::array> CACHING_PAGEBITS)> page_table; Common::ScratchBuffer tmp_buffer; diff --git a/src/video_core/host1x/host1x.cpp b/src/video_core/host1x/host1x.cpp index 293bca6d79..652d387031 100644 --- a/src/video_core/host1x/host1x.cpp +++ b/src/video_core/host1x/host1x.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -18,9 +21,11 @@ Host1x::~Host1x() = default; void Host1x::StartDevice(s32 fd, ChannelType type, u32 syncpt) { switch (type) { case ChannelType::NvDec: + std::call_once(nvdec_first_init, []() {std::this_thread::sleep_for(std::chrono::milliseconds{500});}); // HACK: For Astroneer devices[fd] = std::make_unique(*this, fd, syncpt, frame_queue); break; case ChannelType::VIC: + std::call_once(vic_first_init, []() {std::this_thread::sleep_for(std::chrono::milliseconds{500});}); // HACK: For Astroneer devices[fd] = std::make_unique(*this, fd, syncpt, frame_queue); break; default: diff --git a/src/video_core/host1x/host1x.h b/src/video_core/host1x/host1x.h index 8debac93dd..5ecffa442c 100644 --- a/src/video_core/host1x/host1x.h +++ b/src/video_core/host1x/host1x.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -201,6 +204,8 @@ private: std::unique_ptr> allocator; FrameQueue frame_queue; std::unordered_map> devices; + std::once_flag nvdec_first_init; + std::once_flag vic_first_init; }; } // namespace Tegra::Host1x diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index cbc27344b0..3f67828b86 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -110,10 +110,17 @@ class TextureCache : public VideoCommon::ChannelSetupCaches::max()}; + #ifdef ANDROID + static constexpr s64 TARGET_THRESHOLD = 3_GiB; + static constexpr s64 DEFAULT_EXPECTED_MEMORY = 1_GiB + 125_MiB; + static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB + 625_MiB; + static constexpr size_t GC_EMERGENCY_COUNTS = 2; + #else static constexpr s64 TARGET_THRESHOLD = 4_GiB; static constexpr s64 DEFAULT_EXPECTED_MEMORY = 1_GiB + 125_MiB; static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB + 625_MiB; static constexpr size_t GC_EMERGENCY_COUNTS = 2; + #endif using Runtime = typename P::Runtime; using Image = typename P::Image; @@ -479,7 +486,11 @@ private: }; Common::LeastRecentlyUsedCache lru_cache; + #ifdef ANDROID + static constexpr size_t TICKS_TO_DESTROY = 6; + #else static constexpr size_t TICKS_TO_DESTROY = 8; +#endif DelayedDestructionRing sentenced_images; DelayedDestructionRing sentenced_image_view; DelayedDestructionRing sentenced_framebuffers; diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index e80808621b..04b362f9b0 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp @@ -271,7 +271,10 @@ vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo& ci, MemoryUsa VmaAllocation allocation{}; VkMemoryPropertyFlags property_flags{}; - vk::Check(vmaCreateBuffer(allocator, &ci, &alloc_ci, &handle, &allocation, &alloc_info)); + VkResult result = vmaCreateBuffer(allocator, &ci, &alloc_ci, &handle, &allocation, &alloc_info); + if (result == VK_ERROR_OUT_OF_DEVICE_MEMORY) { + LOG_ERROR(Render_Vulkan, "Out of memory creating buffer (size: {})", ci.size); + } vmaGetAllocationMemoryProperties(allocator, allocation, &property_flags); u8* data = reinterpret_cast(alloc_info.pMappedData); From c07df1160941c849bc6275a6127bcbcbee501931 Mon Sep 17 00:00:00 2001 From: Bixthefin <114880614+Bixthefin@users.noreply.github.com> Date: Fri, 18 Jul 2025 13:05:29 +0100 Subject: [PATCH 24/25] [android] Update applicationId to legacy --- src/android/app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index 8fc4a82088..295e2e971a 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts @@ -58,7 +58,7 @@ android { defaultConfig { // TODO If this is ever modified, change application_id in strings.xml - applicationId = "dev.eden.eden_emulator" + applicationId = "dev.legacy.eden_emulator" minSdk = 30 targetSdk = 35 versionName = getGitVersion() From 1960804696aa40333ac95e11a28fd8dd77b45cdc Mon Sep 17 00:00:00 2001 From: Bixthefin <114880614+Bixthefin@users.noreply.github.com> Date: Fri, 18 Jul 2025 13:17:03 +0100 Subject: [PATCH 25/25] [Android] Change app_name to Eden Legacy --- src/android/app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index 295e2e971a..d35caa8b4a 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts @@ -103,7 +103,7 @@ android { signingConfigs.getByName("default") } - resValue("string", "app_name_suffixed", "Eden") + resValue("string", "app_name_suffixed", "Eden Legacy") isMinifyEnabled = true isDebuggable = false proguardFiles(