[dynarmic] reuse IR::Block memory instead of creating new block each time

Signed-off-by: lizzie <lizzie@eden-emu.dev>
This commit is contained in:
lizzie 2026-02-08 20:57:24 +00:00 committed by crueter
parent 866881d0e3
commit 710bbe0b0d
9 changed files with 46 additions and 48 deletions

View File

@ -25,6 +25,7 @@
#include "dynarmic/backend/x64/devirtualize.h"
#include "dynarmic/backend/x64/jitstate_info.h"
#include "dynarmic/common/atomic.h"
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
#include "dynarmic/frontend/A32/translate/a32_translate.h"
#include "dynarmic/interface/A32/a32.h"
#include "dynarmic/ir/basic_block.h"
@ -63,12 +64,14 @@ static Optimization::PolyfillOptions GenPolyfillOptions(const BlockOfCode& code)
}
struct Jit::Impl {
Impl(Jit* jit, A32::UserConfig conf)
: block_of_code(GenRunCodeCallbacks(conf.callbacks, &GetCurrentBlockThunk, this, conf), JitStateInfo{jit_state}, conf.code_cache_size, GenRCP(conf))
, emitter(block_of_code, conf, jit)
, polyfill_options(GenPolyfillOptions(block_of_code))
, conf(std::move(conf))
, jit_interface(jit) {}
Impl(Jit* jit, A32::UserConfig conf) noexcept
: ir_block{LocationDescriptor(0, PSR(0), FPSCR(0), false)}
, conf(std::move(conf))
, block_of_code(GenRunCodeCallbacks(conf.callbacks, &GetCurrentBlockThunk, this, conf), JitStateInfo{jit_state}, conf.code_cache_size, GenRCP(conf))
, emitter(block_of_code, conf, jit)
, polyfill_options(GenPolyfillOptions(block_of_code))
, jit_interface(jit)
{}
~Impl() = default;
@ -212,7 +215,8 @@ private:
}
block_of_code.EnsureMemoryCommitted(MINIMUM_REMAINING_CODESIZE);
IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
ir_block.Reset(descriptor);
A32::Translate(ir_block, A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
Optimization::Optimize(ir_block, conf, polyfill_options);
return emitter.Emit(ir_block);
}
@ -239,13 +243,12 @@ private:
}
}
IR::Block ir_block;
const A32::UserConfig conf;
A32JitState jit_state;
BlockOfCode block_of_code;
A32EmitX64 emitter;
Optimization::PolyfillOptions polyfill_options;
const A32::UserConfig conf;
Jit* jit_interface;
// Requests made during execution to invalidate the cache are queued up here.

View File

@ -12,6 +12,7 @@
#include <boost/icl/interval_set.hpp>
#include "dynarmic/common/assert.h"
#include "dynarmic/common/fp/fpcr.h"
#include "dynarmic/common/llvm_disassemble.h"
#include <bit>
#include <mcl/scope_exit.hpp>
@ -61,10 +62,12 @@ static Optimization::PolyfillOptions GenPolyfillOptions(const BlockOfCode& code)
struct Jit::Impl final {
public:
Impl(Jit* jit, UserConfig conf)
: conf(conf)
, block_of_code(GenRunCodeCallbacks(conf.callbacks, &GetCurrentBlockThunk, this, conf), JitStateInfo{jit_state}, conf.code_cache_size, GenRCP(conf))
, emitter(block_of_code, conf, jit)
, polyfill_options(GenPolyfillOptions(block_of_code)) {
: ir_block{LocationDescriptor(0, FP::FPCR(0), false)}
, conf(conf)
, block_of_code(GenRunCodeCallbacks(conf.callbacks, &GetCurrentBlockThunk, this, conf), JitStateInfo{jit_state}, conf.code_cache_size, GenRCP(conf))
, emitter(block_of_code, conf, jit)
, polyfill_options(GenPolyfillOptions(block_of_code))
{
ASSERT(conf.page_table_address_space_bits >= 12 && conf.page_table_address_space_bits <= 64);
}
@ -268,8 +271,8 @@ private:
// JIT Compile
const auto get_code = [this](u64 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); };
IR::Block ir_block = A64::Translate(A64::LocationDescriptor{current_location}, get_code,
{conf.define_unpredictable_behaviour, conf.wall_clock_cntpct});
ir_block.Reset(current_location);
A64::Translate(ir_block, A64::LocationDescriptor{current_location}, get_code, {conf.define_unpredictable_behaviour, conf.wall_clock_cntpct});
Optimization::Optimize(ir_block, conf, polyfill_options);
return emitter.Emit(ir_block).entrypoint;
}
@ -296,14 +299,13 @@ private:
}
}
bool is_executing = false;
IR::Block ir_block;
const UserConfig conf;
A64JitState jit_state;
BlockOfCode block_of_code;
A64EmitX64 emitter;
Optimization::PolyfillOptions polyfill_options;
bool is_executing = false;
bool invalidate_entire_cache = false;
boost::icl::interval_set<u64> invalid_cache_ranges;
std::mutex invalidation_mutex;

