refactored to remove hw/ references from jit/ folder

added recc tool for compiling ir offline
This commit is contained in:
Anthony Pesch 2016-04-09 02:29:50 -07:00
parent a87a9a2475
commit 055af5081e
37 changed files with 462 additions and 338 deletions

View File

@ -229,6 +229,28 @@ target_compile_definitions(redream PRIVATE ${REDREAM_DEFS})
target_compile_options(redream PRIVATE ${REDREAM_COMPILE_FLAGS}) target_compile_options(redream PRIVATE ${REDREAM_COMPILE_FLAGS})
#--------------------------------------------------
# tools
#--------------------------------------------------
set(RECC_SOURCES tools/recc.cc)
foreach(file ${REDREAM_SOURCES})
if(file MATCHES "(deps|src/(core|jit|sys))")
list(APPEND RECC_SOURCES ${file})
endif()
endforeach()
# assign source groups for visual studio projects
source_group_by_dir(RECC_SOURCES)
add_executable(recc ${RECC_SOURCES})
target_include_directories(recc SYSTEM PUBLIC ${REDREAM_INCLUDE_DIRS})
target_include_directories(recc PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(recc ${REDREAM_LIBS})
target_compile_definitions(recc PRIVATE MICROPROFILE_ENABLED=0 ${REDREAM_DEFS})
target_compile_options(recc PRIVATE ${REDREAM_COMPILE_FLAGS})
#-------------------------------------------------- #--------------------------------------------------
# tests # tests
#-------------------------------------------------- #--------------------------------------------------
@ -336,7 +358,7 @@ else()
endif() endif()
# build test binary # build test binary
set(REDREAM_TEST_SOURCES set(RETEST_SOURCES
${REDREAM_SOURCES} ${REDREAM_SOURCES}
test/test_interval_tree.cc test/test_interval_tree.cc
test/test_intrusive_list.cc test/test_intrusive_list.cc
@ -345,13 +367,13 @@ set(REDREAM_TEST_SOURCES
test/test_minmax_heap.cc test/test_minmax_heap.cc
test/test_sh4.cc test/test_sh4.cc
${asm_inc}) ${asm_inc})
list(REMOVE_ITEM REDREAM_TEST_SOURCES src/main.cc) list(REMOVE_ITEM RETEST_SOURCES src/main.cc)
# assign source groups for visual studio projects # assign source groups for visual studio projects
source_group_by_dir(REDREAM_TEST_SOURCES) source_group_by_dir(RETEST_SOURCES)
add_executable(redream_test ${REDREAM_TEST_SOURCES}) add_executable(retest ${RETEST_SOURCES})
target_include_directories(redream_test PUBLIC deps/gtest-1.7.0/include src/ test/ ${REDREAM_INCLUDE_DIRS}) target_include_directories(retest PUBLIC deps/gtest-1.7.0/include src/ test/ ${REDREAM_INCLUDE_DIRS})
target_link_libraries(redream_test gtest gtest_main ${REDREAM_LIBS}) target_link_libraries(retest gtest gtest_main ${REDREAM_LIBS})
target_compile_definitions(redream_test PRIVATE REDREAM_TEST=1 ${REDREAM_DEFS}) target_compile_definitions(retest PRIVATE ${REDREAM_DEFS})
target_compile_options(redream_test PRIVATE ${REDREAM_COMPILE_FLAGS}) target_compile_options(retest PRIVATE ${REDREAM_COMPILE_FLAGS})

View File

@ -58,5 +58,5 @@ target remote localhost:24690
## Running tests ## Running tests
```shell ```shell
redream_test retest
``` ```

View File

@ -2,20 +2,25 @@
#include <stdio.h> #include <stdio.h>
#include "core/log.h" #include "core/log.h"
#define ANSI_COLOR_RED "\x1b[31m" namespace re {
#define ANSI_COLOR_GREEN "\x1b[32m"
#define ANSI_COLOR_YELLOW "\x1b[33m"
#define ANSI_COLOR_BLUE "\x1b[34m"
#define ANSI_COLOR_MAGENTA "\x1b[35m"
#define ANSI_COLOR_CYAN "\x1b[36m"
#define ANSI_COLOR_RESET "\x1b[0m"
void Log(LogLevel level, const char *format, ...) { void Log(LogLevel level, const char *format, ...) {
static char buffer[0x20000]; static char sbuffer[0x1000];
int buffer_size = sizeof(sbuffer);
char *buffer = sbuffer;
va_list args; va_list args;
// allocate a temporary buffer if need be to fit the string
va_start(args, format); va_start(args, format);
vsnprintf(buffer, sizeof(buffer), format, args); int len = vsnprintf(0, 0, format, args);
if (len > buffer_size) {
buffer_size = len + 1;
buffer = reinterpret_cast<char *>(malloc(buffer_size));
}
va_end(args);
va_start(args, format);
vsnprintf(buffer, buffer_size, format, args);
va_end(args); va_end(args);
#if defined(PLATFORM_LINUX) || defined(PLATFORM_DARWIN) #if defined(PLATFORM_LINUX) || defined(PLATFORM_DARWIN)
@ -33,4 +38,10 @@ void Log(LogLevel level, const char *format, ...) {
#else #else
printf("%s\n", buffer); printf("%s\n", buffer);
#endif #endif
// cleanup the temporary buffer
if (buffer != sbuffer) {
free(buffer);
}
}
} }

View File

@ -3,7 +3,21 @@
#include <stdlib.h> #include <stdlib.h>
enum LogLevel { LOG_LEVEL_INFO, LOG_LEVEL_WARNING, LOG_LEVEL_FATAL }; namespace re {
enum LogLevel {
LOG_LEVEL_INFO,
LOG_LEVEL_WARNING,
LOG_LEVEL_FATAL,
};
#define ANSI_COLOR_RED "\x1b[31m"
#define ANSI_COLOR_GREEN "\x1b[32m"
#define ANSI_COLOR_YELLOW "\x1b[33m"
#define ANSI_COLOR_BLUE "\x1b[34m"
#define ANSI_COLOR_MAGENTA "\x1b[35m"
#define ANSI_COLOR_CYAN "\x1b[36m"
#define ANSI_COLOR_RESET "\x1b[0m"
void Log(LogLevel level, const char *format, ...); void Log(LogLevel level, const char *format, ...);
@ -31,5 +45,6 @@ void Log(LogLevel level, const char *format, ...);
exit(1); \ exit(1); \
} while (0) } while (0)
#endif #endif
}
#endif #endif

View File

@ -1,5 +1,5 @@
#include "core/memory.h" #include "core/memory.h"
#include "emu/profiler.h" #include "core/profiler.h"
#include "hw/holly/holly.h" #include "hw/holly/holly.h"
#include "hw/holly/pixel_convert.h" #include "hw/holly/pixel_convert.h"
#include "hw/holly/tile_accelerator.h" #include "hw/holly/tile_accelerator.h"

View File

@ -1,5 +1,5 @@
#include "core/assert.h" #include "core/assert.h"
#include "emu/profiler.h" #include "core/profiler.h"
#include "hw/holly/pixel_convert.h" #include "hw/holly/pixel_convert.h"
#include "hw/holly/tile_accelerator.h" #include "hw/holly/tile_accelerator.h"
#include "hw/holly/tile_renderer.h" #include "hw/holly/tile_renderer.h"

View File

@ -78,27 +78,29 @@ MapEntry *MemoryMap::AllocEntry() {
} }
// helpers for emitted assembly // helpers for emitted assembly
uint8_t Memory::R8(Memory *memory, uint32_t addr) { return memory->R8(addr); } uint8_t Memory::R8(void *memory, uint32_t addr) {
uint16_t Memory::R16(Memory *memory, uint32_t addr) { return reinterpret_cast<Memory *>(memory)->R8(addr);
return memory->R16(addr);
} }
uint32_t Memory::R32(Memory *memory, uint32_t addr) { uint16_t Memory::R16(void *memory, uint32_t addr) {
return memory->R32(addr); return reinterpret_cast<Memory *>(memory)->R16(addr);
} }
uint64_t Memory::R64(Memory *memory, uint32_t addr) { uint32_t Memory::R32(void *memory, uint32_t addr) {
return memory->R64(addr); return reinterpret_cast<Memory *>(memory)->R32(addr);
} }
void Memory::W8(Memory *memory, uint32_t addr, uint8_t value) { uint64_t Memory::R64(void *memory, uint32_t addr) {
memory->W8(addr, value); return reinterpret_cast<Memory *>(memory)->R64(addr);
} }
void Memory::W16(Memory *memory, uint32_t addr, uint16_t value) { void Memory::W8(void *memory, uint32_t addr, uint8_t value) {
memory->W16(addr, value); reinterpret_cast<Memory *>(memory)->W8(addr, value);
} }
void Memory::W32(Memory *memory, uint32_t addr, uint32_t value) { void Memory::W16(void *memory, uint32_t addr, uint16_t value) {
memory->W32(addr, value); reinterpret_cast<Memory *>(memory)->W16(addr, value);
} }
void Memory::W64(Memory *memory, uint32_t addr, uint64_t value) { void Memory::W32(void *memory, uint32_t addr, uint32_t value) {
memory->W64(addr, value); reinterpret_cast<Memory *>(memory)->W32(addr, value);
}
void Memory::W64(void *memory, uint32_t addr, uint64_t value) {
reinterpret_cast<Memory *>(memory)->W64(addr, value);
} }
Memory::Memory(Machine &machine) Memory::Memory(Machine &machine)

View File

