mem-cache: Handle zero sizes on compression

The size can be zero in special occasions, which would
generate divisions by zero. This patch expands the
stats to support them. It also fixes the compression
factor calculation in the Multi compressor.

As a side effect, now that zero sizes are handled, allow
the Zero compressor to generate it.

Change-Id: I9f7dee76576b09fdc9bef3e1f3f89be3726dcbd9
Signed-off-by: Daniel R. Carvalho <odanrc@yahoo.com.br>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/33383
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/base.cc b/src/mem/cache/compressors/base.cc
index de29c69..788a76f 100644
--- a/src/mem/cache/compressors/base.cc
+++ b/src/mem/cache/compressors/base.cc
@@ -153,7 +153,11 @@
     // Update stats
     stats.compressions++;
     stats.compressionSizeBits += comp_size_bits;
-    stats.compressionSize[std::ceil(std::log2(comp_size_bits))]++;
+    if (comp_size_bits != 0) {
+        stats.compressionSize[1 + std::ceil(std::log2(comp_size_bits))]++;
+    } else {
+        stats.compressionSize[0]++;
+    }
 
     // Print debug information
     DPRINTF(CacheComp, "Compressed cache line from %d to %d bits. " \
@@ -221,11 +225,15 @@
 {
     Stats::Group::regStats();
 
-    compressionSize.init(std::log2(compressor.blkSize*8) + 1);
+    // Values comprised are {0, 1, 2, 4, ..., blkSize}
+    compressionSize.init(std::log2(compressor.blkSize*8) + 2);
+    compressionSize.subname(0, "0");
+    compressionSize.subdesc(0,
+        "Number of blocks that compressed to fit in 0 bits");
     for (unsigned i = 0; i <= std::log2(compressor.blkSize*8); ++i) {
         std::string str_i = std::to_string(1 << i);
-        compressionSize.subname(i, str_i);
-        compressionSize.subdesc(i,
+        compressionSize.subname(1+i, str_i);
+        compressionSize.subdesc(1+i,
             "Number of blocks that compressed to fit in " + str_i + " bits");
     }
 
diff --git a/src/mem/cache/compressors/multi.cc b/src/mem/cache/compressors/multi.cc
index b740a95..76ec1db 100644
--- a/src/mem/cache/compressors/multi.cc
+++ b/src/mem/cache/compressors/multi.cc
@@ -94,9 +94,16 @@
             const std::size_t size = compData->getSize();
             // If the compressed size is worse than the uncompressed size,
             // we assume the size is the uncompressed size, and thus the
-            // compression factor is 1
+            // compression factor is 1.
+            //
+            // Some compressors (notably the zero compressor) may rely on
+            // extra information being stored in the tags, or added in
+            // another compression layer. Their size can be 0, so it is
+            // assigned the highest possible compression factor (the original
+            // block's size).
             compressionFactor = (size > blk_size) ? 1 :
-                alignToPowerOfTwo(std::floor(blk_size / (double) size));
+                ((size == 0) ? blk_size :
+                alignToPowerOfTwo(std::floor(blk_size / (double) size)));
         }
     };
     struct ResultsComparator
diff --git a/src/mem/cache/compressors/zero.hh b/src/mem/cache/compressors/zero.hh
index 57a2685..c839910 100644
--- a/src/mem/cache/compressors/zero.hh
+++ b/src/mem/cache/compressors/zero.hh
@@ -106,7 +106,7 @@
 {
   public:
     PatternX(const DictionaryEntry bytes, const int match_location)
-        : DictionaryCompressor::UncompressedPattern(X, 0, 1, match_location,
+        : DictionaryCompressor::UncompressedPattern(X, 0, 0, match_location,
           bytes)
     {
     }
@@ -118,7 +118,7 @@
   public:
     PatternZ(const DictionaryEntry bytes, const int match_location)
         : DictionaryCompressor::MaskedValuePattern<0, 0xFFFFFFFFFFFFFFFF>(
-          Z, 1, 1, match_location, bytes)
+          Z, 1, 0, match_location, bytes)
     {
     }
 };