[llvm] a41977d - [Support] Add llvm::compression::{getReasonIfUnsupported,compress,decompress}

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 8 00:59:03 PDT 2022


Author: Fangrui Song
Date: 2022-09-08T00:58:55-07:00
New Revision: a41977dd0f4fd7aaad5ec380d50782296a690b47

URL: https://github.com/llvm/llvm-project/commit/a41977dd0f4fd7aaad5ec380d50782296a690b47
DIFF: https://github.com/llvm/llvm-project/commit/a41977dd0f4fd7aaad5ec380d50782296a690b47.diff

LOG: [Support] Add llvm::compression::{getReasonIfUnsupported,compress,decompress}

as high-level API on top of `llvm::compression::{zlib,zstd}::*`:

* getReasonIfUnsupported: return nullptr if the specified format is
  supported, or (if unsupported) a string like `LLVM was not built with LLVM_ENABLE_ZLIB ...`
* compress: dispatch to zlib::uncompress or zstd::uncompress
* decompress: dispatch to zlib::uncompress or zstd::uncompress

Move `llvm::DebugCompressionType` from MC to Support to avoid Support->MC cyclic
dependency. There are 40+ uses in llvm-project.

Add another enum class `llvm::compression::Format` to represent supported
compression formats, which may be a superset of ELF compression formats.

See D130458 (llvm-objcopy --{,de}compress-debug-sections for zstd) for a use
case.

Link: https://discourse.llvm.org/t/rfc-zstandard-as-a-second-compression-method-to-llvm/63399
("[RFC] Zstandard as a second compression method to LLVM")

---

Note: this patch alone will cause -Wswitch to llvm/lib/ObjCopy/ELF/ELFObject.cpp

Reviewed By: ckissane, dblaikie

Differential Revision: https://reviews.llvm.org/D130506

Added: 
    

Modified: 
    llvm/include/llvm/MC/MCTargetOptions.h
    llvm/include/llvm/Support/Compression.h
    llvm/lib/Support/Compression.cpp
    llvm/unittests/Support/CompressionTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/MC/MCTargetOptions.h b/llvm/include/llvm/MC/MCTargetOptions.h
index ae305564a3536..640b4aa99462b 100644
--- a/llvm/include/llvm/MC/MCTargetOptions.h
+++ b/llvm/include/llvm/MC/MCTargetOptions.h
@@ -10,6 +10,7 @@
 #define LLVM_MC_MCTARGETOPTIONS_H
 
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/Support/Compression.h"
 #include <string>
 #include <vector>
 
@@ -25,11 +26,6 @@ enum class ExceptionHandling {
   AIX,      ///< AIX Exception Handling
 };
 
