use much faster exp2/cos approximations in ffmpeg, large decrease in cpu usage on my machine on decoder thread
properly byteswap r13 for spinlock Add PPCOpcodeBits stub out broken fpscr updating in ppc_hir_builder. it's just code that repeatedly does nothing right now. add note about 0 opcode bytes being executed to ppc_frontend Add assert to check that function end is greater than function start, can happen with malformed functions Disable prefetch and cachecontrol by default, automatic hardware prefetchers already do the job for the most part minor cleanup in simplification_pass, dont loop optimizations, let the pass manager do it for us Add experimental "delay_via_maybeyield" cvar, which uses MaybeYield to "emulate" the db16cyc instruction Add much faster/simpler way of directly calling guest functions, no longer have to do a byte by byte search through the generated code Generate label string ids on the fly Fix unused function warnings for prefetch on clang, fix many other clang warnings Eliminated majority of CallNativeSafes by replacing them with naive generic code paths. ^ Vector rotate left, vector shift left, vector shift right, vector shift arithmetic right, and vector average are included These naive paths are implemented small loops that stash the two inputs to the stack and load them in gprs from there, they are not particularly fast but should be an order of magnitude faster than callnativesafe to a host function, which would involve a call, stashing all volatile registers, an indirect call, potentially setting up a stack frame for the arrays that the inputs get stashed to, the actual operations, a return, loading all volatile registers, a return, etc Added the fast SHR_V128 path back in Implement signed vector average byte, signed vector average word. previously we were emitting no code for them. signed vector average byte appears in many games Fix bug with signed vector average 32, we were doing unsigned shift, turning negative values into big positive ones potentially
This commit is contained in:
@@ -805,22 +805,7 @@ EMITTER_OPCODE_TABLE(OPCODE_VECTOR_SUB, VECTOR_SUB);
|
||||
// ============================================================================
|
||||
// OPCODE_VECTOR_SHL
|
||||
// ============================================================================
|
||||
template <typename T, std::enable_if_t<std::is_integral<T>::value, int> = 0>
|
||||
static __m128i EmulateVectorShl(void*, __m128i src1, __m128i src2) {
|
||||
alignas(16) T value[16 / sizeof(T)];
|
||||
alignas(16) T shamt[16 / sizeof(T)];
|
||||
|
||||
// Load SSE registers into a C array.
|
||||
_mm_store_si128(reinterpret_cast<__m128i*>(value), src1);
|
||||
_mm_store_si128(reinterpret_cast<__m128i*>(shamt), src2);
|
||||
|
||||
for (size_t i = 0; i < (16 / sizeof(T)); ++i) {
|
||||
value[i] = value[i] << (shamt[i] & ((sizeof(T) * 8) - 1));
|
||||
}
|
||||
|
||||
// Store result and return it.
|
||||
return _mm_load_si128(reinterpret_cast<__m128i*>(value));
|
||||
}
|
||||
static XmmConst GetShiftmaskForType(unsigned typ) {
|
||||
if (typ == INT8_TYPE) {
|
||||
return XMMXOPByteShiftMask;
|
||||
@@ -914,28 +899,14 @@ struct VECTOR_SHL_V128
|
||||
}
|
||||
}
|
||||
if (all_same) {
|
||||
// mul by two
|
||||
/*if (seenvalue == 1) {
|
||||
e.vpaddb(i.dest, i.src1, i.src1);
|
||||
} else if (seenvalue == 2) {
|
||||
e.vpaddb(i.dest, i.src1, i.src1);
|
||||
e.vpaddb(i.dest, i.dest, i.dest);
|
||||
} else if (seenvalue == 3) {
|
||||
// mul by 8
|
||||
e.vpaddb(i.dest, i.src1, i.src1);
|
||||
e.vpaddb(i.dest, i.dest, i.dest);
|
||||
e.vpaddb(i.dest, i.dest, i.dest);
|
||||
} else*/
|
||||
{
|
||||
e.vpmovzxbw(e.ymm0, i.src1);
|
||||
e.vpsllw(e.ymm0, e.ymm0, seenvalue);
|
||||
e.vextracti128(e.xmm1, e.ymm0, 1);
|
||||
e.vpmovzxbw(e.ymm0, i.src1);
|
||||
e.vpsllw(e.ymm0, e.ymm0, seenvalue);
|
||||
e.vextracti128(e.xmm1, e.ymm0, 1);
|
||||
|
||||
e.vpshufb(e.xmm0, e.xmm0, e.GetXmmConstPtr(XMMShortsToBytes));
|
||||
e.vpshufb(e.xmm1, e.xmm1, e.GetXmmConstPtr(XMMShortsToBytes));
|
||||
e.vpunpcklqdq(i.dest, e.xmm0, e.xmm1);
|
||||
return;
|
||||
}
|
||||
e.vpshufb(e.xmm0, e.xmm0, e.GetXmmConstPtr(XMMShortsToBytes));
|
||||
e.vpshufb(e.xmm1, e.xmm1, e.GetXmmConstPtr(XMMShortsToBytes));
|
||||
e.vpunpcklqdq(i.dest, e.xmm0, e.xmm1);
|
||||
return;
|
||||
|
||||
} else {
|
||||
e.LoadConstantXmm(e.xmm2, constmask);
|
||||
@@ -966,14 +937,41 @@ struct VECTOR_SHL_V128
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i.src2.is_constant) {
|
||||
e.lea(e.GetNativeParam(1), e.StashConstantXmm(1, i.src2.constant()));
|
||||
|
||||
unsigned stack_offset_src1 = StackLayout::GUEST_SCRATCH;
|
||||
unsigned stack_offset_src2 = StackLayout::GUEST_SCRATCH + 16;
|
||||
|
||||
if (i.src1.is_constant) {
|
||||
e.StashConstantXmm(0, i.src1.constant());
|
||||
stack_offset_src1 = X64Emitter::kStashOffset;
|
||||
} else {
|
||||
e.lea(e.GetNativeParam(1), e.StashXmm(1, i.src2));
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src1], i.src1);
|
||||
}
|
||||
e.lea(e.GetNativeParam(0), e.StashXmm(0, i.src1));
|
||||
e.CallNativeSafe(reinterpret_cast<void*>(EmulateVectorShl<uint8_t>));
|
||||
e.vmovaps(i.dest, e.xmm0);
|
||||
if (i.src2.is_constant) {
|
||||
e.StashConstantXmm(1, i.src2.constant());
|
||||
stack_offset_src2 = X64Emitter::kStashOffset + 16;
|
||||
} else {
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src2], i.src2);
|
||||
}
|
||||
|
||||
Xbyak::Label looper;
|
||||
|
||||
e.xor_(e.edx, e.edx);
|
||||
|
||||
e.L(looper);
|
||||
e.movzx(e.ecx, e.byte[e.rsp + stack_offset_src2 + e.rdx]);
|
||||
|
||||
e.shl(e.byte[e.rsp + stack_offset_src1 + e.rdx], e.cl);
|
||||
|
||||
if (e.IsFeatureEnabled(kX64FlagsIndependentVars)) {
|
||||
e.inc(e.edx);
|
||||
} else {
|
||||
e.add(e.edx, 1);
|
||||
}
|
||||
|
||||
e.cmp(e.edx, 16);
|
||||
e.jnz(looper);
|
||||
e.vmovdqa(i.dest, e.byte[e.rsp + stack_offset_src1]);
|
||||
}
|
||||
static void EmitInt16(X64Emitter& e, const EmitArgType& i) {
|
||||
Xmm src1;
|
||||
@@ -1022,14 +1020,32 @@ struct VECTOR_SHL_V128
|
||||
|
||||
// TODO(benvanik): native version (with shift magic).
|
||||
e.L(emu);
|
||||
|
||||
unsigned stack_offset_src1 = StackLayout::GUEST_SCRATCH;
|
||||
unsigned stack_offset_src2 = StackLayout::GUEST_SCRATCH + 16;
|
||||
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src1], src1);
|
||||
if (i.src2.is_constant) {
|
||||
e.lea(e.GetNativeParam(1), e.StashConstantXmm(1, i.src2.constant()));
|
||||
e.StashConstantXmm(1, i.src2.constant());
|
||||
stack_offset_src2 = X64Emitter::kStashOffset + 16;
|
||||
} else {
|
||||
e.lea(e.GetNativeParam(1), e.StashXmm(1, i.src2));
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src2], i.src2);
|
||||
}
|
||||
e.lea(e.GetNativeParam(0), e.StashXmm(0, src1));
|
||||
e.CallNativeSafe(reinterpret_cast<void*>(EmulateVectorShl<uint16_t>));
|
||||
e.vmovaps(i.dest, e.xmm0);
|
||||
|
||||
Xbyak::Label looper;
|
||||
|
||||
e.xor_(e.edx, e.edx);
|
||||
|
||||
e.L(looper);
|
||||
e.movzx(e.ecx, e.word[e.rsp + stack_offset_src2 + e.rdx]);
|
||||
|
||||
e.shl(e.word[e.rsp + stack_offset_src1 + e.rdx], e.cl);
|
||||
|
||||
e.add(e.edx, 2);
|
||||
|
||||
e.cmp(e.edx, 16);
|
||||
e.jnz(looper);
|
||||
e.vmovdqa(i.dest, e.byte[e.rsp + stack_offset_src1]);
|
||||
|
||||
e.L(end);
|
||||
}
|
||||
@@ -1098,14 +1114,32 @@ struct VECTOR_SHL_V128
|
||||
|
||||
// TODO(benvanik): native version (with shift magic).
|
||||
e.L(emu);
|
||||
|
||||
unsigned stack_offset_src1 = StackLayout::GUEST_SCRATCH;
|
||||
unsigned stack_offset_src2 = StackLayout::GUEST_SCRATCH + 16;
|
||||
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src1], src1);
|
||||
if (i.src2.is_constant) {
|
||||
e.lea(e.GetNativeParam(1), e.StashConstantXmm(1, i.src2.constant()));
|
||||
e.StashConstantXmm(1, i.src2.constant());
|
||||
stack_offset_src2 = X64Emitter::kStashOffset + 16;
|
||||
} else {
|
||||
e.lea(e.GetNativeParam(1), e.StashXmm(1, i.src2));
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src2], i.src2);
|
||||
}
|
||||
e.lea(e.GetNativeParam(0), e.StashXmm(0, src1));
|
||||
e.CallNativeSafe(reinterpret_cast<void*>(EmulateVectorShl<uint32_t>));
|
||||
e.vmovaps(i.dest, e.xmm0);
|
||||
|
||||
Xbyak::Label looper;
|
||||
|
||||
e.xor_(e.edx, e.edx);
|
||||
|
||||
e.L(looper);
|
||||
e.mov(e.ecx, e.dword[e.rsp + stack_offset_src2 + e.rdx]);
|
||||
|
||||
e.shl(e.dword[e.rsp + stack_offset_src1 + e.rdx], e.cl);
|
||||
|
||||
e.add(e.edx, 4);
|
||||
|
||||
e.cmp(e.edx, 16);
|
||||
e.jnz(looper);
|
||||
e.vmovdqa(i.dest, e.byte[e.rsp + stack_offset_src1]);
|
||||
|
||||
e.L(end);
|
||||
}
|
||||
@@ -1116,22 +1150,6 @@ EMITTER_OPCODE_TABLE(OPCODE_VECTOR_SHL, VECTOR_SHL_V128);
|
||||
// ============================================================================
|
||||
// OPCODE_VECTOR_SHR
|
||||
// ============================================================================
|
||||
template <typename T, std::enable_if_t<std::is_integral<T>::value, int> = 0>
|
||||
static __m128i EmulateVectorShr(void*, __m128i src1, __m128i src2) {
|
||||
alignas(16) T value[16 / sizeof(T)];
|
||||
alignas(16) T shamt[16 / sizeof(T)];
|
||||
|
||||
// Load SSE registers into a C array.
|
||||
_mm_store_si128(reinterpret_cast<__m128i*>(value), src1);
|
||||
_mm_store_si128(reinterpret_cast<__m128i*>(shamt), src2);
|
||||
|
||||
for (size_t i = 0; i < (16 / sizeof(T)); ++i) {
|
||||
value[i] = value[i] >> (shamt[i] & ((sizeof(T) * 8) - 1));
|
||||
}
|
||||
|
||||
// Store result and return it.
|
||||
return _mm_load_si128(reinterpret_cast<__m128i*>(value));
|
||||
}
|
||||
|
||||
struct VECTOR_SHR_V128
|
||||
: Sequence<VECTOR_SHR_V128, I<OPCODE_VECTOR_SHR, V128Op, V128Op, V128Op>> {
|
||||
@@ -1179,34 +1197,63 @@ struct VECTOR_SHR_V128
|
||||
}
|
||||
|
||||
static void EmitInt8(X64Emitter& e, const EmitArgType& i) {
|
||||
// TODO(benvanik): native version (with shift magic).
|
||||
if (i.src2.is_constant) {
|
||||
if (e.IsFeatureEnabled(kX64EmitGFNI)) {
|
||||
const auto& shamt = i.src2.constant();
|
||||
bool all_same = true;
|
||||
for (size_t n = 0; n < 16 - n; ++n) {
|
||||
if (shamt.u8[n] != shamt.u8[n + 1]) {
|
||||
all_same = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (all_same) {
|
||||
// Every count is the same, so we can use gf2p8affineqb.
|
||||
const uint8_t shift_amount = shamt.u8[0] & 0b111;
|
||||
const uint64_t shift_matrix = UINT64_C(0x0102040810204080)
|
||||
<< (shift_amount * 8);
|
||||
e.vgf2p8affineqb(i.dest, i.src1,
|
||||
e.StashConstantXmm(0, vec128q(shift_matrix)), 0);
|
||||
return;
|
||||
if (i.src2.is_constant && e.IsFeatureEnabled(kX64EmitGFNI)) {
|
||||
const auto& shamt = i.src2.constant();
|
||||
bool all_same = true;
|
||||
for (size_t n = 0; n < 16 - n; ++n) {
|
||||
if (shamt.u8[n] != shamt.u8[n + 1]) {
|
||||
all_same = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
e.lea(e.GetNativeParam(1), e.StashConstantXmm(1, i.src2.constant()));
|
||||
} else {
|
||||
e.lea(e.GetNativeParam(1), e.StashXmm(1, i.src2));
|
||||
if (all_same) {
|
||||
// Every count is the same, so we can use gf2p8affineqb.
|
||||
const uint8_t shift_amount = shamt.u8[0] & 0b111;
|
||||
const uint64_t shift_matrix = UINT64_C(0x0102040810204080)
|
||||
<< (shift_amount * 8);
|
||||
e.vgf2p8affineqb(i.dest, i.src1,
|
||||
e.StashConstantXmm(0, vec128q(shift_matrix)), 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
e.lea(e.GetNativeParam(0), e.StashXmm(0, i.src1));
|
||||
e.CallNativeSafe(reinterpret_cast<void*>(EmulateVectorShr<uint8_t>));
|
||||
e.vmovaps(i.dest, e.xmm0);
|
||||
unsigned stack_offset_src1 = StackLayout::GUEST_SCRATCH;
|
||||
unsigned stack_offset_src2 = StackLayout::GUEST_SCRATCH + 16;
|
||||
|
||||
if (i.src1.is_constant) {
|
||||
e.StashConstantXmm(0, i.src1.constant());
|
||||
stack_offset_src1 = X64Emitter::kStashOffset;
|
||||
} else {
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src1], i.src1);
|
||||
}
|
||||
if (i.src2.is_constant) {
|
||||
e.StashConstantXmm(1, i.src2.constant());
|
||||
stack_offset_src2 = X64Emitter::kStashOffset + 16;
|
||||
} else {
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src2], i.src2);
|
||||
}
|
||||
|
||||
Xbyak::Label looper;
|
||||
|
||||
e.xor_(e.edx, e.edx);
|
||||
|
||||
e.L(looper);
|
||||
// movzx is to eliminate any possible dep on previous value of rcx at start
|
||||
// of loop
|
||||
e.movzx(e.ecx, e.byte[e.rsp + stack_offset_src2 + e.rdx]);
|
||||
// maybe using a memory operand as the left side isn't the best idea lol,
|
||||
// still better than callnativesafe though agners docs have no timing info
|
||||
// on shx [m], cl so shrug
|
||||
e.shr(e.byte[e.rsp + stack_offset_src1 + e.rdx], e.cl);
|
||||
|
||||
if (e.IsFeatureEnabled(kX64FlagsIndependentVars)) {
|
||||
e.inc(e.edx);
|
||||
} else {
|
||||
e.add(e.edx, 1);
|
||||
}
|
||||
|
||||
e.cmp(e.edx, 16);
|
||||
e.jnz(looper);
|
||||
e.vmovdqa(i.dest, e.byte[e.rsp + stack_offset_src1]);
|
||||
}
|
||||
|
||||
static void EmitInt16(X64Emitter& e, const EmitArgType& i) {
|
||||
@@ -1248,14 +1295,38 @@ struct VECTOR_SHR_V128
|
||||
|
||||
// TODO(benvanik): native version (with shift magic).
|
||||
e.L(emu);
|
||||
if (i.src2.is_constant) {
|
||||
e.lea(e.GetNativeParam(1), e.StashConstantXmm(1, i.src2.constant()));
|
||||
|
||||
unsigned stack_offset_src1 = StackLayout::GUEST_SCRATCH;
|
||||
unsigned stack_offset_src2 = StackLayout::GUEST_SCRATCH + 16;
|
||||
if (i.src1.is_constant) {
|
||||
e.StashConstantXmm(0, i.src1.constant());
|
||||
stack_offset_src1 = X64Emitter::kStashOffset;
|
||||
|
||||
} else {
|
||||
e.lea(e.GetNativeParam(1), e.StashXmm(1, i.src2));
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src1], i.src1);
|
||||
}
|
||||
e.lea(e.GetNativeParam(0), e.StashXmm(0, i.src1));
|
||||
e.CallNativeSafe(reinterpret_cast<void*>(EmulateVectorShr<uint16_t>));
|
||||
e.vmovaps(i.dest, e.xmm0);
|
||||
|
||||
if (i.src2.is_constant) {
|
||||
e.StashConstantXmm(1, i.src2.constant());
|
||||
stack_offset_src2 = X64Emitter::kStashOffset + 16;
|
||||
} else {
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src2], i.src2);
|
||||
}
|
||||
|
||||
Xbyak::Label looper;
|
||||
|
||||
e.xor_(e.edx, e.edx);
|
||||
|
||||
e.L(looper);
|
||||
e.movzx(e.ecx, e.word[e.rsp + stack_offset_src2 + e.rdx]);
|
||||
|
||||
e.shr(e.word[e.rsp + stack_offset_src1 + e.rdx], e.cl);
|
||||
|
||||
e.add(e.edx, 2);
|
||||
|
||||
e.cmp(e.edx, 16);
|
||||
e.jnz(looper);
|
||||
e.vmovdqa(i.dest, e.byte[e.rsp + stack_offset_src1]);
|
||||
|
||||
e.L(end);
|
||||
}
|
||||
@@ -1324,14 +1395,37 @@ struct VECTOR_SHR_V128
|
||||
|
||||
// TODO(benvanik): native version.
|
||||
e.L(emu);
|
||||
if (i.src2.is_constant) {
|
||||
e.lea(e.GetNativeParam(1), e.StashConstantXmm(1, i.src2.constant()));
|
||||
|
||||
unsigned stack_offset_src1 = StackLayout::GUEST_SCRATCH;
|
||||
unsigned stack_offset_src2 = StackLayout::GUEST_SCRATCH + 16;
|
||||
if (i.src1.is_constant) {
|
||||
e.StashConstantXmm(0, i.src1.constant());
|
||||
stack_offset_src1 = X64Emitter::kStashOffset;
|
||||
|
||||
} else {
|
||||
e.lea(e.GetNativeParam(1), e.StashXmm(1, i.src2));
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src1], i.src1);
|
||||
}
|
||||
e.lea(e.GetNativeParam(0), e.StashXmm(0, src1));
|
||||
e.CallNativeSafe(reinterpret_cast<void*>(EmulateVectorShr<uint32_t>));
|
||||
e.vmovaps(i.dest, e.xmm0);
|
||||
|
||||
if (i.src2.is_constant) {
|
||||
e.StashConstantXmm(1, i.src2.constant());
|
||||
stack_offset_src2 = X64Emitter::kStashOffset + 16;
|
||||
} else {
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src2], i.src2);
|
||||
}
|
||||
|
||||
Xbyak::Label looper;
|
||||
|
||||
e.xor_(e.edx, e.edx);
|
||||
|
||||
e.L(looper);
|
||||
e.mov(e.ecx, e.dword[e.rsp + stack_offset_src2 + e.rdx]);
|
||||
e.shr(e.dword[e.rsp + stack_offset_src1 + e.rdx], e.cl);
|
||||
|
||||
e.add(e.edx, 4);
|
||||
|
||||
e.cmp(e.edx, 16);
|
||||
e.jnz(looper);
|
||||
e.vmovdqa(i.dest, e.byte[e.rsp + stack_offset_src1]);
|
||||
|
||||
e.L(end);
|
||||
}
|
||||
@@ -1388,7 +1482,8 @@ struct VECTOR_SHA_V128
|
||||
}
|
||||
|
||||
static void EmitInt8(X64Emitter& e, const EmitArgType& i) {
|
||||
// TODO(benvanik): native version (with shift magic).
|
||||
unsigned stack_offset_src1 = StackLayout::GUEST_SCRATCH;
|
||||
unsigned stack_offset_src2 = StackLayout::GUEST_SCRATCH + 16;
|
||||
if (i.src2.is_constant) {
|
||||
const auto& shamt = i.src2.constant();
|
||||
bool all_same = true;
|
||||
@@ -1399,7 +1494,6 @@ struct VECTOR_SHA_V128
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (e.IsFeatureEnabled(kX64EmitGFNI)) {
|
||||
if (all_same) {
|
||||
// Every count is the same, so we can use gf2p8affineqb.
|
||||
@@ -1412,8 +1506,7 @@ struct VECTOR_SHA_V128
|
||||
e.StashConstantXmm(0, vec128q(shift_matrix)), 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (all_same) {
|
||||
} else if (all_same) {
|
||||
Xmm to_be_shifted = GetInputRegOrConstant(e, i.src1, e.xmm1);
|
||||
|
||||
e.vpmovsxbw(e.xmm0, to_be_shifted); //_mm_srai_epi16 / psraw
|
||||
@@ -1425,14 +1518,41 @@ struct VECTOR_SHA_V128
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
e.lea(e.GetNativeParam(1), e.StashConstantXmm(1, i.src2.constant()));
|
||||
e.StashConstantXmm(1, i.src2.constant());
|
||||
stack_offset_src2 = X64Emitter::kStashOffset + 16;
|
||||
} else {
|
||||
e.lea(e.GetNativeParam(1), e.StashXmm(1, i.src2));
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src2], i.src2);
|
||||
}
|
||||
e.lea(e.GetNativeParam(0), e.StashXmm(0, i.src1));
|
||||
e.CallNativeSafe(reinterpret_cast<void*>(EmulateVectorShr<int8_t>));
|
||||
e.vmovaps(i.dest, e.xmm0);
|
||||
|
||||
if (i.src1.is_constant) {
|
||||
e.StashConstantXmm(0, i.src1.constant());
|
||||
stack_offset_src1 = X64Emitter::kStashOffset;
|
||||
} else {
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src1], i.src1);
|
||||
}
|
||||
|
||||
Xbyak::Label looper;
|
||||
|
||||
e.xor_(e.edx, e.edx);
|
||||
|
||||
e.L(looper);
|
||||
// movzx is to eliminate any possible dep on previous value of rcx at start
|
||||
// of loop
|
||||
e.movzx(e.ecx, e.byte[e.rsp + stack_offset_src2 + e.rdx]);
|
||||
// maybe using a memory operand as the left side isn't the best idea lol,
|
||||
// still better than callnativesafe though agners docs have no timing info
|
||||
// on shx [m], cl so shrug
|
||||
e.sar(e.byte[e.rsp + stack_offset_src1 + e.rdx], e.cl);
|
||||
|
||||
if (e.IsFeatureEnabled(kX64FlagsIndependentVars)) {
|
||||
e.inc(e.edx);
|
||||
} else {
|
||||
e.add(e.edx, 1);
|
||||
}
|
||||
|
||||
e.cmp(e.edx, 16);
|
||||
e.jnz(looper);
|
||||
e.vmovdqa(i.dest, e.byte[e.rsp + stack_offset_src1]);
|
||||
}
|
||||
|
||||
static void EmitInt16(X64Emitter& e, const EmitArgType& i) {
|
||||
@@ -1474,14 +1594,38 @@ struct VECTOR_SHA_V128
|
||||
|
||||
// TODO(benvanik): native version (with shift magic).
|
||||
e.L(emu);
|
||||
if (i.src2.is_constant) {
|
||||
e.lea(e.GetNativeParam(1), e.StashConstantXmm(1, i.src2.constant()));
|
||||
|
||||
unsigned stack_offset_src1 = StackLayout::GUEST_SCRATCH;
|
||||
unsigned stack_offset_src2 = StackLayout::GUEST_SCRATCH + 16;
|
||||
if (i.src1.is_constant) {
|
||||
e.StashConstantXmm(0, i.src1.constant());
|
||||
stack_offset_src1 = X64Emitter::kStashOffset;
|
||||
|
||||
} else {
|
||||
e.lea(e.GetNativeParam(1), e.StashXmm(1, i.src2));
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src1], i.src1);
|
||||
}
|
||||
e.lea(e.GetNativeParam(0), e.StashXmm(0, i.src1));
|
||||
e.CallNativeSafe(reinterpret_cast<void*>(EmulateVectorShr<int16_t>));
|
||||
e.vmovaps(i.dest, e.xmm0);
|
||||
|
||||
if (i.src2.is_constant) {
|
||||
e.StashConstantXmm(1, i.src2.constant());
|
||||
stack_offset_src2 = X64Emitter::kStashOffset + 16;
|
||||
} else {
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src2], i.src2);
|
||||
}
|
||||
|
||||
Xbyak::Label looper;
|
||||
|
||||
e.xor_(e.edx, e.edx);
|
||||
|
||||
e.L(looper);
|
||||
e.movzx(e.ecx, e.word[e.rsp + stack_offset_src2 + e.rdx]);
|
||||
|
||||
e.sar(e.word[e.rsp + stack_offset_src1 + e.rdx], e.cl);
|
||||
|
||||
e.add(e.edx, 2);
|
||||
|
||||
e.cmp(e.edx, 16);
|
||||
e.jnz(looper);
|
||||
e.vmovdqa(i.dest, e.byte[e.rsp + stack_offset_src1]);
|
||||
|
||||
e.L(end);
|
||||
}
|
||||
@@ -1508,9 +1652,9 @@ struct VECTOR_SHA_V128
|
||||
// that happens so we mask.
|
||||
if (i.src2.is_constant) {
|
||||
e.LoadConstantXmm(e.xmm0, i.src2.constant());
|
||||
e.vandps(e.xmm0, e.GetXmmConstPtr(XMMShiftMaskPS));
|
||||
e.vpand(e.xmm0, e.GetXmmConstPtr(XMMShiftMaskPS));
|
||||
} else {
|
||||
e.vandps(e.xmm0, i.src2, e.GetXmmConstPtr(XMMShiftMaskPS));
|
||||
e.vpand(e.xmm0, i.src2, e.GetXmmConstPtr(XMMShiftMaskPS));
|
||||
}
|
||||
e.vpsravd(i.dest, i.src1, e.xmm0);
|
||||
} else {
|
||||
@@ -1535,14 +1679,36 @@ struct VECTOR_SHA_V128
|
||||
|
||||
// TODO(benvanik): native version.
|
||||
e.L(emu);
|
||||
if (i.src2.is_constant) {
|
||||
e.lea(e.GetNativeParam(1), e.StashConstantXmm(1, i.src2.constant()));
|
||||
unsigned stack_offset_src1 = StackLayout::GUEST_SCRATCH;
|
||||
unsigned stack_offset_src2 = StackLayout::GUEST_SCRATCH + 16;
|
||||
if (i.src1.is_constant) {
|
||||
e.StashConstantXmm(0, i.src1.constant());
|
||||
stack_offset_src1 = X64Emitter::kStashOffset;
|
||||
|
||||
} else {
|
||||
e.lea(e.GetNativeParam(1), e.StashXmm(1, i.src2));
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src1], i.src1);
|
||||
}
|
||||
e.lea(e.GetNativeParam(0), e.StashXmm(0, i.src1));
|
||||
e.CallNativeSafe(reinterpret_cast<void*>(EmulateVectorShr<int32_t>));
|
||||
e.vmovaps(i.dest, e.xmm0);
|
||||
|
||||
if (i.src2.is_constant) {
|
||||
e.StashConstantXmm(1, i.src2.constant());
|
||||
stack_offset_src2 = X64Emitter::kStashOffset + 16;
|
||||
} else {
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src2], i.src2);
|
||||
}
|
||||
|
||||
Xbyak::Label looper;
|
||||
|
||||
e.xor_(e.edx, e.edx);
|
||||
|
||||
e.L(looper);
|
||||
e.mov(e.ecx, e.dword[e.rsp + stack_offset_src2 + e.rdx]);
|
||||
e.sar(e.dword[e.rsp + stack_offset_src1 + e.rdx], e.cl);
|
||||
|
||||
e.add(e.edx, 4);
|
||||
|
||||
e.cmp(e.edx, 16);
|
||||
e.jnz(looper);
|
||||
e.vmovdqa(i.dest, e.byte[e.rsp + stack_offset_src1]);
|
||||
|
||||
e.L(end);
|
||||
}
|
||||
@@ -1550,26 +1716,6 @@ struct VECTOR_SHA_V128
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_VECTOR_SHA, VECTOR_SHA_V128);
|
||||
|
||||
// ============================================================================
|
||||
// OPCODE_VECTOR_ROTATE_LEFT
|
||||
// ============================================================================
|
||||
template <typename T, std::enable_if_t<std::is_integral<T>::value, int> = 0>
|
||||
static __m128i EmulateVectorRotateLeft(void*, __m128i src1, __m128i src2) {
|
||||
alignas(16) T value[16 / sizeof(T)];
|
||||
alignas(16) T shamt[16 / sizeof(T)];
|
||||
|
||||
// Load SSE registers into a C array.
|
||||
_mm_store_si128(reinterpret_cast<__m128i*>(value), src1);
|
||||
_mm_store_si128(reinterpret_cast<__m128i*>(shamt), src2);
|
||||
|
||||
for (size_t i = 0; i < (16 / sizeof(T)); ++i) {
|
||||
value[i] = xe::rotate_left<T>(value[i], shamt[i] & ((sizeof(T) * 8) - 1));
|
||||
}
|
||||
|
||||
// Store result and return it.
|
||||
return _mm_load_si128(reinterpret_cast<__m128i*>(value));
|
||||
}
|
||||
|
||||
struct VECTOR_ROTATE_LEFT_V128
|
||||
: Sequence<VECTOR_ROTATE_LEFT_V128,
|
||||
I<OPCODE_VECTOR_ROTATE_LEFT, V128Op, V128Op, V128Op>> {
|
||||
@@ -1594,33 +1740,72 @@ struct VECTOR_ROTATE_LEFT_V128
|
||||
}
|
||||
|
||||
} else {
|
||||
unsigned stack_offset_src1 = StackLayout::GUEST_SCRATCH;
|
||||
unsigned stack_offset_src2 = StackLayout::GUEST_SCRATCH + 16;
|
||||
switch (i.instr->flags) {
|
||||
case INT8_TYPE:
|
||||
// TODO(benvanik): native version (with shift magic).
|
||||
if (i.src2.is_constant) {
|
||||
e.lea(e.GetNativeParam(1),
|
||||
e.StashConstantXmm(1, i.src2.constant()));
|
||||
case INT8_TYPE: {
|
||||
if (i.src1.is_constant) {
|
||||
e.StashConstantXmm(0, i.src1.constant());
|
||||
stack_offset_src1 = X64Emitter::kStashOffset;
|
||||
|
||||
} else {
|
||||
e.lea(e.GetNativeParam(1), e.StashXmm(1, i.src2));
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src1], i.src1);
|
||||
}
|
||||
e.lea(e.GetNativeParam(0), e.StashXmm(0, i.src1));
|
||||
e.CallNativeSafe(
|
||||
reinterpret_cast<void*>(EmulateVectorRotateLeft<uint8_t>));
|
||||
e.vmovaps(i.dest, e.xmm0);
|
||||
break;
|
||||
case INT16_TYPE:
|
||||
// TODO(benvanik): native version (with shift magic).
|
||||
|
||||
if (i.src2.is_constant) {
|
||||
e.lea(e.GetNativeParam(1),
|
||||
e.StashConstantXmm(1, i.src2.constant()));
|
||||
e.StashConstantXmm(1, i.src2.constant());
|
||||
stack_offset_src2 = X64Emitter::kStashOffset + 16;
|
||||
} else {
|
||||
e.lea(e.GetNativeParam(1), e.StashXmm(1, i.src2));
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src2], i.src2);
|
||||
}
|
||||
e.lea(e.GetNativeParam(0), e.StashXmm(0, i.src1));
|
||||
e.CallNativeSafe(
|
||||
reinterpret_cast<void*>(EmulateVectorRotateLeft<uint16_t>));
|
||||
e.vmovaps(i.dest, e.xmm0);
|
||||
break;
|
||||
|
||||
Xbyak::Label rotate_iter;
|
||||
|
||||
e.xor_(e.edx, e.edx);
|
||||
|
||||
e.L(rotate_iter);
|
||||
e.movzx(e.ecx, e.byte[e.rsp + stack_offset_src2 + e.rdx]);
|
||||
|
||||
e.rol(e.byte[e.rsp + stack_offset_src1 + e.rdx], e.cl);
|
||||
|
||||
e.add(e.edx, 1);
|
||||
|
||||
e.cmp(e.edx, 16);
|
||||
e.jnz(rotate_iter);
|
||||
e.vmovdqa(i.dest, e.byte[e.rsp + stack_offset_src1]);
|
||||
|
||||
} break;
|
||||
case INT16_TYPE: {
|
||||
if (i.src1.is_constant) {
|
||||
e.StashConstantXmm(0, i.src1.constant());
|
||||
stack_offset_src1 = X64Emitter::kStashOffset;
|
||||
|
||||
} else {
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src1], i.src1);
|
||||
}
|
||||
|
||||
if (i.src2.is_constant) {
|
||||
e.StashConstantXmm(1, i.src2.constant());
|
||||
stack_offset_src2 = X64Emitter::kStashOffset + 16;
|
||||
} else {
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src2], i.src2);
|
||||
}
|
||||
|
||||
Xbyak::Label rotate_iter;
|
||||
|
||||
e.xor_(e.edx, e.edx);
|
||||
|
||||
e.L(rotate_iter);
|
||||
e.movzx(e.ecx, e.word[e.rsp + stack_offset_src2 + e.rdx]);
|
||||
e.rol(e.word[e.rsp + stack_offset_src1 + e.rdx], e.cl);
|
||||
|
||||
e.add(e.edx, 2);
|
||||
|
||||
e.cmp(e.edx, 16);
|
||||
e.jnz(rotate_iter);
|
||||
e.vmovdqa(i.dest, e.byte[e.rsp + stack_offset_src1]);
|
||||
|
||||
} break;
|
||||
case INT32_TYPE: {
|
||||
if (e.IsFeatureEnabled(kX64EmitAVX512Ortho)) {
|
||||
e.vprolvd(i.dest, i.src1, i.src2);
|
||||
@@ -1638,23 +1823,40 @@ struct VECTOR_ROTATE_LEFT_V128
|
||||
}
|
||||
e.vpsllvd(e.xmm1, i.src1, e.xmm0);
|
||||
// Shift right (to get low bits):
|
||||
e.vmovaps(temp, e.GetXmmConstPtr(XMMPI32));
|
||||
e.vmovdqa(temp, e.GetXmmConstPtr(XMMPI32));
|
||||
e.vpsubd(temp, e.xmm0);
|
||||
e.vpsrlvd(i.dest, i.src1, temp);
|
||||
// Merge:
|
||||
e.vpor(i.dest, e.xmm1);
|
||||
} else {
|
||||
// TODO(benvanik): non-AVX2 native version.
|
||||
if (i.src2.is_constant) {
|
||||
e.lea(e.GetNativeParam(1),
|
||||
e.StashConstantXmm(1, i.src2.constant()));
|
||||
if (i.src1.is_constant) {
|
||||
e.StashConstantXmm(0, i.src1.constant());
|
||||
stack_offset_src1 = X64Emitter::kStashOffset;
|
||||
|
||||
} else {
|
||||
e.lea(e.GetNativeParam(1), e.StashXmm(1, i.src2));
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src1], i.src1);
|
||||
}
|
||||
e.lea(e.GetNativeParam(0), e.StashXmm(0, i.src1));
|
||||
e.CallNativeSafe(
|
||||
reinterpret_cast<void*>(EmulateVectorRotateLeft<uint32_t>));
|
||||
e.vmovaps(i.dest, e.xmm0);
|
||||
|
||||
if (i.src2.is_constant) {
|
||||
e.StashConstantXmm(1, i.src2.constant());
|
||||
stack_offset_src2 = X64Emitter::kStashOffset + 16;
|
||||
} else {
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src2], i.src2);
|
||||
}
|
||||
|
||||
Xbyak::Label rotate_iter;
|
||||
|
||||
e.xor_(e.edx, e.edx);
|
||||
|
||||
e.L(rotate_iter);
|
||||
e.mov(e.ecx, e.dword[e.rsp + stack_offset_src2 + e.rdx]);
|
||||
e.rol(e.dword[e.rsp + stack_offset_src1 + e.rdx], e.cl);
|
||||
|
||||
e.add(e.edx, 4);
|
||||
|
||||
e.cmp(e.edx, 16);
|
||||
e.jnz(rotate_iter);
|
||||
e.vmovdqa(i.dest, e.byte[e.rsp + stack_offset_src1]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1667,80 +1869,120 @@ struct VECTOR_ROTATE_LEFT_V128
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_VECTOR_ROTATE_LEFT, VECTOR_ROTATE_LEFT_V128);
|
||||
|
||||
// ============================================================================
|
||||
// OPCODE_VECTOR_AVERAGE
|
||||
// ============================================================================
|
||||
template <typename T, std::enable_if_t<std::is_integral<T>::value, int> = 0>
|
||||
static __m128i EmulateVectorAverage(void*, __m128i src1, __m128i src2) {
|
||||
alignas(16) T src1v[16 / sizeof(T)];
|
||||
alignas(16) T src2v[16 / sizeof(T)];
|
||||
alignas(16) T value[16 / sizeof(T)];
|
||||
|
||||
// Load SSE registers into a C array.
|
||||
_mm_store_si128(reinterpret_cast<__m128i*>(src1v), src1);
|
||||
_mm_store_si128(reinterpret_cast<__m128i*>(src2v), src2);
|
||||
|
||||
for (size_t i = 0; i < (16 / sizeof(T)); ++i) {
|
||||
auto t = (uint64_t(src1v[i]) + uint64_t(src2v[i]) + 1) / 2;
|
||||
value[i] = T(t);
|
||||
}
|
||||
|
||||
// Store result and return it.
|
||||
return _mm_load_si128(reinterpret_cast<__m128i*>(value));
|
||||
}
|
||||
|
||||
struct VECTOR_AVERAGE
|
||||
: Sequence<VECTOR_AVERAGE,
|
||||
I<OPCODE_VECTOR_AVERAGE, V128Op, V128Op, V128Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
auto i_flags = i.instr->flags;
|
||||
EmitCommutativeBinaryXmmOp(
|
||||
e, i,
|
||||
[&i](X64Emitter& e, const Xmm& dest, const Xmm& src1, const Xmm& src2) {
|
||||
const TypeName part_type =
|
||||
static_cast<TypeName>(i.instr->flags & 0xFF);
|
||||
const uint32_t arithmetic_flags = i.instr->flags >> 8;
|
||||
[i_flags](X64Emitter& e, const Xmm& dest, const Xmm& src1,
|
||||
const Xmm& src2) {
|
||||
const TypeName part_type = static_cast<TypeName>(i_flags & 0xFF);
|
||||
const uint32_t arithmetic_flags = i_flags >> 8;
|
||||
bool is_unsigned = !!(arithmetic_flags & ARITHMETIC_UNSIGNED);
|
||||
unsigned stack_offset_src1 = StackLayout::GUEST_SCRATCH;
|
||||
unsigned stack_offset_src2 = StackLayout::GUEST_SCRATCH + 16;
|
||||
switch (part_type) {
|
||||
case INT8_TYPE:
|
||||
if (is_unsigned) {
|
||||
e.vpavgb(dest, src1, src2);
|
||||
} else {
|
||||
assert_always();
|
||||
// todo: avx2 version or version that sign extends to two __m128
|
||||
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src1], src1);
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src2], src2);
|
||||
|
||||
Xbyak::Label looper;
|
||||
|
||||
e.xor_(e.edx, e.edx);
|
||||
|
||||
e.L(looper);
|
||||
|
||||
e.movsx(e.ecx, e.byte[e.rsp + stack_offset_src2 + e.rdx]);
|
||||
e.movsx(e.eax, e.byte[e.rsp + stack_offset_src1 + e.rdx]);
|
||||
|
||||
e.lea(e.ecx, e.ptr[e.ecx + e.eax + 1]);
|
||||
e.sar(e.ecx, 1);
|
||||
e.mov(e.byte[e.rsp + stack_offset_src1 + e.rdx], e.cl);
|
||||
|
||||
if (e.IsFeatureEnabled(kX64FlagsIndependentVars)) {
|
||||
e.inc(e.edx);
|
||||
} else {
|
||||
e.add(e.edx, 1);
|
||||
}
|
||||
|
||||
e.cmp(e.edx, 16);
|
||||
e.jnz(looper);
|
||||
e.vmovdqa(dest, e.ptr[e.rsp + stack_offset_src1]);
|
||||
}
|
||||
break;
|
||||
case INT16_TYPE:
|
||||
if (is_unsigned) {
|
||||
e.vpavgw(dest, src1, src2);
|
||||
} else {
|
||||
assert_always();
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src1], src1);
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src2], src2);
|
||||
|
||||
Xbyak::Label looper;
|
||||
|
||||
e.xor_(e.edx, e.edx);
|
||||
|
||||
e.L(looper);
|
||||
|
||||
e.movsx(e.ecx, e.word[e.rsp + stack_offset_src2 + e.rdx]);
|
||||
e.movsx(e.eax, e.word[e.rsp + stack_offset_src1 + e.rdx]);
|
||||
|
||||
e.lea(e.ecx, e.ptr[e.ecx + e.eax + 1]);
|
||||
e.sar(e.ecx, 1);
|
||||
e.mov(e.word[e.rsp + stack_offset_src1 + e.rdx], e.cx);
|
||||
|
||||
e.add(e.edx, 2);
|
||||
|
||||
e.cmp(e.edx, 16);
|
||||
e.jnz(looper);
|
||||
e.vmovdqa(dest, e.ptr[e.rsp + stack_offset_src1]);
|
||||
}
|
||||
break;
|
||||
case INT32_TYPE:
|
||||
case INT32_TYPE: {
|
||||
// No 32bit averages in AVX.
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src1], src1);
|
||||
e.vmovdqa(e.ptr[e.rsp + stack_offset_src2], src2);
|
||||
|
||||
Xbyak::Label looper;
|
||||
|
||||
e.xor_(e.edx, e.edx);
|
||||
|
||||
e.L(looper);
|
||||
auto src2_current_ptr =
|
||||
e.dword[e.rsp + stack_offset_src2 + e.rdx];
|
||||
auto src1_current_ptr =
|
||||
e.dword[e.rsp + stack_offset_src1 + e.rdx];
|
||||
|
||||
if (is_unsigned) {
|
||||
if (i.src2.is_constant) {
|
||||
e.lea(e.GetNativeParam(1),
|
||||
e.StashConstantXmm(1, i.src2.constant()));
|
||||
} else {
|
||||
e.lea(e.GetNativeParam(1), e.StashXmm(1, i.src2));
|
||||
}
|
||||
e.lea(e.GetNativeParam(0), e.StashXmm(0, i.src1));
|
||||
e.CallNativeSafe(
|
||||
reinterpret_cast<void*>(EmulateVectorAverage<uint32_t>));
|
||||
e.vmovaps(i.dest, e.xmm0);
|
||||
// implicit zero-ext
|
||||
e.mov(e.ecx, src2_current_ptr);
|
||||
e.mov(e.eax, src1_current_ptr);
|
||||
} else {
|
||||
if (i.src2.is_constant) {
|
||||
e.lea(e.GetNativeParam(1),
|
||||
e.StashConstantXmm(1, i.src2.constant()));
|
||||
} else {
|
||||
e.lea(e.GetNativeParam(1), e.StashXmm(1, i.src2));
|
||||
}
|
||||
e.lea(e.GetNativeParam(0), e.StashXmm(0, i.src1));
|
||||
e.CallNativeSafe(
|
||||
reinterpret_cast<void*>(EmulateVectorAverage<int32_t>));
|
||||
e.vmovaps(i.dest, e.xmm0);
|
||||
e.movsxd(e.rcx, src2_current_ptr);
|
||||
e.movsxd(e.rax, src1_current_ptr);
|
||||
}
|
||||
break;
|
||||
|
||||
e.lea(e.rcx, e.ptr[e.rcx + e.rax + 1]);
|
||||
if (is_unsigned) {
|
||||
e.shr(e.rcx, 1);
|
||||
} else {
|
||||
e.sar(e.rcx, 1);
|
||||
}
|
||||
e.mov(e.dword[e.rsp + stack_offset_src1 + e.rdx], e.ecx);
|
||||
|
||||
e.add(e.edx, 4);
|
||||
|
||||
e.cmp(e.edx, 16);
|
||||
e.jnz(looper);
|
||||
e.vmovdqa(dest, e.ptr[e.rsp + stack_offset_src1]);
|
||||
} break;
|
||||
|
||||
default:
|
||||
assert_unhandled_case(part_type);
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user