diff --git a/.gitignore b/.gitignore index d735e662..4dc28a88 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ src/dynarmic/backend/arm64/mig/ src/dynarmic/backend/x64/mig/ # System files .DS_Store +.vscode diff --git a/src/dynarmic/CMakeLists.txt b/src/dynarmic/CMakeLists.txt index 0d6068af..bfd03de5 100644 --- a/src/dynarmic/CMakeLists.txt +++ b/src/dynarmic/CMakeLists.txt @@ -399,6 +399,24 @@ if ("arm64" IN_LIST ARCHITECTURE) endif() endif() +if ("riscv" IN_LIST ARCHITECTURE) + if ("A32" IN_LIST DYNARMIC_FRONTENDS) + target_sources(dynarmic PRIVATE + backend/riscv64/a32_address_space.cpp + backend/riscv64/a32_address_space.h + backend/riscv64/a32_core.h + backend/riscv64/a32_interface.cpp + backend/riscv64/a32_jitstate.cpp + backend/riscv64/a32_jitstate.h + backend/riscv64/dummy_code_block.h + ) + endif() + + if ("A64" IN_LIST DYNARMIC_FRONTENDS) + message(FATAL_ERROR "TODO: Unimplemented frontend for this host architecture") + endif() +endif() + if (WIN32) target_sources(dynarmic PRIVATE backend/exception_handler_windows.cpp) elseif (APPLE) diff --git a/src/dynarmic/backend/exception_handler.h b/src/dynarmic/backend/exception_handler.h index 179433ba..3e388a20 100644 --- a/src/dynarmic/backend/exception_handler.h +++ b/src/dynarmic/backend/exception_handler.h @@ -20,6 +20,10 @@ class BlockOfCode; namespace oaknut { class CodeBlock; } // namespace oaknut +#elif defined(MCL_ARCHITECTURE_RISCV) +namespace Dynarmic::Backend::RV64 { +class DummyCodeBlock; +} // namespace Dynarmic::Backend::RV64 #else # error "Invalid architecture" #endif @@ -35,6 +39,9 @@ struct FakeCall { struct FakeCall { u64 call_pc; }; +#elif defined(MCL_ARCHITECTURE_RISCV) +struct FakeCall { +}; #else # error "Invalid architecture" #endif @@ -48,6 +55,8 @@ public: void Register(X64::BlockOfCode& code); #elif defined(MCL_ARCHITECTURE_ARM64) void Register(oaknut::CodeBlock& mem, std::size_t mem_size); +#elif defined(MCL_ARCHITECTURE_RISCV) + void Register(RV64::DummyCodeBlock& mem, std::size_t mem_size); #else # error "Invalid architecture" #endif diff --git a/src/dynarmic/backend/exception_handler_generic.cpp b/src/dynarmic/backend/exception_handler_generic.cpp index bbaa071a..6c86c1ee 100644 --- a/src/dynarmic/backend/exception_handler_generic.cpp +++ b/src/dynarmic/backend/exception_handler_generic.cpp @@ -21,6 +21,10 @@ void ExceptionHandler::Register(X64::BlockOfCode&) { void ExceptionHandler::Register(oaknut::CodeBlock&, std::size_t) { // Do nothing } +#elif defined(MCL_ARCHITECTURE_RISCV) +void ExceptionHandler::Register(RV64::DummyCodeBlock&, std::size_t) { + // Do nothing +} #else # error "Invalid architecture" #endif diff --git a/src/dynarmic/backend/exception_handler_posix.cpp b/src/dynarmic/backend/exception_handler_posix.cpp index 0a1c270b..72a178f1 100644 --- a/src/dynarmic/backend/exception_handler_posix.cpp +++ b/src/dynarmic/backend/exception_handler_posix.cpp @@ -32,6 +32,8 @@ # include # include "dynarmic/backend/arm64/abi.h" +#elif defined(MCL_ARCHITECTURE_RISCV) +# include "dynarmic/backend/riscv64/dummy_code_block.h" #else # error "Invalid architecture" #endif @@ -141,9 +143,11 @@ void SigHandler::RemoveCodeBlock(u64 host_pc) { void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) { ASSERT(sig == SIGSEGV || sig == SIGBUS); +#ifndef MCL_ARCHITECTURE_RISCV ucontext_t* ucontext = reinterpret_cast(raw_context); -#ifndef __OpenBSD__ +# ifndef __OpenBSD__ auto& mctx = ucontext->uc_mcontext; +# endif #endif #if defined(MCL_ARCHITECTURE_X86_64) @@ -243,6 +247,10 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) { fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC); +#elif defined(MCL_ARCHITECTURE_RISCV) + + ASSERT_FALSE("Unimplemented"); + #else # error "Invalid architecture" @@ -304,6 +312,12 @@ void ExceptionHandler::Register(oaknut::CodeBlock& mem, std::size_t size) { const u64 code_end = code_begin + size; impl = std::make_unique(code_begin, code_end); } +#elif defined(MCL_ARCHITECTURE_RISCV) +void ExceptionHandler::Register(RV64::DummyCodeBlock& mem, std::size_t size) { + const u64 code_begin = mcl::bit_cast(mem.ptr()); + const u64 code_end = code_begin + size; + impl = std::make_unique(code_begin, code_end); +} #else # error "Invalid architecture" #endif diff --git a/src/dynarmic/backend/riscv64/a32_address_space.cpp b/src/dynarmic/backend/riscv64/a32_address_space.cpp new file mode 100644 index 00000000..6b96fa7a --- /dev/null +++ b/src/dynarmic/backend/riscv64/a32_address_space.cpp @@ -0,0 +1,63 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2024 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#include "dynarmic/backend/riscv64/a32_address_space.h" + +#include "dynarmic/frontend/A32/a32_location_descriptor.h" +#include "dynarmic/frontend/A32/translate/a32_translate.h" +#include "dynarmic/ir/opt/passes.h" + +namespace Dynarmic::Backend::RV64 { + +A32AddressSpace::A32AddressSpace(const A32::UserConfig& conf) + : conf(conf) { + EmitPrelude(); +} + +IR::Block A32AddressSpace::GenerateIR(IR::LocationDescriptor descriptor) const { + IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions}); + + Optimization::PolyfillPass(ir_block, {}); + if (conf.HasOptimization(OptimizationFlag::GetSetElimination)) { + Optimization::A32GetSetElimination(ir_block, {.convert_nzc_to_nz = true}); + Optimization::DeadCodeElimination(ir_block); + } + if (conf.HasOptimization(OptimizationFlag::ConstProp)) { + Optimization::A32ConstantMemoryReads(ir_block, conf.callbacks); + Optimization::ConstantPropagation(ir_block); + Optimization::DeadCodeElimination(ir_block); + } + Optimization::VerificationPass(ir_block); + + return ir_block; +} + +void* A32AddressSpace::Get(IR::LocationDescriptor descriptor) { + if (const auto iter = block_entries.find(descriptor.Value()); iter != block_entries.end()) { + return iter->second; + } + return nullptr; +} + +void* A32AddressSpace::GetOrEmit(IR::LocationDescriptor descriptor) { + if (void* block_entry = Get(descriptor)) { + return block_entry; + } + + IR::Block ir_block = GenerateIR(descriptor); + void* block_entry = Emit(std::move(ir_block)); + block_entries.insert_or_assign(descriptor.Value(), block_entry); + return block_entry; +} + +void A32AddressSpace::EmitPrelude() { + ASSERT_FALSE("Unimplemented"); +} + +void* A32AddressSpace::Emit(IR::Block) { + ASSERT_FALSE("Unimplemented"); +} + +} // namespace Dynarmic::Backend::RV64 diff --git a/src/dynarmic/backend/riscv64/a32_address_space.h b/src/dynarmic/backend/riscv64/a32_address_space.h new file mode 100644 index 00000000..6327821b --- /dev/null +++ b/src/dynarmic/backend/riscv64/a32_address_space.h @@ -0,0 +1,48 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2024 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#pragma once + +#include + +#include "dynarmic/interface/A32/config.h" +#include "dynarmic/interface/halt_reason.h" +#include "dynarmic/ir/basic_block.h" +#include "dynarmic/ir/location_descriptor.h" + +namespace Dynarmic::Backend::RV64 { + +struct A32JitState; + +class A32AddressSpace final { +public: + explicit A32AddressSpace(const A32::UserConfig& conf); + + IR::Block GenerateIR(IR::LocationDescriptor) const; + + void* Get(IR::LocationDescriptor descriptor); + + void* GetOrEmit(IR::LocationDescriptor descriptor); + +private: + friend class A32Core; + + void EmitPrelude(); + + void* Emit(IR::Block ir_block); + + const A32::UserConfig conf; + + tsl::robin_map block_entries; + + struct PreludeInfo { + u32* end_of_prelude; + + using RunCodeFuncType = HaltReason (*)(void* entry_point, A32JitState* context, volatile u32* halt_reason); + RunCodeFuncType run_code; + } prelude_info; +}; + +} // namespace Dynarmic::Backend::RV64 diff --git a/src/dynarmic/backend/riscv64/a32_core.h b/src/dynarmic/backend/riscv64/a32_core.h new file mode 100644 index 00000000..b01d650d --- /dev/null +++ b/src/dynarmic/backend/riscv64/a32_core.h @@ -0,0 +1,24 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2024 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#pragma once + +#include "dynarmic/backend/riscv64/a32_address_space.h" +#include "dynarmic/backend/riscv64/a32_jitstate.h" + +namespace Dynarmic::Backend::RV64 { + +class A32Core final { +public: + explicit A32Core(const A32::UserConfig&) {} + + HaltReason Run(A32AddressSpace& process, A32JitState& thread_ctx, volatile u32* halt_reason) { + const auto location_descriptor = thread_ctx.GetLocationDescriptor(); + const auto entry_point = process.GetOrEmit(location_descriptor); + return process.prelude_info.run_code(entry_point, &thread_ctx, halt_reason); + } +}; + +} // namespace Dynarmic::Backend::RV64 diff --git a/src/dynarmic/backend/riscv64/a32_interface.cpp b/src/dynarmic/backend/riscv64/a32_interface.cpp new file mode 100644 index 00000000..cd2a79ca --- /dev/null +++ b/src/dynarmic/backend/riscv64/a32_interface.cpp @@ -0,0 +1,217 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2024 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#include +#include + +#include +#include +#include +#include + +#include "dynarmic/backend/riscv64/a32_address_space.h" +#include "dynarmic/backend/riscv64/a32_core.h" +#include "dynarmic/backend/riscv64/a32_jitstate.h" +#include "dynarmic/common/atomic.h" +#include "dynarmic/interface/A32/a32.h" + +namespace Dynarmic::A32 { + +using namespace Backend::RV64; + +struct Jit::Impl final { + Impl(Jit* jit_interface, A32::UserConfig conf) + : jit_interface(jit_interface) + , conf(conf) + , current_address_space(conf) + , core(conf) {} + + HaltReason Run() { + ASSERT(!jit_interface->is_executing); + jit_interface->is_executing = true; + SCOPE_EXIT { + jit_interface->is_executing = false; + }; + + HaltReason hr = core.Run(current_address_space, current_state, &halt_reason); + + RequestCacheInvalidation(); + + return hr; + } + + HaltReason Step() { + ASSERT(!jit_interface->is_executing); + jit_interface->is_executing = true; + SCOPE_EXIT { + jit_interface->is_executing = false; + }; + + ASSERT_FALSE("Unimplemented"); + + RequestCacheInvalidation(); + + return HaltReason{}; + } + + void ClearCache() { + std::unique_lock lock{invalidation_mutex}; + invalidate_entire_cache = true; + HaltExecution(HaltReason::CacheInvalidation); + } + + void InvalidateCacheRange(std::uint32_t start_address, std::size_t length) { + std::unique_lock lock{invalidation_mutex}; + invalid_cache_ranges.add(boost::icl::discrete_interval::closed(start_address, static_cast(start_address + length - 1))); + HaltExecution(HaltReason::CacheInvalidation); + } + + void Reset() { + current_state = {}; + } + + void HaltExecution(HaltReason hr) { + Atomic::Or(&halt_reason, ~static_cast(hr)); + } + + void ClearHalt(HaltReason hr) { + Atomic::And(&halt_reason, ~static_cast(hr)); + } + + std::array& Regs() { + return current_state.regs; + } + + const std::array& Regs() const { + return current_state.regs; + } + + std::array& ExtRegs() { + return current_state.ext_regs; + } + + const std::array& ExtRegs() const { + return current_state.ext_regs; + } + + std::uint32_t Cpsr() const { + return current_state.Cpsr(); + } + + void SetCpsr(std::uint32_t value) { + current_state.SetCpsr(value); + } + + std::uint32_t Fpscr() const { + return current_state.Fpscr(); + } + + void SetFpscr(std::uint32_t value) { + current_state.SetFpscr(value); + } + + void ClearExclusiveState() { + current_state.exclusive_state = false; + } + + void DumpDisassembly() const { + ASSERT_FALSE("Unimplemented"); + } + +private: + void RequestCacheInvalidation() { + ASSERT_FALSE("Unimplemented"); + + invalidate_entire_cache = false; + invalid_cache_ranges.clear(); + } + + Jit* jit_interface; + A32::UserConfig conf; + A32JitState current_state{}; + A32AddressSpace current_address_space; + A32Core core; + + volatile u32 halt_reason = 0; + + std::mutex invalidation_mutex; + boost::icl::interval_set invalid_cache_ranges; + bool invalidate_entire_cache = false; +}; + +Jit::Jit(UserConfig conf) + : impl(std::make_unique(this, conf)) {} + +Jit::~Jit() = default; + +HaltReason Jit::Run() { + return impl->Run(); +} + +HaltReason Jit::Step() { + return impl->Step(); +} + +void Jit::ClearCache() { + impl->ClearCache(); +} + +void Jit::InvalidateCacheRange(std::uint32_t 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& Jit::Regs() { + return impl->Regs(); +} + +const std::array& Jit::Regs() const { + return impl->Regs(); +} + +std::array& Jit::ExtRegs() { + return impl->ExtRegs(); +} + +const std::array& Jit::ExtRegs() const { + return impl->ExtRegs(); +} + +std::uint32_t Jit::Cpsr() const { + return impl->Cpsr(); +} + +void Jit::SetCpsr(std::uint32_t value) { + impl->SetCpsr(value); +} + +std::uint32_t Jit::Fpscr() const { + return impl->Fpscr(); +} + +void Jit::SetFpscr(std::uint32_t value) { + impl->SetFpscr(value); +} + +void Jit::ClearExclusiveState() { + impl->ClearExclusiveState(); +} + +void Jit::DumpDisassembly() const { + impl->DumpDisassembly(); +} + +} // namespace Dynarmic::A32 diff --git a/src/dynarmic/backend/riscv64/a32_jitstate.cpp b/src/dynarmic/backend/riscv64/a32_jitstate.cpp new file mode 100644 index 00000000..70482d0a --- /dev/null +++ b/src/dynarmic/backend/riscv64/a32_jitstate.cpp @@ -0,0 +1,73 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2024 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#include "dynarmic/backend/riscv64/a32_jitstate.h" + +#include +#include + +namespace Dynarmic::Backend::RV64 { + +u32 A32JitState::Cpsr() const { + u32 cpsr = 0; + + // NZCV flags + cpsr |= cpsr_nzcv; + // Q flag + cpsr |= cpsr_q; + // GE flags + cpsr |= mcl::bit::get_bit<31>(cpsr_ge) ? 1 << 19 : 0; + cpsr |= mcl::bit::get_bit<23>(cpsr_ge) ? 1 << 18 : 0; + cpsr |= mcl::bit::get_bit<15>(cpsr_ge) ? 1 << 17 : 0; + cpsr |= mcl::bit::get_bit<7>(cpsr_ge) ? 1 << 16 : 0; + // E flag, T flag + cpsr |= mcl::bit::get_bit<1>(upper_location_descriptor) ? 1 << 9 : 0; + cpsr |= mcl::bit::get_bit<0>(upper_location_descriptor) ? 1 << 5 : 0; + // IT state + cpsr |= static_cast(upper_location_descriptor & 0b11111100'00000000); + cpsr |= static_cast(upper_location_descriptor & 0b00000011'00000000) << 17; + // Other flags + cpsr |= cpsr_jaifm; + + return cpsr; +} + +void A32JitState::SetCpsr(u32 cpsr) { + // NZCV flags + cpsr_nzcv = cpsr & 0xF0000000; + // Q flag + cpsr_q = cpsr & (1 << 27); + // GE flags + cpsr_ge = 0; + cpsr_ge |= mcl::bit::get_bit<19>(cpsr) ? 0xFF000000 : 0; + cpsr_ge |= mcl::bit::get_bit<18>(cpsr) ? 0x00FF0000 : 0; + cpsr_ge |= mcl::bit::get_bit<17>(cpsr) ? 0x0000FF00 : 0; + cpsr_ge |= mcl::bit::get_bit<16>(cpsr) ? 0x000000FF : 0; + + upper_location_descriptor &= 0xFFFF0000; + // E flag, T flag + upper_location_descriptor |= mcl::bit::get_bit<9>(cpsr) ? 2 : 0; + upper_location_descriptor |= mcl::bit::get_bit<5>(cpsr) ? 1 : 0; + // IT state + upper_location_descriptor |= (cpsr >> 0) & 0b11111100'00000000; + upper_location_descriptor |= (cpsr >> 17) & 0b00000011'00000000; + + // Other flags + cpsr_jaifm = cpsr & 0x010001DF; +} + +constexpr u32 FPCR_MASK = A32::LocationDescriptor::FPSCR_MODE_MASK; +constexpr u32 FPSR_MASK = 0xF800009F; + +u32 A32JitState::Fpscr() const { + return (upper_location_descriptor & 0xffff0000) | fpsr; +} + +void A32JitState::SetFpscr(u32 fpscr) { + fpsr = fpscr & FPSR_MASK; + upper_location_descriptor = (upper_location_descriptor & 0x0000ffff) | (fpscr & FPCR_MASK); +} + +} // namespace Dynarmic::Backend::RV64 diff --git a/src/dynarmic/backend/riscv64/a32_jitstate.h b/src/dynarmic/backend/riscv64/a32_jitstate.h new file mode 100644 index 00000000..9bf68340 --- /dev/null +++ b/src/dynarmic/backend/riscv64/a32_jitstate.h @@ -0,0 +1,45 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2024 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#pragma once + +#include + +#include + +#include "dynarmic/frontend/A32/a32_location_descriptor.h" +#include "dynarmic/ir/location_descriptor.h" + +namespace Dynarmic::Backend::RV64 { + +struct A32JitState { + u32 cpsr_nzcv = 0; + u32 cpsr_q = 0; + u32 cpsr_jaifm = 0; + u32 cpsr_ge = 0; + + u32 fpsr = 0; + u32 fpsr_nzcv = 0; + + std::array regs{}; + + u32 upper_location_descriptor; + + alignas(16) std::array ext_regs{}; + + u32 exclusive_state = 0; + + u32 Cpsr() const; + void SetCpsr(u32 cpsr); + + u32 Fpscr() const; + void SetFpscr(u32 fpscr); + + IR::LocationDescriptor GetLocationDescriptor() const { + return IR::LocationDescriptor{regs[15] | (static_cast(upper_location_descriptor) << 32)}; + } +}; + +} // namespace Dynarmic::Backend::RV64 diff --git a/src/dynarmic/backend/riscv64/dummy_code_block.h b/src/dynarmic/backend/riscv64/dummy_code_block.h new file mode 100644 index 00000000..208c956f --- /dev/null +++ b/src/dynarmic/backend/riscv64/dummy_code_block.h @@ -0,0 +1,16 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2024 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#pragma once + +namespace Dynarmic::Backend::RV64 { + +class DummyCodeBlock { +public: + DummyCodeBlock() {} + + void* ptr() { return nullptr; } +}; +} // namespace Dynarmic::Backend::RV64