Browse Source

reduce CPU usage on Spooky Mansion by making blocks be page-sized and page-aligned & remove unused set<> overhead in arm64

pull/3278/head
lizzie 1 month ago
committed by Caio Oliveira
parent
commit
0c84194f7f
No known key found for this signature in database GPG Key ID: AAAE6C7FD4186B0C
  1. 2
      src/dynarmic/src/dynarmic/backend/arm64/emit_arm64.cpp
  2. 6
      src/dynarmic/src/dynarmic/backend/arm64/emit_arm64_a32.cpp
  3. 12
      src/dynarmic/src/dynarmic/backend/arm64/reg_alloc.cpp
  4. 17
      src/dynarmic/src/dynarmic/backend/arm64/reg_alloc.h
  5. 20
      src/dynarmic/src/dynarmic/backend/block_range_information.cpp
  6. 10
      src/dynarmic/src/dynarmic/backend/block_range_information.h
  7. 2
      src/dynarmic/src/dynarmic/backend/riscv64/emit_riscv64.cpp
  8. 8
      src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp
  9. 2
      src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp
  10. 5
      src/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp
  11. 4
      src/dynarmic/src/dynarmic/backend/x64/emit_x64.h
  12. 9
      src/dynarmic/src/dynarmic/backend/x64/emit_x64_vector.cpp
  13. 4
      src/dynarmic/src/dynarmic/frontend/A32/translate/conditional_state.cpp
  14. 2
      src/dynarmic/src/dynarmic/frontend/A64/translate/impl/system.cpp
  15. 64
      src/dynarmic/src/dynarmic/ir/basic_block.cpp
  16. 38
      src/dynarmic/src/dynarmic/ir/basic_block.h
  17. 61
      src/dynarmic/src/dynarmic/ir/dense_list.h
  18. 11
      src/dynarmic/src/dynarmic/ir/ir_emitter.h
  19. 53
      src/dynarmic/src/dynarmic/ir/opt_passes.cpp
  20. 2
      src/dynarmic/tests/A32/fuzz_arm.cpp
  21. 2
      src/dynarmic/tests/A64/fuzz_with_unicorn.cpp
  22. 4
      src/dynarmic/tests/decoder_tests.cpp
  23. 2
      src/dynarmic/tests/test_generator.cpp
  24. 2
      src/shader_recompiler/frontend/ir/basic_block.cpp

2
src/dynarmic/src/dynarmic/backend/arm64/emit_arm64.cpp

@ -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()) {

6
src/dynarmic/src/dynarmic/backend/arm64/emit_arm64_a32.cpp

@ -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());
}

12
src/dynarmic/src/dynarmic/backend/arm64/reg_alloc.cpp

