Merge branch 'master' into d3d12

This commit is contained in:
Triang3l
2018-11-24 16:26:27 +03:00
48 changed files with 10195 additions and 5730 deletions

View File

@@ -16,6 +16,7 @@ project("xenia-app")
"imgui",
"libavcodec",
"libavutil",
"mspack",
"snappy",
"spirv-tools",
"volk",

View File

@@ -42,6 +42,15 @@ class X64ThunkEmitter : public X64Emitter {
HostToGuestThunk EmitHostToGuestThunk();
GuestToHostThunk EmitGuestToHostThunk();
ResolveFunctionThunk EmitResolveFunctionThunk();
private:
// The following four functions provide save/load functionality for registers.
// They assume at least StackLayout::THUNK_STACK_SIZE bytes have been
// allocated on the stack.
void EmitSaveVolatileRegs();
void EmitLoadVolatileRegs();
void EmitSaveNonvolatileRegs();
void EmitLoadNonvolatileRegs();
};
X64Backend::X64Backend() : Backend(), code_cache_(nullptr) {
@@ -73,8 +82,6 @@ bool X64Backend::Initialize(Processor* processor) {
return false;
}
RegisterSequences();
// Need movbe to do advanced LOAD/STORE tricks.
if (FLAGS_enable_haswell_instructions) {
machine_info_.supports_extended_load_store =
@@ -406,6 +413,117 @@ HostToGuestThunk X64ThunkEmitter::EmitHostToGuestThunk() {
mov(qword[rsp + 8 * 1], rcx);
sub(rsp, stack_size);
// Save nonvolatile registers.
EmitSaveNonvolatileRegs();
mov(rax, rcx);
mov(rsi, rdx); // context
mov(rcx, r8); // return address
call(rax);
EmitLoadNonvolatileRegs();
add(rsp, stack_size);
mov(rcx, qword[rsp + 8 * 1]);
mov(rdx, qword[rsp + 8 * 2]);
mov(r8, qword[rsp + 8 * 3]);
ret();
void* fn = Emplace(stack_size);
return (HostToGuestThunk)fn;
}
GuestToHostThunk X64ThunkEmitter::EmitGuestToHostThunk() {
// rcx = target function
// rdx = arg0
// r8 = arg1
// r9 = arg2
const size_t stack_size = StackLayout::THUNK_STACK_SIZE;
// rsp + 0 = return address
sub(rsp, stack_size);
// Save off volatile registers.
EmitSaveVolatileRegs();
mov(rax, rcx); // function
mov(rcx, GetContextReg()); // context
call(rax);
EmitLoadVolatileRegs();
add(rsp, stack_size);
ret();
void* fn = Emplace(stack_size);
return (GuestToHostThunk)fn;
}
// X64Emitter handles actually resolving functions.
extern "C" uint64_t ResolveFunction(void* raw_context, uint32_t target_address);
ResolveFunctionThunk X64ThunkEmitter::EmitResolveFunctionThunk() {
// ebx = target PPC address
// rcx = context
const size_t stack_size = StackLayout::THUNK_STACK_SIZE;
// rsp + 0 = return address
sub(rsp, stack_size);
// Save volatile registers
EmitSaveVolatileRegs();
mov(rcx, rsi); // context
mov(rdx, rbx);
mov(rax, uint64_t(&ResolveFunction));
call(rax);
EmitLoadVolatileRegs();
add(rsp, stack_size);
jmp(rax);
void* fn = Emplace(stack_size);
return (ResolveFunctionThunk)fn;
}
void X64ThunkEmitter::EmitSaveVolatileRegs() {
// Save off volatile registers.
// mov(qword[rsp + offsetof(StackLayout::Thunk, r[0])], rax);
mov(qword[rsp + offsetof(StackLayout::Thunk, r[1])], rcx);
mov(qword[rsp + offsetof(StackLayout::Thunk, r[2])], rdx);
mov(qword[rsp + offsetof(StackLayout::Thunk, r[3])], r8);
mov(qword[rsp + offsetof(StackLayout::Thunk, r[4])], r9);
mov(qword[rsp + offsetof(StackLayout::Thunk, r[5])], r10);
mov(qword[rsp + offsetof(StackLayout::Thunk, r[6])], r11);
// movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[0])], xmm0);
movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[1])], xmm1);
movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[2])], xmm2);
movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[3])], xmm3);
movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[4])], xmm4);
movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[5])], xmm5);
}
void X64ThunkEmitter::EmitLoadVolatileRegs() {
// Load volatile registers from our stack frame.
// movaps(xmm0, qword[rsp + offsetof(StackLayout::Thunk, xmm[0])]);
movaps(xmm1, qword[rsp + offsetof(StackLayout::Thunk, xmm[1])]);
movaps(xmm2, qword[rsp + offsetof(StackLayout::Thunk, xmm[2])]);
movaps(xmm3, qword[rsp + offsetof(StackLayout::Thunk, xmm[3])]);
movaps(xmm4, qword[rsp + offsetof(StackLayout::Thunk, xmm[4])]);
movaps(xmm5, qword[rsp + offsetof(StackLayout::Thunk, xmm[5])]);
// mov(rax, qword[rsp + offsetof(StackLayout::Thunk, r[0])]);
mov(rcx, qword[rsp + offsetof(StackLayout::Thunk, r[1])]);
mov(rdx, qword[rsp + offsetof(StackLayout::Thunk, r[2])]);
mov(r8, qword[rsp + offsetof(StackLayout::Thunk, r[3])]);
mov(r9, qword[rsp + offsetof(StackLayout::Thunk, r[4])]);
mov(r10, qword[rsp + offsetof(StackLayout::Thunk, r[5])]);
mov(r11, qword[rsp + offsetof(StackLayout::Thunk, r[6])]);
}
void X64ThunkEmitter::EmitSaveNonvolatileRegs() {
// Preserve nonvolatile registers.
mov(qword[rsp + offsetof(StackLayout::Thunk, r[0])], rbx);
mov(qword[rsp + offsetof(StackLayout::Thunk, r[1])], rcx);
@@ -427,12 +545,9 @@ HostToGuestThunk X64ThunkEmitter::EmitHostToGuestThunk() {
movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[7])], xmm13);
movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[8])], xmm14);
movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[9])], xmm15);
}
mov(rax, rcx);
mov(rsi, rdx); // context
mov(rcx, r8); // return address
call(rax);
void X64ThunkEmitter::EmitLoadNonvolatileRegs() {
movaps(xmm6, qword[rsp + offsetof(StackLayout::Thunk, xmm[0])]);
movaps(xmm7, qword[rsp + offsetof(StackLayout::Thunk, xmm[1])]);
movaps(xmm8, qword[rsp + offsetof(StackLayout::Thunk, xmm[2])]);
@@ -453,100 +568,6 @@ HostToGuestThunk X64ThunkEmitter::EmitHostToGuestThunk() {
mov(r13, qword[rsp + offsetof(StackLayout::Thunk, r[6])]);
mov(r14, qword[rsp + offsetof(StackLayout::Thunk, r[7])]);
mov(r15, qword[rsp + offsetof(StackLayout::Thunk, r[8])]);
add(rsp, stack_size);
mov(rcx, qword[rsp + 8 * 1]);
mov(rdx, qword[rsp + 8 * 2]);
mov(r8, qword[rsp + 8 * 3]);
ret();
void* fn = Emplace(stack_size);
return (HostToGuestThunk)fn;
}
GuestToHostThunk X64ThunkEmitter::EmitGuestToHostThunk() {
// rcx = context
// rdx = target function
// r8 = arg0
// r9 = arg1
// r10 = arg2
const size_t stack_size = StackLayout::THUNK_STACK_SIZE;
// rsp + 0 = return address
mov(qword[rsp + 8 * 2], rdx);
mov(qword[rsp + 8 * 1], rcx);
sub(rsp, stack_size);
// Save off volatile registers.
// TODO(DrChat): Enable this when we actually need this.
// mov(qword[rsp + offsetof(StackLayout::Thunk, r[0])], rcx);
// mov(qword[rsp + offsetof(StackLayout::Thunk, r[1])], rdx);
// mov(qword[rsp + offsetof(StackLayout::Thunk, r[2])], r8);
// mov(qword[rsp + offsetof(StackLayout::Thunk, r[3])], r9);
// mov(qword[rsp + offsetof(StackLayout::Thunk, r[4])], r10);
// mov(qword[rsp + offsetof(StackLayout::Thunk, r[5])], r11);
// movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[1])], xmm1);
// movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[2])], xmm2);
// movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[3])], xmm3);
// movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[4])], xmm4);
// movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[5])], xmm5);
mov(rax, rdx);
mov(rcx, rsi); // context
mov(rdx, r8);
mov(r8, r9);
mov(r9, r10);
call(rax);
// movaps(xmm1, qword[rsp + offsetof(StackLayout::Thunk, xmm[1])]);
// movaps(xmm2, qword[rsp + offsetof(StackLayout::Thunk, xmm[2])]);
// movaps(xmm3, qword[rsp + offsetof(StackLayout::Thunk, xmm[3])]);
// movaps(xmm4, qword[rsp + offsetof(StackLayout::Thunk, xmm[4])]);
// movaps(xmm5, qword[rsp + offsetof(StackLayout::Thunk, xmm[5])]);
// mov(rcx, qword[rsp + offsetof(StackLayout::Thunk, r[0])]);
// mov(rdx, qword[rsp + offsetof(StackLayout::Thunk, r[1])]);
// mov(r8, qword[rsp + offsetof(StackLayout::Thunk, r[2])]);
// mov(r9, qword[rsp + offsetof(StackLayout::Thunk, r[3])]);
// mov(r10, qword[rsp + offsetof(StackLayout::Thunk, r[4])]);
// mov(r11, qword[rsp + offsetof(StackLayout::Thunk, r[5])]);
add(rsp, stack_size);
mov(rcx, qword[rsp + 8 * 1]);
mov(rdx, qword[rsp + 8 * 2]);
ret();
void* fn = Emplace(stack_size);
return (GuestToHostThunk)fn;
}
// X64Emitter handles actually resolving functions.
extern "C" uint64_t ResolveFunction(void* raw_context, uint32_t target_address);
ResolveFunctionThunk X64ThunkEmitter::EmitResolveFunctionThunk() {
// ebx = target PPC address
// rcx = context
uint32_t stack_size = 0x18;
// rsp + 0 = return address
mov(qword[rsp + 8 * 2], rdx);
mov(qword[rsp + 8 * 1], rcx);
sub(rsp, stack_size);
mov(rcx, rsi); // context
mov(rdx, rbx);
mov(rax, uint64_t(&ResolveFunction));
call(rax);
add(rsp, stack_size);
mov(rcx, qword[rsp + 8 * 1]);
mov(rdx, qword[rsp + 8 * 2]);
jmp(rax);
void* fn = Emplace(stack_size);
return (ResolveFunctionThunk)fn;
}
} // namespace x64

View File