-enum class DebugCompressionType {
-  None, ///< No compression
-  Z,    ///< zlib style complession
-};
-
 enum class EmitDwarfUnwindType {
   Always,          // Always emit dwarf unwind
   NoCompactUnwind, // Only emit if compact unwind isn't available

diff  --git a/llvm/include/llvm/Support/Compression.h b/llvm/include/llvm/Support/Compression.h
index 8500396d88a0b..36150f65e8b14 100644
--- a/llvm/include/llvm/Support/Compression.h
+++ b/llvm/include/llvm/Support/Compression.h
@@ -20,6 +20,16 @@ namespace llvm {
 template <typename T> class SmallVectorImpl;
 class Error;
 
+// None indicates no compression. The other members are a subset of
+// compression::Format, which is used for compressed debug sections in some
+// object file formats (e.g. ELF). This is a separate class as we may add new
+// compression::Format members for non-debugging purposes.
+enum class DebugCompressionType {
+  None, ///< No compression
+  Z,    ///< zlib
+  Zstd, ///< Zstandard
+};
+
 namespace compression {
 namespace zlib {
 
@@ -65,6 +75,52 @@ Error uncompress(ArrayRef<uint8_t> Input,
 
 } // End of namespace zstd
 
+enum class Format {
+  Zlib,
+  Zstd,
+};
+
+inline Format formatFor(DebugCompressionType Type) {
+  switch (Type) {
+  case DebugCompressionType::None:
+    llvm_unreachable("not a compression type");
+  case DebugCompressionType::Z:
+    return Format::Zlib;
+  case DebugCompressionType::Zstd:
+    return Format::Zstd;
+  }
+  llvm_unreachable("");
+}
+
+struct Params {
+  constexpr Params(Format F)
+      : format(F), level(F == Format::Zlib ? zlib::DefaultCompression
+                                           : zstd::DefaultCompression) {}
+  Params(DebugCompressionType Type) : Params(formatFor(Type)) {}
+
+  Format format;
+  int level;
+  // This may support multi-threading for zstd in the future. Note that
+  // 
diff erent threads may produce 
diff erent output, so be careful if certain
+  // output determinism is desired.
+};
+
+// Return nullptr if LLVM was built with support (LLVM_ENABLE_ZLIB,
+// LLVM_ENABLE_ZSTD) for the specified compression format; otherwise
+// return a string literal describing the reason.
+const char *getReasonIfUnsupported(Format F);
+
+// Compress Input with the specified format P.Format. If Level is -1, use
+// *::DefaultCompression for the format.
+void compress(Params P, ArrayRef<uint8_t> Input,
+              SmallVectorImpl<uint8_t> &Output);
+
+// Decompress Input. The uncompressed size must be available.
+Error decompress(Format F, ArrayRef<uint8_t> Input,
+                 SmallVectorImpl<uint8_t> &Output, size_t UncompressedSize);
+Error decompress(DebugCompressionType T, ArrayRef<uint8_t> Input,
+                 SmallVectorImpl<uint8_t> &Output, size_t UncompressedSize);
+
 } // End of namespace compression
 
 } // End of namespace llvm

diff  --git a/llvm/lib/Support/Compression.cpp b/llvm/lib/Support/Compression.cpp
index e8fb715aa770e..9a04716508431 100644
--- a/llvm/lib/Support/Compression.cpp
+++ b/llvm/lib/Support/Compression.cpp
@@ -27,6 +27,52 @@
 using namespace llvm;
 using namespace llvm::compression;
 
+const char *compression::getReasonIfUnsupported(compression::Format F) {
+  switch (F) {
+  case compression::Format::Zlib:
+    if (zlib::isAvailable())
+      return nullptr;
+    return "LLVM was not built with LLVM_ENABLE_ZLIB or did not find zlib at "
+           "build time";
+  case compression::Format::Zstd:
+    if (zstd::isAvailable())
+      return nullptr;
+    return "LLVM was not built with LLVM_ENABLE_ZSTD or did not find zstd at "
+           "build time";
+  }
+  llvm_unreachable("");
+}
+
+void compression::compress(Params P, ArrayRef<uint8_t> Input,
+                           SmallVectorImpl<uint8_t> &Output) {
+  switch (P.format) {
+  case compression::Format::Zlib:
+    zlib::compress(Input, Output, P.level);
+    break;
+  case compression::Format::Zstd:
+    zstd::compress(Input, Output, P.level);
+    break;
+  }
+}
+
+Error compression::decompress(compression::Format F, ArrayRef<uint8_t> Input,
+                              SmallVectorImpl<uint8_t> &Output,
+                              size_t UncompressedSize) {
+  switch (F) {
+  case compression::Format::Zlib:
+    return zlib::uncompress(Input, Output, UncompressedSize);
+  case compression::Format::Zstd:
+    return zstd::uncompress(Input, Output, UncompressedSize);
+  }
+  llvm_unreachable("");
+}
+
+Error compression::decompress(DebugCompressionType T, ArrayRef<uint8_t> Input,
+                              SmallVectorImpl<uint8_t> &Output,
+                              size_t UncompressedSize) {
+  return decompress(formatFor(T), Input, Output, UncompressedSize);
+}
+
 #if LLVM_ENABLE_ZLIB
 
 static StringRef convertZlibCodeToString(int Code) {

diff  --git a/llvm/unittests/Support/CompressionTest.cpp b/llvm/unittests/Support/CompressionTest.cpp
index a89dadf5f9ae8..46f3003842df0 100644
--- a/llvm/unittests/Support/CompressionTest.cpp
+++ b/llvm/unittests/Support/CompressionTest.cpp
@@ -30,9 +30,15 @@ static void testZlibCompression(StringRef Input, int Level) {
 
   // Check that uncompressed buffer is the same as original.
   Error E = zlib::uncompress(Compressed, Uncompressed, Input.size());
-  consumeError(std::move(E));
+  EXPECT_FALSE(std::move(E));
+  EXPECT_EQ(Input, toStringRef(Uncompressed));
 
+  // decompress with Z dispatches to zlib::uncompress.
+  E = compression::decompress(DebugCompressionType::Z, Compressed, Uncompressed,
+                              Input.size());
+  EXPECT_FALSE(std::move(E));
   EXPECT_EQ(Input, toStringRef(Uncompressed));
+
   if (Input.size() > 0) {
     // Uncompression fails if expected length is too short.
     E = zlib::uncompress(Compressed, Uncompressed, Input.size() - 1);
@@ -69,9 +75,15 @@ static void testZstdCompression(StringRef Input, int Level) {
 
   // Check that uncompressed buffer is the same as original.
   Error E = zstd::uncompress(Compressed, Uncompressed, Input.size());
-  consumeError(std::move(E));
+  EXPECT_FALSE(std::move(E));
+  EXPECT_EQ(Input, toStringRef(Uncompressed));
 
+  // uncompress with Zstd dispatches to zstd::uncompress.
+  E = compression::decompress(DebugCompressionType::Zstd, Compressed,
+                              Uncompressed, Input.size());
+  EXPECT_FALSE(std::move(E));
   EXPECT_EQ(Input, toStringRef(Uncompressed));
+
   if (Input.size() > 0) {
     // Uncompression fails if expected length is too short.
     E = zstd::uncompress(Compressed, Uncompressed, Input.size() - 1);


        


More information about the llvm-commits mailing list