From 0093f8180d8c188440e3f00e76f2a36c604d0fc9 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Sat, 17 Nov 2018 16:12:34 -0600 Subject: [PATCH] [x64] Factor out memory handling code --- src/xenia/cpu/backend/x64/x64_seq_memory.cc | 1053 +++++++++++++++++++ src/xenia/cpu/backend/x64/x64_sequences.cc | 1023 +----------------- src/xenia/cpu/backend/x64/x64_sequences.h | 1 + 3 files changed, 1055 insertions(+), 1022 deletions(-) create mode 100644 src/xenia/cpu/backend/x64/x64_seq_memory.cc diff --git a/src/xenia/cpu/backend/x64/x64_seq_memory.cc b/src/xenia/cpu/backend/x64/x64_seq_memory.cc new file mode 100644 index 000000000..ba647e045 --- /dev/null +++ b/src/xenia/cpu/backend/x64/x64_seq_memory.cc @@ -0,0 +1,1053 @@ +/** + ****************************************************************************** + * 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 +#include + +#include "xenia/cpu/backend/x64/x64_op.h" +#include "xenia/cpu/backend/x64/x64_tracers.h" + +namespace xe { +namespace cpu { +namespace backend { +namespace x64 { + +void RegisterMemory() {} + +// Note: all types are always aligned in the context. +RegExp ComputeContextAddress(X64Emitter& e, const OffsetOp& offset) { + return e.GetContextReg() + offset.value; +} + +template +RegExp ComputeMemoryAddressOffset(X64Emitter& e, const T& guest, + const T& offset) { + assert_true(offset.is_constant); + int32_t offset_const = static_cast(offset.constant()); + + if (guest.is_constant) { + uint32_t address = static_cast(guest.constant()); + address += offset_const; + if (address < 0x80000000) { + return e.GetMembaseReg() + address; + } else { + e.mov(e.eax, address); + return e.GetMembaseReg() + e.rax; + } + } else { + // Clear the top 32 bits, as they are likely garbage. + // TODO(benvanik): find a way to avoid doing this. + e.mov(e.eax, guest.reg().cvt32()); + return e.GetMembaseReg() + e.rax + offset_const; + } +} + +// Note: most *should* be aligned, but needs to be checked! +template +RegExp ComputeMemoryAddress(X64Emitter& e, const T& guest) { + if (guest.is_constant) { + // TODO(benvanik): figure out how to do this without a temp. + // Since the constant is often 0x8... if we tried to use that as a + // displacement it would be sign extended and mess things up. + uint32_t address = static_cast(guest.constant()); + if (address < 0x80000000) { + return e.GetMembaseReg() + address; + } else { + e.mov(e.eax, address); + return e.GetMembaseReg() + e.rax; + } + } else { + // Clear the top 32 bits, as they are likely garbage. + // TODO(benvanik): find a way to avoid doing this. + e.mov(e.eax, guest.reg().cvt32()); + return e.GetMembaseReg() + e.rax; + } +} + +// ============================================================================ +// OPCODE_ATOMIC_EXCHANGE +// ============================================================================ +// Note that the address we use here is a real, host address! +// This is weird, and should be fixed. +template +void EmitAtomicExchangeXX(X64Emitter& e, const ARGS& i) { + if (i.dest == i.src1) { + e.mov(e.rax, i.src1); + if (i.dest != i.src2) { + if (i.src2.is_constant) { + e.mov(i.dest, i.src2.constant()); + } else { + e.mov(i.dest, i.src2); + } + } + e.lock(); + e.xchg(e.dword[e.rax], i.dest); + } else { + if (i.dest != i.src2) { + if (i.src2.is_constant) { + e.mov(i.dest, i.src2.constant()); + } else { + e.mov(i.dest, i.src2); + } + } + e.lock(); + e.xchg(e.dword[i.src1.reg()], i.dest); + } +} +struct ATOMIC_EXCHANGE_I8 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + EmitAtomicExchangeXX(e, i); + } +}; +struct ATOMIC_EXCHANGE_I16 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + EmitAtomicExchangeXX(e, i); + } +}; +struct ATOMIC_EXCHANGE_I32 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + EmitAtomicExchangeXX(e, i); + } +}; +struct ATOMIC_EXCHANGE_I64 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + EmitAtomicExchangeXX(e, i); + } +}; +EMITTER_OPCODE_TABLE(OPCODE_ATOMIC_EXCHANGE, ATOMIC_EXCHANGE_I8, + ATOMIC_EXCHANGE_I16, ATOMIC_EXCHANGE_I32, + ATOMIC_EXCHANGE_I64); + +// ============================================================================ +// OPCODE_ATOMIC_COMPARE_EXCHANGE +// ============================================================================ +struct ATOMIC_COMPARE_EXCHANGE_I32 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + e.mov(e.eax, i.src2); + e.mov(e.ecx, i.src1.reg().cvt32()); + e.lock(); + e.cmpxchg(e.dword[e.GetMembaseReg() + e.rcx], i.src3); + e.sete(i.dest); + } +}; +struct ATOMIC_COMPARE_EXCHANGE_I64 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + e.mov(e.rax, i.src2); + e.mov(e.ecx, i.src1.reg().cvt32()); + e.lock(); + e.cmpxchg(e.qword[e.GetMembaseReg() + e.rcx], i.src3); + e.sete(i.dest); + } +}; +EMITTER_OPCODE_TABLE(OPCODE_ATOMIC_COMPARE_EXCHANGE, + ATOMIC_COMPARE_EXCHANGE_I32, ATOMIC_COMPARE_EXCHANGE_I64); + +// ============================================================================ +// OPCODE_LOAD_LOCAL +// ============================================================================ +// Note: all types are always aligned on the stack. +struct LOAD_LOCAL_I8 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + e.mov(i.dest, e.byte[e.rsp + i.src1.constant()]); + // e.TraceLoadI8(DATA_LOCAL, i.src1.constant, i.dest); + } +}; +struct LOAD_LOCAL_I16 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + e.mov(i.dest, e.word[e.rsp + i.src1.constant()]); + // e.TraceLoadI16(DATA_LOCAL, i.src1.constant, i.dest); + } +}; +struct LOAD_LOCAL_I32 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + e.mov(i.dest, e.dword[e.rsp + i.src1.constant()]); + // e.TraceLoadI32(DATA_LOCAL, i.src1.constant, i.dest); + } +}; +struct LOAD_LOCAL_I64 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + e.mov(i.dest, e.qword[e.rsp + i.src1.constant()]); + // e.TraceLoadI64(DATA_LOCAL, i.src1.constant, i.dest); + } +}; +struct LOAD_LOCAL_F32 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + e.vmovss(i.dest, e.dword[e.rsp + i.src1.constant()]); + // e.TraceLoadF32(DATA_LOCAL, i.src1.constant, i.dest); + } +}; +struct LOAD_LOCAL_F64 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + e.vmovsd(i.dest, e.qword[e.rsp + i.src1.constant()]); + // e.TraceLoadF64(DATA_LOCAL, i.src1.constant, i.dest); + } +}; +struct LOAD_LOCAL_V128 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + e.vmovaps(i.dest, e.ptr[e.rsp + i.src1.constant()]); + // e.TraceLoadV128(DATA_LOCAL, i.src1.constant, i.dest); + } +}; +EMITTER_OPCODE_TABLE(OPCODE_LOAD_LOCAL, LOAD_LOCAL_I8, LOAD_LOCAL_I16, + LOAD_LOCAL_I32, LOAD_LOCAL_I64, LOAD_LOCAL_F32, + LOAD_LOCAL_F64, LOAD_LOCAL_V128); + +// ============================================================================ +// OPCODE_STORE_LOCAL +// ============================================================================ +// Note: all types are always aligned on the stack. +struct STORE_LOCAL_I8 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + // e.TraceStoreI8(DATA_LOCAL, i.src1.constant, i.src2); + e.mov(e.byte[e.rsp + i.src1.constant()], i.src2); + } +}; +struct STORE_LOCAL_I16 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + // e.TraceStoreI16(DATA_LOCAL, i.src1.constant, i.src2); + e.mov(e.word[e.rsp + i.src1.constant()], i.src2); + } +}; +struct STORE_LOCAL_I32 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + // e.TraceStoreI32(DATA_LOCAL, i.src1.constant, i.src2); + e.mov(e.dword[e.rsp + i.src1.constant()], i.src2); + } +}; +struct STORE_LOCAL_I64 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + // e.TraceStoreI64(DATA_LOCAL, i.src1.constant, i.src2); + e.mov(e.qword[e.rsp + i.src1.constant()], i.src2); + } +}; +struct STORE_LOCAL_F32 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + // e.TraceStoreF32(DATA_LOCAL, i.src1.constant, i.src2); + e.vmovss(e.dword[e.rsp + i.src1.constant()], i.src2); + } +}; +struct STORE_LOCAL_F64 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + // e.TraceStoreF64(DATA_LOCAL, i.src1.constant, i.src2); + e.vmovsd(e.qword[e.rsp + i.src1.constant()], i.src2); + } +}; +struct STORE_LOCAL_V128 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + // e.TraceStoreV128(DATA_LOCAL, i.src1.constant, i.src2); + e.vmovaps(e.ptr[e.rsp + i.src1.constant()], i.src2); + } +}; +EMITTER_OPCODE_TABLE(OPCODE_STORE_LOCAL, STORE_LOCAL_I8, STORE_LOCAL_I16, + STORE_LOCAL_I32, STORE_LOCAL_I64, STORE_LOCAL_F32, + STORE_LOCAL_F64, STORE_LOCAL_V128); + +// ============================================================================ +// OPCODE_LOAD_CONTEXT +// ============================================================================ +struct LOAD_CONTEXT_I8 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeContextAddress(e, i.src1); + e.mov(i.dest, e.byte[addr]); + if (IsTracingData()) { + e.mov(e.r8, e.byte[addr]); + e.mov(e.rdx, i.src1.value); + e.CallNative(reinterpret_cast(TraceContextLoadI8)); + } + } +}; +struct LOAD_CONTEXT_I16 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeContextAddress(e, i.src1); + e.mov(i.dest, e.word[addr]); + if (IsTracingData()) { + e.mov(e.r8, e.word[addr]); + e.mov(e.rdx, i.src1.value); + e.CallNative(reinterpret_cast(TraceContextLoadI16)); + } + } +}; +struct LOAD_CONTEXT_I32 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeContextAddress(e, i.src1); + e.mov(i.dest, e.dword[addr]); + if (IsTracingData()) { + e.mov(e.r8, e.dword[addr]); + e.mov(e.rdx, i.src1.value); + e.CallNative(reinterpret_cast(TraceContextLoadI32)); + } + } +}; +struct LOAD_CONTEXT_I64 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeContextAddress(e, i.src1); + e.mov(i.dest, e.qword[addr]); + if (IsTracingData()) { + e.mov(e.r8, e.qword[addr]); + e.mov(e.rdx, i.src1.value); + e.CallNative(reinterpret_cast(TraceContextLoadI64)); + } + } +}; +struct LOAD_CONTEXT_F32 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeContextAddress(e, i.src1); + e.vmovss(i.dest, e.dword[addr]); + if (IsTracingData()) { + e.lea(e.r8, e.dword[addr]); + e.mov(e.rdx, i.src1.value); + e.CallNative(reinterpret_cast(TraceContextLoadF32)); + } + } +}; +struct LOAD_CONTEXT_F64 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeContextAddress(e, i.src1); + e.vmovsd(i.dest, e.qword[addr]); + if (IsTracingData()) { + e.lea(e.r8, e.qword[addr]); + e.mov(e.rdx, i.src1.value); + e.CallNative(reinterpret_cast(TraceContextLoadF64)); + } + } +}; +struct LOAD_CONTEXT_V128 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeContextAddress(e, i.src1); + e.vmovaps(i.dest, e.ptr[addr]); + if (IsTracingData()) { + e.lea(e.r8, e.ptr[addr]); + e.mov(e.rdx, i.src1.value); + e.CallNative(reinterpret_cast(TraceContextLoadV128)); + } + } +}; +EMITTER_OPCODE_TABLE(OPCODE_LOAD_CONTEXT, LOAD_CONTEXT_I8, LOAD_CONTEXT_I16, + LOAD_CONTEXT_I32, LOAD_CONTEXT_I64, LOAD_CONTEXT_F32, + LOAD_CONTEXT_F64, LOAD_CONTEXT_V128); + +// ============================================================================ +// OPCODE_STORE_CONTEXT +// ============================================================================ +// Note: all types are always aligned on the stack. +struct STORE_CONTEXT_I8 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeContextAddress(e, i.src1); + if (i.src2.is_constant) { + e.mov(e.byte[addr], i.src2.constant()); + } else { + e.mov(e.byte[addr], i.src2); + } + if (IsTracingData()) { + e.mov(e.r8, e.byte[addr]); + e.mov(e.rdx, i.src1.value); + e.CallNative(reinterpret_cast(TraceContextStoreI8)); + } + } +}; +struct STORE_CONTEXT_I16 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeContextAddress(e, i.src1); + if (i.src2.is_constant) { + e.mov(e.word[addr], i.src2.constant()); + } else { + e.mov(e.word[addr], i.src2); + } + if (IsTracingData()) { + e.mov(e.r8, e.word[addr]); + e.mov(e.rdx, i.src1.value); + e.CallNative(reinterpret_cast(TraceContextStoreI16)); + } + } +}; +struct STORE_CONTEXT_I32 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeContextAddress(e, i.src1); + if (i.src2.is_constant) { + e.mov(e.dword[addr], i.src2.constant()); + } else { + e.mov(e.dword[addr], i.src2); + } + if (IsTracingData()) { + e.mov(e.r8, e.dword[addr]); + e.mov(e.rdx, i.src1.value); + e.CallNative(reinterpret_cast(TraceContextStoreI32)); + } + } +}; +struct STORE_CONTEXT_I64 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeContextAddress(e, i.src1); + if (i.src2.is_constant) { + e.MovMem64(addr, i.src2.constant()); + } else { + e.mov(e.qword[addr], i.src2); + } + if (IsTracingData()) { + e.mov(e.r8, e.qword[addr]); + e.mov(e.rdx, i.src1.value); + e.CallNative(reinterpret_cast(TraceContextStoreI64)); + } + } +}; +struct STORE_CONTEXT_F32 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeContextAddress(e, i.src1); + if (i.src2.is_constant) { + e.mov(e.dword[addr], i.src2.value->constant.i32); + } else { + e.vmovss(e.dword[addr], i.src2); + } + if (IsTracingData()) { + e.lea(e.r8, e.dword[addr]); + e.mov(e.rdx, i.src1.value); + e.CallNative(reinterpret_cast(TraceContextStoreF32)); + } + } +}; +struct STORE_CONTEXT_F64 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeContextAddress(e, i.src1); + if (i.src2.is_constant) { + e.MovMem64(addr, i.src2.value->constant.i64); + } else { + e.vmovsd(e.qword[addr], i.src2); + } + if (IsTracingData()) { + e.lea(e.r8, e.qword[addr]); + e.mov(e.rdx, i.src1.value); + e.CallNative(reinterpret_cast(TraceContextStoreF64)); + } + } +}; +struct STORE_CONTEXT_V128 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeContextAddress(e, i.src1); + if (i.src2.is_constant) { + e.LoadConstantXmm(e.xmm0, i.src2.constant()); + e.vmovaps(e.ptr[addr], e.xmm0); + } else { + e.vmovaps(e.ptr[addr], i.src2); + } + if (IsTracingData()) { + e.lea(e.r8, e.ptr[addr]); + e.mov(e.rdx, i.src1.value); + e.CallNative(reinterpret_cast(TraceContextStoreV128)); + } + } +}; +EMITTER_OPCODE_TABLE(OPCODE_STORE_CONTEXT, STORE_CONTEXT_I8, STORE_CONTEXT_I16, + STORE_CONTEXT_I32, STORE_CONTEXT_I64, STORE_CONTEXT_F32, + STORE_CONTEXT_F64, STORE_CONTEXT_V128); + +// ============================================================================ +// OPCODE_LOAD_MMIO +// ============================================================================ +// Note: all types are always aligned in the context. +struct LOAD_MMIO_I32 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + // uint64_t (context, addr) + auto mmio_range = reinterpret_cast(i.src1.value); + auto read_address = uint32_t(i.src2.value); + e.mov(e.r8, uint64_t(mmio_range->callback_context)); + e.mov(e.r9d, read_address); + e.CallNativeSafe(reinterpret_cast(mmio_range->read)); + e.bswap(e.eax); + e.mov(i.dest, e.eax); + if (IsTracingData()) { + e.mov(e.r8, i.dest); + e.mov(e.edx, read_address); + e.CallNative(reinterpret_cast(TraceContextLoadI32)); + } + } +}; +EMITTER_OPCODE_TABLE(OPCODE_LOAD_MMIO, LOAD_MMIO_I32); + +// ============================================================================ +// OPCODE_STORE_MMIO +// ============================================================================ +// Note: all types are always aligned on the stack. +struct STORE_MMIO_I32 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + // void (context, addr, value) + auto mmio_range = reinterpret_cast(i.src1.value); + auto write_address = uint32_t(i.src2.value); + e.mov(e.r8, uint64_t(mmio_range->callback_context)); + e.mov(e.r9d, write_address); + if (i.src3.is_constant) { + e.mov(e.r10d, xe::byte_swap(i.src3.constant())); + } else { + e.mov(e.r10d, i.src3); + e.bswap(e.r10d); + } + e.CallNativeSafe(reinterpret_cast(mmio_range->write)); + if (IsTracingData()) { + if (i.src3.is_constant) { + e.mov(e.r8d, i.src3.constant()); + } else { + e.mov(e.r8d, i.src3); + } + e.mov(e.edx, write_address); + e.CallNative(reinterpret_cast(TraceContextStoreI32)); + } + } +}; +EMITTER_OPCODE_TABLE(OPCODE_STORE_MMIO, STORE_MMIO_I32); + +// ============================================================================ +// OPCODE_LOAD_OFFSET +// ============================================================================ +struct LOAD_OFFSET_I8 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeMemoryAddressOffset(e, i.src1, i.src2); + e.mov(i.dest, e.byte[addr]); + } +}; + +struct LOAD_OFFSET_I16 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeMemoryAddressOffset(e, i.src1, i.src2); + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + if (e.IsFeatureEnabled(kX64EmitMovbe)) { + e.movbe(i.dest, e.word[addr]); + } else { + e.mov(i.dest, e.word[addr]); + e.ror(i.dest, 8); + } + } else { + e.mov(i.dest, e.word[addr]); + } + } +}; + +struct LOAD_OFFSET_I32 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeMemoryAddressOffset(e, i.src1, i.src2); + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + if (e.IsFeatureEnabled(kX64EmitMovbe)) { + e.movbe(i.dest, e.dword[addr]); + } else { + e.mov(i.dest, e.dword[addr]); + e.bswap(i.dest); + } + } else { + e.mov(i.dest, e.dword[addr]); + } + } +}; + +struct LOAD_OFFSET_I64 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeMemoryAddressOffset(e, i.src1, i.src2); + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + if (e.IsFeatureEnabled(kX64EmitMovbe)) { + e.movbe(i.dest, e.qword[addr]); + } else { + e.mov(i.dest, e.qword[addr]); + e.bswap(i.dest); + } + } else { + e.mov(i.dest, e.qword[addr]); + } + } +}; +EMITTER_OPCODE_TABLE(OPCODE_LOAD_OFFSET, LOAD_OFFSET_I8, LOAD_OFFSET_I16, + LOAD_OFFSET_I32, LOAD_OFFSET_I64); + +// ============================================================================ +// OPCODE_STORE_OFFSET +// ============================================================================ +struct STORE_OFFSET_I8 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeMemoryAddressOffset(e, i.src1, i.src2); + if (i.src3.is_constant) { + e.mov(e.byte[addr], i.src3.constant()); + } else { + e.mov(e.byte[addr], i.src3); + } + } +}; + +struct STORE_OFFSET_I16 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeMemoryAddressOffset(e, i.src1, i.src2); + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + assert_false(i.src3.is_constant); + if (e.IsFeatureEnabled(kX64EmitMovbe)) { + e.movbe(e.word[addr], i.src3); + } else { + assert_always("not implemented"); + } + } else { + if (i.src3.is_constant) { + e.mov(e.word[addr], i.src3.constant()); + } else { + e.mov(e.word[addr], i.src3); + } + } + } +}; + +struct STORE_OFFSET_I32 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeMemoryAddressOffset(e, i.src1, i.src2); + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + assert_false(i.src3.is_constant); + if (e.IsFeatureEnabled(kX64EmitMovbe)) { + e.movbe(e.dword[addr], i.src3); + } else { + assert_always("not implemented"); + } + } else { + if (i.src3.is_constant) { + e.mov(e.dword[addr], i.src3.constant()); + } else { + e.mov(e.dword[addr], i.src3); + } + } + } +}; + +struct STORE_OFFSET_I64 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeMemoryAddressOffset(e, i.src1, i.src2); + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + assert_false(i.src3.is_constant); + if (e.IsFeatureEnabled(kX64EmitMovbe)) { + e.movbe(e.qword[addr], i.src3); + } else { + assert_always("not implemented"); + } + } else { + if (i.src3.is_constant) { + e.MovMem64(addr, i.src3.constant()); + } else { + e.mov(e.qword[addr], i.src3); + } + } + } +}; +EMITTER_OPCODE_TABLE(OPCODE_STORE_OFFSET, STORE_OFFSET_I8, STORE_OFFSET_I16, + STORE_OFFSET_I32, STORE_OFFSET_I64); + +// ============================================================================ +// OPCODE_LOAD +// ============================================================================ +struct LOAD_I8 : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeMemoryAddress(e, i.src1); + e.mov(i.dest, e.byte[addr]); + if (IsTracingData()) { + e.mov(e.r8b, i.dest); + e.lea(e.rdx, e.ptr[addr]); + e.CallNative(reinterpret_cast(TraceMemoryLoadI8)); + } + } +}; +struct LOAD_I16 : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeMemoryAddress(e, i.src1); + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + if (e.IsFeatureEnabled(kX64EmitMovbe)) { + e.movbe(i.dest, e.word[addr]); + } else { + e.mov(i.dest, e.word[addr]); + e.ror(i.dest, 8); + } + } else { + e.mov(i.dest, e.word[addr]); + } + if (IsTracingData()) { + e.mov(e.r8w, i.dest); + e.lea(e.rdx, e.ptr[addr]); + e.CallNative(reinterpret_cast(TraceMemoryLoadI16)); + } + } +}; +struct LOAD_I32 : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeMemoryAddress(e, i.src1); + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + if (e.IsFeatureEnabled(kX64EmitMovbe)) { + e.movbe(i.dest, e.dword[addr]); + } else { + e.mov(i.dest, e.dword[addr]); + e.bswap(i.dest); + } + } else { + e.mov(i.dest, e.dword[addr]); + } + if (IsTracingData()) { + e.mov(e.r8d, i.dest); + e.lea(e.rdx, e.ptr[addr]); + e.CallNative(reinterpret_cast(TraceMemoryLoadI32)); + } + } +}; +struct LOAD_I64 : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeMemoryAddress(e, i.src1); + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + if (e.IsFeatureEnabled(kX64EmitMovbe)) { + e.movbe(i.dest, e.qword[addr]); + } else { + e.mov(i.dest, e.qword[addr]); + e.bswap(i.dest); + } + } else { + e.mov(i.dest, e.qword[addr]); + } + if (IsTracingData()) { + e.mov(e.r8, i.dest); + e.lea(e.rdx, e.ptr[addr]); + e.CallNative(reinterpret_cast(TraceMemoryLoadI64)); + } + } +}; +struct LOAD_F32 : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeMemoryAddress(e, i.src1); + e.vmovss(i.dest, e.dword[addr]); + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + assert_always("not implemented yet"); + } + if (IsTracingData()) { + e.lea(e.r8, e.dword[addr]); + e.lea(e.rdx, e.ptr[addr]); + e.CallNative(reinterpret_cast(TraceMemoryLoadF32)); + } + } +}; +struct LOAD_F64 : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeMemoryAddress(e, i.src1); + e.vmovsd(i.dest, e.qword[addr]); + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + assert_always("not implemented yet"); + } + if (IsTracingData()) { + e.lea(e.r8, e.qword[addr]); + e.lea(e.rdx, e.ptr[addr]); + e.CallNative(reinterpret_cast(TraceMemoryLoadF64)); + } + } +}; +struct LOAD_V128 : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeMemoryAddress(e, i.src1); + // TODO(benvanik): we should try to stick to movaps if possible. + e.vmovups(i.dest, e.ptr[addr]); + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + // TODO(benvanik): find a way to do this without the memory load. + e.vpshufb(i.dest, i.dest, e.GetXmmConstPtr(XMMByteSwapMask)); + } + if (IsTracingData()) { + e.lea(e.r8, e.ptr[addr]); + e.lea(e.rdx, e.ptr[addr]); + e.CallNative(reinterpret_cast(TraceMemoryLoadV128)); + } + } +}; +EMITTER_OPCODE_TABLE(OPCODE_LOAD, LOAD_I8, LOAD_I16, LOAD_I32, LOAD_I64, + LOAD_F32, LOAD_F64, LOAD_V128); + +// ============================================================================ +// OPCODE_STORE +// ============================================================================ +// Note: most *should* be aligned, but needs to be checked! +struct STORE_I8 : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeMemoryAddress(e, i.src1); + if (i.src2.is_constant) { + e.mov(e.byte[addr], i.src2.constant()); + } else { + e.mov(e.byte[addr], i.src2); + } + if (IsTracingData()) { + addr = ComputeMemoryAddress(e, i.src1); + e.mov(e.r8b, e.byte[addr]); + e.lea(e.rdx, e.ptr[addr]); + e.CallNative(reinterpret_cast(TraceMemoryStoreI8)); + } + } +}; +struct STORE_I16 : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeMemoryAddress(e, i.src1); + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + assert_false(i.src2.is_constant); + if (e.IsFeatureEnabled(kX64EmitMovbe)) { + e.movbe(e.word[addr], i.src2); + } else { + assert_always("not implemented"); + } + } else { + if (i.src2.is_constant) { + e.mov(e.word[addr], i.src2.constant()); + } else { + e.mov(e.word[addr], i.src2); + } + } + if (IsTracingData()) { + addr = ComputeMemoryAddress(e, i.src1); + e.mov(e.r8w, e.word[addr]); + e.lea(e.rdx, e.ptr[addr]); + e.CallNative(reinterpret_cast(TraceMemoryStoreI16)); + } + } +}; +struct STORE_I32 : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeMemoryAddress(e, i.src1); + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + assert_false(i.src2.is_constant); + if (e.IsFeatureEnabled(kX64EmitMovbe)) { + e.movbe(e.dword[addr], i.src2); + } else { + assert_always("not implemented"); + } + } else { + if (i.src2.is_constant) { + e.mov(e.dword[addr], i.src2.constant()); + } else { + e.mov(e.dword[addr], i.src2); + } + } + if (IsTracingData()) { + addr = ComputeMemoryAddress(e, i.src1); + e.mov(e.r8d, e.dword[addr]); + e.lea(e.rdx, e.ptr[addr]); + e.CallNative(reinterpret_cast(TraceMemoryStoreI32)); + } + } +}; +struct STORE_I64 : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeMemoryAddress(e, i.src1); + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + assert_false(i.src2.is_constant); + if (e.IsFeatureEnabled(kX64EmitMovbe)) { + e.movbe(e.qword[addr], i.src2); + } else { + assert_always("not implemented"); + } + } else { + if (i.src2.is_constant) { + e.MovMem64(addr, i.src2.constant()); + } else { + e.mov(e.qword[addr], i.src2); + } + } + if (IsTracingData()) { + addr = ComputeMemoryAddress(e, i.src1); + e.mov(e.r8, e.qword[addr]); + e.lea(e.rdx, e.ptr[addr]); + e.CallNative(reinterpret_cast(TraceMemoryStoreI64)); + } + } +}; +struct STORE_F32 : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeMemoryAddress(e, i.src1); + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + assert_false(i.src2.is_constant); + assert_always("not yet implemented"); + } else { + if (i.src2.is_constant) { + e.mov(e.dword[addr], i.src2.value->constant.i32); + } else { + e.vmovss(e.dword[addr], i.src2); + } + } + if (IsTracingData()) { + addr = ComputeMemoryAddress(e, i.src1); + e.lea(e.r8, e.ptr[addr]); + e.lea(e.rdx, e.ptr[addr]); + e.CallNative(reinterpret_cast(TraceMemoryStoreF32)); + } + } +}; +struct STORE_F64 : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeMemoryAddress(e, i.src1); + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + assert_false(i.src2.is_constant); + assert_always("not yet implemented"); + } else { + if (i.src2.is_constant) { + e.MovMem64(addr, i.src2.value->constant.i64); + } else { + e.vmovsd(e.qword[addr], i.src2); + } + } + if (IsTracingData()) { + addr = ComputeMemoryAddress(e, i.src1); + e.lea(e.r8, e.ptr[addr]); + e.lea(e.rdx, e.ptr[addr]); + e.CallNative(reinterpret_cast(TraceMemoryStoreF64)); + } + } +}; +struct STORE_V128 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + auto addr = ComputeMemoryAddress(e, i.src1); + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + assert_false(i.src2.is_constant); + e.vpshufb(e.xmm0, i.src2, e.GetXmmConstPtr(XMMByteSwapMask)); + e.vmovaps(e.ptr[addr], e.xmm0); + } else { + if (i.src2.is_constant) { + e.LoadConstantXmm(e.xmm0, i.src2.constant()); + e.vmovaps(e.ptr[addr], e.xmm0); + } else { + e.vmovaps(e.ptr[addr], i.src2); + } + } + if (IsTracingData()) { + addr = ComputeMemoryAddress(e, i.src1); + e.lea(e.r8, e.ptr[addr]); + e.lea(e.rdx, e.ptr[addr]); + e.CallNative(reinterpret_cast(TraceMemoryStoreV128)); + } + } +}; +EMITTER_OPCODE_TABLE(OPCODE_STORE, STORE_I8, STORE_I16, STORE_I32, STORE_I64, + STORE_F32, STORE_F64, STORE_V128); + +// ============================================================================ +// OPCODE_PREFETCH +// ============================================================================ +struct PREFETCH + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + // TODO(benvanik): prefetch addr -> length. + } +}; +EMITTER_OPCODE_TABLE(OPCODE_PREFETCH, PREFETCH); + +// ============================================================================ +// OPCODE_MEMORY_BARRIER +// ============================================================================ +struct MEMORY_BARRIER + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { e.mfence(); } +}; +EMITTER_OPCODE_TABLE(OPCODE_MEMORY_BARRIER, MEMORY_BARRIER); + +// ============================================================================ +// OPCODE_MEMSET +// ============================================================================ +struct MEMSET_I64_I8_I64 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + assert_true(i.src2.is_constant); + assert_true(i.src3.is_constant); + assert_true(i.src2.constant() == 0); + e.vpxor(e.xmm0, e.xmm0); + auto addr = ComputeMemoryAddress(e, i.src1); + switch (i.src3.constant()) { + case 32: + e.vmovaps(e.ptr[addr + 0 * 16], e.xmm0); + e.vmovaps(e.ptr[addr + 1 * 16], e.xmm0); + break; + case 128: + e.vmovaps(e.ptr[addr + 0 * 16], e.xmm0); + e.vmovaps(e.ptr[addr + 1 * 16], e.xmm0); + e.vmovaps(e.ptr[addr + 2 * 16], e.xmm0); + e.vmovaps(e.ptr[addr + 3 * 16], e.xmm0); + e.vmovaps(e.ptr[addr + 4 * 16], e.xmm0); + e.vmovaps(e.ptr[addr + 5 * 16], e.xmm0); + e.vmovaps(e.ptr[addr + 6 * 16], e.xmm0); + e.vmovaps(e.ptr[addr + 7 * 16], e.xmm0); + break; + default: + assert_unhandled_case(i.src3.constant()); + break; + } + if (IsTracingData()) { + addr = ComputeMemoryAddress(e, i.src1); + e.mov(e.r9, i.src3.constant()); + e.mov(e.r8, i.src2.constant()); + e.lea(e.rdx, e.ptr[addr]); + e.CallNative(reinterpret_cast(TraceMemset)); + } + } +}; +EMITTER_OPCODE_TABLE(OPCODE_MEMSET, MEMSET_I64_I8_I64); + +} // namespace x64 +} // namespace backend +} // namespace cpu +} // namespace xe diff --git a/src/xenia/cpu/backend/x64/x64_sequences.cc b/src/xenia/cpu/backend/x64/x64_sequences.cc index d33bf3781..da6ff8891 100644 --- a/src/xenia/cpu/backend/x64/x64_sequences.cc +++ b/src/xenia/cpu/backend/x64/x64_sequences.cc @@ -450,343 +450,6 @@ struct LOAD_CLOCK : Sequence> { }; EMITTER_OPCODE_TABLE(OPCODE_LOAD_CLOCK, LOAD_CLOCK); -// ============================================================================ -// OPCODE_LOAD_LOCAL -// ============================================================================ -// Note: all types are always aligned on the stack. -struct LOAD_LOCAL_I8 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - e.mov(i.dest, e.byte[e.rsp + i.src1.constant()]); - // e.TraceLoadI8(DATA_LOCAL, i.src1.constant, i.dest); - } -}; -struct LOAD_LOCAL_I16 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - e.mov(i.dest, e.word[e.rsp + i.src1.constant()]); - // e.TraceLoadI16(DATA_LOCAL, i.src1.constant, i.dest); - } -}; -struct LOAD_LOCAL_I32 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - e.mov(i.dest, e.dword[e.rsp + i.src1.constant()]); - // e.TraceLoadI32(DATA_LOCAL, i.src1.constant, i.dest); - } -}; -struct LOAD_LOCAL_I64 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - e.mov(i.dest, e.qword[e.rsp + i.src1.constant()]); - // e.TraceLoadI64(DATA_LOCAL, i.src1.constant, i.dest); - } -}; -struct LOAD_LOCAL_F32 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - e.vmovss(i.dest, e.dword[e.rsp + i.src1.constant()]); - // e.TraceLoadF32(DATA_LOCAL, i.src1.constant, i.dest); - } -}; -struct LOAD_LOCAL_F64 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - e.vmovsd(i.dest, e.qword[e.rsp + i.src1.constant()]); - // e.TraceLoadF64(DATA_LOCAL, i.src1.constant, i.dest); - } -}; -struct LOAD_LOCAL_V128 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - e.vmovaps(i.dest, e.ptr[e.rsp + i.src1.constant()]); - // e.TraceLoadV128(DATA_LOCAL, i.src1.constant, i.dest); - } -}; -EMITTER_OPCODE_TABLE(OPCODE_LOAD_LOCAL, LOAD_LOCAL_I8, LOAD_LOCAL_I16, - LOAD_LOCAL_I32, LOAD_LOCAL_I64, LOAD_LOCAL_F32, - LOAD_LOCAL_F64, LOAD_LOCAL_V128); - -// ============================================================================ -// OPCODE_STORE_LOCAL -// ============================================================================ -// Note: all types are always aligned on the stack. -struct STORE_LOCAL_I8 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - // e.TraceStoreI8(DATA_LOCAL, i.src1.constant, i.src2); - e.mov(e.byte[e.rsp + i.src1.constant()], i.src2); - } -}; -struct STORE_LOCAL_I16 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - // e.TraceStoreI16(DATA_LOCAL, i.src1.constant, i.src2); - e.mov(e.word[e.rsp + i.src1.constant()], i.src2); - } -}; -struct STORE_LOCAL_I32 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - // e.TraceStoreI32(DATA_LOCAL, i.src1.constant, i.src2); - e.mov(e.dword[e.rsp + i.src1.constant()], i.src2); - } -}; -struct STORE_LOCAL_I64 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - // e.TraceStoreI64(DATA_LOCAL, i.src1.constant, i.src2); - e.mov(e.qword[e.rsp + i.src1.constant()], i.src2); - } -}; -struct STORE_LOCAL_F32 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - // e.TraceStoreF32(DATA_LOCAL, i.src1.constant, i.src2); - e.vmovss(e.dword[e.rsp + i.src1.constant()], i.src2); - } -}; -struct STORE_LOCAL_F64 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - // e.TraceStoreF64(DATA_LOCAL, i.src1.constant, i.src2); - e.vmovsd(e.qword[e.rsp + i.src1.constant()], i.src2); - } -}; -struct STORE_LOCAL_V128 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - // e.TraceStoreV128(DATA_LOCAL, i.src1.constant, i.src2); - e.vmovaps(e.ptr[e.rsp + i.src1.constant()], i.src2); - } -}; -EMITTER_OPCODE_TABLE(OPCODE_STORE_LOCAL, STORE_LOCAL_I8, STORE_LOCAL_I16, - STORE_LOCAL_I32, STORE_LOCAL_I64, STORE_LOCAL_F32, - STORE_LOCAL_F64, STORE_LOCAL_V128); - -// ============================================================================ -// OPCODE_LOAD_CONTEXT -// ============================================================================ -// Note: all types are always aligned in the context. -RegExp ComputeContextAddress(X64Emitter& e, const OffsetOp& offset) { - return e.GetContextReg() + offset.value; -} -struct LOAD_CONTEXT_I8 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeContextAddress(e, i.src1); - e.mov(i.dest, e.byte[addr]); - if (IsTracingData()) { - e.mov(e.r8, e.byte[addr]); - e.mov(e.rdx, i.src1.value); - e.CallNative(reinterpret_cast(TraceContextLoadI8)); - } - } -}; -struct LOAD_CONTEXT_I16 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeContextAddress(e, i.src1); - e.mov(i.dest, e.word[addr]); - if (IsTracingData()) { - e.mov(e.r8, e.word[addr]); - e.mov(e.rdx, i.src1.value); - e.CallNative(reinterpret_cast(TraceContextLoadI16)); - } - } -}; -struct LOAD_CONTEXT_I32 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeContextAddress(e, i.src1); - e.mov(i.dest, e.dword[addr]); - if (IsTracingData()) { - e.mov(e.r8, e.dword[addr]); - e.mov(e.rdx, i.src1.value); - e.CallNative(reinterpret_cast(TraceContextLoadI32)); - } - } -}; -struct LOAD_CONTEXT_I64 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeContextAddress(e, i.src1); - e.mov(i.dest, e.qword[addr]); - if (IsTracingData()) { - e.mov(e.r8, e.qword[addr]); - e.mov(e.rdx, i.src1.value); - e.CallNative(reinterpret_cast(TraceContextLoadI64)); - } - } -}; -struct LOAD_CONTEXT_F32 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeContextAddress(e, i.src1); - e.vmovss(i.dest, e.dword[addr]); - if (IsTracingData()) { - e.lea(e.r8, e.dword[addr]); - e.mov(e.rdx, i.src1.value); - e.CallNative(reinterpret_cast(TraceContextLoadF32)); - } - } -}; -struct LOAD_CONTEXT_F64 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeContextAddress(e, i.src1); - e.vmovsd(i.dest, e.qword[addr]); - if (IsTracingData()) { - e.lea(e.r8, e.qword[addr]); - e.mov(e.rdx, i.src1.value); - e.CallNative(reinterpret_cast(TraceContextLoadF64)); - } - } -}; -struct LOAD_CONTEXT_V128 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeContextAddress(e, i.src1); - e.vmovaps(i.dest, e.ptr[addr]); - if (IsTracingData()) { - e.lea(e.r8, e.ptr[addr]); - e.mov(e.rdx, i.src1.value); - e.CallNative(reinterpret_cast(TraceContextLoadV128)); - } - } -}; -EMITTER_OPCODE_TABLE(OPCODE_LOAD_CONTEXT, LOAD_CONTEXT_I8, LOAD_CONTEXT_I16, - LOAD_CONTEXT_I32, LOAD_CONTEXT_I64, LOAD_CONTEXT_F32, - LOAD_CONTEXT_F64, LOAD_CONTEXT_V128); - -// ============================================================================ -// OPCODE_STORE_CONTEXT -// ============================================================================ -// Note: all types are always aligned on the stack. -struct STORE_CONTEXT_I8 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeContextAddress(e, i.src1); - if (i.src2.is_constant) { - e.mov(e.byte[addr], i.src2.constant()); - } else { - e.mov(e.byte[addr], i.src2); - } - if (IsTracingData()) { - e.mov(e.r8, e.byte[addr]); - e.mov(e.rdx, i.src1.value); - e.CallNative(reinterpret_cast(TraceContextStoreI8)); - } - } -}; -struct STORE_CONTEXT_I16 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeContextAddress(e, i.src1); - if (i.src2.is_constant) { - e.mov(e.word[addr], i.src2.constant()); - } else { - e.mov(e.word[addr], i.src2); - } - if (IsTracingData()) { - e.mov(e.r8, e.word[addr]); - e.mov(e.rdx, i.src1.value); - e.CallNative(reinterpret_cast(TraceContextStoreI16)); - } - } -}; -struct STORE_CONTEXT_I32 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeContextAddress(e, i.src1); - if (i.src2.is_constant) { - e.mov(e.dword[addr], i.src2.constant()); - } else { - e.mov(e.dword[addr], i.src2); - } - if (IsTracingData()) { - e.mov(e.r8, e.dword[addr]); - e.mov(e.rdx, i.src1.value); - e.CallNative(reinterpret_cast(TraceContextStoreI32)); - } - } -}; -struct STORE_CONTEXT_I64 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeContextAddress(e, i.src1); - if (i.src2.is_constant) { - e.MovMem64(addr, i.src2.constant()); - } else { - e.mov(e.qword[addr], i.src2); - } - if (IsTracingData()) { - e.mov(e.r8, e.qword[addr]); - e.mov(e.rdx, i.src1.value); - e.CallNative(reinterpret_cast(TraceContextStoreI64)); - } - } -}; -struct STORE_CONTEXT_F32 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeContextAddress(e, i.src1); - if (i.src2.is_constant) { - e.mov(e.dword[addr], i.src2.value->constant.i32); - } else { - e.vmovss(e.dword[addr], i.src2); - } - if (IsTracingData()) { - e.lea(e.r8, e.dword[addr]); - e.mov(e.rdx, i.src1.value); - e.CallNative(reinterpret_cast(TraceContextStoreF32)); - } - } -}; -struct STORE_CONTEXT_F64 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeContextAddress(e, i.src1); - if (i.src2.is_constant) { - e.MovMem64(addr, i.src2.value->constant.i64); - } else { - e.vmovsd(e.qword[addr], i.src2); - } - if (IsTracingData()) { - e.lea(e.r8, e.qword[addr]); - e.mov(e.rdx, i.src1.value); - e.CallNative(reinterpret_cast(TraceContextStoreF64)); - } - } -}; -struct STORE_CONTEXT_V128 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeContextAddress(e, i.src1); - if (i.src2.is_constant) { - e.LoadConstantXmm(e.xmm0, i.src2.constant()); - e.vmovaps(e.ptr[addr], e.xmm0); - } else { - e.vmovaps(e.ptr[addr], i.src2); - } - if (IsTracingData()) { - e.lea(e.r8, e.ptr[addr]); - e.mov(e.rdx, i.src1.value); - e.CallNative(reinterpret_cast(TraceContextStoreV128)); - } - } -}; -EMITTER_OPCODE_TABLE(OPCODE_STORE_CONTEXT, STORE_CONTEXT_I8, STORE_CONTEXT_I16, - STORE_CONTEXT_I32, STORE_CONTEXT_I64, STORE_CONTEXT_F32, - STORE_CONTEXT_F64, STORE_CONTEXT_V128); - // ============================================================================ // OPCODE_CONTEXT_BARRIER // ============================================================================ @@ -796,601 +459,6 @@ struct CONTEXT_BARRIER }; EMITTER_OPCODE_TABLE(OPCODE_CONTEXT_BARRIER, CONTEXT_BARRIER); -// ============================================================================ -// OPCODE_LOAD_MMIO -// ============================================================================ -// Note: all types are always aligned in the context. -struct LOAD_MMIO_I32 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - // uint64_t (context, addr) - auto mmio_range = reinterpret_cast(i.src1.value); - auto read_address = uint32_t(i.src2.value); - e.mov(e.r8, uint64_t(mmio_range->callback_context)); - e.mov(e.r9d, read_address); - e.CallNativeSafe(reinterpret_cast(mmio_range->read)); - e.bswap(e.eax); - e.mov(i.dest, e.eax); - if (IsTracingData()) { - e.mov(e.r8, i.dest); - e.mov(e.edx, read_address); - e.CallNative(reinterpret_cast(TraceContextLoadI32)); - } - } -}; -EMITTER_OPCODE_TABLE(OPCODE_LOAD_MMIO, LOAD_MMIO_I32); - -// ============================================================================ -// OPCODE_STORE_MMIO -// ============================================================================ -// Note: all types are always aligned on the stack. -struct STORE_MMIO_I32 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - // void (context, addr, value) - auto mmio_range = reinterpret_cast(i.src1.value); - auto write_address = uint32_t(i.src2.value); - e.mov(e.r8, uint64_t(mmio_range->callback_context)); - e.mov(e.r9d, write_address); - if (i.src3.is_constant) { - e.mov(e.r10d, xe::byte_swap(i.src3.constant())); - } else { - e.mov(e.r10d, i.src3); - e.bswap(e.r10d); - } - e.CallNativeSafe(reinterpret_cast(mmio_range->write)); - if (IsTracingData()) { - if (i.src3.is_constant) { - e.mov(e.r8d, i.src3.constant()); - } else { - e.mov(e.r8d, i.src3); - } - e.mov(e.edx, write_address); - e.CallNative(reinterpret_cast(TraceContextStoreI32)); - } - } -}; -EMITTER_OPCODE_TABLE(OPCODE_STORE_MMIO, STORE_MMIO_I32); - -// ============================================================================ -// OPCODE_LOAD_OFFSET -// ============================================================================ -template -RegExp ComputeMemoryAddressOffset(X64Emitter& e, const T& guest, - const T& offset) { - int32_t offset_const = static_cast(offset.constant()); - - if (guest.is_constant) { - uint32_t address = static_cast(guest.constant()); - address += static_cast(offset.constant()); - if (address < 0x80000000) { - return e.GetMembaseReg() + address; - } else { - e.mov(e.eax, address); - return e.GetMembaseReg() + e.rax; - } - } else { - // Clear the top 32 bits, as they are likely garbage. - // TODO(benvanik): find a way to avoid doing this. - e.mov(e.eax, guest.reg().cvt32()); - return e.GetMembaseReg() + e.rax + offset_const; - } -} - -struct LOAD_OFFSET_I8 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeMemoryAddressOffset(e, i.src1, i.src2); - e.mov(i.dest, e.byte[addr]); - } -}; - -struct LOAD_OFFSET_I16 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeMemoryAddressOffset(e, i.src1, i.src2); - if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { - if (e.IsFeatureEnabled(kX64EmitMovbe)) { - e.movbe(i.dest, e.word[addr]); - } else { - e.mov(i.dest, e.word[addr]); - e.ror(i.dest, 8); - } - } else { - e.mov(i.dest, e.word[addr]); - } - } -}; - -struct LOAD_OFFSET_I32 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeMemoryAddressOffset(e, i.src1, i.src2); - if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { - if (e.IsFeatureEnabled(kX64EmitMovbe)) { - e.movbe(i.dest, e.dword[addr]); - } else { - e.mov(i.dest, e.dword[addr]); - e.bswap(i.dest); - } - } else { - e.mov(i.dest, e.dword[addr]); - } - } -}; - -struct LOAD_OFFSET_I64 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeMemoryAddressOffset(e, i.src1, i.src2); - if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { - if (e.IsFeatureEnabled(kX64EmitMovbe)) { - e.movbe(i.dest, e.qword[addr]); - } else { - e.mov(i.dest, e.qword[addr]); - e.bswap(i.dest); - } - } else { - e.mov(i.dest, e.qword[addr]); - } - } -}; -EMITTER_OPCODE_TABLE(OPCODE_LOAD_OFFSET, LOAD_OFFSET_I8, LOAD_OFFSET_I16, - LOAD_OFFSET_I32, LOAD_OFFSET_I64); - -// ============================================================================ -// OPCODE_STORE_OFFSET -// ============================================================================ -struct STORE_OFFSET_I8 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeMemoryAddressOffset(e, i.src1, i.src2); - if (i.src3.is_constant) { - e.mov(e.byte[addr], i.src3.constant()); - } else { - e.mov(e.byte[addr], i.src3); - } - } -}; - -struct STORE_OFFSET_I16 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeMemoryAddressOffset(e, i.src1, i.src2); - if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { - assert_false(i.src3.is_constant); - if (e.IsFeatureEnabled(kX64EmitMovbe)) { - e.movbe(e.word[addr], i.src3); - } else { - assert_always("not implemented"); - } - } else { - if (i.src3.is_constant) { - e.mov(e.word[addr], i.src3.constant()); - } else { - e.mov(e.word[addr], i.src3); - } - } - } -}; - -struct STORE_OFFSET_I32 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeMemoryAddressOffset(e, i.src1, i.src2); - if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { - assert_false(i.src3.is_constant); - if (e.IsFeatureEnabled(kX64EmitMovbe)) { - e.movbe(e.dword[addr], i.src3); - } else { - assert_always("not implemented"); - } - } else { - if (i.src3.is_constant) { - e.mov(e.dword[addr], i.src3.constant()); - } else { - e.mov(e.dword[addr], i.src3); - } - } - } -}; - -struct STORE_OFFSET_I64 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeMemoryAddressOffset(e, i.src1, i.src2); - if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { - assert_false(i.src3.is_constant); - if (e.IsFeatureEnabled(kX64EmitMovbe)) { - e.movbe(e.qword[addr], i.src3); - } else { - assert_always("not implemented"); - } - } else { - if (i.src3.is_constant) { - e.MovMem64(addr, i.src3.constant()); - } else { - e.mov(e.qword[addr], i.src3); - } - } - } -}; -EMITTER_OPCODE_TABLE(OPCODE_STORE_OFFSET, STORE_OFFSET_I8, STORE_OFFSET_I16, - STORE_OFFSET_I32, STORE_OFFSET_I64); - -// ============================================================================ -// OPCODE_LOAD -// ============================================================================ -// Note: most *should* be aligned, but needs to be checked! -template -RegExp ComputeMemoryAddress(X64Emitter& e, const T& guest) { - if (guest.is_constant) { - // TODO(benvanik): figure out how to do this without a temp. - // Since the constant is often 0x8... if we tried to use that as a - // displacement it would be sign extended and mess things up. - uint32_t address = static_cast(guest.constant()); - if (address < 0x80000000) { - return e.GetMembaseReg() + address; - } else { - e.mov(e.eax, address); - return e.GetMembaseReg() + e.rax; - } - } else { - // Clear the top 32 bits, as they are likely garbage. - // TODO(benvanik): find a way to avoid doing this. - e.mov(e.eax, guest.reg().cvt32()); - return e.GetMembaseReg() + e.rax; - } -} -struct LOAD_I8 : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeMemoryAddress(e, i.src1); - e.mov(i.dest, e.byte[addr]); - if (IsTracingData()) { - e.mov(e.r8b, i.dest); - e.lea(e.rdx, e.ptr[addr]); - e.CallNative(reinterpret_cast(TraceMemoryLoadI8)); - } - } -}; -struct LOAD_I16 : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeMemoryAddress(e, i.src1); - if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { - if (e.IsFeatureEnabled(kX64EmitMovbe)) { - e.movbe(i.dest, e.word[addr]); - } else { - e.mov(i.dest, e.word[addr]); - e.ror(i.dest, 8); - } - } else { - e.mov(i.dest, e.word[addr]); - } - if (IsTracingData()) { - e.mov(e.r8w, i.dest); - e.lea(e.rdx, e.ptr[addr]); - e.CallNative(reinterpret_cast(TraceMemoryLoadI16)); - } - } -}; -struct LOAD_I32 : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeMemoryAddress(e, i.src1); - if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { - if (e.IsFeatureEnabled(kX64EmitMovbe)) { - e.movbe(i.dest, e.dword[addr]); - } else { - e.mov(i.dest, e.dword[addr]); - e.bswap(i.dest); - } - } else { - e.mov(i.dest, e.dword[addr]); - } - if (IsTracingData()) { - e.mov(e.r8d, i.dest); - e.lea(e.rdx, e.ptr[addr]); - e.CallNative(reinterpret_cast(TraceMemoryLoadI32)); - } - } -}; -struct LOAD_I64 : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeMemoryAddress(e, i.src1); - if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { - if (e.IsFeatureEnabled(kX64EmitMovbe)) { - e.movbe(i.dest, e.qword[addr]); - } else { - e.mov(i.dest, e.qword[addr]); - e.bswap(i.dest); - } - } else { - e.mov(i.dest, e.qword[addr]); - } - if (IsTracingData()) { - e.mov(e.r8, i.dest); - e.lea(e.rdx, e.ptr[addr]); - e.CallNative(reinterpret_cast(TraceMemoryLoadI64)); - } - } -}; -struct LOAD_F32 : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeMemoryAddress(e, i.src1); - e.vmovss(i.dest, e.dword[addr]); - if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { - assert_always("not implemented yet"); - } - if (IsTracingData()) { - e.lea(e.r8, e.dword[addr]); - e.lea(e.rdx, e.ptr[addr]); - e.CallNative(reinterpret_cast(TraceMemoryLoadF32)); - } - } -}; -struct LOAD_F64 : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeMemoryAddress(e, i.src1); - e.vmovsd(i.dest, e.qword[addr]); - if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { - assert_always("not implemented yet"); - } - if (IsTracingData()) { - e.lea(e.r8, e.qword[addr]); - e.lea(e.rdx, e.ptr[addr]); - e.CallNative(reinterpret_cast(TraceMemoryLoadF64)); - } - } -}; -struct LOAD_V128 : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeMemoryAddress(e, i.src1); - // TODO(benvanik): we should try to stick to movaps if possible. - e.vmovups(i.dest, e.ptr[addr]); - if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { - // TODO(benvanik): find a way to do this without the memory load. - e.vpshufb(i.dest, i.dest, e.GetXmmConstPtr(XMMByteSwapMask)); - } - if (IsTracingData()) { - e.lea(e.r8, e.ptr[addr]); - e.lea(e.rdx, e.ptr[addr]); - e.CallNative(reinterpret_cast(TraceMemoryLoadV128)); - } - } -}; -EMITTER_OPCODE_TABLE(OPCODE_LOAD, LOAD_I8, LOAD_I16, LOAD_I32, LOAD_I64, - LOAD_F32, LOAD_F64, LOAD_V128); - -// ============================================================================ -// OPCODE_STORE -// ============================================================================ -// Note: most *should* be aligned, but needs to be checked! -struct STORE_I8 : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeMemoryAddress(e, i.src1); - if (i.src2.is_constant) { - e.mov(e.byte[addr], i.src2.constant()); - } else { - e.mov(e.byte[addr], i.src2); - } - if (IsTracingData()) { - addr = ComputeMemoryAddress(e, i.src1); - e.mov(e.r8b, e.byte[addr]); - e.lea(e.rdx, e.ptr[addr]); - e.CallNative(reinterpret_cast(TraceMemoryStoreI8)); - } - } -}; -struct STORE_I16 : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeMemoryAddress(e, i.src1); - if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { - assert_false(i.src2.is_constant); - if (e.IsFeatureEnabled(kX64EmitMovbe)) { - e.movbe(e.word[addr], i.src2); - } else { - assert_always("not implemented"); - } - } else { - if (i.src2.is_constant) { - e.mov(e.word[addr], i.src2.constant()); - } else { - e.mov(e.word[addr], i.src2); - } - } - if (IsTracingData()) { - addr = ComputeMemoryAddress(e, i.src1); - e.mov(e.r8w, e.word[addr]); - e.lea(e.rdx, e.ptr[addr]); - e.CallNative(reinterpret_cast(TraceMemoryStoreI16)); - } - } -}; -struct STORE_I32 : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeMemoryAddress(e, i.src1); - if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { - assert_false(i.src2.is_constant); - if (e.IsFeatureEnabled(kX64EmitMovbe)) { - e.movbe(e.dword[addr], i.src2); - } else { - assert_always("not implemented"); - } - } else { - if (i.src2.is_constant) { - e.mov(e.dword[addr], i.src2.constant()); - } else { - e.mov(e.dword[addr], i.src2); - } - } - if (IsTracingData()) { - addr = ComputeMemoryAddress(e, i.src1); - e.mov(e.r8d, e.dword[addr]); - e.lea(e.rdx, e.ptr[addr]); - e.CallNative(reinterpret_cast(TraceMemoryStoreI32)); - } - } -}; -struct STORE_I64 : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeMemoryAddress(e, i.src1); - if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { - assert_false(i.src2.is_constant); - if (e.IsFeatureEnabled(kX64EmitMovbe)) { - e.movbe(e.qword[addr], i.src2); - } else { - assert_always("not implemented"); - } - } else { - if (i.src2.is_constant) { - e.MovMem64(addr, i.src2.constant()); - } else { - e.mov(e.qword[addr], i.src2); - } - } - if (IsTracingData()) { - addr = ComputeMemoryAddress(e, i.src1); - e.mov(e.r8, e.qword[addr]); - e.lea(e.rdx, e.ptr[addr]); - e.CallNative(reinterpret_cast(TraceMemoryStoreI64)); - } - } -}; -struct STORE_F32 : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeMemoryAddress(e, i.src1); - if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { - assert_false(i.src2.is_constant); - assert_always("not yet implemented"); - } else { - if (i.src2.is_constant) { - e.mov(e.dword[addr], i.src2.value->constant.i32); - } else { - e.vmovss(e.dword[addr], i.src2); - } - } - if (IsTracingData()) { - addr = ComputeMemoryAddress(e, i.src1); - e.lea(e.r8, e.ptr[addr]); - e.lea(e.rdx, e.ptr[addr]); - e.CallNative(reinterpret_cast(TraceMemoryStoreF32)); - } - } -}; -struct STORE_F64 : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeMemoryAddress(e, i.src1); - if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { - assert_false(i.src2.is_constant); - assert_always("not yet implemented"); - } else { - if (i.src2.is_constant) { - e.MovMem64(addr, i.src2.value->constant.i64); - } else { - e.vmovsd(e.qword[addr], i.src2); - } - } - if (IsTracingData()) { - addr = ComputeMemoryAddress(e, i.src1); - e.lea(e.r8, e.ptr[addr]); - e.lea(e.rdx, e.ptr[addr]); - e.CallNative(reinterpret_cast(TraceMemoryStoreF64)); - } - } -}; -struct STORE_V128 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - auto addr = ComputeMemoryAddress(e, i.src1); - if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { - assert_false(i.src2.is_constant); - e.vpshufb(e.xmm0, i.src2, e.GetXmmConstPtr(XMMByteSwapMask)); - e.vmovaps(e.ptr[addr], e.xmm0); - } else { - if (i.src2.is_constant) { - e.LoadConstantXmm(e.xmm0, i.src2.constant()); - e.vmovaps(e.ptr[addr], e.xmm0); - } else { - e.vmovaps(e.ptr[addr], i.src2); - } - } - if (IsTracingData()) { - addr = ComputeMemoryAddress(e, i.src1); - e.lea(e.r8, e.ptr[addr]); - e.lea(e.rdx, e.ptr[addr]); - e.CallNative(reinterpret_cast(TraceMemoryStoreV128)); - } - } -}; -EMITTER_OPCODE_TABLE(OPCODE_STORE, STORE_I8, STORE_I16, STORE_I32, STORE_I64, - STORE_F32, STORE_F64, STORE_V128); - -// ============================================================================ -// OPCODE_PREFETCH -// ============================================================================ -struct PREFETCH - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - // TODO(benvanik): prefetch addr -> length. - } -}; -EMITTER_OPCODE_TABLE(OPCODE_PREFETCH, PREFETCH); - -// ============================================================================ -// OPCODE_MEMORY_BARRIER -// ============================================================================ -struct MEMORY_BARRIER - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { e.mfence(); } -}; -EMITTER_OPCODE_TABLE(OPCODE_MEMORY_BARRIER, MEMORY_BARRIER); - -// ============================================================================ -// OPCODE_MEMSET -// ============================================================================ -struct MEMSET_I64_I8_I64 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - assert_true(i.src2.is_constant); - assert_true(i.src3.is_constant); - assert_true(i.src2.constant() == 0); - e.vpxor(e.xmm0, e.xmm0); - auto addr = ComputeMemoryAddress(e, i.src1); - switch (i.src3.constant()) { - case 32: - e.vmovaps(e.ptr[addr + 0 * 16], e.xmm0); - e.vmovaps(e.ptr[addr + 1 * 16], e.xmm0); - break; - case 128: - e.vmovaps(e.ptr[addr + 0 * 16], e.xmm0); - e.vmovaps(e.ptr[addr + 1 * 16], e.xmm0); - e.vmovaps(e.ptr[addr + 2 * 16], e.xmm0); - e.vmovaps(e.ptr[addr + 3 * 16], e.xmm0); - e.vmovaps(e.ptr[addr + 4 * 16], e.xmm0); - e.vmovaps(e.ptr[addr + 5 * 16], e.xmm0); - e.vmovaps(e.ptr[addr + 6 * 16], e.xmm0); - e.vmovaps(e.ptr[addr + 7 * 16], e.xmm0); - break; - default: - assert_unhandled_case(i.src3.constant()); - break; - } - if (IsTracingData()) { - addr = ComputeMemoryAddress(e, i.src1); - e.mov(e.r9, i.src3.constant()); - e.mov(e.r8, i.src2.constant()); - e.lea(e.rdx, e.ptr[addr]); - e.CallNative(reinterpret_cast(TraceMemset)); - } - } -}; -EMITTER_OPCODE_TABLE(OPCODE_MEMSET, MEMSET_I64_I8_I64); - // ============================================================================ // OPCODE_MAX // ============================================================================ @@ -3970,96 +3038,6 @@ struct CNTLZ_I64 : Sequence> { }; EMITTER_OPCODE_TABLE(OPCODE_CNTLZ, CNTLZ_I8, CNTLZ_I16, CNTLZ_I32, CNTLZ_I64); -// ============================================================================ -// OPCODE_ATOMIC_EXCHANGE -// ============================================================================ -// Note that the address we use here is a real, host address! -// This is weird, and should be fixed. -template -void EmitAtomicExchangeXX(X64Emitter& e, const ARGS& i) { - if (i.dest == i.src1) { - e.mov(e.rax, i.src1); - if (i.dest != i.src2) { - if (i.src2.is_constant) { - e.mov(i.dest, i.src2.constant()); - } else { - e.mov(i.dest, i.src2); - } - } - e.lock(); - e.xchg(e.dword[e.rax], i.dest); - } else { - if (i.dest != i.src2) { - if (i.src2.is_constant) { - e.mov(i.dest, i.src2.constant()); - } else { - e.mov(i.dest, i.src2); - } - } - e.lock(); - e.xchg(e.dword[i.src1.reg()], i.dest); - } -} -struct ATOMIC_EXCHANGE_I8 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - EmitAtomicExchangeXX(e, i); - } -}; -struct ATOMIC_EXCHANGE_I16 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - EmitAtomicExchangeXX(e, i); - } -}; -struct ATOMIC_EXCHANGE_I32 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - EmitAtomicExchangeXX(e, i); - } -}; -struct ATOMIC_EXCHANGE_I64 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - EmitAtomicExchangeXX(e, i); - } -}; -EMITTER_OPCODE_TABLE(OPCODE_ATOMIC_EXCHANGE, ATOMIC_EXCHANGE_I8, - ATOMIC_EXCHANGE_I16, ATOMIC_EXCHANGE_I32, - ATOMIC_EXCHANGE_I64); - -// ============================================================================ -// OPCODE_ATOMIC_COMPARE_EXCHANGE -// ============================================================================ -struct ATOMIC_COMPARE_EXCHANGE_I32 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - e.mov(e.eax, i.src2); - e.mov(e.ecx, i.src1.reg().cvt32()); - e.lock(); - e.cmpxchg(e.dword[e.GetMembaseReg() + e.rcx], i.src3); - e.sete(i.dest); - } -}; -struct ATOMIC_COMPARE_EXCHANGE_I64 - : Sequence> { - static void Emit(X64Emitter& e, const EmitArgType& i) { - e.mov(e.rax, i.src2); - e.mov(e.ecx, i.src1.reg().cvt32()); - e.lock(); - e.cmpxchg(e.qword[e.GetMembaseReg() + e.rcx], i.src3); - e.sete(i.dest); - } -}; -EMITTER_OPCODE_TABLE(OPCODE_ATOMIC_COMPARE_EXCHANGE, - ATOMIC_COMPARE_EXCHANGE_I32, ATOMIC_COMPARE_EXCHANGE_I64); - // ============================================================================ // OPCODE_SET_ROUNDING_MODE // ============================================================================ @@ -4081,6 +3059,7 @@ EMITTER_OPCODE_TABLE(OPCODE_SET_ROUNDING_MODE, SET_ROUNDING_MODE_I32); void RegisterSequences() { RegisterControl(); + RegisterMemory(); RegisterVector(); } diff --git a/src/xenia/cpu/backend/x64/x64_sequences.h b/src/xenia/cpu/backend/x64/x64_sequences.h index 755887efa..5815a3a92 100644 --- a/src/xenia/cpu/backend/x64/x64_sequences.h +++ b/src/xenia/cpu/backend/x64/x64_sequences.h @@ -42,6 +42,7 @@ static bool Register() { // Registration functions to force inclusion of several files void RegisterControl(); +void RegisterMemory(); void RegisterVector(); void RegisterSequences();