@@ -174,15 +174,17 @@ void* X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
// If we are going above the high water mark of committed memory, commit
// some more. It's ok if multiple threads do this, as redundant commits
// aren't harmful.
size_t old_commit_mark = generated_code_commit_mark_;
if (high_mark > old_commit_mark) {
size_t new_commit_mark = old_commit_mark + 16 * 1024 * 1024;
size_t old_commit_mark, new_commit_mark;
do {
old_commit_mark = generated_code_commit_mark_;
if (high_mark <= old_commit_mark) break;
new_commit_mark = old_commit_mark + 16 * 1024 * 1024;
xe::memory::AllocFixed(generated_code_base_, new_commit_mark,
xe::memory::AllocationType::kCommit,
xe::memory::PageAccess::kExecuteReadWrite);
generated_code_commit_mark_.compare_exchange_strong(old_commit_mark,
new_commit_mark);
}
} while (generated_code_commit_mark_.compare_exchange_weak(
old_commit_mark, new_commit_mark));
// Copy code.
std::memcpy(code_address, machine_code, code_size);
@@ -248,15 +250,17 @@ uint32_t X64CodeCache::PlaceData(const void* data, size_t length) {
// If we are going above the high water mark of committed memory, commit some
// more. It's ok if multiple threads do this, as redundant commits aren't
// harmful.
size_t old_commit_mark = generated_code_commit_mark_;
if (high_mark > old_commit_mark) {
size_t new_commit_mark = old_commit_mark + 16 * 1024 * 1024;
size_t old_commit_mark, new_commit_mark;
do {
old_commit_mark = generated_code_commit_mark_;
if (high_mark <= old_commit_mark) break;
new_commit_mark = old_commit_mark + 16 * 1024 * 1024;
xe::memory::AllocFixed(generated_code_base_, new_commit_mark,
xe::memory::AllocationType::kCommit,
xe::memory::PageAccess::kExecuteReadWrite);
generated_code_commit_mark_.compare_exchange_strong(old_commit_mark,
new_commit_mark);
}
} while (generated_code_commit_mark_.compare_exchange_weak(old_commit_mark,
new_commit_mark));
// Copy code.
std::memcpy(data_address, data, length);

View File

@@ -56,12 +56,13 @@ static const size_t kStashOffset = 32;
// static const size_t kStashOffsetHigh = 32 + 32;
const uint32_t X64Emitter::gpr_reg_map_[X64Emitter::GPR_COUNT] = {
Xbyak::Operand::RBX, Xbyak::Operand::R12, Xbyak::Operand::R13,
Xbyak::Operand::R14, Xbyak::Operand::R15,
Xbyak::Operand::RBX, Xbyak::Operand::R10, Xbyak::Operand::R11,
Xbyak::Operand::R12, Xbyak::Operand::R13, Xbyak::Operand::R14,
Xbyak::Operand::R15,
};
const uint32_t X64Emitter::xmm_reg_map_[X64Emitter::XMM_COUNT] = {
6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
};
X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator)
@@ -148,11 +149,13 @@ bool X64Emitter::Emit(HIRBuilder* builder, size_t* out_stack_size) {
for (auto it = locals.begin(); it != locals.end(); ++it) {
auto slot = *it;
size_t type_size = GetTypeSize(slot->type);
// Align to natural size.
stack_offset = xe::align(stack_offset, type_size);
slot->set_constant((uint32_t)stack_offset);
stack_offset += type_size;
}
// Ensure 16b alignment.
stack_offset -= StackLayout::GUEST_STACK_SIZE;
stack_offset = xe::align(stack_offset, static_cast<size_t>(16));
@@ -160,7 +163,7 @@ bool X64Emitter::Emit(HIRBuilder* builder, size_t* out_stack_size) {
// Function prolog.
// Must be 16b aligned.
// Windows is very strict about the form of this and the epilog:
// https://msdn.microsoft.com/en-us/library/tawsa7cb.aspx
// https://docs.microsoft.com/en-us/cpp/build/prolog-and-epilog?view=vs-2017
// IMPORTANT: any changes to the prolog must be kept in sync with
// X64CodeCache, which dynamically generates exception information.
// Adding or changing anything here must be matched!
@@ -168,6 +171,7 @@ bool X64Emitter::Emit(HIRBuilder* builder, size_t* out_stack_size) {
assert_true((stack_size + 8) % 16 == 0);
*out_stack_size = stack_size;
stack_size_ = stack_size;
sub(rsp, (uint32_t)stack_size);
mov(qword[rsp + StackLayout::GUEST_CTX_HOME], GetContextReg());
mov(qword[rsp + StackLayout::GUEST_RET_ADDR], rcx);
@@ -221,6 +225,8 @@ bool X64Emitter::Emit(HIRBuilder* builder, size_t* out_stack_size) {
const Instr* new_tail = instr;
if (!SelectSequence(this, instr, &new_tail)) {
// No sequence found!
// NOTE: If you encounter this after adding a new instruction, do a full
// rebuild!
assert_always();
XELOGE("Unable to process HIR opcode %s", instr->opcode->name);
break;
@@ -340,13 +346,14 @@ void X64Emitter::UnimplementedInstr(const hir::Instr* i) {
// This is used by the X64ThunkEmitter's ResolveFunctionThunk.
extern "C" uint64_t ResolveFunction(void* raw_context,
uint32_t target_address) {
uint64_t target_address) {
auto thread_state = *reinterpret_cast<ThreadState**>(raw_context);
// TODO(benvanik): required?
assert_not_zero(target_address);
auto fn = thread_state->processor()->ResolveFunction(target_address);
auto fn =
thread_state->processor()->ResolveFunction((uint32_t)target_address);
assert_not_null(fn);
auto x64_fn = static_cast<X64Function*>(fn);
uint64_t addr = reinterpret_cast<uint64_t>(x64_fn->machine_code());
@@ -373,10 +380,7 @@ void X64Emitter::Call(const hir::Instr* instr, GuestFunction* function) {
// Old-style resolve.
// Not too important because indirection table is almost always available.
// TODO: Overwrite the call-site with a straight call.
mov(rax, reinterpret_cast<uint64_t>(ResolveFunction));
mov(rcx, GetContextReg());
mov(rdx, function->address());
call(rax);
CallNative(&ResolveFunction, function->address());
}
// Actually jump/call to rax.
@@ -457,16 +461,15 @@ void X64Emitter::CallExtern(const hir::Instr* instr, const Function* function) {
auto builtin_function = static_cast<const BuiltinFunction*>(function);
if (builtin_function->handler()) {
undefined = false;
// rcx = context
// rdx = target host function
// r8 = arg0
// r9 = arg1
mov(rcx, GetContextReg());
mov(rdx, reinterpret_cast<uint64_t>(builtin_function->handler()));
mov(r8, reinterpret_cast<uint64_t>(builtin_function->arg0()));
mov(r9, reinterpret_cast<uint64_t>(builtin_function->arg1()));
// rcx = target function
// rdx = arg0
// r8 = arg1
// r9 = arg2
auto thunk = backend()->guest_to_host_thunk();
mov(rax, reinterpret_cast<uint64_t>(thunk));
mov(rcx, reinterpret_cast<uint64_t>(builtin_function->handler()));
mov(rdx, reinterpret_cast<uint64_t>(builtin_function->arg0()));
mov(r8, reinterpret_cast<uint64_t>(builtin_function->arg1()));
call(rax);
// rax = host return
}
@@ -474,13 +477,15 @@ void X64Emitter::CallExtern(const hir::Instr* instr, const Function* function) {
auto extern_function = static_cast<const GuestFunction*>(function);
if (extern_function->extern_handler()) {
undefined = false;
// rcx = context
// rdx = target host function
mov(rcx, GetContextReg());
mov(rdx, reinterpret_cast<uint64_t>(extern_function->extern_handler()));
mov(r8, qword[GetContextReg() + offsetof(ppc::PPCContext, kernel_state)]);
// rcx = target function
// rdx = arg0
// r8 = arg1
// r9 = arg2
auto thunk = backend()->guest_to_host_thunk();
mov(rax, reinterpret_cast<uint64_t>(thunk));
mov(rcx, reinterpret_cast<uint64_t>(extern_function->extern_handler()));
mov(rdx,
qword[GetContextReg() + offsetof(ppc::PPCContext, kernel_state)]);
call(rax);
// rax = host return
}
@@ -490,42 +495,30 @@ void X64Emitter::CallExtern(const hir::Instr* instr, const Function* function) {
}
}
void X64Emitter::CallNative(void* fn) {
mov(rax, reinterpret_cast<uint64_t>(fn));
mov(rcx, GetContextReg());
call(rax);
}
void X64Emitter::CallNative(void* fn) { CallNativeSafe(fn); }
void X64Emitter::CallNative(uint64_t (*fn)(void* raw_context)) {
mov(rax, reinterpret_cast<uint64_t>(fn));
mov(rcx, GetContextReg());
call(rax);
CallNativeSafe(reinterpret_cast<void*>(fn));
}
void X64Emitter::CallNative(uint64_t (*fn)(void* raw_context, uint64_t arg0)) {
mov(rax, reinterpret_cast<uint64_t>(fn));
mov(rcx, GetContextReg());
call(rax);
CallNativeSafe(reinterpret_cast<void*>(fn));
}
void X64Emitter::CallNative(uint64_t (*fn)(void* raw_context, uint64_t arg0),
uint64_t arg0) {
mov(rax, reinterpret_cast<uint64_t>(fn));
mov(rcx, GetContextReg());
mov(rdx, arg0);
call(rax);
mov(GetNativeParam(0), arg0);
CallNativeSafe(reinterpret_cast<void*>(fn));
}
void X64Emitter::CallNativeSafe(void* fn) {
// rcx = context
// rdx = target function
// r8 = arg0
// r9 = arg1
// r10 = arg2
// rcx = target function
// rdx = arg0
// r8 = arg1
// r9 = arg2
auto thunk = backend()->guest_to_host_thunk();
mov(rax, reinterpret_cast<uint64_t>(thunk));
mov(rcx, GetContextReg());
mov(rdx, reinterpret_cast<uint64_t>(fn));
mov(rcx, reinterpret_cast<uint64_t>(fn));
call(rax);
// rax = host return
}
@@ -535,6 +528,18 @@ void X64Emitter::SetReturnAddress(uint64_t value) {
mov(qword[rsp + StackLayout::GUEST_CALL_RET_ADDR], rax);
}
Xbyak::Reg64 X64Emitter::GetNativeParam(uint32_t param) {
if (param == 0)
return rdx;
else if (param == 1)
return r8;
else if (param == 2)
return r9;
assert_always();
return r9;
}
// Important: If you change these, you must update the thunks in x64_backend.cc!
Xbyak::Reg64 X64Emitter::GetContextReg() { return rsi; }
Xbyak::Reg64 X64Emitter::GetMembaseReg() { return rdi; }

View File

@@ -139,13 +139,13 @@ class X64Emitter : public Xbyak::CodeGenerator {
std::vector<SourceMapEntry>* out_source_map);
public:
// Reserved: rsp
// Reserved: rsp, rsi, rdi
// Scratch: rax/rcx/rdx
// xmm0-2
// Available: rbx, r12-r15 (save to get r8-r11, rbp, rsi, rdi?)
// xmm6-xmm15 (save to get xmm3-xmm5)
static const int GPR_COUNT = 5;
static const int XMM_COUNT = 10;
// Available: rbx, r10-r15
// xmm4-xmm15 (save to get xmm3)
static const int GPR_COUNT = 7;
static const int XMM_COUNT = 12;
static void SetupReg(const hir::Value* v, Xbyak::Reg8& r) {
auto idx = gpr_reg_map_[v->reg.index];
@@ -187,6 +187,8 @@ class X64Emitter : public Xbyak::CodeGenerator {
void CallNativeSafe(void* fn);
void SetReturnAddress(uint64_t value);
Xbyak::Reg64 GetNativeParam(uint32_t param);
Xbyak::Reg64 GetContextReg();
Xbyak::Reg64 GetMembaseReg();
void ReloadContext();

View File

@@ -0,0 +1,629 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2018 Xenia Developers. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CPU_BACKEND_X64_X64_OP_H_
#define XENIA_CPU_BACKEND_X64_X64_OP_H_
#include "xenia/cpu/backend/x64/x64_emitter.h"
#include "xenia/cpu/hir/instr.h"
namespace xe {
namespace cpu {
namespace backend {
namespace x64 {
// TODO(benvanik): direct usings.
using namespace xe::cpu;
using namespace xe::cpu::hir;
using namespace Xbyak;
// Selects the right byte/word/etc from a vector. We need to flip logical
// indices (0,1,2,3,4,5,6,7,...) = (3,2,1,0,7,6,5,4,...)
#define VEC128_B(n) ((n) ^ 0x3)
#define VEC128_W(n) ((n) ^ 0x1)
#define VEC128_D(n) (n)
#define VEC128_F(n) (n)
enum KeyType {
KEY_TYPE_X = OPCODE_SIG_TYPE_X,
KEY_TYPE_L = OPCODE_SIG_TYPE_L,
KEY_TYPE_O = OPCODE_SIG_TYPE_O,
KEY_TYPE_S = OPCODE_SIG_TYPE_S,
KEY_TYPE_V_I8 = OPCODE_SIG_TYPE_V + INT8_TYPE,
KEY_TYPE_V_I16 = OPCODE_SIG_TYPE_V + INT16_TYPE,
KEY_TYPE_V_I32 = OPCODE_SIG_TYPE_V + INT32_TYPE,
KEY_TYPE_V_I64 = OPCODE_SIG_TYPE_V + INT64_TYPE,
KEY_TYPE_V_F32 = OPCODE_SIG_TYPE_V + FLOAT32_TYPE,
KEY_TYPE_V_F64 = OPCODE_SIG_TYPE_V + FLOAT64_TYPE,
KEY_TYPE_V_V128 = OPCODE_SIG_TYPE_V + VEC128_TYPE,
};
#pragma pack(push, 1)
union InstrKey {
struct {
uint32_t opcode : 8;
uint32_t dest : 5;
uint32_t src1 : 5;
uint32_t src2 : 5;
uint32_t src3 : 5;
uint32_t reserved : 4;
};
uint32_t value;
operator uint32_t() const { return value; }
InstrKey() : value(0) {}
InstrKey(uint32_t v) : value(v) {}
InstrKey(const Instr* i) : value(0) {
opcode = i->opcode->num;
uint32_t sig = i->opcode->signature;
dest =
GET_OPCODE_SIG_TYPE_DEST(sig) ? OPCODE_SIG_TYPE_V + i->dest->type : 0;
src1 = GET_OPCODE_SIG_TYPE_SRC1(sig);
if (src1 == OPCODE_SIG_TYPE_V) {
src1 += i->src1.value->type;
}
src2 = GET_OPCODE_SIG_TYPE_SRC2(sig);
if (src2 == OPCODE_SIG_TYPE_V) {
src2 += i->src2.value->type;
}
src3 = GET_OPCODE_SIG_TYPE_SRC3(sig);
if (src3 == OPCODE_SIG_TYPE_V) {
src3 += i->src3.value->type;
}
}
template <Opcode OPCODE, KeyType DEST = KEY_TYPE_X, KeyType SRC1 = KEY_TYPE_X,
KeyType SRC2 = KEY_TYPE_X, KeyType SRC3 = KEY_TYPE_X>
struct Construct {
static const uint32_t value =
(OPCODE) | (DEST << 8) | (SRC1 << 13) | (SRC2 << 18) | (SRC3 << 23);
};
};
#pragma pack(pop)
static_assert(sizeof(InstrKey) <= 4, "Key must be 4 bytes");
template <typename... Ts>
struct CombinedStruct;
template <>
struct CombinedStruct<> {};
template <typename T, typename... Ts>
struct CombinedStruct<T, Ts...> : T, CombinedStruct<Ts...> {};
struct OpBase {};
template <typename T, KeyType KEY_TYPE>
struct Op : OpBase {
static const KeyType key_type = KEY_TYPE;
};
struct VoidOp : Op<VoidOp, KEY_TYPE_X> {
protected:
template <typename T, KeyType KEY_TYPE>
friend struct Op;
template <hir::Opcode OPCODE, typename... Ts>
friend struct I;
void Load(const Instr::Op& op) {}
};
struct OffsetOp : Op<OffsetOp, KEY_TYPE_O> {
uint64_t value;
protected:
template <typename T, KeyType KEY_TYPE>
friend struct Op;
template <hir::Opcode OPCODE, typename... Ts>
friend struct I;
void Load(const Instr::Op& op) { this->value = op.offset; }
};
struct SymbolOp : Op<SymbolOp, KEY_TYPE_S> {
Function* value;
protected:
template <typename T, KeyType KEY_TYPE>
friend struct Op;
template <hir::Opcode OPCODE, typename... Ts>
friend struct I;
bool Load(const Instr::Op& op) {
this->value = op.symbol;
return true;
}
};
struct LabelOp : Op<LabelOp, KEY_TYPE_L> {
hir::Label* value;
protected:
template <typename T, KeyType KEY_TYPE>
friend struct Op;
template <hir::Opcode OPCODE, typename... Ts>
friend struct I;
void Load(const Instr::Op& op) { this->value = op.label; }
};
template <typename T, KeyType KEY_TYPE, typename REG_TYPE, typename CONST_TYPE>
struct ValueOp : Op<ValueOp<T, KEY_TYPE, REG_TYPE, CONST_TYPE>, KEY_TYPE> {
typedef REG_TYPE reg_type;
const Value* value;
bool is_constant;
virtual bool ConstantFitsIn32Reg() const { return true; }
const REG_TYPE& reg() const {
assert_true(!is_constant);
return reg_;
}
operator const REG_TYPE&() const { return reg(); }
bool IsEqual(const T& b) const {
if (is_constant && b.is_constant) {
return reinterpret_cast<const T*>(this)->constant() == b.constant();
} else if (!is_constant && !b.is_constant) {
return reg_.getIdx() == b.reg_.getIdx();
} else {
return false;
}
}
bool IsEqual(const Xbyak::Reg& b) const {
if (is_constant) {
return false;
} else if (!is_constant) {
return reg_.getIdx() == b.getIdx();
} else {
return false;
}
}
bool operator==(const T& b) const { return IsEqual(b); }
bool operator!=(const T& b) const { return !IsEqual(b); }
bool operator==(const Xbyak::Reg& b) const { return IsEqual(b); }
bool operator!=(const Xbyak::Reg& b) const { return !IsEqual(b); }
void Load(const Instr::Op& op) {
value = op.value;
is_constant = value->IsConstant();
if (!is_constant) {
X64Emitter::SetupReg(value, reg_);
}
}
protected:
REG_TYPE reg_;
};
struct I8Op : ValueOp<I8Op, KEY_TYPE_V_I8, Reg8, int8_t> {
typedef ValueOp<I8Op, KEY_TYPE_V_I8, Reg8, int8_t> BASE;
const int8_t constant() const {
assert_true(BASE::is_constant);
return BASE::value->constant.i8;
}
};
struct I16Op : ValueOp<I16Op, KEY_TYPE_V_I16, Reg16, int16_t> {
typedef ValueOp<I16Op, KEY_TYPE_V_I16, Reg16, int16_t> BASE;
const int16_t constant() const {
assert_true(BASE::is_constant);
return BASE::value->constant.i16;
}
};
struct I32Op : ValueOp<I32Op, KEY_TYPE_V_I32, Reg32, int32_t> {
typedef ValueOp<I32Op, KEY_TYPE_V_I32, Reg32, int32_t> BASE;
const int32_t constant() const {
assert_true(BASE::is_constant);
return BASE::value->constant.i32;
}
};
struct I64Op : ValueOp<I64Op, KEY_TYPE_V_I64, Reg64, int64_t> {
typedef ValueOp<I64Op, KEY_TYPE_V_I64, Reg64, int64_t> BASE;
const int64_t constant() const {
assert_true(BASE::is_constant);
return BASE::value->constant.i64;
}
bool ConstantFitsIn32Reg() const override {
int64_t v = BASE::value->constant.i64;
if ((v & ~0x7FFFFFFF) == 0) {
// Fits under 31 bits, so just load using normal mov.
return true;
} else if ((v & ~0x7FFFFFFF) == ~0x7FFFFFFF) {
// Negative number that fits in 32bits.
return true;
}
return false;
}
};
struct F32Op : ValueOp<F32Op, KEY_TYPE_V_F32, Xmm, float> {
typedef ValueOp<F32Op, KEY_TYPE_V_F32, Xmm, float> BASE;
const float constant() const {
assert_true(BASE::is_constant);
return BASE::value->constant.f32;
}
};
struct F64Op : ValueOp<F64Op, KEY_TYPE_V_F64, Xmm, double> {
typedef ValueOp<F64Op, KEY_TYPE_V_F64, Xmm, double> BASE;
const double constant() const {
assert_true(BASE::is_constant);
return BASE::value->constant.f64;
}
};
struct V128Op : ValueOp<V128Op, KEY_TYPE_V_V128, Xmm, vec128_t> {
typedef ValueOp<V128Op, KEY_TYPE_V_V128, Xmm, vec128_t> BASE;
const vec128_t& constant() const {
assert_true(BASE::is_constant);
return BASE::value->constant.v128;
}
};
template <typename DEST, typename... Tf>
struct DestField;
template <typename DEST>
struct DestField<DEST> {
DEST dest;
protected:
bool LoadDest(const Instr* i) {
Instr::Op op;
op.value = i->dest;
dest.Load(op);
return true;
}
};
template <>
struct DestField<VoidOp> {
protected:
bool LoadDest(const Instr* i) { return true; }
};
template <hir::Opcode OPCODE, typename... Ts>
struct I;
template <hir::Opcode OPCODE, typename DEST>
struct I<OPCODE, DEST> : DestField<DEST> {
typedef DestField<DEST> BASE;
static const hir::Opcode opcode = OPCODE;
static const uint32_t key =
InstrKey::Construct<OPCODE, DEST::key_type>::value;
static const KeyType dest_type = DEST::key_type;
const Instr* instr;
protected:
template <typename SEQ, typename T>
friend struct Sequence;
bool Load(const Instr* i) {
if (InstrKey(i).value == key && BASE::LoadDest(i)) {
instr = i;
return true;
}
return false;
}
};
template <hir::Opcode OPCODE, typename DEST, typename SRC1>
struct I<OPCODE, DEST, SRC1> : DestField<DEST> {
typedef DestField<DEST> BASE;
static const hir::Opcode opcode = OPCODE;
static const uint32_t key =
InstrKey::Construct<OPCODE, DEST::key_type, SRC1::key_type>::value;
static const KeyType dest_type = DEST::key_type;
static const KeyType src1_type = SRC1::key_type;
const Instr* instr;
SRC1 src1;
protected:
template <typename SEQ, typename T>
friend struct Sequence;
bool Load(const Instr* i) {
if (InstrKey(i).value == key && BASE::LoadDest(i)) {
instr = i;
src1.Load(i->src1);
return true;
}
return false;
}
};
template <hir::Opcode OPCODE, typename DEST, typename SRC1, typename SRC2>
struct I<OPCODE, DEST, SRC1, SRC2> : DestField<DEST> {
typedef DestField<DEST> BASE;
static const hir::Opcode opcode = OPCODE;
static const uint32_t key =
InstrKey::Construct<OPCODE, DEST::key_type, SRC1::key_type,
SRC2::key_type>::value;
static const KeyType dest_type = DEST::key_type;
static const KeyType src1_type = SRC1::key_type;
static const KeyType src2_type = SRC2::key_type;
const Instr* instr;
SRC1 src1;
SRC2 src2;
protected:
template <typename SEQ, typename T>
friend struct Sequence;
bool Load(const Instr* i) {
if (InstrKey(i).value == key && BASE::LoadDest(i)) {
instr = i;
src1.Load(i->src1);
src2.Load(i->src2);
return true;
}
return false;
}
};
template <hir::Opcode OPCODE, typename DEST, typename SRC1, typename SRC2,
typename SRC3>
struct I<OPCODE, DEST, SRC1, SRC2, SRC3> : DestField<DEST> {
typedef DestField<DEST> BASE;
static const hir::Opcode opcode = OPCODE;
static const uint32_t key =
InstrKey::Construct<OPCODE, DEST::key_type, SRC1::key_type,
SRC2::key_type, SRC3::key_type>::value;
static const KeyType dest_type = DEST::key_type;
static const KeyType src1_type = SRC1::key_type;
static const KeyType src2_type = SRC2::key_type;
static const KeyType src3_type = SRC3::key_type;
const Instr* instr;
SRC1 src1;
SRC2 src2;
SRC3 src3;
protected:
template <typename SEQ, typename T>
friend struct Sequence;
bool Load(const Instr* i) {
if (InstrKey(i).value == key && BASE::LoadDest(i)) {
instr = i;
src1.Load(i->src1);
src2.Load(i->src2);
src3.Load(i->src3);
return true;
}
return false;
}
};
template <typename T>
static const T GetTempReg(X64Emitter& e);
template <>
const Reg8 GetTempReg<Reg8>(X64Emitter& e) {
return e.al;
}
template <>
const Reg16 GetTempReg<Reg16>(X64Emitter& e) {
return e.ax;
}
template <>
const Reg32 GetTempReg<Reg32>(X64Emitter& e) {
return e.eax;
}
template <>
const Reg64 GetTempReg<Reg64>(X64Emitter& e) {
return e.rax;
}
template <typename SEQ, typename T>
struct Sequence {
typedef T EmitArgType;
static constexpr uint32_t head_key() { return T::key; }
static bool Select(X64Emitter& e, const Instr* i) {
T args;
if (!args.Load(i)) {
return false;
}
SEQ::Emit(e, args);
return true;
}
template <typename REG_FN>
static void EmitUnaryOp(X64Emitter& e, const EmitArgType& i,
const REG_FN& reg_fn) {
if (i.src1.is_constant) {
e.mov(i.dest, i.src1.constant());
reg_fn(e, i.dest);
} else {
if (i.dest != i.src1) {
e.mov(i.dest, i.src1);
}
reg_fn(e, i.dest);
}
}
template <typename REG_REG_FN, typename REG_CONST_FN>
static void EmitCommutativeBinaryOp(X64Emitter& e, const EmitArgType& i,
const REG_REG_FN& reg_reg_fn,
const REG_CONST_FN& reg_const_fn) {
if (i.src1.is_constant) {
if (i.src2.is_constant) {
// Both constants.
if (i.src1.ConstantFitsIn32Reg()) {
e.mov(i.dest, i.src2.constant());
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src1.constant()));
} else if (i.src2.ConstantFitsIn32Reg()) {
e.mov(i.dest, i.src1.constant());
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src2.constant()));
} else {
e.mov(i.dest, i.src1.constant());
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
e.mov(temp, i.src2.constant());
reg_reg_fn(e, i.dest, temp);
}
} else {
// src1 constant.
if (i.dest == i.src2) {
if (i.src1.ConstantFitsIn32Reg()) {
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src1.constant()));
} else {
auto temp = GetTempReg<typename decltype(i.src1)::reg_type>(e);
e.mov(temp, i.src1.constant());
reg_reg_fn(e, i.dest, temp);
}
} else {
e.mov(i.dest, i.src1.constant());
reg_reg_fn(e, i.dest, i.src2);
}
}
} else if (i.src2.is_constant) {
if (i.dest == i.src1) {
if (i.src2.ConstantFitsIn32Reg()) {
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src2.constant()));
} else {
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
e.mov(temp, i.src2.constant());
reg_reg_fn(e, i.dest, temp);
}
} else {
e.mov(i.dest, i.src2.constant());
reg_reg_fn(e, i.dest, i.src1);
}
} else {
if (i.dest == i.src1) {
reg_reg_fn(e, i.dest, i.src2);
} else if (i.dest == i.src2) {
reg_reg_fn(e, i.dest, i.src1);
} else {
e.mov(i.dest, i.src1);
reg_reg_fn(e, i.dest, i.src2);
}
}
}
template <typename REG_REG_FN, typename REG_CONST_FN>
static void EmitAssociativeBinaryOp(X64Emitter& e, const EmitArgType& i,
const REG_REG_FN& reg_reg_fn,
const REG_CONST_FN& reg_const_fn) {
if (i.src1.is_constant) {
assert_true(!i.src2.is_constant);
if (i.dest == i.src2) {
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
e.mov(temp, i.src2);
e.mov(i.dest, i.src1.constant());
reg_reg_fn(e, i.dest, temp);
} else {
e.mov(i.dest, i.src1.constant());
reg_reg_fn(e, i.dest, i.src2);
}
} else if (i.src2.is_constant) {
if (i.dest == i.src1) {
if (i.src2.ConstantFitsIn32Reg()) {
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src2.constant()));
} else {
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
e.mov(temp, i.src2.constant());
reg_reg_fn(e, i.dest, temp);
}
} else {
e.mov(i.dest, i.src1);
if (i.src2.ConstantFitsIn32Reg()) {
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src2.constant()));
} else {
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
e.mov(temp, i.src2.constant());
reg_reg_fn(e, i.dest, temp);
}
}
} else {
if (i.dest == i.src1) {
reg_reg_fn(e, i.dest, i.src2);
} else if (i.dest == i.src2) {
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
e.mov(temp, i.src2);
e.mov(i.dest, i.src1);
reg_reg_fn(e, i.dest, temp);
} else {
e.mov(i.dest, i.src1);
reg_reg_fn(e, i.dest, i.src2);
}
}
}
template <typename FN>
static void EmitCommutativeBinaryXmmOp(X64Emitter& e, const EmitArgType& i,
const FN& fn) {
if (i.src1.is_constant) {
assert_true(!i.src2.is_constant);
e.LoadConstantXmm(e.xmm0, i.src1.constant());
fn(e, i.dest, e.xmm0, i.src2);
} else if (i.src2.is_constant) {
assert_true(!i.src1.is_constant);
e.LoadConstantXmm(e.xmm0, i.src2.constant());
fn(e, i.dest, i.src1, e.xmm0);
} else {
fn(e, i.dest, i.src1, i.src2);
}
}
template <typename FN>
static void EmitAssociativeBinaryXmmOp(X64Emitter& e, const EmitArgType& i,
const FN& fn) {
if (i.src1.is_constant) {
assert_true(!i.src2.is_constant);
e.LoadConstantXmm(e.xmm0, i.src1.constant());
fn(e, i.dest, e.xmm0, i.src2);
} else if (i.src2.is_constant) {
assert_true(!i.src1.is_constant);
e.LoadConstantXmm(e.xmm0, i.src2.constant());
fn(e, i.dest, i.src1, e.xmm0);
} else {
fn(e, i.dest, i.src1, i.src2);
}
}
template <typename REG_REG_FN, typename REG_CONST_FN>
static void EmitCommutativeCompareOp(X64Emitter& e, const EmitArgType& i,
const REG_REG_FN& reg_reg_fn,
const REG_CONST_FN& reg_const_fn) {
if (i.src1.is_constant) {
assert_true(!i.src2.is_constant);
if (i.src1.ConstantFitsIn32Reg()) {
reg_const_fn(e, i.src2, static_cast<int32_t>(i.src1.constant()));
} else {
auto temp = GetTempReg<typename decltype(i.src1)::reg_type>(e);
e.mov(temp, i.src1.constant());
reg_reg_fn(e, i.src2, temp);
}
} else if (i.src2.is_constant) {
assert_true(!i.src1.is_constant);
if (i.src2.ConstantFitsIn32Reg()) {
reg_const_fn(e, i.src1, static_cast<int32_t>(i.src2.constant()));
} else {
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
e.mov(temp, i.src2.constant());
reg_reg_fn(e, i.src1, temp);
}
} else {
reg_reg_fn(e, i.src1, i.src2);
}
}
template <typename REG_REG_FN, typename REG_CONST_FN>
static void EmitAssociativeCompareOp(X64Emitter& e, const EmitArgType& i,
const REG_REG_FN& reg_reg_fn,
const REG_CONST_FN& reg_const_fn) {
if (i.src1.is_constant) {
assert_true(!i.src2.is_constant);
if (i.src1.ConstantFitsIn32Reg()) {
reg_const_fn(e, i.dest, i.src2, static_cast<int32_t>(i.src1.constant()),
true);
} else {
auto temp = GetTempReg<typename decltype(i.src1)::reg_type>(e);
e.mov(temp, i.src1.constant());
reg_reg_fn(e, i.dest, i.src2, temp, true);
}
} else if (i.src2.is_constant) {
assert_true(!i.src1.is_constant);
if (i.src2.ConstantFitsIn32Reg()) {
reg_const_fn(e, i.dest, i.src1, static_cast<int32_t>(i.src2.constant()),
false);
} else {
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
e.mov(temp, i.src2.constant());
reg_reg_fn(e, i.dest, i.src1, temp, false);
}
} else {
reg_reg_fn(e, i.dest, i.src1, i.src2, false);
}
}
};
} // namespace x64
} // namespace backend
} // namespace cpu
} // namespace xe
#endif // XENIA_CPU_BACKEND_X64_X64_OP_H_

