ir: separate components out a little more
This commit is contained in:
parent
9690d1423d
commit
841098a0bc
21 changed files with 617 additions and 508 deletions
|
@ -15,9 +15,11 @@ set(SRCS
|
||||||
frontend/arm_types.cpp
|
frontend/arm_types.cpp
|
||||||
frontend/disassembler/disassembler_arm.cpp
|
frontend/disassembler/disassembler_arm.cpp
|
||||||
frontend/disassembler/disassembler_thumb.cpp
|
frontend/disassembler/disassembler_thumb.cpp
|
||||||
frontend/ir/ir.cpp
|
frontend/ir/basic_block.cpp
|
||||||
frontend/ir/ir_emitter.cpp
|
frontend/ir/ir_emitter.cpp
|
||||||
|
frontend/ir/microinstruction.cpp
|
||||||
frontend/ir/opcodes.cpp
|
frontend/ir/opcodes.cpp
|
||||||
|
frontend/ir/value.cpp
|
||||||
frontend/translate/translate.cpp
|
frontend/translate/translate.cpp
|
||||||
frontend/translate/translate_arm.cpp
|
frontend/translate/translate_arm.cpp
|
||||||
frontend/translate/translate_arm/branch.cpp
|
frontend/translate/translate_arm/branch.cpp
|
||||||
|
@ -63,9 +65,12 @@ set(HEADERS
|
||||||
frontend/decoder/thumb32.h
|
frontend/decoder/thumb32.h
|
||||||
frontend/decoder/vfp2.h
|
frontend/decoder/vfp2.h
|
||||||
frontend/disassembler/disassembler.h
|
frontend/disassembler/disassembler.h
|
||||||
frontend/ir/ir.h
|
frontend/ir/basic_block.h
|
||||||
frontend/ir/ir_emitter.h
|
frontend/ir/ir_emitter.h
|
||||||
|
frontend/ir/microinstruction.h
|
||||||
frontend/ir/opcodes.h
|
frontend/ir/opcodes.h
|
||||||
|
frontend/ir/terminal.h
|
||||||
|
frontend/ir/value.h
|
||||||
frontend/translate/translate.h
|
frontend/translate/translate.h
|
||||||
frontend/translate/translate_arm/translate_arm.h
|
frontend/translate/translate_arm/translate_arm.h
|
||||||
interface/interface.h
|
interface/interface.h
|
||||||
|
|
|
@ -8,13 +8,17 @@
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
#include "backend_x64/block_of_code.h"
|
#include "backend_x64/block_of_code.h"
|
||||||
#include "backend_x64/reg_alloc.h"
|
#include "backend_x64/reg_alloc.h"
|
||||||
#include "common/x64/emitter.h"
|
#include "common/x64/emitter.h"
|
||||||
#include "frontend/ir/ir.h"
|
#include "frontend/arm_types.h"
|
||||||
|
#include "frontend/ir/basic_block.h"
|
||||||
|
#include "frontend/ir/microinstruction.h"
|
||||||
|
#include "frontend/ir/terminal.h"
|
||||||
#include "interface/interface.h"
|
#include "interface/interface.h"
|
||||||
|
|
||||||
namespace Dynarmic {
|
namespace Dynarmic {
|
||||||
|
|
|
@ -6,13 +6,18 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
#include "backend_x64/block_of_code.h"
|
#include "backend_x64/block_of_code.h"
|
||||||
#include "backend_x64/jitstate.h"
|
#include "backend_x64/jitstate.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/x64/emitter.h"
|
#include "common/x64/emitter.h"
|
||||||
#include "frontend/ir/ir.h"
|
#include "frontend/ir/microinstruction.h"
|
||||||
|
#include "frontend/ir/value.h"
|
||||||
|
|
||||||
namespace Dynarmic {
|
namespace Dynarmic {
|
||||||
namespace BackendX64 {
|
namespace BackendX64 {
|
||||||
|
|
85
src/frontend/ir/basic_block.cpp
Normal file
85
src/frontend/ir/basic_block.cpp
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "common/string_util.h"
|
||||||
|
#include "frontend/ir/basic_block.h"
|
||||||
|
|
||||||
|
namespace Dynarmic {
|
||||||
|
namespace IR {
|
||||||
|
|
||||||
|
std::string DumpBlock(const IR::Block& block) {
|
||||||
|
std::string ret;
|
||||||
|
|
||||||
|
const auto loc_to_string = [](Arm::LocationDescriptor loc) -> std::string {
|
||||||
|
return Common::StringFromFormat("{%u,%s,%s,%u}",
|
||||||
|
loc.PC(),
|
||||||
|
loc.TFlag() ? "T" : "!T",
|
||||||
|
loc.EFlag() ? "E" : "!E",
|
||||||
|
loc.FPSCR());
|
||||||
|
};
|
||||||
|
|
||||||
|
ret += Common::StringFromFormat("Block: location=%s\n", loc_to_string(block.location).c_str());
|
||||||
|
ret += Common::StringFromFormat("cycles=%zu", block.cycle_count);
|
||||||
|
ret += Common::StringFromFormat(", entry_cond=%s", Arm::CondToString(block.cond, true));
|
||||||
|
if (block.cond != Arm::Cond::AL) {
|
||||||
|
ret += Common::StringFromFormat(", cond_fail=%s", loc_to_string(block.cond_failed.get()).c_str());
|
||||||
|
}
|
||||||
|
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()) {
|
||||||
|
return Common::StringFromFormat("%%%zu", inst_to_index.at(arg.GetInst()));
|
||||||
|
}
|
||||||
|
switch (arg.GetType()) {
|
||||||
|
case Type::U1:
|
||||||
|
return Common::StringFromFormat("#%s", arg.GetU1() ? "1" : "0");
|
||||||
|
case Type::U8:
|
||||||
|
return Common::StringFromFormat("#%u", arg.GetU8());
|
||||||
|
case Type::U32:
|
||||||
|
return Common::StringFromFormat("#%#x", arg.GetU32());
|
||||||
|
case Type::RegRef:
|
||||||
|
return Arm::RegToString(arg.GetRegRef());
|
||||||
|
case Type::ExtRegRef:
|
||||||
|
return Arm::ExtRegToString(arg.GetExtRegRef());
|
||||||
|
default:
|
||||||
|
return "<unknown immediate type>";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto inst = block.instructions.begin(); inst != block.instructions.end(); ++inst) {
|
||||||
|
const Opcode op = inst->GetOpcode();
|
||||||
|
|
||||||
|
if (GetTypeOf(op) != Type::Void) {
|
||||||
|
ret += Common::StringFromFormat("%%%-5zu = ", 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++) {
|
||||||
|
ret += arg_index != 0 ? ", " : " ";
|
||||||
|
ret += arg_to_string(inst->GetArg(arg_index));
|
||||||
|
}
|
||||||
|
|
||||||
|
ret += "\n";
|
||||||
|
inst_to_index[&*inst] = index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace IR
|
||||||
|
} // namespace Dynarmic
|
56
src/frontend/ir/basic_block.h
Normal file
56
src/frontend/ir/basic_block.h
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "common/intrusive_list.h"
|
||||||
|
#include "common/memory_pool.h"
|
||||||
|
#include "frontend/arm_types.h"
|
||||||
|
#include "frontend/ir/microinstruction.h"
|
||||||
|
#include "frontend/ir/terminal.h"
|
||||||
|
|
||||||
|
namespace Dynarmic {
|
||||||
|
namespace IR {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A basic block. It consists of zero or more instructions followed by exactly one terminal.
|
||||||
|
* 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 {
|
||||||
|
public:
|
||||||
|
explicit Block(const Arm::LocationDescriptor& location) : location(location) {}
|
||||||
|
|
||||||
|
/// Description of the starting location of this block
|
||||||
|
Arm::LocationDescriptor location;
|
||||||
|
/// Conditional to pass in order to execute this block
|
||||||
|
Arm::Cond cond = Arm::Cond::AL;
|
||||||
|
/// Block to execute next if `cond` did not pass.
|
||||||
|
boost::optional<Arm::LocationDescriptor> cond_failed = {};
|
||||||
|
|
||||||
|
/// List of instructions in this block.
|
||||||
|
Common::IntrusiveList<Inst> instructions;
|
||||||
|
/// Memory pool for instruction list
|
||||||
|
std::unique_ptr<Common::Pool> instruction_alloc_pool = std::make_unique<Common::Pool>(sizeof(Inst), 4096);
|
||||||
|
/// Terminal instruction of this block.
|
||||||
|
Terminal terminal = Term::Invalid{};
|
||||||
|
|
||||||
|
/// Number of cycles this block takes to execute.
|
||||||
|
size_t cycle_count = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Returns a string representation of the contents of block. Intended for debugging.
|
||||||
|
std::string DumpBlock(const IR::Block& block);
|
||||||
|
|
||||||
|
} // namespace IR
|
||||||
|
} // namespace Dynarmic
|
|
@ -1,254 +0,0 @@
|
||||||
/* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include "common/assert.h"
|
|
||||||
#include "common/string_util.h"
|
|
||||||
#include "frontend/ir/ir.h"
|
|
||||||
#include "frontend/ir/opcodes.h"
|
|
||||||
|
|
||||||
namespace Dynarmic {
|
|
||||||
namespace IR {
|
|
||||||
|
|
||||||
// Value class member definitions
|
|
||||||
|
|
||||||
Value::Value(Inst* value) : type(Type::Opaque) {
|
|
||||||
inner.inst = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
Value::Value(Arm::Reg value) : type(Type::RegRef) {
|
|
||||||
inner.imm_regref = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
Value::Value(Arm::ExtReg value) : type(Type::ExtRegRef) {
|
|
||||||
inner.imm_extregref = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
Value::Value(bool value) : type(Type::U1) {
|
|
||||||
inner.imm_u1 = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
Value::Value(u8 value) : type(Type::U8) {
|
|
||||||
inner.imm_u8 = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
Value::Value(u32 value) : type(Type::U32) {
|
|
||||||
inner.imm_u32 = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
Value::Value(u64 value) : type(Type::U64) {
|
|
||||||
inner.imm_u64 = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Value::IsImmediate() const {
|
|
||||||
if (type == Type::Opaque)
|
|
||||||
return inner.inst->GetOpcode() == Opcode::Identity ? inner.inst->GetArg(0).IsImmediate() : false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Value::IsEmpty() const {
|
|
||||||
return type == Type::Void;
|
|
||||||
}
|
|
||||||
|
|
||||||
Type Value::GetType() const {
|
|
||||||
if (type == Type::Opaque) {
|
|
||||||
if (inner.inst->GetOpcode() == Opcode::Identity) {
|
|
||||||
return inner.inst->GetArg(0).GetType();
|
|
||||||
} else {
|
|
||||||
return inner.inst->GetType();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
Arm::Reg Value::GetRegRef() const {
|
|
||||||
DEBUG_ASSERT(type == Type::RegRef);
|
|
||||||
return inner.imm_regref;
|
|
||||||
}
|
|
||||||
|
|
||||||
Arm::ExtReg Value::GetExtRegRef() const {
|
|
||||||
DEBUG_ASSERT(type == Type::ExtRegRef);
|
|
||||||
return inner.imm_extregref;
|
|
||||||
}
|
|
||||||
|
|
||||||
Inst* Value::GetInst() const {
|
|
||||||
DEBUG_ASSERT(type == Type::Opaque);
|
|
||||||
return inner.inst;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Value::GetU1() const {
|
|
||||||
if (type == Type::Opaque && inner.inst->GetOpcode() == Opcode::Identity)
|
|
||||||
return inner.inst->GetArg(0).GetU1();
|
|
||||||
DEBUG_ASSERT(type == Type::U1);
|
|
||||||
return inner.imm_u1;
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 Value::GetU8() const {
|
|
||||||
if (type == Type::Opaque && inner.inst->GetOpcode() == Opcode::Identity)
|
|
||||||
return inner.inst->GetArg(0).GetU8();
|
|
||||||
DEBUG_ASSERT(type == Type::U8);
|
|
||||||
return inner.imm_u8;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 Value::GetU32() const {
|
|
||||||
if (type == Type::Opaque && inner.inst->GetOpcode() == Opcode::Identity)
|
|
||||||
return inner.inst->GetArg(0).GetU32();
|
|
||||||
DEBUG_ASSERT(type == Type::U32);
|
|
||||||
return inner.imm_u32;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 Value::GetU64() const {
|
|
||||||
if (type == Type::Opaque && inner.inst->GetOpcode() == Opcode::Identity)
|
|
||||||
return inner.inst->GetArg(0).GetU64();
|
|
||||||
DEBUG_ASSERT(type == Type::U64);
|
|
||||||
return inner.imm_u64;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inst class member definitions
|
|
||||||
|
|
||||||
Value Inst::GetArg(size_t index) const {
|
|
||||||
DEBUG_ASSERT(index < GetNumArgsOf(op));
|
|
||||||
DEBUG_ASSERT(!args[index].IsEmpty());
|
|
||||||
|
|
||||||
return args[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
void Inst::SetArg(size_t index, Value value) {
|
|
||||||
DEBUG_ASSERT(index < GetNumArgsOf(op));
|
|
||||||
DEBUG_ASSERT(value.GetType() == GetArgTypeOf(op, index) || Type::Opaque == GetArgTypeOf(op, index));
|
|
||||||
|
|
||||||
if (!args[index].IsImmediate()) {
|
|
||||||
UndoUse(args[index]);
|
|
||||||
}
|
|
||||||
if (!value.IsImmediate()) {
|
|
||||||
Use(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
args[index] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Inst::Invalidate() {
|
|
||||||
for (auto& value : args) {
|
|
||||||
if (!value.IsImmediate()) {
|
|
||||||
UndoUse(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Inst::ReplaceUsesWith(Value& replacement) {
|
|
||||||
Invalidate();
|
|
||||||
|
|
||||||
op = Opcode::Identity;
|
|
||||||
|
|
||||||
if (!replacement.IsImmediate()) {
|
|
||||||
Use(replacement);
|
|
||||||
}
|
|
||||||
|
|
||||||
args[0] = replacement;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Inst::Use(Value& value) {
|
|
||||||
value.GetInst()->use_count++;
|
|
||||||
|
|
||||||
switch (op){
|
|
||||||
case Opcode::GetCarryFromOp:
|
|
||||||
value.GetInst()->carry_inst = this;
|
|
||||||
break;
|
|
||||||
case Opcode::GetOverflowFromOp:
|
|
||||||
value.GetInst()->overflow_inst = this;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Inst::UndoUse(Value& value) {
|
|
||||||
value.GetInst()->use_count--;
|
|
||||||
|
|
||||||
switch (op){
|
|
||||||
case Opcode::GetCarryFromOp:
|
|
||||||
value.GetInst()->carry_inst = nullptr;
|
|
||||||
break;
|
|
||||||
case Opcode::GetOverflowFromOp:
|
|
||||||
value.GetInst()->overflow_inst = nullptr;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string DumpBlock(const IR::Block& block) {
|
|
||||||
std::string ret;
|
|
||||||
|
|
||||||
const auto loc_to_string = [](Arm::LocationDescriptor loc) -> std::string {
|
|
||||||
return Common::StringFromFormat("{%u,%s,%s,%u}",
|
|
||||||
loc.PC(),
|
|
||||||
loc.TFlag() ? "T" : "!T",
|
|
||||||
loc.EFlag() ? "E" : "!E",
|
|
||||||
loc.FPSCR());
|
|
||||||
};
|
|
||||||
|
|
||||||
ret += Common::StringFromFormat("Block: location=%s\n", loc_to_string(block.location).c_str());
|
|
||||||
ret += Common::StringFromFormat("cycles=%zu", block.cycle_count);
|
|
||||||
ret += Common::StringFromFormat(", entry_cond=%s", Arm::CondToString(block.cond, true));
|
|
||||||
if (block.cond != Arm::Cond::AL) {
|
|
||||||
ret += Common::StringFromFormat(", cond_fail=%s", loc_to_string(block.cond_failed.get()).c_str());
|
|
||||||
}
|
|
||||||
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()) {
|
|
||||||
return Common::StringFromFormat("%%%zu", inst_to_index.at(arg.GetInst()));
|
|
||||||
}
|
|
||||||
switch (arg.GetType()) {
|
|
||||||
case Type::U1:
|
|
||||||
return Common::StringFromFormat("#%s", arg.GetU1() ? "1" : "0");
|
|
||||||
case Type::U8:
|
|
||||||
return Common::StringFromFormat("#%u", arg.GetU8());
|
|
||||||
case Type::U32:
|
|
||||||
return Common::StringFromFormat("#%#x", arg.GetU32());
|
|
||||||
case Type::RegRef:
|
|
||||||
return Arm::RegToString(arg.GetRegRef());
|
|
||||||
case Type::ExtRegRef:
|
|
||||||
return Arm::ExtRegToString(arg.GetExtRegRef());
|
|
||||||
default:
|
|
||||||
return "<unknown immediate type>";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (auto inst = block.instructions.begin(); inst != block.instructions.end(); ++inst) {
|
|
||||||
const Opcode op = inst->GetOpcode();
|
|
||||||
|
|
||||||
if (GetTypeOf(op) != Type::Void) {
|
|
||||||
ret += Common::StringFromFormat("%%%-5zu = ", 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++) {
|
|
||||||
ret += arg_index != 0 ? ", " : " ";
|
|
||||||
ret += arg_to_string(inst->GetArg(arg_index));
|
|
||||||
}
|
|
||||||
|
|
||||||
ret += "\n";
|
|
||||||
inst_to_index[&*inst] = index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace IR
|
|
||||||
} // namespace Dynarmic
|
|
|
@ -1,238 +0,0 @@
|
||||||
/* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <list>
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
|
||||||
#include <boost/variant.hpp>
|
|
||||||
|
|
||||||
#include "common/assert.h"
|
|
||||||
#include "common/common_types.h"
|
|
||||||
#include "common/intrusive_list.h"
|
|
||||||
#include "common/memory_pool.h"
|
|
||||||
#include "frontend/arm_types.h"
|
|
||||||
#include "frontend/ir/opcodes.h"
|
|
||||||
|
|
||||||
namespace Dynarmic {
|
|
||||||
namespace IR {
|
|
||||||
|
|
||||||
// ARM JIT Microinstruction Intermediate Representation
|
|
||||||
//
|
|
||||||
// This intermediate representation is an SSA IR. It is designed primarily for analysis,
|
|
||||||
// though it can be lowered into a reduced form for interpretation. Each IR node (Value)
|
|
||||||
// is a microinstruction of an idealised ARM CPU. The choice of microinstructions is made
|
|
||||||
// not based on any existing microarchitecture but on ease of implementation.
|
|
||||||
//
|
|
||||||
// A basic block is represented as an IR::Block.
|
|
||||||
|
|
||||||
// Type declarations
|
|
||||||
|
|
||||||
struct Value;
|
|
||||||
class Inst;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A representation of a value in the IR.
|
|
||||||
* A value may either be an immediate or the result of a microinstruction.
|
|
||||||
*/
|
|
||||||
struct Value final {
|
|
||||||
public:
|
|
||||||
Value() : type(Type::Void) {}
|
|
||||||
explicit Value(Inst* value);
|
|
||||||
explicit Value(Arm::Reg value);
|
|
||||||
explicit Value(Arm::ExtReg value);
|
|
||||||
explicit Value(bool value);
|
|
||||||
explicit Value(u8 value);
|
|
||||||
explicit Value(u32 value);
|
|
||||||
explicit Value(u64 value);
|
|
||||||
|
|
||||||
bool IsEmpty() const;
|
|
||||||
bool IsImmediate() const;
|
|
||||||
Type GetType() const;
|
|
||||||
|
|
||||||
Inst* GetInst() const;
|
|
||||||
Arm::Reg GetRegRef() const;
|
|
||||||
Arm::ExtReg GetExtRegRef() const;
|
|
||||||
bool GetU1() const;
|
|
||||||
u8 GetU8() const;
|
|
||||||
u32 GetU32() const;
|
|
||||||
u64 GetU64() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Type type;
|
|
||||||
|
|
||||||
union {
|
|
||||||
Inst* inst; // type == Type::Opaque
|
|
||||||
Arm::Reg imm_regref;
|
|
||||||
Arm::ExtReg imm_extregref;
|
|
||||||
bool imm_u1;
|
|
||||||
u8 imm_u8;
|
|
||||||
u32 imm_u32;
|
|
||||||
u64 imm_u64;
|
|
||||||
} inner;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A representation of a microinstruction. A single ARM/Thumb instruction may be
|
|
||||||
* converted into zero or more microinstructions.
|
|
||||||
*/
|
|
||||||
class Inst final : public Common::IntrusiveListNode<Inst> {
|
|
||||||
public:
|
|
||||||
Inst(Opcode op) : op(op) {}
|
|
||||||
|
|
||||||
bool HasUses() const { return use_count > 0; }
|
|
||||||
|
|
||||||
/// Get the microop this microinstruction represents.
|
|
||||||
Opcode GetOpcode() const { return op; }
|
|
||||||
/// Get the type this instruction returns.
|
|
||||||
Type GetType() const { return GetTypeOf(op); }
|
|
||||||
/// Get the number of arguments this instruction has.
|
|
||||||
size_t NumArgs() const { return GetNumArgsOf(op); }
|
|
||||||
|
|
||||||
Value GetArg(size_t index) const;
|
|
||||||
void SetArg(size_t index, Value value);
|
|
||||||
|
|
||||||
void Invalidate();
|
|
||||||
|
|
||||||
void ReplaceUsesWith(Value& replacement);
|
|
||||||
|
|
||||||
size_t use_count = 0;
|
|
||||||
Inst* carry_inst = nullptr;
|
|
||||||
Inst* overflow_inst = nullptr;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void Use(Value& value);
|
|
||||||
void UndoUse(Value& value);
|
|
||||||
|
|
||||||
Opcode op;
|
|
||||||
std::array<Value, 3> args;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace Term {
|
|
||||||
|
|
||||||
struct Invalid {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This terminal instruction calls the interpreter, starting at `next`.
|
|
||||||
* The interpreter must interpret exactly one instruction.
|
|
||||||
*/
|
|
||||||
struct Interpret {
|
|
||||||
explicit Interpret(const Arm::LocationDescriptor& next_) : next(next_) {}
|
|
||||||
Arm::LocationDescriptor next; ///< Location at which interpretation starts.
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This terminal instruction returns control to the dispatcher.
|
|
||||||
* The dispatcher will use the value in R15 to determine what comes next.
|
|
||||||
*/
|
|
||||||
struct ReturnToDispatch {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This terminal instruction jumps to the basic block described by `next` if we have enough
|
|
||||||
* cycles remaining. If we do not have enough cycles remaining, we return to the
|
|
||||||
* dispatcher, which will return control to the host.
|
|
||||||
*/
|
|
||||||
struct LinkBlock {
|
|
||||||
explicit LinkBlock(const Arm::LocationDescriptor& next_) : next(next_) {}
|
|
||||||
Arm::LocationDescriptor next; ///< Location descriptor for next block.
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This terminal instruction jumps to the basic block described by `next` unconditionally.
|
|
||||||
* This is an optimization and MUST only be emitted when this is guaranteed not to result
|
|
||||||
* in hanging, even in the face of other optimizations. (In practice, this means that only
|
|
||||||
* forward jumps to short-ish blocks would use this instruction.)
|
|
||||||
* A backend that doesn't support this optimization may choose to implement this exactly
|
|
||||||
* as LinkBlock.
|
|
||||||
*/
|
|
||||||
struct LinkBlockFast {
|
|
||||||
explicit LinkBlockFast(const Arm::LocationDescriptor& next_) : next(next_) {}
|
|
||||||
Arm::LocationDescriptor next; ///< Location descriptor for next block.
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This terminal instruction checks the top of the Return Stack Buffer against R15.
|
|
||||||
* If RSB lookup fails, control is returned to the dispatcher.
|
|
||||||
* This is an optimization for faster function calls. A backend that doesn't support
|
|
||||||
* this optimization or doesn't have a RSB may choose to implement this exactly as
|
|
||||||
* ReturnToDispatch.
|
|
||||||
*/
|
|
||||||
struct PopRSBHint {};
|
|
||||||
|
|
||||||
struct If;
|
|
||||||
struct CheckHalt;
|
|
||||||
/// A Terminal is the terminal instruction in a MicroBlock.
|
|
||||||
using Terminal = boost::variant<
|
|
||||||
Invalid,
|
|
||||||
Interpret,
|
|
||||||
ReturnToDispatch,
|
|
||||||
LinkBlock,
|
|
||||||
LinkBlockFast,
|
|
||||||
PopRSBHint,
|
|
||||||
boost::recursive_wrapper<If>,
|
|
||||||
boost::recursive_wrapper<CheckHalt>
|
|
||||||
>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This terminal instruction conditionally executes one terminal or another depending
|
|
||||||
* on the run-time state of the ARM flags.
|
|
||||||
*/
|
|
||||||
struct If {
|
|
||||||
If(Arm::Cond if_, Terminal then_, Terminal else_) : if_(if_), then_(then_), else_(else_) {}
|
|
||||||
Arm::Cond if_;
|
|
||||||
Terminal then_;
|
|
||||||
Terminal else_;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This terminal instruction checks if a halt was requested. If it wasn't, else_ is
|
|
||||||
* executed.
|
|
||||||
*/
|
|
||||||
struct CheckHalt {
|
|
||||||
CheckHalt(Terminal else_) : else_(else_) {}
|
|
||||||
Terminal else_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Term
|
|
||||||
|
|
||||||
using Term::Terminal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A basic block. It consists of zero or more instructions followed by exactly one terminal.
|
|
||||||
* 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 {
|
|
||||||
public:
|
|
||||||
explicit Block(const Arm::LocationDescriptor& location) : location(location) {}
|
|
||||||
|
|
||||||
/// Description of the starting location of this block
|
|
||||||
Arm::LocationDescriptor location;
|
|
||||||
/// Conditional to pass in order to execute this block
|
|
||||||
Arm::Cond cond = Arm::Cond::AL;
|
|
||||||
/// Block to execute next if `cond` did not pass.
|
|
||||||
boost::optional<Arm::LocationDescriptor> cond_failed = {};
|
|
||||||
|
|
||||||
/// List of instructions in this block.
|
|
||||||
Common::IntrusiveList<Inst> instructions;
|
|
||||||
/// Memory pool for instruction list
|
|
||||||
std::unique_ptr<Common::Pool> instruction_alloc_pool = std::make_unique<Common::Pool>(sizeof(Inst), 4096);
|
|
||||||
/// Terminal instruction of this block.
|
|
||||||
Terminal terminal = Term::Invalid{};
|
|
||||||
|
|
||||||
/// Number of cycles this block takes to execute.
|
|
||||||
size_t cycle_count = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Returns a string representation of the contents of block. Intended for debugging.
|
|
||||||
std::string DumpBlock(const IR::Block& block);
|
|
||||||
|
|
||||||
} // namespace IR
|
|
||||||
} // namespace Dynarmic
|
|
|
@ -6,9 +6,21 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <initializer_list>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
#include "frontend/arm_types.h"
|
#include "frontend/arm_types.h"
|
||||||
#include "frontend/ir/ir.h"
|
#include "frontend/ir/basic_block.h"
|
||||||
#include "frontend/ir/opcodes.h"
|
#include "frontend/ir/opcodes.h"
|
||||||
|
#include "frontend/ir/terminal.h"
|
||||||
|
#include "frontend/ir/value.h"
|
||||||
|
|
||||||
|
// ARM JIT Microinstruction Intermediate Representation
|
||||||
|
//
|
||||||
|
// This intermediate representation is an SSA IR. It is designed primarily for analysis,
|
||||||
|
// though it can be lowered into a reduced form for interpretation. Each IR node (Value)
|
||||||
|
// is a microinstruction of an idealised ARM CPU. The choice of microinstructions is made
|
||||||
|
// not based on any existing microarchitecture but on ease of implementation.
|
||||||
|
|
||||||
namespace Dynarmic {
|
namespace Dynarmic {
|
||||||
namespace Arm {
|
namespace Arm {
|
||||||
|
|
85
src/frontend/ir/microinstruction.cpp
Normal file
85
src/frontend/ir/microinstruction.cpp
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "frontend/ir/microinstruction.h"
|
||||||
|
|
||||||
|
namespace Dynarmic {
|
||||||
|
namespace IR {
|
||||||
|
|
||||||
|
Value Inst::GetArg(size_t index) const {
|
||||||
|
DEBUG_ASSERT(index < GetNumArgsOf(op));
|
||||||
|
DEBUG_ASSERT(!args[index].IsEmpty());
|
||||||
|
|
||||||
|
return args[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Inst::SetArg(size_t index, Value value) {
|
||||||
|
DEBUG_ASSERT(index < GetNumArgsOf(op));
|
||||||
|
DEBUG_ASSERT(value.GetType() == GetArgTypeOf(op, index) || Type::Opaque == GetArgTypeOf(op, index));
|
||||||
|
|
||||||
|
if (!args[index].IsImmediate()) {
|
||||||
|
UndoUse(args[index]);
|
||||||
|
}
|
||||||
|
if (!value.IsImmediate()) {
|
||||||
|
Use(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
args[index] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Inst::Invalidate() {
|
||||||
|
for (auto& value : args) {
|
||||||
|
if (!value.IsImmediate()) {
|
||||||
|
UndoUse(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Inst::ReplaceUsesWith(Value& replacement) {
|
||||||
|
Invalidate();
|
||||||
|
|
||||||
|
op = Opcode::Identity;
|
||||||
|
|
||||||
|
if (!replacement.IsImmediate()) {
|
||||||
|
Use(replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
args[0] = replacement;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Inst::Use(Value& value) {
|
||||||
|
value.GetInst()->use_count++;
|
||||||
|
|
||||||
|
switch (op){
|
||||||
|
case Opcode::GetCarryFromOp:
|
||||||
|
value.GetInst()->carry_inst = this;
|
||||||
|
break;
|
||||||
|
case Opcode::GetOverflowFromOp:
|
||||||
|
value.GetInst()->overflow_inst = this;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Inst::UndoUse(Value& value) {
|
||||||
|
value.GetInst()->use_count--;
|
||||||
|
|
||||||
|
switch (op){
|
||||||
|
case Opcode::GetCarryFromOp:
|
||||||
|
value.GetInst()->carry_inst = nullptr;
|
||||||
|
break;
|
||||||
|
case Opcode::GetOverflowFromOp:
|
||||||
|
value.GetInst()->overflow_inst = nullptr;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace IR
|
||||||
|
} // namespace Dynarmic
|
56
src/frontend/ir/microinstruction.h
Normal file
56
src/frontend/ir/microinstruction.h
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "common/intrusive_list.h"
|
||||||
|
#include "frontend/ir/opcodes.h"
|
||||||
|
#include "frontend/ir/value.h"
|
||||||
|
|
||||||
|
namespace Dynarmic {
|
||||||
|
namespace IR {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A representation of a microinstruction. A single ARM/Thumb instruction may be
|
||||||
|
* converted into zero or more microinstructions.
|
||||||
|
*/
|
||||||
|
class Inst final : public Common::IntrusiveListNode<Inst> {
|
||||||
|
public:
|
||||||
|
Inst(Opcode op) : op(op) {}
|
||||||
|
|
||||||
|
bool HasUses() const { return use_count > 0; }
|
||||||
|
|
||||||
|
/// Get the microop this microinstruction represents.
|
||||||
|
Opcode GetOpcode() const { return op; }
|
||||||
|
/// Get the type this instruction returns.
|
||||||
|
Type GetType() const { return GetTypeOf(op); }
|
||||||
|
/// Get the number of arguments this instruction has.
|
||||||
|
size_t NumArgs() const { return GetNumArgsOf(op); }
|
||||||
|
|
||||||
|
Value GetArg(size_t index) const;
|
||||||
|
void SetArg(size_t index, Value value);
|
||||||
|
|
||||||
|
void Invalidate();
|
||||||
|
|
||||||
|
void ReplaceUsesWith(Value& replacement);
|
||||||
|
|
||||||
|
size_t use_count = 0;
|
||||||
|
Inst* carry_inst = nullptr;
|
||||||
|
Inst* overflow_inst = nullptr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Use(Value& value);
|
||||||
|
void UndoUse(Value& value);
|
||||||
|
|
||||||
|
Opcode op;
|
||||||
|
std::array<Value, 3> args;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace IR
|
||||||
|
} // namespace Dynarmic
|
106
src/frontend/ir/terminal.h
Normal file
106
src/frontend/ir/terminal.h
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <boost/variant.hpp>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "frontend/arm_types.h"
|
||||||
|
|
||||||
|
namespace Dynarmic {
|
||||||
|
namespace IR {
|
||||||
|
namespace Term {
|
||||||
|
|
||||||
|
struct Invalid {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This terminal instruction calls the interpreter, starting at `next`.
|
||||||
|
* The interpreter must interpret exactly one instruction.
|
||||||
|
*/
|
||||||
|
struct Interpret {
|
||||||
|
explicit Interpret(const Arm::LocationDescriptor& next_) : next(next_) {}
|
||||||
|
Arm::LocationDescriptor next; ///< Location at which interpretation starts.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This terminal instruction returns control to the dispatcher.
|
||||||
|
* The dispatcher will use the value in R15 to determine what comes next.
|
||||||
|
*/
|
||||||
|
struct ReturnToDispatch {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This terminal instruction jumps to the basic block described by `next` if we have enough
|
||||||
|
* cycles remaining. If we do not have enough cycles remaining, we return to the
|
||||||
|
* dispatcher, which will return control to the host.
|
||||||
|
*/
|
||||||
|
struct LinkBlock {
|
||||||
|
explicit LinkBlock(const Arm::LocationDescriptor& next_) : next(next_) {}
|
||||||
|
Arm::LocationDescriptor next; ///< Location descriptor for next block.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This terminal instruction jumps to the basic block described by `next` unconditionally.
|
||||||
|
* This is an optimization and MUST only be emitted when this is guaranteed not to result
|
||||||
|
* in hanging, even in the face of other optimizations. (In practice, this means that only
|
||||||
|
* forward jumps to short-ish blocks would use this instruction.)
|
||||||
|
* A backend that doesn't support this optimization may choose to implement this exactly
|
||||||
|
* as LinkBlock.
|
||||||
|
*/
|
||||||
|
struct LinkBlockFast {
|
||||||
|
explicit LinkBlockFast(const Arm::LocationDescriptor& next_) : next(next_) {}
|
||||||
|
Arm::LocationDescriptor next; ///< Location descriptor for next block.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This terminal instruction checks the top of the Return Stack Buffer against R15.
|
||||||
|
* If RSB lookup fails, control is returned to the dispatcher.
|
||||||
|
* This is an optimization for faster function calls. A backend that doesn't support
|
||||||
|
* this optimization or doesn't have a RSB may choose to implement this exactly as
|
||||||
|
* ReturnToDispatch.
|
||||||
|
*/
|
||||||
|
struct PopRSBHint {};
|
||||||
|
|
||||||
|
struct If;
|
||||||
|
struct CheckHalt;
|
||||||
|
/// A Terminal is the terminal instruction in a MicroBlock.
|
||||||
|
using Terminal = boost::variant<
|
||||||
|
Invalid,
|
||||||
|
Interpret,
|
||||||
|
ReturnToDispatch,
|
||||||
|
LinkBlock,
|
||||||
|
LinkBlockFast,
|
||||||
|
PopRSBHint,
|
||||||
|
boost::recursive_wrapper<If>,
|
||||||
|
boost::recursive_wrapper<CheckHalt>
|
||||||
|
>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This terminal instruction conditionally executes one terminal or another depending
|
||||||
|
* on the run-time state of the ARM flags.
|
||||||
|
*/
|
||||||
|
struct If {
|
||||||
|
If(Arm::Cond if_, Terminal then_, Terminal else_) : if_(if_), then_(then_), else_(else_) {}
|
||||||
|
Arm::Cond if_;
|
||||||
|
Terminal then_;
|
||||||
|
Terminal else_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This terminal instruction checks if a halt was requested. If it wasn't, else_ is
|
||||||
|
* executed.
|
||||||
|
*/
|
||||||
|
struct CheckHalt {
|
||||||
|
CheckHalt(Terminal else_) : else_(else_) {}
|
||||||
|
Terminal else_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Term
|
||||||
|
|
||||||
|
using Term::Terminal;
|
||||||
|
|
||||||
|
} // namespace IR
|
||||||
|
} // namespace Dynarmic
|
107
src/frontend/ir/value.cpp
Normal file
107
src/frontend/ir/value.cpp
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "frontend/ir/microinstruction.h"
|
||||||
|
#include "frontend/ir/value.h"
|
||||||
|
|
||||||
|
namespace Dynarmic {
|
||||||
|
namespace IR {
|
||||||
|
|
||||||
|
Value::Value(Inst* value) : type(Type::Opaque) {
|
||||||
|
inner.inst = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value::Value(Arm::Reg value) : type(Type::RegRef) {
|
||||||
|
inner.imm_regref = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value::Value(Arm::ExtReg value) : type(Type::ExtRegRef) {
|
||||||
|
inner.imm_extregref = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value::Value(bool value) : type(Type::U1) {
|
||||||
|
inner.imm_u1 = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value::Value(u8 value) : type(Type::U8) {
|
||||||
|
inner.imm_u8 = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value::Value(u32 value) : type(Type::U32) {
|
||||||
|
inner.imm_u32 = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value::Value(u64 value) : type(Type::U64) {
|
||||||
|
inner.imm_u64 = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Value::IsImmediate() const {
|
||||||
|
if (type == Type::Opaque)
|
||||||
|
return inner.inst->GetOpcode() == Opcode::Identity ? inner.inst->GetArg(0).IsImmediate() : false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Value::IsEmpty() const {
|
||||||
|
return type == Type::Void;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type Value::GetType() const {
|
||||||
|
if (type == Type::Opaque) {
|
||||||
|
if (inner.inst->GetOpcode() == Opcode::Identity) {
|
||||||
|
return inner.inst->GetArg(0).GetType();
|
||||||
|
} else {
|
||||||
|
return inner.inst->GetType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
Arm::Reg Value::GetRegRef() const {
|
||||||
|
DEBUG_ASSERT(type == Type::RegRef);
|
||||||
|
return inner.imm_regref;
|
||||||
|
}
|
||||||
|
|
||||||
|
Arm::ExtReg Value::GetExtRegRef() const {
|
||||||
|
DEBUG_ASSERT(type == Type::ExtRegRef);
|
||||||
|
return inner.imm_extregref;
|
||||||
|
}
|
||||||
|
|
||||||
|
Inst* Value::GetInst() const {
|
||||||
|
DEBUG_ASSERT(type == Type::Opaque);
|
||||||
|
return inner.inst;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Value::GetU1() const {
|
||||||
|
if (type == Type::Opaque && inner.inst->GetOpcode() == Opcode::Identity)
|
||||||
|
return inner.inst->GetArg(0).GetU1();
|
||||||
|
DEBUG_ASSERT(type == Type::U1);
|
||||||
|
return inner.imm_u1;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 Value::GetU8() const {
|
||||||
|
if (type == Type::Opaque && inner.inst->GetOpcode() == Opcode::Identity)
|
||||||
|
return inner.inst->GetArg(0).GetU8();
|
||||||
|
DEBUG_ASSERT(type == Type::U8);
|
||||||
|
return inner.imm_u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 Value::GetU32() const {
|
||||||
|
if (type == Type::Opaque && inner.inst->GetOpcode() == Opcode::Identity)
|
||||||
|
return inner.inst->GetArg(0).GetU32();
|
||||||
|
DEBUG_ASSERT(type == Type::U32);
|
||||||
|
return inner.imm_u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 Value::GetU64() const {
|
||||||
|
if (type == Type::Opaque && inner.inst->GetOpcode() == Opcode::Identity)
|
||||||
|
return inner.inst->GetArg(0).GetU64();
|
||||||
|
DEBUG_ASSERT(type == Type::U64);
|
||||||
|
return inner.imm_u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace IR
|
||||||
|
} // namespace Dynarmic
|
59
src/frontend/ir/value.h
Normal file
59
src/frontend/ir/value.h
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "frontend/arm_types.h"
|
||||||
|
|
||||||
|
namespace Dynarmic {
|
||||||
|
namespace IR {
|
||||||
|
|
||||||
|
class Inst;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A representation of a value in the IR.
|
||||||
|
* A value may either be an immediate or the result of a microinstruction.
|
||||||
|
*/
|
||||||
|
struct Value final {
|
||||||
|
public:
|
||||||
|
Value() : type(Type::Void) {}
|
||||||
|
explicit Value(Inst* value);
|
||||||
|
explicit Value(Arm::Reg value);
|
||||||
|
explicit Value(Arm::ExtReg value);
|
||||||
|
explicit Value(bool value);
|
||||||
|
explicit Value(u8 value);
|
||||||
|
explicit Value(u32 value);
|
||||||
|
explicit Value(u64 value);
|
||||||
|
|
||||||
|
bool IsEmpty() const;
|
||||||
|
bool IsImmediate() const;
|
||||||
|
Type GetType() const;
|
||||||
|
|
||||||
|
Inst* GetInst() const;
|
||||||
|
Arm::Reg GetRegRef() const;
|
||||||
|
Arm::ExtReg GetExtRegRef() const;
|
||||||
|
bool GetU1() const;
|
||||||
|
u8 GetU8() const;
|
||||||
|
u32 GetU32() const;
|
||||||
|
u64 GetU64() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Type type;
|
||||||
|
|
||||||
|
union {
|
||||||
|
Inst* inst; // type == Type::Opaque
|
||||||
|
Arm::Reg imm_regref;
|
||||||
|
Arm::ExtReg imm_extregref;
|
||||||
|
bool imm_u1;
|
||||||
|
u8 imm_u8;
|
||||||
|
u32 imm_u32;
|
||||||
|
u64 imm_u64;
|
||||||
|
} inner;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace IR
|
||||||
|
} // namespace Dynarmic
|
|
@ -5,8 +5,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "frontend/arm_types.h"
|
#include "frontend/arm_types.h"
|
||||||
#include "frontend/ir/ir.h"
|
#include "frontend/ir/basic_block.h"
|
||||||
#include "translate.h"
|
#include "frontend/translate/translate.h"
|
||||||
|
|
||||||
namespace Dynarmic {
|
namespace Dynarmic {
|
||||||
namespace Arm {
|
namespace Arm {
|
||||||
|
|
|
@ -5,10 +5,15 @@
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
#include "frontend/arm_types.h"
|
#include "frontend/arm_types.h"
|
||||||
#include "frontend/ir/ir.h"
|
|
||||||
|
|
||||||
namespace Dynarmic {
|
namespace Dynarmic {
|
||||||
|
|
||||||
|
namespace IR {
|
||||||
|
class Block;
|
||||||
|
} // namespace IR
|
||||||
|
|
||||||
namespace Arm {
|
namespace Arm {
|
||||||
|
|
||||||
using MemoryRead32FuncType = u32 (*)(u32 vaddr);
|
using MemoryRead32FuncType = u32 (*)(u32 vaddr);
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include "frontend/arm_types.h"
|
#include "frontend/arm_types.h"
|
||||||
#include "frontend/decoder/arm.h"
|
#include "frontend/decoder/arm.h"
|
||||||
#include "frontend/decoder/vfp2.h"
|
#include "frontend/decoder/vfp2.h"
|
||||||
#include "frontend/ir/ir.h"
|
#include "frontend/ir/basic_block.h"
|
||||||
#include "frontend/translate/translate.h"
|
#include "frontend/translate/translate.h"
|
||||||
#include "frontend/translate/translate_arm/translate_arm.h"
|
#include "frontend/translate/translate_arm/translate_arm.h"
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "frontend/ir/ir.h"
|
#include "frontend/ir/basic_block.h"
|
||||||
|
#include "frontend/ir/opcodes.h"
|
||||||
#include "ir_opt/passes.h"
|
#include "ir_opt/passes.h"
|
||||||
|
|
||||||
namespace Dynarmic {
|
namespace Dynarmic {
|
||||||
|
|
|
@ -4,8 +4,12 @@
|
||||||
* General Public License version 2 or any later version.
|
* General Public License version 2 or any later version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "frontend/ir/ir.h"
|
#include "common/common_types.h"
|
||||||
|
#include "frontend/ir/basic_block.h"
|
||||||
|
#include "frontend/ir/value.h"
|
||||||
#include "ir_opt/passes.h"
|
#include "ir_opt/passes.h"
|
||||||
|
|
||||||
namespace Dynarmic {
|
namespace Dynarmic {
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "frontend/ir/ir.h"
|
#include "common/common_types.h"
|
||||||
|
#include "frontend/ir/basic_block.h"
|
||||||
|
#include "frontend/ir/microinstruction.h"
|
||||||
#include "ir_opt/passes.h"
|
#include "ir_opt/passes.h"
|
||||||
|
|
||||||
namespace Dynarmic {
|
namespace Dynarmic {
|
||||||
|
|
|
@ -4,9 +4,14 @@
|
||||||
* General Public License version 2 or any later version.
|
* General Public License version 2 or any later version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <tuple>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
|
|
||||||
|
@ -14,7 +19,7 @@
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "frontend/arm_types.h"
|
#include "frontend/arm_types.h"
|
||||||
#include "frontend/disassembler/disassembler.h"
|
#include "frontend/disassembler/disassembler.h"
|
||||||
#include "frontend/ir/ir.h"
|
#include "frontend/ir/basic_block.h"
|
||||||
#include "frontend/translate/translate.h"
|
#include "frontend/translate/translate.h"
|
||||||
#include "interface/interface.h"
|
#include "interface/interface.h"
|
||||||
#include "ir_opt/passes.h"
|
#include "ir_opt/passes.h"
|
||||||
|
|
|
@ -4,23 +4,27 @@
|
||||||
* General Public License version 2 or any later version.
|
* General Public License version 2 or any later version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
|
|
||||||
#include "common/bit_util.h"
|
#include "common/bit_util.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "frontend/disassembler/disassembler.h"
|
#include "frontend/disassembler/disassembler.h"
|
||||||
|
#include "frontend/ir/basic_block.h"
|
||||||
|
#include "frontend/translate/translate.h"
|
||||||
#include "interface/interface.h"
|
#include "interface/interface.h"
|
||||||
|
#include "ir_opt/passes.h"
|
||||||
#include "rand_int.h"
|
#include "rand_int.h"
|
||||||
#include "skyeye_interpreter/dyncom/arm_dyncom_interpreter.h"
|
#include "skyeye_interpreter/dyncom/arm_dyncom_interpreter.h"
|
||||||
#include "skyeye_interpreter/skyeye_common/armstate.h"
|
#include "skyeye_interpreter/skyeye_common/armstate.h"
|
||||||
|
|
||||||
#include "frontend/translate/translate.h"
|
|
||||||
#include "ir_opt/passes.h"
|
|
||||||
|
|
||||||
struct WriteRecord {
|
struct WriteRecord {
|
||||||
size_t size;
|
size_t size;
|
||||||
u32 address;
|
u32 address;
|
||||||
|
|
Loading…
Reference in a new issue