Minor decoder optimizations, kernel fixes, cpu backend fixes
This commit is contained in:
@@ -93,7 +93,8 @@ class X64CodeCache : public CodeCache {
|
||||
// This is picked to be high enough to cover whatever we can reasonably
|
||||
// expect. If we hit issues with this it probably means some corner case
|
||||
// in analysis triggering.
|
||||
static const size_t kMaximumFunctionCount = 100000;
|
||||
//chrispy: raised this, some games that were compiled with low optimization levels can exceed this
|
||||
static const size_t kMaximumFunctionCount = 1000000;
|
||||
|
||||
struct UnwindReservation {
|
||||
size_t data_size = 0;
|
||||
|
||||
@@ -209,7 +209,16 @@ bool Win32X64CodeCache::Initialize() {
|
||||
|
||||
Win32X64CodeCache::UnwindReservation
|
||||
Win32X64CodeCache::RequestUnwindReservation(uint8_t* entry_address) {
|
||||
#if defined(NDEBUG)
|
||||
if (unwind_table_count_ >= kMaximumFunctionCount) {
|
||||
// we should not just be ignoring this in release if it happens
|
||||
xe::FatalError(
|
||||
"Unwind table count (unwind_table_count_) exceeded maximum! Please report this to "
|
||||
"Xenia/Canary developers");
|
||||
}
|
||||
#else
|
||||
assert_false(unwind_table_count_ >= kMaximumFunctionCount);
|
||||
#endif
|
||||
UnwindReservation unwind_reservation;
|
||||
unwind_reservation.data_size = xe::round_up(kUnwindInfoSize, 16);
|
||||
unwind_reservation.table_slot = unwind_table_count_++;
|
||||
|
||||
@@ -46,10 +46,6 @@ DEFINE_bool(ignore_undefined_externs, true,
|
||||
DEFINE_bool(emit_source_annotations, false,
|
||||
"Add extra movs and nops to make disassembly easier to read.",
|
||||
"CPU");
|
||||
DEFINE_bool(resolve_rel32_guest_calls, true,
|
||||
"Experimental optimization, directly call already resolved "
|
||||
"functions via x86 rel32 call/jmp",
|
||||
"CPU");
|
||||
|
||||
DEFINE_bool(enable_incorrect_roundingmode_behavior, false,
|
||||
"Disables the FPU/VMX MXCSR sharing workaround, potentially "
|
||||
@@ -78,7 +74,6 @@ using namespace xe::literals;
|
||||
|
||||
static const size_t kMaxCodeSize = 1_MiB;
|
||||
|
||||
static const size_t kStashOffset = 32;
|
||||
// static const size_t kStashOffsetHigh = 32 + 32;
|
||||
|
||||
const uint32_t X64Emitter::gpr_reg_map_[X64Emitter::GPR_COUNT] = {
|
||||
@@ -141,55 +136,6 @@ bool X64Emitter::Emit(GuestFunction* function, HIRBuilder* builder,
|
||||
|
||||
return true;
|
||||
}
|
||||
#pragma pack(push, 1)
|
||||
struct RGCEmitted {
|
||||
uint8_t ff_;
|
||||
uint32_t rgcid_;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
#if 0
|
||||
void X64Emitter::InjectCallAddresses(void* new_execute_address) {
|
||||
for (auto&& callsite : call_sites_) {
|
||||
RGCEmitted* hunter = (RGCEmitted*)new_execute_address;
|
||||
while (hunter->ff_ != 0xFF || hunter->rgcid_ != callsite.offset_) {
|
||||
hunter =
|
||||
reinterpret_cast<RGCEmitted*>(reinterpret_cast<char*>(hunter) + 1);
|
||||
}
|
||||
|
||||
hunter->ff_ = callsite.is_jump_ ? 0xE9 : 0xE8;
|
||||
hunter->rgcid_ =
|
||||
static_cast<uint32_t>(static_cast<intptr_t>(callsite.destination_) -
|
||||
reinterpret_cast<intptr_t>(hunter + 1));
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
void X64Emitter::InjectCallAddresses(void* new_execute_address) {
|
||||
#if 0
|
||||
RGCEmitted* hunter = (RGCEmitted*)new_execute_address;
|
||||
|
||||
std::map<uint32_t, ResolvableGuestCall*> id_to_rgc{};
|
||||
|
||||
for (auto&& callsite : call_sites_) {
|
||||
id_to_rgc[callsite.offset_] = &callsite;
|
||||
}
|
||||
#else
|
||||
RGCEmitted* hunter = (RGCEmitted*)new_execute_address;
|
||||
for (auto&& callsite : call_sites_) {
|
||||
while (hunter->ff_ != 0xFF || hunter->rgcid_ != callsite.offset_) {
|
||||
hunter =
|
||||
reinterpret_cast<RGCEmitted*>(reinterpret_cast<char*>(hunter) + 1);
|
||||
}
|
||||
|
||||
hunter->ff_ = callsite.is_jump_ ? 0xE9 : 0xE8;
|
||||
hunter->rgcid_ =
|
||||
static_cast<uint32_t>(static_cast<intptr_t>(callsite.destination_) -
|
||||
reinterpret_cast<intptr_t>(hunter + 1));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
void* X64Emitter::Emplace(const EmitFunctionInfo& func_info,
|
||||
GuestFunction* function) {
|
||||
// To avoid changing xbyak, we do a switcharoo here.
|
||||
@@ -207,10 +153,6 @@ void* X64Emitter::Emplace(const EmitFunctionInfo& func_info,
|
||||
if (function) {
|
||||
code_cache_->PlaceGuestCode(function->address(), top_, func_info, function,
|
||||
new_execute_address, new_write_address);
|
||||
|
||||
if (cvars::resolve_rel32_guest_calls) {
|
||||
InjectCallAddresses(new_execute_address);
|
||||
}
|
||||
} else {
|
||||
code_cache_->PlaceHostCode(0, top_, func_info, new_execute_address,
|
||||
new_write_address);
|
||||
@@ -219,7 +161,6 @@ void* X64Emitter::Emplace(const EmitFunctionInfo& func_info,
|
||||
ready();
|
||||
top_ = old_address;
|
||||
reset();
|
||||
call_sites_.clear();
|
||||
tail_code_.clear();
|
||||
for (auto&& cached_label : label_cache_) {
|
||||
delete cached_label;
|
||||
@@ -336,7 +277,7 @@ bool X64Emitter::Emit(HIRBuilder* builder, EmitFunctionInfo& func_info) {
|
||||
// Mark block labels.
|
||||
auto label = block->label_head;
|
||||
while (label) {
|
||||
L(label->name);
|
||||
L(std::to_string(label->id));
|
||||
label = label->next;
|
||||
}
|
||||
|
||||
@@ -418,7 +359,6 @@ void X64Emitter::EmitProfilerEpilogue() {
|
||||
// actually... lets just try without atomics lol
|
||||
// lock();
|
||||
add(qword[r10], rdx);
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -534,44 +474,23 @@ void X64Emitter::Call(const hir::Instr* instr, GuestFunction* function) {
|
||||
auto fn = static_cast<X64Function*>(function);
|
||||
// Resolve address to the function to call and store in rax.
|
||||
|
||||
if (cvars::resolve_rel32_guest_calls && fn->machine_code()) {
|
||||
ResolvableGuestCall rgc;
|
||||
rgc.destination_ = uint32_t(uint64_t(fn->machine_code()));
|
||||
rgc.offset_ = current_rgc_id_;
|
||||
current_rgc_id_++;
|
||||
|
||||
if (fn->machine_code()) {
|
||||
if (!(instr->flags & hir::CALL_TAIL)) {
|
||||
mov(rcx, qword[rsp + StackLayout::GUEST_CALL_RET_ADDR]);
|
||||
|
||||
db(0xFF);
|
||||
rgc.is_jump_ = false;
|
||||
|
||||
dd(rgc.offset_);
|
||||
call((void*)fn->machine_code());
|
||||
|
||||
} else {
|
||||
// tail call
|
||||
EmitTraceUserCallReturn();
|
||||
|
||||
rgc.is_jump_ = true;
|
||||
EmitProfilerEpilogue();
|
||||
// Pass the callers return address over.
|
||||
mov(rcx, qword[rsp + StackLayout::GUEST_RET_ADDR]);
|
||||
|
||||
add(rsp, static_cast<uint32_t>(stack_size()));
|
||||
db(0xFF);
|
||||
dd(rgc.offset_);
|
||||
jmp((void*)fn->machine_code(), T_NEAR);
|
||||
}
|
||||
call_sites_.push_back(rgc);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fn->machine_code()) {
|
||||
// TODO(benvanik): is it worth it to do this? It removes the need for
|
||||
// a ResolveFunction call, but makes the table less useful.
|
||||
assert_zero(uint64_t(fn->machine_code()) & 0xFFFFFFFF00000000);
|
||||
// todo: this should be changed so that we can actually do a call to
|
||||
// fn->machine_code. the code will be emitted near us, so 32 bit rel jmp
|
||||
// should be possible
|
||||
mov(eax, uint32_t(uint64_t(fn->machine_code())));
|
||||
} else if (code_cache_->has_indirection_table()) {
|
||||
// Load the pointer to the indirection table maintained in X64CodeCache.
|
||||
// The target dword will either contain the address of the generated code
|
||||
@@ -1017,7 +936,10 @@ static const vec128_t xmm_consts[] = {
|
||||
/*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)};
|
||||
vec128b((uint8_t)0x83), /*XMMVSRShlByteshuf*/
|
||||
v128_setr_bytes(13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3, 0x80),
|
||||
// XMMVSRMask
|
||||
vec128b(1)};
|
||||
|
||||
void* X64Emitter::FindByteConstantOffset(unsigned bytevalue) {
|
||||
for (auto& vec : xmm_consts) {
|
||||
|
||||
@@ -66,7 +66,7 @@ enum class SimdDomain : uint32_t {
|
||||
};
|
||||
|
||||
enum class MXCSRMode : uint32_t { Unknown, Fpu, Vmx };
|
||||
|
||||
XE_MAYBE_UNUSED
|
||||
static SimdDomain PickDomain2(SimdDomain dom1, SimdDomain dom2) {
|
||||
if (dom1 == dom2) {
|
||||
return dom1;
|
||||
@@ -172,7 +172,9 @@ enum XmmConst {
|
||||
XMMLVLShuffle,
|
||||
XMMLVRCmp16,
|
||||
XMMSTVLShuffle,
|
||||
XMMSTVRSwapMask // swapwordmask with bit 7 set
|
||||
XMMSTVRSwapMask, // swapwordmask with bit 7 set
|
||||
XMMVSRShlByteshuf,
|
||||
XMMVSRMask
|
||||
|
||||
};
|
||||
using amdfx::xopcompare_e;
|
||||
@@ -190,13 +192,6 @@ class XbyakAllocator : public Xbyak::Allocator {
|
||||
virtual bool useProtect() const { return false; }
|
||||
};
|
||||
|
||||
class ResolvableGuestCall {
|
||||
public:
|
||||
bool is_jump_;
|
||||
uintptr_t destination_;
|
||||
// rgcid
|
||||
unsigned offset_;
|
||||
};
|
||||
class X64Emitter;
|
||||
using TailEmitCallback = std::function<void(X64Emitter& e, Xbyak::Label& lbl)>;
|
||||
struct TailEmitter {
|
||||
@@ -220,7 +215,6 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
||||
uint32_t debug_info_flags, FunctionDebugInfo* debug_info,
|
||||
void** out_code_address, size_t* out_code_size,
|
||||
std::vector<SourceMapEntry>* out_source_map);
|
||||
void InjectCallAddresses(void* new_execute_addr);
|
||||
|
||||
public:
|
||||
// Reserved: rsp, rsi, rdi
|
||||
@@ -230,7 +224,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
||||
// xmm4-xmm15 (save to get xmm3)
|
||||
static const int GPR_COUNT = 7;
|
||||
static const int XMM_COUNT = 12;
|
||||
|
||||
static constexpr size_t kStashOffset = 32;
|
||||
static void SetupReg(const hir::Value* v, Xbyak::Reg8& r) {
|
||||
auto idx = gpr_reg_map_[v->reg.index];
|
||||
r = Xbyak::Reg8(idx);
|
||||
@@ -410,8 +404,6 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
||||
|
||||
static const uint32_t gpr_reg_map_[GPR_COUNT];
|
||||
static const uint32_t xmm_reg_map_[XMM_COUNT];
|
||||
uint32_t current_rgc_id_ = 0xEEDDF00F;
|
||||
std::vector<ResolvableGuestCall> call_sites_;
|
||||
/*
|
||||
set to true if the low 32 bits of membase == 0.
|
||||
only really advantageous if you are storing 32 bit 0 to a displaced address,
|
||||
|
||||
@@ -398,21 +398,22 @@ struct I<OPCODE, DEST, SRC1, SRC2, SRC3> : DestField<DEST> {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
XE_MAYBE_UNUSED
|
||||
static const T GetTempReg(X64Emitter& e);
|
||||
template <>
|
||||
const Reg8 GetTempReg<Reg8>(X64Emitter& e) {
|
||||
XE_MAYBE_UNUSED const Reg8 GetTempReg<Reg8>(X64Emitter& e) {
|
||||
return e.al;
|
||||
}
|
||||
template <>
|
||||
const Reg16 GetTempReg<Reg16>(X64Emitter& e) {
|
||||
XE_MAYBE_UNUSED const Reg16 GetTempReg<Reg16>(X64Emitter& e) {
|
||||
return e.ax;
|
||||
}
|
||||
template <>
|
||||
const Reg32 GetTempReg<Reg32>(X64Emitter& e) {
|
||||
XE_MAYBE_UNUSED const Reg32 GetTempReg<Reg32>(X64Emitter& e) {
|
||||
return e.eax;
|
||||
}
|
||||
template <>
|
||||
const Reg64 GetTempReg<Reg64>(X64Emitter& e) {
|
||||
XE_MAYBE_UNUSED const Reg64 GetTempReg<Reg64>(X64Emitter& e) {
|
||||
return e.rax;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,46 +25,46 @@ static void EmitFusedBranch(X64Emitter& e, const T& i) {
|
||||
bool valid = i.instr->prev && i.instr->prev->dest == i.src1.value;
|
||||
auto opcode = valid ? i.instr->prev->opcode->num : -1;
|
||||
if (valid) {
|
||||
auto name = i.src2.value->name;
|
||||
std::string name = i.src2.value->GetIdString();
|
||||
switch (opcode) {
|
||||
case OPCODE_COMPARE_EQ:
|
||||
e.je(name, e.T_NEAR);
|
||||
e.je(std::move(name), e.T_NEAR);
|
||||
break;
|
||||
case OPCODE_COMPARE_NE:
|
||||
e.jne(name, e.T_NEAR);
|
||||
e.jne(std::move(name), e.T_NEAR);
|
||||
break;
|
||||
case OPCODE_COMPARE_SLT:
|
||||
e.jl(name, e.T_NEAR);
|
||||
e.jl(std::move(name), e.T_NEAR);
|
||||
break;
|
||||
case OPCODE_COMPARE_SLE:
|
||||
e.jle(name, e.T_NEAR);
|
||||
e.jle(std::move(name), e.T_NEAR);
|
||||
break;
|
||||
case OPCODE_COMPARE_SGT:
|
||||
e.jg(name, e.T_NEAR);
|
||||
e.jg(std::move(name), e.T_NEAR);
|
||||
break;
|
||||
case OPCODE_COMPARE_SGE:
|
||||
e.jge(name, e.T_NEAR);
|
||||
e.jge(std::move(name), e.T_NEAR);
|
||||
break;
|
||||
case OPCODE_COMPARE_ULT:
|
||||
e.jb(name, e.T_NEAR);
|
||||
e.jb(std::move(name), e.T_NEAR);
|
||||
break;
|
||||
case OPCODE_COMPARE_ULE:
|
||||
e.jbe(name, e.T_NEAR);
|
||||
e.jbe(std::move(name), e.T_NEAR);
|
||||
break;
|
||||
case OPCODE_COMPARE_UGT:
|
||||
e.ja(name, e.T_NEAR);
|
||||
e.ja(std::move(name), e.T_NEAR);
|
||||
break;
|
||||
case OPCODE_COMPARE_UGE:
|
||||
e.jae(name, e.T_NEAR);
|
||||
e.jae(std::move(name), e.T_NEAR);
|
||||
break;
|
||||
default:
|
||||
e.test(i.src1, i.src1);
|
||||
e.jnz(name, e.T_NEAR);
|
||||
e.jnz(std::move(name), e.T_NEAR);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
e.test(i.src1, i.src1);
|
||||
e.jnz(i.src2.value->name, e.T_NEAR);
|
||||
e.jnz(i.src2.value->GetIdString(), e.T_NEAR);
|
||||
}
|
||||
}
|
||||
// ============================================================================
|
||||
@@ -490,7 +490,7 @@ EMITTER_OPCODE_TABLE(OPCODE_SET_RETURN_ADDRESS, SET_RETURN_ADDRESS);
|
||||
// ============================================================================
|
||||
struct BRANCH : Sequence<BRANCH, I<OPCODE_BRANCH, VoidOp, LabelOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.jmp(i.src1.value->name, e.T_NEAR);
|
||||
e.jmp(i.src1.value->GetIdString(), e.T_NEAR);
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_BRANCH, BRANCH);
|
||||
@@ -534,7 +534,7 @@ struct BRANCH_TRUE_F32
|
||||
Xmm input = GetInputRegOrConstant(e, i.src1, e.xmm0);
|
||||
e.vmovd(e.eax, input);
|
||||
e.test(e.eax, e.eax);
|
||||
e.jnz(i.src2.value->name, e.T_NEAR);
|
||||
e.jnz(i.src2.value->GetIdString(), e.T_NEAR);
|
||||
}
|
||||
};
|
||||
struct BRANCH_TRUE_F64
|
||||
@@ -543,7 +543,7 @@ struct BRANCH_TRUE_F64
|
||||
Xmm input = GetInputRegOrConstant(e, i.src1, e.xmm0);
|
||||
e.vmovq(e.rax, input);
|
||||
e.test(e.rax, e.rax);
|
||||
e.jnz(i.src2.value->name, e.T_NEAR);
|
||||
e.jnz(i.src2.value->GetIdString(), e.T_NEAR);
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_BRANCH_TRUE, BRANCH_TRUE_I8, BRANCH_TRUE_I16,
|
||||
@@ -557,7 +557,7 @@ struct BRANCH_FALSE_I8
|
||||
: Sequence<BRANCH_FALSE_I8, I<OPCODE_BRANCH_FALSE, VoidOp, I8Op, LabelOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
e.jz(i.src2.value->name, e.T_NEAR);
|
||||
e.jz(i.src2.value->GetIdString(), e.T_NEAR);
|
||||
}
|
||||
};
|
||||
struct BRANCH_FALSE_I16
|
||||
@@ -565,7 +565,7 @@ struct BRANCH_FALSE_I16
|
||||
I<OPCODE_BRANCH_FALSE, VoidOp, I16Op, LabelOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
e.jz(i.src2.value->name, e.T_NEAR);
|
||||
e.jz(i.src2.value->GetIdString(), e.T_NEAR);
|
||||
}
|
||||
};
|
||||
struct BRANCH_FALSE_I32
|
||||
@@ -573,7 +573,7 @@ struct BRANCH_FALSE_I32
|
||||
I<OPCODE_BRANCH_FALSE, VoidOp, I32Op, LabelOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
e.jz(i.src2.value->name, e.T_NEAR);
|
||||
e.jz(i.src2.value->GetIdString(), e.T_NEAR);
|
||||
}
|
||||
};
|
||||
struct BRANCH_FALSE_I64
|
||||
@@ -581,7 +581,7 @@ struct BRANCH_FALSE_I64
|
||||
I<OPCODE_BRANCH_FALSE, VoidOp, I64Op, LabelOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
e.jz(i.src2.value->name, e.T_NEAR);
|
||||
e.jz(i.src2.value->GetIdString(), e.T_NEAR);
|
||||
}
|
||||
};
|
||||
struct BRANCH_FALSE_F32
|
||||
@@ -591,7 +591,7 @@ struct BRANCH_FALSE_F32
|
||||
Xmm input = GetInputRegOrConstant(e, i.src1, e.xmm0);
|
||||
e.vmovd(e.eax, input);
|
||||
e.test(e.eax, e.eax);
|
||||
e.jz(i.src2.value->name, e.T_NEAR);
|
||||
e.jz(i.src2.value->GetIdString(), e.T_NEAR);
|
||||
}
|
||||
};
|
||||
struct BRANCH_FALSE_F64
|
||||
@@ -601,7 +601,7 @@ struct BRANCH_FALSE_F64
|
||||
Xmm input = GetInputRegOrConstant(e, i.src1, e.xmm0);
|
||||
e.vmovq(e.rax, input);
|
||||
e.test(e.rax, e.rax);
|
||||
e.jz(i.src2.value->name, e.T_NEAR);
|
||||
e.jz(i.src2.value->GetIdString(), e.T_NEAR);
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_BRANCH_FALSE, BRANCH_FALSE_I8, BRANCH_FALSE_I16,
|
||||
|
||||
@@ -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;
|
||||
@@ -2163,82 +2405,6 @@ struct PERMUTE_V128
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_PERMUTE, PERMUTE_I32, PERMUTE_V128);
|
||||
|
||||
#define LCPI(name, quad1) const __m128i name = _mm_set1_epi32(quad1)
|
||||
// xmm0 is precasted to int, but contains float
|
||||
// chrispy: todo: make available to gpu code
|
||||
static __m128i xenos_float4_to_float16_x4(__m128i xmm0) {
|
||||
LCPI(LCPI0_0, 2147483647);
|
||||
LCPI(LCPI0_1, 1207951360);
|
||||
LCPI(LCPI0_2, 134217728);
|
||||
LCPI(LCPI0_3, 3347054592);
|
||||
LCPI(LCPI0_4, 260038655);
|
||||
LCPI(LCPI0_5, 32767);
|
||||
LCPI(LCPI0_6, 4294934528);
|
||||
|
||||
__m128i xmm1 = _mm_and_si128(xmm0, LCPI0_0);
|
||||
|
||||
__m128i xmm2 = LCPI0_1;
|
||||
|
||||
__m128i xmm3 = _mm_add_epi32(xmm0, LCPI0_2);
|
||||
xmm2 = _mm_cmpgt_epi32(xmm2, xmm1);
|
||||
xmm3 = _mm_srli_epi32(xmm3, 13);
|
||||
xmm1 = _mm_add_epi32(xmm1, LCPI0_3);
|
||||
__m128i xmm4 = _mm_min_epu32(xmm1, LCPI0_4);
|
||||
xmm1 = _mm_cmpeq_epi32(xmm1, xmm4);
|
||||
xmm4 = LCPI0_5;
|
||||
xmm3 = _mm_and_si128(xmm3, xmm4);
|
||||
xmm1 = _mm_and_si128(xmm1, xmm3);
|
||||
|
||||
xmm1 = _mm_castps_si128(_mm_blendv_ps(
|
||||
_mm_castsi128_ps(xmm4), _mm_castsi128_ps(xmm1), _mm_castsi128_ps(xmm2)));
|
||||
xmm0 = _mm_srli_epi32(xmm0, 16);
|
||||
xmm0 = _mm_and_si128(xmm0, LCPI0_6);
|
||||
xmm0 = _mm_or_si128(xmm1, xmm0);
|
||||
xmm0 = _mm_packus_epi32(xmm0, _mm_setzero_si128());
|
||||
return xmm0;
|
||||
}
|
||||
// returns floats, uncasted
|
||||
// chrispy: todo, make this available to gpu code?
|
||||
static __m128i xenos_halves_to_floats(__m128i xmm0) {
|
||||
LCPI(LCPI3_0, 0x1f);
|
||||
LCPI(LCPI3_1, 0x80000000);
|
||||
LCPI(LCPI3_2, 0x38000000);
|
||||
LCPI(LCPI3_3, 0x7fe000);
|
||||
|
||||
__m128i xmm1, xmm2, xmm3, xmm4;
|
||||
|
||||
xmm1 = _mm_cvtepu16_epi32(xmm0);
|
||||
|
||||
xmm2 = _mm_srli_epi32(xmm1, 10);
|
||||
|
||||
xmm2 = _mm_and_si128(xmm2, LCPI3_0);
|
||||
|
||||
xmm0 = _mm_cvtepi16_epi32(xmm0);
|
||||
|
||||
xmm0 = _mm_and_si128(xmm0, LCPI3_1);
|
||||
|
||||
xmm3 = _mm_setzero_si128();
|
||||
|
||||
xmm4 = _mm_slli_epi32(xmm2, 23);
|
||||
|
||||
xmm4 = _mm_add_epi32(xmm4, LCPI3_2);
|
||||
|
||||
xmm2 = _mm_cmpeq_epi32(xmm2, xmm3);
|
||||
|
||||
xmm1 = _mm_slli_epi32(xmm1, 13);
|
||||
|
||||
xmm1 = _mm_and_si128(xmm1, LCPI3_3);
|
||||
|
||||
xmm3 = _mm_andnot_si128(xmm2, xmm4);
|
||||
|
||||
xmm1 = _mm_andnot_si128(xmm2, xmm1);
|
||||
|
||||
xmm0 = _mm_or_si128(xmm1, xmm0);
|
||||
xmm0 = _mm_or_si128(xmm0, xmm3);
|
||||
return xmm0;
|
||||
}
|
||||
|
||||
#undef LCPI
|
||||
template <typename Inst>
|
||||
static void emit_fast_f16_unpack(X64Emitter& e, const Inst& i,
|
||||
XmmConst initial_shuffle) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user