[K/N] add custom builtin_mul_overflow to workaround llvm bug

^KT-52365
This commit is contained in:
Pavel Kunyavskiy
2022-05-13 13:24:41 +02:00
committed by Space
parent 0da23198e6
commit 134647169e
3 changed files with 87 additions and 9 deletions
@@ -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>