mem-cache: Add a masked pattern to compressors

The masked pattern compares masked values to masked dictionary entries
to determine whether these values match. If successful, the bits that
do not match must be added to the compressed data.

Change-Id: I4b1c8feb0faa99576382b54a73a20c353f965d2a
Signed-off-by: Daniel R. Carvalho <odanrc@yahoo.com.br>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/21150
Reviewed-by: Nikos Nikoleris <nikos.nikoleris@arm.com>
Maintainer: Nikos Nikoleris <nikos.nikoleris@arm.com>
Tested-by: kokoro <noreply+kokoro@google.com>
diff --git a/src/mem/cache/compressors/cpack.hh b/src/mem/cache/compressors/cpack.hh
index 3ecab50..7b10ac3 100644
--- a/src/mem/cache/compressors/cpack.hh
+++ b/src/mem/cache/compressors/cpack.hh
@@ -154,55 +154,21 @@
     }
 };
 
-class CPack::PatternMMMM : public DictionaryCompressor::Pattern
+class CPack::PatternMMMM : public MaskedPattern<0xFFFFFFFF>
 {
   public:
     PatternMMMM(const DictionaryEntry bytes, const int match_location)
-        : Pattern(MMMM, 0x2, 6, 0, match_location, true) {}
-
-    static bool isPattern(const DictionaryEntry& bytes,
-        const DictionaryEntry& dict_bytes,
-        const int match_location)
+        : MaskedPattern<0xFFFFFFFF>(MMMM, 0x2, 6, match_location, bytes, true)
     {
-        return (bytes == dict_bytes) && (match_location >= 0);
-    }
-
-    DictionaryEntry
-    decompress(const DictionaryEntry dict_bytes) const override
-    {
-        return dict_bytes;
     }
 };
 
-class CPack::PatternMMXX : public DictionaryCompressor::Pattern
+class CPack::PatternMMXX : public MaskedPattern<0xFFFF0000>
 {
-  private:
-    /**
-     * A copy of the unmatched bytes.
-     */
-    const uint8_t byte0;
-    const uint8_t byte1;
-
   public:
     PatternMMXX(const DictionaryEntry bytes, const int match_location)
-        : Pattern(MMXX, 0xC, 8, 2, match_location, true),
-                  byte0(bytes[0]), byte1(bytes[1]) {}
-
-    static bool isPattern(const DictionaryEntry& bytes,
-        const DictionaryEntry& dict_bytes,
-        const int match_location)
+        : MaskedPattern<0xFFFF0000>(MMXX, 0xC, 8, match_location, bytes, true)
     {
-        // Notice we don't compare bytes[0], as otherwise we'd be unnecessarily
-        // discarding MMXM. If that pattern is added this should be modified
-        return (bytes[3] == dict_bytes[3]) && (bytes[2] == dict_bytes[2]) &&
-               (bytes[1] != dict_bytes[1]) && (match_location >= 0);
-
-    }
-
-    DictionaryEntry
-    decompress(const DictionaryEntry dict_bytes) const override
-    {
-        return {byte0, byte1, dict_bytes[2], dict_bytes[3]};
     }
 };
 
@@ -233,32 +199,12 @@
     }
 };
 
-class CPack::PatternMMMX : public DictionaryCompressor::Pattern
+class CPack::PatternMMMX : public MaskedPattern<0xFFFFFF00>
 {
-  private:
-    /**
-     * A copy of the unmatched byte.
-     */
-    const uint8_t byte;
-
   public:
     PatternMMMX(const DictionaryEntry bytes, const int match_location)
-        : Pattern(MMMX, 0xE, 8, 1, match_location, true),
-                  byte(bytes[0]) {}
-
-    static bool isPattern(const DictionaryEntry& bytes,
-        const DictionaryEntry& dict_bytes,
-        const int match_location)
+        : MaskedPattern<0xFFFFFF00>(MMMX, 0xE, 8, match_location, bytes, true)
     {
-        return (bytes[3] == dict_bytes[3]) && (bytes[2] == dict_bytes[2]) &&
-               (bytes[1] == dict_bytes[1]) && (bytes[0] != dict_bytes[0]) &&
-               (match_location >= 0);
-    }
-
-    DictionaryEntry
-    decompress(const DictionaryEntry dict_bytes) const override
-    {
-        return {byte, dict_bytes[1], dict_bytes[2], dict_bytes[3]};
     }
 };
 