View File

@@ -0,0 +1,553 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2018 Xenia Developers. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/cpu/backend/x64/x64_sequences.h"
#include <algorithm>
#include <cstring>
#include "xenia/cpu/backend/x64/x64_op.h"
namespace xe {
namespace cpu {
namespace backend {
namespace x64 {
volatile int anchor_control = 0;
// ============================================================================
// OPCODE_DEBUG_BREAK
// ============================================================================
struct DEBUG_BREAK : Sequence<DEBUG_BREAK, I<OPCODE_DEBUG_BREAK, VoidOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) { e.DebugBreak(); }
};
EMITTER_OPCODE_TABLE(OPCODE_DEBUG_BREAK, DEBUG_BREAK);
// ============================================================================
// OPCODE_DEBUG_BREAK_TRUE
// ============================================================================
struct DEBUG_BREAK_TRUE_I8
: Sequence<DEBUG_BREAK_TRUE_I8, I<OPCODE_DEBUG_BREAK_TRUE, VoidOp, I8Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.test(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip);
e.DebugBreak();
e.L(skip);
}
};
struct DEBUG_BREAK_TRUE_I16
: Sequence<DEBUG_BREAK_TRUE_I16,
I<OPCODE_DEBUG_BREAK_TRUE, VoidOp, I16Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.test(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip);
e.DebugBreak();
e.L(skip);
}
};
struct DEBUG_BREAK_TRUE_I32
: Sequence<DEBUG_BREAK_TRUE_I32,
I<OPCODE_DEBUG_BREAK_TRUE, VoidOp, I32Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.test(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip);
e.DebugBreak();
e.L(skip);
}
};
struct DEBUG_BREAK_TRUE_I64
: Sequence<DEBUG_BREAK_TRUE_I64,
I<OPCODE_DEBUG_BREAK_TRUE, VoidOp, I64Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.test(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip);
e.DebugBreak();
e.L(skip);
}
};
struct DEBUG_BREAK_TRUE_F32
: Sequence<DEBUG_BREAK_TRUE_F32,
I<OPCODE_DEBUG_BREAK_TRUE, VoidOp, F32Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.vptest(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip);
e.DebugBreak();
e.L(skip);
}
};
struct DEBUG_BREAK_TRUE_F64
: Sequence<DEBUG_BREAK_TRUE_F64,
I<OPCODE_DEBUG_BREAK_TRUE, VoidOp, F64Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.vptest(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip);
e.DebugBreak();
e.L(skip);
}
};
EMITTER_OPCODE_TABLE(OPCODE_DEBUG_BREAK_TRUE, DEBUG_BREAK_TRUE_I8,
DEBUG_BREAK_TRUE_I16, DEBUG_BREAK_TRUE_I32,
DEBUG_BREAK_TRUE_I64, DEBUG_BREAK_TRUE_F32,
DEBUG_BREAK_TRUE_F64);
// ============================================================================
// OPCODE_TRAP
// ============================================================================
struct TRAP : Sequence<TRAP, I<OPCODE_TRAP, VoidOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.Trap(i.instr->flags);
}
};
EMITTER_OPCODE_TABLE(OPCODE_TRAP, TRAP);
// ============================================================================
// OPCODE_TRAP_TRUE
// ============================================================================
struct TRAP_TRUE_I8
: Sequence<TRAP_TRUE_I8, I<OPCODE_TRAP_TRUE, VoidOp, I8Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.test(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip);
e.Trap(i.instr->flags);
e.L(skip);
}
};
struct TRAP_TRUE_I16
: Sequence<TRAP_TRUE_I16, I<OPCODE_TRAP_TRUE, VoidOp, I16Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.test(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip);
e.Trap(i.instr->flags);
e.L(skip);
}
};
struct TRAP_TRUE_I32
: Sequence<TRAP_TRUE_I32, I<OPCODE_TRAP_TRUE, VoidOp, I32Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.test(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip);
e.Trap(i.instr->flags);
e.L(skip);
}
};
struct TRAP_TRUE_I64
: Sequence<TRAP_TRUE_I64, I<OPCODE_TRAP_TRUE, VoidOp, I64Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.test(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip);
e.Trap(i.instr->flags);
e.L(skip);
}
};
struct TRAP_TRUE_F32
: Sequence<TRAP_TRUE_F32, I<OPCODE_TRAP_TRUE, VoidOp, F32Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.vptest(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip);
e.Trap(i.instr->flags);
e.L(skip);
}
};
struct TRAP_TRUE_F64
: Sequence<TRAP_TRUE_F64, I<OPCODE_TRAP_TRUE, VoidOp, F64Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.vptest(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip);
e.Trap(i.instr->flags);
e.L(skip);
}
};
EMITTER_OPCODE_TABLE(OPCODE_TRAP_TRUE, TRAP_TRUE_I8, TRAP_TRUE_I16,
TRAP_TRUE_I32, TRAP_TRUE_I64, TRAP_TRUE_F32,
TRAP_TRUE_F64);
// ============================================================================
// OPCODE_CALL
// ============================================================================
struct CALL : Sequence<CALL, I<OPCODE_CALL, VoidOp, SymbolOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
assert_true(i.src1.value->is_guest());
e.Call(i.instr, static_cast<GuestFunction*>(i.src1.value));
}
};
EMITTER_OPCODE_TABLE(OPCODE_CALL, CALL);
// ============================================================================
// OPCODE_CALL_TRUE
// ============================================================================
struct CALL_TRUE_I8
: Sequence<CALL_TRUE_I8, I<OPCODE_CALL_TRUE, VoidOp, I8Op, SymbolOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
assert_true(i.src2.value->is_guest());
e.test(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip);
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
e.L(skip);
}
};
struct CALL_TRUE_I16
: Sequence<CALL_TRUE_I16, I<OPCODE_CALL_TRUE, VoidOp, I16Op, SymbolOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
assert_true(i.src2.value->is_guest());
e.test(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip);
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
e.L(skip);
}
};
struct CALL_TRUE_I32
: Sequence<CALL_TRUE_I32, I<OPCODE_CALL_TRUE, VoidOp, I32Op, SymbolOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
assert_true(i.src2.value->is_guest());
e.test(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip);
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
e.L(skip);
}
};
struct CALL_TRUE_I64
: Sequence<CALL_TRUE_I64, I<OPCODE_CALL_TRUE, VoidOp, I64Op, SymbolOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
assert_true(i.src2.value->is_guest());
e.test(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip);
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
e.L(skip);
}
};
struct CALL_TRUE_F32
: Sequence<CALL_TRUE_F32, I<OPCODE_CALL_TRUE, VoidOp, F32Op, SymbolOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
assert_true(i.src2.value->is_guest());
e.vptest(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip);
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
e.L(skip);
}
};
struct CALL_TRUE_F64
: Sequence<CALL_TRUE_F64, I<OPCODE_CALL_TRUE, VoidOp, F64Op, SymbolOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
assert_true(i.src2.value->is_guest());
e.vptest(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip);
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
e.L(skip);
}
};
EMITTER_OPCODE_TABLE(OPCODE_CALL_TRUE, CALL_TRUE_I8, CALL_TRUE_I16,
CALL_TRUE_I32, CALL_TRUE_I64, CALL_TRUE_F32,
CALL_TRUE_F64);
// ============================================================================
// OPCODE_CALL_INDIRECT
// ============================================================================
struct CALL_INDIRECT
: Sequence<CALL_INDIRECT, I<OPCODE_CALL_INDIRECT, VoidOp, I64Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.CallIndirect(i.instr, i.src1);
}
};
EMITTER_OPCODE_TABLE(OPCODE_CALL_INDIRECT, CALL_INDIRECT);
// ============================================================================
// OPCODE_CALL_INDIRECT_TRUE
// ============================================================================
struct CALL_INDIRECT_TRUE_I8
: Sequence<CALL_INDIRECT_TRUE_I8,
I<OPCODE_CALL_INDIRECT_TRUE, VoidOp, I8Op, I64Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.test(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip, CodeGenerator::T_NEAR);
e.CallIndirect(i.instr, i.src2);
e.L(skip);
}
};
struct CALL_INDIRECT_TRUE_I16
: Sequence<CALL_INDIRECT_TRUE_I16,
I<OPCODE_CALL_INDIRECT_TRUE, VoidOp, I16Op, I64Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.test(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip, CodeGenerator::T_NEAR);
e.CallIndirect(i.instr, i.src2);
e.L(skip);
}
};
struct CALL_INDIRECT_TRUE_I32
: Sequence<CALL_INDIRECT_TRUE_I32,
I<OPCODE_CALL_INDIRECT_TRUE, VoidOp, I32Op, I64Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.test(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip, CodeGenerator::T_NEAR);
e.CallIndirect(i.instr, i.src2);
e.L(skip);
}
};
struct CALL_INDIRECT_TRUE_I64
: Sequence<CALL_INDIRECT_TRUE_I64,
I<OPCODE_CALL_INDIRECT_TRUE, VoidOp, I64Op, I64Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.test(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip, CodeGenerator::T_NEAR);
e.CallIndirect(i.instr, i.src2);
e.L(skip);
}
};
struct CALL_INDIRECT_TRUE_F32
: Sequence<CALL_INDIRECT_TRUE_F32,
I<OPCODE_CALL_INDIRECT_TRUE, VoidOp, F32Op, I64Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.vptest(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip, CodeGenerator::T_NEAR);
e.CallIndirect(i.instr, i.src2);
e.L(skip);
}
};
struct CALL_INDIRECT_TRUE_F64
: Sequence<CALL_INDIRECT_TRUE_F64,
I<OPCODE_CALL_INDIRECT_TRUE, VoidOp, F64Op, I64Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.vptest(i.src1, i.src1);
Xbyak::Label skip;
e.jz(skip, CodeGenerator::T_NEAR);
e.CallIndirect(i.instr, i.src2);
e.L(skip);
}
};
EMITTER_OPCODE_TABLE(OPCODE_CALL_INDIRECT_TRUE, CALL_INDIRECT_TRUE_I8,
CALL_INDIRECT_TRUE_I16, CALL_INDIRECT_TRUE_I32,
CALL_INDIRECT_TRUE_I64, CALL_INDIRECT_TRUE_F32,
CALL_INDIRECT_TRUE_F64);
// ============================================================================
// OPCODE_CALL_EXTERN
// ============================================================================
struct CALL_EXTERN
: Sequence<CALL_EXTERN, I<OPCODE_CALL_EXTERN, VoidOp, SymbolOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.CallExtern(i.instr, i.src1.value);
}
};
EMITTER_OPCODE_TABLE(OPCODE_CALL_EXTERN, CALL_EXTERN);
// ============================================================================
// OPCODE_RETURN
// ============================================================================
struct RETURN : Sequence<RETURN, I<OPCODE_RETURN, VoidOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
// If this is the last instruction in the last block, just let us
// fall through.
if (i.instr->next || i.instr->block->next) {
e.jmp(e.epilog_label(), CodeGenerator::T_NEAR);
}
}
};
EMITTER_OPCODE_TABLE(OPCODE_RETURN, RETURN);
// ============================================================================
// OPCODE_RETURN_TRUE
// ============================================================================
struct RETURN_TRUE_I8
: Sequence<RETURN_TRUE_I8, I<OPCODE_RETURN_TRUE, VoidOp, I8Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.test(i.src1, i.src1);
e.jnz(e.epilog_label(), CodeGenerator::T_NEAR);
}
};
struct RETURN_TRUE_I16
: Sequence<RETURN_TRUE_I16, I<OPCODE_RETURN_TRUE, VoidOp, I16Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.test(i.src1, i.src1);
e.jnz(e.epilog_label(), CodeGenerator::T_NEAR);
}
};
struct RETURN_TRUE_I32
: Sequence<RETURN_TRUE_I32, I<OPCODE_RETURN_TRUE, VoidOp, I32Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.test(i.src1, i.src1);
e.jnz(e.epilog_label(), CodeGenerator::T_NEAR);
}
};
struct RETURN_TRUE_I64
: Sequence<RETURN_TRUE_I64, I<OPCODE_RETURN_TRUE, VoidOp, I64Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.test(i.src1, i.src1);
e.jnz(e.epilog_label(), CodeGenerator::T_NEAR);
}
};
struct RETURN_TRUE_F32
: Sequence<RETURN_TRUE_F32, I<OPCODE_RETURN_TRUE, VoidOp, F32Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.vptest(i.src1, i.src1);
e.jnz(e.epilog_label(), CodeGenerator::T_NEAR);
}
};
struct RETURN_TRUE_F64
: Sequence<RETURN_TRUE_F64, I<OPCODE_RETURN_TRUE, VoidOp, F64Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.vptest(i.src1, i.src1);
e.jnz(e.epilog_label(), CodeGenerator::T_NEAR);
}
};
EMITTER_OPCODE_TABLE(OPCODE_RETURN_TRUE, RETURN_TRUE_I8, RETURN_TRUE_I16,
RETURN_TRUE_I32, RETURN_TRUE_I64, RETURN_TRUE_F32,
RETURN_TRUE_F64);
// ============================================================================
// OPCODE_SET_RETURN_ADDRESS
// ============================================================================
struct SET_RETURN_ADDRESS
: Sequence<SET_RETURN_ADDRESS,
I<OPCODE_SET_RETURN_ADDRESS, VoidOp, I64Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.SetReturnAddress(i.src1.constant());
}
};
EMITTER_OPCODE_TABLE(OPCODE_SET_RETURN_ADDRESS, SET_RETURN_ADDRESS);
// ============================================================================
// OPCODE_BRANCH
// ============================================================================
struct BRANCH : Sequence<BRANCH, I<OPCODE_BRANCH, VoidOp, LabelOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.jmp(i.src1.value->name, e.T_NEAR);
}
};
EMITTER_OPCODE_TABLE(OPCODE_BRANCH, BRANCH);
// ============================================================================
// OPCODE_BRANCH_TRUE
// ============================================================================
struct BRANCH_TRUE_I8
: Sequence<BRANCH_TRUE_I8, I<OPCODE_BRANCH_TRUE, VoidOp, I8Op, LabelOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.test(i.src1, i.src1);
e.jnz(i.src2.value->name, e.T_NEAR);
}
};
struct BRANCH_TRUE_I16
: Sequence<BRANCH_TRUE_I16, I<OPCODE_BRANCH_TRUE, VoidOp, I16Op, LabelOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.test(i.src1, i.src1);
e.jnz(i.src2.value->name, e.T_NEAR);
}
};
struct BRANCH_TRUE_I32
: Sequence<BRANCH_TRUE_I32, I<OPCODE_BRANCH_TRUE, VoidOp, I32Op, LabelOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.test(i.src1, i.src1);
e.jnz(i.src2.value->name, e.T_NEAR);
}
};
struct BRANCH_TRUE_I64
: Sequence<BRANCH_TRUE_I64, I<OPCODE_BRANCH_TRUE, VoidOp, I64Op, LabelOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.test(i.src1, i.src1);
e.jnz(i.src2.value->name, e.T_NEAR);
}
};
struct BRANCH_TRUE_F32
: Sequence<BRANCH_TRUE_F32, I<OPCODE_BRANCH_TRUE, VoidOp, F32Op, LabelOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.vptest(i.src1, i.src1);
e.jnz(i.src2.value->name, e.T_NEAR);
}
};
struct BRANCH_TRUE_F64
: Sequence<BRANCH_TRUE_F64, I<OPCODE_BRANCH_TRUE, VoidOp, F64Op, LabelOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.vptest(i.src1, i.src1);
e.jnz(i.src2.value->name, e.T_NEAR);
}
};
EMITTER_OPCODE_TABLE(OPCODE_BRANCH_TRUE, BRANCH_TRUE_I8, BRANCH_TRUE_I16,
BRANCH_TRUE_I32, BRANCH_TRUE_I64, BRANCH_TRUE_F32,
BRANCH_TRUE_F64);
// ============================================================================
// OPCODE_BRANCH_FALSE
// ============================================================================
struct BRANCH_FALSE_I8
: Sequence<BRANCH_FALSE_I8, I<OPCODE_BRANCH_FALSE, VoidOp, I8Op, LabelOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.test(i.src1, i.src1);
e.jz(i.src2.value->name, e.T_NEAR);
}
};
struct BRANCH_FALSE_I16
: Sequence<BRANCH_FALSE_I16,
I<OPCODE_BRANCH_FALSE, VoidOp, I16Op, LabelOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.test(i.src1, i.src1);
e.jz(i.src2.value->name, e.T_NEAR);
}
};
struct BRANCH_FALSE_I32
: Sequence<BRANCH_FALSE_I32,
I<OPCODE_BRANCH_FALSE, VoidOp, I32Op, LabelOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.test(i.src1, i.src1);
e.jz(i.src2.value->name, e.T_NEAR);
}
};
struct BRANCH_FALSE_I64
: Sequence<BRANCH_FALSE_I64,
I<OPCODE_BRANCH_FALSE, VoidOp, I64Op, LabelOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.test(i.src1, i.src1);
e.jz(i.src2.value->name, e.T_NEAR);
}
};
struct BRANCH_FALSE_F32
: Sequence<BRANCH_FALSE_F32,
I<OPCODE_BRANCH_FALSE, VoidOp, F32Op, LabelOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.vptest(i.src1, i.src1);
e.jz(i.src2.value->name, e.T_NEAR);
}
};
struct BRANCH_FALSE_F64
: Sequence<BRANCH_FALSE_F64,
I<OPCODE_BRANCH_FALSE, VoidOp, F64Op, LabelOp>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.vptest(i.src1, i.src1);
e.jz(i.src2.value->name, e.T_NEAR);
}
};
EMITTER_OPCODE_TABLE(OPCODE_BRANCH_FALSE, BRANCH_FALSE_I8, BRANCH_FALSE_I16,
BRANCH_FALSE_I32, BRANCH_FALSE_I64, BRANCH_FALSE_F32,
BRANCH_FALSE_F64);
} // namespace x64
} // namespace backend
} // namespace cpu
} // namespace xe

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -12,6 +12,8 @@
#include "xenia/cpu/hir/instr.h"
#include <unordered_map>
namespace xe {
namespace cpu {
namespace backend {
@@ -19,7 +21,25 @@ namespace x64 {
class X64Emitter;
void RegisterSequences();
typedef bool (*SequenceSelectFn)(X64Emitter&, const hir::Instr*);
extern std::unordered_map<uint32_t, SequenceSelectFn> sequence_table;
template <typename T>
bool Register() {
sequence_table.insert({T::head_key(), T::Select});
return true;
}
template <typename T, typename Tn, typename... Ts>
static bool Register() {
bool b = true;
b = b && Register<T>(); // Call the above function
b = b && Register<Tn, Ts...>(); // Call ourself again (recursively)
return b;
}
#define EMITTER_OPCODE_TABLE(name, ...) \
const auto X64_INSTR_##name = Register<__VA_ARGS__>();
bool SelectSequence(X64Emitter* e, const hir::Instr* i,
const hir::Instr** new_tail);

View File

@@ -10,6 +10,8 @@
#ifndef XENIA_CPU_COMPILER_COMPILER_PASSES_H_
#define XENIA_CPU_COMPILER_COMPILER_PASSES_H_
#include "xenia/cpu/compiler/passes/conditional_group_pass.h"
#include "xenia/cpu/compiler/passes/conditional_group_subpass.h"
#include "xenia/cpu/compiler/passes/constant_propagation_pass.h"
#include "xenia/cpu/compiler/passes/context_promotion_pass.h"
#include "xenia/cpu/compiler/passes/control_flow_analysis_pass.h"

View File

@@ -0,0 +1,85 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/cpu/compiler/passes/conditional_group_pass.h"
#include <gflags/gflags.h>
#include "xenia/base/profiling.h"
#include "xenia/cpu/compiler/compiler.h"
#include "xenia/cpu/ppc/ppc_context.h"
#include "xenia/cpu/processor.h"
namespace xe {
namespace cpu {
namespace compiler {
namespace passes {
// TODO(benvanik): remove when enums redefined.
using namespace xe::cpu::hir;
using xe::cpu::hir::Block;
using xe::cpu::hir::HIRBuilder;
using xe::cpu::hir::Instr;
using xe::cpu::hir::Value;
ConditionalGroupPass::ConditionalGroupPass() : CompilerPass() {}
ConditionalGroupPass::~ConditionalGroupPass() {}
bool ConditionalGroupPass::Initialize(Compiler* compiler) {
if (!CompilerPass::Initialize(compiler)) {
return false;
}
for (size_t i = 0; i < passes_.size(); ++i) {
auto& pass = passes_[i];
if (!pass->Initialize(compiler)) {
return false;
}
}
return true;
}
bool ConditionalGroupPass::Run(HIRBuilder* builder) {
bool dirty;
int loops = 0;
do {
assert_true(loops < 20); // arbitrary number
dirty = false;
for (size_t i = 0; i < passes_.size(); ++i) {
scratch_arena()->Reset();
auto& pass = passes_[i];
auto subpass = dynamic_cast<ConditionalGroupSubpass*>(pass.get());
if (!subpass) {
if (!pass->Run(builder)) {
return false;
}
} else {
bool result = false;
if (!subpass->Run(builder, result)) {
return false;
}
dirty |= result;
}
}
loops++;
} while (dirty);
return true;
}
void ConditionalGroupPass::AddPass(std::unique_ptr<CompilerPass> pass) {
passes_.push_back(std::move(pass));
}
} // namespace passes
} // namespace compiler
} // namespace cpu
} // namespace xe

View File

@@ -0,0 +1,45 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CPU_COMPILER_PASSES_CONDITIONAL_GROUP_PASS_H_
#define XENIA_CPU_COMPILER_PASSES_CONDITIONAL_GROUP_PASS_H_
#include <cmath>
#include <vector>
#include "xenia/base/platform.h"
#include "xenia/cpu/compiler/compiler_pass.h"
#include "xenia/cpu/compiler/passes/conditional_group_subpass.h"
namespace xe {
namespace cpu {
namespace compiler {
namespace passes {
class ConditionalGroupPass : public CompilerPass {
public:
ConditionalGroupPass();
virtual ~ConditionalGroupPass() override;
bool Initialize(Compiler* compiler) override;
bool Run(hir::HIRBuilder* builder) override;
void AddPass(std::unique_ptr<CompilerPass> pass);
private:
std::vector<std::unique_ptr<CompilerPass>> passes_;
};
} // namespace passes
} // namespace compiler
} // namespace cpu
} // namespace xe
#endif // XENIA_CPU_COMPILER_PASSES_CONDITIONAL_GROUP_PASS_H_

View File

@@ -0,0 +1,26 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/cpu/compiler/passes/conditional_group_subpass.h"
#include "xenia/cpu/compiler/compiler.h"
namespace xe {
namespace cpu {
namespace compiler {
namespace passes {
ConditionalGroupSubpass::ConditionalGroupSubpass() : CompilerPass() {}
ConditionalGroupSubpass::~ConditionalGroupSubpass() = default;
} // namespace passes
} // namespace compiler
} // namespace cpu
} // namespace xe

View File

@@ -0,0 +1,47 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CPU_COMPILER_PASSES_CONDITIONAL_GROUP_SUBPASS_H_
#define XENIA_CPU_COMPILER_PASSES_CONDITIONAL_GROUP_SUBPASS_H_
#include "xenia/base/arena.h"
#include "xenia/cpu/compiler/compiler_pass.h"
#include "xenia/cpu/hir/hir_builder.h"
namespace xe {
namespace cpu {
class Processor;
} // namespace cpu
} // namespace xe
namespace xe {
namespace cpu {
namespace compiler {
class Compiler;
namespace passes {
class ConditionalGroupSubpass : public CompilerPass {
public:
ConditionalGroupSubpass();
virtual ~ConditionalGroupSubpass();
bool Run(hir::HIRBuilder* builder) override {
bool dummy;
return Run(builder, dummy);
}
virtual bool Run(hir::HIRBuilder* builder, bool& result) = 0;
};
} // namespace passes
} // namespace compiler
} // namespace cpu
} // namespace xe
#endif // XENIA_CPU_COMPILER_PASSES_CONDITIONAL_GROUP_SUBPASS_H_

View File

@@ -31,11 +31,12 @@ using xe::cpu::hir::HIRBuilder;
using xe::cpu::hir::TypeName;
using xe::cpu::hir::Value;
ConstantPropagationPass::ConstantPropagationPass() : CompilerPass() {}
ConstantPropagationPass::ConstantPropagationPass()
: ConditionalGroupSubpass() {}
ConstantPropagationPass::~ConstantPropagationPass() {}
bool ConstantPropagationPass::Run(HIRBuilder* builder) {
bool ConstantPropagationPass::Run(HIRBuilder* builder, bool& result) {
// Once ContextPromotion has run there will likely be a whole slew of
// constants that can be pushed through the function.
// Example:
@@ -63,6 +64,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
// v1 = 19
// v2 = 0
result = false;
auto block = builder->first_block();
while (block) {
auto i = block->instr_head;
@@ -76,6 +78,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
} else {
i->Remove();
}
result = true;
}
break;
@@ -86,6 +89,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
} else {
i->Remove();
}
result = true;
}
break;
@@ -98,6 +102,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
} else {
i->Remove();
}
result = true;
}
break;
case OPCODE_CALL_INDIRECT:
@@ -109,6 +114,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
}
i->Replace(&OPCODE_CALL_info, i->flags);
i->src1.symbol = function;
result = true;
}
break;
case OPCODE_CALL_INDIRECT_TRUE:
@@ -120,6 +126,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
} else {
i->Remove();
}
result = true;
}
break;
@@ -132,6 +139,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
} else {
i->Remove();
}
result = true;
}
break;
case OPCODE_BRANCH_FALSE:
@@ -143,6 +151,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
} else {
i->Remove();
}
result = true;
}
break;
@@ -152,6 +161,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->Cast(target_type);
i->Remove();
result = true;
}
break;
case OPCODE_CONVERT:
@@ -160,6 +170,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->Convert(target_type, RoundMode(i->flags));
i->Remove();
result = true;
}
break;
case OPCODE_ROUND:
@@ -167,6 +178,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->Round(RoundMode(i->flags));
i->Remove();
result = true;
}
break;
case OPCODE_ZERO_EXTEND:
@@ -175,6 +187,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->ZeroExtend(target_type);
i->Remove();
result = true;
}
break;
case OPCODE_SIGN_EXTEND:
@@ -183,6 +196,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->SignExtend(target_type);
i->Remove();
result = true;
}
break;
case OPCODE_TRUNCATE:
@@ -191,6 +205,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->Truncate(target_type);
i->Remove();
result = true;
}
break;
@@ -210,6 +225,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
i->Replace(&OPCODE_LOAD_MMIO_info, 0);
i->src1.offset = reinterpret_cast<uint64_t>(mmio_range);
i->src2.offset = address;
result = true;
} else {
auto heap = memory->LookupHeap(address);
uint32_t protect;
@@ -222,18 +238,22 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
case INT8_TYPE:
v->set_constant(xe::load<uint8_t>(host_addr));
i->Remove();
result = true;
break;
case INT16_TYPE:
v->set_constant(xe::load<uint16_t>(host_addr));
i->Remove();
result = true;
break;
case INT32_TYPE:
v->set_constant(xe::load<uint32_t>(host_addr));
i->Remove();
result = true;
break;
case INT64_TYPE:
v->set_constant(xe::load<uint64_t>(host_addr));
i->Remove();
result = true;
break;
case VEC128_TYPE:
vec128_t val;
@@ -241,6 +261,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
val.high = xe::load<uint64_t>(host_addr + 8);
v->set_constant(val);
i->Remove();
result = true;
break;
default:
assert_unhandled_case(v->type);
@@ -270,6 +291,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
i->src1.offset = reinterpret_cast<uint64_t>(mmio_range);
i->src2.offset = address;
i->set_src3(value);
result = true;
}
}
break;
@@ -281,10 +303,12 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
auto src2 = i->src2.value;
i->Replace(&OPCODE_ASSIGN_info, 0);
i->set_src1(src2);
result = true;
} else if (i->src1.value->IsConstantFalse()) {
auto src3 = i->src3.value;
i->Replace(&OPCODE_ASSIGN_info, 0);
i->set_src1(src3);
result = true;
} else if (i->src2.value->IsConstant() &&
i->src3.value->IsConstant()) {
// TODO: Select
@@ -305,6 +329,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_constant(uint8_t(0));
}
i->Remove();
result = true;
}
break;
case OPCODE_IS_FALSE:
@@ -315,6 +340,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_constant(uint8_t(0));
}
i->Remove();
result = true;
}
break;
case OPCODE_IS_NAN:
@@ -329,6 +355,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_constant(uint8_t(0));
}
i->Remove();
result = true;
}
break;
@@ -338,6 +365,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
bool value = i->src1.value->IsConstantEQ(i->src2.value);
i->dest->set_constant(uint8_t(value));
i->Remove();
result = true;
}
break;
case OPCODE_COMPARE_NE:
@@ -345,6 +373,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
bool value = i->src1.value->IsConstantNE(i->src2.value);
i->dest->set_constant(uint8_t(value));
i->Remove();
result = true;
}
break;
case OPCODE_COMPARE_SLT:
@@ -352,6 +381,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
bool value = i->src1.value->IsConstantSLT(i->src2.value);
i->dest->set_constant(uint8_t(value));
i->Remove();
result = true;
}
break;
case OPCODE_COMPARE_SLE:
@@ -359,6 +389,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
bool value = i->src1.value->IsConstantSLE(i->src2.value);
i->dest->set_constant(uint8_t(value));
i->Remove();
result = true;
}
break;
case OPCODE_COMPARE_SGT:
@@ -366,6 +397,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
bool value = i->src1.value->IsConstantSGT(i->src2.value);
i->dest->set_constant(uint8_t(value));
i->Remove();
result = true;
}
break;
case OPCODE_COMPARE_SGE:
@@ -373,6 +405,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
bool value = i->src1.value->IsConstantSGE(i->src2.value);
i->dest->set_constant(uint8_t(value));
i->Remove();
result = true;
}
break;
case OPCODE_COMPARE_ULT:
@@ -380,6 +413,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
bool value = i->src1.value->IsConstantULT(i->src2.value);
i->dest->set_constant(uint8_t(value));
i->Remove();
result = true;
}
break;
case OPCODE_COMPARE_ULE:
@@ -387,6 +421,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
bool value = i->src1.value->IsConstantULE(i->src2.value);
i->dest->set_constant(uint8_t(value));
i->Remove();
result = true;
}
break;
case OPCODE_COMPARE_UGT:
@@ -394,6 +429,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
bool value = i->src1.value->IsConstantUGT(i->src2.value);
i->dest->set_constant(uint8_t(value));
i->Remove();
result = true;
}
break;
case OPCODE_COMPARE_UGE:
@@ -401,6 +437,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
bool value = i->src1.value->IsConstantUGE(i->src2.value);
i->dest->set_constant(uint8_t(value));
i->Remove();
result = true;
}
break;
@@ -413,6 +450,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->Add(i->src2.value);
i->Remove();
result = true;
}
break;
case OPCODE_ADD_CARRY:
@@ -433,6 +471,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
i->set_src1(ca);
}
}
result = true;
}
break;
case OPCODE_SUB:
@@ -440,6 +479,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->Sub(i->src2.value);
i->Remove();
result = true;
}
break;
case OPCODE_MUL:
@@ -447,6 +487,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->Mul(i->src2.value);
i->Remove();
result = true;
} else if (i->src1.value->IsConstant() ||
i->src2.value->IsConstant()) {
// Reorder the sources to make things simpler.
@@ -460,12 +501,14 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
if (s2->type != VEC128_TYPE && s2->IsConstantOne()) {
i->Replace(&OPCODE_ASSIGN_info, 0);
i->set_src1(s1);
result = true;
} else if (s2->type == VEC128_TYPE) {
auto& c = s2->constant;
if (c.v128.f32[0] == 1.f && c.v128.f32[1] == 1.f &&
c.v128.f32[2] == 1.f && c.v128.f32[3] == 1.f) {
i->Replace(&OPCODE_ASSIGN_info, 0);
i->set_src1(s1);
result = true;
}
}
}
@@ -475,6 +518,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->MulHi(i->src2.value, (i->flags & ARITHMETIC_UNSIGNED) != 0);
i->Remove();
result = true;
}
break;
case OPCODE_DIV:
@@ -482,6 +526,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->Div(i->src2.value, (i->flags & ARITHMETIC_UNSIGNED) != 0);
i->Remove();
result = true;
} else if (i->src2.value->IsConstant()) {
// Division by one = no-op.
Value* src1 = i->src1.value;
@@ -489,12 +534,14 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
i->src2.value->IsConstantOne()) {
i->Replace(&OPCODE_ASSIGN_info, 0);
i->set_src1(src1);
result = true;
} else if (i->src2.value->type == VEC128_TYPE) {
auto& c = i->src2.value->constant;
if (c.v128.f32[0] == 1.f && c.v128.f32[1] == 1.f &&
c.v128.f32[2] == 1.f && c.v128.f32[3] == 1.f) {
i->Replace(&OPCODE_ASSIGN_info, 0);
i->set_src1(src1);
result = true;
}
}
}
@@ -505,6 +552,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
Value::MulAdd(v, i->src1.value, i->src2.value, i->src3.value);
i->Remove();
result = true;
} else {
// Multiply part is constant.
Value* mul = builder->AllocValue();
@@ -515,6 +563,8 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
i->Replace(&OPCODE_ADD_info, 0);
i->set_src1(mul);
i->set_src2(add);
result = true;
}
}
break;
@@ -525,6 +575,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
Value::MulSub(v, i->src1.value, i->src2.value, i->src3.value);
i->Remove();
result = true;
} else {
// Multiply part is constant.
Value* mul = builder->AllocValue();
@@ -535,6 +586,8 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
i->Replace(&OPCODE_SUB_info, 0);
i->set_src1(mul);
i->set_src2(add);
result = true;
}
}
break;
@@ -543,6 +596,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->Max(i->src2.value);
i->Remove();
result = true;
}
break;
case OPCODE_NEG:
@@ -550,6 +604,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->Neg();
i->Remove();
result = true;
}
break;
case OPCODE_ABS:
@@ -557,6 +612,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->Abs();
i->Remove();
result = true;
}
break;
case OPCODE_SQRT:
@@ -564,6 +620,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->Sqrt();
i->Remove();
result = true;
}
break;
case OPCODE_RSQRT:
@@ -571,6 +628,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->RSqrt();
i->Remove();
result = true;
}
break;
case OPCODE_RECIP:
@@ -578,6 +636,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->Recip();
i->Remove();
result = true;
}
break;
case OPCODE_AND:
@@ -585,6 +644,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->And(i->src2.value);
i->Remove();
result = true;
}
break;
case OPCODE_OR:
@@ -592,6 +652,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->Or(i->src2.value);
i->Remove();
result = true;
}
break;
case OPCODE_XOR:
@@ -599,11 +660,13 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->Xor(i->src2.value);
i->Remove();
result = true;
} else if (!i->src1.value->IsConstant() &&
!i->src2.value->IsConstant() &&
i->src1.value == i->src2.value) {
v->set_zero(v->type);
i->Remove();
result = true;
}
break;
case OPCODE_NOT:
@@ -611,6 +674,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->Not();
i->Remove();
result = true;
}
break;
case OPCODE_SHL:
@@ -618,10 +682,12 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->Shl(i->src2.value);
i->Remove();
result = true;
} else if (i->src2.value->IsConstantZero()) {
auto src1 = i->src1.value;
i->Replace(&OPCODE_ASSIGN_info, 0);
i->set_src1(src1);
result = true;
}
break;
case OPCODE_SHR:
@@ -629,10 +695,12 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->Shr(i->src2.value);
i->Remove();
result = true;
} else if (i->src2.value->IsConstantZero()) {
auto src1 = i->src1.value;
i->Replace(&OPCODE_ASSIGN_info, 0);
i->set_src1(src1);
result = true;
}
break;
case OPCODE_SHA:
@@ -640,6 +708,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->Sha(i->src2.value);
i->Remove();
result = true;
}
break;
// TODO(benvanik): ROTATE_LEFT
@@ -648,6 +717,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->ByteSwap();
i->Remove();
result = true;
}
break;
case OPCODE_CNTLZ:
@@ -655,6 +725,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_zero(v->type);
v->CountLeadingZeros(i->src1.value);
i->Remove();
result = true;
}
break;
// TODO(benvanik): INSERT/EXTRACT
@@ -664,6 +735,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_zero(v->type);
v->Extract(i->src1.value, i->src2.value);
i->Remove();
result = true;
}
break;
case OPCODE_SPLAT:
@@ -671,6 +743,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_zero(v->type);
v->Splat(i->src1.value);
i->Remove();
result = true;
}
break;
case OPCODE_VECTOR_COMPARE_EQ:
@@ -678,6 +751,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->VectorCompareEQ(i->src2.value, hir::TypeName(i->flags));
i->Remove();
result = true;
}
break;
case OPCODE_VECTOR_COMPARE_SGT:
@@ -685,6 +759,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->VectorCompareSGT(i->src2.value, hir::TypeName(i->flags));
i->Remove();
result = true;
}
break;
case OPCODE_VECTOR_COMPARE_SGE:
@@ -692,6 +767,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->VectorCompareSGE(i->src2.value, hir::TypeName(i->flags));
i->Remove();
result = true;
}
break;
case OPCODE_VECTOR_COMPARE_UGT:
@@ -699,6 +775,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->VectorCompareUGT(i->src2.value, hir::TypeName(i->flags));
i->Remove();
result = true;
}
break;
case OPCODE_VECTOR_COMPARE_UGE:
@@ -706,6 +783,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->VectorCompareUGE(i->src2.value, hir::TypeName(i->flags));
i->Remove();
result = true;
}
break;
case OPCODE_VECTOR_CONVERT_F2I:
@@ -714,6 +792,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->VectorConvertF2I(i->src1.value,
!!(i->flags & ARITHMETIC_UNSIGNED));
i->Remove();
result = true;
}
break;
case OPCODE_VECTOR_CONVERT_I2F:
@@ -722,6 +801,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->VectorConvertI2F(i->src1.value,
!!(i->flags & ARITHMETIC_UNSIGNED));
i->Remove();
result = true;
}
break;
case OPCODE_VECTOR_SHL:
@@ -729,6 +809,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->VectorShl(i->src2.value, hir::TypeName(i->flags));
i->Remove();
result = true;
}
break;
case OPCODE_VECTOR_SHR:
@@ -736,6 +817,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->VectorShr(i->src2.value, hir::TypeName(i->flags));
i->Remove();
result = true;
}
break;
case OPCODE_VECTOR_ROTATE_LEFT:
@@ -743,6 +825,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->VectorRol(i->src2.value, hir::TypeName(i->flags));
i->Remove();
result = true;
}
break;
case OPCODE_VECTOR_ADD:
@@ -753,6 +836,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
!!(arith_flags & ARITHMETIC_UNSIGNED),
!!(arith_flags & ARITHMETIC_SATURATE));
i->Remove();
result = true;
}
break;
case OPCODE_VECTOR_SUB:
@@ -763,6 +847,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
!!(arith_flags & ARITHMETIC_UNSIGNED),
!!(arith_flags & ARITHMETIC_SATURATE));
i->Remove();
result = true;
}
break;
@@ -771,6 +856,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->DotProduct3(i->src2.value);
i->Remove();
result = true;
}
break;
@@ -779,6 +865,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
v->set_from(i->src1.value);
v->DotProduct4(i->src2.value);
i->Remove();
result = true;
}
break;
@@ -790,6 +877,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
!!(arith_flags & ARITHMETIC_UNSIGNED),
!!(arith_flags & ARITHMETIC_SATURATE));
i->Remove();
result = true;
}
break;

