[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
+11 -7
View File
@@ -612,7 +612,8 @@ abiSpecificLibraries.linux_arm32_hfp = lib usr/lib
# targetSysRoot relative
crtFilesLocation.linux_arm32_hfp = usr/lib
runtimeDefinitions.linux_arm32_hfp = USE_GCC_UNWIND=1 KONAN_LINUX=1 \
KONAN_ARM32=1 USE_ELF_SYMBOLS=1 ELFSIZE=32 KONAN_NO_UNALIGNED_ACCESS=1
KONAN_ARM32=1 USE_ELF_SYMBOLS=1 ELFSIZE=32 KONAN_NO_UNALIGNED_ACCESS=1 \
KONAN_FORBID_BUILTIN_MUL_OVERFLOW=1
# Linux arm64
gccToolchain.linux_arm64 = $toolchainDependency.linux_arm64
@@ -710,7 +711,8 @@ abiSpecificLibraries.linux_mips32 = lib usr/lib
crtFilesLocation.linux_mips32 = usr/lib
# TODO: reconsider KONAN_NO_64BIT_ATOMIC, once target MIPS can do proper 64-bit load/store/CAS.
runtimeDefinitions.linux_mips32 = USE_GCC_UNWIND=1 KONAN_LINUX=1 KONAN_MIPS32=1 \
USE_ELF_SYMBOLS=1 ELFSIZE=32 KONAN_NO_64BIT_ATOMIC=1 KONAN_NO_UNALIGNED_ACCESS=1
USE_ELF_SYMBOLS=1 ELFSIZE=32 KONAN_NO_64BIT_ATOMIC=1 KONAN_NO_UNALIGNED_ACCESS=1 \
KONAN_FORBID_BUILTIN_MUL_OVERFLOW=1
# MIPSel
gccToolchain.linux_mipsel32 = $toolchainDependency.linux_mipsel32
@@ -749,7 +751,8 @@ abiSpecificLibraries.linux_mipsel32 = lib usr/lib
crtFilesLocation.linux_mipsel32 = usr/lib
# TODO: reconsider KONAN_NO_64BIT_ATOMIC, once target MIPS can do proper 64-bit load/store/CAS.
runtimeDefinitions.linux_mipsel32 = USE_GCC_UNWIND=1 KONAN_LINUX=1 \
KONAN_MIPSEL32=1 USE_ELF_SYMBOLS=1 ELFSIZE=32 KONAN_NO_64BIT_ATOMIC=1 KONAN_NO_UNALIGNED_ACCESS=1
KONAN_MIPSEL32=1 USE_ELF_SYMBOLS=1 ELFSIZE=32 KONAN_NO_64BIT_ATOMIC=1 KONAN_NO_UNALIGNED_ACCESS=1 \
KONAN_FORBID_BUILTIN_MUL_OVERFLOW=1
# Android ARM32, based on NDK for android-21.
targetToolchain.macos_x64-android_arm32 = target-toolchain-2-osx-android_ndk
@@ -779,7 +782,8 @@ clangNooptFlags.android_arm32 = -O1
targetSysRoot.android_arm32 = target-sysroot-1-android_ndk
linkerKonanFlags.android_arm32 = -lm -lc++_static -lc++abi -landroid -llog -latomic
runtimeDefinitions.android_arm32 = __ANDROID__ USE_GCC_UNWIND=1 USE_ELF_SYMBOLS=1 ELFSIZE=32 \
KONAN_ANDROID=1 KONAN_ARM32=1 KONAN_NO_UNALIGNED_ACCESS=1 KONAN_NO_64BIT_ATOMIC=1
KONAN_ANDROID=1 KONAN_ARM32=1 KONAN_NO_UNALIGNED_ACCESS=1 KONAN_NO_64BIT_ATOMIC=1 \
KONAN_FORBID_BUILTIN_MUL_OVERFLOW=1
# Android ARM64, based on NDK.
targetToolchain.macos_x64-android_arm64 = target-toolchain-2-osx-android_ndk
@@ -838,7 +842,7 @@ targetSysRoot.android_x86 = target-sysroot-1-android_ndk
linkerKonanFlags.android_x86 = -lm -lc++_static -lc++abi -landroid -llog -latomic
linkerNoDebugFlags.android_x86 = -Wl,-S
runtimeDefinitions.android_x86 = __ANDROID__ USE_GCC_UNWIND=1 USE_ELF_SYMBOLS=1 \
ELFSIZE=32 KONAN_ANDROID=1 KONAN_X86=1
ELFSIZE=32 KONAN_ANDROID=1 KONAN_X86=1 KONAN_FORBID_BUILTIN_MUL_OVERFLOW=1
# Android X64, based on NDK.
targetToolchain.macos_x64-android_x64 = target-toolchain-2-osx-android_ndk
@@ -959,7 +963,7 @@ linkerKonanFlags.mingw_x86 = -static-libgcc -static-libstdc++ \
mimallocLinkerDependencies.mingw_x86 = -lbcrypt
linkerOptimizationFlags.mingw_x86 = -Wl,--gc-sections
runtimeDefinitions.mingw_x86 = USE_GCC_UNWIND=1 USE_PE_COFF_SYMBOLS=1 KONAN_WINDOWS=1 \
UNICODE KONAN_X86=1 KONAN_NO_MEMMEM=1
UNICODE KONAN_X86=1 KONAN_NO_MEMMEM=1 KONAN_FORBID_BUILTIN_MUL_OVERFLOW=1
# WebAssembly 32-bit.
targetToolchain.macos_x64-wasm32 = target-toolchain-3-macos-wasm
@@ -994,7 +998,7 @@ lld.wasm32 = --allow-undefined --no-entry --global-base=0 --export-dynamic
runtimeDefinitions.wasm32 = KONAN_WASM=1 KONAN_NO_FFI=1 KONAN_NO_THREADS=1 \
KONAN_NO_EXCEPTIONS=1 KONAN_INTERNAL_DLMALLOC=1 KONAN_INTERNAL_SNPRINTF=1 \
KONAN_INTERNAL_NOW=1 KONAN_NO_MEMMEM KONAN_NO_CTORS_SECTION=1 KONAN_NO_BACKTRACE=1 \
KONAN_NO_EXTERNAL_CALLS_CHECKER=1
KONAN_NO_EXTERNAL_CALLS_CHECKER=1 KONAN_FORBID_BUILTIN_MUL_OVERFLOW=1
# The version of Kotlin/Native compiler
compilerVersion=@compilerVersion@
@@ -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>