Merge pull request #52 from chrisps/canary_experimental
Fix previous batch of CPU changes
This commit is contained in:
@@ -63,6 +63,10 @@ class Backend {
|
||||
virtual void InstallBreakpoint(Breakpoint* breakpoint) {}
|
||||
virtual void InstallBreakpoint(Breakpoint* breakpoint, Function* fn) {}
|
||||
virtual void UninstallBreakpoint(Breakpoint* breakpoint) {}
|
||||
// ctx points to the start of a ppccontext, ctx - page_allocation_granularity
|
||||
// up until the start of ctx may be used by the backend to store whatever data
|
||||
// they want
|
||||
virtual void InitializeBackendContext(void* ctx) {}
|
||||
|
||||
protected:
|
||||
Processor* processor_ = nullptr;
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
#include "xenia/cpu/cpu_flags.h"
|
||||
#include "xenia/cpu/function.h"
|
||||
#include "xenia/cpu/function_debug_info.h"
|
||||
#include "xenia/cpu/hir/instr.h"
|
||||
#include "xenia/cpu/hir/opcodes.h"
|
||||
#include "xenia/cpu/hir/value.h"
|
||||
#include "xenia/cpu/processor.h"
|
||||
#include "xenia/cpu/symbol.h"
|
||||
#include "xenia/cpu/thread_state.h"
|
||||
@@ -393,7 +396,8 @@ void X64Emitter::DebugBreak() {
|
||||
}
|
||||
|
||||
uint64_t TrapDebugPrint(void* raw_context, uint64_t address) {
|
||||
auto thread_state = *reinterpret_cast<ThreadState**>(raw_context);
|
||||
auto thread_state =
|
||||
reinterpret_cast<ppc::PPCContext_s*>(raw_context)->thread_state;
|
||||
uint32_t str_ptr = uint32_t(thread_state->context()->r[3]);
|
||||
// uint16_t str_len = uint16_t(thread_state->context()->r[4]);
|
||||
auto str = thread_state->memory()->TranslateVirtual<const char*>(str_ptr);
|
||||
@@ -408,7 +412,8 @@ uint64_t TrapDebugPrint(void* raw_context, uint64_t address) {
|
||||
}
|
||||
|
||||
uint64_t TrapDebugBreak(void* raw_context, uint64_t address) {
|
||||
auto thread_state = *reinterpret_cast<ThreadState**>(raw_context);
|
||||
auto thread_state =
|
||||
reinterpret_cast<ppc::PPCContext_s*>(raw_context)->thread_state;
|
||||
XELOGE("tw/td forced trap hit! This should be a crash!");
|
||||
if (cvars::break_on_debugbreak) {
|
||||
xe::debugging::Break();
|
||||
@@ -447,7 +452,8 @@ void X64Emitter::UnimplementedInstr(const hir::Instr* i) {
|
||||
|
||||
// This is used by the X64ThunkEmitter's ResolveFunctionThunk.
|
||||
uint64_t ResolveFunction(void* raw_context, uint64_t target_address) {
|
||||
auto thread_state = *reinterpret_cast<ThreadState**>(raw_context);
|
||||
auto thread_state =
|
||||
reinterpret_cast<ppc::PPCContext_s*>(raw_context)->thread_state;
|
||||
|
||||
// TODO(benvanik): required?
|
||||
assert_not_zero(target_address);
|
||||
@@ -1191,7 +1197,109 @@ Xbyak::Address X64Emitter::StashConstantXmm(int index, const vec128_t& v) {
|
||||
MovMem64(addr + 8, v.high);
|
||||
return ptr[addr];
|
||||
}
|
||||
static bool IsVectorCompare(const Instr* i) {
|
||||
hir::Opcode op = i->opcode->num;
|
||||
return op >= hir::OPCODE_VECTOR_COMPARE_EQ &&
|
||||
op <= hir::OPCODE_VECTOR_COMPARE_UGE;
|
||||
}
|
||||
|
||||
static bool IsFlaggedVectorOp(const Instr* i) {
|
||||
if (IsVectorCompare(i)) {
|
||||
return true;
|
||||
}
|
||||
hir::Opcode op = i->opcode->num;
|
||||
using namespace hir;
|
||||
switch (op) {
|
||||
case OPCODE_VECTOR_SUB:
|
||||
case OPCODE_VECTOR_ADD:
|
||||
case OPCODE_SWIZZLE:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static SimdDomain GetDomainForFlaggedVectorOp(const hir::Instr* df) {
|
||||
switch (df->flags) { // check what datatype we compared as
|
||||
case hir::INT16_TYPE:
|
||||
case hir::INT32_TYPE:
|
||||
case hir::INT8_TYPE:
|
||||
case hir::INT64_TYPE:
|
||||
return SimdDomain::INTEGER;
|
||||
case hir::FLOAT32_TYPE:
|
||||
case hir::FLOAT64_TYPE: // pretty sure float64 doesnt occur with vectors.
|
||||
// here for completeness
|
||||
return SimdDomain::FLOATING;
|
||||
default:
|
||||
return SimdDomain::DONTCARE;
|
||||
}
|
||||
return SimdDomain::DONTCARE;
|
||||
}
|
||||
// this list is incomplete
|
||||
static bool IsDefiniteIntegerDomainOpcode(hir::Opcode opc) {
|
||||
using namespace hir;
|
||||
switch (opc) {
|
||||
case OPCODE_LOAD_VECTOR_SHL:
|
||||
case OPCODE_LOAD_VECTOR_SHR:
|
||||
case OPCODE_VECTOR_CONVERT_F2I:
|
||||
case OPCODE_VECTOR_MIN: // there apparently is no FLOAT32_TYPE for min/maxs
|
||||
// flags
|
||||
case OPCODE_VECTOR_MAX:
|
||||
case OPCODE_VECTOR_SHL:
|
||||
case OPCODE_VECTOR_SHR:
|
||||
case OPCODE_VECTOR_SHA:
|
||||
case OPCODE_VECTOR_ROTATE_LEFT:
|
||||
case OPCODE_VECTOR_AVERAGE: // apparently no float32 type for this
|
||||
case OPCODE_EXTRACT:
|
||||
case OPCODE_INSERT: // apparently no f32 type for these two
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static bool IsDefiniteFloatingDomainOpcode(hir::Opcode opc) {
|
||||
using namespace hir;
|
||||
switch (opc) {
|
||||
case OPCODE_VECTOR_CONVERT_I2F:
|
||||
case OPCODE_VECTOR_DENORMFLUSH:
|
||||
case OPCODE_DOT_PRODUCT_3:
|
||||
case OPCODE_DOT_PRODUCT_4:
|
||||
case OPCODE_LOG2:
|
||||
case OPCODE_POW2:
|
||||
case OPCODE_RECIP:
|
||||
case OPCODE_ROUND:
|
||||
case OPCODE_SQRT:
|
||||
case OPCODE_MUL:
|
||||
case OPCODE_MUL_SUB:
|
||||
case OPCODE_MUL_ADD:
|
||||
case OPCODE_ABS:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
SimdDomain X64Emitter::DeduceSimdDomain(const hir::Value* for_value) {
|
||||
hir::Instr* df = for_value->def;
|
||||
if (!df) {
|
||||
// todo: visit uses to figure out domain
|
||||
return SimdDomain::DONTCARE;
|
||||
|
||||
} else {
|
||||
SimdDomain result = SimdDomain::DONTCARE;
|
||||
|
||||
if (IsFlaggedVectorOp(df)) {
|
||||
result = GetDomainForFlaggedVectorOp(df);
|
||||
} else if (IsDefiniteIntegerDomainOpcode(df->opcode->num)) {
|
||||
result = SimdDomain::INTEGER;
|
||||
} else if (IsDefiniteFloatingDomainOpcode(df->opcode->num)) {
|
||||
result = SimdDomain::FLOATING;
|
||||
}
|
||||
|
||||
// todo: check if still dontcare, if so, visit uses of the value to figure
|
||||
// it out
|
||||
return result;
|
||||
}
|
||||
|
||||
return SimdDomain::DONTCARE;
|
||||
}
|
||||
} // namespace x64
|
||||
} // namespace backend
|
||||
} // namespace cpu
|
||||
|
||||
@@ -44,7 +44,39 @@ enum RegisterFlags {
|
||||
REG_DEST = (1 << 0),
|
||||
REG_ABCD = (1 << 1),
|
||||
};
|
||||
/*
|
||||
SSE/AVX/AVX512 has seperate move instructions/shuffle instructions for float
|
||||
data and int data for a reason most processors implement two distinct
|
||||
pipelines, one for the integer domain and one for the floating point domain
|
||||
currently, xenia makes no distinction between the two. Crossing domains is
|
||||
expensive. On Zen processors the penalty is one cycle each time you cross,
|
||||
plus the two pipelines need to synchronize Often xenia will emit an integer
|
||||
instruction, then a floating instruction, then integer again. this
|
||||
effectively adds at least two cycles to the time taken These values will in
|
||||
the future be used as tags to operations that tell them which domain to
|
||||
operate in, if its at all possible to avoid crossing
|
||||
*/
|
||||
enum class SimdDomain : uint32_t {
|
||||
FLOATING,
|
||||
INTEGER,
|
||||
DONTCARE,
|
||||
CONFLICTING // just used as a special result for PickDomain, different from
|
||||
// dontcare (dontcare means we just dont know the domain,
|
||||
// CONFLICTING means its used in multiple domains)
|
||||
};
|
||||
|
||||
static SimdDomain PickDomain2(SimdDomain dom1, SimdDomain dom2) {
|
||||
if (dom1 == dom2) {
|
||||
return dom1;
|
||||
}
|
||||
if (dom1 == SimdDomain::DONTCARE) {
|
||||
return dom2;
|
||||
}
|
||||
if (dom2 == SimdDomain::DONTCARE) {
|
||||
return dom1;
|
||||
}
|
||||
return SimdDomain::CONFLICTING;
|
||||
}
|
||||
enum XmmConst {
|
||||
XMMZero = 0,
|
||||
XMMOne,
|
||||
@@ -122,7 +154,7 @@ enum XmmConst {
|
||||
XMMLVSLTableBase,
|
||||
XMMLVSRTableBase,
|
||||
XMMSingleDenormalMask,
|
||||
XMMThreeFloatMask, //for clearing the fourth float prior to DOT_PRODUCT_3
|
||||
XMMThreeFloatMask, // for clearing the fourth float prior to DOT_PRODUCT_3
|
||||
XMMXenosF16ExtRangeStart
|
||||
};
|
||||
|
||||
@@ -150,8 +182,9 @@ enum X64EmitterFeatureFlags {
|
||||
|
||||
kX64EmitAVX512Ortho = kX64EmitAVX512F | kX64EmitAVX512VL,
|
||||
kX64EmitAVX512Ortho64 = kX64EmitAVX512Ortho | kX64EmitAVX512DQ,
|
||||
kX64FastJrcx = 1 << 12, //jrcxz is as fast as any other jump ( >= Zen1)
|
||||
kX64FastLoop = 1 << 13, //loop/loope/loopne is as fast as any other jump ( >= Zen2)
|
||||
kX64FastJrcx = 1 << 12, // jrcxz is as fast as any other jump ( >= Zen1)
|
||||
kX64FastLoop =
|
||||
1 << 13, // loop/loope/loopne is as fast as any other jump ( >= Zen2)
|
||||
kX64EmitAVX512VBMI = 1 << 14
|
||||
};
|
||||
class ResolvableGuestCall {
|
||||
@@ -259,6 +292,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
||||
FunctionDebugInfo* debug_info() const { return debug_info_; }
|
||||
|
||||
size_t stack_size() const { return stack_size_; }
|
||||
SimdDomain DeduceSimdDomain(const hir::Value* for_value);
|
||||
|
||||
protected:
|
||||
void* Emplace(const EmitFunctionInfo& func_info,
|
||||
|
||||
@@ -12,11 +12,11 @@
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include "xenia/base/cvar.h"
|
||||
#include "xenia/base/memory.h"
|
||||
#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/base/cvar.h"
|
||||
|
||||
DEFINE_bool(
|
||||
elide_e0_check, false,
|
||||
@@ -83,11 +83,17 @@ RegExp ComputeMemoryAddressOffset(X64Emitter& e, const T& guest,
|
||||
!is_definitely_not_eo(guest)) {
|
||||
// Emulate the 4 KB physical address offset in 0xE0000000+ when can't do
|
||||
// it via memory mapping.
|
||||
|
||||
// todo: do branching or use an alt membase and cmov
|
||||
e.xor_(e.eax, e.eax);
|
||||
e.cmp(guest.reg().cvt32(), 0xE0000000 - offset_const);
|
||||
e.lea(e.edx, e.ptr[guest.reg().cvt32() + offset_const]);
|
||||
|
||||
e.cmp(e.edx, e.GetContextReg().cvt32());
|
||||
e.setae(e.al);
|
||||
e.shl(e.eax, 12);
|
||||
e.add(e.eax, guest.reg().cvt32());
|
||||
e.add(e.eax, e.edx);
|
||||
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.
|
||||
@@ -122,7 +128,7 @@ RegExp ComputeMemoryAddress(X64Emitter& e, const T& guest) {
|
||||
// Emulate the 4 KB physical address offset in 0xE0000000+ when can't do
|
||||
// it via memory mapping.
|
||||
e.xor_(e.eax, e.eax);
|
||||
e.cmp(guest.reg().cvt32(), 0xE0000000);
|
||||
e.cmp(guest.reg().cvt32(), e.GetContextReg().cvt32());
|
||||
e.setae(e.al);
|
||||
e.shl(e.eax, 12);
|
||||
e.add(e.eax, guest.reg().cvt32());
|
||||
@@ -208,7 +214,7 @@ struct ATOMIC_COMPARE_EXCHANGE_I32
|
||||
if (xe::memory::allocation_granularity() > 0x1000) {
|
||||
// Emulate the 4 KB physical address offset in 0xE0000000+ when can't do
|
||||
// it via memory mapping.
|
||||
e.cmp(i.src1.reg().cvt32(), 0xE0000000);
|
||||
e.cmp(i.src1.reg().cvt32(), e.GetContextReg().cvt32());
|
||||
e.setae(e.cl);
|
||||
e.movzx(e.ecx, e.cl);
|
||||
e.shl(e.ecx, 12);
|
||||
@@ -229,7 +235,7 @@ struct ATOMIC_COMPARE_EXCHANGE_I64
|
||||
if (xe::memory::allocation_granularity() > 0x1000) {
|
||||
// Emulate the 4 KB physical address offset in 0xE0000000+ when can't do
|
||||
// it via memory mapping.
|
||||
e.cmp(i.src1.reg().cvt32(), 0xE0000000);
|
||||
e.cmp(i.src1.reg().cvt32(), e.GetContextReg().cvt32());
|
||||
e.setae(e.cl);
|
||||
e.movzx(e.ecx, e.cl);
|
||||
e.shl(e.ecx, 12);
|
||||
@@ -1113,7 +1119,7 @@ struct CACHE_CONTROL
|
||||
if (xe::memory::allocation_granularity() > 0x1000) {
|
||||
// Emulate the 4 KB physical address offset in 0xE0000000+ when can't do
|
||||
// it via memory mapping.
|
||||
e.cmp(i.src1.reg().cvt32(), 0xE0000000);
|
||||
e.cmp(i.src1.reg().cvt32(), e.GetContextReg().cvt32());
|
||||
e.setae(e.al);
|
||||
e.movzx(e.eax, e.al);
|
||||
e.shl(e.eax, 12);
|
||||
|
||||
@@ -1826,7 +1826,7 @@ struct PERMUTE_I32
|
||||
}
|
||||
}
|
||||
};
|
||||
//todo: use this on const src1
|
||||
// todo: use this on const src1
|
||||
static vec128_t FixupConstantShuf8(vec128_t input) {
|
||||
for (uint32_t i = 0; i < 16; ++i) {
|
||||
input.u8[i] ^= 0x03;
|
||||
@@ -1984,7 +1984,11 @@ struct SWIZZLE
|
||||
} else {
|
||||
src1 = i.src1;
|
||||
}
|
||||
e.vpshufd(i.dest, src1, swizzle_mask);
|
||||
if (element_type == INT32_TYPE) {
|
||||
e.vpshufd(i.dest, src1, swizzle_mask);
|
||||
} else if (element_type == FLOAT32_TYPE) {
|
||||
e.vshufps(i.dest, src1, src1, swizzle_mask);
|
||||
}
|
||||
} else if (element_type == INT64_TYPE || element_type == FLOAT64_TYPE) {
|
||||
assert_always();
|
||||
} else {
|
||||
|
||||
@@ -717,6 +717,9 @@ struct SELECT_V128_I8
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
// TODO(benvanik): find a shorter sequence.
|
||||
// dest = src1 != 0 ? src2 : src3
|
||||
/*
|
||||
chrispy: this is dead code, this sequence is never emitted
|
||||
*/
|
||||
e.movzx(e.eax, i.src1);
|
||||
e.vmovd(e.xmm1, e.eax);
|
||||
e.vpbroadcastd(e.xmm1, e.xmm1);
|
||||
@@ -737,11 +740,46 @@ struct SELECT_V128_I8
|
||||
e.vpor(i.dest, e.xmm1);
|
||||
}
|
||||
};
|
||||
|
||||
enum class PermittedBlend : uint32_t { NotPermitted, Int8, Ps };
|
||||
static bool IsVectorCompare(const Instr* i) {
|
||||
Opcode op = i->opcode->num;
|
||||
return op >= OPCODE_VECTOR_COMPARE_EQ && op <= OPCODE_VECTOR_COMPARE_UGE;
|
||||
}
|
||||
/*
|
||||
OPCODE_SELECT does a bit by bit selection, however, if the selector is the
|
||||
result of a comparison or if each element may only be 0xff or 0 we may use a
|
||||
blend instruction instead
|
||||
*/
|
||||
static PermittedBlend GetPermittedBlendForSelectV128(const Value* src1v) {
|
||||
const Instr* df = src1v->def;
|
||||
if (!df) {
|
||||
return PermittedBlend::NotPermitted;
|
||||
} else {
|
||||
if (!IsVectorCompare(df)) {
|
||||
return PermittedBlend::NotPermitted; // todo: check ors, ands of
|
||||
// condition
|
||||
} else {
|
||||
switch (df->flags) { // check what datatype we compared as
|
||||
case INT16_TYPE:
|
||||
case INT32_TYPE:
|
||||
case INT8_TYPE:
|
||||
return PermittedBlend::Int8; // use vpblendvb
|
||||
case FLOAT32_TYPE:
|
||||
return PermittedBlend::Ps; // use vblendvps
|
||||
default: // unknown type! just ignore
|
||||
return PermittedBlend::NotPermitted;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
struct SELECT_V128_V128
|
||||
: Sequence<SELECT_V128_V128,
|
||||
I<OPCODE_SELECT, V128Op, V128Op, V128Op, V128Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
Xmm src1 = i.src1.is_constant ? e.xmm0 : i.src1;
|
||||
PermittedBlend mayblend = GetPermittedBlendForSelectV128(i.src1.value);
|
||||
//todo: detect whether src1 is only 0 or FFFF and use blends if so. currently we only detect cmps
|
||||
if (i.src1.is_constant) {
|
||||
e.LoadConstantXmm(src1, i.src1.constant());
|
||||
}
|
||||
@@ -756,10 +794,16 @@ struct SELECT_V128_V128
|
||||
e.LoadConstantXmm(src3, i.src3.constant());
|
||||
}
|
||||
|
||||
// src1 ? src2 : src3;
|
||||
e.vpandn(e.xmm3, src1, src2);
|
||||
e.vpand(i.dest, src1, src3);
|
||||
e.vpor(i.dest, i.dest, e.xmm3);
|
||||
if (mayblend == PermittedBlend::Int8) {
|
||||
e.vpblendvb(i.dest, src2, src3, src1);
|
||||
} else if (mayblend == PermittedBlend::Ps) {
|
||||
e.vblendvps(i.dest, src2, src3, src1);
|
||||
} else {
|
||||
// src1 ? src2 : src3;
|
||||
e.vpandn(e.xmm3, src1, src2);
|
||||
e.vpand(i.dest, src1, src3);
|
||||
e.vpor(i.dest, i.dest, e.xmm3);
|
||||
}
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_SELECT, SELECT_I8, SELECT_I16, SELECT_I32,
|
||||
@@ -2122,7 +2166,8 @@ struct MUL_ADD_V128
|
||||
// TODO(benvanik): the vfmadd sequence produces slightly different results
|
||||
// than vmul+vadd and it'd be nice to know why. Until we know, it's
|
||||
// disabled so tests pass.
|
||||
if (false && e.IsFeatureEnabled(kX64EmitFMA)) {
|
||||
// chrispy: reenabled, i have added the DAZ behavior that was missing
|
||||
if (true && e.IsFeatureEnabled(kX64EmitFMA)) {
|
||||
EmitCommutativeBinaryXmmOp(e, i,
|
||||
[&i](X64Emitter& e, const Xmm& dest,
|
||||
const Xmm& src1, const Xmm& src2) {
|
||||
@@ -2139,7 +2184,11 @@ struct MUL_ADD_V128
|
||||
e.vfmadd231ps(i.dest, src1, src2);
|
||||
} else {
|
||||
// Dest not equal to anything
|
||||
e.vmovdqa(i.dest, src1);
|
||||
// e.vmovdqa(i.dest,
|
||||
// src1);
|
||||
// chrispy: vmovdqa was a domain pipeline
|
||||
// hazard
|
||||
e.vmovaps(i.dest, src1);
|
||||
e.vfmadd213ps(i.dest, src2, src3);
|
||||
}
|
||||
});
|
||||
@@ -2152,7 +2201,8 @@ struct MUL_ADD_V128
|
||||
// If i.dest == i.src3, back up i.src3 so we don't overwrite it.
|
||||
src3 = i.src3;
|
||||
if (i.dest == i.src3) {
|
||||
e.vmovdqa(e.xmm1, i.src3);
|
||||
// e.vmovdqa(e.xmm1, i.src3);
|
||||
e.vmovaps(e.xmm1, i.src3);
|
||||
src3 = e.xmm1;
|
||||
}
|
||||
}
|
||||
@@ -2384,17 +2434,17 @@ EMITTER_OPCODE_TABLE(OPCODE_NEG, NEG_I8, NEG_I16, NEG_I32, NEG_I64, NEG_F32,
|
||||
// ============================================================================
|
||||
struct ABS_F32 : Sequence<ABS_F32, I<OPCODE_ABS, F32Op, F32Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.vpand(i.dest, i.src1, e.GetXmmConstPtr(XMMAbsMaskPS));
|
||||
e.vandps(i.dest, i.src1, e.GetXmmConstPtr(XMMAbsMaskPS));
|
||||
}
|
||||
};
|
||||
struct ABS_F64 : Sequence<ABS_F64, I<OPCODE_ABS, F64Op, F64Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.vpand(i.dest, i.src1, e.GetXmmConstPtr(XMMAbsMaskPD));
|
||||
e.vandpd(i.dest, i.src1, e.GetXmmConstPtr(XMMAbsMaskPD));
|
||||
}
|
||||
};
|
||||
struct ABS_V128 : Sequence<ABS_V128, I<OPCODE_ABS, V128Op, V128Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.vpand(i.dest, i.src1, e.GetXmmConstPtr(XMMAbsMaskPS));
|
||||
e.vandps(i.dest, i.src1, e.GetXmmConstPtr(XMMAbsMaskPS));
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_ABS, ABS_F32, ABS_F64, ABS_V128);
|
||||
@@ -2634,6 +2684,8 @@ struct DOT_PRODUCT_3_V128
|
||||
*/
|
||||
e.vstmxcsr(mxcsr_storage);
|
||||
|
||||
e.vmovaps(e.xmm2, e.GetXmmConstPtr(XMMThreeFloatMask));
|
||||
|
||||
e.mov(e.eax, 8);
|
||||
|
||||
auto src1v = e.xmm0;
|
||||
@@ -2655,8 +2707,8 @@ struct DOT_PRODUCT_3_V128
|
||||
// so that in the future this could be optimized away if the top is known to
|
||||
// be zero. Right now im not sure that happens often though and its
|
||||
// currently not worth it also, maybe pre-and if constant
|
||||
e.vandps(e.xmm3, src1v, e.GetXmmConstPtr(XMMThreeFloatMask));
|
||||
e.vandps(e.xmm2, src2v, e.GetXmmConstPtr(XMMThreeFloatMask));
|
||||
e.vandps(e.xmm3, src1v, e.xmm2);
|
||||
e.vandps(e.xmm2, src2v, e.xmm2);
|
||||
|
||||
e.and_(mxcsr_storage, e.eax);
|
||||
e.vldmxcsr(mxcsr_storage); // overflow flag is cleared, now we're good to
|
||||
@@ -2682,8 +2734,7 @@ struct DOT_PRODUCT_3_V128
|
||||
Xbyak::Label ret_qnan;
|
||||
Xbyak::Label done;
|
||||
e.jnz(ret_qnan);
|
||||
// e.vshufps(i.dest, e.xmm1,e.xmm1, 0); // broadcast
|
||||
e.vbroadcastss(i.dest, e.xmm1);
|
||||
e.vshufps(i.dest, e.xmm1, e.xmm1, 0); // broadcast
|
||||
e.jmp(done);
|
||||
e.L(ret_qnan);
|
||||
e.vmovaps(i.dest, e.GetXmmConstPtr(XMMQNaN));
|
||||
@@ -2728,27 +2779,7 @@ struct DOT_PRODUCT_4_V128
|
||||
|
||||
e.vcvtps2pd(e.ymm0, src1v);
|
||||
e.vcvtps2pd(e.ymm1, src2v);
|
||||
/*
|
||||
e.vandps(e.xmm3, src1v, e.GetXmmConstPtr(XMMThreeFloatMask));
|
||||
e.vandps(e.xmm2, src2v, e.GetXmmConstPtr(XMMThreeFloatMask));
|
||||
|
||||
e.and_(mxcsr_storage, e.eax);
|
||||
e.vldmxcsr(mxcsr_storage); // overflow flag is cleared, now we're good to
|
||||
// go
|
||||
|
||||
e.vcvtps2pd(e.ymm0, e.xmm3);
|
||||
e.vcvtps2pd(e.ymm1, e.xmm2);
|
||||
|
||||
|
||||
e.vmulpd(e.ymm5, e.ymm0, e.ymm1);
|
||||
e.vextractf128(e.xmm4, e.ymm5, 1);
|
||||
e.vunpckhpd(e.xmm3, e.xmm5, e.xmm5); // get element [1] in xmm3
|
||||
e.vaddsd(e.xmm5, e.xmm5, e.xmm4);
|
||||
e.not_(e.eax);
|
||||
e.vaddsd(e.xmm2, e.xmm5, e.xmm3);
|
||||
e.vcvtsd2ss(e.xmm1, e.xmm2);
|
||||
|
||||
*/
|
||||
e.vmulpd(e.ymm3, e.ymm0, e.ymm1);
|
||||
e.vextractf128(e.xmm2, e.ymm3, 1);
|
||||
e.vaddpd(e.xmm3, e.xmm3, e.xmm2);
|
||||
@@ -2765,8 +2796,7 @@ struct DOT_PRODUCT_4_V128
|
||||
Xbyak::Label ret_qnan;
|
||||
Xbyak::Label done;
|
||||
e.jnz(ret_qnan); // reorder these jmps later, just want to get this fix in
|
||||
// e.vshufps(i.dest, e.xmm1, e.xmm1, 0);
|
||||
e.vbroadcastss(i.dest, e.xmm1);
|
||||
e.vshufps(i.dest, e.xmm1, e.xmm1, 0);
|
||||
e.jmp(done);
|
||||
e.L(ret_qnan);
|
||||
e.vmovaps(i.dest, e.GetXmmConstPtr(XMMQNaN));
|
||||
@@ -2846,10 +2876,17 @@ struct AND_I64 : Sequence<AND_I64, I<OPCODE_AND, I64Op, I64Op, I64Op>> {
|
||||
};
|
||||
struct AND_V128 : Sequence<AND_V128, I<OPCODE_AND, V128Op, V128Op, V128Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
EmitCommutativeBinaryXmmOp(e, i,
|
||||
[](X64Emitter& e, Xmm dest, Xmm src1, Xmm src2) {
|
||||
e.vpand(dest, src1, src2);
|
||||
});
|
||||
SimdDomain dom = PickDomain2(e.DeduceSimdDomain(i.src1.value),
|
||||
e.DeduceSimdDomain(i.src2.value));
|
||||
|
||||
EmitCommutativeBinaryXmmOp(
|
||||
e, i, [dom](X64Emitter& e, Xmm dest, Xmm src1, Xmm src2) {
|
||||
if (dom == SimdDomain::FLOATING) {
|
||||
e.vandps(dest, src2, src1);
|
||||
} else {
|
||||
e.vpand(dest, src2, src1);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_AND, AND_I8, AND_I16, AND_I32, AND_I64, AND_V128);
|
||||
@@ -2948,10 +2985,17 @@ struct AND_NOT_I64
|
||||
struct AND_NOT_V128
|
||||
: Sequence<AND_NOT_V128, I<OPCODE_AND_NOT, V128Op, V128Op, V128Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
EmitCommutativeBinaryXmmOp(e, i,
|
||||
[](X64Emitter& e, Xmm dest, Xmm src1, Xmm src2) {
|
||||
e.vpandn(dest, src2, src1);
|
||||
});
|
||||
SimdDomain dom = PickDomain2(e.DeduceSimdDomain(i.src1.value),
|
||||
e.DeduceSimdDomain(i.src2.value));
|
||||
|
||||
EmitCommutativeBinaryXmmOp(
|
||||
e, i, [dom](X64Emitter& e, Xmm dest, Xmm src1, Xmm src2) {
|
||||
if (dom == SimdDomain::FLOATING) {
|
||||
e.vandnps(dest, src2, src1);
|
||||
} else {
|
||||
e.vpandn(dest, src2, src1);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_AND_NOT, AND_NOT_I8, AND_NOT_I16, AND_NOT_I32,
|
||||
@@ -2994,10 +3038,17 @@ struct OR_I64 : Sequence<OR_I64, I<OPCODE_OR, I64Op, I64Op, I64Op>> {
|
||||
};
|
||||
struct OR_V128 : Sequence<OR_V128, I<OPCODE_OR, V128Op, V128Op, V128Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
EmitCommutativeBinaryXmmOp(e, i,
|
||||
[](X64Emitter& e, Xmm dest, Xmm src1, Xmm src2) {
|
||||
e.vpor(dest, src1, src2);
|
||||
});
|
||||
SimdDomain dom = PickDomain2(e.DeduceSimdDomain(i.src1.value),
|
||||
e.DeduceSimdDomain(i.src2.value));
|
||||
|
||||
EmitCommutativeBinaryXmmOp(
|
||||
e, i, [dom](X64Emitter& e, Xmm dest, Xmm src1, Xmm src2) {
|
||||
if (dom == SimdDomain::FLOATING) {
|
||||
e.vorps(dest, src1, src2);
|
||||
} else {
|
||||
e.vpor(dest, src1, src2);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_OR, OR_I8, OR_I16, OR_I32, OR_I64, OR_V128);
|
||||
@@ -3039,10 +3090,17 @@ struct XOR_I64 : Sequence<XOR_I64, I<OPCODE_XOR, I64Op, I64Op, I64Op>> {
|
||||
};
|
||||
struct XOR_V128 : Sequence<XOR_V128, I<OPCODE_XOR, V128Op, V128Op, V128Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
EmitCommutativeBinaryXmmOp(e, i,
|
||||
[](X64Emitter& e, Xmm dest, Xmm src1, Xmm src2) {
|
||||
e.vpxor(dest, src1, src2);
|
||||
});
|
||||
SimdDomain dom = PickDomain2(e.DeduceSimdDomain(i.src1.value),
|
||||
e.DeduceSimdDomain(i.src2.value));
|
||||
|
||||
EmitCommutativeBinaryXmmOp(
|
||||
e, i, [dom](X64Emitter& e, Xmm dest, Xmm src1, Xmm src2) {
|
||||
if (dom == SimdDomain::FLOATING) {
|
||||
e.vxorps(dest, src1, src2);
|
||||
} else {
|
||||
e.vpxor(dest, src1, src2);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_XOR, XOR_I8, XOR_I16, XOR_I32, XOR_I64, XOR_V128);
|
||||
@@ -3078,8 +3136,15 @@ struct NOT_I64 : Sequence<NOT_I64, I<OPCODE_NOT, I64Op, I64Op>> {
|
||||
};
|
||||
struct NOT_V128 : Sequence<NOT_V128, I<OPCODE_NOT, V128Op, V128Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
// dest = src ^ 0xFFFF...
|
||||
e.vpxor(i.dest, i.src1, e.GetXmmConstPtr(XMMFFFF /* FF... */));
|
||||
|
||||
SimdDomain domain =
|
||||
e.DeduceSimdDomain(i.src1.value);
|
||||
if (domain == SimdDomain::FLOATING) {
|
||||
e.vxorps(i.dest, i.src1, e.GetXmmConstPtr(XMMFFFF /* FF... */));
|
||||
} else {
|
||||
// dest = src ^ 0xFFFF...
|
||||
e.vpxor(i.dest, i.src1, e.GetXmmConstPtr(XMMFFFF /* FF... */));
|
||||
}
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_NOT, NOT_I8, NOT_I16, NOT_I32, NOT_I64, NOT_V128);
|
||||
@@ -3217,7 +3282,7 @@ struct SHR_V128 : Sequence<SHR_V128, I<OPCODE_SHR, V128Op, V128Op, I8Op>> {
|
||||
}
|
||||
e.lea(e.GetNativeParam(0), e.StashXmm(0, i.src1));
|
||||
e.CallNativeSafe(reinterpret_cast<void*>(EmulateShrV128));
|
||||
e.vmovaps(i.dest, e.xmm0);
|
||||
e.vmovdqa(i.dest, e.xmm0);
|
||||
}
|
||||
static __m128i EmulateShrV128(void*, __m128i src1, uint8_t src2) {
|
||||
// Almost all instances are shamt = 1, but non-constant.
|
||||
|
||||
Reference in New Issue
Block a user