[K/N] add custom builtin_mul_overflow to workaround llvm bug
^KT-52365
This commit is contained in:
@@ -137,6 +137,34 @@ constexpr auto saturating_sub(T lhs, U rhs) noexcept {
|
||||
return result;
|
||||
}
|
||||
|
||||
// here is workaround for bug https://bugs.llvm.org/show_bug.cgi?id=28629. It can be removed after migrating to llvm-13
|
||||
// function is not too careful around std::numeric_limits<T>::min(). It can sometimes report overflow, when result equal to it,
|
||||
// but it's okey for current usages
|
||||
template<typename T>
|
||||
constexpr bool custom_builtin_mul_overflow(T lhs, T rhs, T* result) {
|
||||
if (lhs != 0 && rhs != 0) {
|
||||
if constexpr (std::is_signed_v<T>) {
|
||||
if (lhs == std::numeric_limits<T>::min() || rhs == std::numeric_limits<T>::min()) {
|
||||
if (lhs != 1 && rhs != 1) return true;
|
||||
} else {
|
||||
T lhs_abs = (lhs < 0) ? -lhs : lhs;
|
||||
T rhs_abs = (rhs < 0) ? -rhs : rhs;
|
||||
if (std::numeric_limits<T>::max() / rhs_abs < lhs_abs) return true;
|
||||
}
|
||||
} else {
|
||||
if (std::numeric_limits<T>::max() / rhs < lhs) return true;
|
||||
}
|
||||
}
|
||||
*result = lhs * rhs;
|
||||
return false;
|
||||
}
|
||||
|
||||
#if KONAN_FORBID_BUILTIN_MUL_OVERFLOW
|
||||
#define mul_overflow custom_builtin_mul_overflow
|
||||
#else
|
||||
#define mul_overflow __builtin_mul_overflow
|
||||
#endif
|
||||
|
||||
template <typename T, typename U>
|
||||
constexpr auto saturating_mul(T lhs, U rhs) noexcept {
|
||||
static_assert(std::is_integral_v<T>, "T must be integral");
|
||||
@@ -144,8 +172,8 @@ constexpr auto saturating_mul(T lhs, U rhs) noexcept {
|
||||
static_assert(std::is_signed_v<T> == std::is_signed_v<U>, "T and U must have the same sign");
|
||||
|
||||
using Result = internal::wider_t<T, U>;
|
||||
Result result;
|
||||
if (__builtin_mul_overflow(lhs, rhs, &result)) {
|
||||
Result result{0};
|
||||
if (mul_overflow(static_cast<Result>(lhs), static_cast<Result>(rhs), &result)) {
|
||||
if (lhs >= 0 && rhs >= 0) {
|
||||
// Multiplying non-negative numbers caused an overflow => overflowed upwards.
|
||||
result = std::numeric_limits<Result>::max();
|
||||
|
||||
@@ -114,6 +114,52 @@ TEST(SaturatingSanityTest, SaturatingInPlace) {
|
||||
EXPECT_THAT(int8_t(value), -128);
|
||||
}
|
||||
|
||||
TEST(SaturatingSanityTest, CustomBuiltinMullOverflow) {
|
||||
int32_t result;
|
||||
EXPECT_TRUE(custom_builtin_mul_overflow(1 << 16, 1 << 15, &result));
|
||||
EXPECT_FALSE(custom_builtin_mul_overflow(1 << 15, 1 << 15, &result));
|
||||
EXPECT_THAT(result, 1 << 30);
|
||||
EXPECT_TRUE(custom_builtin_mul_overflow(std::numeric_limits<int32_t>::min(), 2, &result));
|
||||
EXPECT_TRUE(custom_builtin_mul_overflow(std::numeric_limits<int32_t>::min(), -1, &result));
|
||||
EXPECT_TRUE(custom_builtin_mul_overflow(std::numeric_limits<int32_t>::min(), -2, &result));
|
||||
EXPECT_FALSE(custom_builtin_mul_overflow(std::numeric_limits<int32_t>::min(), 1, &result));
|
||||
EXPECT_THAT(result, std::numeric_limits<int32_t>::min());
|
||||
EXPECT_FALSE(custom_builtin_mul_overflow(std::numeric_limits<int32_t>::min(), 0, &result));
|
||||
EXPECT_THAT(result, 0);
|
||||
|
||||
EXPECT_TRUE(custom_builtin_mul_overflow(2, std::numeric_limits<int32_t>::min(), &result));
|
||||
EXPECT_TRUE(custom_builtin_mul_overflow(-1, std::numeric_limits<int32_t>::min(), &result));
|
||||
EXPECT_TRUE(custom_builtin_mul_overflow(-2, std::numeric_limits<int32_t>::min(), &result));
|
||||
EXPECT_FALSE(custom_builtin_mul_overflow(1, std::numeric_limits<int32_t>::min(), &result));
|
||||
EXPECT_THAT(result, std::numeric_limits<int32_t>::min());
|
||||
EXPECT_FALSE(custom_builtin_mul_overflow(0, std::numeric_limits<int32_t>::min(), &result));
|
||||
EXPECT_THAT(result, 0);
|
||||
|
||||
EXPECT_FALSE(custom_builtin_mul_overflow(0, 0, &result));
|
||||
EXPECT_THAT(result, 0);
|
||||
|
||||
EXPECT_FALSE(custom_builtin_mul_overflow(0, 1, &result));
|
||||
EXPECT_THAT(result, 0);
|
||||
EXPECT_FALSE(custom_builtin_mul_overflow(1, 0, &result));
|
||||
EXPECT_THAT(result, 0);
|
||||
|
||||
EXPECT_TRUE(custom_builtin_mul_overflow(1 << 16, 1 << 15, &result));
|
||||
|
||||
EXPECT_FALSE(custom_builtin_mul_overflow(95, 22605091, &result));
|
||||
EXPECT_THAT(result, 2147483645);
|
||||
|
||||
uint32_t uresult;
|
||||
EXPECT_FALSE(custom_builtin_mul_overflow(65535u, 65537u, &uresult));
|
||||
EXPECT_THAT(uresult, 4294967295u);
|
||||
EXPECT_TRUE(custom_builtin_mul_overflow(65537u, 65537u, &uresult));
|
||||
EXPECT_FALSE(custom_builtin_mul_overflow(0u, 4294967295u, &uresult));
|
||||
EXPECT_THAT(uresult, 0);
|
||||
EXPECT_FALSE(custom_builtin_mul_overflow(4294967295u, 0u, &uresult));
|
||||
EXPECT_THAT(uresult, 0);
|
||||
EXPECT_FALSE(custom_builtin_mul_overflow(0u, 0u, &uresult));
|
||||
EXPECT_THAT(uresult, 0);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename From, typename Into>
|
||||
|
||||
Reference in New Issue
Block a user