Fix handling Memset<> assignment and comparison

Previously the compiler would implicitly declare a copy constructor and
assignment operator for classes derived from Memset<>, if they didn't
have user-defined ones. This can cause them to have uninitialized
padding bytes. By defining them ourselves using memcpy() we can ensure
they're zero-initialized.

Also define equality and less-than operators. The latter makes classes
derived from Memset<> suitable as std::map<> keys.

is_memcmparable<> now no longer works on classed derived from Memset<>,
because it uses std::is_trivially_copyable<> which isn't true when a
user-defined copy constructor or assignment operator is provided.
Instead just rely on Memset<>'s new equality operators.

Bug: b/131246679
Change-Id: I6e4963db8186955d8d3d3ef356fa42ef6a024c64
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/42728
Presubmit-Ready: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/src/Device/Blitter.hpp b/src/Device/Blitter.hpp
index 435cbaf..8006945 100644
--- a/src/Device/Blitter.hpp
+++ b/src/Device/Blitter.hpp
@@ -89,12 +89,6 @@
 		    , destSamples(destSamples)
 		{}
 
-		bool operator==(const State &state) const
-		{
-			static_assert(is_memcmparable<State>::value, "Cannot memcmp State");
-			return memcmp(this, &state, sizeof(State)) == 0;
-		}
-
 		vk::Format sourceFormat;
 		vk::Format destFormat;
 		int srcSamples = 0;
diff --git a/src/Device/LRUCache.hpp b/src/Device/LRUCache.hpp
index d4d2bdb..dedca45 100644
--- a/src/Device/LRUCache.hpp
+++ b/src/Device/LRUCache.hpp
@@ -73,19 +73,6 @@
 	std::unordered_map<Key, Data, Hasher> constCache;
 };
 
-// Traits-like helper class for checking if objects can be compared using memcmp().
-// Useful for statically asserting if a cache key can implement operator==() with memcmp().
-template<typename T>
-struct is_memcmparable
-{
-// std::is_trivially_copyable is not available in older GCC versions.
-#if !defined(__GNUC__) || __GNUC__ > 5
-	static const bool value = std::is_trivially_copyable<T>::value;
-#else
-	// At least check it doesn't have virtual methods.
-	static const bool value = !std::is_polymorphic<T>::value;
-#endif
-};
 }  // namespace sw
 
 namespace sw {
diff --git a/src/Device/Memset.hpp b/src/Device/Memset.hpp
index 6ce53ae..877d10b 100644
--- a/src/Device/Memset.hpp
+++ b/src/Device/Memset.hpp
@@ -18,10 +18,19 @@
 #include <cstring>
 #include <type_traits>
 
+// GCC 8+ warns that
+// "‘void* memset(void*, int, size_t)’ clearing an object of non-trivial type ‘T’;
+//  use assignment or value-initialization instead [-Werror=class-memaccess]"
+// This is benign iff it happens before any of the base or member constructors are called.
+#if defined(__GNUC__) && (__GNUC__ >= 8)
+#	pragma GCC diagnostic push
+#	pragma GCC diagnostic ignored "-Wclass-memaccess"
+#endif
+
 namespace sw {
 
-// Helper class for clearing the memory of objects at construction.
-// Useful as the first base class of cache keys which may contain padding
+// Memset<> is a helper class for clearing the memory of objects at construction.
+// It is useful as the *first* base class of map keys which may contain padding
 // bytes or bits otherwise left uninitialized.
 template<class T>
 struct Memset
@@ -29,24 +38,49 @@
 	Memset(T *object, int val)
 	{
 		static_assert(std::is_base_of<Memset<T>, T>::value, "Memset<T> must only clear the memory of a type of which it is a base class");
+		::memset(object, 0, sizeof(T));
+	}
 
-// GCC 8+ warns that
-// "‘void* memset(void*, int, size_t)’ clearing an object of non-trivial type ‘T’;
-//  use assignment or value-initialization instead [-Werror=class-memaccess]"
-// This is benign iff it happens before any of the base or member constructrs are called.
-#if defined(__GNUC__) && (__GNUC__ >= 8)
-#	pragma GCC diagnostic push
-#	pragma GCC diagnostic ignored "-Wclass-memaccess"
-#endif
+	// Don't rely on the implicitly declared copy constructor and copy assignment operator.
+	// They can leave padding bytes uninitialized.
+	Memset(const Memset &rhs)
+	{
+		::memcpy(this, &rhs, sizeof(T));
+	}
 
-		memset(object, 0, sizeof(T));
+	Memset &operator=(const Memset &rhs)
+	{
+		::memcpy(this, &rhs, sizeof(T));
+		return *this;
+	}
 
-#if defined(__GNUC__) && (__GNUC__ >= 8)
-#	pragma GCC diagnostic pop
-#endif
+	// The compiler won't declare an implicit move constructor and move assignment operator
+	// due to having a user-defined copy constructor and copy assignment operator. Delete
+	// them for explicitness. We always want memcpy() being called.
+	Memset(const Memset &&rhs) = delete;
+	Memset &operator=(const Memset &&rhs) = delete;
+
+	friend bool operator==(const T &a, const T &b)
+	{
+		return ::memcmp(&a, &b, sizeof(T)) == 0;
+	}
+
+	friend bool operator!=(const T &a, const T &b)
+	{
+		return ::memcmp(&a, &b, sizeof(T)) != 0;
+	}
+
+	friend bool operator<(const T &a, const T &b)
+	{
+		return ::memcmp(&a, &b, sizeof(T)) < 0;
 	}
 };
 
 }  // namespace sw
 