@ -146,14 +146,14 @@ struct MemoryRegion {
class Memory { class Memory {
public: public:
static uint8_t R8(Memory *memory, uint32_t addr); static uint8_t R8(void *memory, uint32_t addr);
static uint16_t R16(Memory *memory, uint32_t addr); static uint16_t R16(void *memory, uint32_t addr);
static uint32_t R32(Memory *memory, uint32_t addr); static uint32_t R32(void *memory, uint32_t addr);
static uint64_t R64(Memory *memory, uint32_t addr); static uint64_t R64(void *memory, uint32_t addr);
static void W8(Memory *memory, uint32_t addr, uint8_t value); static void W8(void *memory, uint32_t addr, uint8_t value);
static void W16(Memory *memory, uint32_t addr, uint16_t value); static void W16(void *memory, uint32_t addr, uint16_t value);
static void W32(Memory *memory, uint32_t addr, uint32_t value); static void W32(void *memory, uint32_t addr, uint32_t value);
static void W64(Memory *memory, uint32_t addr, uint64_t value); static void W64(void *memory, uint32_t addr, uint64_t value);
Memory(Machine &machine); Memory(Machine &machine);
~Memory(); ~Memory();

View File

@ -1,7 +1,7 @@
#include <imgui.h> #include <imgui.h>
#include "core/math.h" #include "core/math.h"
#include "core/memory.h" #include "core/memory.h"
#include "emu/profiler.h" #include "core/profiler.h"
#include "hw/sh4/sh4.h" #include "hw/sh4/sh4.h"
#include "hw/dreamcast.h" #include "hw/dreamcast.h"
#include "hw/debugger.h" #include "hw/debugger.h"
@ -12,6 +12,7 @@ using namespace re;
using namespace re::hw; using namespace re::hw;
using namespace re::hw::sh4; using namespace re::hw::sh4;
using namespace re::jit; using namespace re::jit;
using namespace re::jit::backend;
using namespace re::jit::frontend::sh4; using namespace re::jit::frontend::sh4;
using namespace re::sys; using namespace re::sys;
@ -57,7 +58,11 @@ bool SH4::Init() {
memory_ = dc_.memory; memory_ = dc_.memory;
scheduler_ = dc_.scheduler; scheduler_ = dc_.scheduler;
code_cache_ = new SH4CodeCache(memory_, &ctx_, &SH4::CompilePC); code_cache_ =
new SH4CodeCache({&ctx_, memory_->protected_base(), memory_, &Memory::R8,
&Memory::R16, &Memory::R32, &Memory::R64, &Memory::W8,
&Memory::W16, &Memory::W32, &Memory::W64},
&SH4::CompilePC);
// initialize context // initialize context
memset(&ctx_, 0, sizeof(ctx_)); memset(&ctx_, 0, sizeof(ctx_));
@ -238,7 +243,12 @@ void SH4::Step() {
code_cache_->RemoveBlocks(ctx_.pc); code_cache_->RemoveBlocks(ctx_.pc);
// recompile it with only one instruction and run it // recompile it with only one instruction and run it
SH4BlockEntry *block = code_cache_->CompileBlock(ctx_.pc, 1); uint32_t guest_addr = ctx_.pc;
uint8_t *host_addr = memory_->TranslateVirtual(guest_addr);
int flags = GetCompileFlags() | SH4_SINGLE_INSTR;
SH4BlockEntry *block =
code_cache_->CompileBlock(guest_addr, host_addr, flags);
ctx_.pc = block->run(); ctx_.pc = block->run();
// let the debugger know we've stopped // let the debugger know we've stopped
@ -414,9 +424,13 @@ void SH4::OnPaint(bool show_main_menu) {
} }
uint32_t SH4::CompilePC() { uint32_t SH4::CompilePC() {
SH4CodeCache *code_cache = s_current_cpu->code_cache_; uint32_t guest_addr = s_current_cpu->ctx_.pc;
SH4Context *ctx = &s_current_cpu->ctx_; uint8_t *host_addr = s_current_cpu->memory_->TranslateVirtual(guest_addr);
SH4BlockEntry *block = code_cache->CompileBlock(ctx->pc, 0); int flags = s_current_cpu->GetCompileFlags();
SH4BlockEntry *block =
s_current_cpu->code_cache_->CompileBlock(guest_addr, host_addr, flags);
return block->run(); return block->run();
} }
@ -479,6 +493,17 @@ void SH4::FPSCRUpdated(SH4Context *ctx, uint64_t old_fpscr) {
} }
} }
int SH4::GetCompileFlags() {
int flags = 0;
if (ctx_.fpscr & PR) {
flags |= SH4_DOUBLE_PR;
}
if (ctx_.fpscr & SZ) {
flags |= SH4_DOUBLE_SZ;
}
return flags;
}
void SH4::SwapRegisterBank() { void SH4::SwapRegisterBank() {
for (int s = 0; s < 8; s++) { for (int s = 0; s < 8; s++) {
uint32_t tmp = ctx_.r[s]; uint32_t tmp = ctx_.r[s];

View File

@ -104,6 +104,7 @@ class SH4 : public Device,
static void FPSCRUpdated(jit::frontend::sh4::SH4Context *ctx, static void FPSCRUpdated(jit::frontend::sh4::SH4Context *ctx,
uint64_t old_fpscr); uint64_t old_fpscr);
int GetCompileFlags();
void SwapRegisterBank(); void SwapRegisterBank();
void SwapFPRegisterBank(); void SwapFPRegisterBank();

View File

@ -1,10 +1,12 @@
#include <gflags/gflags.h> #include <gflags/gflags.h>
#include "emu/profiler.h" #include "core/profiler.h"
#include "hw/sh4/sh4_code_cache.h" #include "hw/sh4/sh4_code_cache.h"
#include "hw/memory.h"
#include "jit/backend/x64/x64_backend.h" #include "jit/backend/x64/x64_backend.h"
#include "jit/frontend/sh4/sh4_frontend.h" #include "jit/frontend/sh4/sh4_frontend.h"
#include "jit/ir/ir_builder.h" #include "jit/ir/ir_builder.h"
// #include "jit/ir/passes/constant_propagation_pass.h" // #include "jit/ir/passes/constant_propagation_pass.h"
// #include "jit/ir/passes/conversion_elimination_pass.h"
#include "jit/ir/passes/dead_code_elimination_pass.h" #include "jit/ir/passes/dead_code_elimination_pass.h"
#include "jit/ir/passes/load_store_elimination_pass.h" #include "jit/ir/passes/load_store_elimination_pass.h"
#include "jit/ir/passes/register_allocation_pass.h" #include "jit/ir/passes/register_allocation_pass.h"
@ -19,7 +21,7 @@ using namespace re::jit::ir;
using namespace re::jit::ir::passes; using namespace re::jit::ir::passes;
using namespace re::sys; using namespace re::sys;
SH4CodeCache::SH4CodeCache(Memory *memory, void *guest_ctx, SH4CodeCache::SH4CodeCache(const MemoryInterface &memif,
BlockPointer default_block) BlockPointer default_block)
: default_block_(default_block) { : default_block_(default_block) {
// add exception handler to help recompile blocks when protected memory is // add exception handler to help recompile blocks when protected memory is
@ -28,12 +30,13 @@ SH4CodeCache::SH4CodeCache(Memory *memory, void *guest_ctx,
this, &SH4CodeCache::HandleException); this, &SH4CodeCache::HandleException);
// setup parser and emitter // setup parser and emitter
frontend_ = new SH4Frontend(*memory, guest_ctx); frontend_ = new SH4Frontend();
backend_ = new X64Backend(*memory, guest_ctx); backend_ = new X64Backend(memif);
// setup optimization passes // setup optimization passes
pass_runner_.AddPass(std::unique_ptr<Pass>(new LoadStoreEliminationPass())); pass_runner_.AddPass(std::unique_ptr<Pass>(new LoadStoreEliminationPass()));
// pass_runner_.AddPass(std::unique_ptr<Pass>(new ConstantPropagationPass())); // pass_runner_.AddPass(std::unique_ptr<Pass>(new ConstantPropagationPass()));
// pass_runner_.AddPass(std::unique_ptr<Pass>(new ConversionEliminationPass()));
pass_runner_.AddPass(std::unique_ptr<Pass>(new DeadCodeEliminationPass())); pass_runner_.AddPass(std::unique_ptr<Pass>(new DeadCodeEliminationPass()));
pass_runner_.AddPass( pass_runner_.AddPass(
std::unique_ptr<Pass>(new RegisterAllocationPass(*backend_))); std::unique_ptr<Pass>(new RegisterAllocationPass(*backend_)));
@ -59,10 +62,11 @@ SH4CodeCache::~SH4CodeCache() {
delete[] blocks_; delete[] blocks_;
} }
SH4BlockEntry *SH4CodeCache::CompileBlock(uint32_t addr, int max_instrs) { SH4BlockEntry *SH4CodeCache::CompileBlock(uint32_t guest_addr,
uint8_t *host_addr, int flags) {
PROFILER_RUNTIME("SH4CodeCache::CompileBlock"); PROFILER_RUNTIME("SH4CodeCache::CompileBlock");
int offset = BLOCK_OFFSET(addr); int offset = BLOCK_OFFSET(guest_addr);
CHECK_LT(offset, MAX_BLOCKS); CHECK_LT(offset, MAX_BLOCKS);
SH4BlockEntry *block = &blocks_[offset]; SH4BlockEntry *block = &blocks_[offset];
@ -79,7 +83,7 @@ SH4BlockEntry *SH4CodeCache::CompileBlock(uint32_t addr, int max_instrs) {
} }
// compile the SH4 into IR // compile the SH4 into IR
IRBuilder &builder = frontend_->BuildBlock(addr, max_instrs); IRBuilder &builder = frontend_->BuildBlock(guest_addr, host_addr, flags);
pass_runner_.Run(builder, false); pass_runner_.Run(builder, false);
@ -100,7 +104,7 @@ SH4BlockEntry *SH4CodeCache::CompileBlock(uint32_t addr, int max_instrs) {
} }
// add the cache entry to the lookup maps // add the cache entry to the lookup maps
auto res = block_map_.insert(std::make_pair(addr, block)); auto res = block_map_.insert(std::make_pair(guest_addr, block));
CHECK(res.second); CHECK(res.second);
auto rres = reverse_block_map_.insert( auto rres = reverse_block_map_.insert(
@ -115,10 +119,10 @@ SH4BlockEntry *SH4CodeCache::CompileBlock(uint32_t addr, int max_instrs) {
return block; return block;
} }
void SH4CodeCache::RemoveBlocks(uint32_t addr) { void SH4CodeCache::RemoveBlocks(uint32_t guest_addr) {
// remove any block which overlaps the address // remove any block which overlaps the address
while (true) { while (true) {
SH4BlockEntry *block = LookupBlock(addr); SH4BlockEntry *block = LookupBlock(guest_addr);
if (!block) { if (!block) {
break; break;

View File

@ -1,7 +1,9 @@
#ifndef SH4_CODE_CACHE_H #ifndef SH4_CODE_CACHE_H
#define SH4_CODE_CACHE_H #define SH4_CODE_CACHE_H
#include <map>
#include "jit/backend/x64/x64_backend.h" #include "jit/backend/x64/x64_backend.h"
#include "jit/frontend/sh4/sh4_context.h"
#include "jit/frontend/sh4/sh4_frontend.h" #include "jit/frontend/sh4/sh4_frontend.h"
#include "jit/ir/passes/pass_runner.h" #include "jit/ir/passes/pass_runner.h"
#include "sys/exception_handler.h" #include "sys/exception_handler.h"
@ -32,7 +34,7 @@ struct SH4BlockEntry {
class SH4CodeCache { class SH4CodeCache {
public: public:
SH4CodeCache(hw::Memory *memory, void *guest_ctx, SH4CodeCache(const jit::backend::MemoryInterface &memif,
jit::backend::BlockPointer default_block); jit::backend::BlockPointer default_block);
~SH4CodeCache(); ~SH4CodeCache();
@ -52,13 +54,14 @@ class SH4CodeCache {
// block, and the cache is initialized with all entries pointing to a special // block, and the cache is initialized with all entries pointing to a special
// default block. this default block, when called, will compile the actual // default block. this default block, when called, will compile the actual
// block and update the cache to point to it // block and update the cache to point to it
SH4BlockEntry *GetBlock(uint32_t addr) { SH4BlockEntry *GetBlock(uint32_t guest_addr) {
int offset = BLOCK_OFFSET(addr); int offset = BLOCK_OFFSET(guest_addr);
CHECK_LT(offset, MAX_BLOCKS); CHECK_LT(offset, MAX_BLOCKS);
return &blocks_[offset]; return &blocks_[offset];
} }
SH4BlockEntry *CompileBlock(uint32_t addr, int max_instrs); SH4BlockEntry *CompileBlock(uint32_t guest_addr, uint8_t *host_addr,
void RemoveBlocks(uint32_t addr); int flags);
void RemoveBlocks(uint32_t guest_addr);
void UnlinkBlocks(); void UnlinkBlocks();
void ClearBlocks(); void ClearBlocks();

View File

@ -1,40 +1,47 @@
#ifndef BACKEND_H #ifndef BACKEND_H
#define BACKEND_H #define BACKEND_H
#include <map>
#include "jit/ir/ir_builder.h" #include "jit/ir/ir_builder.h"
namespace re { namespace re {
namespace hw {
class Memory;
}
namespace sys { namespace sys {
struct Exception; struct Exception;
} }
namespace jit { namespace jit {
namespace backend { namespace backend {
enum BlockFlags {
// compile the block without fast memory access optimizations
BF_SLOWMEM = 0x1,
};
struct MemoryInterface {
void *ctx_base;
void *mem_base;
void *mem_self;
uint8_t (*r8)(void *, uint32_t);
uint16_t (*r16)(void *, uint32_t);
uint32_t (*r32)(void *, uint32_t);
uint64_t (*r64)(void *, uint32_t);
void (*w8)(void *, uint32_t, uint8_t);
void (*w16)(void *, uint32_t, uint16_t);
void (*w32)(void *, uint32_t, uint32_t);
void (*w64)(void *, uint32_t, uint64_t);
};
struct Register { struct Register {
const char *name; const char *name;
int value_types; int value_types;
const void *data; const void *data;
}; };
enum BlockFlags {
// compile the block without fast memory access optimizations
BF_SLOWMEM = 0x1,
};
typedef uint32_t (*BlockPointer)(); typedef uint32_t (*BlockPointer)();
class Backend { class Backend {
public: public:
Backend(hw::Memory &memory, void *guest_ctx) Backend(const MemoryInterface &memif) : memif_(memif) {}
: memory_(memory), guest_ctx_(guest_ctx) {}
virtual ~Backend() {} virtual ~Backend() {}
virtual const Register *registers() const = 0; virtual const Register *registers() const = 0;
@ -49,8 +56,7 @@ class Backend {
virtual bool HandleFastmemException(sys::Exception &ex) = 0; virtual bool HandleFastmemException(sys::Exception &ex) = 0;
protected: protected:
hw::Memory &memory_; MemoryInterface memif_;
void *guest_ctx_;
}; };
} }
} }

View File

@ -3,14 +3,12 @@
#include <beaengine/BeaEngine.h> #include <beaengine/BeaEngine.h>
#include <xbyak/xbyak.h> #include <xbyak/xbyak.h>
#include "core/memory.h" #include "core/memory.h"
#include "emu/profiler.h" #include "core/profiler.h"
#include "hw/memory.h"
#include "jit/backend/x64/x64_backend.h" #include "jit/backend/x64/x64_backend.h"
#include "jit/backend/x64/x64_disassembler.h" #include "jit/backend/x64/x64_disassembler.h"
#include "sys/exception_handler.h" #include "sys/exception_handler.h"
using namespace re; using namespace re;
using namespace re::hw;
using namespace re::jit; using namespace re::jit;
using namespace re::jit::backend; using namespace re::jit::backend;
using namespace re::jit::backend::x64; using namespace re::jit::backend::x64;
@ -109,8 +107,8 @@ const int x64_tmp1_idx = Xbyak::Operand::R11;
static const size_t x64_code_size = 1024 * 1024 * 8; static const size_t x64_code_size = 1024 * 1024 * 8;
static uint8_t x64_codegen[x64_code_size]; static uint8_t x64_codegen[x64_code_size];
X64Backend::X64Backend(Memory &memory, void *guest_ctx) X64Backend::X64Backend(const MemoryInterface &memif)
: Backend(memory, guest_ctx), emitter_(x64_codegen, x64_code_size) { : Backend(memif), emitter_(memif, x64_codegen, x64_code_size) {
Xbyak::CodeArray::protect(x64_codegen, x64_code_size, true); Xbyak::CodeArray::protect(x64_codegen, x64_code_size, true);
Reset(); Reset();
@ -136,7 +134,7 @@ BlockPointer X64Backend::AssembleBlock(ir::IRBuilder &builder,
// know so it can reset the cache and try again // know so it can reset the cache and try again
BlockPointer fn; BlockPointer fn;
try { try {
fn = emitter_.Emit(builder, memory_, guest_ctx_, block_flags); fn = emitter_.Emit(builder, block_flags);
} catch (const Xbyak::Error &e) { } catch (const Xbyak::Error &e) {
if (e == Xbyak::ERR_CODE_IS_TOO_BIG) { if (e == Xbyak::ERR_CODE_IS_TOO_BIG) {
return nullptr; return nullptr;
@ -196,7 +194,8 @@ bool X64Backend::HandleFastmemException(Exception &ex) {
// figure out the guest address that was being accessed // figure out the guest address that was being accessed
const uint8_t *fault_addr = reinterpret_cast<const uint8_t *>(ex.fault_addr); const uint8_t *fault_addr = reinterpret_cast<const uint8_t *>(ex.fault_addr);
const uint8_t *protected_start = memory_.protected_base(); const uint8_t *protected_start =
reinterpret_cast<const uint8_t *>(memif_.mem_base);
uint32_t guest_addr = static_cast<uint32_t>(fault_addr - protected_start); uint32_t guest_addr = static_cast<uint32_t>(fault_addr - protected_start);
// instead of handling the dynamic callback from inside of the exception // instead of handling the dynamic callback from inside of the exception
@ -215,26 +214,23 @@ bool X64Backend::HandleFastmemException(Exception &ex) {
if (mov.is_load) { if (mov.is_load) {
// prep argument registers (memory object, guest_addr) for read function // prep argument registers (memory object, guest_addr) for read function
ex.thread_state.r[x64_arg0_idx] = reinterpret_cast<uint64_t>(&memory_); ex.thread_state.r[x64_arg0_idx] =
reinterpret_cast<uint64_t>(memif_.mem_self);
ex.thread_state.r[x64_arg1_idx] = static_cast<uint64_t>(guest_addr); ex.thread_state.r[x64_arg1_idx] = static_cast<uint64_t>(guest_addr);
// prep function call address for thunk // prep function call address for thunk
switch (mov.operand_size) { switch (mov.operand_size) {
case 1: case 1:
ex.thread_state.rax = reinterpret_cast<uint64_t>( ex.thread_state.rax = reinterpret_cast<uint64_t>(memif_.r8);
static_cast<uint8_t (*)(Memory *, uint32_t)>(&Memory::R8));
break; break;
case 2: case 2:
ex.thread_state.rax = reinterpret_cast<uint64_t>( ex.thread_state.rax = reinterpret_cast<uint64_t>(memif_.r16);
static_cast<uint16_t (*)(Memory *, uint32_t)>(&Memory::R16));
break; break;
case 4: case 4:
ex.thread_state.rax = reinterpret_cast<uint64_t>( ex.thread_state.rax = reinterpret_cast<uint64_t>(memif_.r32);
static_cast<uint32_t (*)(Memory *, uint32_t)>(&Memory::R32));
break; break;
case 8: case 8:
ex.thread_state.rax = reinterpret_cast<uint64_t>( ex.thread_state.rax = reinterpret_cast<uint64_t>(memif_.r64);
static_cast<uint64_t (*)(Memory *, uint32_t)>(&Memory::R64));
break; break;
} }
@ -243,27 +239,24 @@ bool X64Backend::HandleFastmemException(Exception &ex) {
} else { } else {
// prep argument registers (memory object, guest_addr, value) for write // prep argument registers (memory object, guest_addr, value) for write
// function // function
ex.thread_state.r[x64_arg0_idx] = reinterpret_cast<uint64_t>(&memory_); ex.thread_state.r[x64_arg0_idx] =
reinterpret_cast<uint64_t>(memif_.mem_self);
ex.thread_state.r[x64_arg1_idx] = static_cast<uint64_t>(guest_addr); ex.thread_state.r[x64_arg1_idx] = static_cast<uint64_t>(guest_addr);
ex.thread_state.r[x64_arg2_idx] = ex.thread_state.r[mov.reg]; ex.thread_state.r[x64_arg2_idx] = ex.thread_state.r[mov.reg];
// prep function call address for thunk // prep function call address for thunk
switch (mov.operand_size) { switch (mov.operand_size) {
case 1: case 1:
ex.thread_state.rax = reinterpret_cast<uint64_t>( ex.thread_state.rax = reinterpret_cast<uint64_t>(memif_.w8);
static_cast<void (*)(Memory *, uint32_t, uint8_t)>(&Memory::W8));
break; break;
case 2: case 2:
ex.thread_state.rax = reinterpret_cast<uint64_t>( ex.thread_state.rax = reinterpret_cast<uint64_t>(memif_.w16);
static_cast<void (*)(Memory *, uint32_t, uint16_t)>(&Memory::W16));
break; break;
case 4: case 4:
ex.thread_state.rax = reinterpret_cast<uint64_t>( ex.thread_state.rax = reinterpret_cast<uint64_t>(memif_.w32);
static_cast<void (*)(Memory *, uint32_t, uint32_t)>(&Memory::W32));
break; break;
case 8: case 8:
ex.thread_state.rax = reinterpret_cast<uint64_t>( ex.thread_state.rax = reinterpret_cast<uint64_t>(memif_.w64);
static_cast<void (*)(Memory *, uint32_t, uint64_t)>(&Memory::W64));
break; break;
} }

View File

@ -21,7 +21,7 @@ typedef void (*SlowmemThunk)();
class X64Backend : public Backend { class X64Backend : public Backend {
public: public:
X64Backend(hw::Memory &memory, void *guest_ctx); X64Backend(const MemoryInterface &memif);
~X64Backend(); ~X64Backend();
const Register *registers() const; const Register *registers() const;

View File

@ -1,13 +1,11 @@
#include <math.h> #include <math.h>
#include "core/math.h" #include "core/math.h"
#include "core/memory.h" #include "core/memory.h"
#include "emu/profiler.h" #include "core/profiler.h"
#include "hw/memory.h"
#include "jit/backend/x64/x64_backend.h" #include "jit/backend/x64/x64_backend.h"
#include "jit/backend/x64/x64_emitter.h" #include "jit/backend/x64/x64_emitter.h"
using namespace re; using namespace re;
using namespace re::hw;
using namespace re::jit; using namespace re::jit;
using namespace re::jit::backend; using namespace re::jit::backend;
using namespace re::jit::backend::x64; using namespace re::jit::backend::x64;
@ -63,11 +61,9 @@ static bool IsCalleeSaved(const Xbyak::Reg &reg) {
return callee_saved[reg.getIdx()]; return callee_saved[reg.getIdx()];
} }
X64Emitter::X64Emitter(void *buffer, size_t buffer_size) X64Emitter::X64Emitter(const MemoryInterface &memif, void *buffer,
: CodeGenerator(buffer_size, buffer), size_t buffer_size)
memory_(nullptr), : CodeGenerator(buffer_size, buffer), memif_(memif), block_flags_(0) {
guest_ctx_(nullptr),
block_flags_(0) {
// temporary registers aren't tracked to be pushed and popped // temporary registers aren't tracked to be pushed and popped
CHECK(!IsCalleeSaved(tmp0) && !IsCalleeSaved(tmp1)); CHECK(!IsCalleeSaved(tmp0) && !IsCalleeSaved(tmp1));
@ -88,13 +84,10 @@ void X64Emitter::Reset() {
EmitConstants(); EmitConstants();
} }
BlockPointer X64Emitter::Emit(IRBuilder &builder, Memory &memory, BlockPointer X64Emitter::Emit(IRBuilder &builder, int block_flags) {
void *guest_ctx, int block_flags) {
PROFILER_RUNTIME("X64Emitter::Emit"); PROFILER_RUNTIME("X64Emitter::Emit");
// save off parameters for ease of access // save off parameters for ease of access
memory_ = &memory;
guest_ctx_ = guest_ctx;
block_flags_ = block_flags; block_flags_ = block_flags;
// getCurr returns the current spot in the codegen buffer which the function // getCurr returns the current spot in the codegen buffer which the function
@ -178,8 +171,8 @@ void X64Emitter::EmitProlog(IRBuilder &builder, int *out_stack_size) {
sub(rsp, stack_size); sub(rsp, stack_size);
// copy guest context and memory base to argument registers // copy guest context and memory base to argument registers
mov(r14, reinterpret_cast<uint64_t>(guest_ctx_)); mov(r14, reinterpret_cast<uint64_t>(memif_.ctx_base));
mov(r15, reinterpret_cast<uint64_t>(memory_->protected_base())); mov(r15, reinterpret_cast<uint64_t>(memif_.mem_base));
*out_stack_size = stack_size; *out_stack_size = stack_size;
} }
@ -392,44 +385,45 @@ EMITTER(STORE_HOST) {
EMITTER(LOAD_GUEST) { EMITTER(LOAD_GUEST) {
const Xbyak::Reg result = e.GetRegister(instr); const Xbyak::Reg result = e.GetRegister(instr);
if (instr->arg0()->constant()) { // TODO move to new constprop
// try to resolve the address to a physical page // if (instr->arg0()->constant()) {
uint32_t addr = static_cast<uint32_t>(instr->arg0()->i32()); // // try to resolve the address to a physical page
uint8_t *host_addr = nullptr; // uint32_t addr = static_cast<uint32_t>(instr->arg0()->i32());
MemoryRegion *region = nullptr; // uint8_t *host_addr = nullptr;
uint32_t offset = 0; // MemoryRegion *region = nullptr;
// uint32_t offset = 0;
e.memory()->Lookup(addr, &host_addr, &region, &offset); // e.memory()->Lookup(addr, &host_addr, &region, &offset);
// if the address maps to a physical page, not a dynamic handler, make it // // if the address maps to a physical page, not a dynamic handler, make it
// fast // // fast
if (host_addr) { // if (host_addr) {
// FIXME it'd be nice if xbyak had a mov operation which would convert // // FIXME it'd be nice if xbyak had a mov operation which would convert
// the displacement to a RIP-relative address when finalizing code so // // the displacement to a RIP-relative address when finalizing code so
// we didn't have to store the absolute address in the scratch register // // we didn't have to store the absolute address in the scratch register
e.mov(e.rax, reinterpret_cast<uint64_t>(host_addr)); // e.mov(e.rax, reinterpret_cast<uint64_t>(host_addr));
switch (instr->type()) { // switch (instr->type()) {
case VALUE_I8: // case VALUE_I8:
e.mov(result, e.byte[e.rax]); // e.mov(result, e.byte[e.rax]);
break; // break;
case VALUE_I16: // case VALUE_I16:
e.mov(result, e.word[e.rax]); // e.mov(result, e.word[e.rax]);
break; // break;
case VALUE_I32: // case VALUE_I32:
e.mov(result, e.dword[e.rax]); // e.mov(result, e.dword[e.rax]);
break; // break;
case VALUE_I64: // case VALUE_I64:
e.mov(result, e.qword[e.rax]); // e.mov(result, e.qword[e.rax]);
break; // break;
default: // default:
LOG_FATAL("Unexpected load result type"); // LOG_FATAL("Unexpected load result type");
break; // break;
} // }
return; // return;
} // }
} // }
const Xbyak::Reg a = e.GetRegister(instr->arg0()); const Xbyak::Reg a = e.GetRegister(instr->arg0());
@ -437,27 +431,23 @@ EMITTER(LOAD_GUEST) {
void *fn = nullptr; void *fn = nullptr;
switch (instr->type()) { switch (instr->type()) {
case VALUE_I8: case VALUE_I8:
fn = reinterpret_cast<void *>( fn = reinterpret_cast<void *>(e.memif().r8);
static_cast<uint8_t (*)(Memory *, uint32_t)>(&Memory::R8));
break; break;
case VALUE_I16: case VALUE_I16:
fn = reinterpret_cast<void *>( fn = reinterpret_cast<void *>(e.memif().r16);
static_cast<uint16_t (*)(Memory *, uint32_t)>(&Memory::R16));
break; break;
case VALUE_I32: case VALUE_I32:
fn = reinterpret_cast<void *>( fn = reinterpret_cast<void *>(e.memif().r32);
static_cast<uint32_t (*)(Memory *, uint32_t)>(&Memory::R32));
break; break;
case VALUE_I64: case VALUE_I64:
fn = reinterpret_cast<void *>( fn = reinterpret_cast<void *>(e.memif().r64);
static_cast<uint64_t (*)(Memory *, uint32_t)>(&Memory::R64));
break; break;
default: default:
LOG_FATAL("Unexpected load result type"); LOG_FATAL("Unexpected load result type");
break; break;
} }
e.mov(arg0, reinterpret_cast<uint64_t>(e.memory())); e.mov(arg0, reinterpret_cast<uint64_t>(e.memif().mem_self));
e.mov(arg1, a); e.mov(arg1, a);
e.call(reinterpret_cast<void *>(fn)); e.call(reinterpret_cast<void *>(fn));
e.mov(result, e.rax); e.mov(result, e.rax);
@ -483,44 +473,45 @@ EMITTER(LOAD_GUEST) {
} }
EMITTER(STORE_GUEST) { EMITTER(STORE_GUEST) {
if (instr->arg0()->constant()) { // TODO move to new constprop
// try to resolve the address to a physical page // if (instr->arg0()->constant()) {
uint32_t addr = static_cast<uint32_t>(instr->arg0()->i32()); // // try to resolve the address to a physical page
uint8_t *host_addr = nullptr; // uint32_t addr = static_cast<uint32_t>(instr->arg0()->i32());
MemoryRegion *bank = nullptr; // uint8_t *host_addr = nullptr;
uint32_t offset = 0; // MemoryRegion *bank = nullptr;
// uint32_t offset = 0;
e.memory()->Lookup(addr, &host_addr, &bank, &offset); // e.memory()->Lookup(addr, &host_addr, &bank, &offset);
if (host_addr) { // if (host_addr) {
const Xbyak::Reg b = e.GetRegister(instr->arg1()); // const Xbyak::Reg b = e.GetRegister(instr->arg1());
// FIXME it'd be nice if xbyak had a mov operation which would convert // // FIXME it'd be nice if xbyak had a mov operation which would convert
// the displacement to a RIP-relative address when finalizing code so // // the displacement to a RIP-relative address when finalizing code so
// we didn't have to store the absolute address in the scratch register // // we didn't have to store the absolute address in the scratch register
e.mov(e.rax, reinterpret_cast<uint64_t>(host_addr)); // e.mov(e.rax, reinterpret_cast<uint64_t>(host_addr));
switch (instr->arg1()->type()) { // switch (instr->arg1()->type()) {
case VALUE_I8: // case VALUE_I8:
e.mov(e.byte[e.rax], b); // e.mov(e.byte[e.rax], b);
break; // break;
case VALUE_I16: // case VALUE_I16:
e.mov(e.word[e.rax], b); // e.mov(e.word[e.rax], b);
break; // break;
case VALUE_I32: // case VALUE_I32:
e.mov(e.dword[e.rax], b); // e.mov(e.dword[e.rax], b);
break; // break;
case VALUE_I64: // case VALUE_I64:
e.mov(e.qword[e.rax], b); // e.mov(e.qword[e.rax], b);
break; // break;
default: // default:
LOG_FATAL("Unexpected store value type"); // LOG_FATAL("Unexpected store value type");
break; // break;
} // }
return; // return;
} // }
} // }
const Xbyak::Reg a = e.GetRegister(instr->arg0()); const Xbyak::Reg a = e.GetRegister(instr->arg0());
const Xbyak::Reg b = e.GetRegister(instr->arg1()); const Xbyak::Reg b = e.GetRegister(instr->arg1());
@ -529,27 +520,23 @@ EMITTER(STORE_GUEST) {
void *fn = nullptr; void *fn = nullptr;
switch (instr->arg1()->type()) { switch (instr->arg1()->type()) {
case VALUE_I8: case VALUE_I8:
fn = reinterpret_cast<void *>( fn = reinterpret_cast<void *>(e.memif().w8);
static_cast<void (*)(Memory *, uint32_t, uint8_t)>(&Memory::W8));
break; break;
case VALUE_I16: case VALUE_I16:
fn = reinterpret_cast<void *>( fn = reinterpret_cast<void *>(e.memif().w16);
static_cast<void (*)(Memory *, uint32_t, uint16_t)>(&Memory::W16));
break; break;
case VALUE_I32: case VALUE_I32:
fn = reinterpret_cast<void *>( fn = reinterpret_cast<void *>(e.memif().w32);
static_cast<void (*)(Memory *, uint32_t, uint32_t)>(&Memory::W32));
break; break;
case VALUE_I64: case VALUE_I64:
fn = reinterpret_cast<void *>( fn = reinterpret_cast<void *>(e.memif().w64);
static_cast<void (*)(Memory *, uint32_t, uint64_t)>(&Memory::W64));
break; break;
default: default:
LOG_FATAL("Unexpected store value type"); LOG_FATAL("Unexpected store value type");
break; break;
} }
e.mov(arg0, reinterpret_cast<uint64_t>(e.memory())); e.mov(arg0, reinterpret_cast<uint64_t>(e.memif().mem_self));
e.mov(arg1, a); e.mov(arg1, a);
e.mov(arg2, b); e.mov(arg2, b);
e.call(reinterpret_cast<void *>(fn)); e.call(reinterpret_cast<void *>(fn));
@ -1434,7 +1421,7 @@ EMITTER(BRANCH_COND) {
EMITTER(CALL_EXTERNAL) { EMITTER(CALL_EXTERNAL) {
const Xbyak::Reg addr = e.GetRegister(instr->arg0()); const Xbyak::Reg addr = e.GetRegister(instr->arg0());
e.mov(arg0, reinterpret_cast<uint64_t>(e.guest_ctx())); e.mov(arg0, reinterpret_cast<uint64_t>(e.memif().ctx_base));
if (instr->arg1()) { if (instr->arg1()) {
const Xbyak::Reg arg = e.GetRegister(instr->arg1()); const Xbyak::Reg arg = e.GetRegister(instr->arg1());
e.mov(arg1, arg); e.mov(arg1, arg);

View File

@ -2,13 +2,9 @@
#define X64_EMITTER_H #define X64_EMITTER_H
#include <xbyak/xbyak.h> #include <xbyak/xbyak.h>
#include "jit/backend/backend.h"
namespace re { namespace re {
namespace hw {
class Memory;
}
namespace jit { namespace jit {
namespace backend { namespace backend {
namespace x64 { namespace x64 {
@ -33,17 +29,15 @@ enum XmmConstant {
class X64Emitter : public Xbyak::CodeGenerator { class X64Emitter : public Xbyak::CodeGenerator {
public: public:
X64Emitter(void *buffer, size_t buffer_size); X64Emitter(const MemoryInterface &memif, void *buffer, size_t buffer_size);
~X64Emitter(); ~X64Emitter();
void *guest_ctx() { return guest_ctx_; } const MemoryInterface &memif() { return memif_; }
hw::Memory *memory() { return memory_; }
int block_flags() { return block_flags_; } int block_flags() { return block_flags_; }
void Reset(); void Reset();
BlockPointer Emit(ir::IRBuilder &builder, hw::Memory &memory, void *guest_ctx, BlockPointer Emit(ir::IRBuilder &builder, int block_flags);
int block_flags);
// helpers for the emitter callbacks // helpers for the emitter callbacks
const Xbyak::Reg GetRegister(const ir::Value *v); const Xbyak::Reg GetRegister(const ir::Value *v);
@ -58,8 +52,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
void EmitBody(ir::IRBuilder &builder); void EmitBody(ir::IRBuilder &builder);
void EmitEpilog(ir::IRBuilder &builder, int stack_size); void EmitEpilog(ir::IRBuilder &builder, int stack_size);
hw::Memory *memory_; MemoryInterface memif_;
void *guest_ctx_;
int block_flags_; int block_flags_;
int modified_marker_; int modified_marker_;
int *modified_; int *modified_;

View File

@ -1,29 +1,18 @@
#ifndef FRONTEND_H #ifndef FRONTEND_H
#define FRONTEND_H #define FRONTEND_H
#include <memory>
#include "jit/ir/ir_builder.h" #include "jit/ir/ir_builder.h"
namespace re { namespace re {
namespace hw {
class Memory;
}
namespace jit { namespace jit {
namespace frontend { namespace frontend {
class Frontend { class Frontend {
public: public:
Frontend(hw::Memory &memory, void *guest_ctx)
: memory_(memory), guest_ctx_(guest_ctx) {}
virtual ~Frontend() {} virtual ~Frontend() {}
virtual ir::IRBuilder &BuildBlock(uint32_t addr, int max_instrs) = 0; virtual ir::IRBuilder &BuildBlock(uint32_t guest_addr, uint8_t *host_addr,
int flags) = 0;
protected:
hw::Memory &memory_;
void *guest_ctx_;
}; };
} }
} }

View File

@ -1,10 +1,10 @@
#include "core/assert.h" #include "core/assert.h"
#include "emu/profiler.h" #include "core/memory.h"
#include "hw/memory.h" #include "core/profiler.h"
#include "jit/frontend/sh4/sh4_builder.h" #include "jit/frontend/sh4/sh4_builder.h"
#include "jit/frontend/sh4/sh4_frontend.h"
using namespace re; using namespace re;
using namespace re::hw;
using namespace re::jit; using namespace re::jit;
using namespace re::jit::frontend; using namespace re::jit::frontend;
using namespace re::jit::frontend::sh4; using namespace re::jit::frontend::sh4;
@ -14,17 +14,17 @@ static uint32_t s_fsca_table[0x20000] = {
#include "jit/frontend/sh4/sh4_fsca.inc" #include "jit/frontend/sh4/sh4_fsca.inc"
}; };
typedef void (*EmitCallback)(SH4Builder &b, const FPUState &, typedef void (*EmitCallback)(SH4Builder &b, const sh4::Instr &i, int,
const sh4::Instr &i, bool *endblock); bool *endblock);
#define EMITTER(name) \ #define EMITTER(name) \
void Emit_OP_##name(SH4Builder &b, const FPUState &fpu, const sh4::Instr &i, \ void Emit_OP_##name(SH4Builder &b, const sh4::Instr &i, int flags, \
bool *endblock) bool *endblock)
#define EMIT_DELAYED() \ #define EMIT_DELAYED() \
if (!b.EmitDelayInstr(i)) { \ if (!b.EmitDelayInstr(i, flags)) { \
*endblock = true; \ *endblock = true; \
return; \ return; \
} }
#define SH4_INSTR(name, desc, instr_code, cycles, flags) static EMITTER(name); #define SH4_INSTR(name, desc, instr_code, cycles, flags) static EMITTER(name);
@ -37,23 +37,20 @@ EmitCallback emit_callbacks[sh4::NUM_OPCODES] = {
#undef SH4_INSTR #undef SH4_INSTR
}; };
SH4Builder::SH4Builder(Arena &arena, Memory &memory, SH4Builder::SH4Builder(Arena &arena) : IRBuilder(arena) {}
const SH4Context &guest_ctx)
: IRBuilder(arena), memory_(memory), guest_ctx_(guest_ctx) {}
void SH4Builder::Emit(uint32_t start_addr, int max_instrs) { void SH4Builder::Emit(uint32_t guest_addr, uint8_t *host_addr, int flags) {
PROFILER_RUNTIME("SH4Builder::Emit"); PROFILER_RUNTIME("SH4Builder::Emit");
pc_ = start_addr; pc_ = guest_addr;
host_addr_ = host_addr;
guest_cycles_ = 0; guest_cycles_ = 0;
fpu_state_.double_pr = guest_ctx_.fpscr & PR;
fpu_state_.double_sz = guest_ctx_.fpscr & SZ;
// clamp block to max_instrs if non-zero // clamp block to max_instrs if non-zero
for (int i = 0; !max_instrs || i < max_instrs; i++) { while (true) {
Instr instr; Instr instr;
instr.addr = pc_; instr.addr = pc_;
instr.opcode = memory_.R16(instr.addr); instr.opcode = re::load<uint16_t>(host_addr_);
Disasm(&instr); Disasm(&instr);
if (!instr.type) { if (!instr.type) {
@ -62,11 +59,12 @@ void SH4Builder::Emit(uint32_t start_addr, int max_instrs) {
} }
pc_ += 2; pc_ += 2;
host_addr_ += 2;
guest_cycles_ += instr.type->cycles; guest_cycles_ += instr.type->cycles;
// emit the current instruction // emit the current instruction
bool endblock = false; bool endblock = false;
(emit_callbacks[instr.type->op])(*this, fpu_state_, instr, &endblock); (emit_callbacks[instr.type->op])(*this, instr, flags, &endblock);
// end block if delay instruction is invalid // end block if delay instruction is invalid
if (endblock) { if (endblock) {
@ -81,6 +79,11 @@ void SH4Builder::Emit(uint32_t start_addr, int max_instrs) {
(OP_FLAG_BRANCH | OP_FLAG_SET_FPSCR | OP_FLAG_SET_SR)) { (OP_FLAG_BRANCH | OP_FLAG_SET_FPSCR | OP_FLAG_SET_SR)) {
break; break;
} }
// used by gdb server when stepping through instructions
if (flags & SH4_SINGLE_INSTR) {
break;
}
} }
ir::Instr *tail_instr = instrs_.tail(); ir::Instr *tail_instr = instrs_.tail();
@ -100,7 +103,7 @@ void SH4Builder::Emit(uint32_t start_addr, int max_instrs) {
StoreContext(offsetof(SH4Context, num_cycles), num_cycles); StoreContext(offsetof(SH4Context, num_cycles), num_cycles);
// update num instructions // update num instructions
int sh4_num_instrs = (pc_ - start_addr) >> 1; int sh4_num_instrs = (pc_ - guest_addr) >> 1;
Value *num_instrs = LoadContext(offsetof(SH4Context, num_instrs), VALUE_I32); Value *num_instrs = LoadContext(offsetof(SH4Context, num_instrs), VALUE_I32);
num_instrs = Add(num_instrs, AllocConstant(sh4_num_instrs)); num_instrs = Add(num_instrs, AllocConstant(sh4_num_instrs));
StoreContext(offsetof(SH4Context, num_instrs), num_instrs); StoreContext(offsetof(SH4Context, num_instrs), num_instrs);
@ -144,7 +147,6 @@ void SH4Builder::StoreXFR(int n, Value *v) {
return StoreContext(offsetof(SH4Context, xf[n]), v); return StoreContext(offsetof(SH4Context, xf[n]), v);
} }
Value *SH4Builder::LoadSR() { Value *SH4Builder::LoadSR() {
return LoadContext(offsetof(SH4Context, sr), VALUE_I32); return LoadContext(offsetof(SH4Context, sr), VALUE_I32);
} }
@ -208,12 +210,12 @@ void SH4Builder::InvalidInstruction(uint32_t guest_addr) {
AllocConstant(static_cast<uint64_t>(guest_addr))); AllocConstant(static_cast<uint64_t>(guest_addr)));
} }
bool SH4Builder::EmitDelayInstr(const sh4::Instr &prev) { bool SH4Builder::EmitDelayInstr(const sh4::Instr &prev, int flags) {
CHECK(prev.type->flags & OP_FLAG_DELAYED); CHECK(prev.type->flags & OP_FLAG_DELAYED);
Instr delay; Instr delay;
delay.addr = prev.addr + 2; delay.addr = prev.addr + 2;
delay.opcode = memory_.R16(delay.addr); delay.opcode = re::load<uint16_t>(host_addr_);
Disasm(&delay); Disasm(&delay);
if (!delay.type) { if (!delay.type) {
@ -224,10 +226,11 @@ bool SH4Builder::EmitDelayInstr(const sh4::Instr &prev) {
CHECK(!(delay.type->flags & OP_FLAG_DELAYED)); CHECK(!(delay.type->flags & OP_FLAG_DELAYED));
pc_ += 2; pc_ += 2;
host_addr_ += 2;
guest_cycles_ += delay.type->cycles; guest_cycles_ += delay.type->cycles;
bool endblock = false; bool endblock = false;
(emit_callbacks[delay.type->op])(*this, fpu_state_, delay, &endblock); (emit_callbacks[delay.type->op])(*this, delay, flags, &endblock);
return true; return true;
} }
@ -1677,7 +1680,7 @@ EMITTER(FLDI1) { b.StoreFPR(i.Rn, b.AllocConstant(0x3F800000)); }
// FMOV DRm,XDn 1111nnn1mmm01100 // FMOV DRm,XDn 1111nnn1mmm01100
// FMOV XDm,XDn 1111nnn1mmm11100 // FMOV XDm,XDn 1111nnn1mmm11100
EMITTER(FMOV) { EMITTER(FMOV) {
if (fpu.double_sz) { if (flags & SH4_DOUBLE_SZ) {
if (i.Rm & 1) { if (i.Rm & 1) {
Value *rm = b.LoadXFR(i.Rm & 0xe, VALUE_I64); Value *rm = b.LoadXFR(i.Rm & 0xe, VALUE_I64);
if (i.Rn & 1) { if (i.Rn & 1) {
@ -1704,7 +1707,7 @@ EMITTER(FMOV) {
EMITTER(FMOV_LOAD) { EMITTER(FMOV_LOAD) {
Value *addr = b.LoadGPR(i.Rm, VALUE_I32); Value *addr = b.LoadGPR(i.Rm, VALUE_I32);
if (fpu.double_sz) { if (flags & SH4_DOUBLE_SZ) {
Value *v_low = b.LoadGuest(addr, VALUE_I32); Value *v_low = b.LoadGuest(addr, VALUE_I32);
Value *v_high = b.LoadGuest(b.Add(addr, b.AllocConstant(4)), VALUE_I32); Value *v_high = b.LoadGuest(b.Add(addr, b.AllocConstant(4)), VALUE_I32);
if (i.Rn & 1) { if (i.Rn & 1) {
@ -1725,7 +1728,7 @@ EMITTER(FMOV_LOAD) {
EMITTER(FMOV_INDEX_LOAD) { EMITTER(FMOV_INDEX_LOAD) {
Value *addr = b.Add(b.LoadGPR(0, VALUE_I32), b.LoadGPR(i.Rm, VALUE_I32)); Value *addr = b.Add(b.LoadGPR(0, VALUE_I32), b.LoadGPR(i.Rm, VALUE_I32));
if (fpu.double_sz) { if (flags & SH4_DOUBLE_SZ) {
Value *v_low = b.LoadGuest(addr, VALUE_I32); Value *v_low = b.LoadGuest(addr, VALUE_I32);
Value *v_high = b.LoadGuest(b.Add(addr, b.AllocConstant(4)), VALUE_I32); Value *v_high = b.LoadGuest(b.Add(addr, b.AllocConstant(4)), VALUE_I32);
if (i.Rn & 1) { if (i.Rn & 1) {
@ -1746,7 +1749,7 @@ EMITTER(FMOV_INDEX_LOAD) {
EMITTER(FMOV_STORE) { EMITTER(FMOV_STORE) {
Value *addr = b.LoadGPR(i.Rn, VALUE_I32); Value *addr = b.LoadGPR(i.Rn, VALUE_I32);
if (fpu.double_sz) { if (flags & SH4_DOUBLE_SZ) {
Value *addr_low = addr; Value *addr_low = addr;
Value *addr_high = b.Add(addr, b.AllocConstant(4)); Value *addr_high = b.Add(addr, b.AllocConstant(4));
if (i.Rm & 1) { if (i.Rm & 1) {
@ -1767,7 +1770,7 @@ EMITTER(FMOV_STORE) {
EMITTER(FMOV_INDEX_STORE) { EMITTER(FMOV_INDEX_STORE) {
Value *addr = b.Add(b.LoadGPR(0, VALUE_I32), b.LoadGPR(i.Rn, VALUE_I32)); Value *addr = b.Add(b.LoadGPR(0, VALUE_I32), b.LoadGPR(i.Rn, VALUE_I32));
if (fpu.double_sz) { if (flags & SH4_DOUBLE_SZ) {
Value *addr_low = addr; Value *addr_low = addr;
Value *addr_high = b.Add(addr, b.AllocConstant(4)); Value *addr_high = b.Add(addr, b.AllocConstant(4));
if (i.Rm & 1) { if (i.Rm & 1) {
@ -1786,7 +1789,7 @@ EMITTER(FMOV_INDEX_STORE) {
// FMOV DRm,@-Rn 1111nnnnmmm01011 // FMOV DRm,@-Rn 1111nnnnmmm01011
// FMOV XDm,@-Rn 1111nnnnmmm11011 // FMOV XDm,@-Rn 1111nnnnmmm11011
EMITTER(FMOV_SAVE) { EMITTER(FMOV_SAVE) {
if (fpu.double_sz) { if (flags & SH4_DOUBLE_SZ) {
Value *addr = b.Sub(b.LoadGPR(i.Rn, VALUE_I32), b.AllocConstant(8)); Value *addr = b.Sub(b.LoadGPR(i.Rn, VALUE_I32), b.AllocConstant(8));
b.StoreGPR(i.Rn, addr); b.StoreGPR(i.Rn, addr);
@ -1813,7 +1816,7 @@ EMITTER(FMOV_SAVE) {
EMITTER(FMOV_RESTORE) { EMITTER(FMOV_RESTORE) {
Value *addr = b.LoadGPR(i.Rm, VALUE_I32); Value *addr = b.LoadGPR(i.Rm, VALUE_I32);
if (fpu.double_sz) { if (flags & SH4_DOUBLE_SZ) {
Value *v_low = b.LoadGuest(addr, VALUE_I32); Value *v_low = b.LoadGuest(addr, VALUE_I32);
Value *v_high = b.LoadGuest(b.Add(addr, b.AllocConstant(4)), VALUE_I32); Value *v_high = b.LoadGuest(b.Add(addr, b.AllocConstant(4)), VALUE_I32);
if (i.Rn & 1) { if (i.Rn & 1) {
@ -1845,7 +1848,7 @@ EMITTER(FSTS) {
// FABS FRn PR=0 1111nnnn01011101 // FABS FRn PR=0 1111nnnn01011101
// FABS DRn PR=1 1111nnn001011101 // FABS DRn PR=1 1111nnn001011101
EMITTER(FABS) { EMITTER(FABS) {
if (fpu.double_pr) { if (flags & SH4_DOUBLE_PR) {
int n = i.Rn & 0xe; int n = i.Rn & 0xe;
Value *v = b.FAbs(b.LoadFPR(n, VALUE_F64)); Value *v = b.FAbs(b.LoadFPR(n, VALUE_F64));
b.StoreFPR(n, v); b.StoreFPR(n, v);
@ -1865,7 +1868,7 @@ EMITTER(FSRRA) {
// FADD FRm,FRn PR=0 1111nnnnmmmm0000 // FADD FRm,FRn PR=0 1111nnnnmmmm0000
// FADD DRm,DRn PR=1 1111nnn0mmm00000 // FADD DRm,DRn PR=1 1111nnn0mmm00000
EMITTER(FADD) { EMITTER(FADD) {
if (fpu.double_pr) { if (flags & SH4_DOUBLE_PR) {
int n = i.Rn & 0xe; int n = i.Rn & 0xe;
int m = i.Rm & 0xe; int m = i.Rm & 0xe;
Value *drn = b.LoadFPR(n, VALUE_F64); Value *drn = b.LoadFPR(n, VALUE_F64);
@ -1883,7 +1886,7 @@ EMITTER(FADD) {
// FCMP/EQ FRm,FRn PR=0 1111nnnnmmmm0100 // FCMP/EQ FRm,FRn PR=0 1111nnnnmmmm0100
// FCMP/EQ DRm,DRn PR=1 1111nnn0mmm00100 // FCMP/EQ DRm,DRn PR=1 1111nnn0mmm00100
EMITTER(FCMPEQ) { EMITTER(FCMPEQ) {
if (fpu.double_pr) { if (flags & SH4_DOUBLE_PR) {
int n = i.Rn & 0xe; int n = i.Rn & 0xe;
int m = i.Rm & 0xe; int m = i.Rm & 0xe;
Value *drn = b.LoadFPR(n, VALUE_F64); Value *drn = b.LoadFPR(n, VALUE_F64);
@ -1901,7 +1904,7 @@ EMITTER(FCMPEQ) {
// FCMP/GT FRm,FRn PR=0 1111nnnnmmmm0101 // FCMP/GT FRm,FRn PR=0 1111nnnnmmmm0101
// FCMP/GT DRm,DRn PR=1 1111nnn0mmm00101 // FCMP/GT DRm,DRn PR=1 1111nnn0mmm00101
EMITTER(FCMPGT) { EMITTER(FCMPGT) {
if (fpu.double_pr) { if (flags & SH4_DOUBLE_PR) {
int n = i.Rn & 0xe; int n = i.Rn & 0xe;
int m = i.Rm & 0xe; int m = i.Rm & 0xe;
Value *drn = b.LoadFPR(n, VALUE_F64); Value *drn = b.LoadFPR(n, VALUE_F64);
@ -1919,7 +1922,7 @@ EMITTER(FCMPGT) {
// FDIV FRm,FRn PR=0 1111nnnnmmmm0011 // FDIV FRm,FRn PR=0 1111nnnnmmmm0011
// FDIV DRm,DRn PR=1 1111nnn0mmm00011 // FDIV DRm,DRn PR=1 1111nnn0mmm00011
EMITTER(FDIV) { EMITTER(FDIV) {
if (fpu.double_pr) { if (flags & SH4_DOUBLE_PR) {
int n = i.Rn & 0xe; int n = i.Rn & 0xe;
int m = i.Rm & 0xe; int m = i.Rm & 0xe;
Value *drn = b.LoadFPR(n, VALUE_F64); Value *drn = b.LoadFPR(n, VALUE_F64);
@ -1939,7 +1942,7 @@ EMITTER(FDIV) {
EMITTER(FLOAT) { EMITTER(FLOAT) {
Value *fpul = b.LoadContext(offsetof(SH4Context, fpul), VALUE_I32); Value *fpul = b.LoadContext(offsetof(SH4Context, fpul), VALUE_I32);
if (fpu.double_pr) { if (flags & SH4_DOUBLE_PR) {
int n = i.Rn & 0xe; int n = i.Rn & 0xe;
Value *v = b.IToF(b.SExt(fpul, VALUE_I64), VALUE_F64); Value *v = b.IToF(b.SExt(fpul, VALUE_I64), VALUE_F64);
b.StoreFPR(n, v); b.StoreFPR(n, v);
@ -1951,7 +1954,7 @@ EMITTER(FLOAT) {
// FMAC FR0,FRm,FRn PR=0 1111nnnnmmmm1110 // FMAC FR0,FRm,FRn PR=0 1111nnnnmmmm1110
EMITTER(FMAC) { EMITTER(FMAC) {
CHECK(!fpu.double_pr); CHECK(!(flags & SH4_DOUBLE_PR));
Value *frn = b.LoadFPR(i.Rn, VALUE_F32); Value *frn = b.LoadFPR(i.Rn, VALUE_F32);
Value *frm = b.LoadFPR(i.Rm, VALUE_F32); Value *frm = b.LoadFPR(i.Rm, VALUE_F32);
@ -1963,7 +1966,7 @@ EMITTER(FMAC) {
// FMUL FRm,FRn PR=0 1111nnnnmmmm0010 // FMUL FRm,FRn PR=0 1111nnnnmmmm0010
// FMUL DRm,DRn PR=1 1111nnn0mmm00010 // FMUL DRm,DRn PR=1 1111nnn0mmm00010
EMITTER(FMUL) { EMITTER(FMUL) {
if (fpu.double_pr) { if (flags & SH4_DOUBLE_PR) {
int n = i.Rn & 0xe; int n = i.Rn & 0xe;
int m = i.Rm & 0xe; int m = i.Rm & 0xe;
Value *drn = b.LoadFPR(n, VALUE_F64); Value *drn = b.LoadFPR(n, VALUE_F64);
@ -1981,7 +1984,7 @@ EMITTER(FMUL) {
// FNEG FRn PR=0 1111nnnn01001101 // FNEG FRn PR=0 1111nnnn01001101
// FNEG DRn PR=1 1111nnn001001101 // FNEG DRn PR=1 1111nnn001001101
EMITTER(FNEG) { EMITTER(FNEG) {
if (fpu.double_pr) { if (flags & SH4_DOUBLE_PR) {
int n = i.Rn & 0xe; int n = i.Rn & 0xe;
Value *drn = b.LoadFPR(n, VALUE_F64); Value *drn = b.LoadFPR(n, VALUE_F64);
Value *v = b.FNeg(drn); Value *v = b.FNeg(drn);
@ -1996,7 +1999,7 @@ EMITTER(FNEG) {
// FSQRT FRn PR=0 1111nnnn01101101 // FSQRT FRn PR=0 1111nnnn01101101
// FSQRT DRn PR=1 1111nnnn01101101 // FSQRT DRn PR=1 1111nnnn01101101
EMITTER(FSQRT) { EMITTER(FSQRT) {
if (fpu.double_pr) { if (flags & SH4_DOUBLE_PR) {
int n = i.Rn & 0xe; int n = i.Rn & 0xe;
Value *drn = b.LoadFPR(n, VALUE_F64); Value *drn = b.LoadFPR(n, VALUE_F64);
Value *v = b.Sqrt(drn); Value *v = b.Sqrt(drn);
@ -2011,7 +2014,7 @@ EMITTER(FSQRT) {
// FSUB FRm,FRn PR=0 1111nnnnmmmm0001 // FSUB FRm,FRn PR=0 1111nnnnmmmm0001
// FSUB DRm,DRn PR=1 1111nnn0mmm00001 // FSUB DRm,DRn PR=1 1111nnn0mmm00001
EMITTER(FSUB) { EMITTER(FSUB) {
if (fpu.double_pr) { if (flags & SH4_DOUBLE_PR) {
int n = i.Rn & 0xe; int n = i.Rn & 0xe;
int m = i.Rm & 0xe; int m = i.Rm & 0xe;
Value *drn = b.LoadFPR(n, VALUE_F64); Value *drn = b.LoadFPR(n, VALUE_F64);
@ -2029,7 +2032,7 @@ EMITTER(FSUB) {
// FTRC FRm,FPUL PR=0 1111mmmm00111101 // FTRC FRm,FPUL PR=0 1111mmmm00111101
// FTRC DRm,FPUL PR=1 1111mmm000111101 // FTRC DRm,FPUL PR=1 1111mmm000111101
EMITTER(FTRC) { EMITTER(FTRC) {
if (fpu.double_pr) { if (flags & SH4_DOUBLE_PR) {
int m = i.Rm & 0xe; int m = i.Rm & 0xe;
Value *drm = b.LoadFPR(m, VALUE_F64); Value *drm = b.LoadFPR(m, VALUE_F64);
Value *dpv = b.Trunc(b.FToI(drm, VALUE_I64), VALUE_I32); Value *dpv = b.Trunc(b.FToI(drm, VALUE_I64), VALUE_I32);
@ -2043,7 +2046,7 @@ EMITTER(FTRC) {
// FCNVDS DRm,FPUL PR=1 1111mmm010111101 // FCNVDS DRm,FPUL PR=1 1111mmm010111101
EMITTER(FCNVDS) { EMITTER(FCNVDS) {
CHECK(fpu.double_pr); CHECK(flags & SH4_DOUBLE_PR);
// TODO rounding modes? // TODO rounding modes?
@ -2055,7 +2058,7 @@ EMITTER(FCNVDS) {
// FCNVSD FPUL, DRn PR=1 1111nnn010101101 // FCNVSD FPUL, DRn PR=1 1111nnn010101101
EMITTER(FCNVSD) { EMITTER(FCNVSD) {
CHECK(fpu.double_pr); CHECK(flags & SH4_DOUBLE_PR);
// TODO rounding modes? // TODO rounding modes?
@ -2155,16 +2158,9 @@ EMITTER(FSCA) {
EMITTER(FTRV) { EMITTER(FTRV) {
int n = i.Rn << 2; int n = i.Rn << 2;
// XF0 XF4 XF8 XF12 FR0 XF0 * FR0 + XF4 * FR1 + XF8 * FR2 + XF12 * FR3
// XF1 XF5 XF9 XF13 * FR1 = XF1 * FR0 + XF5 * FR1 + XF9 * FR2 + XF13 * FR3
// XF2 XF6 XF10 XF14 FR2 XF2 * FR0 + XF6 * FR1 + XF10 * FR2 + XF14 * FR3
// XF3 XF7 XF11 XF15 FR3 XF3 * FR0 + XF7 * FR1 + XF11 * FR2 + XF15 * FR3
Value *result = nullptr;
Value *col0 = b.LoadXFR(0, VALUE_V128); Value *col0 = b.LoadXFR(0, VALUE_V128);
Value *row0 = b.VBroadcast(b.LoadFPR(n + 0, VALUE_F32)); Value *row0 = b.VBroadcast(b.LoadFPR(n + 0, VALUE_F32));
result = b.VMul(col0, row0, VALUE_F32); Value *result = b.VMul(col0, row0, VALUE_F32);
Value *col1 = b.LoadXFR(4, VALUE_V128); Value *col1 = b.LoadXFR(4, VALUE_V128);
Value *row1 = b.VBroadcast(b.LoadFPR(n + 1, VALUE_F32)); Value *row1 = b.VBroadcast(b.LoadFPR(n + 1, VALUE_F32));

View File

@ -15,16 +15,11 @@ namespace jit {
namespace frontend { namespace frontend {
namespace sh4 { namespace sh4 {
struct FPUState {
bool double_pr;
bool double_sz;
};
class SH4Builder : public ir::IRBuilder { class SH4Builder : public ir::IRBuilder {
public: public:
SH4Builder(Arena &arena, hw::Memory &memory, const SH4Context &guest_ctx); SH4Builder(Arena &arena);
void Emit(uint32_t addr, int max_instrs); void Emit(uint32_t guest_addr, uint8_t *host_addr, int flags);
ir::Value *LoadGPR(int n, ir::ValueType type); ir::Value *LoadGPR(int n, ir::ValueType type);
void StoreGPR(int n, ir::Value *v); void StoreGPR(int n, ir::Value *v);
@ -45,14 +40,12 @@ class SH4Builder : public ir::IRBuilder {
void InvalidInstruction(uint32_t guest_addr); void InvalidInstruction(uint32_t guest_addr);
bool EmitDelayInstr(const Instr &prev); bool EmitDelayInstr(const Instr &prev, int flags);
private: private:
hw::Memory &memory_;
const SH4Context &guest_ctx_;
uint32_t pc_; uint32_t pc_;
uint8_t *host_addr_;
int guest_cycles_; int guest_cycles_;
FPUState fpu_state_;
}; };
} }
} }

View File

@ -1,6 +1,6 @@
#include "core/assert.h"
#include "core/memory.h" #include "core/memory.h"
#include "core/string.h" #include "core/string.h"
#include "hw/memory.h"
#include "jit/frontend/sh4/sh4_disassembler.h" #include "jit/frontend/sh4/sh4_disassembler.h"
using namespace re; using namespace re;

View File

@ -1,23 +1,21 @@
#include "jit/frontend/sh4/sh4_builder.h" #include "jit/frontend/sh4/sh4_builder.h"
#include "jit/frontend/sh4/sh4_frontend.h" #include "jit/frontend/sh4/sh4_frontend.h"
using namespace re::hw;
using namespace re::jit; using namespace re::jit;
using namespace re::jit::frontend::sh4; using namespace re::jit::frontend::sh4;
using namespace re::jit::ir; using namespace re::jit::ir;
SH4Frontend::SH4Frontend(Memory &memory, void *guest_ctx) SH4Frontend::SH4Frontend() : arena_(4096) {}
: Frontend(memory, guest_ctx), arena_(4096) {}
IRBuilder &SH4Frontend::BuildBlock(uint32_t addr, int max_instrs) { IRBuilder &SH4Frontend::BuildBlock(uint32_t guest_addr, uint8_t *host_addr,
int flags) {
arena_.Reset(); arena_.Reset();
SH4Builder *builder = arena_.Alloc<SH4Builder>(); SH4Builder *builder = arena_.Alloc<SH4Builder>();
new (builder) SH4Builder(arena_, memory_, new (builder) SH4Builder(arena_);
*reinterpret_cast<const SH4Context *>(guest_ctx_));
builder->Emit(addr, max_instrs); builder->Emit(guest_addr, host_addr, flags);
return *builder; return *builder;
} }

View File

@ -9,11 +9,17 @@ namespace jit {
namespace frontend { namespace frontend {
namespace sh4 { namespace sh4 {
enum SH4BlockFlags {
SH4_SINGLE_INSTR = 0x1,
SH4_DOUBLE_PR = 0x2,
SH4_DOUBLE_SZ = 0x4,
};
class SH4Frontend : public Frontend { class SH4Frontend : public Frontend {
public: public:
SH4Frontend(hw::Memory &memory, void *guest_ctx); SH4Frontend();
ir::IRBuilder &BuildBlock(uint32_t addr, int max_instrs); ir::IRBuilder &BuildBlock(uint32_t guest_addr, uint8_t *host_addr, int flags);
private: private:
Arena arena_; Arena arena_;

View File

@ -33,6 +33,9 @@ void IRWriter::PrintType(ValueType type, std::ostream &output) const {
case VALUE_F64: case VALUE_F64:
output << "f64"; output << "f64";
break; break;
case VALUE_V128:
output << "v128";
break;
default: default:
LOG_FATAL("Unexpected value type"); LOG_FATAL("Unexpected value type");
break; break;
@ -53,17 +56,7 @@ void IRWriter::PrintValue(const Value *value, std::ostream &output) {
output << " "; output << " ";
if (!value->constant()) { if (value->constant()) {
uintptr_t key = reinterpret_cast<uintptr_t>(value);
auto it = slots_.find(key);
if (it == slots_.end()) {
auto res = slots_.insert(std::make_pair(key, next_slot_++));
it = res.first;
}
output << "%" << it->second;
} else {
switch (value->type()) { switch (value->type()) {
case VALUE_I8: case VALUE_I8:
output << "0x" << std::hex << value->i8() << std::dec; output << "0x" << std::hex << value->i8() << std::dec;
@ -87,6 +80,16 @@ void IRWriter::PrintValue(const Value *value, std::ostream &output) {
LOG_FATAL("Unexpected value type"); LOG_FATAL("Unexpected value type");
break; break;
} }
} else {
uintptr_t key = reinterpret_cast<uintptr_t>(value);
auto it = slots_.find(key);
if (it == slots_.end()) {
auto res = slots_.insert(std::make_pair(key, next_slot_++));
it = res.first;
}
output << "%" << it->second;
} }
} }

View File

@ -1,6 +1,5 @@
#include <type_traits> #include <type_traits>
#include <unordered_map> #include <unordered_map>
#include "emu/profiler.h"
#include "jit/ir/passes/constant_propagation_pass.h" #include "jit/ir/passes/constant_propagation_pass.h"
using namespace re::jit::ir; using namespace re::jit::ir;
@ -91,8 +90,6 @@ static int GetConstantSig(Instr *instr) {
} }
void ConstantPropagationPass::Run(IRBuilder &builder, bool debug) { void ConstantPropagationPass::Run(IRBuilder &builder, bool debug) {
PROFILER_RUNTIME("ConstantPropagationPass::Run");
auto it = builder.instrs().begin(); auto it = builder.instrs().begin();
auto end = builder.instrs().end(); auto end = builder.instrs().end();

View File

@ -10,7 +10,7 @@ namespace passes {
class ConstantPropagationPass : public Pass { class ConstantPropagationPass : public Pass {
public: public:
const char *name() { return "Constant Propagation Pass"; } const char *name() { return "constprop"; }
void Run(IRBuilder &builder, bool debug); void Run(IRBuilder &builder, bool debug);
}; };

View File

@ -1,4 +1,3 @@
#include "emu/profiler.h"
#include "jit/ir/passes/dead_code_elimination_pass.h" #include "jit/ir/passes/dead_code_elimination_pass.h"
using namespace re::jit::backend; using namespace re::jit::backend;
@ -6,8 +5,6 @@ using namespace re::jit::ir;
using namespace re::jit::ir::passes; using namespace re::jit::ir::passes;
void DeadCodeEliminationPass::Run(IRBuilder &builder, bool debug) { void DeadCodeEliminationPass::Run(IRBuilder &builder, bool debug) {
PROFILER_RUNTIME("DeadCodeEliminationPass::Run");
// iterate in reverse in order to remove groups of dead instructions that // iterate in reverse in order to remove groups of dead instructions that
// only use eachother // only use eachother
auto it = builder.instrs().rbegin(); auto it = builder.instrs().rbegin();

View File

@ -11,7 +11,7 @@ namespace passes {
class DeadCodeEliminationPass : public Pass { class DeadCodeEliminationPass : public Pass {
public: public:
const char *name() { return "Dead Code Elimination Pass"; } const char *name() { return "dce"; }
void Run(IRBuilder &builder, bool debug); void Run(IRBuilder &builder, bool debug);
}; };

View File

@ -1,4 +1,3 @@
#include "emu/profiler.h"
#include "jit/ir/passes/load_store_elimination_pass.h" #include "jit/ir/passes/load_store_elimination_pass.h"
using namespace re::jit::ir; using namespace re::jit::ir;
@ -8,8 +7,6 @@ LoadStoreEliminationPass::LoadStoreEliminationPass()
: available_(nullptr), num_available_(0) {} : available_(nullptr), num_available_(0) {}
void LoadStoreEliminationPass::Run(IRBuilder &builder, bool debug) { void LoadStoreEliminationPass::Run(IRBuilder &builder, bool debug) {
PROFILER_RUNTIME("LoadStoreEliminationPass::Run");
Reset(); Reset();
// eliminate redundant loads // eliminate redundant loads

View File

@ -17,7 +17,7 @@ class LoadStoreEliminationPass : public Pass {
public: public:
LoadStoreEliminationPass(); LoadStoreEliminationPass();
const char *name() { return "Load / Store Elimination Pass"; } const char *name() { return "lse"; }
void Run(IRBuilder &builder, bool debug); void Run(IRBuilder &builder, bool debug);

View File

@ -1,4 +1,4 @@
#include "emu/profiler.h" #include "core/profiler.h"
#include "jit/ir/ir_builder.h" #include "jit/ir/ir_builder.h"
#include "jit/ir/passes/pass_runner.h" #include "jit/ir/passes/pass_runner.h"
@ -15,12 +15,14 @@ void PassRunner::Run(IRBuilder &builder, bool debug) {
PROFILER_RUNTIME("PassRunner::Run"); PROFILER_RUNTIME("PassRunner::Run");
if (debug) { if (debug) {
LOG_INFO("Original:"); LOG_INFO("original:");
builder.Dump(); builder.Dump();
LOG_INFO(""); LOG_INFO("");
} }
for (auto &pass : passes_) { for (auto &pass : passes_) {
PROFILER_RUNTIME(pass->name());
pass->Run(builder, debug); pass->Run(builder, debug);
if (debug) { if (debug) {

View File

@ -1,5 +1,4 @@
#include "core/minmax_heap.h" #include "core/minmax_heap.h"
#include "emu/profiler.h"
#include "jit/ir/passes/register_allocation_pass.h" #include "jit/ir/passes/register_allocation_pass.h"
using namespace re::jit::backend; using namespace re::jit::backend;
@ -93,8 +92,6 @@ RegisterAllocationPass::RegisterAllocationPass(const Backend &backend)
RegisterAllocationPass::~RegisterAllocationPass() { delete[] intervals_; } RegisterAllocationPass::~RegisterAllocationPass() { delete[] intervals_; }
void RegisterAllocationPass::Run(IRBuilder &builder, bool debug) { void RegisterAllocationPass::Run(IRBuilder &builder, bool debug) {
PROFILER_RUNTIME("RegisterAllocationPass::Run");
Reset(); Reset();
AssignOrdinals(builder); AssignOrdinals(builder);

View File

@ -49,7 +49,7 @@ class RegisterAllocationPass : public Pass {
RegisterAllocationPass(const backend::Backend &backend); RegisterAllocationPass(const backend::Backend &backend);
~RegisterAllocationPass(); ~RegisterAllocationPass();
const char *name() { return "Register Allocation Pass"; } const char *name() { return "ra"; }
void Run(IRBuilder &builder, bool debug); void Run(IRBuilder &builder, bool debug);

View File

@ -1,5 +1,5 @@
#include "core/assert.h" #include "core/assert.h"
#include "emu/profiler.h" #include "core/profiler.h"
#include "renderer/gl_backend.h" #include "renderer/gl_backend.h"
using namespace re; using namespace re;

View File

@ -1,5 +1,5 @@
#include "core/interval_tree.h" #include "core/interval_tree.h"
#include "emu/profiler.h" #include "core/profiler.h"
#include "sys/exception_handler.h" #include "sys/exception_handler.h"
using namespace re::sys; using namespace re::sys;

87
tools/recc.cc Normal file
View File

@ -0,0 +1,87 @@
#include <fstream>
#include <memory>
#include <sstream>
#include <gflags/gflags.h>
#include "jit/backend/x64/x64_backend.h"
#include "jit/frontend/sh4/sh4_frontend.h"
#include "jit/ir/ir_builder.h"
#include "jit/ir/ir_reader.h"
#include "jit/ir/passes/dead_code_elimination_pass.h"
#include "jit/ir/passes/load_store_elimination_pass.h"
using namespace re;
using namespace re::jit::ir;
using namespace re::jit::ir::passes;
DEFINE_string(pass, "lse,dce", "Comma-separated list of passes to run");
DEFINE_bool(debug, false, "Enable debug spew for passes");
static std::vector<std::string> split(const std::string &s, char delim) {
std::stringstream ss(s);
std::string item;
std::vector<std::string> elems;
while (std::getline(ss, item, delim)) {
elems.push_back(std::move(item));
}
return elems;
}
static int get_num_instrs(IRBuilder &builder) {
int n = 0;
for (auto instr : builder.instrs()) {
((void)instr);
n++;
}
return n;
}
int main(int argc, const char **argv) {
const char *file = argv[1];
Arena arena(4096);
IRBuilder builder(arena);
// read in the input ir
IRReader reader;
std::ifstream input_stream(file);
CHECK(reader.Parse(input_stream, builder));
// run optimization passes
std::vector<std::string> passes = split(FLAGS_pass, ',');
for (auto name : passes) {
std::unique_ptr<Pass> pass;
int num_instrs_before = get_num_instrs(builder);
if (name == "lse") {
pass = std::unique_ptr<Pass>(new LoadStoreEliminationPass());
} else if (name == "dce") {
pass = std::unique_ptr<Pass>(new DeadCodeEliminationPass());
} else {
LOG_WARNING("Unknown pass %s", name.c_str());
}
pass->Run(builder, FLAGS_debug);
int num_instrs_after = get_num_instrs(builder);
// print out the resulting ir
LOG_INFO("%s:", pass->name());
builder.Dump();
// print out stats about the optimization pass
if (num_instrs_after <= num_instrs_before) {
int delta = num_instrs_before - num_instrs_after;
LOG_INFO(ANSI_COLOR_GREEN "%d (%.2f%%) instructions removed" ANSI_COLOR_RESET,
delta, (delta / static_cast<float>(num_instrs_before)) * 100.0f);
} else {
int delta = num_instrs_after - num_instrs_before;
LOG_INFO(ANSI_COLOR_RED "%d (%.2f%%) instructions added" ANSI_COLOR_RESET,
delta, (delta / static_cast<float>(num_instrs_before)) * 100.0f);
}
LOG_INFO("");
}
return 0;
}