From 3d718db9a1a451adce17b9b944428dab76a4913a Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Tue, 10 Mar 2020 18:29:03 -0400
Subject: [PATCH] Cleanup and optimize LLMaterialID to be trivially
 copy/movable and introduce SIMD operators

---
 indra/llprimitive/llmaterialid.cpp |  75 ------------------
 indra/llprimitive/llmaterialid.h   | 122 +++++++++++++++++++++++++----
 2 files changed, 108 insertions(+), 89 deletions(-)

diff --git a/indra/llprimitive/llmaterialid.cpp b/indra/llprimitive/llmaterialid.cpp
index f88a607c4f9..6f3b45a5b8e 100644
--- a/indra/llprimitive/llmaterialid.cpp
+++ b/indra/llprimitive/llmaterialid.cpp
@@ -35,11 +35,6 @@
 
 const LLMaterialID LLMaterialID::null;
 
-LLMaterialID::LLMaterialID()
-{
-	clear();
-}
-
 LLMaterialID::LLMaterialID(const LLSD& pMaterialID)
 {
 	llassert(pMaterialID.isBinary());
@@ -56,61 +51,11 @@ LLMaterialID::LLMaterialID(const void* pMemory)
 	set(pMemory);
 }
 
-LLMaterialID::LLMaterialID(const LLMaterialID& pOtherMaterialID)
-{
-	copyFromOtherMaterialID(pOtherMaterialID);
-}
-
 LLMaterialID::LLMaterialID(const LLUUID& lluid)
 {
 	set(lluid.mData);
 }
 
-LLMaterialID::~LLMaterialID()
-{
-}
-
-bool LLMaterialID::operator == (const LLMaterialID& pOtherMaterialID) const
-{
-	return (compareToOtherMaterialID(pOtherMaterialID) == 0);
-}
-
-bool LLMaterialID::operator != (const LLMaterialID& pOtherMaterialID) const
-{
-	return (compareToOtherMaterialID(pOtherMaterialID) != 0);
-}
-
-bool LLMaterialID::operator < (const LLMaterialID& pOtherMaterialID) const
-{
-	return (compareToOtherMaterialID(pOtherMaterialID) < 0);
-}
-
-bool LLMaterialID::operator <= (const LLMaterialID& pOtherMaterialID) const
-{
-	return (compareToOtherMaterialID(pOtherMaterialID) <= 0);
-}
-
-bool LLMaterialID::operator > (const LLMaterialID& pOtherMaterialID) const
-{
-	return (compareToOtherMaterialID(pOtherMaterialID) > 0);
-}
-
-bool LLMaterialID::operator >= (const LLMaterialID& pOtherMaterialID) const
-{
-	return (compareToOtherMaterialID(pOtherMaterialID) >= 0);
-}
-
-LLMaterialID& LLMaterialID::operator = (const LLMaterialID& pOtherMaterialID)
-{
-	copyFromOtherMaterialID(pOtherMaterialID);
-	return (*this);
-}
-
-bool LLMaterialID::isNull() const
-{
-	return (compareToOtherMaterialID(LLMaterialID::null) == 0);
-}
-
 const U8* LLMaterialID::get() const
 {
 	return mID;
@@ -161,28 +106,8 @@ std::ostream& operator<<(std::ostream& s, const LLMaterialID &material_id)
 	return s;
 }
 
-
 void LLMaterialID::parseFromBinary (const LLSD::Binary& pMaterialID)
 {
 	llassert(pMaterialID.size() == (MATERIAL_ID_SIZE * sizeof(U8)));
 	memcpy(mID, &pMaterialID[0], MATERIAL_ID_SIZE * sizeof(U8));
 }
-
-void LLMaterialID::copyFromOtherMaterialID(const LLMaterialID& pOtherMaterialID)
-{
-	memcpy(mID, pOtherMaterialID.get(), MATERIAL_ID_SIZE * sizeof(U8));
-}
-
-int LLMaterialID::compareToOtherMaterialID(const LLMaterialID& pOtherMaterialID) const
-{
-	int retVal = 0;
-
-	for (unsigned int i = 0U; (retVal == 0) && (i < static_cast<unsigned int>(MATERIAL_ID_SIZE / sizeof(U32))); ++i)
-	{
-		const U32 *thisValue = reinterpret_cast<const U32*>(&get()[i * sizeof(U32)]);
-		const U32 *otherValue = reinterpret_cast<const U32*>(&pOtherMaterialID.get()[i * sizeof(U32)]);
-		retVal = ((*thisValue < *otherValue) ? -1 : ((*thisValue > *otherValue) ? 1 : 0));
-	}
-
-	return retVal;
-}
diff --git a/indra/llprimitive/llmaterialid.h b/indra/llprimitive/llmaterialid.h
index ee663f8f99f..3f7a0bb3ed6 100644
--- a/indra/llprimitive/llmaterialid.h
+++ b/indra/llprimitive/llmaterialid.h
@@ -30,30 +30,126 @@
 #define MATERIAL_ID_SIZE 16
 
 #include <string>
