dynarmic/src/frontend/ir/basic_block.cpp

243 lines
7.3 KiB
C++
Raw Normal View History

/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
2020-04-23 15:25:11 +01:00
* SPDX-License-Identifier: 0BSD
*/
#include <algorithm>
#include <initializer_list>
#include <map>
#include <string>
2016-08-25 23:41:31 +01:00
#include <fmt/format.h>
#include <fmt/ostream.h>
2016-08-25 23:41:31 +01:00
#include "common/assert.h"
#include "common/memory_pool.h"
2018-01-01 15:23:56 +00:00
#include "frontend/A32/types.h"
2018-01-07 00:11:57 +00:00
#include "frontend/A64/types.h"
#include "frontend/ir/basic_block.h"
#include "frontend/ir/cond.h"
#include "frontend/ir/opcodes.h"
namespace Dynarmic::IR {
Block::Block(const LocationDescriptor& location)
: location{location}, end_location{location}, cond{Cond::AL},
instruction_alloc_pool{std::make_unique<Common::Pool>(sizeof(Inst), 4096)} {}
Block::~Block() = default;
Block::Block(Block&&) = default;
Block& Block::operator=(Block&&) = default;
void Block::AppendNewInst(Opcode opcode, std::initializer_list<IR::Value> args) {
PrependNewInst(end(), opcode, args);
}
Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode opcode, std::initializer_list<Value> args) {
IR::Inst* inst = new(instruction_alloc_pool->Alloc()) IR::Inst(opcode);
2018-01-07 00:11:57 +00:00
ASSERT(args.size() == inst->NumArgs());
std::for_each(args.begin(), args.end(), [&inst, index = size_t(0)](const auto& arg) mutable {
inst->SetArg(index, arg);
index++;
});
return instructions.insert_before(insertion_point, inst);
}
LocationDescriptor Block::Location() const {
return location;
}
2017-02-16 18:18:29 +00:00
LocationDescriptor Block::EndLocation() const {
return end_location;
}
void Block::SetEndLocation(const LocationDescriptor& descriptor) {
end_location = descriptor;
}
2018-01-01 15:23:56 +00:00
Cond Block::GetCondition() const {
return cond;
}
2018-01-01 15:23:56 +00:00
void Block::SetCondition(Cond condition) {
cond = condition;
}
LocationDescriptor Block::ConditionFailedLocation() const {
return *cond_failed;
}
void Block::SetConditionFailedLocation(LocationDescriptor fail_location) {
cond_failed = fail_location;
}
size_t& Block::ConditionFailedCycleCount() {
return cond_failed_cycle_count;
}
const size_t& Block::ConditionFailedCycleCount() const {
return cond_failed_cycle_count;
}
bool Block::HasConditionFailedLocation() const {
return cond_failed.has_value();
}
Block::InstructionList& Block::Instructions() {
return instructions;
}
const Block::InstructionList& Block::Instructions() const {
return instructions;
}
Terminal Block::GetTerminal() const {
return terminal;
}
void Block::SetTerminal(Terminal term) {
ASSERT_MSG(!HasTerminal(), "Terminal has already been set.");
terminal = std::move(term);
}
void Block::ReplaceTerminal(Terminal term) {
ASSERT_MSG(HasTerminal(), "Terminal has not been set.");
terminal = std::move(term);
}
bool Block::HasTerminal() const {
return terminal.which() != 0;
}
size_t& Block::CycleCount() {
return cycle_count;
}
const size_t& Block::CycleCount() const {
return cycle_count;
}
static std::string TerminalToString(const Terminal& terminal_variant) {
struct : boost::static_visitor<std::string> {
std::string operator()(const Term::Invalid&) const {
return "<invalid terminal>";
}
std::string operator()(const Term::Interpret& terminal) const {
return fmt::format("Interpret{{{}}}", terminal.next);
}
std::string operator()(const Term::ReturnToDispatch&) const {
return "ReturnToDispatch{}";
}
std::string operator()(const Term::LinkBlock& terminal) const {
return fmt::format("LinkBlock{{{}}}", terminal.next);
}
std::string operator()(const Term::LinkBlockFast& terminal) const {
return fmt::format("LinkBlockFast{{{}}}", terminal.next);
}
std::string operator()(const Term::PopRSBHint&) const {
return "PopRSBHint{}";
}
std::string operator()(const Term::FastDispatchHint&) const {
return "FastDispatchHint{}";
}
std::string operator()(const Term::If& terminal) const {
return fmt::format("If{{{}, {}, {}}}", A64::CondToString(terminal.if_), TerminalToString(terminal.then_), TerminalToString(terminal.else_));
}
std::string operator()(const Term::CheckBit& terminal) const {
return fmt::format("CheckBit{{{}, {}}}", TerminalToString(terminal.then_), TerminalToString(terminal.else_));
}
std::string operator()(const Term::CheckHalt& terminal) const {
return fmt::format("CheckHalt{{{}}}", TerminalToString(terminal.else_));
}
} visitor;
return boost::apply_visitor(visitor, terminal_variant);
}
std::string DumpBlock(const IR::Block& block) {
std::string ret;
ret += fmt::format("Block: location={}\n", block.Location());
2016-08-25 23:41:31 +01:00
ret += fmt::format("cycles={}", block.CycleCount());
ret += fmt::format(", entry_cond={}", A64::CondToString(block.GetCondition()));
2018-01-01 15:23:56 +00:00
if (block.GetCondition() != Cond::AL) {
ret += fmt::format(", cond_fail={}", block.ConditionFailedLocation());
}
2016-08-25 23:41:31 +01:00
ret += '\n';
std::map<const IR::Inst*, size_t> inst_to_index;
size_t index = 0;
const auto arg_to_string = [&inst_to_index](const IR::Value& arg) -> std::string {
if (arg.IsEmpty()) {
return "<null>";
} else if (!arg.IsImmediate()) {
2016-08-25 23:41:31 +01:00
return fmt::format("%{}", inst_to_index.at(arg.GetInst()));
}
switch (arg.GetType()) {
case Type::U1:
2016-08-25 23:41:31 +01:00
return fmt::format("#{}", arg.GetU1() ? '1' : '0');
case Type::U8:
2016-08-25 23:41:31 +01:00
return fmt::format("#{}", arg.GetU8());
case Type::U16:
return fmt::format("#{:#x}", arg.GetU16());
case Type::U32:
2016-08-25 23:41:31 +01:00
return fmt::format("#{:#x}", arg.GetU32());
2018-01-08 22:03:03 +00:00
case Type::U64:
return fmt::format("#{:#x}", arg.GetU64());
case Type::A32Reg:
2018-01-01 15:23:56 +00:00
return A32::RegToString(arg.GetA32RegRef());
case Type::A32ExtReg:
2018-01-01 15:23:56 +00:00
return A32::ExtRegToString(arg.GetA32ExtRegRef());
2018-01-07 00:11:57 +00:00
case Type::A64Reg:
return A64::RegToString(arg.GetA64RegRef());
case Type::A64Vec:
return A64::VecToString(arg.GetA64VecRef());
default:
return "<unknown immediate type>";
}
};
for (const auto& inst : block) {
const Opcode op = inst.GetOpcode();
ret += fmt::format("[{:016x}] ", reinterpret_cast<u64>(&inst));
if (GetTypeOf(op) != Type::Void) {
2016-08-25 23:41:31 +01:00
ret += fmt::format("%{:<5} = ", index);
} else {
ret += " "; // '%00000 = ' -> 1 + 5 + 3 = 9 spaces
}
ret += GetNameOf(op);
const size_t arg_count = GetNumArgsOf(op);
for (size_t arg_index = 0; arg_index < arg_count; arg_index++) {
const Value arg = inst.GetArg(arg_index);
2016-08-17 13:29:05 +01:00
ret += arg_index != 0 ? ", " : " ";
2016-08-17 13:29:05 +01:00
ret += arg_to_string(arg);
Type actual_type = arg.GetType();
Type expected_type = GetArgTypeOf(op, arg_index);
if (!AreTypesCompatible(actual_type, expected_type)) {
2016-08-25 23:41:31 +01:00
ret += fmt::format("<type error: {} != {}>", GetNameOf(actual_type), GetNameOf(expected_type));
2016-08-17 13:29:05 +01:00
}
}
ret += fmt::format(" (uses: {})", inst.UseCount());
2016-08-25 23:41:31 +01:00
ret += '\n';
inst_to_index[&inst] = index++;
}
2016-08-25 23:41:31 +01:00
ret += "terminal = " + TerminalToString(block.GetTerminal()) + '\n';
return ret;
}
} // namespace Dynarmic::IR