remove useless tag field from hir::Value
pack local_slot and constant in hir::Value Instead of loading membase at the start of every function, just load it in HostToGuestThunk vzeroupper in GuestToHostThunk before calling host function, and in HostToGuestThunk after calling function to prevent AVX dirty state slowdowns. In the future, check if CPU implements AVX as 128x2 and skip if so (https://john-h-k.github.io/VexTransitionPenalties.html) Remove useless save/restore of ctx pointer, nothing modifies it and it prevents cpus from doing cross-function memory renaming (https://www.agner.org/forum/viewtopic.php?t=41). Could not remove the space on stack because of alignment issues, instead turned it into GUEST_SCRATCH64 which is a temporary that sequences may use Reorder OpcodeInfo so that name is at offset 0, remove name and add GetOpcodeName function (name is only used for debug code, we are seperating frequently accessed data and rarely accessed data) Add VECTOR_DENORMFLUSH opcode for handling output to DOT_PRODUCT and other opcodes that implicitly force denormal inputs/outputs to zero, will eventually use for implementing NJM Rewrite sequences for LOAD_VECTOR_SHL/SHR. The mask with 0xf in it was pointless as all InstrEmit_ functions that create the load shift instructions do that in HIR. The tables are only used for nonzero constant inputs now, which are probably pretty rare. Instead of doing a shift and lookup, a base value is used for both in the constant table and adding/subtracting of the input is done Reuse result of LoadVectorShl/Shr in InstrEmit_stvlx_, InstrEmit_stvrx_. We were previously calculating it twice which was contributing to the final sequences' fatness. Use OPCODE_SELECT instead of the sequence of or, andnot, and that it was using for merging Add the proper unconditional denormal input flushing behavior to vfmadd, add it also to vfmsub (making the assumption it has the same behavior) Remove constant propagation for DOT_PRODUCT_3/4 DOT_PRODUCT_3/4 now returns a vector with all four elements set to the result. (what we were doing before, truncating to float32 and then splatting didnt make any sense) Add much more correct versions of DOT_PRODUCT_3/4, matching the Xb360's to 1 bit. Still needs work to be a perfect emulation. Add constant folding for OPCODE_SELECT, OPCODE_INSERT, OPCODE_PERMUTE, OPCODE_SWIZZLE Remove constant folding for DOT_PRODUCT Removed the multibyte nop code I committed earlier, it doesnt help us much because nops are only used for debug stuff and its ugly and wouldnt survive in a pr to main Check for AVX512BMI, use vpermb to shuffle if supported
This commit is contained in:
@@ -260,9 +260,9 @@ void HIRBuilder::Dump(StringBuffer* str) {
|
||||
str->Append(" = ");
|
||||
}
|
||||
if (i->flags) {
|
||||
str->AppendFormat("{}.{}", info->name, i->flags);
|
||||
str->AppendFormat("{}.{}", GetOpcodeName(info), i->flags);
|
||||
} else {
|
||||
str->Append(info->name);
|
||||
str->Append(GetOpcodeName(info));
|
||||
}
|
||||
if (src1_type) {
|
||||
str->Append(' ');
|
||||
@@ -712,7 +712,6 @@ Value* HIRBuilder::AllocValue(TypeName type) {
|
||||
value->use_head = NULL;
|
||||
value->last_use = NULL;
|
||||
value->local_slot = NULL;
|
||||
value->tag = NULL;
|
||||
value->reg.set = NULL;
|
||||
value->reg.index = -1;
|
||||
return value;
|
||||
@@ -723,12 +722,11 @@ Value* HIRBuilder::CloneValue(Value* source) {
|
||||
value->ordinal = next_value_ordinal_++;
|
||||
value->type = source->type;
|
||||
value->flags = source->flags;
|
||||
value->local_slot = NULL;
|
||||
value->constant.v128 = source->constant.v128;
|
||||
value->def = NULL;
|
||||
value->use_head = NULL;
|
||||
value->last_use = NULL;
|
||||
value->local_slot = NULL;
|
||||
value->tag = NULL;
|
||||
value->reg.set = NULL;
|
||||
value->reg.index = -1;
|
||||
return value;
|
||||
@@ -1493,7 +1491,16 @@ Value* HIRBuilder::VectorCompareUGE(Value* value1, Value* value2,
|
||||
return VectorCompareXX(OPCODE_VECTOR_COMPARE_UGE_info, value1, value2,
|
||||
part_type);
|
||||
}
|
||||
|
||||
Value* HIRBuilder::VectorDenormFlush(Value* value1) {
|
||||
return value1;
|
||||
ASSERT_VECTOR_TYPE(value1);
|
||||
Instr* i =
|
||||
AppendInstr(OPCODE_VECTOR_DENORMFLUSH_info, 0, AllocValue(VEC128_TYPE));
|
||||
i->set_src1(value1);
|
||||
i->src2.value = nullptr;
|
||||
i->src3.value = nullptr;
|
||||
return i->dest;
|
||||
}
|
||||
Value* HIRBuilder::Add(Value* value1, Value* value2,
|
||||
uint32_t arithmetic_flags) {
|
||||
ASSERT_TYPES_EQUAL(value1, value2);
|
||||
@@ -1713,13 +1720,13 @@ Value* HIRBuilder::Log2(Value* value) {
|
||||
return i->dest;
|
||||
}
|
||||
|
||||
|
||||
Value* HIRBuilder::DotProduct3(Value* value1, Value* value2) {
|
||||
ASSERT_VECTOR_TYPE(value1);
|
||||
ASSERT_VECTOR_TYPE(value2);
|
||||
ASSERT_TYPES_EQUAL(value1, value2);
|
||||
|
||||
Instr* i =
|
||||
AppendInstr(OPCODE_DOT_PRODUCT_3_info, 0, AllocValue(FLOAT32_TYPE));
|
||||
Instr* i = AppendInstr(OPCODE_DOT_PRODUCT_3_info, 0, AllocValue(VEC128_TYPE));
|
||||
i->set_src1(value1);
|
||||
i->set_src2(value2);
|
||||
i->src3.value = NULL;
|
||||
@@ -1731,8 +1738,7 @@ Value* HIRBuilder::DotProduct4(Value* value1, Value* value2) {
|
||||
ASSERT_VECTOR_TYPE(value2);
|
||||
ASSERT_TYPES_EQUAL(value1, value2);
|
||||
|
||||
Instr* i =
|
||||
AppendInstr(OPCODE_DOT_PRODUCT_4_info, 0, AllocValue(FLOAT32_TYPE));
|
||||
Instr* i = AppendInstr(OPCODE_DOT_PRODUCT_4_info, 0, AllocValue(VEC128_TYPE));
|
||||
i->set_src1(value1);
|
||||
i->set_src2(value2);
|
||||
i->src3.value = NULL;
|
||||
|
||||
@@ -199,6 +199,7 @@ class HIRBuilder {
|
||||
Value* VectorCompareSGE(Value* value1, Value* value2, TypeName part_type);
|
||||
Value* VectorCompareUGT(Value* value1, Value* value2, TypeName part_type);
|
||||
Value* VectorCompareUGE(Value* value1, Value* value2, TypeName part_type);
|
||||
Value* VectorDenormFlush(Value* value1);
|
||||
|
||||
Value* Add(Value* value1, Value* value2, uint32_t arithmetic_flags = 0);
|
||||
Value* AddWithCarry(Value* value1, Value* value2, Value* value3,
|
||||
|
||||
@@ -15,14 +15,23 @@ namespace hir {
|
||||
|
||||
#define DEFINE_OPCODE(num, name, sig, flags) \
|
||||
const OpcodeInfo num##_info = { \
|
||||
num, \
|
||||
flags, \
|
||||
sig, \
|
||||
name, \
|
||||
num, \
|
||||
};
|
||||
#include "xenia/cpu/hir/opcodes.inl"
|
||||
#undef DEFINE_OPCODE
|
||||
|
||||
const char* GetOpcodeName(Opcode num) {
|
||||
switch (num) {
|
||||
#define DEFINE_OPCODE(num, name, sig, flags) \
|
||||
case num: \
|
||||
return name;
|
||||
#include "xenia/cpu/hir/opcodes.inl"
|
||||
#undef DEFINE_OPCODE
|
||||
}
|
||||
return "invalid opcode";
|
||||
}
|
||||
} // namespace hir
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
@@ -280,7 +280,8 @@ enum Opcode {
|
||||
OPCODE_ATOMIC_EXCHANGE,
|
||||
OPCODE_ATOMIC_COMPARE_EXCHANGE,
|
||||
OPCODE_SET_ROUNDING_MODE,
|
||||
__OPCODE_MAX_VALUE, // Keep at end.
|
||||
OPCODE_VECTOR_DENORMFLUSH, // converts denormals to signed zeros in a vector
|
||||
__OPCODE_MAX_VALUE, // Keep at end.
|
||||
};
|
||||
|
||||
enum OpcodeFlags {
|
||||
@@ -352,17 +353,42 @@ static bool IsOpcodeBinaryValue(uint32_t signature) {
|
||||
((OPCODE_SIG_TYPE_V << 3) | (OPCODE_SIG_TYPE_V << 6));
|
||||
}
|
||||
|
||||
static void UnpackOpcodeSig(uint32_t sig, OpcodeSignatureType& dest,
|
||||
OpcodeSignatureType& src1,
|
||||
OpcodeSignatureType& src2,
|
||||
OpcodeSignatureType& src3) {
|
||||
dest = GET_OPCODE_SIG_TYPE_DEST(sig);
|
||||
src1 = GET_OPCODE_SIG_TYPE_SRC1(sig);
|
||||
src2 = GET_OPCODE_SIG_TYPE_SRC2(sig);
|
||||
src3 = GET_OPCODE_SIG_TYPE_SRC3(sig);
|
||||
}
|
||||
|
||||
constexpr uint32_t GetNumOperandsForSig(uint32_t sig) {
|
||||
sig >>= 3;
|
||||
|
||||
uint32_t result = 0;
|
||||
while (sig) {
|
||||
if (sig & 0x7) {
|
||||
++result;
|
||||
}
|
||||
sig >>= 3;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
typedef struct {
|
||||
Opcode num;
|
||||
uint32_t flags;
|
||||
uint32_t signature;
|
||||
const char* name;
|
||||
Opcode num;
|
||||
} OpcodeInfo;
|
||||
|
||||
#define DEFINE_OPCODE(num, name, sig, flags) extern const OpcodeInfo num##_info;
|
||||
#include "xenia/cpu/hir/opcodes.inl"
|
||||
#undef DEFINE_OPCODE
|
||||
|
||||
const char* GetOpcodeName(Opcode num);
|
||||
static inline const char* GetOpcodeName(const OpcodeInfo* info) {
|
||||
return GetOpcodeName(info->num);
|
||||
}
|
||||
} // namespace hir
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
@@ -673,3 +673,10 @@ DEFINE_OPCODE(
|
||||
"set_rounding_mode",
|
||||
OPCODE_SIG_X_V,
|
||||
0)
|
||||
|
||||
DEFINE_OPCODE(
|
||||
OPCODE_VECTOR_DENORMFLUSH,
|
||||
"vector_denormflush",
|
||||
OPCODE_SIG_V_V,
|
||||
0
|
||||
)
|
||||
@@ -864,10 +864,112 @@ void Value::Extract(Value* vec, Value* index) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
void Value::Permute(Value* src1, Value* src2, TypeName type) {
|
||||
if (type == INT8_TYPE) {
|
||||
uint8_t table[32];
|
||||
|
||||
for (uint32_t i = 0; i < 16; ++i) {
|
||||
table[i] = src1->constant.v128.u8[i];
|
||||
table[i + 16] = src2->constant.v128.u8[i];
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < 16; ++i) {
|
||||
constant.v128.u8[i] = table[(constant.v128.u8[i] ^ 3) & 0x1f];
|
||||
}
|
||||
} else if (type == INT16_TYPE) {
|
||||
vec128_t perm = (constant.v128 & vec128s(0xF)) ^ vec128s(0x1);
|
||||
vec128_t perm_ctrl = vec128b(0);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
perm_ctrl.i16[i] = perm.i16[i] > 7 ? -1 : 0;
|
||||
|
||||
auto v = uint8_t(perm.u16[i]);
|
||||
perm.u8[i * 2] = v * 2;
|
||||
perm.u8[i * 2 + 1] = v * 2 + 1;
|
||||
}
|
||||
auto lod = [](const vec128_t& v) {
|
||||
return _mm_loadu_si128((const __m128i*)&v);
|
||||
};
|
||||
auto sto = [](vec128_t& v, __m128i x) {
|
||||
return _mm_storeu_si128((__m128i*)&v, x);
|
||||
};
|
||||
|
||||
__m128i xmm1 = lod(src1->constant.v128);
|
||||
__m128i xmm2 = lod(src2->constant.v128);
|
||||
xmm1 = _mm_shuffle_epi8(xmm1, lod(perm));
|
||||
xmm2 = _mm_shuffle_epi8(xmm2, lod(perm));
|
||||
uint8_t mask = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (perm_ctrl.i16[i] == 0) {
|
||||
mask |= 1 << (7 - i);
|
||||
}
|
||||
}
|
||||
|
||||
vec128_t unp_mask = vec128b(0);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (mask & (1 << i)) {
|
||||
unp_mask.u16[i] = 0xFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
sto(constant.v128, _mm_blendv_epi8(xmm1, xmm2, lod(unp_mask)));
|
||||
|
||||
} else {
|
||||
assert_unhandled_case(type);
|
||||
}
|
||||
}
|
||||
void Value::Insert(Value* index, Value* part, TypeName type) {
|
||||
vec128_t* me = &constant.v128;
|
||||
|
||||
switch (type) {
|
||||
case INT8_TYPE:
|
||||
me->u8[index->constant.u8 ^ 3] = part->constant.u8;
|
||||
break;
|
||||
case INT16_TYPE:
|
||||
me->u16[index->constant.u8 ^ 1] = part->constant.u16;
|
||||
break;
|
||||
case INT32_TYPE:
|
||||
me->u32[index->constant.u8] = part->constant.u32;
|
||||
break;
|
||||
}
|
||||
}
|
||||
void Value::Swizzle(uint32_t mask, TypeName type) {
|
||||
if (type == INT32_TYPE || type == FLOAT32_TYPE) {
|
||||
vec128_t result = vec128b(0);
|
||||
for (uint32_t i = 0; i < 4; ++i) {
|
||||
result.u32[i] = constant.v128.u32[(mask >> (i * 2)) & 0b11];
|
||||
}
|
||||
constant.v128 = result;
|
||||
} else {
|
||||
assert_unhandled_case(type);
|
||||
}
|
||||
}
|
||||
void Value::Select(Value* other, Value* ctrl) {
|
||||
// TODO
|
||||
assert_always();
|
||||
if (ctrl->type == VEC128_TYPE) {
|
||||
constant.v128.low = (constant.v128.low & ~ctrl->constant.v128.low) |
|
||||
(other->constant.v128.low & ctrl->constant.v128.low);
|
||||
constant.v128.high = (constant.v128.high & ~ctrl->constant.v128.high) |
|
||||
(other->constant.v128.high & ctrl->constant.v128.high);
|
||||
|
||||
} else {
|
||||
if (ctrl->constant.u8) {
|
||||
switch (other->type) {
|
||||
case INT8_TYPE:
|
||||
constant.u8 = other->constant.u8;
|
||||
break;
|
||||
case INT16_TYPE:
|
||||
constant.u16 = other->constant.u16;
|
||||
break;
|
||||
case INT32_TYPE:
|
||||
case FLOAT32_TYPE:
|
||||
constant.u32 = other->constant.u32;
|
||||
break;
|
||||
case INT64_TYPE:
|
||||
case FLOAT64_TYPE:
|
||||
constant.u64 = other->constant.u64;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Value::Splat(Value* other) {
|
||||
@@ -1532,7 +1634,15 @@ void Value::ByteSwap() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Value::DenormalFlush() {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
uint32_t current_element = constant.v128.u32[i];
|
||||
if ((current_element & 0x7f800000) == 0) {
|
||||
current_element = current_element & 0x80000000;
|
||||
}
|
||||
constant.v128.u32[i] = current_element;
|
||||
}
|
||||
}
|
||||
void Value::CountLeadingZeros(const Value* other) {
|
||||
switch (other->type) {
|
||||
case INT8_TYPE:
|
||||
|
||||
@@ -104,6 +104,9 @@ struct ValueMask {
|
||||
|
||||
class Value {
|
||||
public:
|
||||
/*
|
||||
todo : this should be intrusive and be part of Instr instead.
|
||||
*/
|
||||
typedef struct Use_s {
|
||||
Instr* instr;
|
||||
Use_s* prev;
|
||||
@@ -128,17 +131,16 @@ class Value {
|
||||
TypeName type;
|
||||
|
||||
uint32_t flags;
|
||||
RegAssignment reg;
|
||||
ConstantValue constant;
|
||||
|
||||
Instr* def;
|
||||
Use* use_head;
|
||||
// NOTE: for performance reasons this is not maintained during construction.
|
||||
Instr* last_use;
|
||||
Value* local_slot;
|
||||
|
||||
// TODO(benvanik): remove to shrink size.
|
||||
void* tag;
|
||||
RegAssignment reg;
|
||||
union {
|
||||
Value* local_slot;
|
||||
ConstantValue constant;
|
||||
};
|
||||
|
||||
Use* AddUse(Arena* arena, Instr* instr);
|
||||
void RemoveUse(Use* use);
|
||||
@@ -209,7 +211,20 @@ class Value {
|
||||
flags = other->flags;
|
||||
constant.v128 = other->constant.v128;
|
||||
}
|
||||
bool HasLocalSlot() const {
|
||||
return !(flags & VALUE_IS_CONSTANT) && local_slot;
|
||||
}
|
||||
void SetLocalSlot(Value* lslot) {
|
||||
assert(!(flags & VALUE_IS_CONSTANT));
|
||||
local_slot = lslot;
|
||||
}
|
||||
|
||||
Value* GetLocalSlot() {
|
||||
return (flags & VALUE_IS_CONSTANT) ? nullptr : local_slot;
|
||||
}
|
||||
const Value* GetLocalSlot() const {
|
||||
return (flags & VALUE_IS_CONSTANT) ? nullptr : local_slot;
|
||||
}
|
||||
inline bool IsConstant() const { return !!(flags & VALUE_IS_CONSTANT); }
|
||||
bool IsConstantTrue() const {
|
||||
if (type == VEC128_TYPE) {
|
||||
@@ -555,7 +570,10 @@ class Value {
|
||||
void Shr(Value* other);
|
||||
void Sha(Value* other);
|
||||
void RotateLeft(Value* other);
|
||||
void Insert(Value* index, Value* part, TypeName type);
|
||||
void Extract(Value* vec, Value* index);
|
||||
void Permute(Value* src1, Value* src2, TypeName type);
|
||||
void Swizzle(uint32_t mask, TypeName type);
|
||||
void Select(Value* other, Value* ctrl);
|
||||
void Splat(Value* other);
|
||||
void VectorCompareEQ(Value* other, TypeName type);
|
||||
@@ -575,6 +593,8 @@ class Value {
|
||||
void VectorAverage(Value* other, TypeName type, bool is_unsigned,
|
||||
bool saturate);
|
||||
void ByteSwap();
|
||||
void DenormalFlush();
|
||||
|
||||
void CountLeadingZeros(const Value* other);
|
||||
bool Compare(Opcode opcode, Value* other);
|
||||
hir::Instr* GetDefSkipAssigns();
|
||||
|
||||
Reference in New Issue
Block a user