[dynarmic] bootstrap loongarch64 host build (#4015)
Some checks are pending
tx-src / sources (push) Waiting to run
Check Strings / check-strings (push) Waiting to run

Minimal additions to make Eden compile.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4015
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
This commit is contained in:
Yang Liu 2026-05-30 01:42:23 +02:00 committed by crueter
parent c84d605426
commit ff7bbaea7d
No known key found for this signature in database
GPG key ID: 425ACD2D4830EBC6
15 changed files with 607 additions and 9 deletions

View file

@ -82,6 +82,11 @@ if (ARCHITECTURE_riscv64)
AddJsonPackage(biscuit)
endif()
# Lagoon
if (ARCHITECTURE_loongarch64)
AddJsonPackage(lagoon)
endif()
# Vulkan stuff
AddDependentPackages(vulkan-headers vulkan-utility-libraries)

View file

@ -67,6 +67,12 @@
"tag": "v%VERSION%",
"hash": "9697e80a7d5d9bcb3ce51051a9a24962fb90ca79d215f1f03ae6b58da8ba13a63b5dda1b4dde3d26ac6445029696b8ef2883f4e5a777b342bba01283ed293856"
},
"lagoon": {
"repo": "loongson-community/lagoon",
"tag": "%VERSION%",
"version": "1.0.0",
"hash": "b9380f99c6effaeccc6d8f81d4942e852c11ad28613df637e155451556ae5826f93765bee57a5c87a9740d2bd1db463ad0f55a947772fe9d57eeabae3efa373e"
},
"libadrenotools": {
"repo": "eden-emulator/libadrenotools",
"sha": "8ba23b42d7",

View file

@ -8,7 +8,7 @@
include_directories(.)
# Dynarmic
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64) AND NOT YUZU_STATIC_ROOM)
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_loongarch64) AND NOT YUZU_STATIC_ROOM)
add_subdirectory(dynarmic)
add_library(dynarmic::dynarmic ALIAS dynarmic)
endif()

View file