View File

@ -10,11 +10,11 @@
namespace Dynarmic::A32 {
IR::Block TranslateArm(LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options);
IR::Block TranslateThumb(LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options);
void TranslateArm(IR::Block& block, LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options);
void TranslateThumb(IR::Block& block, LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options);
IR::Block Translate(LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options) {
return (descriptor.TFlag() ? TranslateThumb : TranslateArm)(descriptor, tcb, options);
void Translate(IR::Block& block, LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options) {
return (descriptor.TFlag() ? TranslateThumb : TranslateArm)(block, descriptor, tcb, options);
}
bool TranslateSingleArmInstruction(IR::Block& block, LocationDescriptor descriptor, u32 instruction);

View File

@ -41,7 +41,7 @@ struct TranslationOptions {
* @param options Configures how certain instructions are translated.
* @return A translated basic block in the intermediate representation.
*/
IR::Block Translate(LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options);
void Translate(IR::Block& block, LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options);
/**
* This function translates a single provided instruction into our intermediate representation.

View File

@ -22,12 +22,9 @@
namespace Dynarmic::A32 {
IR::Block TranslateArm(LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options) {
void TranslateArm(IR::Block& block, LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options) {
const bool single_step = descriptor.SingleStepping();
IR::Block block{descriptor};
TranslatorVisitor visitor{block, descriptor, options};
bool should_continue = true;
do {
const u32 arm_pc = visitor.ir.current_location.PC();
@ -76,12 +73,8 @@ IR::Block TranslateArm(LocationDescriptor descriptor, TranslateCallbacks* tcb, c
}
}
}
ASSERT(block.HasTerminal() && "Terminal has not been set");
block.SetEndLocation(visitor.ir.current_location);
return block;
}
bool TranslateSingleArmInstruction(IR::Block& block, LocationDescriptor descriptor, u32 arm_instruction) {

View File

@ -24,6 +24,7 @@
#include "dynarmic/frontend/A32/translate/translate_callbacks.h"
#include "dynarmic/frontend/imm.h"
#include "dynarmic/interface/A32/config.h"
#include "dynarmic/ir/basic_block.h"
namespace Dynarmic::A32 {
namespace {
@ -103,12 +104,9 @@ inline bool MaybeVFPOrASIMDInstruction(u32 thumb_instruction) noexcept {
} // namespace
IR::Block TranslateThumb(LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options) {
void TranslateThumb(IR::Block& block, LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options) {
const bool single_step = descriptor.SingleStepping();
IR::Block block{descriptor};
TranslatorVisitor visitor{block, descriptor, options};
bool should_continue = true;
do {
const u32 arm_pc = visitor.ir.current_location.PC();
@ -175,12 +173,8 @@ IR::Block TranslateThumb(LocationDescriptor descriptor, TranslateCallbacks* tcb,
}
}
}
ASSERT(block.HasTerminal() && "Terminal has not been set");
block.SetEndLocation(visitor.ir.current_location);
return block;
}
bool TranslateSingleThumbInstruction(IR::Block& block, LocationDescriptor descriptor, u32 thumb_instruction) {

View File

@ -16,10 +16,8 @@
namespace Dynarmic::A64 {
IR::Block Translate(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, TranslationOptions options) {
void Translate(IR::Block& block, LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, TranslationOptions options) {
const bool single_step = descriptor.SingleStepping();
IR::Block block{descriptor};
TranslatorVisitor visitor{block, descriptor, std::move(options)};
bool should_continue = true;
@ -43,12 +41,8 @@ IR::Block Translate(LocationDescriptor descriptor, MemoryReadCodeFuncType memory
if (single_step && should_continue) {
visitor.ir.SetTerm(IR::Term::LinkBlock{*visitor.ir.current_location});
}
ASSERT(block.HasTerminal() && "Terminal has not been set");
block.SetEndLocation(*visitor.ir.current_location);
return block;
}
bool TranslateSingleInstruction(IR::Block& block, LocationDescriptor descriptor, u32 instruction) {

View File

@ -47,7 +47,7 @@ struct TranslationOptions {
* @param options Configures how certain instructions are translated.
* @return A translated basic block in the intermediate representation.
*/
IR::Block Translate(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, TranslationOptions options);
void Translate(IR::Block& block, LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, TranslationOptions options);
/**
* This function translates a single provided instruction into our intermediate representation.

View File

@ -140,6 +140,18 @@ public:
return cycle_count;
}
inline void Reset(LocationDescriptor location_) noexcept {
inlined_inst.clear();
pooled_inst.clear();
cond_failed.reset();
location = location_;
terminal = Term::Invalid{};
cond_failed_cycle_count = 0;
cycle_count = 0;
instruction_list_type tmp{};
instructions.swap(tmp);
}
/// "Hot cache" for small blocks so we don't call global allocator
boost::container::static_vector<Inst, 30> inlined_inst;
/// List of instructions in this block.