Huge set of performance improvements, combined with an architecture specific build and clang-cl users have reported absurd gains over master for some gains, in the range 50%-90%
But for normal msvc builds i would put it at around 30-50% Added per-xexmodule caching of information per instruction, can be used to remember what code needs compiling at start up Record what guest addresses wrote mmio and backpropagate that to future runs, eliminating dependence on exception trapping. this makes many games like h3 actually tolerable to run under a debugger fixed a number of errors where temporaries were being passed by reference/pointer Can now be compiled with clang-cl 14.0.1, requires -Werror off though and some other solution/project changes. Added macros wrapping compiler extensions like noinline, forceinline, __expect, and cold. Removed the "global lock" in guest code completely. It does not properly emulate the behavior of mfmsrd/mtmsr and it seriously cripples amd cpus. Removing this yielded around a 3x speedup in Halo Reach for me. Disabled the microprofiler for now. The microprofiler has a huge performance cost associated with it. Developers can re-enable it in the base/profiling header if they really need it Disable the trace writer in release builds. despite just returning after checking if the file was open the trace functions were consuming about 0.60% cpu time total Add IsValidReg, GetRegisterInfo is a huge (about 45k) branching function and using that to check if a register was valid consumed a significant chunk of time Optimized RingBuffer::ReadAndSwap and RingBuffer::read_count. This gave us the largest overall boost in performance. The memcpies were unnecessary and one of them was always a no-op Added simplification rules for multiplicative patterns like (x+x), (x<<1)+x For the most frequently called win32 functions i added code to call their underlying NT implementations, which lets us skip a lot of MS code we don't care about/isnt relevant to our usecases ^this can be toggled off in the platform_win header handle indirect call true with constant function pointer, was occurring in h3 lookup host format swizzle in denser array by default, don't check if a gpu register is unknown, instead just check if its out of range. controlled by a cvar ^looking up whether its known or not took approx 0.3% cpu time Changed some things in /cpu to make the project UNITYBUILD friendly The timer thread was spinning way too much and consuming a ton of cpu, changed it to use a blocking wait instead tagged some conditions as XE_UNLIKELY/LIKELY based on profiler feedback (will only affect clang builds) Shifted around some code in CommandProcessor::WriteRegister based on how frequently it was executed added support for docdecaduple precision floating point so that we can represent our performance gains numerically tons of other stuff im probably forgetting
This commit is contained in:
@@ -25,7 +25,7 @@
|
||||
#include "xenia/cpu/breakpoint.h"
|
||||
#include "xenia/cpu/processor.h"
|
||||
#include "xenia/cpu/stack_walker.h"
|
||||
|
||||
#include "xenia/cpu/xex_module.h"
|
||||
DEFINE_int32(x64_extension_mask, -1,
|
||||
"Allow the detection and utilization of specific instruction set "
|
||||
"features.\n"
|
||||
@@ -45,6 +45,12 @@ DEFINE_int32(x64_extension_mask, -1,
|
||||
" -1 = Detect and utilize all possible processor features\n",
|
||||
"x64");
|
||||
|
||||
DEFINE_bool(record_mmio_access_exceptions, true,
|
||||
"For guest addresses records whether we caught any mmio accesses "
|
||||
"for them. This info can then be used on a subsequent run to "
|
||||
"instruct the recompiler to emit checks",
|
||||
"CPU");
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace backend {
|
||||
@@ -86,6 +92,11 @@ X64Backend::~X64Backend() {
|
||||
ExceptionHandler::Uninstall(&ExceptionCallbackThunk, this);
|
||||
}
|
||||
|
||||
static void ForwardMMIOAccessForRecording(void* context, void* hostaddr) {
|
||||
reinterpret_cast<X64Backend*>(context)
|
||||
->RecordMMIOExceptionForGuestInstruction(hostaddr);
|
||||
}
|
||||
|
||||
bool X64Backend::Initialize(Processor* processor) {
|
||||
if (!Backend::Initialize(processor)) {
|
||||
return false;
|
||||
@@ -146,6 +157,8 @@ bool X64Backend::Initialize(Processor* processor) {
|
||||
// Setup exception callback
|
||||
ExceptionHandler::Install(&ExceptionCallbackThunk, this);
|
||||
|
||||
processor->memory()->SetMMIOExceptionRecordingCallback(
|
||||
ForwardMMIOAccessForRecording, (void*)this);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -390,7 +403,28 @@ bool X64Backend::ExceptionCallbackThunk(Exception* ex, void* data) {
|
||||
auto backend = reinterpret_cast<X64Backend*>(data);
|
||||
return backend->ExceptionCallback(ex);
|
||||
}
|
||||
void X64Backend::RecordMMIOExceptionForGuestInstruction(void* host_address) {
|
||||
uint64_t host_addr_u64 = (uint64_t)host_address;
|
||||
|
||||
auto fnfor = code_cache()->LookupFunction(host_addr_u64);
|
||||
if (fnfor) {
|
||||
uint32_t guestaddr = fnfor->MapMachineCodeToGuestAddress(host_addr_u64);
|
||||
|
||||
Module* guest_module = fnfor->module();
|
||||
if (guest_module) {
|
||||
XexModule* xex_guest_module = dynamic_cast<XexModule*>(guest_module);
|
||||
|
||||
if (xex_guest_module) {
|
||||
cpu::InfoCacheFlags* icf =
|
||||
xex_guest_module->GetInstructionAddressFlags(guestaddr);
|
||||
|
||||
if (icf) {
|
||||
icf->accessed_mmio = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bool X64Backend::ExceptionCallback(Exception* ex) {
|
||||
if (ex->code() != Exception::Code::kIllegalInstruction) {
|
||||
// We only care about illegal instructions. Other things will be handled by
|
||||
@@ -399,6 +433,8 @@ bool X64Backend::ExceptionCallback(Exception* ex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// processor_->memory()->LookupVirtualMappedRange()
|
||||
|
||||
// Verify an expected illegal instruction.
|
||||
auto instruction_bytes =
|
||||
xe::load_and_swap<uint16_t>(reinterpret_cast<void*>(ex->pc()));
|
||||
|
||||
@@ -92,6 +92,8 @@ class X64Backend : public Backend {
|
||||
}
|
||||
virtual void SetGuestRoundingMode(void* ctx, unsigned int mode) override;
|
||||
|
||||
void RecordMMIOExceptionForGuestInstruction(void* host_address);
|
||||
|
||||
private:
|
||||
static bool ExceptionCallbackThunk(Exception* ex, void* data);
|
||||
bool ExceptionCallback(Exception* ex);
|
||||
|
||||
@@ -156,7 +156,7 @@ bool X64Emitter::Emit(GuestFunction* function, HIRBuilder* builder,
|
||||
void** out_code_address, size_t* out_code_size,
|
||||
std::vector<SourceMapEntry>* out_source_map) {
|
||||
SCOPE_profile_cpu_f("cpu");
|
||||
|
||||
guest_module_ = dynamic_cast<XexModule*>(function->module());
|
||||
// Reset.
|
||||
debug_info_ = debug_info;
|
||||
debug_info_flags_ = debug_info_flags;
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
#include "xenia/cpu/hir/hir_builder.h"
|
||||
#include "xenia/cpu/hir/instr.h"
|
||||
#include "xenia/cpu/hir/value.h"
|
||||
#include "xenia/cpu/xex_module.h"
|
||||
#include "xenia/memory.h"
|
||||
|
||||
// NOTE: must be included last as it expects windows.h to already be included.
|
||||
#include "third_party/xbyak/xbyak/xbyak.h"
|
||||
#include "third_party/xbyak/xbyak/xbyak_util.h"
|
||||
@@ -65,11 +65,7 @@ enum class SimdDomain : uint32_t {
|
||||
// CONFLICTING means its used in multiple domains)
|
||||
};
|
||||
|
||||
enum class MXCSRMode : uint32_t {
|
||||
Unknown,
|
||||
Fpu,
|
||||
Vmx
|
||||
};
|
||||
enum class MXCSRMode : uint32_t { Unknown, Fpu, Vmx };
|
||||
|
||||
static SimdDomain PickDomain2(SimdDomain dom1, SimdDomain dom2) {
|
||||
if (dom1 == dom2) {
|
||||
@@ -326,16 +322,21 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
||||
size_t stack_size() const { return stack_size_; }
|
||||
SimdDomain DeduceSimdDomain(const hir::Value* for_value);
|
||||
|
||||
void ForgetMxcsrMode() {
|
||||
mxcsr_mode_ = MXCSRMode::Unknown;
|
||||
}
|
||||
void ForgetMxcsrMode() { mxcsr_mode_ = MXCSRMode::Unknown; }
|
||||
/*
|
||||
returns true if had to load mxcsr. DOT_PRODUCT can use this to skip clearing the overflow flag, as it will never be set in the vmx fpscr
|
||||
returns true if had to load mxcsr. DOT_PRODUCT can use this to skip
|
||||
clearing the overflow flag, as it will never be set in the vmx fpscr
|
||||
*/
|
||||
bool ChangeMxcsrMode(MXCSRMode new_mode, bool already_set=false);//already_set means that the caller already did vldmxcsr, used for SET_ROUNDING_MODE
|
||||
bool ChangeMxcsrMode(
|
||||
MXCSRMode new_mode,
|
||||
bool already_set = false); // already_set means that the caller already
|
||||
// did vldmxcsr, used for SET_ROUNDING_MODE
|
||||
|
||||
void LoadFpuMxcsrDirect(); // unsafe, does not change mxcsr_mode_
|
||||
void LoadVmxMxcsrDirect(); // unsafe, does not change mxcsr_mode_
|
||||
|
||||
XexModule* GuestModule() { return guest_module_; }
|
||||
|
||||
void LoadFpuMxcsrDirect(); //unsafe, does not change mxcsr_mode_
|
||||
void LoadVmxMxcsrDirect(); //unsafe, does not change mxcsr_mode_
|
||||
protected:
|
||||
void* Emplace(const EmitFunctionInfo& func_info,
|
||||
GuestFunction* function = nullptr);
|
||||
@@ -348,6 +349,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
||||
X64Backend* backend_ = nullptr;
|
||||
X64CodeCache* code_cache_ = nullptr;
|
||||
XbyakAllocator* allocator_ = nullptr;
|
||||
XexModule* guest_module_ = nullptr;
|
||||
Xbyak::util::Cpu cpu_;
|
||||
uint32_t feature_flags_ = 0;
|
||||
|
||||
|
||||
@@ -60,23 +60,46 @@ union InstrKey {
|
||||
|
||||
InstrKey() : value(0) { static_assert_size(*this, sizeof(value)); }
|
||||
InstrKey(uint32_t v) : value(v) {}
|
||||
|
||||
// this used to take about 1% cpu while precompiling
|
||||
// it kept reloading opcode, and also constantly repacking and unpacking the
|
||||
// bitfields. instead, we pack the fields at the very end
|
||||
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;
|
||||
const OpcodeInfo* info = i->GetOpcodeInfo();
|
||||
|
||||
uint32_t sig = info->signature;
|
||||
|
||||
OpcodeSignatureType dest_type, src1_type, src2_type, src3_type;
|
||||
|
||||
UnpackOpcodeSig(sig, dest_type, src1_type, src2_type, src3_type);
|
||||
|
||||
uint32_t out_desttype = (uint32_t)dest_type;
|
||||
uint32_t out_src1type = (uint32_t)src1_type;
|
||||
uint32_t out_src2type = (uint32_t)src2_type;
|
||||
uint32_t out_src3type = (uint32_t)src3_type;
|
||||
|
||||
Value* destv = i->dest;
|
||||
// pre-deref, even if not value
|
||||
Value* src1v = i->src1.value;
|
||||
Value* src2v = i->src2.value;
|
||||
Value* src3v = i->src3.value;
|
||||
|
||||
if (out_src1type == OPCODE_SIG_TYPE_V) {
|
||||
out_src1type += src1v->type;
|
||||
}
|
||||
src2 = GET_OPCODE_SIG_TYPE_SRC2(sig);
|
||||
if (src2 == OPCODE_SIG_TYPE_V) {
|
||||
src2 += i->src2.value->type;
|
||||
|
||||
if (out_src2type == OPCODE_SIG_TYPE_V) {
|
||||
out_src2type += src2v->type;
|
||||
}
|
||||
src3 = GET_OPCODE_SIG_TYPE_SRC3(sig);
|
||||
if (src3 == OPCODE_SIG_TYPE_V) {
|
||||
src3 += i->src3.value->type;
|
||||
|
||||
if (out_src3type == OPCODE_SIG_TYPE_V) {
|
||||
out_src3type += src3v->type;
|
||||
}
|
||||
opcode = info->num;
|
||||
dest = out_desttype ? OPCODE_SIG_TYPE_V + destv->type : 0;
|
||||
src1 = out_src1type;
|
||||
src2 = out_src2type;
|
||||
src3 = out_src3type;
|
||||
}
|
||||
|
||||
template <Opcode OPCODE, KeyType DEST = KEY_TYPE_X, KeyType SRC1 = KEY_TYPE_X,
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "xenia/cpu/backend/x64/x64_op.h"
|
||||
#include "xenia/cpu/backend/x64/x64_tracers.h"
|
||||
#include "xenia/cpu/ppc/ppc_context.h"
|
||||
|
||||
#include "xenia/cpu/processor.h"
|
||||
DEFINE_bool(
|
||||
elide_e0_check, false,
|
||||
"Eliminate e0 check on some memory accesses, like to r13(tls) or r1(sp)",
|
||||
@@ -27,6 +27,10 @@ DEFINE_bool(enable_rmw_context_merging, false,
|
||||
"Permit merging read-modify-write HIR instr sequences together "
|
||||
"into x86 instructions that use a memory operand.",
|
||||
"x64");
|
||||
DEFINE_bool(emit_mmio_aware_stores_for_recorded_exception_addresses, true,
|
||||
"Uses info gathered via record_mmio_access_exceptions to emit "
|
||||
"special stores that are faster than trapping the exception",
|
||||
"CPU");
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
@@ -965,6 +969,21 @@ struct STORE_MMIO_I32
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_STORE_MMIO, STORE_MMIO_I32);
|
||||
// according to triangle we dont support mmio reads atm so no point in
|
||||
// implementing this for them
|
||||
static bool IsPossibleMMIOInstruction(X64Emitter& e, const hir::Instr* i) {
|
||||
if (!cvars::emit_mmio_aware_stores_for_recorded_exception_addresses) {
|
||||
return false;
|
||||
}
|
||||
uint32_t guestaddr = i->GuestAddressFor();
|
||||
if (!guestaddr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto flags = e.GuestModule()->GetInstructionAddressFlags(guestaddr);
|
||||
|
||||
return flags && flags->accessed_mmio;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// OPCODE_LOAD_OFFSET
|
||||
@@ -1030,6 +1049,28 @@ struct LOAD_OFFSET_I64
|
||||
EMITTER_OPCODE_TABLE(OPCODE_LOAD_OFFSET, LOAD_OFFSET_I8, LOAD_OFFSET_I16,
|
||||
LOAD_OFFSET_I32, LOAD_OFFSET_I64);
|
||||
|
||||
template <typename T, bool swap>
|
||||
static void MMIOAwareStore(void* _ctx, unsigned int guestaddr, T value) {
|
||||
if (swap) {
|
||||
value = xe::byte_swap(value);
|
||||
}
|
||||
if (guestaddr >= 0xE0000000) {
|
||||
guestaddr += 0x1000;
|
||||
}
|
||||
|
||||
auto ctx = reinterpret_cast<ppc::PPCContext*>(_ctx);
|
||||
|
||||
auto gaddr = ctx->processor->memory()->LookupVirtualMappedRange(guestaddr);
|
||||
if (!gaddr) {
|
||||
*reinterpret_cast<T*>(ctx->virtual_membase + guestaddr) = value;
|
||||
} else {
|
||||
value = xe::byte_swap(value); /*
|
||||
was having issues, found by comparing the values used with exceptions
|
||||
to these that we were reversed...
|
||||
*/
|
||||
gaddr->write(nullptr, gaddr->callback_context, guestaddr, value);
|
||||
}
|
||||
}
|
||||
// ============================================================================
|
||||
// OPCODE_STORE_OFFSET
|
||||
// ============================================================================
|
||||
@@ -1038,6 +1079,7 @@ struct STORE_OFFSET_I8
|
||||
I<OPCODE_STORE_OFFSET, VoidOp, I64Op, I64Op, I8Op>> {
|
||||
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 {
|
||||
@@ -1076,23 +1118,48 @@ struct STORE_OFFSET_I32
|
||||
: Sequence<STORE_OFFSET_I32,
|
||||
I<OPCODE_STORE_OFFSET, VoidOp, I64Op, I64Op, I32Op>> {
|
||||
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");
|
||||
if (IsPossibleMMIOInstruction(e, i.instr)) {
|
||||
void* addrptr = (void*)&MMIOAwareStore<uint32_t, false>;
|
||||
|
||||
if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) {
|
||||
addrptr = (void*)&MMIOAwareStore<uint32_t, true>;
|
||||
}
|
||||
if (i.src1.is_constant) {
|
||||
e.mov(e.GetNativeParam(0).cvt32(), i.src1.constant());
|
||||
} else {
|
||||
e.mov(e.GetNativeParam(0).cvt32(), i.src1.reg().cvt32());
|
||||
}
|
||||
if (i.src2.is_constant) {
|
||||
e.add(e.GetNativeParam(0).cvt32(), (uint32_t)i.src2.constant());
|
||||
} else {
|
||||
e.add(e.GetNativeParam(0).cvt32(), i.src2);
|
||||
}
|
||||
} else {
|
||||
if (i.src3.is_constant) {
|
||||
if (i.src3.constant() == 0 && e.CanUseMembaseLow32As0()) {
|
||||
e.mov(e.dword[addr], e.GetMembaseReg().cvt32());
|
||||
e.mov(e.GetNativeParam(1).cvt32(), i.src3.constant());
|
||||
} else {
|
||||
e.mov(e.GetNativeParam(1).cvt32(), i.src3);
|
||||
}
|
||||
e.CallNativeSafe(addrptr);
|
||||
|
||||
} else {
|
||||
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 {
|
||||
e.mov(e.dword[addr], i.src3.constant());
|
||||
assert_always("not implemented");
|
||||
}
|
||||
} else {
|
||||
e.mov(e.dword[addr], i.src3);
|
||||
if (i.src3.is_constant) {
|
||||
if (i.src3.constant() == 0 && e.CanUseMembaseLow32As0()) {
|
||||
e.mov(e.dword[addr], e.GetMembaseReg().cvt32());
|
||||
} else {
|
||||
e.mov(e.dword[addr], i.src3.constant());
|
||||
}
|
||||
} else {
|
||||
e.mov(e.dword[addr], i.src3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1290,23 +1357,43 @@ struct STORE_I16 : Sequence<STORE_I16, I<OPCODE_STORE, VoidOp, I64Op, I16Op>> {
|
||||
};
|
||||
struct STORE_I32 : Sequence<STORE_I32, I<OPCODE_STORE, VoidOp, I64Op, I32Op>> {
|
||||
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");
|
||||
if (IsPossibleMMIOInstruction(e, i.instr)) {
|
||||
void* addrptr = (void*)&MMIOAwareStore<uint32_t, false>;
|
||||
|
||||
if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) {
|
||||
addrptr = (void*)&MMIOAwareStore<uint32_t, true>;
|
||||
}
|
||||
} else {
|
||||
if (i.src2.is_constant) {
|
||||
e.mov(e.dword[addr], i.src2.constant());
|
||||
if (i.src1.is_constant) {
|
||||
e.mov(e.GetNativeParam(0).cvt32(), (uint32_t)i.src1.constant());
|
||||
} else {
|
||||
e.mov(e.dword[addr], i.src2);
|
||||
e.mov(e.GetNativeParam(0).cvt32(), i.src1.reg().cvt32());
|
||||
}
|
||||
if (i.src2.is_constant) {
|
||||
e.mov(e.GetNativeParam(1).cvt32(), i.src2.constant());
|
||||
} else {
|
||||
e.mov(e.GetNativeParam(1).cvt32(), i.src2);
|
||||
}
|
||||
e.CallNativeSafe(addrptr);
|
||||
|
||||
} else {
|
||||
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);
|
||||
auto addr = ComputeMemoryAddress(e, i.src1);
|
||||
e.mov(e.GetNativeParam(1).cvt32(), e.dword[addr]);
|
||||
e.lea(e.GetNativeParam(0), e.ptr[addr]);
|
||||
e.CallNative(reinterpret_cast<void*>(TraceMemoryStoreI32));
|
||||
|
||||
@@ -1683,6 +1683,9 @@ struct DIV_I16 : Sequence<DIV_I16, I<OPCODE_DIV, I16Op, I16Op, I16Op>> {
|
||||
assert_impossible_sequence(DIV_I16);
|
||||
}
|
||||
};
|
||||
/*
|
||||
TODO: hoist the overflow/zero checks into HIR
|
||||
*/
|
||||
struct DIV_I32 : Sequence<DIV_I32, I<OPCODE_DIV, I32Op, I32Op, I32Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
Xbyak::Label skip;
|
||||
@@ -1766,6 +1769,9 @@ struct DIV_I32 : Sequence<DIV_I32, I<OPCODE_DIV, I32Op, I32Op, I32Op>> {
|
||||
e.mov(i.dest, e.eax);
|
||||
}
|
||||
};
|
||||
/*
|
||||
TODO: hoist the overflow/zero checks into HIR
|
||||
*/
|
||||
struct DIV_I64 : Sequence<DIV_I64, I<OPCODE_DIV, I64Op, I64Op, I64Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
Xbyak::Label skip;
|
||||
@@ -1811,7 +1817,7 @@ struct DIV_I64 : Sequence<DIV_I64, I<OPCODE_DIV, I64Op, I64Op, I64Op>> {
|
||||
} else {
|
||||
// check for signed overflow
|
||||
if (i.src1.is_constant) {
|
||||
if (i.src1.constant() != (1 << 31)) {
|
||||
if (i.src1.constant() != (1ll << 63)) {
|
||||
// we're good, overflow is impossible
|
||||
} else {
|
||||
e.cmp(i.src2, -1); // otherwise, if src2 is -1 then we have
|
||||
|
||||
Reference in New Issue
Block a user