Bug 1443706 - Introduce ConstExprHashString(const char16_t*). r=jwalden

This is a `constexpr` alternative to HashString(const char16_t*). We can't make
HashString(const char16_t*) itself `constexpr` because HashUntilZero(const T*)
isn't in a form that older compilers (like GCC 4.9) allow to be made
`constexpr`. (The trick to satisfying those compilers is to use recursion
instead of iteration, to get the function into a single `return` statement.)

This requires making a bunch of other functions `constexpr` as well. It also
requires adding MOZ_{PUSH,POP}_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
macros to avoid some MSVC weirdness.

The introduction of RotateLeft5() partly undoes one of the patches from bug
1443342, but that's unavoidable.

This change will help with static allocation of static atoms (bug 1411469).

MozReview-Commit-ID: 7r3PnrQXb29
This commit is contained in:
Nicholas Nethercote 2018-03-08 10:27:14 +11:00
Родитель 22e8dbd8fb
Коммит cecefaa849
2 изменённых файлов: 72 добавлений и 26 удалений

Просмотреть файл

@ -65,7 +65,14 @@ static const uint32_t kGoldenRatioU32 = 0x9E3779B9U;
namespace detail {
inline uint32_t
MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW
constexpr uint32_t
RotateLeft5(uint32_t aValue)
{
return (aValue << 5) | (aValue >> 27);
}
constexpr uint32_t
AddU32ToHash(uint32_t aHash, uint32_t aValue)
{
/*
@ -89,7 +96,7 @@ AddU32ToHash(uint32_t aHash, uint32_t aValue)
* Otherwise, if |aHash| is 0 (as it often is for the beginning of a
* message), the expression
*
* mozilla::WrappingMultiply(kGoldenRatioU32, RotateBitsLeft(aHash, 5))
* mozilla::WrappingMultiply(kGoldenRatioU32, RotateLeft5(aHash))
* |xor|
* aValue
*
@ -110,14 +117,14 @@ AddU32ToHash(uint32_t aHash, uint32_t aValue)
* more than enough for our purposes.)
*/
return mozilla::WrappingMultiply(kGoldenRatioU32,
RotateLeft(aHash, 5) ^ aValue);
RotateLeft5(aHash) ^ aValue);
}
/**
* AddUintptrToHash takes sizeof(uintptr_t) as a template parameter.
*/
template<size_t PtrSize>
inline uint32_t
constexpr uint32_t
AddUintptrToHash(uint32_t aHash, uintptr_t aValue)
{
return AddU32ToHash(aHash, static_cast<uint32_t>(aValue));
@ -173,7 +180,7 @@ AddToHash(uint32_t aHash, A* aA)
// implicitly converted to 32 bits and then passed to AddUintptrToHash() to be hashed.
template<typename T,
typename U = typename mozilla::EnableIf<mozilla::IsIntegral<T>::value>::Type>
MOZ_MUST_USE inline uint32_t
MOZ_MUST_USE constexpr uint32_t
AddToHash(uint32_t aHash, T aA)
{
return detail::AddUintptrToHash<sizeof(T)>(aHash, aA);
@ -213,6 +220,19 @@ HashUntilZero(const T* aStr)
return hash;
}
// This is a `constexpr` alternative to HashUntilZero(const T*). It should
// only be used for compile-time computation because it uses recursion.
// XXX: once support for GCC 4.9 is dropped, this function should be removed
// and HashUntilZero(const T*) should be made `constexpr`.
template<typename T>
constexpr uint32_t
ConstExprHashUntilZero(const T* aStr, uint32_t aHash)
{
return !*aStr
? aHash
: ConstExprHashUntilZero(aStr + 1, AddToHash(aHash, *aStr));
}
template<typename T>
uint32_t
HashKnownLength(const T* aStr, size_t aLength)
@ -257,6 +277,21 @@ HashString(const char16_t* aStr)
return detail::HashUntilZero(aStr);
}
// This is a `constexpr` alternative to HashString(const char16_t*). It should
// only be used for compile-time computation because it uses recursion.
//
// You may need to use the
// MOZ_{PUSH,POP}_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING macros if you use
// this function. See the comment on those macros' definitions for more detail.
//
// XXX: once support for GCC 4.9 is dropped, this function should be removed
// and HashString(const char16_t*) should be made `constexpr`.
MOZ_MUST_USE constexpr uint32_t
ConstExprHashString(const char16_t* aStr)
{
return detail::ConstExprHashUntilZero(aStr, 0);
}
MOZ_MUST_USE inline uint32_t
HashString(const char16_t* aStr, size_t aLength)
{

Просмотреть файл

@ -94,29 +94,16 @@ struct WrapToSignedHelper
* happening.
*/
template<typename UnsignedType>
inline constexpr typename detail::WrapToSignedHelper<UnsignedType>::SignedType
constexpr typename detail::WrapToSignedHelper<UnsignedType>::SignedType
WrapToSigned(UnsignedType aValue)
{
return detail::WrapToSignedHelper<UnsignedType>::compute(aValue);
}
// The |mozilla::Wrapping*| functions aren't constexpr because MSVC warns about
// well-defined unsigned integer overflows that may occur within the constexpr
// math. If/when MSVC fix this bug, we should make them all constexpr.
//
// https://msdn.microsoft.com/en-us/library/4kze989h.aspx (C4307)
// https://developercommunity.visualstudio.com/content/problem/211134/unsigned-integer-overflows-in-constexpr-functionsa.html (bug report)
//
// For now there's no practical, readable way to avoid such overflows in pure
// C++. And we can't add narrow #pragmas where overflow can occur to disable
// the warnings, because constexpr apparently causes the warning to be emitted
// at the outermost call *sites* (so every user of |mozilla::Wrapping*| would
// have to add them).
namespace detail {
template<typename T>
inline constexpr T
constexpr T
ToResult(typename MakeUnsigned<T>::Type aUnsigned)
{
// We could *always* return WrapToSigned and rely on unsigned conversion to
@ -132,7 +119,7 @@ private:
public:
MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW
static T compute(T aX, T aY)
static constexpr T compute(T aX, T aY)
{
return ToResult<T>(static_cast<UnsignedT>(aX) + static_cast<UnsignedT>(aY));
}
@ -165,7 +152,7 @@ public:
* permissibly -- triggers different behavior.
*/
template<typename T>
inline T
constexpr T
WrappingAdd(T aX, T aY)
{
return detail::WrappingAddHelper<T>::compute(aX, aY);
@ -181,7 +168,7 @@ private:
public:
MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW
static T compute(T aX, T aY)
static constexpr T compute(T aX, T aY)
{
return ToResult<T>(static_cast<UnsignedT>(aX) - static_cast<UnsignedT>(aY));
}
@ -215,7 +202,7 @@ public:
* permissibly -- triggers different behavior.
*/
template<typename T>
inline T
constexpr T
WrappingSubtract(T aX, T aY)
{
return detail::WrappingSubtractHelper<T>::compute(aX, aY);
@ -231,7 +218,7 @@ private:
public:
MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW
static T compute(T aX, T aY)
static constexpr T compute(T aX, T aY)
{
// Begin with |1U| to ensure the overall operation chain is never promoted
// to signed integer operations that might have *signed* integer overflow.
@ -277,12 +264,36 @@ public:
* or similar -- quite permissibly -- triggers different behavior.
*/
template<typename T>
inline T
constexpr T
WrappingMultiply(T aX, T aY)
{
return detail::WrappingMultiplyHelper<T>::compute(aX, aY);
}
// The |mozilla::Wrapping*| functions are constexpr. Unfortunately, MSVC warns
// about well-defined unsigned integer overflows that may occur within the
// constexpr math.
//
// https://msdn.microsoft.com/en-us/library/4kze989h.aspx (C4307)
// https://developercommunity.visualstudio.com/content/problem/211134/unsigned-integer-overflows-in-constexpr-functionsa.html (bug report)
//
// So we need a way to suppress these warnings. Unfortunately, the warnings are
// issued at the very top of the `constexpr` chain, which is often some
// distance from the triggering Wrapping*() operation. So we can't suppress
// them within this file. Instead, callers have to do it with these macros.
//
// If/when MSVC fix this bug, we should remove these macros.
#ifdef _MSC_VER
#define MOZ_PUSH_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING \
__pragma(warning(push)) \
__pragma(warning(disable:4307))
#define MOZ_POP_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING \
__pragma(warning(pop))
#else
#define MOZ_PUSH_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
#define MOZ_POP_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
#endif
} /* namespace mozilla */
#endif /* mozilla_WrappingOperations_h */