diff --git a/src/mem/cache/compressors/dictionary_compressor.hh b/src/mem/cache/compressors/dictionary_compressor.hh
index 9e0e4df..3c828f0 100644
--- a/src/mem/cache/compressors/dictionary_compressor.hh
+++ b/src/mem/cache/compressors/dictionary_compressor.hh
@@ -111,6 +111,9 @@
 class DictionaryCompressor : public BaseDictionaryCompressor
 {
   protected:
+    /** Convenience typedef for a dictionary entry. */
+    typedef std::array<uint8_t, sizeof(T)> DictionaryEntry;
+
     /**
      * Compression data for the dictionary compressor. It consists of a vector
      * of patterns.
@@ -120,9 +123,8 @@
     // Forward declaration of a pattern
     class Pattern;
     class UncompressedPattern;
-
-    /** Convenience typedef for a dictionary entry. */
-    typedef std::array<uint8_t, sizeof(T)> DictionaryEntry;
+    template <T mask>
+    class MaskedPattern;
 
     /**
      * Create a factory to determine if input matches a pattern. The if else
@@ -420,4 +422,62 @@
     }
 };
 
+/**
+ * A pattern that compares masked values against dictionary entries. If
+ * the masked dictionary entry matches perfectly the masked value to be
+ * compressed, there is a pattern match.
+ *
+ * For example, if the mask is 0xFF00 (that is, this pattern matches the MSB),
+ * the value (V) 0xFF20 is being compressed, and the dictionary contains
+ * the value (D) 0xFF03, this is a match (V & mask == 0xFF00 == D & mask),
+ * and 0x0020 is added to the list of unmatched bits.
+ *
+ * @tparam mask A mask containing the bits that must match.
+ */
+template <class T>
+template <T mask>
+class DictionaryCompressor<T>::MaskedPattern
+    : public DictionaryCompressor<T>::Pattern
+{
+  private:
+    static_assert(mask != 0, "The pattern's value mask must not be zero. Use "
+        "the uncompressed pattern instead.");
+
+    /** A copy of the bits that do not belong to the mask. */
+    const T bits;
+
+  public:
+    MaskedPattern(const int number,
+        const uint64_t code,
+        const uint64_t metadata_length,
+        const int match_location,
+        const DictionaryEntry bytes,
+        const bool allocate = true)
+      : DictionaryCompressor<T>::Pattern(number, code, metadata_length,
+            popCount(~mask) / 8, match_location, allocate),
+        bits(DictionaryCompressor<T>::fromDictionaryEntry(bytes) & ~mask)
+    {
+    }
+
+    static bool
+    isPattern(const DictionaryEntry& bytes, const DictionaryEntry& dict_bytes,
+        const int match_location)
+    {
+        const T masked_bytes =
+            DictionaryCompressor<T>::fromDictionaryEntry(bytes) & mask;
+        const T masked_dict_bytes =
+            DictionaryCompressor<T>::fromDictionaryEntry(dict_bytes) & mask;
+        return (match_location >= 0) && (masked_bytes == masked_dict_bytes);
+    }
+
+    DictionaryEntry
+    decompress(const DictionaryEntry dict_bytes) const override
+    {
+        const T masked_dict_bytes =
+            DictionaryCompressor<T>::fromDictionaryEntry(dict_bytes) & mask;
+        return DictionaryCompressor<T>::toDictionaryEntry(
+            bits | masked_dict_bytes);
+    }
+};
+
 #endif //__MEM_CACHE_COMPRESSORS_DICTIONARY_COMPRESSOR_HH__