QUuid: simplify the three-way comparison functions to make them constexpr

While retaining the old sorting order, this allows us to simplify the
ifdef'ery and produces much better code.

With Clang, an equality check is
        vmovdqu (%rdi), %xmm0
        vpxor   (%rsi), %xmm0, %xmm0
        vptest  %xmm0, %xmm0
        sete    %al
in C++20 mode.

GCC generates four 64-bit loads instead of using vectors:
        movbeq  (%rdi), %rax
        movbeq  8(%rdi), %rdx
        movbeq  (%rsi), %r8
        movbeq  8(%rsi), %rcx
        movq    %rdx, %r10
        movq    %rax, %r11
        movq    %r8, %rdx
        movq    %rcx, %rax
        xorq    %r10, %rax
        xorq    %r11, %rdx
        orq     %rdx, %rax
        sete    %al
(the four MOV in the middle don't seem necessary)

For the sorting case, the compilers need to generate extra code
because of the check on the variant, something I'm scheduling for
removal in Qt 7.0. For long-term sorting code, both GCC and Clang
generate four 64-bit load-and-swap-endianness instructions, but Clang
for some reason also kept the 128-bit vector code (I'm guessing it's a
minor optimization bug that will be corrected in due time).

Change-Id: I46feca3a447244a8ba19fffd17dceacc8e528c3e
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
(cherry picked from commit 15f753ca5a60b5273d243f528978e25c28a9b56d)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Thiago Macieira 2024-06-27 09:55:21 -07:00 committed by Qt Cherry-pick Bot
parent 079b8b16d2
commit bcfa650ced
2 changed files with 42 additions and 66 deletions

View File

@ -910,50 +910,17 @@ QUuid::Version QUuid::version() const noexcept
/*! /*!
\fn bool QUuid::operator<(const QUuid &lhs, const QUuid &rhs) \fn bool QUuid::operator<(const QUuid &lhs, const QUuid &rhs)
Returns \c true if \a lhs QUuid has the same \l{Variant field}
{variant field} as the \a rhs QUuid and is lexicographically
\e{before} the \a rhs QUuid. If the \a rhs QUuid has a
different variant field, the return value is determined by
comparing the two \l{QUuid::Variant} {variants}.
\sa variant()
*/
/*!
\fn bool QUuid::operator>(const QUuid &lhs, const QUuid &rhs) \fn bool QUuid::operator>(const QUuid &lhs, const QUuid &rhs)
Returns \c true if \a lhs QUuid has the same \l{Variant field}
{variant field} as the \a rhs QUuid and is lexicographically
\e{after} the \a rhs QUuid. If the \a rhs QUuid has a
different variant field, the return value is determined by
comparing the two \l{QUuid::Variant} {variants}.
\sa variant()
*/
/*!
\fn bool QUuid::operator<=(const QUuid &lhs, const QUuid &rhs) \fn bool QUuid::operator<=(const QUuid &lhs, const QUuid &rhs)
\since 5.5
Returns \c true if \a lhs has the same \l{Variant field}
{variant field} as \a rhs and is lexicographically
\e{not after} \a rhs. If \a rhs has a
different variant field, the return value is determined by
comparing the two \l{QUuid::Variant} {variants}.
\sa {QUuid::}{variant()}
*/
/*!
\fn bool QUuid::operator>=(const QUuid &lhs, const QUuid &rhs) \fn bool QUuid::operator>=(const QUuid &lhs, const QUuid &rhs)
\since 5.5 \since 5.5
Returns \c true if \a lhs has the same \l{Variant field} Performs a comparison of \a lhs against \a rhs and returns \c true if the
{variant field} as \a rhs and is lexicographically relative sorting of \a lhs and \a rhs is correct for the operation in
\e{not before} \a rhs. If \a rhs has a question, \c false otherwise. Note that the sorting performed by this
different variant field, the return value is determined by functions may not be equal to the sorting of the strings created by
comparing the two \l{QUuid::Variant} {variants}. toString(), nor the integers toId128(), or the byte array returned by
toBytes() and toRfc4122().
\sa {QUuid::}{variant()} \sa {QUuid::}{variant()}
*/ */

View File

@ -126,29 +126,45 @@ QT_WARNING_POP
private: private:
friend constexpr bool comparesEqual(const QUuid &lhs, const QUuid &rhs) noexcept friend constexpr bool comparesEqual(const QUuid &lhs, const QUuid &rhs) noexcept
{ {
if (lhs.data1 != rhs.data1 || lhs.data2 != rhs.data2 || lhs.data3 != rhs.data3) return is_eq(compareThreeWay_helper(lhs, rhs));
return false;
for (uint i = 0; i < 8; i++) {
if (lhs.data4[i] != rhs.data4[i])
return false;
}
return true;
} }
friend Qt::strong_ordering compareThreeWay(const QUuid &lhs, const QUuid &rhs) noexcept static constexpr Qt::strong_ordering
compareThreeWay_helper(const QUuid &lhs, const QUuid &rhs) noexcept
{ {
if (const auto c = Qt::compareThreeWay(lhs.variant(), rhs.variant()); !is_eq(c)) #if defined(__cpp_lib_bit_cast) && defined(QT_SUPPORTS_INT128)
return c; quint128 lu = qFromBigEndian(std::bit_cast<quint128>(lhs));
if (const auto c = Qt::compareThreeWay(lhs.data1, rhs.data1); !is_eq(c)) quint128 ru = qFromBigEndian(std::bit_cast<quint128>(rhs));
return c; return Qt::compareThreeWay(lu, ru);
if (const auto c = Qt::compareThreeWay(lhs.data2, rhs.data2); !is_eq(c)) #else
return c; auto make_int = [](const QUuid &u) {
if (const auto c = Qt::compareThreeWay(lhs.data3, rhs.data3); !is_eq(c)) quint64 result = quint64(u.data3) << 48;
result |= quint64(u.data2) << 32;
return qFromBigEndian(result | u.data1);
};
if (const auto c = Qt::compareThreeWay(make_int(lhs), make_int(rhs)); !is_eq(c))
return c; return c;
int c = std::memcmp(lhs.data4, rhs.data4, sizeof(lhs.data4)); for (unsigned i = 0; i < sizeof(lhs.data4); ++i) {
return Qt::compareThreeWay(c, 0); if (const auto c = Qt::compareThreeWay(lhs.data4[i], rhs.data4[i]); !is_eq(c))
return c;
}
return Qt::strong_ordering::equal;
#endif
}
friend constexpr Qt::strong_ordering compareThreeWay(const QUuid &lhs, const QUuid &rhs) noexcept
{
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) && !defined(QT_BOOTSTRAPPED)
// Keep the old sorting order from before Qt 6.8, which sorted first on
// variant(). We don't need the exact algorithm to achieve same results.
auto fastVariant = [](const QUuid &uuid) {
quint8 v = uuid.data4[0];
// i.e.: return v >= Microsoft ? v : v >= DCE ? DCE : NCS;
return v >= 0xC0 ? v & 0xE0 : v >= 0x80 ? 0x80 : 0;
};
if (const auto c = Qt::compareThreeWay(fastVariant(lhs), fastVariant(rhs)); !is_eq(c))
return c;
#endif
return compareThreeWay_helper(lhs, rhs);
} }
public: public:
@ -168,15 +184,8 @@ public:
bool operator<(const QUuid &other) const noexcept; bool operator<(const QUuid &other) const noexcept;
bool operator>(const QUuid &other) const noexcept; bool operator>(const QUuid &other) const noexcept;
#else #else
private: Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(QUuid)
#if defined(__cpp_lib_three_way_comparison) && !defined(Q_QDOC)
QT_DECLARE_3WAY_HELPER_STRONG(QUuid, QUuid, /* non-constexpr */, /* no attributes */)
#else
QT_DECLARE_ORDERING_HELPER_STRONG(QUuid, QUuid, /* non-constexpr */, /* no attributes */)
#endif // defined(__cpp_lib_three_way_comparison) && !defined(Q_QDOC)
Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QUuid)
#endif // QT_CORE_REMOVED_SINCE(6, 8) #endif // QT_CORE_REMOVED_SINCE(6, 8)
public:
#if defined(Q_OS_WIN) || defined(Q_QDOC) #if defined(Q_OS_WIN) || defined(Q_QDOC)
// On Windows we have a type GUID that is used by the platform API, so we // On Windows we have a type GUID that is used by the platform API, so we
// provide convenience operators to cast from and to this type. // provide convenience operators to cast from and to this type.