A64: Optimization: Merge interpret blocks
This commit is contained in:
parent
99b7516c8c
commit
c6a091d874
9 changed files with 76 additions and 2 deletions
|
@ -90,6 +90,7 @@ add_library(dynarmic
|
||||||
frontend/ir/value.h
|
frontend/ir/value.h
|
||||||
ir_opt/a32_constant_memory_reads_pass.cpp
|
ir_opt/a32_constant_memory_reads_pass.cpp
|
||||||
ir_opt/a32_get_set_elimination_pass.cpp
|
ir_opt/a32_get_set_elimination_pass.cpp
|
||||||
|
ir_opt/a64_merge_interpret_blocks.cpp
|
||||||
ir_opt/constant_propagation_pass.cpp
|
ir_opt/constant_propagation_pass.cpp
|
||||||
ir_opt/dead_code_elimination_pass.cpp
|
ir_opt/dead_code_elimination_pass.cpp
|
||||||
ir_opt/passes.h
|
ir_opt/passes.h
|
||||||
|
|
|
@ -1075,6 +1075,7 @@ void A32EmitX64::EmitA32CoprocStoreWords(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
void A32EmitX64::EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor initial_location) {
|
void A32EmitX64::EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor initial_location) {
|
||||||
ASSERT_MSG(A32::LocationDescriptor{terminal.next}.TFlag() == A32::LocationDescriptor{initial_location}.TFlag(), "Unimplemented");
|
ASSERT_MSG(A32::LocationDescriptor{terminal.next}.TFlag() == A32::LocationDescriptor{initial_location}.TFlag(), "Unimplemented");
|
||||||
ASSERT_MSG(A32::LocationDescriptor{terminal.next}.EFlag() == A32::LocationDescriptor{initial_location}.EFlag(), "Unimplemented");
|
ASSERT_MSG(A32::LocationDescriptor{terminal.next}.EFlag() == A32::LocationDescriptor{initial_location}.EFlag(), "Unimplemented");
|
||||||
|
ASSERT_MSG(terminal.num_instructions == 1, "Unimplemented");
|
||||||
|
|
||||||
code->mov(code->ABI_PARAM1.cvt32(), A32::LocationDescriptor{terminal.next}.PC());
|
code->mov(code->ABI_PARAM1.cvt32(), A32::LocationDescriptor{terminal.next}.PC());
|
||||||
code->mov(code->ABI_PARAM2, reinterpret_cast<u64>(jit_interface));
|
code->mov(code->ABI_PARAM2, reinterpret_cast<u64>(jit_interface));
|
||||||
|
|
|
@ -310,7 +310,7 @@ void A64EmitX64::EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDesc
|
||||||
DEVIRT(conf.callbacks, &A64::UserCallbacks::InterpreterFallback).EmitCall(code, [&](Xbyak::Reg64 param1, Xbyak::Reg64 param2) {
|
DEVIRT(conf.callbacks, &A64::UserCallbacks::InterpreterFallback).EmitCall(code, [&](Xbyak::Reg64 param1, Xbyak::Reg64 param2) {
|
||||||
code->mov(param1, A64::LocationDescriptor{terminal.next}.PC());
|
code->mov(param1, A64::LocationDescriptor{terminal.next}.PC());
|
||||||
code->mov(qword[r15 + offsetof(A64JitState, pc)], param1);
|
code->mov(qword[r15 + offsetof(A64JitState, pc)], param1);
|
||||||
code->mov(param2.cvt32(), 1);
|
code->mov(param2.cvt32(), terminal.num_instructions);
|
||||||
});
|
});
|
||||||
code->ReturnFromRunCode(true); // TODO: Check cycles
|
code->ReturnFromRunCode(true); // TODO: Check cycles
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,6 +176,7 @@ private:
|
||||||
// JIT Compile
|
// JIT Compile
|
||||||
IR::Block ir_block = A64::Translate(A64::LocationDescriptor{current_location}, [this](u64 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); });
|
IR::Block ir_block = A64::Translate(A64::LocationDescriptor{current_location}, [this](u64 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); });
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
Optimization::DeadCodeElimination(ir_block);
|
||||||
|
Optimization::A64MergeInterpretBlocksPass(ir_block, conf.callbacks);
|
||||||
// printf("%s\n", IR::DumpBlock(ir_block).c_str());
|
// printf("%s\n", IR::DumpBlock(ir_block).c_str());
|
||||||
Optimization::VerificationPass(ir_block);
|
Optimization::VerificationPass(ir_block);
|
||||||
return emitter.Emit(ir_block).entrypoint;
|
return emitter.Emit(ir_block).entrypoint;
|
||||||
|
|
|
@ -90,6 +90,11 @@ void Block::SetTerminal(Terminal term) {
|
||||||
terminal = term;
|
terminal = term;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Block::ReplaceTerminal(Terminal term) {
|
||||||
|
ASSERT_MSG(HasTerminal(), "Terminal has not been set.");
|
||||||
|
terminal = term;
|
||||||
|
}
|
||||||
|
|
||||||
bool Block::HasTerminal() const {
|
bool Block::HasTerminal() const {
|
||||||
return terminal.which() != 0;
|
return terminal.which() != 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,6 +111,8 @@ public:
|
||||||
Terminal GetTerminal() const;
|
Terminal GetTerminal() const;
|
||||||
/// Sets the terminal instruction for this basic block.
|
/// Sets the terminal instruction for this basic block.
|
||||||
void SetTerminal(Terminal term);
|
void SetTerminal(Terminal term);
|
||||||
|
/// Replaces the terminal instruction for this basic block.
|
||||||
|
void ReplaceTerminal(Terminal term);
|
||||||
/// Determines whether or not this basic block has a terminal instruction.
|
/// Determines whether or not this basic block has a terminal instruction.
|
||||||
bool HasTerminal() const;
|
bool HasTerminal() const;
|
||||||
|
|
||||||
|
|
|
@ -20,11 +20,12 @@ struct Invalid {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This terminal instruction calls the interpreter, starting at `next`.
|
* This terminal instruction calls the interpreter, starting at `next`.
|
||||||
* The interpreter must interpret exactly one instruction.
|
* The interpreter must interpret exactly `num_instructions` instructions.
|
||||||
*/
|
*/
|
||||||
struct Interpret {
|
struct Interpret {
|
||||||
explicit Interpret(const LocationDescriptor& next_) : next(next_) {}
|
explicit Interpret(const LocationDescriptor& next_) : next(next_) {}
|
||||||
LocationDescriptor next; ///< Location at which interpretation starts.
|
LocationDescriptor next; ///< Location at which interpretation starts.
|
||||||
|
size_t num_instructions = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
61
src/ir_opt/a64_merge_interpret_blocks.cpp
Normal file
61
src/ir_opt/a64_merge_interpret_blocks.cpp
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/* This file is part of the dynarmic project.
|
||||||
|
* Copyright (c) 2018 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 <array>
|
||||||
|
|
||||||
|
#include <boost/variant/get.hpp>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "dynarmic/A64/config.h"
|
||||||
|
#include "frontend/A64/location_descriptor.h"
|
||||||
|
#include "frontend/A64/translate/translate.h"
|
||||||
|
#include "frontend/ir/basic_block.h"
|
||||||
|
#include "ir_opt/passes.h"
|
||||||
|
|
||||||
|
namespace Dynarmic {
|
||||||
|
namespace Optimization {
|
||||||
|
|
||||||
|
void A64MergeInterpretBlocksPass(IR::Block& block, A64::UserCallbacks* cb) {
|
||||||
|
const auto is_interpret_instruction = [cb](A64::LocationDescriptor location) {
|
||||||
|
const u32 instruction = cb->MemoryReadCode(location.PC());
|
||||||
|
|
||||||
|
IR::Block new_block{location};
|
||||||
|
A64::TranslateSingleInstruction(new_block, location, instruction);
|
||||||
|
|
||||||
|
if (!new_block.Instructions().empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const IR::Terminal terminal = new_block.GetTerminal();
|
||||||
|
if (auto term = boost::get<IR::Term::Interpret>(&terminal)) {
|
||||||
|
printf("INTERPET %08x \\x%02x\\x%02x\\x%02x\\x%02x \n", instruction, u8(instruction >> 0), u8(instruction >> 8), u8(instruction >> 16), u8(instruction >> 24));
|
||||||
|
return term->next == location;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
IR::Terminal terminal = block.GetTerminal();
|
||||||
|
auto term = boost::get<IR::Term::Interpret>(&terminal);
|
||||||
|
if (!term)
|
||||||
|
return;
|
||||||
|
|
||||||
|
A64::LocationDescriptor location{term->next};
|
||||||
|
size_t num_instructions = 1;
|
||||||
|
|
||||||
|
while (is_interpret_instruction(location.AdvancePC(static_cast<int>(num_instructions * 4)))) {
|
||||||
|
num_instructions++;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%zu\n", num_instructions);
|
||||||
|
|
||||||
|
term->num_instructions = num_instructions;
|
||||||
|
block.ReplaceTerminal(terminal);
|
||||||
|
block.CycleCount() += num_instructions - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Optimization
|
||||||
|
} // namespace Dynarmic
|
|
@ -7,6 +7,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <dynarmic/A32/callbacks.h>
|
#include <dynarmic/A32/callbacks.h>
|
||||||
|
#include <dynarmic/A64/config.h>
|
||||||
|
|
||||||
namespace Dynarmic {
|
namespace Dynarmic {
|
||||||
namespace IR {
|
namespace IR {
|
||||||
|
@ -19,6 +20,7 @@ namespace Optimization {
|
||||||
|
|
||||||
void A32GetSetElimination(IR::Block& block);
|
void A32GetSetElimination(IR::Block& block);
|
||||||
void A32ConstantMemoryReads(IR::Block& block, const A32::UserCallbacks::Memory& memory_callbacks);
|
void A32ConstantMemoryReads(IR::Block& block, const A32::UserCallbacks::Memory& memory_callbacks);
|
||||||
|
void A64MergeInterpretBlocksPass(IR::Block& block, A64::UserCallbacks* cb);
|
||||||
void ConstantPropagation(IR::Block& block);
|
void ConstantPropagation(IR::Block& block);
|
||||||
void DeadCodeElimination(IR::Block& block);
|
void DeadCodeElimination(IR::Block& block);
|
||||||
void VerificationPass(const IR::Block& block);
|
void VerificationPass(const IR::Block& block);
|
||||||
|
|
Loading…
Reference in a new issue