+// Restore -Wclass-memaccess
+#if defined(__GNUC__) && (__GNUC__ >= 8)
+#	pragma GCC diagnostic pop
+#endif
+
 #endif  // sw_Memset_hpp
\ No newline at end of file
diff --git a/src/Device/PixelProcessor.cpp b/src/Device/PixelProcessor.cpp
index b24d2d3..6e05935 100644
--- a/src/Device/PixelProcessor.cpp
+++ b/src/Device/PixelProcessor.cpp
@@ -44,8 +44,7 @@
 		return false;
 	}
 
-	static_assert(is_memcmparable<State>::value, "Cannot memcmp State");
-	return memcmp(static_cast<const States *>(this), static_cast<const States *>(&state), sizeof(States)) == 0;
+	return *static_cast<const States *>(this) == static_cast<const States &>(state);
 }
 
 PixelProcessor::PixelProcessor()
diff --git a/src/Device/SetupProcessor.cpp b/src/Device/SetupProcessor.cpp
index 9a9a15f..19e7843 100644
--- a/src/Device/SetupProcessor.cpp
+++ b/src/Device/SetupProcessor.cpp
@@ -47,8 +47,7 @@
 		return false;
 	}
 
-	static_assert(is_memcmparable<State>::value, "Cannot memcmp States");
-	return memcmp(static_cast<const States *>(this), static_cast<const States *>(&state), sizeof(States)) == 0;
+	return *static_cast<const States *>(this) == static_cast<const States &>(state);
 }
 
 SetupProcessor::SetupProcessor()
diff --git a/src/Device/VertexProcessor.cpp b/src/Device/VertexProcessor.cpp
index c7f4d54..a2a33c5 100644
--- a/src/Device/VertexProcessor.cpp
+++ b/src/Device/VertexProcessor.cpp
@@ -51,8 +51,7 @@
 		return false;
 	}
 
-	static_assert(is_memcmparable<State>::value, "Cannot memcmp States");
-	return memcmp(static_cast<const States *>(this), static_cast<const States *>(&state), sizeof(States)) == 0;
+	return *static_cast<const States *>(this) == static_cast<const States &>(state);
 }
 
 VertexProcessor::VertexProcessor()