@ -144,7 +144,7 @@ RegAlloc::ArgumentInfo RegAlloc::GetArgumentInfo(IR::Inst* inst) {
}
bool RegAlloc::WasValueDefined(IR::Inst* inst) const {
return defined_insts.count(inst) > 0;
return defined_insts_count > 0;
}
void RegAlloc::PrepareForCall(std::optional<Argument::copyable_reference> arg0, std::optional<Argument::copyable_reference> arg1, std::optional<Argument::copyable_reference> arg2, std::optional<Argument::copyable_reference> arg3) {
@ -192,7 +192,7 @@ void RegAlloc::PrepareForCall(std::optional<Argument::copyable_reference> arg0,
}
void RegAlloc::DefineAsExisting(IR::Inst* inst, Argument& arg) {
defined_insts.insert(inst);
++defined_insts_count;
ASSERT(!ValueLocation(inst));
@ -207,7 +207,7 @@ void RegAlloc::DefineAsExisting(IR::Inst* inst, Argument& arg) {
}
void RegAlloc::DefineAsRegister(IR::Inst* inst, oaknut::Reg reg) {
defined_insts.insert(inst);
++defined_insts_count;
ASSERT(!ValueLocation(inst));
auto& info = reg.is_vector() ? fprs[reg.index()] : gprs[reg.index()];
@ -374,7 +374,7 @@ int RegAlloc::RealizeReadImpl(const IR::Value& value) {
template<HostLoc::Kind kind>
int RegAlloc::RealizeWriteImpl(const IR::Inst* value) {
defined_insts.insert(value);
++defined_insts_count;
ASSERT(!ValueLocation(value));
@ -399,7 +399,7 @@ 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);
++defined_insts_count;
// TODO: Move elimination
@ -463,7 +463,7 @@ void RegAlloc::SpillFpr(int index) {
}
void RegAlloc::ReadWriteFlags(Argument& read, IR::Inst* write) {
defined_insts.insert(write);
++defined_insts_count;
const auto current_location = ValueLocation(read.value.GetInst());
ASSERT(current_location);

17
src/dynarmic/src/dynarmic/backend/arm64/reg_alloc.h

@ -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);
@ -338,7 +333,7 @@ private:
mutable std::mt19937 rand_gen;
ankerl::unordered_dense::set<const IR::Inst*> defined_insts;
size_t defined_insts_count;
};
template<typename T>

20
src/dynarmic/src/dynarmic/backend/block_range_information.cpp

@ -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;

10
src/dynarmic/src/dynarmic/backend/block_range_information.h

@ -15,15 +15,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

2
src/dynarmic/src/dynarmic/backend/riscv64/emit_riscv64.cpp

@ -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()) {

8
src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp

@ -127,7 +127,7 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
EmitCondPrelude(ctx);
auto const loop_all_inst = [this, &block, &ctx](auto const func) {
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()) {
@ -734,11 +734,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());
}

2
src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp

@ -120,7 +120,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) {

5
src/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp

@ -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);

4
src/dynarmic/src/dynarmic/backend/x64/emit_x64.h

@ -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;

9
src/dynarmic/src/dynarmic/backend/x64/emit_x64_vector.cpp

@ -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) {

4
src/dynarmic/src/dynarmic/frontend/A32/translate/conditional_state.cpp

@ -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});

2
src/dynarmic/src/dynarmic/frontend/A64/translate/impl/system.cpp

@ -126,7 +126,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;

64
src/dynarmic/src/dynarmic/ir/basic_block.cpp

@ -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;
}

38
src/dynarmic/src/dynarmic/ir/basic_block.h

@ -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 {
alignas(4096) class 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;

61
src/dynarmic/src/dynarmic/ir/dense_list.h

@ -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;
};
}

11
src/dynarmic/src/dynarmic/ir/ir_emitter.h

@ -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;

53
src/dynarmic/src/dynarmic/ir/opt_passes.cpp

@ -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,43 +1221,34 @@ 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*, 128> to_invalidate;
auto iter = block.begin();
while (iter != block.end()) {
IR::Inst& inst = *iter;
const size_t num_args = inst.NumArgs();
for (size_t i = 0; i < num_args; i++) {
while (true) {
IR::Value arg = inst.GetArg(i);
if (!arg.IsIdentity())
break;
inst.SetArg(i, arg.GetInst()->GetArg(0));
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 (inst.GetOpcode() == IR::Opcode::Identity || inst.GetOpcode() == IR::Opcode::Void) {
iter = block.Instructions().erase(inst);
to_invalidate.push_back(&inst);
if (it->GetOpcode() == IR::Opcode::Identity || it->GetOpcode() == IR::Opcode::Void) {
to_invalidate.push_back(&*it);
it = block.Instructions().erase(it);
} else {
++iter;
++it;
}
}
for (IR::Inst* inst : to_invalidate) {
for (IR::Inst* const inst : to_invalidate)
inst->Invalidate();
}
}
static void NamingPass(IR::Block& block) {
u32 name = 1;
for (auto& inst : block)
for (auto& inst : block.instructions)
inst.SetName(name++);
}
@ -1406,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()) {
@ -1462,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);
@ -1470,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()]++;

2
src/dynarmic/tests/A32/fuzz_arm.cpp

@ -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:

2
src/dynarmic/tests/A64/fuzz_with_unicorn.cpp

@ -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:

4
src/dynarmic/tests/decoder_tests.cpp

@ -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) {

2
src/dynarmic/tests/test_generator.cpp

@ -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:

2
src/shader_recompiler/frontend/ir/basic_block.cpp

@ -16,7 +16,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) {

Loading…
Cancel
Save