+#include <immintrin.h>
 #include "llsd.h"
 
 class LLMaterialID
 {
 public:
-	LLMaterialID();
+	LLMaterialID() = default;
 	LLMaterialID(const LLSD& pMaterialID);
 	LLMaterialID(const LLSD::Binary& pMaterialID);
 	LLMaterialID(const void* pMemory);
-	LLMaterialID(const LLMaterialID& pOtherMaterialID);
 	LLMaterialID(const LLUUID& lluid);
-	~LLMaterialID();
 
-	bool          operator == (const LLMaterialID& pOtherMaterialID) const;
-	bool          operator != (const LLMaterialID& pOtherMaterialID) const;
+// BEGIN BOOST
+// Contains code from the Boost Library with license below.
+/*
+ *            Copyright Andrey Semashev 2013.
+ * Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ */
+	LL_FORCE_INLINE __m128i load_unaligned_si128(const U8* p) const
+	{
+#if defined(AL_AVX)
+		return _mm_lddqu_si128(reinterpret_cast<const __m128i*>(p));
+#else
+		return _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
+#endif
+	}
 
-	bool          operator < (const LLMaterialID& pOtherMaterialID) const;
-	bool          operator <= (const LLMaterialID& pOtherMaterialID) const;
-	bool          operator > (const LLMaterialID& pOtherMaterialID) const;
-	bool          operator >= (const LLMaterialID& pOtherMaterialID) const;
+	bool operator==(const LLMaterialID& rhs) const
+	{
+		__m128i mm_left = load_unaligned_si128(mID);
+		__m128i mm_right = load_unaligned_si128(rhs.mID);
 
-	LLMaterialID& operator = (const LLMaterialID& pOtherMaterialID);
+		__m128i mm_cmp = _mm_cmpeq_epi32(mm_left, mm_right);
+#if defined(AL_AVX)
+		return _mm_test_all_ones(mm_cmp);
+#else
+		return _mm_movemask_epi8(mm_cmp) == 0xFFFF;
+#endif
+	}
 
-	bool          isNull() const;
+	bool operator!=(const LLMaterialID& rhs) const
+	{
+		return !((*this) == rhs);
+	}
+
+	bool operator<(const LLMaterialID& rhs) const
+	{
+		__m128i mm_left = _mm_lddqu_si128(reinterpret_cast<const __m128i*>(mID));
+		__m128i mm_right = _mm_lddqu_si128(reinterpret_cast<const __m128i*>(rhs.mID));
+
+		// To emulate lexicographical_compare behavior we have to perform two comparisons - the forward and reverse one.
+		// Then we know which bytes are equivalent and which ones are different, and for those different the comparison results
+		// will be opposite. Then we'll be able to find the first differing comparison result (for both forward and reverse ways),
+		// and depending on which way it is for, this will be the result of the operation. There are a few notes to consider:
+		//
+		// 1. Due to little endian byte order the first bytes go into the lower part of the xmm registers,
+		//    so the comparison results in the least significant bits will actually be the most signigicant for the final operation result.
+		//    This means we have to determine which of the comparison results have the least significant bit on, and this is achieved with
+		//    the "(x - 1) ^ x" trick.
+		// 2. Because there is only signed comparison in SSE/AVX, we have to invert byte comparison results whenever signs of the corresponding
+		//    bytes are different. I.e. in signed comparison it's -1 < 1, but in unsigned it is the opposite (255 > 1). To do that we XOR left and right,
+		//    making the most significant bit of each byte 1 if the signs are different, and later apply this mask with another XOR to the comparison results.
+		// 3. pcmpgtw compares for "greater" relation, so we swap the arguments to get what we need.
+
+		const __m128i mm_signs_mask = _mm_xor_si128(mm_left, mm_right);
+
+		__m128i mm_cmp = _mm_cmpgt_epi8(mm_right, mm_left), mm_rcmp = _mm_cmpgt_epi8(mm_left, mm_right);
+
+		mm_cmp = _mm_xor_si128(mm_signs_mask, mm_cmp);
+		mm_rcmp = _mm_xor_si128(mm_signs_mask, mm_rcmp);
+
+		uint32_t cmp = static_cast<uint32_t>(_mm_movemask_epi8(mm_cmp)), rcmp = static_cast<uint32_t>(_mm_movemask_epi8(mm_rcmp));
+
+		cmp = (cmp - 1u) ^ cmp;
+		rcmp = (rcmp - 1u) ^ rcmp;
+
+		return static_cast<uint16_t>(cmp) < static_cast<uint16_t>(rcmp);
+	}
+
+	bool operator>(const LLMaterialID& rhs) const
+	{
+		return rhs < (*this);
+	}
+
+	bool operator<=(const LLMaterialID& rhs) const
+	{
+		return !(rhs < (*this));
+	}
+
+	bool operator>=(const LLMaterialID& rhs) const
+	{
+		return !((*this) < rhs);
+	}
+
+	bool isNull() const
+	{
+		__m128i mm = load_unaligned_si128(mID);
+#if defined(AL_AVX)
+		return _mm_test_all_zeros(mm, mm) != 0;
+#else
+		mm = _mm_cmpeq_epi8(mm, _mm_setzero_si128());
+		return _mm_movemask_epi8(mm) == 0xFFFF;
+#endif
+	}
+
+	inline size_t hash() const
+	{
+		size_t seed = 0;
+		for (U8 i = 0; i < 4; ++i)
+		{
+			seed ^= static_cast<size_t>(mID[i * 4]) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+			seed ^= static_cast<size_t>(mID[i * 4 + 1]) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+			seed ^= static_cast<size_t>(mID[i * 4 + 2]) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+			seed ^= static_cast<size_t>(mID[i * 4 + 3]) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+		}
+		return seed;
+	}
+// END BOOST
 
 	const U8*     get() const;
 	void          set(const void* pMemory);
@@ -68,10 +164,8 @@ class LLMaterialID
 
 private:
 	void parseFromBinary(const LLSD::Binary& pMaterialID);
-	void copyFromOtherMaterialID(const LLMaterialID& pOtherMaterialID);
-	int  compareToOtherMaterialID(const LLMaterialID& pOtherMaterialID) const;
 
-	U8 mID[MATERIAL_ID_SIZE];
+	U8 mID[MATERIAL_ID_SIZE] = {};
 } ;
 
 #endif // LL_LLMATERIALID_H
-- 
GitLab