diff --git a/kotlin-native/konan/konan.properties b/kotlin-native/konan/konan.properties index ae75e27780f..e114b231c98 100644 --- a/kotlin-native/konan/konan.properties +++ b/kotlin-native/konan/konan.properties @@ -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@ diff --git a/kotlin-native/runtime/src/main/cpp/Saturating.hpp b/kotlin-native/runtime/src/main/cpp/Saturating.hpp index 6c740a43763..c49f74470d5 100644 --- a/kotlin-native/runtime/src/main/cpp/Saturating.hpp +++ b/kotlin-native/runtime/src/main/cpp/Saturating.hpp @@ -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::min(). It can sometimes report overflow, when result equal to it, +// but it's okey for current usages +template +constexpr bool custom_builtin_mul_overflow(T lhs, T rhs, T* result) { + if (lhs != 0 && rhs != 0) { + if constexpr (std::is_signed_v) { + if (lhs == std::numeric_limits::min() || rhs == std::numeric_limits::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::max() / rhs_abs < lhs_abs) return true; + } + } else { + if (std::numeric_limits::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 constexpr auto saturating_mul(T lhs, U rhs) noexcept { static_assert(std::is_integral_v, "T must be integral"); @@ -144,8 +172,8 @@ constexpr auto saturating_mul(T lhs, U rhs) noexcept { static_assert(std::is_signed_v == std::is_signed_v, "T and U must have the same sign"); using Result = internal::wider_t; - Result result; - if (__builtin_mul_overflow(lhs, rhs, &result)) { + Result result{0}; + if (mul_overflow(static_cast(lhs), static_cast(rhs), &result)) { if (lhs >= 0 && rhs >= 0) { // Multiplying non-negative numbers caused an overflow => overflowed upwards. result = std::numeric_limits::max(); diff --git a/kotlin-native/runtime/src/main/cpp/SaturatingTest.cpp b/kotlin-native/runtime/src/main/cpp/SaturatingTest.cpp index 4f858699036..503f1a85dee 100644 --- a/kotlin-native/runtime/src/main/cpp/SaturatingTest.cpp +++ b/kotlin-native/runtime/src/main/cpp/SaturatingTest.cpp @@ -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::min(), 2, &result)); + EXPECT_TRUE(custom_builtin_mul_overflow(std::numeric_limits::min(), -1, &result)); + EXPECT_TRUE(custom_builtin_mul_overflow(std::numeric_limits::min(), -2, &result)); + EXPECT_FALSE(custom_builtin_mul_overflow(std::numeric_limits::min(), 1, &result)); + EXPECT_THAT(result, std::numeric_limits::min()); + EXPECT_FALSE(custom_builtin_mul_overflow(std::numeric_limits::min(), 0, &result)); + EXPECT_THAT(result, 0); + + EXPECT_TRUE(custom_builtin_mul_overflow(2, std::numeric_limits::min(), &result)); + EXPECT_TRUE(custom_builtin_mul_overflow(-1, std::numeric_limits::min(), &result)); + EXPECT_TRUE(custom_builtin_mul_overflow(-2, std::numeric_limits::min(), &result)); + EXPECT_FALSE(custom_builtin_mul_overflow(1, std::numeric_limits::min(), &result)); + EXPECT_THAT(result, std::numeric_limits::min()); + EXPECT_FALSE(custom_builtin_mul_overflow(0, std::numeric_limits::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