mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-02-04 02:51:18 +01:00
[dynarmic] reduce CPU usage on Spooky Mansion by making blocks be page-sized and page-aligned; replace std::set<> with ankerl set in arm64 (#3253)
reduces CPU usage by about 10-20%, may be placebo Signed-off-by: lizzie lizzie@eden-emu.dev Co-authored-by: Caio Oliveira <caiooliveirafarias0@gmail.com> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3253 Reviewed-by: Maufeat <sahyno1996@gmail.com> Reviewed-by: DraVee <dravee@eden-emu.dev> Reviewed-by: MaranBr <maranbr@eden-emu.dev> Co-authored-by: lizzie <lizzie@eden-emu.dev> Co-committed-by: lizzie <lizzie@eden-emu.dev>
This commit is contained in:
@@ -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.
|
||||
@@ -218,7 +218,7 @@ EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block, const E
|
||||
code.l(pass);
|
||||
}
|
||||
|
||||
for (auto iter = block.begin(); iter != block.end(); ++iter) {
|
||||
for (auto iter = block.instructions.begin(); iter != block.instructions.end(); ++iter) {
|
||||
IR::Inst* inst = &*iter;
|
||||
|
||||
switch (inst->GetOpcode()) {
|
||||
|
||||
@@ -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.
|
||||
@@ -582,11 +582,9 @@ void EmitIR<IR::Opcode::A32BXWritePC>(oaknut::CodeGenerator& code, EmitContext&
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32UpdateUpperLocationDescriptor>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst*) {
|
||||
for (auto& inst : ctx.block) {
|
||||
if (inst.GetOpcode() == IR::Opcode::A32BXWritePC) {
|
||||
for (auto& inst : ctx.block.instructions)
|
||||
if (inst.GetOpcode() == IR::Opcode::A32BXWritePC)
|
||||
return;
|
||||
}
|
||||
}
|
||||
EmitSetUpperLocationDescriptor(code, ctx, ctx.block.EndLocation(), ctx.block.Location());
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -193,7 +193,6 @@ void RegAlloc::PrepareForCall(std::optional<Argument::copyable_reference> arg0,
|
||||
|
||||
void RegAlloc::DefineAsExisting(IR::Inst* inst, Argument& arg) {
|
||||
defined_insts.insert(inst);
|
||||
|
||||
ASSERT(!ValueLocation(inst));
|
||||
|
||||
if (arg.value.IsImmediate()) {
|
||||
@@ -208,7 +207,6 @@ void RegAlloc::DefineAsExisting(IR::Inst* inst, Argument& arg) {
|
||||
|
||||
void RegAlloc::DefineAsRegister(IR::Inst* inst, oaknut::Reg reg) {
|
||||
defined_insts.insert(inst);
|
||||
|
||||
ASSERT(!ValueLocation(inst));
|
||||
auto& info = reg.is_vector() ? fprs[reg.index()] : gprs[reg.index()];
|
||||
ASSERT(info.IsCompletelyEmpty());
|
||||
@@ -375,7 +373,6 @@ int RegAlloc::RealizeReadImpl(const IR::Value& value) {
|
||||
template<HostLoc::Kind kind>
|
||||
int RegAlloc::RealizeWriteImpl(const IR::Inst* value) {
|
||||
defined_insts.insert(value);
|
||||
|
||||
ASSERT(!ValueLocation(value));
|
||||
|
||||
if constexpr (kind == HostLoc::Kind::Gpr) {
|
||||
@@ -400,7 +397,6 @@ int RegAlloc::RealizeWriteImpl(const IR::Inst* value) {
|
||||
template<HostLoc::Kind kind>
|
||||
int RegAlloc::RealizeReadWriteImpl(const IR::Value& read_value, const IR::Inst* write_value) {
|
||||
defined_insts.insert(write_value);
|
||||
|
||||
// TODO: Move elimination
|
||||
|
||||
const int write_loc = RealizeWriteImpl<kind>(write_value);
|
||||
@@ -464,7 +460,6 @@ void RegAlloc::SpillFpr(int index) {
|
||||
|
||||
void RegAlloc::ReadWriteFlags(Argument& read, IR::Inst* write) {
|
||||
defined_insts.insert(write);
|
||||
|
||||
const auto current_location = ValueLocation(read.value.GetInst());
|
||||
ASSERT(current_location);
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -302,17 +302,12 @@ public:
|
||||
|
||||
private:
|
||||
friend struct Argument;
|
||||
template<typename>
|
||||
friend struct RAReg;
|
||||
template<typename> friend struct RAReg;
|
||||
|
||||
template<HostLoc::Kind kind>
|
||||
int GenerateImmediate(const IR::Value& value);
|
||||
template<HostLoc::Kind kind>
|
||||
int RealizeReadImpl(const IR::Value& value);
|
||||
template<HostLoc::Kind kind>
|
||||
int RealizeWriteImpl(const IR::Inst* value);
|
||||
template<HostLoc::Kind kind>
|
||||
int RealizeReadWriteImpl(const IR::Value& read_value, const IR::Inst* write_value);
|
||||
template<HostLoc::Kind kind> int GenerateImmediate(const IR::Value& value);
|
||||
template<HostLoc::Kind kind> int RealizeReadImpl(const IR::Value& value);
|
||||
template<HostLoc::Kind kind> int RealizeWriteImpl(const IR::Inst* value);
|
||||
template<HostLoc::Kind kind> int RealizeReadWriteImpl(const IR::Value& read_value, const IR::Inst* write_value);
|
||||
|
||||
int AllocateRegister(const std::array<HostLocInfo, 32>& regs, const std::vector<int>& order) const;
|
||||
void SpillGpr(int index);
|
||||
@@ -337,7 +332,6 @@ private:
|
||||
std::array<HostLocInfo, SpillCount> spills;
|
||||
|
||||
mutable std::mt19937 rand_gen;
|
||||
|
||||
ankerl::unordered_dense::set<const IR::Inst*> defined_insts;
|
||||
};
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -15,26 +15,24 @@
|
||||
|
||||
namespace Dynarmic::Backend {
|
||||
|
||||
template<typename ProgramCounterType>
|
||||
void BlockRangeInformation<ProgramCounterType>::AddRange(boost::icl::discrete_interval<ProgramCounterType> range, IR::LocationDescriptor location) {
|
||||
block_ranges.add(std::make_pair(range, std::set<IR::LocationDescriptor>{location}));
|
||||
template<typename P>
|
||||
void BlockRangeInformation<P>::AddRange(boost::icl::discrete_interval<P> range, IR::LocationDescriptor location) {
|
||||
block_ranges.add(std::make_pair(range, ankerl::unordered_dense::set<IR::LocationDescriptor>{location}));
|
||||
}
|
||||
|
||||
template<typename ProgramCounterType>
|
||||
void BlockRangeInformation<ProgramCounterType>::ClearCache() {
|
||||
template<typename P>
|
||||
void BlockRangeInformation<P>::ClearCache() {
|
||||
block_ranges.clear();
|
||||
}
|
||||
|
||||
template<typename ProgramCounterType>
|
||||
ankerl::unordered_dense::set<IR::LocationDescriptor> BlockRangeInformation<ProgramCounterType>::InvalidateRanges(const boost::icl::interval_set<ProgramCounterType>& ranges) {
|
||||
template<typename P>
|
||||
ankerl::unordered_dense::set<IR::LocationDescriptor> BlockRangeInformation<P>::InvalidateRanges(const boost::icl::interval_set<P>& ranges) {
|
||||
ankerl::unordered_dense::set<IR::LocationDescriptor> erase_locations;
|
||||
for (auto invalidate_interval : ranges) {
|
||||
auto pair = block_ranges.equal_range(invalidate_interval);
|
||||
for (auto it = pair.first; it != pair.second; ++it) {
|
||||
for (const auto& descriptor : it->second) {
|
||||
for (auto it = pair.first; it != pair.second; ++it)
|
||||
for (const auto& descriptor : it->second)
|
||||
erase_locations.insert(descriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: EFFICIENCY: Remove ranges that are to be erased.
|
||||
return erase_locations;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2018 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
@@ -15,15 +18,13 @@
|
||||
|
||||
namespace Dynarmic::Backend {
|
||||
|
||||
template<typename ProgramCounterType>
|
||||
template<typename P>
|
||||
class BlockRangeInformation {
|
||||
public:
|
||||
void AddRange(boost::icl::discrete_interval<ProgramCounterType> range, IR::LocationDescriptor location);
|
||||
void AddRange(boost::icl::discrete_interval<P> range, IR::LocationDescriptor location);
|
||||
void ClearCache();
|
||||
ankerl::unordered_dense::set<IR::LocationDescriptor> InvalidateRanges(const boost::icl::interval_set<ProgramCounterType>& ranges);
|
||||
|
||||
private:
|
||||
boost::icl::interval_map<ProgramCounterType, std::set<IR::LocationDescriptor>> block_ranges;
|
||||
ankerl::unordered_dense::set<IR::LocationDescriptor> InvalidateRanges(const boost::icl::interval_set<P>& ranges);
|
||||
boost::icl::interval_map<P, ankerl::unordered_dense::set<IR::LocationDescriptor>> block_ranges;
|
||||
};
|
||||
|
||||
} // namespace Dynarmic::Backend
|
||||
|
||||
@@ -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.
|
||||
@@ -122,7 +122,7 @@ EmittedBlockInfo EmitRV64(biscuit::Assembler& as, IR::Block block, const EmitCon
|
||||
|
||||
ebi.entry_point = reinterpret_cast<CodePtr>(as.GetCursorPointer());
|
||||
|
||||
for (auto iter = block.begin(); iter != block.end(); ++iter) {
|
||||
for (auto iter = block.instructions.begin(); iter != block.instructions.end(); ++iter) {
|
||||
IR::Inst* inst = &*iter;
|
||||
|
||||
switch (inst->GetOpcode()) {
|
||||
|
||||
@@ -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.
|
||||
@@ -123,7 +123,7 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
|
||||
|
||||
EmitCondPrelude(ctx);
|
||||
|
||||
for (auto iter = block.begin(); iter != block.end(); ++iter) [[likely]] {
|
||||
for (auto iter = block.instructions.begin(); iter != block.instructions.end(); ++iter) [[likely]] {
|
||||
auto* inst = &*iter;
|
||||
// Call the relevant Emit* member function.
|
||||
switch (inst->GetOpcode()) {
|
||||
@@ -727,11 +727,9 @@ void A32EmitX64::EmitA32BXWritePC(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32UpdateUpperLocationDescriptor(A32EmitContext& ctx, IR::Inst*) {
|
||||
for (auto& inst : ctx.block) {
|
||||
if (inst.GetOpcode() == IR::Opcode::A32BXWritePC) {
|
||||
for (auto& inst : ctx.block.instructions)
|
||||
if (inst.GetOpcode() == IR::Opcode::A32BXWritePC)
|
||||
return;
|
||||
}
|
||||
}
|
||||
EmitSetUpperLocationDescriptor(ctx.EndLocation(), ctx.Location());
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -116,7 +116,7 @@ A64EmitX64::BlockDescriptor A64EmitX64::Emit(IR::Block& block) noexcept {
|
||||
#undef A64OPC
|
||||
};
|
||||
|
||||
for (auto& inst : block) {
|
||||
for (auto& inst : block.instructions) {
|
||||
auto const opcode = inst.GetOpcode();
|
||||
// Call the relevant Emit* member function.
|
||||
switch (opcode) {
|
||||
|
||||
@@ -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.
|
||||
@@ -38,11 +38,6 @@ EmitContext::EmitContext(RegAlloc& reg_alloc, IR::Block& block)
|
||||
|
||||
EmitContext::~EmitContext() = default;
|
||||
|
||||
void EmitContext::EraseInstruction(IR::Inst* inst) {
|
||||
block.Instructions().erase(inst);
|
||||
inst->ClearArgs();
|
||||
}
|
||||
|
||||
EmitX64::EmitX64(BlockOfCode& code)
|
||||
: code(code) {
|
||||
exception_handler.Register(code);
|
||||
|
||||
@@ -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.
|
||||
@@ -56,11 +56,7 @@ using HalfVectorArray = std::array<T, A64FullVectorWidth::value / mcl::bitsizeof
|
||||
struct EmitContext {
|
||||
EmitContext(RegAlloc& reg_alloc, IR::Block& block);
|
||||
virtual ~EmitContext();
|
||||
|
||||
void EraseInstruction(IR::Inst* inst);
|
||||
|
||||
virtual FP::FPCR FPCR(bool fpcr_controlled = true) const = 0;
|
||||
|
||||
virtual bool HasOptimization(OptimizationFlag flag) const = 0;
|
||||
|
||||
RegAlloc& reg_alloc;
|
||||
|
||||
@@ -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.
|
||||
@@ -34,14 +34,7 @@ namespace Dynarmic::Backend::X64 {
|
||||
|
||||
using namespace Xbyak::util;
|
||||
|
||||
#define ICODE(NAME) \
|
||||
[&code](auto... args) { \
|
||||
if constexpr (esize == 32) { \
|
||||
code.NAME##d(args...); \
|
||||
} else { \
|
||||
code.NAME##q(args...); \
|
||||
} \
|
||||
}
|
||||
#define ICODE(NAME) [&](auto... args) { if (esize == 32) code.NAME##d(args...); else code.NAME##q(args...); }
|
||||
|
||||
template<typename Function>
|
||||
static void EmitVectorOperation(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst, Function fn) {
|
||||
|
||||
@@ -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.
|
||||
@@ -26,7 +26,7 @@ bool CondCanContinue(const ConditionalState cond_state, const A32::IREmitter& ir
|
||||
return true;
|
||||
|
||||
// TODO: This is more conservative than necessary.
|
||||
return std::all_of(ir.block.begin(), ir.block.end(), [](const IR::Inst& inst) {
|
||||
return std::all_of(ir.block.instructions.begin(), ir.block.instructions.end(), [](const IR::Inst& inst) {
|
||||
return !WritesToCPSR(inst.GetOpcode());
|
||||
});
|
||||
}
|
||||
@@ -66,7 +66,7 @@ bool IsConditionPassed(TranslatorVisitor& v, IR::Cond cond) {
|
||||
|
||||
// non-AL cond
|
||||
|
||||
if (!v.ir.block.empty()) {
|
||||
if (!v.ir.block.instructions.empty()) {
|
||||
// We've already emitted instructions. Quit for now, we'll make a new block here later.
|
||||
v.cond_state = ConditionalState::Break;
|
||||
v.ir.SetTerm(IR::Term::LinkBlockFast{v.ir.current_location});
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2018 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
@@ -126,7 +129,7 @@ bool TranslatorVisitor::MRS(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3
|
||||
return true;
|
||||
case SystemRegisterEncoding::CNTPCT_EL0:
|
||||
// HACK: Ensure that this is the first instruction in the block it's emitted in, so the cycle count is most up-to-date.
|
||||
if (!ir.block.empty() && !options.wall_clock_cntpct) {
|
||||
if (!ir.block.instructions.empty() && !options.wall_clock_cntpct) {
|
||||
ir.block.CycleCount()--;
|
||||
ir.SetTerm(IR::Term::LinkBlock{*ir.current_location});
|
||||
return false;
|
||||
|
||||
@@ -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.
|
||||
@@ -100,57 +100,39 @@ std::string DumpBlock(const IR::Block& block) noexcept {
|
||||
std::string ret = fmt::format("Block: location={}-{}\n", block.Location(), block.EndLocation())
|
||||
+ fmt::format("cycles={}", block.CycleCount())
|
||||
+ fmt::format(", entry_cond={}", A64::CondToString(block.GetCondition()));
|
||||
if (block.GetCondition() != Cond::AL) {
|
||||
if (block.GetCondition() != Cond::AL)
|
||||
ret += fmt::format(", cond_fail={}", block.ConditionFailedLocation());
|
||||
}
|
||||
ret += '\n';
|
||||
|
||||
const auto arg_to_string = [](const IR::Value& arg) -> std::string {
|
||||
if (arg.IsEmpty()) {
|
||||
return "<null>";
|
||||
} else if (!arg.IsImmediate()) {
|
||||
if (const unsigned name = arg.GetInst()->GetName()) {
|
||||
if (auto const name = arg.GetInst()->GetName())
|
||||
return fmt::format("%{}", name);
|
||||
}
|
||||
return fmt::format("%<unnamed inst {:016x}>", reinterpret_cast<u64>(arg.GetInst()));
|
||||
return fmt::format("%<unnamed inst {:016x}>", u64(arg.GetInst()));
|
||||
}
|
||||
switch (arg.GetType()) {
|
||||
case Type::U1:
|
||||
return fmt::format("#{}", arg.GetU1() ? '1' : '0');
|
||||
case Type::U8:
|
||||
return fmt::format("#{}", arg.GetU8());
|
||||
case Type::U16:
|
||||
return fmt::format("#{:#x}", arg.GetU16());
|
||||
case Type::U32:
|
||||
return fmt::format("#{:#x}", arg.GetU32());
|
||||
case Type::U64:
|
||||
return fmt::format("#{:#x}", arg.GetU64());
|
||||
case Type::U128:
|
||||
return fmt::format("#<u128 imm>");
|
||||
case Type::A32Reg:
|
||||
return A32::RegToString(arg.GetA32RegRef());
|
||||
case Type::A32ExtReg:
|
||||
return A32::ExtRegToString(arg.GetA32ExtRegRef());
|
||||
case Type::A64Reg:
|
||||
return A64::RegToString(arg.GetA64RegRef());
|
||||
case Type::A64Vec:
|
||||
return A64::VecToString(arg.GetA64VecRef());
|
||||
case Type::CoprocInfo:
|
||||
return fmt::format("#<coproc>");
|
||||
case Type::NZCVFlags:
|
||||
return fmt::format("#<NZCV flags>");
|
||||
case Type::Cond:
|
||||
return fmt::format("#<cond={}>", A32::CondToString(arg.GetCond()));
|
||||
case Type::Table:
|
||||
return fmt::format("#<table>");
|
||||
case Type::AccType:
|
||||
return fmt::format("#<acc-type={}>", u32(arg.GetAccType()));
|
||||
default:
|
||||
return fmt::format("<unknown immediate type {}>", arg.GetType());
|
||||
case Type::U1: return fmt::format("#{}", arg.GetU1() ? '1' : '0');
|
||||
case Type::U8: return fmt::format("#{}", arg.GetU8());
|
||||
case Type::U16: return fmt::format("#{:#x}", arg.GetU16());
|
||||
case Type::U32: return fmt::format("#{:#x}", arg.GetU32());
|
||||
case Type::U64: return fmt::format("#{:#x}", arg.GetU64());
|
||||
case Type::U128: return fmt::format("#<u128 imm>");
|
||||
case Type::A32Reg: return A32::RegToString(arg.GetA32RegRef());
|
||||
case Type::A32ExtReg: return A32::ExtRegToString(arg.GetA32ExtRegRef());
|
||||
case Type::A64Reg: return A64::RegToString(arg.GetA64RegRef());
|
||||
case Type::A64Vec: return A64::VecToString(arg.GetA64VecRef());
|
||||
case Type::CoprocInfo: return fmt::format("#<coproc>");
|
||||
case Type::NZCVFlags: return fmt::format("#<NZCV flags>");
|
||||
case Type::Cond: return fmt::format("#<cond={}>", A32::CondToString(arg.GetCond()));
|
||||
case Type::Table: return fmt::format("#<table>");
|
||||
case Type::AccType: return fmt::format("#<acc-type={}>", u32(arg.GetAccType()));
|
||||
default: return fmt::format("<unknown immediate type {}>", arg.GetType());
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto& inst : block) {
|
||||
for (const auto& inst : block.instructions) {
|
||||
const Opcode op = inst.GetOpcode();
|
||||
|
||||
ret += fmt::format("[{:016x}] ", reinterpret_cast<u64>(&inst));
|
||||
@@ -180,13 +162,9 @@ std::string DumpBlock(const IR::Block& block) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
ret += fmt::format(" (uses: {})", inst.UseCount());
|
||||
|
||||
ret += '\n';
|
||||
ret += fmt::format(" (uses: {})", inst.UseCount()) + '\n';
|
||||
}
|
||||
|
||||
ret += "terminal = " + TerminalToString(block.GetTerminal()) + '\n';
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "dynarmic/ir/microinstruction.h"
|
||||
#include "dynarmic/ir/terminal.h"
|
||||
#include "dynarmic/ir/value.h"
|
||||
#include "dynarmic/ir/dense_list.h"
|
||||
|
||||
namespace Dynarmic::IR {
|
||||
|
||||
@@ -34,7 +33,7 @@ enum class Opcode;
|
||||
/// Note that this is a linear IR and not a pure tree-based IR: i.e.: there is an ordering to
|
||||
/// the microinstructions. This only matters before chaining is done in order to correctly
|
||||
/// order memory accesses.
|
||||
class Block final {
|
||||
class alignas(4096) Block final {
|
||||
public:
|
||||
//using instruction_list_type = dense_list<Inst>;
|
||||
using instruction_list_type = mcl::intrusive_list<Inst>;
|
||||
@@ -51,37 +50,12 @@ public:
|
||||
Block(Block&&) = default;
|
||||
Block& operator=(Block&&) = default;
|
||||
|
||||
bool empty() const { return instructions.empty(); }
|
||||
size_type size() const { return instructions.size(); }
|
||||
|
||||
Inst& front() { return instructions.front(); }
|
||||
const Inst& front() const { return instructions.front(); }
|
||||
|
||||
Inst& back() { return instructions.back(); }
|
||||
const Inst& back() const { return instructions.back(); }
|
||||
|
||||
iterator begin() { return instructions.begin(); }
|
||||
const_iterator begin() const { return instructions.begin(); }
|
||||
iterator end() { return instructions.end(); }
|
||||
const_iterator end() const { return instructions.end(); }
|
||||
|
||||
reverse_iterator rbegin() { return instructions.rbegin(); }
|
||||
const_reverse_iterator rbegin() const { return instructions.rbegin(); }
|
||||
reverse_iterator rend() { return instructions.rend(); }
|
||||
const_reverse_iterator rend() const { return instructions.rend(); }
|
||||
|
||||
const_iterator cbegin() const { return instructions.cbegin(); }
|
||||
const_iterator cend() const { return instructions.cend(); }
|
||||
|
||||
const_reverse_iterator crbegin() const { return instructions.crbegin(); }
|
||||
const_reverse_iterator crend() const { return instructions.crend(); }
|
||||
|
||||
/// Appends a new instruction to the end of this basic block,
|
||||
/// handling any allocations necessary to do so.
|
||||
/// @param op Opcode representing the instruction to add.
|
||||
/// @param args A sequence of Value instances used as arguments for the instruction.
|
||||
inline void AppendNewInst(const Opcode opcode, const std::initializer_list<IR::Value> args) noexcept {
|
||||
PrependNewInst(end(), opcode, args);
|
||||
inline iterator AppendNewInst(const Opcode opcode, const std::initializer_list<IR::Value> args) noexcept {
|
||||
return PrependNewInst(instructions.end(), opcode, args);
|
||||
}
|
||||
iterator PrependNewInst(iterator insertion_point, Opcode op, std::initializer_list<Value> args) noexcept;
|
||||
|
||||
@@ -165,9 +139,9 @@ public:
|
||||
inline const size_t& CycleCount() const noexcept {
|
||||
return cycle_count;
|
||||
}
|
||||
private:
|
||||
|
||||
/// "Hot cache" for small blocks so we don't call global allocator
|
||||
boost::container::static_vector<Inst, 14> inlined_inst;
|
||||
boost::container::static_vector<Inst, 30> inlined_inst;
|
||||
/// List of instructions in this block.
|
||||
instruction_list_type instructions;
|
||||
/// "Long/far" memory pool
|
||||
@@ -187,7 +161,7 @@ private:
|
||||
/// Number of cycles this block takes to execute.
|
||||
size_t cycle_count = 0;
|
||||
};
|
||||
static_assert(sizeof(Block) == 2048);
|
||||
static_assert(sizeof(Block) == 4096);
|
||||
|
||||
/// Returns a string representation of the contents of block. Intended for debugging.
|
||||
std::string DumpBlock(const IR::Block& block) noexcept;
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <deque>
|
||||
|
||||
namespace Dynarmic {
|
||||
template<typename T> struct dense_list {
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using size_type = std::size_t;
|
||||
using value_type = T;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const value_type*;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using iterator = typename std::deque<value_type>::iterator;
|
||||
using const_iterator = typename std::deque<value_type>::const_iterator;
|
||||
using reverse_iterator = typename std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = typename std::reverse_iterator<const_iterator>;
|
||||
|
||||
inline bool empty() const noexcept { return list.empty(); }
|
||||
inline size_type size() const noexcept { return list.size(); }
|
||||
|
||||
inline value_type& front() noexcept { return list.front(); }
|
||||
inline const value_type& front() const noexcept { return list.front(); }
|
||||
|
||||
inline value_type& back() noexcept { return list.back(); }
|
||||
inline const value_type& back() const noexcept { return list.back(); }
|
||||
|
||||
inline iterator begin() noexcept { return list.begin(); }
|
||||
inline const_iterator begin() const noexcept { return list.begin(); }
|
||||
inline iterator end() noexcept { return list.end(); }
|
||||
inline const_iterator end() const noexcept { return list.end(); }
|
||||
|
||||
inline reverse_iterator rbegin() noexcept { return list.rbegin(); }
|
||||
inline const_reverse_iterator rbegin() const noexcept { return list.rbegin(); }
|
||||
inline reverse_iterator rend() noexcept { return list.rend(); }
|
||||
inline const_reverse_iterator rend() const noexcept { return list.rend(); }
|
||||
|
||||
inline const_iterator cbegin() const noexcept { return list.cbegin(); }
|
||||
inline const_iterator cend() const noexcept { return list.cend(); }
|
||||
|
||||
inline const_reverse_iterator crbegin() const noexcept { return list.crbegin(); }
|
||||
inline const_reverse_iterator crend() const noexcept { return list.crend(); }
|
||||
|
||||
inline iterator insert_before(iterator it, value_type& value) noexcept {
|
||||
if (it == list.begin()) {
|
||||
list.push_front(value);
|
||||
return list.begin();
|
||||
}
|
||||
auto const index = std::distance(list.begin(), it - 1);
|
||||
list.insert(it - 1, value);
|
||||
return list.begin() + index;
|
||||
}
|
||||
|
||||
std::deque<value_type> list;
|
||||
};
|
||||
}
|
||||
@@ -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.
|
||||
@@ -65,15 +65,12 @@ enum class MemOp {
|
||||
PREFETCH,
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenience class to construct a basic block of the intermediate representation.
|
||||
* `block` is the resulting block.
|
||||
* The user of this class updates `current_location` as appropriate.
|
||||
*/
|
||||
/// @brief Convenience class to construct a basic block of the intermediate representation.
|
||||
/// `block` is the resulting block.
|
||||
/// The user of this class updates `current_location` as appropriate.
|
||||
class IREmitter {
|
||||
public:
|
||||
explicit IREmitter(Block& block)
|
||||
: block(block), insertion_point(block.end()) {}
|
||||
explicit IREmitter(Block& block) : block(block), insertion_point(block.instructions.end()) {}
|
||||
|
||||
Block& block;
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
namespace Dynarmic::Optimization {
|
||||
|
||||
static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
|
||||
for (auto& inst : block) {
|
||||
for (auto& inst : block.instructions) {
|
||||
switch (inst.GetOpcode()) {
|
||||
case IR::Opcode::A32ReadMemory8:
|
||||
case IR::Opcode::A64ReadMemory8: {
|
||||
@@ -131,7 +131,7 @@ static void FlagsPass(IR::Block& block) {
|
||||
|
||||
A32::IREmitter ir{block, A32::LocationDescriptor{block.Location()}, {}};
|
||||
|
||||
for (auto inst = block.rbegin(); inst != block.rend(); ++inst) {
|
||||
for (auto inst = block.instructions.rbegin(); inst != block.instructions.rend(); ++inst) {
|
||||
auto const opcode = inst->GetOpcode();
|
||||
switch (opcode) {
|
||||
case IR::Opcode::A32GetCFlag: {
|
||||
@@ -318,7 +318,7 @@ static void RegisterPass(IR::Block& block) {
|
||||
// Location and version don't matter here.
|
||||
A32::IREmitter ir{block, A32::LocationDescriptor{block.Location()}, {}};
|
||||
|
||||
for (auto inst = block.begin(); inst != block.end(); ++inst) {
|
||||
for (auto inst = block.instructions.begin(); inst != block.instructions.end(); ++inst) {
|
||||
auto const opcode = inst->GetOpcode();
|
||||
switch (opcode) {
|
||||
case IR::Opcode::A32GetRegister: {
|
||||
@@ -448,7 +448,7 @@ static void A64CallbackConfigPass(IR::Block& block, const A64::UserConfig& conf)
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& inst : block) {
|
||||
for (auto& inst : block.instructions) {
|
||||
if (inst.GetOpcode() != IR::Opcode::A64DataCacheOperationRaised) {
|
||||
continue;
|
||||
}
|
||||
@@ -541,7 +541,7 @@ static void A64GetSetElimination(IR::Block& block) {
|
||||
do_nothing();
|
||||
};
|
||||
|
||||
for (auto inst = block.begin(); inst != block.end(); ++inst) {
|
||||
for (auto inst = block.instructions.begin(); inst != block.instructions.end(); ++inst) {
|
||||
auto const opcode = inst->GetOpcode();
|
||||
switch (opcode) {
|
||||
case IR::Opcode::A64GetW: {
|
||||
@@ -1041,7 +1041,7 @@ static void FoldZeroExtendXToLong(IR::Inst& inst) {
|
||||
}
|
||||
|
||||
static void ConstantPropagation(IR::Block& block) {
|
||||
for (auto& inst : block) {
|
||||
for (auto& inst : block.instructions) {
|
||||
auto const opcode = inst.GetOpcode();
|
||||
switch (opcode) {
|
||||
case Op::LeastSignificantWord:
|
||||
@@ -1221,25 +1221,20 @@ static void ConstantPropagation(IR::Block& block) {
|
||||
static void DeadCodeElimination(IR::Block& block) {
|
||||
// We iterate over the instructions in reverse order.
|
||||
// This is because removing an instruction reduces the number of uses for earlier instructions.
|
||||
for (auto it = block.rbegin(); it != block.rend(); ++it)
|
||||
for (auto it = block.instructions.rbegin(); it != block.instructions.rend(); ++it)
|
||||
if (!it->HasUses() && !MayHaveSideEffects(it->GetOpcode()))
|
||||
it->Invalidate();
|
||||
}
|
||||
|
||||
static void IdentityRemovalPass(IR::Block& block) {
|
||||
boost::container::small_vector<IR::Inst*, 16> to_invalidate;
|
||||
for (auto it = block.begin(); it != block.end();) {
|
||||
const size_t num_args = it->NumArgs();
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
IR::Value arg = it->GetArg(i);
|
||||
if (arg.IsIdentity()) {
|
||||
do {
|
||||
arg = arg.GetInst()->GetArg(0);
|
||||
} while (arg.IsIdentity());
|
||||
boost::container::small_vector<IR::Inst*, 128> to_invalidate;
|
||||
for (auto it = block.instructions.begin(); it != block.instructions.end();) {
|
||||
auto const num_args = it->NumArgs();
|
||||
for (size_t i = 0; i < num_args; ++i)
|
||||
if (IR::Value arg = it->GetArg(i); arg.IsIdentity()) {
|
||||
do arg = arg.GetInst()->GetArg(0); while (arg.IsIdentity());
|
||||
it->SetArg(i, arg);
|
||||
}
|
||||
}
|
||||
|
||||
if (it->GetOpcode() == IR::Opcode::Identity || it->GetOpcode() == IR::Opcode::Void) {
|
||||
to_invalidate.push_back(&*it);
|
||||
it = block.Instructions().erase(it);
|
||||
@@ -1253,7 +1248,7 @@ static void IdentityRemovalPass(IR::Block& block) {
|
||||
|
||||
static void NamingPass(IR::Block& block) {
|
||||
u32 name = 1;
|
||||
for (auto& inst : block)
|
||||
for (auto& inst : block.instructions)
|
||||
inst.SetName(name++);
|
||||
}
|
||||
|
||||
@@ -1402,7 +1397,7 @@ static void PolyfillPass(IR::Block& block, const PolyfillOptions& polyfill) {
|
||||
|
||||
IR::IREmitter ir{block};
|
||||
|
||||
for (auto& inst : block) {
|
||||
for (auto& inst : block.instructions) {
|
||||
ir.SetInsertionPointBefore(&inst);
|
||||
|
||||
switch (inst.GetOpcode()) {
|
||||
@@ -1458,7 +1453,7 @@ static void PolyfillPass(IR::Block& block, const PolyfillOptions& polyfill) {
|
||||
}
|
||||
|
||||
static void VerificationPass(const IR::Block& block) {
|
||||
for (auto const& inst : block) {
|
||||
for (auto const& inst : block.instructions) {
|
||||
for (size_t i = 0; i < inst.NumArgs(); i++) {
|
||||
const IR::Type t1 = inst.GetArg(i).GetType();
|
||||
const IR::Type t2 = IR::GetArgTypeOf(inst.GetOpcode(), i);
|
||||
@@ -1466,7 +1461,7 @@ static void VerificationPass(const IR::Block& block) {
|
||||
}
|
||||
}
|
||||
ankerl::unordered_dense::map<IR::Inst*, size_t> actual_uses;
|
||||
for (auto const& inst : block) {
|
||||
for (auto const& inst : block.instructions) {
|
||||
for (size_t i = 0; i < inst.NumArgs(); i++)
|
||||
if (IR::Value const arg = inst.GetArg(i); !arg.IsImmediate())
|
||||
actual_uses[arg.GetInst()]++;
|
||||
|
||||
@@ -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.
|
||||
@@ -93,7 +93,7 @@ bool ShouldTestInst(u32 instruction, u32 pc, bool is_thumb, bool is_last_inst, A
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& ir_inst : block) {
|
||||
for (const auto& ir_inst : block.instructions) {
|
||||
switch (ir_inst.GetOpcode()) {
|
||||
case IR::Opcode::A32ExceptionRaised:
|
||||
case IR::Opcode::A32CallSupervisor:
|
||||
|
||||
@@ -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.
|
||||
@@ -45,7 +45,7 @@ static bool ShouldTestInst(u32 instruction, u64 pc, bool is_last_inst) {
|
||||
return false;
|
||||
if (auto terminal = block.GetTerminal(); boost::get<IR::Term::Interpret>(&terminal))
|
||||
return false;
|
||||
for (const auto& ir_inst : block) {
|
||||
for (const auto& ir_inst : block.instructions) {
|
||||
switch (ir_inst.GetOpcode()) {
|
||||
case IR::Opcode::A64ExceptionRaised:
|
||||
case IR::Opcode::A64CallSupervisor:
|
||||
|
||||
@@ -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.
|
||||
@@ -36,9 +36,9 @@ TEST_CASE("ASIMD Decoder: Ensure table order correctness", "[decode][a32][.]") {
|
||||
|
||||
const auto is_decode_error = [&get_ir](const A32::ASIMDMatcher<A32::TranslatorVisitor>& matcher, u32 instruction) {
|
||||
const auto block = get_ir(matcher, instruction);
|
||||
return std::find_if(block.cbegin(), block.cend(), [](auto const& e) {
|
||||
return std::find_if(block.instructions.cbegin(), block.instructions.cend(), [](auto const& e) {
|
||||
return e.GetOpcode() == IR::Opcode::A32ExceptionRaised && A32::Exception(e.GetArg(1).GetU64()) == A32::Exception::DecodeError;
|
||||
}) != block.cend();
|
||||
}) != block.instructions.cend();
|
||||
};
|
||||
|
||||
for (auto iter = table.cbegin(); iter != table.cend(); ++iter) {
|
||||
|
||||
@@ -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.
|
||||
@@ -54,7 +54,7 @@ bool ShouldTestInst(IR::Block& block) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& ir_inst : block) {
|
||||
for (const auto& ir_inst : block.instructions) {
|
||||
switch (ir_inst.GetOpcode()) {
|
||||
// A32
|
||||
case IR::Opcode::A32GetFpscr:
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 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
|
||||
|
||||
@@ -16,7 +19,7 @@ Block::Block(ObjectPool<Inst>& inst_pool_) : inst_pool{&inst_pool_} {}
|
||||
Block::~Block() = default;
|
||||
|
||||
void Block::AppendNewInst(Opcode op, std::initializer_list<Value> args) {
|
||||
PrependNewInst(end(), op, args);
|
||||
PrependNewInst(instructions.end(), op, args);
|
||||
}
|
||||
|
||||
Block::iterator Block::PrependNewInst(iterator insertion_point, const Inst& base_inst) {
|
||||
|
||||
Reference in New Issue
Block a user