nasty commit with a bunch of test code left in, will clean up and pr
Remove the logger_ != nullptr check from shouldlog, it will nearly always be true except on initialization and gets checked later anyway, this shrinks the size of the generated code for some Select specialized vastcpy for current cpu, for now only have paths for MOVDIR64B and generic avx1 Add XE_UNLIKELY/LIKELY if, they map better to the c++ unlikely/likely attributes which we will need to use soon Finished reimplementing STVL/STVR/LVL/LVR as their own opcodes. we now generate far less code for these instructions. this also means optimization passes can be written to simplify/remove/replace these instructions in some cases. Found that a good deal of the X86 we were emitting for these instructions was dead code or redundant. the reduction in generated HIR/x86 should help a lot with compilation times and make function precompilation more feasible as a default Don't static assert in default prefetch impl, in c++20 the assertion will be triggered even without an instantiation Reorder some if/else to prod msvc into ordering the branches optimally. it somewhat worked... Added some notes about which opcodes should be removed/refactored Dispatch in WriteRegister via vector compares for the bounds. still not very optimal, we ought to be checking whether any register in a range may be special A lot of work on trying to optimize writeregister, moved wraparound path into a noinline function based on profiling info Hoist the IsUcodeAnalyzed check out of AnalyzeShader, instead check it before each call. Profiler recorded many hits in the stack frame setup of the function, but none in the actual body of it, so the check is often true but the stack frame setup is run unconditionally Pre-check whether we're about to write a single register from a ring Replace more jump tables from draw_util/texture_info with popcnt based sparse indexing/bit tables/shuffle lookups Place the GPU register file on its own VAD/virtual allocation, it is no longer a member of graphics system
This commit is contained in:
@@ -1068,7 +1068,15 @@ static const vec128_t xmm_consts[] = {
|
||||
/*XMMXOPWordShiftMask*/
|
||||
vec128s(15),
|
||||
/*XMMXOPDwordShiftMask*/
|
||||
vec128i(31)};
|
||||
vec128i(31),
|
||||
/*XMMLVLShuffle*/
|
||||
v128_setr_bytes(3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12),
|
||||
/*XMMLVRCmp16*/
|
||||
vec128b(16),
|
||||
/*XMMSTVLShuffle*/
|
||||
v128_setr_bytes(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15),
|
||||
/* XMMSTVRSwapMask*/
|
||||
vec128b((uint8_t)0x83)};
|
||||
|
||||
void* X64Emitter::FindByteConstantOffset(unsigned bytevalue) {
|
||||
for (auto& vec : xmm_consts) {
|
||||
|
||||
@@ -168,6 +168,10 @@ enum XmmConst {
|
||||
XMMXOPByteShiftMask,
|
||||
XMMXOPWordShiftMask,
|
||||
XMMXOPDwordShiftMask,
|
||||
XMMLVLShuffle,
|
||||
XMMLVRCmp16,
|
||||
XMMSTVLShuffle,
|
||||
XMMSTVRSwapMask // swapwordmask with bit 7 set
|
||||
|
||||
};
|
||||
using amdfx::xopcompare_e;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "xenia/base/memory.h"
|
||||
#include "xenia/cpu/backend/x64/x64_backend.h"
|
||||
#include "xenia/cpu/backend/x64/x64_op.h"
|
||||
#include "xenia/cpu/backend/x64/x64_stack_layout.h"
|
||||
#include "xenia/cpu/backend/x64/x64_tracers.h"
|
||||
#include "xenia/cpu/ppc/ppc_context.h"
|
||||
#include "xenia/cpu/processor.h"
|
||||
@@ -359,6 +360,451 @@ EMITTER_OPCODE_TABLE(OPCODE_ATOMIC_EXCHANGE, ATOMIC_EXCHANGE_I8,
|
||||
ATOMIC_EXCHANGE_I16, ATOMIC_EXCHANGE_I32,
|
||||
ATOMIC_EXCHANGE_I64);
|
||||
|
||||
static __m128i callnativesafe_lvl(void* ctx, void* addr) {
|
||||
uintptr_t uaddr = reinterpret_cast<uintptr_t>(addr);
|
||||
|
||||
uintptr_t bad_offs = uaddr & 0xf;
|
||||
|
||||
uaddr &= ~0xfULL;
|
||||
|
||||
__m128i tempload = _mm_loadu_si128((const __m128i*)uaddr);
|
||||
|
||||
__m128i badhelper =
|
||||
_mm_setr_epi8(3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12);
|
||||
|
||||
__m128i tmpshuf = _mm_add_epi8(badhelper, _mm_set1_epi8((char)bad_offs));
|
||||
|
||||
tmpshuf = _mm_or_si128(tmpshuf, _mm_cmpgt_epi8(tmpshuf, _mm_set1_epi8(15)));
|
||||
return _mm_shuffle_epi8(tempload, tmpshuf);
|
||||
}
|
||||
|
||||
struct LVL_V128 : Sequence<LVL_V128, I<OPCODE_LVL, V128Op, I64Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.mov(e.edx, 0xf);
|
||||
|
||||
e.lea(e.rcx, e.ptr[ComputeMemoryAddress(e, i.src1)]);
|
||||
e.mov(e.eax, 0xf);
|
||||
|
||||
e.and_(e.eax, e.ecx);
|
||||
e.or_(e.rcx, e.rdx);
|
||||
e.vmovd(e.xmm0, e.eax);
|
||||
|
||||
e.xor_(e.rcx, e.rdx);
|
||||
e.vpxor(e.xmm1, e.xmm1);
|
||||
e.vmovdqa(e.xmm3, e.ptr[e.rcx]);
|
||||
e.vmovdqa(e.xmm2, e.GetXmmConstPtr(XMMLVLShuffle));
|
||||
e.vmovdqa(i.dest, e.GetXmmConstPtr(XMMPermuteControl15));
|
||||
e.vpshufb(e.xmm0, e.xmm0, e.xmm1);
|
||||
|
||||
e.vpaddb(e.xmm2, e.xmm0);
|
||||
|
||||
e.vpcmpgtb(e.xmm1, e.xmm2, i.dest);
|
||||
e.vpor(e.xmm0, e.xmm1, e.xmm2);
|
||||
e.vpshufb(i.dest, e.xmm3, e.xmm0);
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_LVL, LVL_V128);
|
||||
|
||||
static __m128i callnativesafe_lvr(void* ctx, void* addr) {
|
||||
uintptr_t uaddr = reinterpret_cast<uintptr_t>(addr);
|
||||
|
||||
uintptr_t bad_offs = uaddr & 0xf;
|
||||
if (!bad_offs) {
|
||||
return _mm_setzero_si128();
|
||||
}
|
||||
uaddr &= ~0xfULL;
|
||||
|
||||
__m128i tempload = _mm_loadu_si128((const __m128i*)uaddr);
|
||||
|
||||
__m128i badhelper =
|
||||
_mm_setr_epi8(3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12);
|
||||
|
||||
__m128i tmpshuf = _mm_add_epi8(badhelper, _mm_set1_epi8((char)bad_offs));
|
||||
|
||||
tmpshuf = _mm_or_si128(tmpshuf, _mm_cmplt_epi8(tmpshuf, _mm_set1_epi8(16)));
|
||||
return _mm_shuffle_epi8(tempload, tmpshuf);
|
||||
}
|
||||
|
||||
struct LVR_V128 : Sequence<LVR_V128, I<OPCODE_LVR, V128Op, I64Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
Xbyak::Label endpoint{};
|
||||
// todo: bailout instead? dont know how frequently the zero skip happens
|
||||
e.vpxor(i.dest, i.dest);
|
||||
e.mov(e.edx, 0xf);
|
||||
|
||||
e.lea(e.rcx, e.ptr[ComputeMemoryAddress(e, i.src1)]);
|
||||
e.mov(e.eax, 0xf);
|
||||
|
||||
e.and_(e.eax, e.ecx);
|
||||
e.jz(endpoint);
|
||||
e.or_(e.rcx, e.rdx);
|
||||
e.vmovd(e.xmm0, e.eax);
|
||||
|
||||
e.xor_(e.rcx, e.rdx);
|
||||
e.vpxor(e.xmm1, e.xmm1);
|
||||
e.vmovdqa(e.xmm3, e.ptr[e.rcx]);
|
||||
e.vmovdqa(e.xmm2, e.GetXmmConstPtr(XMMLVLShuffle));
|
||||
e.vmovdqa(i.dest, e.GetXmmConstPtr(XMMLVRCmp16));
|
||||
e.vpshufb(e.xmm0, e.xmm0, e.xmm1);
|
||||
|
||||
e.vpaddb(e.xmm2, e.xmm0);
|
||||
|
||||
e.vpcmpgtb(e.xmm1, i.dest, e.xmm2);
|
||||
e.vpor(e.xmm0, e.xmm1, e.xmm2);
|
||||
e.vpshufb(i.dest, e.xmm3, e.xmm0);
|
||||
e.L(endpoint);
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_LVR, LVR_V128);
|
||||
|
||||
static __m128i PermuteV128Bytes(__m128i selector, __m128i src1, __m128i src2) {
|
||||
#if 1
|
||||
__m128i selector2 = _mm_xor_si128(selector, _mm_set1_epi8(3));
|
||||
|
||||
__m128i src1_shuf = _mm_shuffle_epi8(src1, selector2);
|
||||
__m128i src2_shuf = _mm_shuffle_epi8(src2, selector2);
|
||||
|
||||
__m128i src2_selection = _mm_cmpgt_epi8(selector2, _mm_set1_epi8(15));
|
||||
|
||||
return _mm_blendv_epi8(src1_shuf, src2_shuf, src2_selection);
|
||||
|
||||
#else
|
||||
// not the issue
|
||||
unsigned char tmpbuffer[32];
|
||||
|
||||
_mm_storeu_si128((__m128i*)tmpbuffer, src1);
|
||||
_mm_storeu_si128((__m128i*)(&tmpbuffer[16]), src2);
|
||||
|
||||
__m128i result;
|
||||
|
||||
for (unsigned i = 0; i < 16; ++i) {
|
||||
result.m128i_u8[i] = tmpbuffer[(selector.m128i_u8[i] ^ 3) & 0x1f];
|
||||
}
|
||||
return result;
|
||||
|
||||
#endif
|
||||
}
|
||||
static __m128i ByteSwap(__m128i input) {
|
||||
return _mm_shuffle_epi8(input, _mm_setr_epi32(0x00010203u, 0x04050607u,
|
||||
0x08090A0Bu, 0x0C0D0E0Fu));
|
||||
}
|
||||
static __m128i LVSR(char input) {
|
||||
__m128i lvsr_table_base = ByteSwap(_mm_setr_epi8(
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31));
|
||||
|
||||
__m128i base_as_vec = _mm_loadu_si128((const __m128i*)&lvsr_table_base);
|
||||
|
||||
__m128i shr_for_offset = _mm_sub_epi8(base_as_vec, _mm_set1_epi8(input));
|
||||
return shr_for_offset;
|
||||
}
|
||||
|
||||
/*
|
||||
Value* eb = f.And(f.Truncate(ea, INT8_TYPE), f.LoadConstantInt8(0xF));
|
||||
// ea &= ~0xF
|
||||
ea = f.And(ea, f.LoadConstantUint64(~0xFull));
|
||||
Value* shrs = f.LoadVectorShr(eb);
|
||||
Value* zerovec = f.LoadZeroVec128();
|
||||
|
||||
// v = (old & ~mask) | ((new >> eb) & mask)
|
||||
Value* new_value = f.Permute(shrs, zerovec, f.LoadVR(vd), INT8_TYPE);
|
||||
Value* old_value = f.ByteSwap(f.Load(ea, VEC128_TYPE));
|
||||
|
||||
// mask = FFFF... >> eb
|
||||
Value* mask = f.Permute(shrs, zerovec, f.Not(zerovec), INT8_TYPE);
|
||||
|
||||
Value* v = f.Select(mask, old_value, new_value);
|
||||
// ea &= ~0xF (handled above)
|
||||
f.Store(ea, f.ByteSwap(v));
|
||||
*/
|
||||
#if 0
|
||||
|
||||
static void callnativesafe_stvl(void* ctx, void* addr, __m128i* value) {
|
||||
uintptr_t uaddr = reinterpret_cast<uintptr_t>(addr);
|
||||
|
||||
uintptr_t bad_offs = uaddr & 0xf;
|
||||
|
||||
uaddr &= ~0xfULL;
|
||||
|
||||
__m128i tempload = ByteSwap(_mm_loadu_si128((const __m128i*)uaddr));
|
||||
|
||||
__m128i our_value_to_store = _mm_loadu_si128(value);
|
||||
|
||||
__m128i shr_for_offset = LVSR((char)bad_offs);
|
||||
|
||||
__m128i permuted_us =
|
||||
PermuteV128Bytes(shr_for_offset, _mm_setzero_si128(), our_value_to_store);
|
||||
//__m128i mask = PermuteV128Bytes(shr_for_offset, _mm_setzero_si128(),
|
||||
// _mm_set1_epi8((char)0xff));
|
||||
|
||||
__m128i mask = _mm_cmpgt_epi8(shr_for_offset, _mm_set1_epi8(15));
|
||||
__m128i blended_input_and_memory =
|
||||
_mm_blendv_epi8(tempload, permuted_us, mask);
|
||||
|
||||
__m128i swapped_final_result = ByteSwap(blended_input_and_memory);
|
||||
|
||||
_mm_storeu_si128((__m128i*)uaddr, swapped_final_result);
|
||||
}
|
||||
#else
|
||||
static void callnativesafe_stvl(void* ctx, void* addr, __m128i* value) {
|
||||
uintptr_t uaddr = reinterpret_cast<uintptr_t>(addr);
|
||||
|
||||
uintptr_t bad_offs = uaddr & 0xf;
|
||||
|
||||
uaddr &= ~0xfULL;
|
||||
|
||||
__m128i tempload = _mm_loadu_si128((const __m128i*)uaddr);
|
||||
|
||||
__m128i our_value_to_store = _mm_loadu_si128(value);
|
||||
|
||||
__m128i shr_for_offset;
|
||||
{
|
||||
__m128i lvsr_table_base =
|
||||
_mm_sub_epi8(_mm_setr_epi8(16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
|
||||
27, 28, 29, 30, 31),
|
||||
_mm_set1_epi8(16));
|
||||
shr_for_offset =
|
||||
_mm_sub_epi8(lvsr_table_base, _mm_set1_epi8((char)bad_offs));
|
||||
}
|
||||
__m128i permuted_us;
|
||||
{
|
||||
__m128i selector2 = _mm_xor_si128(shr_for_offset, _mm_set1_epi8(3));
|
||||
|
||||
__m128i src2_shuf = _mm_shuffle_epi8(our_value_to_store, selector2);
|
||||
|
||||
permuted_us = src2_shuf;
|
||||
}
|
||||
|
||||
__m128i blended_input_and_memory =
|
||||
_mm_blendv_epi8(permuted_us, tempload, shr_for_offset);
|
||||
|
||||
__m128i swapped_final_result = blended_input_and_memory;
|
||||
|
||||
_mm_storeu_si128((__m128i*)uaddr, swapped_final_result);
|
||||
}
|
||||
static void callnativesafe_stvl_experiment(void* addr, __m128i* value) {
|
||||
uintptr_t uaddr = reinterpret_cast<uintptr_t>(addr);
|
||||
|
||||
uintptr_t bad_offs = uaddr & 0xf;
|
||||
|
||||
uaddr &= ~0xfULL;
|
||||
|
||||
__m128i tempload = _mm_loadu_si128((const __m128i*)uaddr);
|
||||
|
||||
__m128i our_value_to_store = _mm_loadu_si128(value);
|
||||
|
||||
__m128i shr_for_offset;
|
||||
{
|
||||
__m128i lvsr_table_base =
|
||||
_mm_sub_epi8(_mm_setr_epi8(16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
|
||||
27, 28, 29, 30, 31),
|
||||
_mm_set1_epi8(16));
|
||||
|
||||
// lvsr_table_base = _mm_xor_si128(lvsr_table_base, _mm_set1_epi8(3));
|
||||
// lvsr_table_base = ByteSwap(lvsr_table_base);
|
||||
shr_for_offset =
|
||||
_mm_sub_epi8(lvsr_table_base, _mm_set1_epi8((char)bad_offs));
|
||||
}
|
||||
__m128i permuted_us;
|
||||
{
|
||||
shr_for_offset = _mm_xor_si128(shr_for_offset, _mm_set1_epi8(3));
|
||||
|
||||
__m128i src2_shuf = _mm_shuffle_epi8(our_value_to_store, shr_for_offset);
|
||||
|
||||
permuted_us = src2_shuf;
|
||||
}
|
||||
|
||||
__m128i blended_input_and_memory =
|
||||
_mm_blendv_epi8(permuted_us, tempload, shr_for_offset);
|
||||
|
||||
__m128i swapped_final_result = blended_input_and_memory;
|
||||
|
||||
_mm_storeu_si128((__m128i*)uaddr, swapped_final_result);
|
||||
}
|
||||
|
||||
#endif
|
||||
struct STVL_V128 : Sequence<STVL_V128, I<OPCODE_STVL, VoidOp, I64Op, V128Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
#if 0
|
||||
e.lea(e.GetNativeParam(0), e.ptr[ComputeMemoryAddress(e, i.src1)]);
|
||||
Xmm src2 = GetInputRegOrConstant(e, i.src2, e.xmm1);
|
||||
|
||||
e.lea(e.GetNativeParam(1), e.StashXmm(0, src2));
|
||||
e.CallNativeSafe((void*)callnativesafe_stvl);
|
||||
|
||||
#else
|
||||
e.mov(e.ecx, 15);
|
||||
e.mov(e.edx, e.ecx);
|
||||
e.lea(e.rax, e.ptr[ComputeMemoryAddress(e, i.src1)]);
|
||||
e.and_(e.ecx, e.eax);
|
||||
e.vmovd(e.xmm0, e.ecx);
|
||||
e.not_(e.rdx);
|
||||
e.and_(e.rax, e.rdx);
|
||||
e.vmovdqa(e.xmm1, e.GetXmmConstPtr(XMMSTVLShuffle));
|
||||
// e.vmovdqa(e.xmm2, e.GetXmmConstPtr(XMMSwapWordMask));
|
||||
if (e.IsFeatureEnabled(kX64EmitAVX2)) {
|
||||
e.vpbroadcastb(e.xmm3, e.xmm0);
|
||||
} else {
|
||||
e.vpshufb(e.xmm3, e.xmm0, e.GetXmmConstPtr(XMMZero));
|
||||
}
|
||||
e.vpsubb(e.xmm0, e.xmm1, e.xmm3);
|
||||
e.vpxor(e.xmm1, e.xmm0,
|
||||
e.GetXmmConstPtr(XMMSwapWordMask)); // xmm1 from now on will be our
|
||||
// selector for blend/shuffle
|
||||
// we can reuse xmm0, xmm2 and xmm3 now
|
||||
// e.vmovdqa(e.xmm0, e.ptr[e.rax]);
|
||||
|
||||
Xmm src2 = GetInputRegOrConstant(e, i.src2, e.xmm0);
|
||||
|
||||
e.vpshufb(e.xmm2, src2, e.xmm1);
|
||||
e.vpblendvb(e.xmm3, e.xmm2, e.ptr[e.rax], e.xmm1);
|
||||
e.vmovdqa(e.ptr[e.rax], e.xmm3);
|
||||
|
||||
#endif
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_STVL, STVL_V128);
|
||||
|
||||
/*
|
||||
Value* eb = f.And(f.Truncate(ea, INT8_TYPE), f.LoadConstantInt8(0xF));
|
||||
// Skip if %16=0 (no data to store).
|
||||
auto skip_label = f.NewLabel();
|
||||
f.BranchFalse(eb, skip_label);
|
||||
// ea &= ~0xF
|
||||
// NOTE: need to recalculate ea and eb because after Branch we start a new
|
||||
// block and we can't use their previous instantiation in the new block
|
||||
ea = CalculateEA_0(f, ra, rb);
|
||||
eb = f.And(f.Truncate(ea, INT8_TYPE), f.LoadConstantInt8(0xF));
|
||||
ea = f.And(ea, f.LoadConstantUint64(~0xFull));
|
||||
Value* shrs = f.LoadVectorShr(eb);
|
||||
Value* zerovec = f.LoadZeroVec128();
|
||||
// v = (old & ~mask) | ((new << eb) & mask)
|
||||
Value* new_value = f.Permute(shrs, f.LoadVR(vd), zerovec, INT8_TYPE);
|
||||
Value* old_value = f.ByteSwap(f.Load(ea, VEC128_TYPE));
|
||||
// mask = ~FFFF... >> eb
|
||||
Value* mask = f.Permute(shrs, f.Not(zerovec), zerovec, INT8_TYPE);
|
||||
Value* v = f.Select(mask, old_value, new_value);
|
||||
// ea &= ~0xF (handled above)
|
||||
f.Store(ea, f.ByteSwap(v));
|
||||
f.MarkLabel(skip_label);
|
||||
*/
|
||||
#if 0
|
||||
static void callnativesafe_stvr(void* ctx, void* addr, __m128i* value) {
|
||||
uintptr_t uaddr = reinterpret_cast<uintptr_t>(addr);
|
||||
|
||||
uintptr_t bad_offs = uaddr & 0xf;
|
||||
if (!bad_offs) {
|
||||
return;
|
||||
}
|
||||
uaddr &= ~0xfULL;
|
||||
|
||||
__m128i tempload = ByteSwap(_mm_loadu_si128((const __m128i*)uaddr));
|
||||
|
||||
__m128i our_value_to_store = _mm_loadu_si128(value);
|
||||
|
||||
__m128i shr_for_offset = LVSR((char)bad_offs);
|
||||
|
||||
__m128i permuted_us = PermuteV128Bytes(
|
||||
shr_for_offset, our_value_to_store, _mm_setzero_si128() );
|
||||
__m128i mask = PermuteV128Bytes(
|
||||
shr_for_offset, _mm_set1_epi8((char)0xff) ,_mm_setzero_si128()
|
||||
);
|
||||
|
||||
//__m128i mask = _mm_cmpgt_epi8(shr_for_offset, _mm_set1_epi8(15));
|
||||
__m128i blended_input_and_memory =
|
||||
_mm_blendv_epi8(tempload, permuted_us, mask);
|
||||
|
||||
__m128i swapped_final_result = ByteSwap(blended_input_and_memory);
|
||||
|
||||
_mm_storeu_si128((__m128i*)uaddr, swapped_final_result);
|
||||
}
|
||||
#else
|
||||
static void callnativesafe_stvr(void* ctx, void* addr, __m128i* value) {
|
||||
uintptr_t uaddr = reinterpret_cast<uintptr_t>(addr);
|
||||
|
||||
uintptr_t bad_offs = uaddr & 0xf;
|
||||
|
||||
uaddr &= ~0xfULL;
|
||||
if (!bad_offs) {
|
||||
return;
|
||||
}
|
||||
__m128i tempload = _mm_loadu_si128((const __m128i*)uaddr);
|
||||
|
||||
__m128i our_value_to_store = _mm_loadu_si128(value);
|
||||
|
||||
__m128i shr_for_offset;
|
||||
{
|
||||
__m128i lvsr_table_base =
|
||||
_mm_sub_epi8(_mm_setr_epi8(16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
|
||||
27, 28, 29, 30, 31),
|
||||
_mm_set1_epi8(16));
|
||||
|
||||
// lvsr_table_base = _mm_xor_si128(lvsr_table_base, _mm_set1_epi8(3));
|
||||
// lvsr_table_base = ByteSwap(lvsr_table_base);
|
||||
shr_for_offset =
|
||||
_mm_sub_epi8(lvsr_table_base, _mm_set1_epi8((char)bad_offs));
|
||||
}
|
||||
__m128i permuted_us;
|
||||
{
|
||||
shr_for_offset = _mm_xor_si128(shr_for_offset, _mm_set1_epi8((char)0x83));
|
||||
|
||||
__m128i src2_shuf = _mm_shuffle_epi8(our_value_to_store, shr_for_offset);
|
||||
|
||||
permuted_us = src2_shuf;
|
||||
}
|
||||
|
||||
__m128i blended_input_and_memory =
|
||||
_mm_blendv_epi8(permuted_us, tempload, shr_for_offset);
|
||||
|
||||
__m128i swapped_final_result = blended_input_and_memory;
|
||||
|
||||
_mm_storeu_si128((__m128i*)uaddr, swapped_final_result);
|
||||
}
|
||||
#endif
|
||||
struct STVR_V128 : Sequence<STVR_V128, I<OPCODE_STVR, VoidOp, I64Op, V128Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
#if 0
|
||||
e.lea(e.GetNativeParam(0), e.ptr[ComputeMemoryAddress(e, i.src1)]);
|
||||
Xmm src2 = GetInputRegOrConstant(e, i.src2, e.xmm1);
|
||||
|
||||
e.lea(e.GetNativeParam(1), e.StashXmm(0, src2));
|
||||
e.CallNativeSafe((void*)callnativesafe_stvr);
|
||||
|
||||
#else
|
||||
Xbyak::Label skipper{};
|
||||
e.mov(e.ecx, 15);
|
||||
e.mov(e.edx, e.ecx);
|
||||
e.lea(e.rax, e.ptr[ComputeMemoryAddress(e, i.src1)]);
|
||||
e.and_(e.ecx, e.eax);
|
||||
e.jz(skipper);
|
||||
e.vmovd(e.xmm0, e.ecx);
|
||||
e.not_(e.rdx);
|
||||
e.and_(e.rax, e.rdx);
|
||||
e.vmovdqa(e.xmm1, e.GetXmmConstPtr(XMMSTVLShuffle));
|
||||
// todo: maybe a table lookup might be a better idea for getting the
|
||||
// shuffle/blend
|
||||
// e.vmovdqa(e.xmm2, e.GetXmmConstPtr(XMMSTVRSwapMask));
|
||||
if (e.IsFeatureEnabled(kX64EmitAVX2)) {
|
||||
e.vpbroadcastb(e.xmm3, e.xmm0);
|
||||
} else {
|
||||
e.vpshufb(e.xmm3, e.xmm0, e.GetXmmConstPtr(XMMZero));
|
||||
}
|
||||
e.vpsubb(e.xmm0, e.xmm1, e.xmm3);
|
||||
e.vpxor(e.xmm1, e.xmm0,
|
||||
e.GetXmmConstPtr(XMMSTVRSwapMask)); // xmm1 from now on will be our
|
||||
// selector for blend/shuffle
|
||||
// we can reuse xmm0, xmm2 and xmm3 now
|
||||
// e.vmovdqa(e.xmm0, e.ptr[e.rax]);
|
||||
|
||||
Xmm src2 = GetInputRegOrConstant(e, i.src2, e.xmm0);
|
||||
|
||||
e.vpshufb(e.xmm2, src2, e.xmm1);
|
||||
e.vpblendvb(e.xmm3, e.xmm2, e.ptr[e.rax], e.xmm1);
|
||||
e.vmovdqa(e.ptr[e.rax], e.xmm3);
|
||||
e.L(skipper);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_STVR, STVR_V128);
|
||||
// ============================================================================
|
||||
// OPCODE_ATOMIC_COMPARE_EXCHANGE
|
||||
// ============================================================================
|
||||
|
||||
@@ -122,16 +122,16 @@ enum Opcode {
|
||||
OPCODE_NOP,
|
||||
OPCODE_SOURCE_OFFSET,
|
||||
OPCODE_DEBUG_BREAK,
|
||||
OPCODE_DEBUG_BREAK_TRUE,
|
||||
OPCODE_DEBUG_BREAK_TRUE, // remove, branch and break
|
||||
OPCODE_TRAP,
|
||||
OPCODE_TRAP_TRUE,
|
||||
OPCODE_TRAP_TRUE, // remove, branch and trap
|
||||
OPCODE_CALL,
|
||||
OPCODE_CALL_TRUE,
|
||||
OPCODE_CALL_TRUE, // remove, branch and call
|
||||
OPCODE_CALL_INDIRECT,
|
||||
OPCODE_CALL_INDIRECT_TRUE,
|
||||
OPCODE_CALL_INDIRECT_TRUE, // remove, branch and call
|
||||
OPCODE_CALL_EXTERN,
|
||||
OPCODE_RETURN,
|
||||
OPCODE_RETURN_TRUE,
|
||||
OPCODE_RETURN_TRUE, // remove, branch and return
|
||||
OPCODE_SET_RETURN_ADDRESS,
|
||||
OPCODE_BRANCH,
|
||||
OPCODE_BRANCH_TRUE,
|
||||
@@ -194,8 +194,8 @@ enum Opcode {
|
||||
// 0x4F7FFD00.
|
||||
OPCODE_VECTOR_CONVERT_I2F,
|
||||
OPCODE_VECTOR_CONVERT_F2I,
|
||||
OPCODE_LOAD_VECTOR_SHL,
|
||||
OPCODE_LOAD_VECTOR_SHR,
|
||||
OPCODE_LOAD_VECTOR_SHL, // remove, use arithmetic instead
|
||||
OPCODE_LOAD_VECTOR_SHR, // remove, use arithmetic instead
|
||||
OPCODE_LOAD_CLOCK,
|
||||
OPCODE_LOAD_LOCAL,
|
||||
OPCODE_STORE_LOCAL,
|
||||
@@ -204,8 +204,8 @@ enum Opcode {
|
||||
OPCODE_CONTEXT_BARRIER,
|
||||
OPCODE_LOAD_MMIO,
|
||||
OPCODE_STORE_MMIO,
|
||||
OPCODE_LOAD_OFFSET,
|
||||
OPCODE_STORE_OFFSET,
|
||||
OPCODE_LOAD_OFFSET, // remove, use add instead?
|
||||
OPCODE_STORE_OFFSET, // remove, use add instead?
|
||||
OPCODE_LOAD,
|
||||
OPCODE_STORE,
|
||||
// chrispy: todo: implement, our current codegen for the unaligned loads is
|
||||
@@ -222,7 +222,7 @@ enum Opcode {
|
||||
OPCODE_MIN,
|
||||
OPCODE_VECTOR_MIN,
|
||||
OPCODE_SELECT,
|
||||
OPCODE_IS_NAN,
|
||||
OPCODE_IS_NAN, // remove? compare_eq with self instead
|
||||
OPCODE_COMPARE_EQ,
|
||||
OPCODE_COMPARE_NE,
|
||||
OPCODE_COMPARE_SLT,
|
||||
@@ -233,14 +233,14 @@ enum Opcode {
|
||||
OPCODE_COMPARE_ULE,
|
||||
OPCODE_COMPARE_UGT,
|
||||
OPCODE_COMPARE_UGE,
|
||||
OPCODE_DID_SATURATE,
|
||||
OPCODE_DID_SATURATE, // remove, use different way of tracking saturation
|
||||
OPCODE_VECTOR_COMPARE_EQ,
|
||||
OPCODE_VECTOR_COMPARE_SGT,
|
||||
OPCODE_VECTOR_COMPARE_SGE,
|
||||
OPCODE_VECTOR_COMPARE_UGT,
|
||||
OPCODE_VECTOR_COMPARE_UGE,
|
||||
OPCODE_ADD,
|
||||
OPCODE_ADD_CARRY,
|
||||
OPCODE_ADD_CARRY, // remove, instead zero extend carry and add
|
||||
OPCODE_VECTOR_ADD,
|
||||
OPCODE_SUB,
|
||||
OPCODE_VECTOR_SUB,
|
||||
@@ -261,7 +261,7 @@ enum Opcode {
|
||||
OPCODE_DOT_PRODUCT_3,
|
||||
OPCODE_DOT_PRODUCT_4,
|
||||
OPCODE_AND,
|
||||
OPCODE_AND_NOT,
|
||||
OPCODE_AND_NOT, // remove, Not+And instead
|
||||
OPCODE_OR,
|
||||
OPCODE_XOR,
|
||||
OPCODE_NOT,
|
||||
@@ -271,8 +271,8 @@ enum Opcode {
|
||||
OPCODE_VECTOR_SHR,
|
||||
OPCODE_SHA,
|
||||
OPCODE_VECTOR_SHA,
|
||||
OPCODE_ROTATE_LEFT,
|
||||
OPCODE_VECTOR_ROTATE_LEFT,
|
||||
OPCODE_ROTATE_LEFT, // remove, left/right shift combo instead
|
||||
OPCODE_VECTOR_ROTATE_LEFT, // eliminate, replace with left/right shift combo
|
||||
OPCODE_VECTOR_AVERAGE,
|
||||
OPCODE_BYTE_SWAP,
|
||||
OPCODE_CNTLZ,
|
||||
@@ -281,7 +281,8 @@ enum Opcode {
|
||||
OPCODE_SPLAT,
|
||||
OPCODE_PERMUTE,
|
||||
OPCODE_SWIZZLE,
|
||||
OPCODE_PACK,
|
||||
OPCODE_PACK, // break up into smaller operations and add a float16 convert
|
||||
// opcode
|
||||
OPCODE_UNPACK,
|
||||
OPCODE_ATOMIC_EXCHANGE,
|
||||
OPCODE_ATOMIC_COMPARE_EXCHANGE,
|
||||
|
||||
@@ -208,6 +208,7 @@ int InstrEmit_stvxl128(PPCHIRBuilder& f, const InstrData& i) {
|
||||
int InstrEmit_lvlx_(PPCHIRBuilder& f, const InstrData& i, uint32_t vd,
|
||||
uint32_t ra, uint32_t rb) {
|
||||
Value* ea = CalculateEA_0(f, ra, rb);
|
||||
#if 0
|
||||
Value* eb = f.And(f.Truncate(ea, INT8_TYPE), f.LoadConstantInt8(0xF));
|
||||
// ea &= ~0xF
|
||||
ea = f.And(ea, f.LoadConstantUint64(~0xFull));
|
||||
@@ -216,6 +217,11 @@ int InstrEmit_lvlx_(PPCHIRBuilder& f, const InstrData& i, uint32_t vd,
|
||||
f.LoadZeroVec128(), INT8_TYPE);
|
||||
f.StoreVR(vd, v);
|
||||
return 0;
|
||||
#else
|
||||
Value* val = f.LoadVectorLeft(ea);
|
||||
f.StoreVR(vd, val);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
int InstrEmit_lvlx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
return InstrEmit_lvlx_(f, i, i.X.RT, i.X.RA, i.X.RB);
|
||||
@@ -237,6 +243,7 @@ int InstrEmit_lvrx_(PPCHIRBuilder& f, const InstrData& i, uint32_t vd,
|
||||
// buffer, which sometimes may be nothing and hang off the end of the valid
|
||||
// page area. We still need to zero the resulting register, though.
|
||||
Value* ea = CalculateEA_0(f, ra, rb);
|
||||
#if 0
|
||||
Value* eb = f.And(f.Truncate(ea, INT8_TYPE), f.LoadConstantInt8(0xF));
|
||||
// Skip if %16=0 (just load zero).
|
||||
auto load_label = f.NewLabel();
|
||||
@@ -257,6 +264,11 @@ int InstrEmit_lvrx_(PPCHIRBuilder& f, const InstrData& i, uint32_t vd,
|
||||
f.StoreVR(vd, v);
|
||||
f.MarkLabel(end_label);
|
||||
return 0;
|
||||
#else
|
||||
Value* val = f.LoadVectorRight(ea);
|
||||
f.StoreVR(vd, val);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
int InstrEmit_lvrx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
return InstrEmit_lvrx_(f, i, i.X.RT, i.X.RA, i.X.RB);
|
||||
@@ -275,7 +287,9 @@ int InstrEmit_stvlx_(PPCHIRBuilder& f, const InstrData& i, uint32_t vd,
|
||||
uint32_t ra, uint32_t rb) {
|
||||
// NOTE: if eb == 0 (so 16b aligned) this equals new_value
|
||||
// we could optimize this to prevent the other load/mask, in that case.
|
||||
|
||||
Value* ea = CalculateEA_0(f, ra, rb);
|
||||
#if 0
|
||||
Value* eb = f.And(f.Truncate(ea, INT8_TYPE), f.LoadConstantInt8(0xF));
|
||||
// ea &= ~0xF
|
||||
ea = f.And(ea, f.LoadConstantUint64(~0xFull));
|
||||
@@ -283,6 +297,8 @@ int InstrEmit_stvlx_(PPCHIRBuilder& f, const InstrData& i, uint32_t vd,
|
||||
Value* zerovec = f.LoadZeroVec128();
|
||||
|
||||
// v = (old & ~mask) | ((new >> eb) & mask)
|
||||
|
||||
Value* mask = f.Permute(shrs, zerovec, f.Not(zerovec), INT8_TYPE);
|
||||
Value* new_value = f.Permute(shrs, zerovec, f.LoadVR(vd), INT8_TYPE);
|
||||
Value* old_value = f.ByteSwap(f.Load(ea, VEC128_TYPE));
|
||||
/*
|
||||
@@ -291,11 +307,16 @@ int InstrEmit_stvlx_(PPCHIRBuilder& f, const InstrData& i, uint32_t vd,
|
||||
here looks as if it might make more sense as a comparison (
|
||||
*/
|
||||
// mask = FFFF... >> eb
|
||||
Value* mask = f.Permute(shrs, zerovec, f.Not(zerovec), INT8_TYPE);
|
||||
|
||||
|
||||
Value* v = f.Select(mask, old_value, new_value);
|
||||
// ea &= ~0xF (handled above)
|
||||
f.Store(ea, f.ByteSwap(v));
|
||||
#else
|
||||
|
||||
Value* vdr = f.LoadVR(vd);
|
||||
f.StoreVectorLeft(ea, vdr);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
int InstrEmit_stvlx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
@@ -318,6 +339,7 @@ int InstrEmit_stvrx_(PPCHIRBuilder& f, const InstrData& i, uint32_t vd,
|
||||
// buffer, which sometimes may be nothing and hang off the end of the valid
|
||||
// page area.
|
||||
Value* ea = CalculateEA_0(f, ra, rb);
|
||||
#if 0
|
||||
Value* eb = f.And(f.Truncate(ea, INT8_TYPE), f.LoadConstantInt8(0xF));
|
||||
// Skip if %16=0 (no data to store).
|
||||
auto skip_label = f.NewLabel();
|
||||
@@ -339,6 +361,10 @@ int InstrEmit_stvrx_(PPCHIRBuilder& f, const InstrData& i, uint32_t vd,
|
||||
// ea &= ~0xF (handled above)
|
||||
f.Store(ea, f.ByteSwap(v));
|
||||
f.MarkLabel(skip_label);
|
||||
#else
|
||||
Value* vdr = f.LoadVR(vd);
|
||||
f.StoreVectorRight(ea, vdr);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
int InstrEmit_stvrx(PPCHIRBuilder& f, const InstrData& i) {
|
||||
|
||||
Reference in New Issue
Block a user