@ -1246,7 +1246,7 @@ if (HAS_NCE)
target_link_libraries(core PRIVATE merry::oaknut)
endif()
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64)
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_loongarch64)
target_sources(core PRIVATE
arm/dynarmic/arm_dynarmic.h
arm/dynarmic/arm_dynarmic_64.cpp

View file

@ -130,6 +130,10 @@ if ("riscv64" IN_LIST ARCHITECTURE)
find_package(biscuit 0.9.1 REQUIRED)
endif()
if ("loongarch64" IN_LIST ARCHITECTURE)
find_package(lagoon REQUIRED)
endif()
if ("x86_64" IN_LIST ARCHITECTURE)
find_package(xbyak 7 CONFIG)
endif()

View file

@ -295,6 +295,20 @@ if ("riscv64" IN_LIST ARCHITECTURE)
message(WARNING "TODO: Incomplete frontend for this host architecture")
endif()
if ("loongarch64" IN_LIST ARCHITECTURE)
target_link_libraries(dynarmic PRIVATE lagoon::lagoon)
target_sources(dynarmic PRIVATE
backend/loongarch64/exclusive_monitor.cpp
backend/loongarch64/a32_interface.cpp
backend/loongarch64/a64_interface.cpp
backend/loongarch64/code_block.h
common/spin_lock_loongarch64.cpp
)
message(WARNING "TODO: Incomplete frontend for this host architecture")
endif()
if (WIN32)
target_sources(dynarmic PRIVATE backend/exception_handler_windows.cpp)
elseif (APPLE)

View file

@ -26,6 +26,10 @@ class CodeBlock;
namespace Dynarmic::Backend::RV64 {
class CodeBlock;
} // namespace Dynarmic::Backend::RV64
#elif defined(ARCHITECTURE_loongarch64)
namespace Dynarmic::Backend::LoongArch64 {
class CodeBlock;
} // namespace Dynarmic::Backend::LoongArch64
#else
# error "Invalid architecture"
#endif
@ -45,6 +49,10 @@ struct FakeCall {
struct FakeCall {
u64 call_sepc;
};
#elif defined(ARCHITECTURE_loongarch64)
struct FakeCall {
u64 call_pc;
};
#else
# error "Invalid architecture"
#endif
@ -60,6 +68,8 @@ public:
void Register(oaknut::CodeBlock& mem, std::size_t mem_size);
#elif defined(ARCHITECTURE_riscv64)
void Register(RV64::CodeBlock& mem, std::size_t mem_size);
#elif defined(ARCHITECTURE_loongarch64)
void Register(LoongArch64::CodeBlock& mem, std::size_t mem_size);
#else
# error "Invalid architecture"
#endif

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
@ -28,6 +28,10 @@ void ExceptionHandler::Register(oaknut::CodeBlock&, std::size_t) {
void ExceptionHandler::Register(RV64::CodeBlock&, std::size_t) {
// Do nothing
}
#elif defined(ARCHITECTURE_loongarch64)
void ExceptionHandler::Register(LoongArch64::CodeBlock&, std::size_t) {
// Do nothing
}
#else
# error "Invalid architecture"
#endif

View file

@ -6,20 +6,25 @@
* SPDX-License-Identifier: 0BSD
*/
#include <signal.h>
#include <algorithm>
#include <bit>
#include <cstring>
#include <functional>
#include <algorithm>
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <optional>
#include <bit>
#include <fmt/format.h>
#include <shared_mutex>
#include <ankerl/unordered_dense.h>
#include "dynarmic/backend/exception_handler.h"
#include <fmt/format.h>
#include <sys/mman.h>
#include "common/assert.h"
#include "dynarmic/common/context.h"
#include "common/common_types.h"
#include "dynarmic/backend/exception_handler.h"
#include "dynarmic/common/context.h"
#if defined(ARCHITECTURE_x86_64)
# include "dynarmic/backend/x64/block_of_code.h"
#elif defined(ARCHITECTURE_arm64)
@ -27,6 +32,8 @@
# include "dynarmic/backend/arm64/abi.h"
#elif defined(ARCHITECTURE_riscv64)
# include "dynarmic/backend/riscv64/code_block.h"
#elif defined(ARCHITECTURE_loongarch64)
# include "dynarmic/backend/loongarch64/code_block.h"
#else
# error "Invalid architecture"
#endif
@ -150,6 +157,16 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
}
}
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_SEPC);
#elif defined(ARCHITECTURE_loongarch64)
{
std::shared_lock guard(sig_handler->code_block_infos_mutex);
if (const auto iter = sig_handler->FindCodeBlockInfo(CTX_PC); iter != sig_handler->code_block_infos.end()) {
FakeCall fc = iter->second.cb(CTX_PC);
CTX_PC = fc.call_pc;
return;
}
}
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
#else
# error "Invalid architecture"
#endif
@ -209,6 +226,10 @@ void ExceptionHandler::Register(oaknut::CodeBlock& mem, std::size_t size) {
void ExceptionHandler::Register(RV64::CodeBlock& mem, std::size_t size) {
impl = std::make_unique<Impl>(std::bit_cast<u64>(mem.ptr<u64>()), size);
}
#elif defined(ARCHITECTURE_loongarch64)
void ExceptionHandler::Register(LoongArch64::CodeBlock& mem, std::size_t size) {
impl = std::make_unique<Impl>(std::bit_cast<u64>(mem.ptr<u64>()), size);
}
#else
# error "Invalid architecture"
#endif

View file

@ -0,0 +1,170 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <array>
#include <memory>
#include <string>
#include <utility>
#include "common/assert.h"
#include "common/common_types.h"
#include "dynarmic/interface/A32/a32.h"
namespace Dynarmic::A32 {
struct Jit::Impl final {
explicit Impl(UserConfig conf_) : conf(std::move(conf_)) {}
HaltReason Run() {
UNIMPLEMENTED();
return halt_reason;
}
HaltReason Step() {
UNIMPLEMENTED();
return halt_reason | HaltReason::Step;
}
void ClearCache() {
HaltExecution(HaltReason::CacheInvalidation);
}
void InvalidateCacheRange(u32, std::size_t) {
HaltExecution(HaltReason::CacheInvalidation);
}
void Reset() {
regs = {};
ext_regs = {};
cpsr = 0;
fpscr = 0;
halt_reason = {};
}
void HaltExecution(HaltReason hr) {
halt_reason |= hr;
}
void ClearHalt(HaltReason hr) {
halt_reason &= ~hr;
}
std::array<u32, 16>& Regs() {
return regs;
}
const std::array<u32, 16>& Regs() const {
return regs;
}
std::array<u32, 64>& ExtRegs() {
return ext_regs;
}
const std::array<u32, 64>& ExtRegs() const {
return ext_regs;
}
u32 Cpsr() const {
return cpsr;
}
void SetCpsr(u32 value) {
cpsr = value;
}
u32 Fpscr() const {
return fpscr;
}
void SetFpscr(u32 value) {
fpscr = value;
}
void ClearExclusiveState() {}
std::string Disassemble() const {
return {};
}
UserConfig conf;
std::array<u32, 16> regs{};
std::array<u32, 64> ext_regs{};
u32 cpsr = 0;
u32 fpscr = 0;
HaltReason halt_reason{};
};
Jit::Jit(UserConfig conf) : impl(std::make_unique<Impl>(std::move(conf))) {}
Jit::~Jit() = default;
HaltReason Jit::Run() {
return impl->Run();
}
HaltReason Jit::Step() {
return impl->Step();
}
void Jit::ClearCache() {
impl->ClearCache();
}
void Jit::InvalidateCacheRange(u32 start_address, std::size_t length) {
impl->InvalidateCacheRange(start_address, length);
}
void Jit::Reset() {
impl->Reset();
}
void Jit::HaltExecution(HaltReason hr) {
impl->HaltExecution(hr);
}
void Jit::ClearHalt(HaltReason hr) {
impl->ClearHalt(hr);
}
std::array<u32, 16>& Jit::Regs() {
return impl->Regs();
}
const std::array<u32, 16>& Jit::Regs() const {
return impl->Regs();
}
std::array<u32, 64>& Jit::ExtRegs() {
return impl->ExtRegs();
}
const std::array<u32, 64>& Jit::ExtRegs() const {
return impl->ExtRegs();
}
u32 Jit::Cpsr() const {
return impl->Cpsr();
}
void Jit::SetCpsr(u32 value) {
impl->SetCpsr(value);
}
u32 Jit::Fpscr() const {
return impl->Fpscr();
}
void Jit::SetFpscr(u32 value) {
impl->SetFpscr(value);
}
void Jit::ClearExclusiveState() {
impl->ClearExclusiveState();
}
std::string Jit::Disassemble() const {
return impl->Disassemble();
}
} // namespace Dynarmic::A32

View file

@ -0,0 +1,268 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <array>
#include <memory>
#include <string>
#include <utility>
#include "common/assert.h"
#include "common/common_types.h"
#include "dynarmic/interface/A64/a64.h"
namespace Dynarmic::A64 {
struct Jit::Impl final {
explicit Impl(UserConfig conf_) : conf(std::move(conf_)) {}
HaltReason Run() {
UNIMPLEMENTED();
return halt_reason;
}
HaltReason Step() {
UNIMPLEMENTED();
return halt_reason | HaltReason::Step;
}
void ClearCache() {
HaltExecution(HaltReason::CacheInvalidation);
}
void InvalidateCacheRange(u64, std::size_t) {
HaltExecution(HaltReason::CacheInvalidation);
}
void Reset() {
regs = {};
vectors = {};
sp = 0;
pc = 0;
fpcr = 0;
fpsr = 0;
pstate = 0;
halt_reason = {};
}
void HaltExecution(HaltReason hr) {
halt_reason |= hr;
}
void ClearHalt(HaltReason hr) {
halt_reason &= ~hr;
}
u64 GetSP() const {
return sp;
}
void SetSP(u64 value) {
sp = value;
}
u64 GetPC() const {
return pc;
}
void SetPC(u64 value) {
pc = value;
}
u64 GetRegister(std::size_t index) const {
return index == 31 ? sp : regs.at(index);
}
void SetRegister(std::size_t index, u64 value) {
if (index == 31) {
sp = value;
return;
}
regs.at(index) = value;
}
std::array<u64, 31> GetRegisters() const {
return regs;
}
void SetRegisters(const std::array<u64, 31>& value) {
regs = value;
}
Vector GetVector(std::size_t index) const {
return vectors.at(index);
}
void SetVector(std::size_t index, Vector value) {
vectors.at(index) = value;
}
std::array<Vector, 32> GetVectors() const {
return vectors;
}
void SetVectors(const std::array<Vector, 32>& value) {
vectors = value;
}
u32 GetFpcr() const {
return fpcr;
}
void SetFpcr(u32 value) {
fpcr = value;
}
u32 GetFpsr() const {
return fpsr;
}
void SetFpsr(u32 value) {
fpsr = value;
}
u32 GetPstate() const {
return pstate;
}
void SetPstate(u32 value) {
pstate = value;
}
void ClearExclusiveState() {}
bool IsExecuting() const {
return false;
}
std::string Disassemble() const {
return {};
}
UserConfig conf;
std::array<u64, 31> regs{};
std::array<Vector, 32> vectors{};
u64 sp = 0;
u64 pc = 0;
u32 fpcr = 0;
u32 fpsr = 0;
u32 pstate = 0;
HaltReason halt_reason{};
};
Jit::Jit(UserConfig conf) : impl(std::make_unique<Impl>(std::move(conf))) {}
Jit::~Jit() = default;
HaltReason Jit::Run() {
return impl->Run();
}
HaltReason Jit::Step() {
return impl->Step();
}
void Jit::ClearCache() {
impl->ClearCache();
}
void Jit::InvalidateCacheRange(u64 start_address, std::size_t length) {
impl->InvalidateCacheRange(start_address, length);
}
void Jit::Reset() {
impl->Reset();
}
void Jit::HaltExecution(HaltReason hr) {
impl->HaltExecution(hr);
}
void Jit::ClearHalt(HaltReason hr) {
impl->ClearHalt(hr);
}
u64 Jit::GetSP() const {
return impl->GetSP();
}
void Jit::SetSP(u64 value) {
impl->SetSP(value);
}
u64 Jit::GetPC() const {
return impl->GetPC();
}
void Jit::SetPC(u64 value) {
impl->SetPC(value);
}
u64 Jit::GetRegister(std::size_t index) const {
return impl->GetRegister(index);
}
void Jit::SetRegister(std::size_t index, u64 value) {
impl->SetRegister(index, value);
}
std::array<u64, 31> Jit::GetRegisters() const {
return impl->GetRegisters();
}
void Jit::SetRegisters(const std::array<u64, 31>& value) {
impl->SetRegisters(value);
}
Vector Jit::GetVector(std::size_t index) const {
return impl->GetVector(index);
}
void Jit::SetVector(std::size_t index, Vector value) {
impl->SetVector(index, value);
}
std::array<Vector, 32> Jit::GetVectors() const {
return impl->GetVectors();
}
void Jit::SetVectors(const std::array<Vector, 32>& value) {
impl->SetVectors(value);
}
u32 Jit::GetFpcr() const {
return impl->GetFpcr();
}
void Jit::SetFpcr(u32 value) {
impl->SetFpcr(value);
}
u32 Jit::GetFpsr() const {
return impl->GetFpsr();
}
void Jit::SetFpsr(u32 value) {
impl->SetFpsr(value);
}
u32 Jit::GetPstate() const {
return impl->GetPstate();
}
void Jit::SetPstate(u32 value) {
impl->SetPstate(value);
}
void Jit::ClearExclusiveState() {
impl->ClearExclusiveState();
}
bool Jit::IsExecuting() const {
return impl->IsExecuting();
}
std::string Jit::Disassemble() const {
return impl->Disassemble();
}
} // namespace Dynarmic::A64

View file

@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <cstdint>
#include "common/common_types.h"
namespace Dynarmic::Backend::LoongArch64 {
class CodeBlock {
public:
template<typename T>
T ptr() const noexcept {
return reinterpret_cast<T>(mem);
}
private:
u8* mem = nullptr;
};
} // namespace Dynarmic::Backend::LoongArch64

View file

@ -0,0 +1,54 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "dynarmic/interface/exclusive_monitor.h"
#include <algorithm>
namespace Dynarmic {
ExclusiveMonitor::ExclusiveMonitor(std::size_t processor_count)
: exclusive_addresses(processor_count, INVALID_EXCLUSIVE_ADDRESS), exclusive_values(processor_count) {}
size_t ExclusiveMonitor::GetProcessorCount() const {
return exclusive_addresses.size();
}
void ExclusiveMonitor::Lock() {
lock.Lock();
}
void ExclusiveMonitor::Unlock() {
lock.Unlock();
}
bool ExclusiveMonitor::CheckAndClear(std::size_t processor_id, VAddr address) {
const VAddr masked_address = address & RESERVATION_GRANULE_MASK;
Lock();
if (exclusive_addresses[processor_id] != masked_address) {
Unlock();
return false;
}
for (VAddr& other_address : exclusive_addresses) {
if (other_address == masked_address) {
other_address = INVALID_EXCLUSIVE_ADDRESS;
}
}
return true;
}
void ExclusiveMonitor::Clear() {
Lock();
std::fill(exclusive_addresses.begin(), exclusive_addresses.end(), INVALID_EXCLUSIVE_ADDRESS);
Unlock();
}
void ExclusiveMonitor::ClearProcessor(std::size_t processor_id) {
Lock();
exclusive_addresses[processor_id] = INVALID_EXCLUSIVE_ADDRESS;
Unlock();
}
} // namespace Dynarmic

View file

@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "dynarmic/common/spin_lock.h"
namespace Dynarmic {
void SpinLock::Lock() noexcept {
}
void SpinLock::Unlock() noexcept {
}
} // namespace Dynarmic

View file

@ -9,6 +9,7 @@
#pragma once
#include <functional>
#include <tuple>
#include "common/assert.h"
@ -31,6 +32,10 @@ public:
, expected{expected}
{}
constexpr Matcher(std::tuple<T, T> t) noexcept
: Matcher(std::get<0>(t), std::get<1>(t))
{}
/// @brief Gets the mask for this instruction.
constexpr inline T GetMask() const noexcept {
return mask;