mirror of
https://github.com/vdemydiuk/mtapi.git
synced 2026-08-18 13:18:12 +00:00
192 lines
5.5 KiB
C++
Executable File
192 lines
5.5 KiB
C++
Executable File
// Copyright 2020-2023 Daniel Lemire
|
|
// Copyright 2023 Matt Borland
|
|
// Distributed under the Boost Software License, Version 1.0.
|
|
// https://www.boost.org/LICENSE_1_0.txt
|
|
|
|
// If the architecture (e.g. ARM) does not have __int128 we need to emulate it
|
|
|
|
#ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_EMULATED128_HPP
|
|
#define BOOST_JSON_DETAIL_CHARCONV_DETAIL_EMULATED128_HPP
|
|
|
|
#include <boost/json/detail/charconv/detail/config.hpp>
|
|
#include <cstdint>
|
|
#include <cassert>
|
|
|
|
namespace boost { namespace json { namespace detail { namespace charconv { namespace detail {
|
|
|
|
// Compilers might support built-in 128-bit integer types. However, it seems that
|
|
// emulating them with a pair of 64-bit integers actually produces a better code,
|
|
// so we avoid using those built-ins. That said, they are still useful for
|
|
// implementing 64-bit x 64-bit -> 128-bit multiplication.
|
|
|
|
struct uint128
|
|
{
|
|
std::uint64_t high;
|
|
std::uint64_t low;
|
|
|
|
uint128& operator+=(std::uint64_t n) & noexcept
|
|
{
|
|
#if BOOST_JSON_HAS_BUILTIN(__builtin_addcll)
|
|
|
|
unsigned long long carry;
|
|
low = __builtin_addcll(low, n, 0, &carry);
|
|
high = __builtin_addcll(high, 0, carry, &carry);
|
|
|
|
#elif BOOST_JSON_HAS_BUILTIN(__builtin_ia32_addcarryx_u64)
|
|
|
|
unsigned long long result;
|
|
auto carry = __builtin_ia32_addcarryx_u64(0, low, n, &result);
|
|
low = result;
|
|
__builtin_ia32_addcarryx_u64(carry, high, 0, &result);
|
|
high = result;
|
|
|
|
#elif defined(BOOST_MSVC) && defined(_M_X64)
|
|
|
|
auto carry = _addcarry_u64(0, low, n, &low);
|
|
_addcarry_u64(carry, high, 0, &high);
|
|
|
|
#else
|
|
|
|
auto sum = low + n;
|
|
high += (sum < low ? 1 : 0);
|
|
low = sum;
|
|
|
|
#endif
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
static inline std::uint64_t umul64(std::uint32_t x, std::uint32_t y) noexcept
|
|
{
|
|
#if defined(BOOST_JSON_HAS_MSVC_32BIT_INTRINSICS) && !defined(_M_ARM)
|
|
|
|
return __emulu(x, y);
|
|
|
|
#else
|
|
|
|
return x * static_cast<std::uint64_t>(y);
|
|
|
|
#endif
|
|
}
|
|
|
|
// Get 128-bit result of multiplication of two 64-bit unsigned integers.
|
|
BOOST_JSON_SAFEBUFFERS inline uint128 umul128(std::uint64_t x, std::uint64_t y) noexcept
|
|
{
|
|
#if defined(BOOST_HAS_INT128)
|
|
|
|
auto result = static_cast<boost::uint128_type>(x) * static_cast<boost::uint128_type>(y);
|
|
return {static_cast<std::uint64_t>(result >> 64), static_cast<std::uint64_t>(result)};
|
|
|
|
#elif defined(BOOST_JSON_HAS_MSVC_64BIT_INTRINSICS) && !defined(_M_ARM64)
|
|
|
|
std::uint64_t high;
|
|
std::uint64_t low = _umul128(x, y, &high);
|
|
return {high, low};
|
|
|
|
// https://developer.arm.com/documentation/dui0802/a/A64-General-Instructions/UMULH
|
|
#elif defined(_M_ARM64) && !defined(__MINGW32__)
|
|
|
|
std::uint64_t high = __umulh(x, y);
|
|
std::uint64_t low = x * y;
|
|
return {high, low};
|
|
|
|
#else
|
|
|
|
auto a = static_cast<std::uint32_t>(x >> 32);
|
|
auto b = static_cast<std::uint32_t>(x);
|
|
auto c = static_cast<std::uint32_t>(y >> 32);
|
|
auto d = static_cast<std::uint32_t>(y);
|
|
|
|
auto ac = umul64(a, c);
|
|
auto bc = umul64(b, c);
|
|
auto ad = umul64(a, d);
|
|
auto bd = umul64(b, d);
|
|
|
|
auto intermediate = (bd >> 32) + static_cast<std::uint32_t>(ad) + static_cast<std::uint32_t>(bc);
|
|
|
|
return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32),
|
|
(intermediate << 32) + static_cast<std::uint32_t>(bd)};
|
|
|
|
#endif
|
|
}
|
|
|
|
BOOST_JSON_SAFEBUFFERS inline std::uint64_t umul128_upper64(std::uint64_t x, std::uint64_t y) noexcept
|
|
{
|
|
#if defined(BOOST_HAS_INT128)
|
|
|
|
auto result = static_cast<boost::uint128_type>(x) * static_cast<boost::uint128_type>(y);
|
|
return static_cast<std::uint64_t>(result >> 64);
|
|
|
|
#elif defined(BOOST_JSON_HAS_MSVC_64BIT_INTRINSICS)
|
|
|
|
return __umulh(x, y);
|
|
|
|
#else
|
|
|
|
auto a = static_cast<std::uint32_t>(x >> 32);
|
|
auto b = static_cast<std::uint32_t>(x);
|
|
auto c = static_cast<std::uint32_t>(y >> 32);
|
|
auto d = static_cast<std::uint32_t>(y);
|
|
|
|
auto ac = umul64(a, c);
|
|
auto bc = umul64(b, c);
|
|
auto ad = umul64(a, d);
|
|
auto bd = umul64(b, d);
|
|
|
|
auto intermediate = (bd >> 32) + static_cast<std::uint32_t>(ad) + static_cast<std::uint32_t>(bc);
|
|
|
|
return ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32);
|
|
|
|
#endif
|
|
}
|
|
|
|
// Get upper 128-bits of multiplication of a 64-bit unsigned integer and a 128-bit
|
|
// unsigned integer.
|
|
BOOST_JSON_SAFEBUFFERS inline uint128 umul192_upper128(std::uint64_t x, uint128 y) noexcept
|
|
{
|
|
auto r = umul128(x, y.high);
|
|
r += umul128_upper64(x, y.low);
|
|
return r;
|
|
}
|
|
|
|
// Get upper 64-bits of multiplication of a 32-bit unsigned integer and a 64-bit
|
|
// unsigned integer.
|
|
inline std::uint64_t umul96_upper64(std::uint32_t x, std::uint64_t y) noexcept
|
|
{
|
|
#if defined(BOOST_HAS_INT128) || defined(BOOST_JSON_HAS_MSVC_64BIT_INTRINSICS)
|
|
|
|
return umul128_upper64(static_cast<std::uint64_t>(x) << 32, y);
|
|
|
|
#else
|
|
|
|
auto yh = static_cast<std::uint32_t>(y >> 32);
|
|
auto yl = static_cast<std::uint32_t>(y);
|
|
|
|
auto xyh = umul64(x, yh);
|
|
auto xyl = umul64(x, yl);
|
|
|
|
return xyh + (xyl >> 32);
|
|
|
|
#endif
|
|
}
|
|
|
|
// Get lower 128-bits of multiplication of a 64-bit unsigned integer and a 128-bit
|
|
// unsigned integer.
|
|
BOOST_JSON_SAFEBUFFERS inline uint128 umul192_lower128(std::uint64_t x, uint128 y) noexcept
|
|
{
|
|
auto high = x * y.high;
|
|
auto highlow = umul128(x, y.low);
|
|
return {high + highlow.high, highlow.low};
|
|
}
|
|
|
|
// Get lower 64-bits of multiplication of a 32-bit unsigned integer and a 64-bit
|
|
// unsigned integer.
|
|
inline std::uint64_t umul96_lower64(std::uint32_t x, std::uint64_t y) noexcept
|
|
{
|
|
return x * y;
|
|
}
|
|
|
|
}}}}} // Namespaces
|
|
|
|
#endif // BOOST_JSON_DETAIL_CHARCONV_DETAIL_EMULATED128_HPP
|