2016-08-17 15:53:36 +01:00
|
|
|
/* This file is part of the dynarmic project.
|
|
|
|
* Copyright (c) 2016 MerryMage
|
|
|
|
* This software may be used and distributed according to the terms of the GNU
|
|
|
|
* General Public License version 2 or any later version.
|
|
|
|
*/
|
|
|
|
|
2016-08-25 15:35:50 +01:00
|
|
|
#include <algorithm>
|
|
|
|
#include <initializer_list>
|
2016-08-17 15:53:36 +01:00
|
|
|
#include <map>
|
|
|
|
#include <string>
|
|
|
|
|
2016-08-25 23:41:31 +01:00
|
|
|
#include <fmt/format.h>
|
|
|
|
|
2016-08-25 15:35:50 +01:00
|
|
|
#include "common/assert.h"
|
2016-08-17 15:53:36 +01:00
|
|
|
#include "frontend/ir/basic_block.h"
|
2016-08-25 15:35:50 +01:00
|
|
|
#include "frontend/ir/opcodes.h"
|
2016-08-17 15:53:36 +01:00
|
|
|
|
|
|
|
namespace Dynarmic {
|
|
|
|
namespace IR {
|
|
|
|
|
2016-08-25 15:35:50 +01:00
|
|
|
void Block::AppendNewInst(Opcode opcode, std::initializer_list<IR::Value> args) {
|
|
|
|
IR::Inst* inst = new(instruction_alloc_pool->Alloc()) IR::Inst(opcode);
|
|
|
|
DEBUG_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++;
|
|
|
|
});
|
|
|
|
|
|
|
|
instructions.push_back(inst);
|
|
|
|
}
|
|
|
|
|
2016-09-05 11:54:09 +01:00
|
|
|
LocationDescriptor Block::Location() const {
|
2016-08-25 15:35:50 +01:00
|
|
|
return location;
|
|
|
|
}
|
|
|
|
|
|
|
|
Arm::Cond Block::GetCondition() const {
|
|
|
|
return cond;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Block::SetCondition(Arm::Cond condition) {
|
|
|
|
cond = condition;
|
|
|
|
}
|
|
|
|
|
2016-09-05 11:54:09 +01:00
|
|
|
LocationDescriptor Block::ConditionFailedLocation() const {
|
2016-08-25 15:35:50 +01:00
|
|
|
return cond_failed.get();
|
|
|
|
}
|
|
|
|
|
2016-09-05 11:54:09 +01:00
|
|
|
void Block::SetConditionFailedLocation(LocationDescriptor fail_location) {
|
2016-09-04 11:30:57 +01:00
|
|
|
cond_failed = fail_location;
|
2016-08-25 15:35:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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.is_initialized();
|
|
|
|
}
|
|
|
|
|
|
|
|
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 = 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;
|
|
|
|
}
|
|
|
|
|
2016-09-05 11:54:09 +01:00
|
|
|
static std::string LocDescToString(const LocationDescriptor& loc) {
|
2016-08-25 23:41:31 +01:00
|
|
|
return fmt::format("{{{},{},{},{}}}",
|
|
|
|
loc.PC(),
|
|
|
|
loc.TFlag() ? "T" : "!T",
|
|
|
|
loc.EFlag() ? "E" : "!E",
|
|
|
|
loc.FPSCR().Value());
|
2016-08-25 02:58:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static std::string TerminalToString(const Terminal& terminal_variant) {
|
|
|
|
switch (terminal_variant.which()) {
|
|
|
|
case 1: {
|
|
|
|
auto terminal = boost::get<IR::Term::Interpret>(terminal_variant);
|
2016-08-25 23:41:31 +01:00
|
|
|
return fmt::format("Interpret{{{}}}", LocDescToString(terminal.next));
|
2016-08-25 02:58:50 +01:00
|
|
|
}
|
|
|
|
case 2: {
|
2016-08-25 23:41:31 +01:00
|
|
|
return "ReturnToDispatch{}";
|
2016-08-25 02:58:50 +01:00
|
|
|
}
|
|
|
|
case 3: {
|
|
|
|
auto terminal = boost::get<IR::Term::LinkBlock>(terminal_variant);
|
2016-08-25 23:41:31 +01:00
|
|
|
return fmt::format("LinkBlock{{{}}}", LocDescToString(terminal.next));
|
2016-08-25 02:58:50 +01:00
|
|
|
}
|
|
|
|
case 4: {
|
|
|
|
auto terminal = boost::get<IR::Term::LinkBlockFast>(terminal_variant);
|
2016-08-25 23:41:31 +01:00
|
|
|
return fmt::format("LinkBlockFast{{{}}}", LocDescToString(terminal.next));
|
2016-08-25 02:58:50 +01:00
|
|
|
}
|
|
|
|
case 5: {
|
2016-08-25 23:41:31 +01:00
|
|
|
return "PopRSBHint{}";
|
2016-08-25 02:58:50 +01:00
|
|
|
}
|
|
|
|
case 6: {
|
|
|
|
auto terminal = boost::get<IR::Term::If>(terminal_variant);
|
2016-08-25 23:41:31 +01:00
|
|
|
return fmt::format("If{{{}, {}, {}}}", CondToString(terminal.if_), TerminalToString(terminal.then_), TerminalToString(terminal.else_));
|
2016-08-25 02:58:50 +01:00
|
|
|
}
|
|
|
|
case 7: {
|
|
|
|
auto terminal = boost::get<IR::Term::CheckHalt>(terminal_variant);
|
2016-08-25 23:41:31 +01:00
|
|
|
return fmt::format("CheckHalt{{{}}}", TerminalToString(terminal.else_));
|
2016-08-25 02:58:50 +01:00
|
|
|
}
|
|
|
|
default:
|
|
|
|
return "<invalid terminal>";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-17 15:53:36 +01:00
|
|
|
std::string DumpBlock(const IR::Block& block) {
|
|
|
|
std::string ret;
|
|
|
|
|
2016-08-25 23:41:31 +01:00
|
|
|
ret += fmt::format("Block: location={}\n", LocDescToString(block.Location()));
|
|
|
|
ret += fmt::format("cycles={}", block.CycleCount());
|
|
|
|
ret += fmt::format(", entry_cond={}", Arm::CondToString(block.GetCondition(), true));
|
2016-08-25 15:35:50 +01:00
|
|
|
if (block.GetCondition() != Arm::Cond::AL) {
|
2016-08-25 23:41:31 +01:00
|
|
|
ret += fmt::format(", cond_fail={}", LocDescToString(block.ConditionFailedLocation()));
|
2016-08-17 15:53:36 +01:00
|
|
|
}
|
2016-08-25 23:41:31 +01:00
|
|
|
ret += '\n';
|
2016-08-17 15:53:36 +01:00
|
|
|
|
|
|
|
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()));
|
2016-08-17 15:53:36 +01:00
|
|
|
}
|
|
|
|
switch (arg.GetType()) {
|
2016-08-22 23:40:30 +01:00
|
|
|
case Type::U1:
|
2016-08-25 23:41:31 +01:00
|
|
|
return fmt::format("#{}", arg.GetU1() ? '1' : '0');
|
2016-08-22 23:40:30 +01:00
|
|
|
case Type::U8:
|
2016-08-25 23:41:31 +01:00
|
|
|
return fmt::format("#{}", arg.GetU8());
|
2016-08-22 23:40:30 +01:00
|
|
|
case Type::U32:
|
2016-08-25 23:41:31 +01:00
|
|
|
return fmt::format("#{:#x}", arg.GetU32());
|
2016-08-22 23:40:30 +01:00
|
|
|
case Type::RegRef:
|
|
|
|
return Arm::RegToString(arg.GetRegRef());
|
|
|
|
case Type::ExtRegRef:
|
|
|
|
return Arm::ExtRegToString(arg.GetExtRegRef());
|
|
|
|
default:
|
|
|
|
return "<unknown immediate type>";
|
2016-08-17 15:53:36 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-08-23 17:35:57 +01:00
|
|
|
for (const auto& inst : block) {
|
|
|
|
const Opcode op = inst.GetOpcode();
|
2016-08-17 15:53:36 +01:00
|
|
|
|
|
|
|
if (GetTypeOf(op) != Type::Void) {
|
2016-08-25 23:41:31 +01:00
|
|
|
ret += fmt::format("%{:<5} = ", index);
|
2016-08-17 15:53:36 +01:00
|
|
|
} 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++) {
|
2016-08-23 17:35:57 +01:00
|
|
|
const Value arg = inst.GetArg(arg_index);
|
2016-08-17 13:29:05 +01:00
|
|
|
|
2016-08-17 15:53:36 +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);
|
2016-08-19 01:34:14 +01:00
|
|
|
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
|
|
|
}
|
2016-08-17 15:53:36 +01:00
|
|
|
}
|
|
|
|
|
2016-08-25 23:41:31 +01:00
|
|
|
ret += '\n';
|
2016-08-23 17:35:57 +01:00
|
|
|
inst_to_index[&inst] = index++;
|
2016-08-17 15:53:36 +01:00
|
|
|
}
|
|
|
|
|
2016-08-25 23:41:31 +01:00
|
|
|
ret += "terminal = " + TerminalToString(block.GetTerminal()) + '\n';
|
2016-08-25 02:58:50 +01:00
|
|
|
|
2016-08-17 15:53:36 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace IR
|
|
|
|
} // namespace Dynarmic
|