[K/N] add custom builtin_mul_overflow to workaround llvm bug
^KT-52365
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user