2016-07-04 11:22:11 +02: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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <memory>
|
|
|
|
|
2017-12-05 22:34:40 +01:00
|
|
|
#include <boost/icl/interval_set.hpp>
|
2016-08-26 00:41:31 +02:00
|
|
|
#include <fmt/format.h>
|
|
|
|
|
2016-08-05 02:50:31 +02:00
|
|
|
#ifdef DYNARMIC_USE_LLVM
|
|
|
|
#include <llvm-c/Disassembler.h>
|
|
|
|
#include <llvm-c/Target.h>
|
|
|
|
#endif
|
|
|
|
|
2016-08-07 19:08:48 +02:00
|
|
|
#include "backend_x64/block_of_code.h"
|
2016-07-04 11:22:11 +02:00
|
|
|
#include "backend_x64/emit_x64.h"
|
|
|
|
#include "backend_x64/jitstate.h"
|
|
|
|
#include "common/assert.h"
|
|
|
|
#include "common/common_types.h"
|
|
|
|
#include "common/scope_exit.h"
|
2016-08-25 19:22:08 +02:00
|
|
|
#include "dynarmic/dynarmic.h"
|
2016-09-03 22:48:03 +02:00
|
|
|
#include "frontend/ir/basic_block.h"
|
2016-09-05 12:54:09 +02:00
|
|
|
#include "frontend/ir/location_descriptor.h"
|
2016-07-14 15:39:43 +02:00
|
|
|
#include "frontend/translate/translate.h"
|
2016-07-21 22:48:45 +02:00
|
|
|
#include "ir_opt/passes.h"
|
2016-07-04 11:22:11 +02:00
|
|
|
|
|
|
|
namespace Dynarmic {
|
|
|
|
|
|
|
|
using namespace BackendX64;
|
|
|
|
|
|
|
|
struct Jit::Impl {
|
2016-08-13 01:10:23 +02:00
|
|
|
Impl(Jit* jit, UserCallbacks callbacks)
|
2017-04-07 11:52:44 +02:00
|
|
|
: block_of_code(callbacks, &GetCurrentBlock, this)
|
2016-08-31 22:57:33 +02:00
|
|
|
, jit_state()
|
2016-08-13 01:10:23 +02:00
|
|
|
, emitter(&block_of_code, callbacks, jit)
|
|
|
|
, callbacks(callbacks)
|
2017-02-16 19:18:29 +01:00
|
|
|
, jit_interface(jit)
|
2016-08-13 01:10:23 +02:00
|
|
|
{}
|
|
|
|
|
|
|
|
BlockOfCode block_of_code;
|
|
|
|
JitState jit_state;
|
2016-07-04 11:22:11 +02:00
|
|
|
EmitX64 emitter;
|
2016-07-04 15:37:50 +02:00
|
|
|
const UserCallbacks callbacks;
|
2016-07-04 11:22:11 +02:00
|
|
|
|
2017-02-16 19:18:29 +01:00
|
|
|
// Requests made during execution to invalidate the cache are queued up here.
|
2017-12-05 22:34:40 +01:00
|
|
|
boost::icl::interval_set<u32> invalid_cache_ranges;
|
2017-09-11 01:09:52 +02:00
|
|
|
bool invalidate_entire_cache = false;
|
2016-09-02 11:58:37 +02:00
|
|
|
|
2017-12-03 03:42:22 +01:00
|
|
|
void Execute(size_t cycle_count) {
|
|
|
|
block_of_code.RunCode(&jit_state, cycle_count);
|
2016-07-04 11:22:11 +02:00
|
|
|
}
|
2016-08-05 02:50:31 +02:00
|
|
|
|
2016-09-05 12:54:09 +02:00
|
|
|
std::string Disassemble(const IR::LocationDescriptor& descriptor) {
|
2016-08-05 02:50:31 +02:00
|
|
|
auto block = GetBasicBlock(descriptor);
|
2017-02-16 19:18:29 +01:00
|
|
|
std::string result = fmt::format("address: {}\nsize: {} bytes\n", block.entrypoint, block.size);
|
2016-08-05 02:50:31 +02:00
|
|
|
|
|
|
|
#ifdef DYNARMIC_USE_LLVM
|
|
|
|
LLVMInitializeX86TargetInfo();
|
|
|
|
LLVMInitializeX86TargetMC();
|
|
|
|
LLVMInitializeX86Disassembler();
|
|
|
|
LLVMDisasmContextRef llvm_ctx = LLVMCreateDisasm("x86_64", nullptr, 0, nullptr, nullptr);
|
|
|
|
LLVMSetDisasmOptions(llvm_ctx, LLVMDisassembler_Option_AsmPrinterVariant);
|
|
|
|
|
2017-02-16 19:18:29 +01:00
|
|
|
const u8* pos = static_cast<const u8*>(block.entrypoint);
|
2017-03-07 00:29:36 +01:00
|
|
|
const u8* end = pos + block.size;
|
|
|
|
size_t remaining = block.size;
|
|
|
|
|
|
|
|
while (pos < end) {
|
2016-08-05 02:50:31 +02:00
|
|
|
char buffer[80];
|
|
|
|
size_t inst_size = LLVMDisasmInstruction(llvm_ctx, const_cast<u8*>(pos), remaining, (u64)pos, buffer, sizeof(buffer));
|
2016-08-19 02:53:24 +02:00
|
|
|
ASSERT(inst_size);
|
2017-03-07 00:29:36 +01:00
|
|
|
for (const u8* i = pos; i < pos + inst_size; i++)
|
2016-08-26 00:41:31 +02:00
|
|
|
result += fmt::format("{:02x} ", *i);
|
2016-08-05 02:50:31 +02:00
|
|
|
for (size_t i = inst_size; i < 10; i++)
|
2016-08-26 00:41:31 +02:00
|
|
|
result += " ";
|
|
|
|
result += buffer;
|
|
|
|
result += '\n';
|
2016-08-05 02:50:31 +02:00
|
|
|
|
|
|
|
pos += inst_size;
|
|
|
|
remaining -= inst_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
LLVMDisasmDispose(llvm_ctx);
|
|
|
|
#else
|
|
|
|
result.append("(recompile with DYNARMIC_USE_LLVM=ON to disassemble the generated x86_64 code)\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-02-16 19:18:29 +01:00
|
|
|
void PerformCacheInvalidation() {
|
2017-09-11 01:09:52 +02:00
|
|
|
if (invalidate_entire_cache) {
|
|
|
|
jit_state.ResetRSB();
|
|
|
|
block_of_code.ClearCache();
|
|
|
|
emitter.ClearCache();
|
|
|
|
|
|
|
|
invalid_cache_ranges.clear();
|
|
|
|
invalidate_entire_cache = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-02-16 19:18:29 +01:00
|
|
|
if (invalid_cache_ranges.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-09-02 11:58:37 +02:00
|
|
|
jit_state.ResetRSB();
|
2017-12-05 22:34:40 +01:00
|
|
|
emitter.InvalidateCacheRanges(invalid_cache_ranges);
|
|
|
|
invalid_cache_ranges.clear();
|
2017-02-16 19:18:29 +01:00
|
|
|
}
|
|
|
|
|
2017-09-11 01:09:52 +02:00
|
|
|
void RequestCacheInvalidation() {
|
2017-02-16 19:18:29 +01:00
|
|
|
if (jit_interface->is_executing) {
|
|
|
|
jit_state.halt_requested = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
PerformCacheInvalidation();
|
2016-09-02 11:58:37 +02:00
|
|
|
}
|
|
|
|
|
2016-07-04 11:22:11 +02:00
|
|
|
private:
|
2017-02-16 19:18:29 +01:00
|
|
|
Jit* jit_interface;
|
|
|
|
|
2017-04-07 11:52:44 +02:00
|
|
|
static CodePtr GetCurrentBlock(void *this_voidptr) {
|
|
|
|
Jit::Impl& this_ = *reinterpret_cast<Jit::Impl*>(this_voidptr);
|
|
|
|
JitState& jit_state = this_.jit_state;
|
|
|
|
|
|
|
|
u32 pc = jit_state.Reg[15];
|
|
|
|
Arm::PSR cpsr{jit_state.Cpsr};
|
|
|
|
Arm::FPSCR fpscr{jit_state.FPSCR_mode};
|
|
|
|
IR::LocationDescriptor descriptor{pc, cpsr, fpscr};
|
|
|
|
|
|
|
|
return this_.GetBasicBlock(descriptor).entrypoint;
|
|
|
|
}
|
|
|
|
|
2016-09-05 12:54:09 +02:00
|
|
|
EmitX64::BlockDescriptor GetBasicBlock(IR::LocationDescriptor descriptor) {
|
2016-08-05 02:50:31 +02:00
|
|
|
auto block = emitter.GetBasicBlock(descriptor);
|
|
|
|
if (block)
|
2016-08-12 19:17:31 +02:00
|
|
|
return *block;
|
2016-07-04 11:22:11 +02:00
|
|
|
|
2017-12-03 15:32:01 +01:00
|
|
|
constexpr size_t MINIMUM_REMAINING_CODESIZE = 1 * 1024 * 1024;
|
|
|
|
if (block_of_code.SpaceRemaining() < MINIMUM_REMAINING_CODESIZE) {
|
|
|
|
invalidate_entire_cache = true;
|
|
|
|
PerformCacheInvalidation();
|
|
|
|
}
|
|
|
|
|
2017-01-30 22:42:17 +01:00
|
|
|
IR::Block ir_block = Arm::Translate(descriptor, callbacks.memory.ReadCode);
|
2016-07-21 22:48:45 +02:00
|
|
|
Optimization::GetSetElimination(ir_block);
|
2017-02-19 12:05:16 +01:00
|
|
|
Optimization::DeadCodeElimination(ir_block);
|
2017-01-30 22:43:40 +01:00
|
|
|
Optimization::ConstantPropagation(ir_block, callbacks.memory);
|
2016-07-21 22:48:45 +02:00
|
|
|
Optimization::DeadCodeElimination(ir_block);
|
|
|
|
Optimization::VerificationPass(ir_block);
|
2016-08-26 20:14:25 +02:00
|
|
|
return emitter.Emit(ir_block);
|
2016-07-04 11:22:11 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-07-12 15:31:43 +02:00
|
|
|
Jit::Jit(UserCallbacks callbacks) : impl(std::make_unique<Impl>(this, callbacks)) {}
|
2016-07-04 11:22:11 +02:00
|
|
|
|
|
|
|
Jit::~Jit() {}
|
|
|
|
|
2017-12-03 03:42:22 +01:00
|
|
|
void Jit::Run(size_t cycle_count) {
|
2016-07-04 11:22:11 +02:00
|
|
|
ASSERT(!is_executing);
|
|
|
|
is_executing = true;
|
|
|
|
SCOPE_EXIT({ this->is_executing = false; });
|
|
|
|
|
2016-08-15 16:02:08 +02:00
|
|
|
impl->jit_state.halt_requested = false;
|
2016-07-04 11:22:11 +02:00
|
|
|
|
2017-12-03 03:42:22 +01:00
|
|
|
impl->Execute(cycle_count);
|
2016-07-04 11:22:11 +02:00
|
|
|
|
2017-02-16 19:18:29 +01:00
|
|
|
impl->PerformCacheInvalidation();
|
2016-07-04 11:22:11 +02:00
|
|
|
}
|
|
|
|
|
2016-09-01 10:47:09 +02:00
|
|
|
void Jit::ClearCache() {
|
2017-09-11 01:09:52 +02:00
|
|
|
impl->invalidate_entire_cache = true;
|
|
|
|
impl->RequestCacheInvalidation();
|
2017-02-16 19:18:29 +01:00
|
|
|
}
|
2016-09-02 11:58:37 +02:00
|
|
|
|
2017-02-16 19:18:29 +01:00
|
|
|
void Jit::InvalidateCacheRange(std::uint32_t start_address, std::size_t length) {
|
2017-12-05 22:34:40 +01:00
|
|
|
impl->invalid_cache_ranges.add(boost::icl::discrete_interval<u32>::closed(start_address, start_address + length - 1));
|
2017-09-11 01:09:52 +02:00
|
|
|
impl->RequestCacheInvalidation();
|
2016-07-04 11:22:11 +02:00
|
|
|
}
|
|
|
|
|
2016-08-09 23:45:54 +02:00
|
|
|
void Jit::Reset() {
|
|
|
|
ASSERT(!is_executing);
|
2016-08-31 22:57:33 +02:00
|
|
|
impl->jit_state = {};
|
2016-08-09 23:45:54 +02:00
|
|
|
}
|
|
|
|
|
2016-07-04 11:22:11 +02:00
|
|
|
void Jit::HaltExecution() {
|
2016-08-15 16:02:08 +02:00
|
|
|
impl->jit_state.halt_requested = true;
|
2016-07-04 11:22:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
std::array<u32, 16>& Jit::Regs() {
|
|
|
|
return impl->jit_state.Reg;
|
|
|
|
}
|
2016-08-25 02:01:42 +02:00
|
|
|
const std::array<u32, 16>& Jit::Regs() const {
|
2016-07-04 11:22:11 +02:00
|
|
|
return impl->jit_state.Reg;
|
|
|
|
}
|
|
|
|
|
2016-08-05 19:54:19 +02:00
|
|
|
std::array<u32, 64>& Jit::ExtRegs() {
|
|
|
|
return impl->jit_state.ExtReg;
|
|
|
|
}
|
|
|
|
|
2016-08-25 02:01:42 +02:00
|
|
|
const std::array<u32, 64>& Jit::ExtRegs() const {
|
2016-08-05 19:54:19 +02:00
|
|
|
return impl->jit_state.ExtReg;
|
|
|
|
}
|
|
|
|
|
2016-07-04 11:22:11 +02:00
|
|
|
u32& Jit::Cpsr() {
|
|
|
|
return impl->jit_state.Cpsr;
|
|
|
|
}
|
2016-08-05 19:54:19 +02:00
|
|
|
|
2016-07-04 11:22:11 +02:00
|
|
|
u32 Jit::Cpsr() const {
|
|
|
|
return impl->jit_state.Cpsr;
|
|
|
|
}
|
|
|
|
|
2016-08-05 19:54:19 +02:00
|
|
|
u32 Jit::Fpscr() const {
|
|
|
|
return impl->jit_state.Fpscr();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Jit::SetFpscr(u32 value) const {
|
|
|
|
return impl->jit_state.SetFpscr(value);
|
|
|
|
}
|
|
|
|
|
2016-09-05 12:54:09 +02:00
|
|
|
std::string Jit::Disassemble(const IR::LocationDescriptor& descriptor) {
|
2016-08-05 02:50:31 +02:00
|
|
|
return impl->Disassemble(descriptor);
|
|
|
|
}
|
|
|
|
|
2016-07-04 11:22:11 +02:00
|
|
|
} // namespace Dynarmic
|