View File

@@ -10,19 +10,19 @@
#ifndef XENIA_CPU_COMPILER_PASSES_CONSTANT_PROPAGATION_PASS_H_
#define XENIA_CPU_COMPILER_PASSES_CONSTANT_PROPAGATION_PASS_H_
#include "xenia/cpu/compiler/compiler_pass.h"
#include "xenia/cpu/compiler/passes/conditional_group_subpass.h"
namespace xe {
namespace cpu {
namespace compiler {
namespace passes {
class ConstantPropagationPass : public CompilerPass {
class ConstantPropagationPass : public ConditionalGroupSubpass {
public:
ConstantPropagationPass();
~ConstantPropagationPass() override;
bool Run(hir::HIRBuilder* builder) override;
bool Run(hir::HIRBuilder* builder, bool& result) override;
private:
};

View File

@@ -23,17 +23,18 @@ using xe::cpu::hir::HIRBuilder;
using xe::cpu::hir::Instr;
using xe::cpu::hir::Value;
SimplificationPass::SimplificationPass() : CompilerPass() {}
SimplificationPass::SimplificationPass() : ConditionalGroupSubpass() {}
SimplificationPass::~SimplificationPass() {}
bool SimplificationPass::Run(HIRBuilder* builder) {
EliminateConversions(builder);
SimplifyAssignments(builder);
bool SimplificationPass::Run(HIRBuilder* builder, bool& result) {
result = false;
result |= EliminateConversions(builder);
result |= SimplifyAssignments(builder);
return true;
}
void SimplificationPass::EliminateConversions(HIRBuilder* builder) {
bool SimplificationPass::EliminateConversions(HIRBuilder* builder) {
// First, we check for truncates/extensions that can be skipped.
// This generates some assignments which then the second step will clean up.
// Both zero/sign extends can be skipped:
@@ -43,6 +44,7 @@ void SimplificationPass::EliminateConversions(HIRBuilder* builder) {
// v1.i64 = zero/sign_extend v0.i32 (may be dead code removed later)
// v2.i32 = v0.i32
bool result = false;
auto block = builder->first_block();
while (block) {
auto i = block->instr_head;
@@ -51,20 +53,21 @@ void SimplificationPass::EliminateConversions(HIRBuilder* builder) {
// back to definition).
if (i->opcode == &OPCODE_TRUNCATE_info) {
// Matches zero/sign_extend + truncate.
CheckTruncate(i);
result |= CheckTruncate(i);
} else if (i->opcode == &OPCODE_BYTE_SWAP_info) {
// Matches byte swap + byte swap.
// This is pretty rare within the same basic block, but is in the
// memcpy hot path and (probably) worth it. Maybe.
CheckByteSwap(i);
result |= CheckByteSwap(i);
}
i = i->next;
}
block = block->next;
}
return result;
}
void SimplificationPass::CheckTruncate(Instr* i) {
bool SimplificationPass::CheckTruncate(Instr* i) {
// Walk backward up src's chain looking for an extend. We may have
// assigns, so skip those.
auto src = i->src1.value;
@@ -80,6 +83,7 @@ void SimplificationPass::CheckTruncate(Instr* i) {
// Types match, use original by turning this into an assign.
i->Replace(&OPCODE_ASSIGN_info, 0);
i->set_src1(def->src1.value);
return true;
}
} else if (def->opcode == &OPCODE_ZERO_EXTEND_info) {
// Value comes from a zero extend.
@@ -87,12 +91,14 @@ void SimplificationPass::CheckTruncate(Instr* i) {
// Types match, use original by turning this into an assign.
i->Replace(&OPCODE_ASSIGN_info, 0);
i->set_src1(def->src1.value);
return true;
}
}
}
return false;
}
void SimplificationPass::CheckByteSwap(Instr* i) {
bool SimplificationPass::CheckByteSwap(Instr* i) {
// Walk backward up src's chain looking for a byte swap. We may have
// assigns, so skip those.
auto src = i->src1.value;
@@ -107,11 +113,13 @@ void SimplificationPass::CheckByteSwap(Instr* i) {
// Types match, use original by turning this into an assign.
i->Replace(&OPCODE_ASSIGN_info, 0);
i->set_src1(def->src1.value);
return true;
}
}
return false;
}
void SimplificationPass::SimplifyAssignments(HIRBuilder* builder) {
bool SimplificationPass::SimplifyAssignments(HIRBuilder* builder) {
// Run over the instructions and rename assigned variables:
// v1 = v0
// v2 = v1
@@ -129,27 +137,35 @@ void SimplificationPass::SimplifyAssignments(HIRBuilder* builder) {
// of that instr. Because we may have chains, we do this recursively until
// we find a non-assign def.
bool result = false;
auto block = builder->first_block();
while (block) {
auto i = block->instr_head;
while (i) {
uint32_t signature = i->opcode->signature;
if (GET_OPCODE_SIG_TYPE_SRC1(signature) == OPCODE_SIG_TYPE_V) {
i->set_src1(CheckValue(i->src1.value));
bool modified = false;
i->set_src1(CheckValue(i->src1.value, modified));
result |= modified;
}
if (GET_OPCODE_SIG_TYPE_SRC2(signature) == OPCODE_SIG_TYPE_V) {
i->set_src2(CheckValue(i->src2.value));
bool modified = false;
i->set_src2(CheckValue(i->src2.value, modified));
result |= modified;
}
if (GET_OPCODE_SIG_TYPE_SRC3(signature) == OPCODE_SIG_TYPE_V) {
i->set_src3(CheckValue(i->src3.value));
bool modified = false;
i->set_src3(CheckValue(i->src3.value, modified));
result |= modified;
}
i = i->next;
}
block = block->next;
}
return result;
}
Value* SimplificationPass::CheckValue(Value* value) {
Value* SimplificationPass::CheckValue(Value* value, bool& result) {
auto def = value->def;
if (def && def->opcode == &OPCODE_ASSIGN_info) {
// Value comes from an assignment - recursively find if it comes from
@@ -162,8 +178,10 @@ Value* SimplificationPass::CheckValue(Value* value) {
}
replacement = def->src1.value;
}
result = true;
return replacement;
}
result = false;
return value;
}

View File

@@ -10,27 +10,27 @@
#ifndef XENIA_CPU_COMPILER_PASSES_SIMPLIFICATION_PASS_H_
#define XENIA_CPU_COMPILER_PASSES_SIMPLIFICATION_PASS_H_
#include "xenia/cpu/compiler/compiler_pass.h"
#include "xenia/cpu/compiler/passes/conditional_group_subpass.h"
namespace xe {
namespace cpu {
namespace compiler {
namespace passes {
class SimplificationPass : public CompilerPass {
class SimplificationPass : public ConditionalGroupSubpass {
public:
SimplificationPass();
~SimplificationPass() override;
bool Run(hir::HIRBuilder* builder) override;
bool Run(hir::HIRBuilder* builder, bool& result) override;
private:
void EliminateConversions(hir::HIRBuilder* builder);
void CheckTruncate(hir::Instr* i);
void CheckByteSwap(hir::Instr* i);
bool EliminateConversions(hir::HIRBuilder* builder);
bool CheckTruncate(hir::Instr* i);
bool CheckByteSwap(hir::Instr* i);
void SimplifyAssignments(hir::HIRBuilder* builder);
hir::Value* CheckValue(hir::Value* value);
bool SimplifyAssignments(hir::HIRBuilder* builder);
hir::Value* CheckValue(hir::Value* value, bool& result);
};
} // namespace passes

View File

@@ -170,6 +170,7 @@ class Value {
constant.v128 = value;
}
void set_from(const Value* other) {
assert_true(other->IsConstant());
type = other->type;
flags = other->flags;
constant.v128 = other->constant.v128;

View File

@@ -53,15 +53,16 @@ PPCTranslator::PPCTranslator(PPCFrontend* frontend) : frontend_(frontend) {
if (validate) compiler_->AddPass(std::make_unique<passes::ValidationPass>());
compiler_->AddPass(std::make_unique<passes::ContextPromotionPass>());
if (validate) compiler_->AddPass(std::make_unique<passes::ValidationPass>());
// TODO(gibbed): loop until these passes stop making changes?
for (int i = 0; i < 5; ++i) {
compiler_->AddPass(std::make_unique<passes::SimplificationPass>());
if (validate)
compiler_->AddPass(std::make_unique<passes::ValidationPass>());
compiler_->AddPass(std::make_unique<passes::ConstantPropagationPass>());
if (validate)
compiler_->AddPass(std::make_unique<passes::ValidationPass>());
}
// Grouped simplification + constant propagation.
// Loops until no changes are made.
auto sap = std::make_unique<passes::ConditionalGroupPass>();
sap->AddPass(std::make_unique<passes::SimplificationPass>());
if (validate) sap->AddPass(std::make_unique<passes::ValidationPass>());
sap->AddPass(std::make_unique<passes::ConstantPropagationPass>());
if (validate) sap->AddPass(std::make_unique<passes::ValidationPass>());
compiler_->AddPass(std::move(sap));
if (backend->machine_info()->supports_extended_load_store) {
// Backend supports the advanced LOAD/STORE instructions.
// These will save us a lot of HIR opcodes.

View File

@@ -13,6 +13,7 @@ project("xenia-cpu-ppc-tests")
"xenia-base",
"gflags",
"capstone", -- cpu-backend-x64
"mspack",
})
files({
"ppc_testing_main.cc",

View File

@@ -8,6 +8,7 @@ project("xenia-cpu")
language("C++")
links({
"xenia-base",
"mspack",
})
includedirs({
project_root.."/third_party/llvm/include",

View File

@@ -25,7 +25,6 @@
#include "third_party/crypto/rijndael-alg-fst.c"
#include "third_party/crypto/rijndael-alg-fst.h"
#include "third_party/mspack/lzx.h"
#include "third_party/mspack/lzxd.c"
#include "third_party/mspack/mspack.h"
#include "third_party/pe/pe_image.h"
@@ -120,7 +119,7 @@ int lzx_decompress(const void* lzx_data, size_t lzx_len, void* dest,
mspack_memory_file* lzxdst = mspack_memory_open(sys, dest, dest_len);
lzxd_stream* lzxd =
lzxd_init(sys, (struct mspack_file*)lzxsrc, (struct mspack_file*)lzxdst,
window_bits, 0, 0x8000, (off_t)dest_len);
window_bits, 0, 0x8000, (off_t)dest_len, 0);
if (lzxd) {
if (window_data) {
@@ -1120,23 +1119,23 @@ bool XexModule::LoadContinue() {
processor_->backend()->CommitExecutableRange(low_address_, high_address_);
// Add all imports (variables/functions).
xex2_opt_import_libraries* opt_import_header = nullptr;
GetOptHeader(XEX_HEADER_IMPORT_LIBRARIES, &opt_import_header);
xex2_opt_import_libraries* opt_import_libraries = nullptr;
GetOptHeader(XEX_HEADER_IMPORT_LIBRARIES, &opt_import_libraries);
if (opt_import_header) {
if (opt_import_libraries) {
// FIXME: Don't know if 32 is the actual limit, but haven't seen more than
// 2.
const char* string_table[32];
std::memset(string_table, 0, sizeof(string_table));
size_t max_string_table_index = 0;
// Parse the string table
for (size_t i = 0; i < opt_import_header->string_table_size;
++max_string_table_index) {
assert_true(max_string_table_index < xe::countof(string_table));
const char* str = opt_import_header->string_table + i;
for (size_t i = 0, o = 0; i < opt_import_libraries->string_table.size &&
o < opt_import_libraries->string_table.count;
++o) {
assert_true(o < xe::countof(string_table));
const char* str = &opt_import_libraries->string_table.data[i];
string_table[max_string_table_index] = str;
string_table[o] = str;
i += std::strlen(str) + 1;
// Padding
@@ -1145,15 +1144,19 @@ bool XexModule::LoadContinue() {
}
}
auto libraries_ptr = reinterpret_cast<uint8_t*>(opt_import_header) +
opt_import_header->string_table_size + 12;
auto library_data = reinterpret_cast<uint8_t*>(opt_import_libraries) +
opt_import_libraries->string_table.size + 12;
uint32_t library_offset = 0;
uint32_t library_count = opt_import_header->library_count;
for (uint32_t i = 0; i < library_count; i++) {
auto library = reinterpret_cast<xex2_import_library*>(libraries_ptr +
library_offset);
while (library_offset < opt_import_libraries->size) {
auto library =
reinterpret_cast<xex2_import_library*>(library_data + library_offset);
if (!library->size) {
break;
}
size_t library_name_index = library->name_index & 0xFF;
assert_true(library_name_index < max_string_table_index);
assert_true(library_name_index <
opt_import_libraries->string_table.count);
assert_not_null(string_table[library_name_index]);
SetupLibraryImports(string_table[library_name_index], library);
library_offset += library->size;
}
@@ -1313,10 +1316,12 @@ bool XexModule::SetupLibraryImports(const char* name,
var_info->set_status(Symbol::Status::kDefined);
} else if (record_type == 1) {
// Thunk.
assert_true(library_info.imports.size() > 0);
auto& prev_import = library_info.imports[library_info.imports.size() - 1];
assert_true(prev_import.ordinal == ordinal);
prev_import.thunk_address = record_addr;
if (library_info.imports.size() > 0) {
auto& prev_import =
library_info.imports[library_info.imports.size() - 1];
assert_true(prev_import.ordinal == ordinal);
prev_import.thunk_address = record_addr;
}
if (kernel_export) {
import_name.AppendFormat("%s", kernel_export->name);

View File

@@ -38,6 +38,7 @@ project("xenia-gpu-vulkan-trace-viewer")
"imgui",
"libavcodec",
"libavutil",
"mspack",
"snappy",
"spirv-tools",
"volk",
@@ -110,6 +111,7 @@ project("xenia-gpu-vulkan-trace-dump")
"imgui",
"libavcodec",
"libavutil",
"mspack",
"snappy",
"spirv-tools",
"volk",

View File

@@ -486,29 +486,33 @@ void UserModule::Dump() {
std::memset(string_table, 0, sizeof(string_table));
// Parse the string table
for (size_t l = 0, j = 0; l < opt_import_libraries->string_table_size;
j++) {
assert_true(j < xe::countof(string_table));
const char* str = opt_import_libraries->string_table + l;
for (size_t j = 0, o = 0; j < opt_import_libraries->string_table.size &&
o < opt_import_libraries->string_table.count;
o++) {
assert_true(o < xe::countof(string_table));
const char* str = &opt_import_libraries->string_table.data[j];
string_table[j] = str;
l += std::strlen(str) + 1;
string_table[o] = str;
j += std::strlen(str) + 1;
// Padding
if ((l % 4) != 0) {
l += 4 - (l % 4);
if ((j % 4) != 0) {
j += 4 - (j % 4);
}
}
auto libraries =
auto library_data =
reinterpret_cast<const uint8_t*>(opt_import_libraries) +
opt_import_libraries->string_table_size + 12;
opt_import_libraries->string_table.size + 12;
uint32_t library_offset = 0;
uint32_t library_count = opt_import_libraries->library_count;
for (uint32_t l = 0; l < library_count; l++) {
while (library_offset < opt_import_libraries->size) {
auto library = reinterpret_cast<const xex2_import_library*>(
libraries + library_offset);
library_data + library_offset);
if (!library->size) {
break;
}
auto name = string_table[library->name_index & 0xFF];
assert_not_null(name);
sb.AppendFormat(" %s - %d imports\n", name,
(uint16_t)library->count);
@@ -786,11 +790,11 @@ void UserModule::Dump() {
}
if (kernel_export &&
kernel_export->type == cpu::Export::Type::kVariable) {
sb.AppendFormat(" V %.8X %.3X (%3d) %s %s\n",
sb.AppendFormat(" V %.8X %.3X (%4d) %s %s\n",
info->value_address, info->ordinal, info->ordinal,
implemented ? " " : "!!", name);
} else if (info->thunk_address) {
sb.AppendFormat(" F %.8X %.8X %.3X (%3d) %s %s\n",
sb.AppendFormat(" F %.8X %.8X %.3X (%4d) %s %s\n",
info->value_address, info->thunk_address,
info->ordinal, info->ordinal,
implemented ? " " : "!!", name);

View File

@@ -474,10 +474,12 @@ struct xex2_opt_execution_info {
static_assert_size(xex2_opt_execution_info, 0x18);
struct xex2_opt_import_libraries {
xe::be<uint32_t> section_size; // 0x0
xe::be<uint32_t> string_table_size; // 0x4
xe::be<uint32_t> library_count; // 0x8
char string_table[1]; // 0xC string_table_size bytes
xe::be<uint32_t> size; // 0x0
struct {
xe::be<uint32_t> size; // 0x4
xe::be<uint32_t> count; // 0x8
char data[1]; // 0xC string_table_size bytes
} string_table;
};
struct xex2_import_library {

View File

@@ -23,7 +23,7 @@ struct DeviceInfo {
uint32_t device_type;
uint64_t total_bytes;
uint64_t free_bytes;
std::wstring name;
wchar_t name[28];
};
static const DeviceInfo dummy_device_info_ = {
0xF00D0000,
@@ -57,7 +57,7 @@ dword_result_t XamContentGetDeviceName(dword_t device_id,
return X_ERROR_DEVICE_NOT_CONNECTED;
}
if (name_capacity < dummy_device_info_.name.size() + 1) {
if (name_capacity < wcslen(dummy_device_info_.name) + 1) {
return X_ERROR_INSUFFICIENT_BUFFER;
}
@@ -174,6 +174,35 @@ dword_result_t XamContentCreateEnumerator(dword_t user_index, dword_t device_id,
}
DECLARE_XAM_EXPORT1(XamContentCreateEnumerator, kContent, kImplemented);
dword_result_t XamContentCreateDeviceEnumerator(dword_t content_type,
dword_t content_flags,
dword_t max_count,
lpdword_t buffer_size_ptr,
lpdword_t handle_out) {
assert_not_null(handle_out);
if (buffer_size_ptr) {
*buffer_size_ptr = sizeof(DeviceInfo) * max_count;
}
auto e = new XStaticEnumerator(kernel_state(), max_count, sizeof(DeviceInfo));
e->Initialize();
// Copy our dummy device into the enumerator
DeviceInfo* dev = (DeviceInfo*)e->AppendItem();
if (dev) {
xe::store_and_swap(&dev->device_id, dummy_device_info_.device_id);
xe::store_and_swap(&dev->device_type, dummy_device_info_.device_type);
xe::store_and_swap(&dev->total_bytes, dummy_device_info_.total_bytes);
xe::store_and_swap(&dev->free_bytes, dummy_device_info_.free_bytes);
xe::copy_and_swap(dev->name, dummy_device_info_.name, 28);
}
*handle_out = e->handle();
return X_ERROR_SUCCESS;
}
DECLARE_XAM_EXPORT1(XamContentCreateDeviceEnumerator, kNone, kImplemented);
dword_result_t XamContentCreateEx(dword_t user_index, lpstring_t root_name,
lpvoid_t content_data_ptr, dword_t flags,
lpdword_t disposition_ptr,

View File

@@ -17,6 +17,10 @@
#include "xenia/kernel/xthread.h"
#include "xenia/xbox.h"
#if XE_PLATFORM_WIN32
#include "xenia/base/platform_win.h"
#endif
namespace xe {
namespace kernel {
namespace xam {
@@ -24,6 +28,152 @@ namespace xam {
constexpr uint32_t X_LANGUAGE_ENGLISH = 1;
constexpr uint32_t X_LANGUAGE_JAPANESE = 2;
dword_result_t XamGetOnlineSchema() {
static uint32_t schema_guest = 0;
static uint32_t schema_ptr_guest = 0;
if (!schema_guest) {
// create a dummy schema, 8 bytes of 0 seems to work fine
// (with another 8 bytes for schema ptr/schema size)
schema_guest = kernel_state()->memory()->SystemHeapAlloc(16);
schema_ptr_guest = schema_guest + 8;
auto schema = kernel_state()->memory()->TranslateVirtual(schema_guest);
memset(schema, 0, 16);
// store schema ptr + size
xe::store_and_swap<uint32_t>(schema + 0x8, schema_guest);
xe::store_and_swap<uint32_t>(schema + 0xC, 0x8);
}
// return pointer to the schema ptr/schema size struct
return schema_ptr_guest;
}
DECLARE_XAM_EXPORT2(XamGetOnlineSchema, kNone, kImplemented, kSketchy);
void XamFormatDateString(dword_t unk, qword_t filetime, lpvoid_t buffer,
dword_t buffer_length) {
std::memset(buffer, 0, buffer_length * 2);
// TODO: implement this for other platforms
#if XE_PLATFORM_WIN32
FILETIME t;
t.dwHighDateTime = filetime >> 32;
t.dwLowDateTime = (uint32_t)filetime;
SYSTEMTIME st;
SYSTEMTIME stLocal;
FileTimeToSystemTime(&t, &st);
SystemTimeToTzSpecificLocalTime(NULL, &st, &stLocal);
wchar_t buf[256];
// TODO: format this depending on users locale?
swprintf(buf, 256, L"%02d/%02d/%d", stLocal.wMonth, stLocal.wDay,
stLocal.wYear);
xe::copy_and_swap((wchar_t*)buffer.host_address(), buf, buffer_length);
#else
assert_always();
#endif
}
DECLARE_XAM_EXPORT1(XamFormatDateString, kNone, kImplemented);
void XamFormatTimeString(dword_t unk, qword_t filetime, lpvoid_t buffer,
dword_t buffer_length) {
std::memset(buffer, 0, buffer_length * 2);
// TODO: implement this for other platforms
#if XE_PLATFORM_WIN32
FILETIME t;
t.dwHighDateTime = filetime >> 32;
t.dwLowDateTime = (uint32_t)filetime;
SYSTEMTIME st;
SYSTEMTIME stLocal;
FileTimeToSystemTime(&t, &st);
SystemTimeToTzSpecificLocalTime(NULL, &st, &stLocal);
wchar_t buf[256];
swprintf(buf, 256, L"%02d:%02d", stLocal.wHour, stLocal.wMinute);
xe::copy_and_swap((wchar_t*)buffer.host_address(), buf, buffer_length);
#else
assert_always();
#endif
}
DECLARE_XAM_EXPORT1(XamFormatTimeString, kNone, kImplemented);
dword_result_t keXamBuildResourceLocator(uint64_t module,
const wchar_t* container,
const wchar_t* resource,
lpvoid_t buffer,
uint32_t buffer_length) {
wchar_t buf[256];
if (!module) {
swprintf(buf, 256, L"file://media:/%s.xzp#%s", container, resource);
XELOGD(
"XamBuildResourceLocator(%ws) returning locator to local file %ws.xzp",
container, container);
} else {
swprintf(buf, 256, L"section://%X,%s#%s", (uint32_t)module, container,
resource);
}
xe::copy_and_swap((wchar_t*)buffer.host_address(), buf, buffer_length);
return 0;
}
dword_result_t XamBuildResourceLocator(qword_t module, lpwstring_t container,
lpwstring_t resource, lpvoid_t buffer,
dword_t buffer_length) {
return keXamBuildResourceLocator(module, container.value().c_str(),
resource.value().c_str(), buffer,
buffer_length);
}
DECLARE_XAM_EXPORT1(XamBuildResourceLocator, kNone, kImplemented);
dword_result_t XamBuildGamercardResourceLocator(lpwstring_t filename,
lpvoid_t buffer,
dword_t buffer_length) {
// On an actual xbox these funcs would return a locator to xam.xex resources,
// but for Xenia we can return a locator to the resources as local files. (big
// thanks to MS for letting XamBuildResourceLocator return local file
// locators!)
// If you're running an app that'll need them, make sure to extract xam.xex
// resources with xextool ("xextool -d . xam.xex") and add a .xzp extension.
return keXamBuildResourceLocator(0, L"gamercrd", filename.value().c_str(),
buffer, buffer_length);
}
DECLARE_XAM_EXPORT1(XamBuildGamercardResourceLocator, kNone, kImplemented);
dword_result_t XamBuildSharedSystemResourceLocator(lpwstring_t filename,
lpvoid_t buffer,
dword_t buffer_length) {
// see notes inside XamBuildGamercardResourceLocator above
return keXamBuildResourceLocator(0, L"shrdres", filename.value().c_str(),
buffer, buffer_length);
}
DECLARE_XAM_EXPORT1(XamBuildSharedSystemResourceLocator, kNone, kImplemented);
dword_result_t XamBuildLegacySystemResourceLocator(lpwstring_t filename,
lpvoid_t buffer,
dword_t buffer_length) {
return XamBuildSharedSystemResourceLocator(filename, buffer, buffer_length);
}
DECLARE_XAM_EXPORT1(XamBuildLegacySystemResourceLocator, kNone, kImplemented);
dword_result_t XamBuildXamResourceLocator(lpwstring_t filename, lpvoid_t buffer,
dword_t buffer_length) {
return keXamBuildResourceLocator(0, L"xam", filename.value().c_str(), buffer,
buffer_length);
}
DECLARE_XAM_EXPORT1(XamBuildXamResourceLocator, kNone, kImplemented);
dword_result_t XamGetSystemVersion() {
// eh, just picking one. If we go too low we may break new games, but
// this value seems to be used for conditionally loading symbols and if

View File

@@ -18,7 +18,8 @@ namespace xe {
namespace kernel {
namespace xam {
dword_result_t XamNotifyCreateListener(qword_t mask, dword_t one) {
dword_result_t XamNotifyCreateListenerInternal(qword_t mask, dword_t unk,
dword_t one) {
// r4=1 may indicate user process?
auto listener =
@@ -30,6 +31,12 @@ dword_result_t XamNotifyCreateListener(qword_t mask, dword_t one) {
return handle;
}
DECLARE_XAM_EXPORT2(XamNotifyCreateListenerInternal, kNone, kImplemented,
kSketchy);
dword_result_t XamNotifyCreateListener(qword_t mask, dword_t one) {
return XamNotifyCreateListenerInternal(mask, 0, one);
}
DECLARE_XAM_EXPORT1(XamNotifyCreateListener, kNone, kImplemented);
// https://github.com/CodeAsm/ffplay360/blob/master/Common/AtgSignIn.cpp

View File

@@ -588,7 +588,7 @@ XE_EXPORT(xam, 0x00000318, XamVoiceGetMicArrayStatus,
XE_EXPORT(xam, 0x00000319, XamVoiceSetAudioCaptureRoutine, kFunction),
XE_EXPORT(xam, 0x0000031A, XamVoiceGetDirectionalData, kFunction),
XE_EXPORT(xam, 0x0000031B, XamBuildResourceLocator, kFunction),
XE_EXPORT(xam, 0x0000031C, XamBuildSharedSystemResourceLocator_, kFunction),
XE_EXPORT(xam, 0x0000031C, XamBuildLegacySystemResourceLocator, kFunction),
XE_EXPORT(xam, 0x0000031D, XamBuildGamercardResourceLocator, kFunction),
XE_EXPORT(xam, 0x0000031E, XamBuildDynamicResourceLocator, kFunction),
XE_EXPORT(xam, 0x0000031F, XamBuildXamResourceLocator, kFunction),

View File

@@ -159,6 +159,14 @@ XboxkrnlModule::XboxkrnlModule(Emulator* emulator, KernelState* kernel_state)
xe::store_and_swap<uint8_t>(lpXboxHardwareInfo + 4, 0x06); // cpu count
// Remaining 11b are zeroes?
// ExConsoleGameRegion, probably same values as keyvault region uses?
// Just return all 0xFF, should satisfy anything that checks it
uint32_t pExConsoleGameRegion = memory_->SystemHeapAlloc(4);
auto lpExConsoleGameRegion = memory_->TranslateVirtual(pExConsoleGameRegion);
export_resolver_->SetVariableMapping(
"xboxkrnl.exe", ordinals::ExConsoleGameRegion, pExConsoleGameRegion);
xe::store<uint32_t>(lpExConsoleGameRegion, 0xFFFFFFFF);
// XexExecutableModuleHandle (?**)
// Games try to dereference this to get a pointer to some module struct.
// So far it seems like it's just in loader code, and only used to look up

View File

@@ -1009,6 +1009,46 @@ SHIM_CALL _vsnprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) {
SHIM_SET_RETURN_32(count);
}
// https://msdn.microsoft.com/en-us/library/1kt27hek.aspx
SHIM_CALL _vsnwprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) {
uint32_t buffer_ptr = SHIM_GET_ARG_32(0);
int32_t buffer_count = SHIM_GET_ARG_32(1);
uint32_t format_ptr = SHIM_GET_ARG_32(2);
uint32_t arg_ptr = SHIM_GET_ARG_32(3);
XELOGD("_vsnwprintf(%08X, %i, %08X, %08X)", buffer_ptr, buffer_count,
format_ptr, arg_ptr);
if (buffer_ptr == 0 || buffer_count <= 0 || format_ptr == 0) {
SHIM_SET_RETURN_32(-1);
return;
}
auto buffer = (uint16_t*)SHIM_MEM_ADDR(buffer_ptr);
auto format = (const uint16_t*)SHIM_MEM_ADDR(format_ptr);
ArrayArgList args(ppc_context, arg_ptr);
WideStringFormatData data(format);
int32_t count = format_core(ppc_context, data, args, true);
if (count < 0) {
// Error.
if (buffer_count > 0) {
buffer[0] = '\0'; // write a null, just to be safe
}
} else if (count <= buffer_count) {
// Fit within the buffer.
xe::copy_and_swap(buffer, (uint16_t*)data.wstr().c_str(), count);
if (count < buffer_count) {
buffer[count] = '\0';
}
} else {
// Overflowed buffer. We still return the count we would have written.
xe::copy_and_swap(buffer, (uint16_t*)data.wstr().c_str(), buffer_count);
}
SHIM_SET_RETURN_32(count);
}
// https://msdn.microsoft.com/en-us/library/28d5ce15.aspx
SHIM_CALL vsprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) {
uint32_t buffer_ptr = SHIM_GET_ARG_32(0);
@@ -1100,6 +1140,7 @@ void RegisterStringExports(xe::cpu::ExportResolver* export_resolver,
SHIM_SET_MAPPING("xboxkrnl.exe", vsprintf, state);
SHIM_SET_MAPPING("xboxkrnl.exe", _vscwprintf, state);
SHIM_SET_MAPPING("xboxkrnl.exe", vswprintf, state);
SHIM_SET_MAPPING("xboxkrnl.exe", _vsnwprintf, state);
}
} // namespace xboxkrnl