[CPU] Move saturating add/sub to base/math.h
This commit is contained in:
parent
2b411f4c8b
commit
26c24b4726
|
@ -14,6 +14,7 @@
|
|||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include "xenia/base/platform.h"
|
||||
|
||||
|
@ -302,6 +303,38 @@ int64_t m128_i64(const __m128& v) {
|
|||
uint16_t float_to_half(float value);
|
||||
float half_to_float(uint16_t value);
|
||||
|
||||
// http://locklessinc.com/articles/sat_arithmetic/
|
||||
template <typename T>
|
||||
inline T sat_add(T a, T b) {
|
||||
using TU = std::make_unsigned<T>::type;
|
||||
TU result = TU(a) + TU(b);
|
||||
if (std::is_unsigned<T>::value) {
|
||||
result |= TU(-static_cast<std::make_signed<T>::type>(result < TU(a)));
|
||||
} else {
|
||||
TU overflowed =
|
||||
(TU(a) >> (sizeof(T) * 8 - 1)) + std::numeric_limits<T>::max();
|
||||
if (T((overflowed ^ TU(b)) | ~(TU(b) ^ result)) >= 0) {
|
||||
result = overflowed;
|
||||
}
|
||||
}
|
||||
return T(result);
|
||||
}
|
||||
template <typename T>
|
||||
inline T sat_sub(T a, T b) {
|
||||
using TU = std::make_unsigned<T>::type;
|
||||
TU result = TU(a) - TU(b);
|
||||
if (std::is_unsigned<T>::value) {
|
||||
result &= TU(-static_cast<std::make_signed<T>::type>(result <= TU(a)));
|
||||
} else {
|
||||
TU overflowed =
|
||||
(TU(a) >> (sizeof(T) * 8 - 1)) + std::numeric_limits<T>::max();
|
||||
if (T((overflowed ^ TU(b)) & (overflowed ^ result)) < 0) {
|
||||
result = overflowed;
|
||||
}
|
||||
}
|
||||
return T(result);
|
||||
}
|
||||
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_BASE_MATH_H_
|
||||
|
|
|
@ -1202,21 +1202,15 @@ void Value::VectorAdd(Value* other, TypeName type, bool is_unsigned,
|
|||
break;
|
||||
case INT8_TYPE:
|
||||
if (saturate) {
|
||||
// http://locklessinc.com/articles/sat_arithmetic/
|
||||
for (int i = 0; i < 16; i++) {
|
||||
uint8_t src1 = constant.v128.u8[i];
|
||||
uint8_t src2 = other->constant.v128.u8[i];
|
||||
uint8_t result = src1 + src2;
|
||||
if (is_unsigned) {
|
||||
result |= -int8_t(result < src1);
|
||||
constant.v128.u8[i] =
|
||||
xe::sat_add(constant.v128.u8[i], other->constant.v128.u8[i]);
|
||||
} else {
|
||||
uint8_t overflowed = (src1 >> 7) + INT8_MAX;
|
||||
if (int8_t((overflowed ^ src2) | ~(src2 ^ result)) >= 0) {
|
||||
result = overflowed;
|
||||
constant.v128.i8[i] =
|
||||
xe::sat_add(constant.v128.i8[i], other->constant.v128.i8[i]);
|
||||
}
|
||||
}
|
||||
constant.v128.u8[i] = result;
|
||||
}
|
||||
// TODO(Triang3l): Trace DID_SATURATE.
|
||||
} else {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
|
@ -1230,21 +1224,15 @@ void Value::VectorAdd(Value* other, TypeName type, bool is_unsigned,
|
|||
break;
|
||||
case INT16_TYPE:
|
||||
if (saturate) {
|
||||
// http://locklessinc.com/articles/sat_arithmetic/
|
||||
for (int i = 0; i < 8; i++) {
|
||||
uint16_t src1 = constant.v128.u16[i];
|
||||
uint16_t src2 = other->constant.v128.u16[i];
|
||||
uint16_t result = src1 + src2;
|
||||
if (is_unsigned) {
|
||||
result |= -int16_t(result < src1);
|
||||
constant.v128.u16[i] =
|
||||
xe::sat_add(constant.v128.u16[i], other->constant.v128.u16[i]);
|
||||
} else {
|
||||
uint16_t overflowed = (src1 >> 15) + INT16_MAX;
|
||||
if (int16_t((overflowed ^ src2) | ~(src2 ^ result)) >= 0) {
|
||||
result = overflowed;
|
||||
constant.v128.i16[i] =
|
||||
xe::sat_add(constant.v128.i16[i], other->constant.v128.i16[i]);
|
||||
}
|
||||
}
|
||||
constant.v128.u16[i] = result;
|
||||
}
|
||||
// TODO(Triang3l): Trace DID_SATURATE.
|
||||
} else {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
|
@ -1258,21 +1246,15 @@ void Value::VectorAdd(Value* other, TypeName type, bool is_unsigned,
|
|||
break;
|
||||
case INT32_TYPE:
|
||||
if (saturate) {
|
||||
// http://locklessinc.com/articles/sat_arithmetic/
|
||||
for (int i = 0; i < 4; i++) {
|
||||
uint32_t src1 = constant.v128.u32[i];
|
||||
uint32_t src2 = other->constant.v128.u32[i];
|
||||
uint32_t result = src1 + src2;
|
||||
if (is_unsigned) {
|
||||
result |= -int32_t(result < src1);
|
||||
constant.v128.u32[i] =
|
||||
xe::sat_add(constant.v128.u32[i], other->constant.v128.u32[i]);
|
||||
} else {
|
||||
uint32_t overflowed = (src1 >> 31) + INT32_MAX;
|
||||
if (int32_t((overflowed ^ src2) | ~(src2 ^ result)) >= 0) {
|
||||
result = overflowed;
|
||||
constant.v128.i32[i] =
|
||||
xe::sat_add(constant.v128.i32[i], other->constant.v128.i32[i]);
|
||||
}
|
||||
}
|
||||
constant.v128.u32[i] = result;
|
||||
}
|
||||
// TODO(Triang3l): Trace DID_SATURATE.
|
||||
} else {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
|
@ -1286,21 +1268,15 @@ void Value::VectorAdd(Value* other, TypeName type, bool is_unsigned,
|
|||
break;
|
||||
case INT64_TYPE:
|
||||
if (saturate) {
|
||||
// http://locklessinc.com/articles/sat_arithmetic/
|
||||
for (int i = 0; i < 2; i++) {
|
||||
uint64_t src1 = constant.v128.u64[i];
|
||||
uint64_t src2 = other->constant.v128.u64[i];
|
||||
uint64_t result = src1 + src2;
|
||||
if (is_unsigned) {
|
||||
result |= -int64_t(result < src1);
|
||||
constant.v128.u64[i] =
|
||||
xe::sat_add(constant.v128.u64[i], other->constant.v128.u64[i]);
|
||||
} else {
|
||||
uint64_t overflowed = (src1 >> 63) + INT64_MAX;
|
||||
if (int64_t((overflowed ^ src2) | ~(src2 ^ result)) >= 0) {
|
||||
result = overflowed;
|
||||
constant.v128.i64[i] =
|
||||
xe::sat_add(constant.v128.i64[i], other->constant.v128.i64[i]);
|
||||
}
|
||||
}
|
||||
constant.v128.u64[i] = result;
|
||||
}
|
||||
// TODO(Triang3l): Trace DID_SATURATE.
|
||||
} else {
|
||||
if (is_unsigned) {
|
||||
|
@ -1342,21 +1318,15 @@ void Value::VectorSub(Value* other, TypeName type, bool is_unsigned,
|
|||
break;
|
||||
case INT8_TYPE:
|
||||
if (saturate) {
|
||||
// http://locklessinc.com/articles/sat_arithmetic/
|
||||
for (int i = 0; i < 16; i++) {
|
||||
uint8_t src1 = constant.v128.u8[i];
|
||||
uint8_t src2 = other->constant.v128.u8[i];
|
||||
uint8_t result = src1 - src2;
|
||||
if (is_unsigned) {
|
||||
result &= -int8_t(result <= src1);
|
||||
constant.v128.u8[i] =
|
||||
xe::sat_sub(constant.v128.u8[i], other->constant.v128.u8[i]);
|
||||
} else {
|
||||
uint8_t overflowed = (src1 >> 7) + INT8_MAX;
|
||||
if (int8_t((overflowed ^ src2) & (overflowed ^ result)) < 0) {
|
||||
result = overflowed;
|
||||
constant.v128.i8[i] =
|
||||
xe::sat_sub(constant.v128.i8[i], other->constant.v128.i8[i]);
|
||||
}
|
||||
}
|
||||
constant.v128.u8[i] = result;
|
||||
}
|
||||
// TODO(Triang3l): Trace DID_SATURATE.
|
||||
} else {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
|
@ -1370,21 +1340,15 @@ void Value::VectorSub(Value* other, TypeName type, bool is_unsigned,
|
|||
break;
|
||||
case INT16_TYPE:
|
||||
if (saturate) {
|
||||
// http://locklessinc.com/articles/sat_arithmetic/
|
||||
for (int i = 0; i < 8; i++) {
|
||||
uint16_t src1 = constant.v128.u16[i];
|
||||
uint16_t src2 = other->constant.v128.u16[i];
|
||||
uint16_t result = src1 - src2;
|
||||
if (is_unsigned) {
|
||||
result &= -int16_t(result <= src1);
|
||||
constant.v128.u16[i] =
|
||||
xe::sat_sub(constant.v128.u16[i], other->constant.v128.u16[i]);
|
||||
} else {
|
||||
uint16_t overflowed = (src1 >> 15) + INT16_MAX;
|
||||
if (int16_t((overflowed ^ src2) & (overflowed ^ result)) < 0) {
|
||||
result = overflowed;
|
||||
constant.v128.i16[i] =
|
||||
xe::sat_sub(constant.v128.i16[i], other->constant.v128.i16[i]);
|
||||
}
|
||||
}
|
||||
constant.v128.u16[i] = result;
|
||||
}
|
||||
// TODO(Triang3l): Trace DID_SATURATE.
|
||||
} else {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
|
@ -1398,21 +1362,15 @@ void Value::VectorSub(Value* other, TypeName type, bool is_unsigned,
|
|||
break;
|
||||
case INT32_TYPE:
|
||||
if (saturate) {
|
||||
// http://locklessinc.com/articles/sat_arithmetic/
|
||||
for (int i = 0; i < 4; i++) {
|
||||
uint32_t src1 = constant.v128.u32[i];
|
||||
uint32_t src2 = other->constant.v128.u32[i];
|
||||
uint32_t result = src1 - src2;
|
||||
if (is_unsigned) {
|
||||
result &= -int32_t(result <= src1);
|
||||
constant.v128.u32[i] =
|
||||
xe::sat_sub(constant.v128.u32[i], other->constant.v128.u32[i]);
|
||||
} else {
|
||||
uint32_t overflowed = (src1 >> 31) + INT32_MAX;
|
||||
if (int32_t((overflowed ^ src2) & (overflowed ^ result)) < 0) {
|
||||
result = overflowed;
|
||||
constant.v128.i32[i] =
|
||||
xe::sat_sub(constant.v128.i32[i], other->constant.v128.i32[i]);
|
||||
}
|
||||
}
|
||||
constant.v128.u32[i] = result;
|
||||
}
|
||||
// TODO(Triang3l): Trace DID_SATURATE.
|
||||
} else {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
|
@ -1426,21 +1384,15 @@ void Value::VectorSub(Value* other, TypeName type, bool is_unsigned,
|
|||
break;
|
||||
case INT64_TYPE:
|
||||
if (saturate) {
|
||||
// http://locklessinc.com/articles/sat_arithmetic/
|
||||
for (int i = 0; i < 2; i++) {
|
||||
uint64_t src1 = constant.v128.u64[i];
|
||||
uint64_t src2 = other->constant.v128.u64[i];
|
||||
uint64_t result = src1 - src2;
|
||||
if (is_unsigned) {
|
||||
result &= -int64_t(result <= src1);
|
||||
constant.v128.u64[i] =
|
||||
xe::sat_sub(constant.v128.u64[i], other->constant.v128.u64[i]);
|
||||
} else {
|
||||
uint64_t overflowed = (src1 >> 63) + INT64_MAX;
|
||||
if (int64_t((overflowed ^ src2) & (overflowed ^ result)) < 0) {
|
||||
result = overflowed;
|
||||
constant.v128.i64[i] =
|
||||
xe::sat_sub(constant.v128.i64[i], other->constant.v128.i64[i]);
|
||||
}
|
||||
}
|
||||
constant.v128.u64[i] = result;
|
||||
}
|
||||
// TODO(Triang3l): Trace DID_SATURATE.
|
||||
} else {
|
||||
if (is_unsigned) {
|
||||
|
|
Loading…
Reference in New Issue