diff --git a/clang-tools-extra/clangd/index/Serialization.h b/clang-tools-extra/clangd/index/Serialization.h --- a/clang-tools-extra/clangd/index/Serialization.h +++ b/clang-tools-extra/clangd/index/Serialization.h @@ -28,11 +28,14 @@ #include "index/Index.h" #include "index/Symbol.h" #include "clang/Tooling/CompilationDatabase.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/Error.h" namespace clang { namespace clangd { +extern llvm::compression::CompressionAlgorithm *StringTableCompressionScheme; + enum class IndexFileFormat { RIFF, // Versioned binary format, suitable for production use. YAML, // Human-readable format, suitable for experiments and debugging. diff --git a/clang-tools-extra/clangd/index/Serialization.cpp b/clang-tools-extra/clangd/index/Serialization.cpp --- a/clang-tools-extra/clangd/index/Serialization.cpp +++ b/clang-tools-extra/clangd/index/Serialization.cpp @@ -27,6 +27,10 @@ namespace clang { namespace clangd { + +llvm::compression::CompressionAlgorithm *StringTableCompressionScheme = + new llvm::compression::ZlibCompressionAlgorithm(); + namespace { // IO PRIMITIVES @@ -190,10 +194,12 @@ RawTable.append(std::string(S)); RawTable.push_back(0); } - if (llvm::compression::zlib::isAvailable()) { + llvm::compression::CompressionAlgorithm *CompressionScheme = + StringTableCompressionScheme; + if (CompressionScheme->supported()) { llvm::SmallVector Compressed; - llvm::compression::zlib::compress(llvm::arrayRefFromStringRef(RawTable), - Compressed); + CompressionScheme->compress(llvm::arrayRefFromStringRef(RawTable), + Compressed); write32(RawTable.size(), OS); OS << llvm::toStringRef(Compressed); } else { @@ -224,23 +230,29 @@ llvm::SmallVector UncompressedStorage; if (UncompressedSize == 0) // No compression Uncompressed = R.rest(); - else if (llvm::compression::zlib::isAvailable()) { - // Don't allocate a massive buffer if UncompressedSize was corrupted - // This is effective for sharded index, but not big monolithic ones, as - // once compressed size reaches 4MB nothing can be ruled out. - // Theoretical max ratio from https://zlib.net/zlib_tech.html - constexpr int MaxCompressionRatio = 1032; - if (UncompressedSize / MaxCompressionRatio > R.rest().size()) - return error("Bad stri table: uncompress {0} -> {1} bytes is implausible", - R.rest().size(), UncompressedSize); - - if (llvm::Error E = llvm::compression::zlib::uncompress( - llvm::arrayRefFromStringRef(R.rest()), UncompressedStorage, - UncompressedSize)) - return std::move(E); - Uncompressed = toStringRef(UncompressedStorage); - } else - return error("Compressed string table, but zlib is unavailable"); + else { + llvm::compression::CompressionAlgorithm *CompressionScheme = + StringTableCompressionScheme; + if (CompressionScheme->supported()) { + // Don't allocate a massive buffer if UncompressedSize was corrupted + // This is effective for sharded index, but not big monolithic ones, as + // once compressed size reaches 4MB nothing can be ruled out. + // Theoretical max ratio from https://zlib.net/zlib_tech.html + constexpr int MaxCompressionRatio = 1032; + if (UncompressedSize / MaxCompressionRatio > R.rest().size()) + return error( + "Bad stri table: uncompress {0} -> {1} bytes is implausible", + R.rest().size(), UncompressedSize); + + if (llvm::Error E = CompressionScheme->decompress( + llvm::arrayRefFromStringRef(R.rest()), UncompressedStorage, + UncompressedSize)) + return std::move(E); + Uncompressed = toStringRef(UncompressedStorage); + } else + return error("Compressed string table, but " + + (CompressionScheme->getName() + " is unavailable").str()); + } StringTableIn Table; llvm::StringSaver Saver(Table.Arena); diff --git a/clang-tools-extra/clangd/unittests/SerializationTests.cpp b/clang-tools-extra/clangd/unittests/SerializationTests.cpp --- a/clang-tools-extra/clangd/unittests/SerializationTests.cpp +++ b/clang-tools-extra/clangd/unittests/SerializationTests.cpp @@ -391,7 +391,7 @@ // Check we detect invalid string table size size without allocating it first. // If this detection fails, the test should allocate a huge array and crash. TEST(SerializationTest, NoCrashOnBadStringTableSize) { - if (!llvm::compression::zlib::isAvailable()) { + if (!llvm::compression::ZlibCompressionAlgorithm().supported()) { log("skipping test, no zlib"); return; } diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -726,7 +726,12 @@ /// Describes a source location entry (SLocEntry) for a /// macro expansion. - SM_SLOC_EXPANSION_ENTRY = 5 + SM_SLOC_EXPANSION_ENTRY = 5, + + /// Describes a compressed blob that contains the data for + /// a buffer entry with a size header (like SM_SLOC_BUFFER_BLOB_COMPRESSED) + /// but also a part in the record to indicate which compression algorithm. + SM_SLOC_BUFFER_BLOB_COMPRESSED_DYNAMIC = 6, }; /// Record types used within a preprocessor block. diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h --- a/clang/include/clang/Serialization/ASTWriter.h +++ b/clang/include/clang/Serialization/ASTWriter.h @@ -35,6 +35,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Bitstream/BitstreamWriter.h" +#include "llvm/Support/Compression.h" #include #include #include @@ -93,6 +94,7 @@ using RecordDataRef = ArrayRef; private: + llvm::compression::CompressionAlgorithm *CompressionScheme; /// Map that provides the ID numbers of each type within the /// output stream, plus those deserialized from a chained PCH. /// @@ -542,6 +544,7 @@ ASTWriter(llvm::BitstreamWriter &Stream, SmallVectorImpl &Buffer, InMemoryModuleCache &ModuleCache, ArrayRef> Extensions, + llvm::compression::CompressionAlgorithm *CompressionScheme, bool IncludeTimestamps = true); ~ASTWriter() override; diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp --- a/clang/lib/CodeGen/CoverageMappingGen.cpp +++ b/clang/lib/CodeGen/CoverageMappingGen.cpp @@ -24,6 +24,7 @@ #include "llvm/ProfileData/Coverage/CoverageMappingReader.h" #include "llvm/ProfileData/Coverage/CoverageMappingWriter.h" #include "llvm/ProfileData/InstrProfReader.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" @@ -1704,7 +1705,8 @@ std::string Filenames; { llvm::raw_string_ostream OS(Filenames); - CoverageFilenamesSectionWriter(FilenameStrs).write(OS); + CoverageFilenamesSectionWriter(FilenameStrs) + .write(OS, new llvm::compression::ZlibCompressionAlgorithm()); } auto *FilenamesVal = llvm::ConstantDataArray::getString(Ctx, Filenames, false); diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -1139,7 +1139,14 @@ if (Value == "none") { CmdArgs.push_back("--compress-debug-sections=none"); } else if (Value == "zlib") { - if (llvm::compression::zlib::isAvailable()) { + if (llvm::compression::ZlibCompressionAlgorithm().supported()) { + CmdArgs.push_back( + Args.MakeArgString("--compress-debug-sections=" + Twine(Value))); + } else { + D.Diag(diag::warn_debug_compression_unavailable); + } + } else if (Value == "zstd") { + if (llvm::compression::ZStdCompressionAlgorithm().supported()) { CmdArgs.push_back( Args.MakeArgString("--compress-debug-sections=" + Twine(Value))); } else { diff --git a/clang/lib/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp --- a/clang/lib/Frontend/ASTUnit.cpp +++ b/clang/lib/Frontend/ASTUnit.cpp @@ -80,6 +80,7 @@ #include "llvm/Bitstream/BitstreamWriter.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Casting.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/DJB.h" #include "llvm/Support/ErrorHandling.h" @@ -221,7 +222,9 @@ ASTWriter Writer; ASTWriterData(InMemoryModuleCache &ModuleCache) - : Stream(Buffer), Writer(Stream, Buffer, ModuleCache, {}) {} + : Stream(Buffer), + Writer(Stream, Buffer, ModuleCache, {}, + new llvm::compression::ZlibCompressionAlgorithm()) {} }; void ASTUnit::clearFileLevelDecls() { @@ -2323,7 +2326,8 @@ SmallString<128> Buffer; llvm::BitstreamWriter Stream(Buffer); InMemoryModuleCache ModuleCache; - ASTWriter Writer(Stream, Buffer, ModuleCache, {}); + ASTWriter Writer(Stream, Buffer, ModuleCache, {}, + new llvm::compression::ZlibCompressionAlgorithm()); return serializeUnit(Writer, Buffer, getSema(), hasErrors, OS); } diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -1461,13 +1461,22 @@ } unsigned RecCode = MaybeRecCode.get(); - if (RecCode == SM_SLOC_BUFFER_BLOB_COMPRESSED) { - if (!llvm::compression::zlib::isAvailable()) { - Error("zlib is not available"); + if (RecCode == SM_SLOC_BUFFER_BLOB_COMPRESSED || + RecCode == SM_SLOC_BUFFER_BLOB_COMPRESSED_DYNAMIC) { + uint8_t CompressionSchemeId = + RecCode == SM_SLOC_BUFFER_BLOB_COMPRESSED + ? static_cast( + llvm::compression::SupportCompressionType::Zlib) + : Record[1]; + llvm::compression::CompressionAlgorithm *CompressionScheme = + llvm::compression::CompressionAlgorithmFromId(CompressionSchemeId); + if (!CompressionScheme->supported()) { + Error("compression class " + + (CompressionScheme->getName() + " is not available").str()); return nullptr; } SmallVector Uncompressed; - if (llvm::Error E = llvm::compression::zlib::uncompress( + if (llvm::Error E = CompressionScheme->decompress( llvm::arrayRefFromStringRef(Blob), Uncompressed, Record[0])) { Error("could not decompress embedded file contents: " + llvm::toString(std::move(E))); @@ -1475,6 +1484,7 @@ } return llvm::MemoryBuffer::getMemBufferCopy( llvm::toStringRef(Uncompressed), Name); + } else if (RecCode == SM_SLOC_BUFFER_BLOB) { return llvm::MemoryBuffer::getMemBuffer(Blob.drop_back(1), Name, true); } else { diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -866,6 +866,7 @@ RECORD(SM_SLOC_BUFFER_BLOB); RECORD(SM_SLOC_BUFFER_BLOB_COMPRESSED); RECORD(SM_SLOC_EXPANSION_ENTRY); + RECORD(SM_SLOC_BUFFER_BLOB_COMPRESSED_DYNAMIC); // Preprocessor Block. BLOCK(PREPROCESSOR_BLOCK); @@ -1694,14 +1695,21 @@ /// Create an abbreviation for the SLocEntry that refers to a /// buffer's blob. static unsigned CreateSLocBufferBlobAbbrev(llvm::BitstreamWriter &Stream, - bool Compressed) { + bool Compressed, + bool CompressedDynamic) { using namespace llvm; auto Abbrev = std::make_shared(); - Abbrev->Add(BitCodeAbbrevOp(Compressed ? SM_SLOC_BUFFER_BLOB_COMPRESSED - : SM_SLOC_BUFFER_BLOB)); - if (Compressed) + Abbrev->Add(BitCodeAbbrevOp(CompressedDynamic + ? SM_SLOC_BUFFER_BLOB_COMPRESSED_DYNAMIC + : (Compressed ? SM_SLOC_BUFFER_BLOB_COMPRESSED + : SM_SLOC_BUFFER_BLOB))); + + if (Compressed || CompressedDynamic) Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Uncompressed size + if (CompressedDynamic) { + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Compression Scheme + } Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Blob return Stream.EmitAbbrev(std::move(Abbrev)); } @@ -1993,19 +2001,38 @@ free(const_cast(SavedStrings[I])); } -static void emitBlob(llvm::BitstreamWriter &Stream, StringRef Blob, - unsigned SLocBufferBlobCompressedAbbrv, - unsigned SLocBufferBlobAbbrv) { +static void +emitBlob(llvm::BitstreamWriter &Stream, StringRef Blob, + unsigned SLocBufferBlobCompressedAbbrv, + unsigned SLocBufferBlobCompressedDynamicAbbrv, + unsigned SLocBufferBlobAbbrv, + llvm::compression::CompressionAlgorithm *CompressionScheme) { using RecordDataType = ASTWriter::RecordData::value_type; // Compress the buffer if possible. We expect that almost all PCM // consumers will not want its contents. - SmallVector CompressedBuffer; - if (llvm::compression::zlib::isAvailable()) { - llvm::compression::zlib::compress( - llvm::arrayRefFromStringRef(Blob.drop_back(1)), CompressedBuffer); - RecordDataType Record[] = {SM_SLOC_BUFFER_BLOB_COMPRESSED, Blob.size() - 1}; - Stream.EmitRecordWithBlob(SLocBufferBlobCompressedAbbrv, Record, + + if (CompressionScheme->supported()) { + + SmallVector CompressedBuffer; + + CompressionScheme->compress(llvm::arrayRefFromStringRef(Blob.drop_back(1)), + CompressedBuffer); + // if our chosen CompressionAlgorithm happens to be zlib output old format + // for extra back compat + if (CompressionScheme->getAlgorithmId() == + llvm::compression::SupportCompressionType::Zlib) { + + RecordDataType Record[] = {SM_SLOC_BUFFER_BLOB_COMPRESSED, + Blob.size() - 1}; + Stream.EmitRecordWithBlob(SLocBufferBlobCompressedAbbrv, Record, + llvm::toStringRef(CompressedBuffer)); + return; + } + RecordDataType Record[] = { + SM_SLOC_BUFFER_BLOB_COMPRESSED_DYNAMIC, Blob.size() - 1, + static_cast(CompressionScheme->getAlgorithmId())}; + Stream.EmitRecordWithBlob(SLocBufferBlobCompressedDynamicAbbrv, Record, llvm::toStringRef(CompressedBuffer)); return; } @@ -2033,9 +2060,12 @@ // Abbreviations for the various kinds of source-location entries. unsigned SLocFileAbbrv = CreateSLocFileAbbrev(Stream); unsigned SLocBufferAbbrv = CreateSLocBufferAbbrev(Stream); - unsigned SLocBufferBlobAbbrv = CreateSLocBufferBlobAbbrev(Stream, false); + unsigned SLocBufferBlobAbbrv = + CreateSLocBufferBlobAbbrev(Stream, false, false); unsigned SLocBufferBlobCompressedAbbrv = - CreateSLocBufferBlobAbbrev(Stream, true); + CreateSLocBufferBlobAbbrev(Stream, true, false); + unsigned SLocBufferBlobCompressedDynamicAbbrv = + CreateSLocBufferBlobAbbrev(Stream, true, true); unsigned SLocExpansionAbbrv = CreateSLocExpansionAbbrev(Stream); // Write out the source location entry table. We skip the first @@ -2135,7 +2165,8 @@ Buffer = llvm::MemoryBufferRef("<<>>", ""); StringRef Blob(Buffer->getBufferStart(), Buffer->getBufferSize() + 1); emitBlob(Stream, Blob, SLocBufferBlobCompressedAbbrv, - SLocBufferBlobAbbrv); + SLocBufferBlobCompressedDynamicAbbrv, SLocBufferBlobAbbrv, + CompressionScheme); } } else { // The source location entry is a macro expansion. @@ -4461,8 +4492,10 @@ SmallVectorImpl &Buffer, InMemoryModuleCache &ModuleCache, ArrayRef> Extensions, + llvm::compression::CompressionAlgorithm *CompressionScheme, bool IncludeTimestamps) : Stream(Stream), Buffer(Buffer), ModuleCache(ModuleCache), + CompressionScheme(CompressionScheme), IncludeTimestamps(IncludeTimestamps) { for (const auto &Ext : Extensions) { if (auto Writer = Ext->createExtensionWriter(*this)) diff --git a/clang/lib/Serialization/GeneratePCH.cpp b/clang/lib/Serialization/GeneratePCH.cpp --- a/clang/lib/Serialization/GeneratePCH.cpp +++ b/clang/lib/Serialization/GeneratePCH.cpp @@ -17,6 +17,7 @@ #include "clang/Sema/SemaConsumer.h" #include "clang/Serialization/ASTWriter.h" #include "llvm/Bitstream/BitstreamWriter.h" +#include "llvm/Support/Compression.h" using namespace clang; @@ -29,6 +30,7 @@ : PP(PP), OutputFile(OutputFile), isysroot(isysroot.str()), SemaPtr(nullptr), Buffer(std::move(Buffer)), Stream(this->Buffer->Data), Writer(Stream, this->Buffer->Data, ModuleCache, Extensions, + new llvm::compression::ZlibCompressionAlgorithm(), IncludeTimestamps), AllowASTWithErrors(AllowASTWithErrors), ShouldCacheASTInMemory(ShouldCacheASTInMemory) { diff --git a/clang/test/CodeGen/coverage-compilation-dir.c b/clang/test/CodeGen/coverage-compilation-dir.c --- a/clang/test/CodeGen/coverage-compilation-dir.c +++ b/clang/test/CodeGen/coverage-compilation-dir.c @@ -1,6 +1,6 @@ // RUN: mkdir -p %t.dir && cd %t.dir // RUN: cp %s rel.c -// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-compilation-dir=/nonsense -fcoverage-mapping -emit-llvm -mllvm -enable-name-compression=false rel.c -o - | FileCheck -check-prefix=CHECK-NONSENSE %s +// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-compilation-dir=/nonsense -fcoverage-mapping -emit-llvm -mllvm -name-compression=none rel.c -o - | FileCheck -check-prefix=CHECK-NONSENSE %s // CHECK-NONSENSE: nonsense diff --git a/clang/test/CoverageMapping/abspath.cpp b/clang/test/CoverageMapping/abspath.cpp --- a/clang/test/CoverageMapping/abspath.cpp +++ b/clang/test/CoverageMapping/abspath.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -mllvm -enable-name-compression=false -emit-llvm -main-file-name abspath.cpp %S/Inputs/../abspath.cpp -o - | FileCheck -check-prefix=RMDOTS %s +// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -mllvm -name-compression=none -emit-llvm -main-file-name abspath.cpp %S/Inputs/../abspath.cpp -o - | FileCheck -check-prefix=RMDOTS %s // RMDOTS: @__llvm_coverage_mapping = {{.*}}"\02 // RMDOTS-NOT: Inputs @@ -6,7 +6,7 @@ // RUN: mkdir -p %t/test && cd %t/test // RUN: echo "void f1(void) {}" > f1.c -// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -mllvm -enable-name-compression=false -emit-llvm -main-file-name abspath.cpp %t/test/f1.c -o - | FileCheck -check-prefix=ABSPATH %s +// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -mllvm -name-compression=none -emit-llvm -main-file-name abspath.cpp %t/test/f1.c -o - | FileCheck -check-prefix=ABSPATH %s // RELPATH: @__llvm_coverage_mapping = {{.*}}"\02 // RELPATH: {{..(/|\\\\)test(/|\\\\)f1}}.c diff --git a/clang/test/CoverageMapping/ir.c b/clang/test/CoverageMapping/ir.c --- a/clang/test/CoverageMapping/ir.c +++ b/clang/test/CoverageMapping/ir.c @@ -1,7 +1,7 @@ // Check the data structures emitted by coverage mapping -// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -triple x86_64-apple-macosx10.9 -main-file-name ir.c %s -o - -emit-llvm -fprofile-instrument=clang -fcoverage-mapping -mllvm -enable-name-compression=false -no-opaque-pointers | FileCheck %s -check-prefixes=COMMON,DARWIN -// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -triple x86_64-apple-macosx10.9 -main-file-name ir.c %s -o - -emit-llvm -fprofile-instrument=clang -fcoverage-mapping -mllvm -enable-name-compression=false -opaque-pointers | FileCheck %s -check-prefixes=COMMON,DARWIN -// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -triple x86_64--windows-msvc -main-file-name ir.c %s -o - -emit-llvm -fprofile-instrument=clang -fcoverage-mapping -mllvm -enable-name-compression=false | FileCheck %s -check-prefixes=COMMON,WINDOWS +// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -triple x86_64-apple-macosx10.9 -main-file-name ir.c %s -o - -emit-llvm -fprofile-instrument=clang -fcoverage-mapping -mllvm -name-compression=none -no-opaque-pointers | FileCheck %s -check-prefixes=COMMON,DARWIN +// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -triple x86_64-apple-macosx10.9 -main-file-name ir.c %s -o - -emit-llvm -fprofile-instrument=clang -fcoverage-mapping -mllvm -name-compression=none -opaque-pointers | FileCheck %s -check-prefixes=COMMON,DARWIN +// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -triple x86_64--windows-msvc -main-file-name ir.c %s -o - -emit-llvm -fprofile-instrument=clang -fcoverage-mapping -mllvm -name-compression=none | FileCheck %s -check-prefixes=COMMON,WINDOWS static inline void unused(void) {} diff --git a/clang/test/Profile/coverage-prefix-map.c b/clang/test/Profile/coverage-prefix-map.c --- a/clang/test/Profile/coverage-prefix-map.c +++ b/clang/test/Profile/coverage-prefix-map.c @@ -5,17 +5,17 @@ // RUN: echo "void f1(void) {}" > %t/root/nested/coverage-prefix-map.c // RUN: cd %t/root -// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -mllvm -enable-name-compression=false -main-file-name coverage-prefix-map.c %t/root/nested/coverage-prefix-map.c -o - | FileCheck --check-prefix=ABSOLUTE %s +// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -mllvm -name-compression=none -main-file-name coverage-prefix-map.c %t/root/nested/coverage-prefix-map.c -o - | FileCheck --check-prefix=ABSOLUTE %s // // ABSOLUTE: @__llvm_coverage_mapping = {{.*"\\02.*root.*nested.*coverage-prefix-map\.c}} -// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -mllvm -enable-name-compression=false -main-file-name coverage-prefix-map.c ../root/nested/coverage-prefix-map.c -o - | FileCheck --check-prefix=RELATIVE %s +// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -mllvm -name-compression=none -main-file-name coverage-prefix-map.c ../root/nested/coverage-prefix-map.c -o - | FileCheck --check-prefix=RELATIVE %s // // RELATIVE: @__llvm_coverage_mapping = {{.*"\\02.*}}..{{/|\\+}}root{{/|\\+}}nested{{.*coverage-prefix-map\.c}} -// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -mllvm -enable-name-compression=false -main-file-name coverage-prefix-map.c %t/root/nested/coverage-prefix-map.c -fcoverage-prefix-map=%/t/root=. -o - | FileCheck --check-prefix=COVERAGE-PREFIX-MAP %s -// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -mllvm -enable-name-compression=false -main-file-name coverage-prefix-map.c ../root/nested/coverage-prefix-map.c -fcoverage-prefix-map=../root=. -o - | FileCheck --check-prefix=COVERAGE-PREFIX-MAP %s +// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -mllvm -name-compression=none -main-file-name coverage-prefix-map.c %t/root/nested/coverage-prefix-map.c -fcoverage-prefix-map=%/t/root=. -o - | FileCheck --check-prefix=COVERAGE-PREFIX-MAP %s +// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -mllvm -name-compression=none -main-file-name coverage-prefix-map.c ../root/nested/coverage-prefix-map.c -fcoverage-prefix-map=../root=. -o - | FileCheck --check-prefix=COVERAGE-PREFIX-MAP %s // COVERAGE-PREFIX-MAP: @__llvm_coverage_mapping = {{.*"\\02.*}}.{{/|\\+}}nested{{.*coverage-prefix-map\.c}} -// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -mllvm -enable-name-compression=false -main-file-name coverage-prefix-map.c %t/root/nested/coverage-prefix-map.c -fcoverage-compilation-dir=/custom -fcoverage-prefix-map=/custom=/nonsense -o - | FileCheck --check-prefix=COVERAGE-COMPILATION-DIR %s +// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -mllvm -name-compression=none -main-file-name coverage-prefix-map.c %t/root/nested/coverage-prefix-map.c -fcoverage-compilation-dir=/custom -fcoverage-prefix-map=/custom=/nonsense -o - | FileCheck --check-prefix=COVERAGE-COMPILATION-DIR %s // COVERAGE-COMPILATION-DIR: @__llvm_coverage_mapping = {{.*"\\02.*}}nonsense diff --git a/compiler-rt/include/profile/InstrProfData.inc b/compiler-rt/include/profile/InstrProfData.inc --- a/compiler-rt/include/profile/InstrProfData.inc +++ b/compiler-rt/include/profile/InstrProfData.inc @@ -648,11 +648,11 @@ /* FIXME: Please remedy the fixme in the header before bumping the version. */ /* Raw profile format version (start from 1). */ -#define INSTR_PROF_RAW_VERSION 8 +#define INSTR_PROF_RAW_VERSION 9 /* Indexed profile format version (start from 1). */ -#define INSTR_PROF_INDEX_VERSION 8 +#define INSTR_PROF_INDEX_VERSION 9 /* Coverage mapping format version (start from 0). */ -#define INSTR_PROF_COVMAP_VERSION 5 +#define INSTR_PROF_COVMAP_VERSION 6 /* Profile version is always of type uint64_t. Reserve the upper 8 bits in the * version for other variants of profile. We set the lowest bit of the upper 8 diff --git a/compiler-rt/test/profile/instrprof-darwin-dead-strip.c b/compiler-rt/test/profile/instrprof-darwin-dead-strip.c --- a/compiler-rt/test/profile/instrprof-darwin-dead-strip.c +++ b/compiler-rt/test/profile/instrprof-darwin-dead-strip.c @@ -1,7 +1,7 @@ // REQUIRES: osx-ld64-live_support // REQUIRES: lto -// RUN: %clang_profgen=%t.profraw -fcoverage-mapping -mllvm -enable-name-compression=false -DCODE=1 -Wl,-dead_strip -o %t %s +// RUN: %clang_profgen=%t.profraw -fcoverage-mapping -mllvm -name-compression=none -DCODE=1 -Wl,-dead_strip -o %t %s // RUN: %run %t // RUN: llvm-profdata merge -o %t.profdata %t.profraw // RUN: llvm-profdata show --all-functions %t.profdata | FileCheck %s -check-prefix=PROF @@ -10,7 +10,7 @@ // RUN: otool -V -s __DATA __llvm_prf_names %t | FileCheck %s -check-prefix=PRF_NAMES // RUN: otool -V -s __DATA __llvm_prf_cnts %t | FileCheck %s -check-prefix=PRF_CNTS -// RUN: %clang_lto_profgen=%t.lto.profraw -fcoverage-mapping -mllvm -enable-name-compression=false -DCODE=1 -Wl,-dead_strip -flto -o %t.lto %s +// RUN: %clang_lto_profgen=%t.lto.profraw -fcoverage-mapping -mllvm -name-compression=none -DCODE=1 -Wl,-dead_strip -flto -o %t.lto %s // RUN: %run %t.lto // RUN: llvm-profdata merge -o %t.lto.profdata %t.lto.profraw // RUN: llvm-profdata show --all-functions %t.lto.profdata | FileCheck %s -check-prefix=PROF diff --git a/compiler-rt/test/profile/instrprof-gc-sections.c b/compiler-rt/test/profile/instrprof-gc-sections.c --- a/compiler-rt/test/profile/instrprof-gc-sections.c +++ b/compiler-rt/test/profile/instrprof-gc-sections.c @@ -1,7 +1,7 @@ // REQUIRES: linux, lld-available // RUN: rm -rf %t.profraw -// RUN: %clang_profgen=%t.profraw -fuse-ld=lld -fcoverage-mapping -mllvm -enable-name-compression=false -DCODE=1 -ffunction-sections -fdata-sections -Wl,--gc-sections -o %t %s +// RUN: %clang_profgen=%t.profraw -fuse-ld=lld -fcoverage-mapping -mllvm -name-compression=none -DCODE=1 -ffunction-sections -fdata-sections -Wl,--gc-sections -o %t %s // RUN: %run %t // RUN: llvm-profdata merge -o %t.profdata %t.profraw // RUN: llvm-profdata show --all-functions %t.profdata | FileCheck %s -check-prefix=PROF @@ -11,7 +11,7 @@ // RUN: llvm-size -A %t | FileCheck %s -check-prefix=PRF_CNTS // RUN: rm -rf %t.lto.profraw -// RUN: %clang_lto_profgen=%t.lto.profraw -fuse-ld=lld -fcoverage-mapping -mllvm -enable-name-compression=false -DCODE=1 -ffunction-sections -fdata-sections -Wl,--gc-sections -flto -o %t.lto %s +// RUN: %clang_lto_profgen=%t.lto.profraw -fuse-ld=lld -fcoverage-mapping -mllvm -name-compression=none -DCODE=1 -ffunction-sections -fdata-sections -Wl,--gc-sections -flto -o %t.lto %s // RUN: %run %t.lto // RUN: llvm-profdata merge -o %t.lto.profdata %t.lto.profraw // RUN: llvm-profdata show --all-functions %t.lto.profdata | FileCheck %s -check-prefix=PROF diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -953,12 +953,18 @@ static bool getCompressDebugSections(opt::InputArgList &args) { StringRef s = args.getLastArgValue(OPT_compress_debug_sections, "none"); - if (s == "none") + if (s == "none") { return false; - if (s != "zlib") + } else if (s == "zlib") { + if (!compression::ZlibCompressionAlgorithm().supported()) + error("--compress-debug-sections: zlib is not available"); + } else if (s == "zstd") { + if (!compression::ZStdCompressionAlgorithm().supported()) + error("--compress-debug-sections: zstd is not available"); + } else { error("unknown --compress-debug-sections value: " + s); - if (!compression::zlib::isAvailable()) - error("--compress-debug-sections: zlib is not available"); + } + return true; } diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -109,6 +109,22 @@ return rawData.size() - bytesDropped; } +template +static void uncompressAux(const InputSectionBase *sec, uint8_t *out, + size_t size) { + auto *hdr = + reinterpret_cast(sec->rawData.data()); + ArrayRef compressed = + sec->rawData.slice(sizeof(typename ELFT::Chdr)); + if (Error e = (hdr->ch_type == ELFCOMPRESS_ZLIB + ? compression::ZlibCompressionAlgorithm().decompress( + compressed, out, size) + : compression::ZStdCompressionAlgorithm().decompress( + compressed, out, size))) + fatal(toString(sec) + + ": uncompress failed: " + llvm::toString(std::move(e))); +} + void InputSectionBase::uncompress() const { size_t size = uncompressedSize; uint8_t *uncompressedBuf; @@ -117,10 +133,7 @@ std::lock_guard lock(mu); uncompressedBuf = bAlloc().Allocate(size); } - - if (Error e = compression::zlib::uncompress(rawData, uncompressedBuf, size)) - fatal(toString(this) + - ": uncompress failed: " + llvm::toString(std::move(e))); + invokeELFT(uncompressAux, this, uncompressedBuf, size); rawData = makeArrayRef(uncompressedBuf, size); uncompressedSize = -1; } @@ -195,7 +208,7 @@ } // When a section is compressed, `rawData` consists with a header followed -// by zlib-compressed data. This function parses a header to initialize +// by zlib or zstd-compressed data. This function parses a header to initialize // `uncompressedSize` member and remove the header from `rawData`. template void InputSectionBase::parseCompressedHeader() { flags &= ~(uint64_t)SHF_COMPRESSED; @@ -208,9 +221,13 @@ auto *hdr = reinterpret_cast(rawData.data()); if (hdr->ch_type == ELFCOMPRESS_ZLIB) { - if (!compression::zlib::isAvailable()) + if (!compression::ZlibCompressionAlgorithm().supported()) error(toString(this) + " is compressed with ELFCOMPRESS_ZLIB, but lld is " "not built with zlib support"); + } else if (hdr->ch_type == ELFCOMPRESS_ZSTD) { + if (!compression::ZStdCompressionAlgorithm().supported()) + error(toString(this) + " is compressed with ELFCOMPRESS_ZSTD, but lld is " + "not built with zstd support"); } else { error(toString(this) + ": unsupported compression type (" + Twine(hdr->ch_type) + ")"); @@ -219,7 +236,6 @@ uncompressedSize = hdr->ch_size; alignment = std::max(hdr->ch_addralign, 1); - rawData = rawData.slice(sizeof(*hdr)); } InputSection *InputSectionBase::getLinkOrderDep() const { @@ -1217,10 +1233,35 @@ // If this is a compressed section, uncompress section contents directly // to the buffer. if (uncompressedSize >= 0) { + auto *hdr = reinterpret_cast(rawData.data()); size_t size = uncompressedSize; - if (Error e = compression::zlib::uncompress(rawData, buf, size)) - fatal(toString(this) + - ": uncompress failed: " + llvm::toString(std::move(e))); + if (hdr->ch_type == ELFCOMPRESS_ZLIB) { + if (!compression::ZlibCompressionAlgorithm().supported()) { + error(toString(this) + + " is compressed with ELFCOMPRESS_ZLIB, but lld is " + "not built with zlib support"); + } else { + if (Error e = compression::ZlibCompressionAlgorithm().decompress( + rawData.slice(sizeof(typename ELFT::Chdr)), buf, size)) + fatal(toString(this) + + ": uncompress failed: " + llvm::toString(std::move(e))); + } + } else if (hdr->ch_type == ELFCOMPRESS_ZSTD) { + if (!compression::ZStdCompressionAlgorithm().supported()) { + error(toString(this) + + " is compressed with ELFCOMPRESS_ZSTD, but lld is " + "not built with zstd support"); + } else { + if (Error e = compression::ZStdCompressionAlgorithm().decompress( + rawData.slice(sizeof(typename ELFT::Chdr)), buf, size)) + fatal(toString(this) + + ": uncompress failed: " + llvm::toString(std::move(e))); + } + } else { + error(toString(this) + ": unsupported compression type (" + + Twine(hdr->ch_type) + ")"); + return; + } uint8_t *bufEnd = buf + size; relocate(buf, bufEnd); return; diff --git a/lld/test/CMakeLists.txt b/lld/test/CMakeLists.txt --- a/lld/test/CMakeLists.txt +++ b/lld/test/CMakeLists.txt @@ -1,6 +1,7 @@ llvm_canonicalize_cmake_booleans( ENABLE_BACKTRACES LLVM_ENABLE_ZLIB + LLVM_ENABLE_ZSTD LLVM_ENABLE_LIBXML2 LLD_DEFAULT_LD_LLD_IS_MINGW LLVM_HAVE_LIBXAR diff --git a/lld/test/ELF/compressed-debug-input-zstd.s b/lld/test/ELF/compressed-debug-input-zstd.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/compressed-debug-input-zstd.s @@ -0,0 +1,42 @@ +# REQUIRES: x86, zstd + +# RUN: llvm-mc -filetype=obj -triple=x86_64 --compress-debug-sections=zstd %s -o %t.o + +# RUN: ld.lld %t.o -o %t.so -shared +# RUN: llvm-readobj --sections --section-data %t.so | FileCheck -check-prefix=DATA %s + +# DATA: Section { +# DATA: Index: 6 +# DATA: Name: .debug_str +# DATA-NEXT: Type: SHT_PROGBITS +# DATA-NEXT: Flags [ +# DATA-NEXT: SHF_MERGE (0x10) +# DATA-NEXT: SHF_STRINGS (0x20) +# DATA-NEXT: ] +# DATA-NEXT: Address: 0x0 +# DATA-NEXT: Offset: +# DATA-NEXT: Size: 69 +# DATA-NEXT: Link: 0 +# DATA-NEXT: Info: 0 +# DATA-NEXT: AddressAlignment: 1 +# DATA-NEXT: EntrySize: 1 +# DATA-NEXT: SectionData ( +# DATA-NEXT: 0000: 756E7369 676E6564 20696E74 00636861 |unsigned int.cha| +# DATA-NEXT: 0010: 7200756E 7369676E 65642063 68617200 |r.unsigned char.| +# DATA-NEXT: 0020: 73686F72 7420756E 7369676E 65642069 |short unsigned i| +# DATA-NEXT: 0030: 6E74006C 6F6E6720 756E7369 676E6564 |nt.long unsigned| +# DATA-NEXT: 0040: 20696E74 00 | int.| +# DATA-NEXT: ) +# DATA-NEXT: } + +.section .debug_str,"MS",@progbits,1 +.LASF2: + .string "short unsigned int" +.LASF3: + .string "unsigned int" +.LASF0: + .string "long unsigned int" +.LASF8: + .string "char" +.LASF1: + .string "unsigned char" diff --git a/lld/test/ELF/compressed-input-err-zstd.s b/lld/test/ELF/compressed-input-err-zstd.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/compressed-input-err-zstd.s @@ -0,0 +1,18 @@ +# UNSUPPORTED: zstd +# RUN: yaml2obj %s -o %t.o +# RUN: not ld.lld %t.o -o /dev/null 2>&1 | FileCheck %s + +# CHECK: error: {{.*}}.o:(.debug_info) is compressed with ELFCOMPRESS_ZSTD, but lld is not built with zstd support + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Type: SHT_PROGBITS + Name: .debug_info + Flags: [ SHF_COMPRESSED ] + AddressAlign: 8 + Content: "020000000000000000000000000000000100000000000000789c030000000001" diff --git a/lld/test/lit.site.cfg.py.in b/lld/test/lit.site.cfg.py.in --- a/lld/test/lit.site.cfg.py.in +++ b/lld/test/lit.site.cfg.py.in @@ -18,6 +18,7 @@ config.target_triple = "@LLVM_TARGET_TRIPLE@" config.python_executable = "@Python3_EXECUTABLE@" config.have_zlib = @LLVM_ENABLE_ZLIB@ +config.have_zstd = @LLVM_ENABLE_ZSTD@ config.have_libxar = @LLVM_HAVE_LIBXAR@ config.have_libxml2 = @LLVM_ENABLE_LIBXML2@ config.sizeof_void_p = @CMAKE_SIZEOF_VOID_P@ diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -1798,6 +1798,7 @@ // Legal values for ch_type field of compressed section header. enum { ELFCOMPRESS_ZLIB = 1, // ZLIB/DEFLATE algorithm. + ELFCOMPRESS_ZSTD = 2, // ZStandard algorithm. ELFCOMPRESS_LOOS = 0x60000000, // Start of OS-specific. ELFCOMPRESS_HIOS = 0x6fffffff, // End of OS-specific. ELFCOMPRESS_LOPROC = 0x70000000, // Start of processor-specific. diff --git a/llvm/include/llvm/MC/MCTargetOptions.h b/llvm/include/llvm/MC/MCTargetOptions.h --- a/llvm/include/llvm/MC/MCTargetOptions.h +++ b/llvm/include/llvm/MC/MCTargetOptions.h @@ -28,6 +28,7 @@ enum class DebugCompressionType { None, ///< No compression Z, ///< zlib style complession + ZStd, ///< zstd style complession }; enum class EmitDwarfUnwindType { diff --git a/llvm/include/llvm/Object/Decompressor.h b/llvm/include/llvm/Object/Decompressor.h --- a/llvm/include/llvm/Object/Decompressor.h +++ b/llvm/include/llvm/Object/Decompressor.h @@ -11,6 +11,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/Error.h" namespace llvm { @@ -44,10 +45,11 @@ private: Decompressor(StringRef Data); - Error consumeCompressedZLibHeader(bool Is64Bit, bool IsLittleEndian); + Error consumeCompressedSectionHeader(bool Is64Bit, bool IsLittleEndian); StringRef SectionData; uint64_t DecompressedSize; + compression::CompressionAlgorithm *CompressionScheme; }; } // end namespace object diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h --- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h +++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h @@ -1005,7 +1005,9 @@ // Compilation directory is stored separately and combined with relative // filenames to produce an absolute file path. Version6 = 5, - // The current version is Version6. + // not just zlib compression + Version7 = 6, + // The current version is Version7. CurrentVersion = INSTR_PROF_COVMAP_VERSION }; diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMappingWriter.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMappingWriter.h --- a/llvm/include/llvm/ProfileData/Coverage/CoverageMappingWriter.h +++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMappingWriter.h @@ -17,6 +17,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/ProfileData/Coverage/CoverageMapping.h" +#include "llvm/Support/Compression.h" namespace llvm { @@ -32,9 +33,12 @@ public: CoverageFilenamesSectionWriter(ArrayRef Filenames); - /// Write encoded filenames to the given output stream. If \p Compress is - /// true, attempt to compress the filenames. - void write(raw_ostream &OS, bool Compress = true); + /// Write encoded filenames to the given output stream. If \p + /// CompressionScheme is not an instance of + /// llvm::compression::NoneCompressionAlgorithm, attempt to compress the + /// filenames. + void write(raw_ostream &OS, + compression::CompressionAlgorithm *CompressionScheme); }; /// Writer for instrumentation based coverage mapping data. diff --git a/llvm/include/llvm/ProfileData/InstrProf.h b/llvm/include/llvm/ProfileData/InstrProf.h --- a/llvm/include/llvm/ProfileData/InstrProf.h +++ b/llvm/include/llvm/ProfileData/InstrProf.h @@ -26,6 +26,7 @@ #include "llvm/ProfileData/InstrProfData.inc" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compiler.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" @@ -214,13 +215,15 @@ /// third field is the uncompressed strings; otherwise it is the /// compressed string. When the string compression is off, the /// second field will have value zero. -Error collectPGOFuncNameStrings(ArrayRef NameStrs, - bool doCompression, std::string &Result); +Error collectPGOFuncNameStrings( + ArrayRef NameStrs, + compression::CompressionAlgorithm *CompressionScheme, std::string &Result); /// Produce \c Result string with the same format described above. The input /// is vector of PGO function name variables that are referenced. -Error collectPGOFuncNameStrings(ArrayRef NameVars, - std::string &Result, bool doCompression = true); +Error collectPGOFuncNameStrings( + ArrayRef NameVars, std::string &Result, + compression::CompressionAlgorithm *CompressionScheme); /// \c NameStrings is a string composed of one of more sub-strings encoded in /// the format described above. The substrings are separated by 0 or more zero @@ -1021,7 +1024,9 @@ Version7 = 7, // An additional (optional) memory profile type is added. Version8 = 8, - // The current version is 8. + // more compression types + Version9 = 9, + // The current version is 9. CurrentVersion = INSTR_PROF_INDEX_VERSION }; const uint64_t Version = ProfVersion::CurrentVersion; @@ -1198,7 +1203,8 @@ // Whether to compress function names in profile records, and filenames in // code coverage mappings. Used by the Instrumentation library and unit tests. -extern cl::opt DoInstrProfNameCompression; +extern cl::opt + InstrProfNameCompressionScheme; } // end namespace llvm #endif // LLVM_PROFILEDATA_INSTRPROF_H diff --git a/llvm/include/llvm/ProfileData/InstrProfData.inc b/llvm/include/llvm/ProfileData/InstrProfData.inc --- a/llvm/include/llvm/ProfileData/InstrProfData.inc +++ b/llvm/include/llvm/ProfileData/InstrProfData.inc @@ -648,11 +648,11 @@ /* FIXME: Please remedy the fixme in the header before bumping the version. */ /* Raw profile format version (start from 1). */ -#define INSTR_PROF_RAW_VERSION 8 +#define INSTR_PROF_RAW_VERSION 9 /* Indexed profile format version (start from 1). */ -#define INSTR_PROF_INDEX_VERSION 8 +#define INSTR_PROF_INDEX_VERSION 9 /* Coverage mapping format version (start from 0). */ -#define INSTR_PROF_COVMAP_VERSION 5 +#define INSTR_PROF_COVMAP_VERSION 6 /* Profile version is always of type uint64_t. Reserve the upper 8 bits in the * version for other variants of profile. We set the lowest bit of the upper 8 diff --git a/llvm/include/llvm/ProfileData/SampleProfWriter.h b/llvm/include/llvm/ProfileData/SampleProfWriter.h --- a/llvm/include/llvm/ProfileData/SampleProfWriter.h +++ b/llvm/include/llvm/ProfileData/SampleProfWriter.h @@ -16,6 +16,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/IR/ProfileSummary.h" #include "llvm/ProfileData/SampleProf.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/raw_ostream.h" #include @@ -56,12 +57,14 @@ /// /// Create a new file writer based on the value of \p Format. static ErrorOr> - create(StringRef Filename, SampleProfileFormat Format); + create(StringRef Filename, SampleProfileFormat Format, + compression::CompressionAlgorithm *CompressionScheme); /// Create a new stream writer based on the value of \p Format. /// For testing. static ErrorOr> - create(std::unique_ptr &OS, SampleProfileFormat Format); + create(std::unique_ptr &OS, SampleProfileFormat Format, + compression::CompressionAlgorithm *CompressionScheme); virtual void setProfileSymbolList(ProfileSymbolList *PSL) {} virtual void setToCompressAllSections() {} @@ -70,8 +73,9 @@ virtual void resetSecLayout(SectionLayout SL) {} protected: - SampleProfileWriter(std::unique_ptr &OS) - : OutputStream(std::move(OS)) {} + SampleProfileWriter(std::unique_ptr &OS, + compression::CompressionAlgorithm *CompressionScheme) + : OutputStream(std::move(OS)), CompressionScheme(CompressionScheme) {} /// Write a file header for the profile file. virtual std::error_code writeHeader(const SampleProfileMap &ProfileMap) = 0; @@ -90,6 +94,9 @@ /// Profile format. SampleProfileFormat Format = SPF_None; + + /// Compression scheme; + compression::CompressionAlgorithm *CompressionScheme; }; /// Sample-based profile writer (text format). @@ -98,8 +105,9 @@ std::error_code writeSample(const FunctionSamples &S) override; protected: - SampleProfileWriterText(std::unique_ptr &OS) - : SampleProfileWriter(OS), Indent(0) {} + SampleProfileWriterText(std::unique_ptr &OS, + compression::CompressionAlgorithm *CompressionScheme) + : SampleProfileWriter(OS, CompressionScheme), Indent(0) {} std::error_code writeHeader(const SampleProfileMap &ProfileMap) override { return sampleprof_error::success; @@ -112,15 +120,18 @@ unsigned Indent; friend ErrorOr> - SampleProfileWriter::create(std::unique_ptr &OS, - SampleProfileFormat Format); + SampleProfileWriter::create( + std::unique_ptr &OS, SampleProfileFormat Format, + compression::CompressionAlgorithm *CompressionScheme); }; /// Sample-based profile writer (binary format). class SampleProfileWriterBinary : public SampleProfileWriter { public: - SampleProfileWriterBinary(std::unique_ptr &OS) - : SampleProfileWriter(OS) {} + SampleProfileWriterBinary( + std::unique_ptr &OS, + compression::CompressionAlgorithm *CompressionScheme) + : SampleProfileWriter(OS, CompressionScheme) {} std::error_code writeSample(const FunctionSamples &S) override; @@ -144,8 +155,9 @@ private: friend ErrorOr> - SampleProfileWriter::create(std::unique_ptr &OS, - SampleProfileFormat Format); + SampleProfileWriter::create( + std::unique_ptr &OS, SampleProfileFormat Format, + compression::CompressionAlgorithm *CompressionScheme); }; class SampleProfileWriterRawBinary : public SampleProfileWriterBinary { @@ -324,8 +336,10 @@ class SampleProfileWriterExtBinary : public SampleProfileWriterExtBinaryBase { public: - SampleProfileWriterExtBinary(std::unique_ptr &OS) - : SampleProfileWriterExtBinaryBase(OS) {} + SampleProfileWriterExtBinary( + std::unique_ptr &OS, + compression::CompressionAlgorithm *CompressionScheme) + : SampleProfileWriterExtBinaryBase(OS, CompressionScheme) {} private: std::error_code writeDefaultLayout(const SampleProfileMap &ProfileMap); diff --git a/llvm/include/llvm/Support/CommandLine.h b/llvm/include/llvm/Support/CommandLine.h --- a/llvm/include/llvm/Support/CommandLine.h +++ b/llvm/include/llvm/Support/CommandLine.h @@ -27,8 +27,10 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Twine.h" #include "llvm/ADT/iterator_range.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/raw_ostream.h" @@ -1157,6 +1159,30 @@ //-------------------------------------------------- +extern template class basic_parser; + +template <> +class parser + : public basic_parser { +public: + parser(Option &O) : basic_parser(O) {} + + // Return true on error. + bool parse(Option &, StringRef, StringRef Arg, + compression::CompressionAlgorithm *&Value); + + // Overload in subclass to provide a better default value. + StringRef getValueName() const override { return "compression scheme"; } + + void printOptionDiff(const Option &O, compression::CompressionAlgorithm *V, + OptVal Default, size_t GlobalWidth) const; + + // An out-of-line virtual method to provide a 'home' for this class. + void anchor() override; +}; + +//-------------------------------------------------- + extern template class basic_parser; template <> class parser : public basic_parser { diff --git a/llvm/include/llvm/Support/Compression.h b/llvm/include/llvm/Support/Compression.h --- a/llvm/include/llvm/Support/Compression.h +++ b/llvm/include/llvm/Support/Compression.h @@ -15,55 +15,173 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/DataTypes.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" namespace llvm { template class SmallVectorImpl; class Error; namespace compression { -namespace zlib { -constexpr int NoCompression = 0; -constexpr int BestSpeedCompression = 1; -constexpr int DefaultCompression = 6; -constexpr int BestSizeCompression = 9; - -bool isAvailable(); - -void compress(ArrayRef Input, - SmallVectorImpl &CompressedBuffer, - int Level = DefaultCompression); - -Error uncompress(ArrayRef Input, uint8_t *UncompressedBuffer, - size_t &UncompressedSize); - -Error uncompress(ArrayRef Input, - SmallVectorImpl &UncompressedBuffer, - size_t UncompressedSize); - -} // End of namespace zlib - -namespace zstd { - -constexpr int NoCompression = -5; -constexpr int BestSpeedCompression = 1; -constexpr int DefaultCompression = 5; -constexpr int BestSizeCompression = 12; - -bool isAvailable(); - -void compress(ArrayRef Input, - SmallVectorImpl &CompressedBuffer, - int Level = DefaultCompression); - -Error uncompress(ArrayRef Input, uint8_t *UncompressedBuffer, - size_t &UncompressedSize); - -Error uncompress(ArrayRef Input, - SmallVectorImpl &UncompressedBuffer, - size_t UncompressedSize); - -} // End of namespace zstd +enum class SupportCompressionType : uint8_t { + Unknown = 255, ///< Abstract compression + None = 0, ///< No compression + Zlib = 1, ///< zlib style complession + ZStd = 2, ///< zstd style complession +}; + +// This is the base class of all compression algorithms that llvm support +// handles. + +class CompressionAlgorithm { +public: + virtual SupportCompressionType getAlgorithmId() = 0; + + virtual StringRef getName() = 0; + + virtual bool supported() = 0; + + virtual int getBestSpeedLevel() = 0; + virtual int getDefaultLevel() = 0; + virtual int getBestSizeLevel() = 0; + + virtual void compress(ArrayRef Input, + SmallVectorImpl &CompressedBuffer, + int Level) = 0; + virtual void compress(ArrayRef Input, + SmallVectorImpl &CompressedBuffer) = 0; + + virtual Error decompress(ArrayRef Input, uint8_t *UncompressedBuffer, + size_t &UncompressedSize) = 0; + virtual Error decompress(ArrayRef Input, + SmallVectorImpl &UncompressedBuffer, + size_t UncompressedSize) = 0; +}; + +template +class CompressionAlgorithmImpl : public CompressionAlgorithm { +public: + virtual SupportCompressionType getAlgorithmId() { + return CompressionAlgorithmType::AlgorithmId; + } + + virtual StringRef getName() { return CompressionAlgorithmType::Name; } + + virtual bool supported() { return CompressionAlgorithmType::Supported(); } + + virtual int getBestSpeedLevel() { + return CompressionAlgorithmType::BestSpeedCompression; + } + virtual int getDefaultLevel() { + return CompressionAlgorithmType::DefaultCompression; + } + virtual int getBestSizeLevel() { + return CompressionAlgorithmType::BestSizeCompression; + } + + virtual void compress(ArrayRef Input, + SmallVectorImpl &CompressedBuffer, int Level) { + + return CompressionAlgorithmType::Compress(Input, CompressedBuffer, Level); + } + virtual void compress(ArrayRef Input, + SmallVectorImpl &CompressedBuffer) { + return CompressionAlgorithmType::Compress( + Input, CompressedBuffer, CompressionAlgorithmType::DefaultCompression); + } + + virtual Error decompress(ArrayRef Input, uint8_t *UncompressedBuffer, + size_t &UncompressedSize) { + return CompressionAlgorithmType::Decompress(Input, UncompressedBuffer, + UncompressedSize); + } + virtual Error decompress(ArrayRef Input, + SmallVectorImpl &UncompressedBuffer, + size_t UncompressedSize) { + UncompressedBuffer.resize_for_overwrite(UncompressedSize); + Error E = CompressionAlgorithmType::Decompress( + Input, UncompressedBuffer.data(), UncompressedSize); + if (UncompressedSize < UncompressedBuffer.size()) + UncompressedBuffer.truncate(UncompressedSize); + return E; + } +}; + +class ZStdCompressionAlgorithm + : public CompressionAlgorithmImpl { +public: + constexpr static SupportCompressionType AlgorithmId = + SupportCompressionType::ZStd; + constexpr static StringRef Name = "zstd"; + constexpr static int BestSpeedCompression = 1; + constexpr static int DefaultCompression = 5; + constexpr static int BestSizeCompression = 12; + static void Compress(ArrayRef Input, + SmallVectorImpl &CompressedBuffer, int Level); + static Error Decompress(ArrayRef Input, uint8_t *UncompressedBuffer, + size_t &UncompressedSize); + static bool Supported(); + + constexpr ZStdCompressionAlgorithm(){}; +}; + +class ZlibCompressionAlgorithm + : public CompressionAlgorithmImpl { +public: + constexpr static SupportCompressionType AlgorithmId = + SupportCompressionType::Zlib; + constexpr static StringRef Name = "zlib"; + constexpr static int BestSpeedCompression = 1; + constexpr static int DefaultCompression = 6; + constexpr static int BestSizeCompression = 9; + static void Compress(ArrayRef Input, + SmallVectorImpl &CompressedBuffer, int Level); + static Error Decompress(ArrayRef Input, uint8_t *UncompressedBuffer, + size_t &UncompressedSize); + static bool Supported(); + + constexpr ZlibCompressionAlgorithm(){}; +}; + +class UnknownCompressionAlgorithm + : public CompressionAlgorithmImpl { +public: + constexpr static SupportCompressionType AlgorithmId = + SupportCompressionType::Unknown; + constexpr static StringRef Name = "unknown"; + constexpr static int BestSpeedCompression = -999; + constexpr static int DefaultCompression = -999; + constexpr static int BestSizeCompression = -999; + static void Compress(ArrayRef Input, + SmallVectorImpl &CompressedBuffer, int Level); + static Error Decompress(ArrayRef Input, uint8_t *UncompressedBuffer, + size_t &UncompressedSize); + static bool Supported(); + + constexpr UnknownCompressionAlgorithm(){}; +}; + +class NoneCompressionAlgorithm + : public CompressionAlgorithmImpl { +public: + constexpr static SupportCompressionType AlgorithmId = + SupportCompressionType::None; + constexpr static StringRef Name = "none"; + constexpr static int BestSpeedCompression = 0; + constexpr static int DefaultCompression = 0; + constexpr static int BestSizeCompression = 0; + static void Compress(ArrayRef Input, + SmallVectorImpl &CompressedBuffer, int Level); + static Error Decompress(ArrayRef Input, uint8_t *UncompressedBuffer, + size_t &UncompressedSize); + static bool Supported(); + + constexpr NoneCompressionAlgorithm(){}; +}; + +llvm::compression::CompressionAlgorithm * +CompressionAlgorithmFromId(uint8_t CompressionSchemeId); } // End of namespace compression diff --git a/llvm/include/llvm/Transforms/Instrumentation.h b/llvm/include/llvm/Transforms/Instrumentation.h --- a/llvm/include/llvm/Transforms/Instrumentation.h +++ b/llvm/include/llvm/Transforms/Instrumentation.h @@ -19,6 +19,7 @@ #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Instruction.h" +#include "llvm/Support/Compression.h" #include #include #include @@ -120,6 +121,8 @@ // Name of the profile file to use as output std::string InstrProfileOutput; + compression::CompressionAlgorithm *CompressionScheme; + InstrProfOptions() = default; }; diff --git a/llvm/include/llvm/Transforms/Instrumentation/InstrProfiling.h b/llvm/include/llvm/Transforms/Instrumentation/InstrProfiling.h --- a/llvm/include/llvm/Transforms/Instrumentation/InstrProfiling.h +++ b/llvm/include/llvm/Transforms/Instrumentation/InstrProfiling.h @@ -18,6 +18,7 @@ #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/PassManager.h" #include "llvm/ProfileData/InstrProf.h" +#include "llvm/Support/Compression.h" #include "llvm/Transforms/Instrumentation.h" #include #include @@ -134,6 +135,7 @@ /// Create a static initializer for our data, on platforms that need it, /// and for any profile output file that was specified. void emitInitialization(); + compression::CompressionAlgorithm *CompressionScheme; }; } // end namespace llvm diff --git a/llvm/lib/MC/ELFObjectWriter.cpp b/llvm/lib/MC/ELFObjectWriter.cpp --- a/llvm/lib/MC/ELFObjectWriter.cpp +++ b/llvm/lib/MC/ELFObjectWriter.cpp @@ -144,7 +144,8 @@ uint64_t align(unsigned Alignment); - bool maybeWriteCompression(uint32_t ChType, uint64_t Size, + bool maybeWriteCompression(DebugCompressionType CompressionType, + uint64_t Size, SmallVectorImpl &CompressedContents, unsigned Alignment); @@ -818,12 +819,24 @@ // Include the debug info compression header. bool ELFWriter::maybeWriteCompression( - uint32_t ChType, uint64_t Size, + DebugCompressionType CompressionType, uint64_t Size, SmallVectorImpl &CompressedContents, unsigned Alignment) { uint64_t HdrSize = is64Bit() ? sizeof(ELF::Elf32_Chdr) : sizeof(ELF::Elf64_Chdr); if (Size <= HdrSize + CompressedContents.size()) return false; + uint64_t ChType; + switch (CompressionType) { + case DebugCompressionType::Z: + ChType = ELF::ELFCOMPRESS_ZLIB; + break; + case DebugCompressionType::ZStd: + ChType = ELF::ELFCOMPRESS_ZSTD; + break; + default: + return false; + } + // Platform specific header is followed by compressed data. if (is64Bit()) { // Write Elf64_Chdr header. @@ -847,30 +860,41 @@ auto &MC = Asm.getContext(); const auto &MAI = MC.getAsmInfo(); - - bool CompressionEnabled = - MAI->compressDebugSections() != DebugCompressionType::None; + const DebugCompressionType CompressionType = MAI->compressDebugSections(); + bool CompressionEnabled = CompressionType != DebugCompressionType::None; if (!CompressionEnabled || !SectionName.startswith(".debug_")) { Asm.writeSectionData(W.OS, &Section, Layout); return; } - assert(MAI->compressDebugSections() == DebugCompressionType::Z && - "expected zlib style compression"); + assert((CompressionType == DebugCompressionType::Z || + CompressionType == DebugCompressionType::ZStd) && + "expected zlib or zstd style compression"); SmallVector UncompressedData; raw_svector_ostream VecOS(UncompressedData); Asm.writeSectionData(VecOS, &Section, Layout); SmallVector Compressed; - const uint32_t ChType = ELF::ELFCOMPRESS_ZLIB; - compression::zlib::compress( - makeArrayRef(reinterpret_cast(UncompressedData.data()), - UncompressedData.size()), - Compressed); - - if (!maybeWriteCompression(ChType, UncompressedData.size(), Compressed, - Sec.getAlignment())) { + switch (CompressionType) { + case DebugCompressionType::Z: + compression::ZlibCompressionAlgorithm().compress( + makeArrayRef(reinterpret_cast(UncompressedData.data()), + UncompressedData.size()), + Compressed); + break; + case DebugCompressionType::ZStd: + compression::ZStdCompressionAlgorithm().compress( + makeArrayRef(reinterpret_cast(UncompressedData.data()), + UncompressedData.size()), + Compressed); + break; + case DebugCompressionType::None: + break; + } + + if (!maybeWriteCompression(CompressionType, UncompressedData.size(), + Compressed, Sec.getAlignment())) { W.OS << UncompressedData; return; } diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.cpp b/llvm/lib/ObjCopy/ELF/ELFObject.cpp --- a/llvm/lib/ObjCopy/ELF/ELFObject.cpp +++ b/llvm/lib/ObjCopy/ELF/ELFObject.cpp @@ -439,10 +439,33 @@ ArrayRef Compressed = Sec.OriginalData.slice(sizeof(Elf_Chdr_Impl)); SmallVector DecompressedContent; - if (Error Err = compression::zlib::uncompress(Compressed, DecompressedContent, - static_cast(Sec.Size))) - return createStringError(errc::invalid_argument, - "'" + Sec.Name + "': " + toString(std::move(Err))); + DebugCompressionType CompressionType = + reinterpret_cast *>(Sec.OriginalData.data()) + ->ch_type == ELF::ELFCOMPRESS_ZSTD + ? DebugCompressionType::ZStd + : DebugCompressionType::Z; + + switch (CompressionType) { + case DebugCompressionType::Z: + if (Error Err1 = compression::ZlibCompressionAlgorithm().decompress( + Compressed, DecompressedContent, static_cast(Sec.Size))) { + return createStringError(errc::invalid_argument, + "'" + Sec.Name + + "': " + toString(std::move(Err1))); + } + break; + case DebugCompressionType::ZStd: + if (Error Err = compression::ZStdCompressionAlgorithm().decompress( + Compressed, DecompressedContent, static_cast(Sec.Size))) { + return createStringError(errc::invalid_argument, + "'" + Sec.Name + + "': " + toString(std::move(Err))); + } + break; + case DebugCompressionType::None: + llvm_unreachable("unexpected DebugCompressionType::None"); + break; + } uint8_t *Buf = reinterpret_cast(Out.getBufferStart()) + Sec.Offset; std::copy(DecompressedContent.begin(), DecompressedContent.end(), Buf); @@ -498,6 +521,9 @@ case DebugCompressionType::Z: Chdr.ch_type = ELF::ELFCOMPRESS_ZLIB; break; + case DebugCompressionType::ZStd: + Chdr.ch_type = ELF::ELFCOMPRESS_ZSTD; + break; } Chdr.ch_size = Sec.DecompressedSize; Chdr.ch_addralign = Sec.DecompressedAlign; @@ -512,7 +538,18 @@ DebugCompressionType CompressionType) : SectionBase(Sec), CompressionType(CompressionType), DecompressedSize(Sec.OriginalData.size()), DecompressedAlign(Sec.Align) { - compression::zlib::compress(OriginalData, CompressedData); + switch (CompressionType) { + case DebugCompressionType::Z: + compression::ZlibCompressionAlgorithm().compress(OriginalData, + CompressedData); + break; + case DebugCompressionType::ZStd: + compression::ZStdCompressionAlgorithm().compress(OriginalData, + CompressedData); + break; + case DebugCompressionType::None: + break; + } assert(CompressionType != DebugCompressionType::None); Flags |= ELF::SHF_COMPRESSED; diff --git a/llvm/lib/Object/Decompressor.cpp b/llvm/lib/Object/Decompressor.cpp --- a/llvm/lib/Object/Decompressor.cpp +++ b/llvm/lib/Object/Decompressor.cpp @@ -19,11 +19,9 @@ Expected Decompressor::create(StringRef Name, StringRef Data, bool IsLE, bool Is64Bit) { - if (!compression::zlib::isAvailable()) - return createError("zlib is not available"); Decompressor D(Data); - if (Error Err = D.consumeCompressedZLibHeader(Is64Bit, IsLE)) + if (Error Err = D.consumeCompressedSectionHeader(Is64Bit, IsLE)) return std::move(Err); return D; } @@ -31,8 +29,8 @@ Decompressor::Decompressor(StringRef Data) : SectionData(Data), DecompressedSize(0) {} -Error Decompressor::consumeCompressedZLibHeader(bool Is64Bit, - bool IsLittleEndian) { +Error Decompressor::consumeCompressedSectionHeader(bool Is64Bit, + bool IsLittleEndian) { using namespace ELF; uint64_t HdrSize = Is64Bit ? sizeof(Elf64_Chdr) : sizeof(Elf32_Chdr); if (SectionData.size() < HdrSize) @@ -40,10 +38,17 @@ DataExtractor Extractor(SectionData, IsLittleEndian, 0); uint64_t Offset = 0; - if (Extractor.getUnsigned(&Offset, Is64Bit ? sizeof(Elf64_Word) - : sizeof(Elf32_Word)) != - ELFCOMPRESS_ZLIB) + uint64_t ELFCompressionSchemeId = Extractor.getUnsigned( + &Offset, Is64Bit ? sizeof(Elf64_Word) : sizeof(Elf32_Word)); + if (ELFCompressionSchemeId == ELFCOMPRESS_ZLIB) { + CompressionScheme = new compression::ZlibCompressionAlgorithm(); + } else if (ELFCompressionSchemeId == ELFCOMPRESS_ZSTD) { + CompressionScheme = new compression::ZStdCompressionAlgorithm(); + } else { return createError("unsupported compression type"); + } + if (!CompressionScheme->supported()) + return createError(CompressionScheme->getName() + " is not available"); // Skip Elf64_Chdr::ch_reserved field. if (Is64Bit) @@ -57,6 +62,6 @@ Error Decompressor::decompress(MutableArrayRef Buffer) { size_t Size = Buffer.size(); - return compression::zlib::uncompress(arrayRefFromStringRef(SectionData), + return CompressionScheme->decompress(arrayRefFromStringRef(SectionData), Buffer.data(), Size); } diff --git a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp --- a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp +++ b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp @@ -119,7 +119,16 @@ return Err; if (CompressedLen > 0) { - if (!compression::zlib::isAvailable()) + compression::CompressionAlgorithm *CompressionScheme = + new compression::ZlibCompressionAlgorithm(); + if (Version >= CovMapVersion::Version7) { + uint64_t CompressionSchemeId; + if (auto Err = readULEB128(CompressionSchemeId)) + return Err; + CompressionScheme = + compression::CompressionAlgorithmFromId(CompressionSchemeId); + } + if (!CompressionScheme->supported()) return make_error( coveragemap_error::decompression_failed); @@ -129,7 +138,7 @@ // Read compressed filenames. StringRef CompressedFilenames = Data.substr(0, CompressedLen); Data = Data.substr(CompressedLen); - auto Err = compression::zlib::uncompress( + auto Err = CompressionScheme->decompress( arrayRefFromStringRef(CompressedFilenames), StorageBuf, UncompressedLen); if (Err) { diff --git a/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp b/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp --- a/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp +++ b/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp @@ -36,7 +36,8 @@ #endif } -void CoverageFilenamesSectionWriter::write(raw_ostream &OS, bool Compress) { +void CoverageFilenamesSectionWriter::write( + raw_ostream &OS, compression::CompressionAlgorithm *CompressionScheme) { std::string FilenamesStr; { raw_string_ostream FilenamesOS{FilenamesStr}; @@ -47,20 +48,26 @@ } SmallVector CompressedStr; - bool doCompression = Compress && compression::zlib::isAvailable() && - DoInstrProfNameCompression; + bool doCompression = (CompressionScheme->getAlgorithmId() != + compression::NoneCompressionAlgorithm::AlgorithmId) && + CompressionScheme->supported(); if (doCompression) - compression::zlib::compress(arrayRefFromStringRef(FilenamesStr), + CompressionScheme->compress(arrayRefFromStringRef(FilenamesStr), CompressedStr, - compression::zlib::BestSizeCompression); + CompressionScheme->getBestSizeLevel()); // ::= // // + // IF compressed: + // // ( | ) encodeULEB128(Filenames.size(), OS); encodeULEB128(FilenamesStr.size(), OS); encodeULEB128(doCompression ? CompressedStr.size() : 0U, OS); + if (doCompression) + encodeULEB128(static_cast(CompressionScheme->getAlgorithmId()), + OS); OS << (doCompression ? toStringRef(CompressedStr) : StringRef(FilenamesStr)); } diff --git a/llvm/lib/ProfileData/InstrProf.cpp b/llvm/lib/ProfileData/InstrProf.cpp --- a/llvm/lib/ProfileData/InstrProf.cpp +++ b/llvm/lib/ProfileData/InstrProf.cpp @@ -205,9 +205,11 @@ namespace llvm { -cl::opt DoInstrProfNameCompression( - "enable-name-compression", - cl::desc("Enable name/filename string compression"), cl::init(true)); +cl::opt InstrProfNameCompressionScheme( + "name-compression", + cl::desc("Scheme for name/filename string compression (none/zlib/ztsd), " + "defaults to zstd"), + cl::init(new compression::ZStdCompressionAlgorithm())); std::string getInstrProfSectionName(InstrProfSectKind IPSK, Triple::ObjectFormatType OF, @@ -435,8 +437,9 @@ return 0; } -Error collectPGOFuncNameStrings(ArrayRef NameStrs, - bool doCompression, std::string &Result) { +Error collectPGOFuncNameStrings( + ArrayRef NameStrs, + compression::CompressionAlgorithm *CompressionScheme, std::string &Result) { assert(!NameStrs.empty() && "No name data to emit"); uint8_t Header[16], *P = Header; @@ -450,26 +453,43 @@ unsigned EncLen = encodeULEB128(UncompressedNameStrings.length(), P); P += EncLen; - auto WriteStringToResult = [&](size_t CompressedLen, StringRef InputStr) { - EncLen = encodeULEB128(CompressedLen, P); - P += EncLen; - char *HeaderStr = reinterpret_cast(&Header[0]); - unsigned HeaderLen = P - &Header[0]; - Result.append(HeaderStr, HeaderLen); - Result += InputStr; - return Error::success(); - }; - - if (!doCompression) { - return WriteStringToResult(0, UncompressedNameStrings); + auto WriteStringToResult = + [&](size_t CompressedLen, + compression::SupportCompressionType CompressionSchemeId, + StringRef InputStr) { + if (CompressedLen == 0) { + EncLen = encodeULEB128(CompressedLen, P); + P += EncLen; + char *HeaderStr = reinterpret_cast(&Header[0]); + unsigned HeaderLen = P - &Header[0]; + Result.append(HeaderStr, HeaderLen); + Result += InputStr; + return Error::success(); + } else { + EncLen = encodeULEB128(CompressedLen, P); + P += EncLen; + EncLen = encodeULEB128(static_cast(CompressionSchemeId), P); + P += EncLen; + char *HeaderStr = reinterpret_cast(&Header[0]); + unsigned HeaderLen = P - &Header[0]; + Result.append(HeaderStr, HeaderLen); + Result += InputStr; + return Error::success(); + } + }; + + if (CompressionScheme->getAlgorithmId() == + compression::NoneCompressionAlgorithm::AlgorithmId) { + return WriteStringToResult(0, compression::SupportCompressionType::None, + UncompressedNameStrings); } - SmallVector CompressedNameStrings; - compression::zlib::compress(arrayRefFromStringRef(UncompressedNameStrings), + CompressionScheme->compress(arrayRefFromStringRef(UncompressedNameStrings), CompressedNameStrings, - compression::zlib::BestSizeCompression); + CompressionScheme->getBestSizeLevel()); return WriteStringToResult(CompressedNameStrings.size(), + CompressionScheme->getAlgorithmId(), toStringRef(CompressedNameStrings)); } @@ -480,14 +500,19 @@ return NameStr; } -Error collectPGOFuncNameStrings(ArrayRef NameVars, - std::string &Result, bool doCompression) { +Error collectPGOFuncNameStrings( + ArrayRef NameVars, std::string &Result, + compression::CompressionAlgorithm *CompressionScheme) { std::vector NameStrs; for (auto *NameVar : NameVars) { NameStrs.push_back(std::string(getPGOFuncNameVarInitializer(NameVar))); } return collectPGOFuncNameStrings( - NameStrs, compression::zlib::isAvailable() && doCompression, Result); + NameStrs, + CompressionScheme->supported() + ? CompressionScheme + : new compression::NoneCompressionAlgorithm(), + Result); } Error readPGOFuncNameStrings(StringRef NameStrings, InstrProfSymtab &Symtab) { @@ -500,13 +525,21 @@ uint64_t CompressedSize = decodeULEB128(P, &N); P += N; bool isCompressed = (CompressedSize != 0); + compression::CompressionAlgorithm *CompressionScheme = + new compression::NoneCompressionAlgorithm(); + if (isCompressed) { + uint64_t CompressionSchemeId = decodeULEB128(P, &N); + P += N; + CompressionScheme = + compression::CompressionAlgorithmFromId(CompressionSchemeId); + } SmallVector UncompressedNameStrings; StringRef NameStrings; if (isCompressed) { - if (!llvm::compression::zlib::isAvailable()) + if (!CompressionScheme->supported()) return make_error(instrprof_error::zlib_unavailable); - if (Error E = compression::zlib::uncompress( + if (Error E = CompressionScheme->decompress( makeArrayRef(P, CompressedSize), UncompressedNameStrings, UncompressedSize)) { consumeError(std::move(E)); @@ -1344,7 +1377,7 @@ // When a new field is added in the header add a case statement here to // populate it. static_assert( - IndexedInstrProf::ProfVersion::CurrentVersion == Version8, + IndexedInstrProf::ProfVersion::CurrentVersion == Version9, "Please update the reading code below if a new field has been added, " "if not add a case statement to fall through to the latest version."); case 8ull: @@ -1363,7 +1396,7 @@ // When a new field is added to the header add a case statement here to // compute the size as offset of the new field + size of the new field. This // relies on the field being added to the end of the list. - static_assert(IndexedInstrProf::ProfVersion::CurrentVersion == Version8, + static_assert(IndexedInstrProf::ProfVersion::CurrentVersion == Version9, "Please update the size computation below if a new field has " "been added to the header, if not add a case statement to " "fall through to the latest version."); diff --git a/llvm/lib/ProfileData/InstrProfCorrelator.cpp b/llvm/lib/ProfileData/InstrProfCorrelator.cpp --- a/llvm/lib/ProfileData/InstrProfCorrelator.cpp +++ b/llvm/lib/ProfileData/InstrProfCorrelator.cpp @@ -148,8 +148,8 @@ return make_error( instrprof_error::unable_to_correlate_profile, "could not find any profile metadata in debug info"); - auto Result = - collectPGOFuncNameStrings(NamesVec, /*doCompression=*/false, Names); + auto Result = collectPGOFuncNameStrings( + NamesVec, new compression::NoneCompressionAlgorithm(), Names); CounterOffsets.clear(); NamesVec.clear(); return Result; diff --git a/llvm/lib/ProfileData/SampleProfReader.cpp b/llvm/lib/ProfileData/SampleProfReader.cpp --- a/llvm/lib/ProfileData/SampleProfReader.cpp +++ b/llvm/lib/ProfileData/SampleProfReader.cpp @@ -877,12 +877,18 @@ if (std::error_code EC = CompressSize.getError()) return EC; - if (!llvm::compression::zlib::isAvailable()) + auto CompressionSchemeId = readNumber(); + if (std::error_code EC = CompressionSchemeId.getError()) + return EC; + + compression::CompressionAlgorithm *CompressionScheme = + compression::CompressionAlgorithmFromId(*CompressionSchemeId); + if (!CompressionScheme->supported()) return sampleprof_error::zlib_unavailable; uint8_t *Buffer = Allocator.Allocate(DecompressBufSize); size_t UCSize = DecompressBufSize; - llvm::Error E = compression::zlib::uncompress( + llvm::Error E = CompressionScheme->decompress( makeArrayRef(Data, *CompressSize), Buffer, UCSize); if (E) return sampleprof_error::uncompress_failed; diff --git a/llvm/lib/ProfileData/SampleProfWriter.cpp b/llvm/lib/ProfileData/SampleProfWriter.cpp --- a/llvm/lib/ProfileData/SampleProfWriter.cpp +++ b/llvm/lib/ProfileData/SampleProfWriter.cpp @@ -78,7 +78,7 @@ } std::error_code SampleProfileWriterExtBinaryBase::compressAndOutput() { - if (!llvm::compression::zlib::isAvailable()) + if (!CompressionScheme->supported()) return sampleprof_error::zlib_unavailable; std::string &UncompressedStrings = static_cast(LocalBufStream.get())->str(); @@ -86,11 +86,12 @@ return sampleprof_error::success; auto &OS = *OutputStream; SmallVector CompressedStrings; - compression::zlib::compress(arrayRefFromStringRef(UncompressedStrings), + CompressionScheme->compress(arrayRefFromStringRef(UncompressedStrings), CompressedStrings, - compression::zlib::BestSizeCompression); + CompressionScheme->getBestSizeLevel()); encodeULEB128(UncompressedStrings.size(), OS); encodeULEB128(CompressedStrings.size(), OS); + encodeULEB128(static_cast(CompressionScheme->getAlgorithmId()), OS); OS << toStringRef(CompressedStrings); UncompressedStrings.clear(); return sampleprof_error::success; @@ -840,8 +841,9 @@ /// \param Format Encoding format for the profile file. /// /// \returns an error code indicating the status of the created writer. -ErrorOr> -SampleProfileWriter::create(StringRef Filename, SampleProfileFormat Format) { +ErrorOr> SampleProfileWriter::create( + StringRef Filename, SampleProfileFormat Format, + compression::CompressionAlgorithm *CompressionScheme) { std::error_code EC; std::unique_ptr OS; if (Format == SPF_Binary || Format == SPF_Ext_Binary || @@ -852,7 +854,7 @@ if (EC) return EC; - return create(OS, Format); + return create(OS, Format, CompressionScheme); } /// Create a sample profile stream writer based on the specified format. @@ -862,9 +864,9 @@ /// \param Format Encoding format for the profile file. /// /// \returns an error code indicating the status of the created writer. -ErrorOr> -SampleProfileWriter::create(std::unique_ptr &OS, - SampleProfileFormat Format) { +ErrorOr> SampleProfileWriter::create( + std::unique_ptr &OS, SampleProfileFormat Format, + compression::CompressionAlgorithm *CompressionScheme) { std::error_code EC; std::unique_ptr Writer; @@ -874,13 +876,13 @@ return sampleprof_error::unsupported_writing_format; if (Format == SPF_Binary) - Writer.reset(new SampleProfileWriterRawBinary(OS)); + Writer.reset(new SampleProfileWriterRawBinary(OS, CompressionScheme)); else if (Format == SPF_Ext_Binary) - Writer.reset(new SampleProfileWriterExtBinary(OS)); + Writer.reset(new SampleProfileWriterExtBinary(OS, CompressionScheme)); else if (Format == SPF_Compact_Binary) - Writer.reset(new SampleProfileWriterCompactBinary(OS)); + Writer.reset(new SampleProfileWriterCompactBinary(OS, CompressionScheme)); else if (Format == SPF_Text) - Writer.reset(new SampleProfileWriterText(OS)); + Writer.reset(new SampleProfileWriterText(OS, CompressionScheme)); else if (Format == SPF_GCC) EC = sampleprof_error::unsupported_writing_format; else diff --git a/llvm/lib/Support/CommandLine.cpp b/llvm/lib/Support/CommandLine.cpp --- a/llvm/lib/Support/CommandLine.cpp +++ b/llvm/lib/Support/CommandLine.cpp @@ -31,6 +31,7 @@ #include "llvm/ADT/Triple.h" #include "llvm/ADT/Twine.h" #include "llvm/Config/config.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Error.h" @@ -43,6 +44,7 @@ #include "llvm/Support/Process.h" #include "llvm/Support/StringSaver.h" #include "llvm/Support/VirtualFileSystem.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -67,6 +69,7 @@ template class basic_parser; template class basic_parser; template class basic_parser; +template class basic_parser; template class basic_parser; template class opt; @@ -74,6 +77,7 @@ template class opt; template class opt; template class opt; +template class opt; } // namespace cl } // namespace llvm @@ -95,6 +99,7 @@ void parser::anchor() {} void parser::anchor() {} void parser::anchor() {} +void parser::anchor() {} //===----------------------------------------------------------------------===// @@ -1767,7 +1772,12 @@ else Errs << GlobalParser->ProgramName << ": for the " << PrintArg(ArgName, 0); - Errs << " option: " << Message << "\n"; + Errs << " option:\n"; + Errs << "- "; + Errs.changeColor(raw_ostream::RED, true); + Errs << "error:"; + Errs.resetColor(); + Errs << " " << Message << "\n"; return true; } @@ -1913,6 +1923,34 @@ "' is invalid value for boolean argument! Try 0 or 1"); } +// parser implementation +// +bool parser::parse( + Option &O, StringRef, StringRef Arg, + compression::CompressionAlgorithm *&Value) { + Value = llvm::StringSwitch(Arg.str()) + .Case("none", new compression::NoneCompressionAlgorithm()) + .Case("zlib", new compression::ZlibCompressionAlgorithm()) + .Case("zstd", new compression::ZStdCompressionAlgorithm()) + .Default(new compression::UnknownCompressionAlgorithm()); + if (Value->getAlgorithmId() == + compression::UnknownCompressionAlgorithm::AlgorithmId) { + + StringRef ArgName = O.ArgStr; + errs() << GlobalParser->ProgramName << ": "; + errs() << "for the " << PrintArg(ArgName, 0) << " option:\n"; + errs() << "- "; + WithColor::error() << ("'" + Arg.str() + + "' is not a recognized compression scheme!") + << "\n"; + outs() << "- "; + WithColor::note() << "Try one of none, zstd, or zlib" + << "\n"; + return true; + } + return false; +} + // parser implementation // bool parser::parse(Option &O, StringRef ArgName, StringRef Arg, @@ -2158,6 +2196,7 @@ PRINT_OPT_DIFF(unsigned long long) PRINT_OPT_DIFF(double) PRINT_OPT_DIFF(float) +PRINT_OPT_DIFF(compression::CompressionAlgorithm *) PRINT_OPT_DIFF(char) void parser::printOptionDiff(const Option &O, StringRef V, diff --git a/llvm/lib/Support/Compression.cpp b/llvm/lib/Support/Compression.cpp --- a/llvm/lib/Support/Compression.cpp +++ b/llvm/lib/Support/Compression.cpp @@ -27,6 +27,71 @@ using namespace llvm; using namespace llvm::compression; +constexpr SupportCompressionType UnknownCompressionAlgorithm::AlgorithmId; +constexpr StringRef UnknownCompressionAlgorithm::Name; +constexpr int UnknownCompressionAlgorithm::BestSpeedCompression; +constexpr int UnknownCompressionAlgorithm::DefaultCompression; +constexpr int UnknownCompressionAlgorithm::BestSizeCompression; + +bool UnknownCompressionAlgorithm::Supported() { return false; }; + +void UnknownCompressionAlgorithm::Compress( + ArrayRef Input, SmallVectorImpl &CompressedBuffer, + int Level) { + llvm_unreachable("method:\"compress\" is unsupported for compression " + "algorithm:\"unknown\", reason:\"can't call on unknown\""); +}; +Error UnknownCompressionAlgorithm::Decompress(ArrayRef Input, + uint8_t *UncompressedBuffer, + size_t &UncompressedSize) { + llvm_unreachable("method:\"decompress\" is unsupported for compression " + "algorithm:\"unknown\", reason:\"can't call on unknown\""); +} + +constexpr SupportCompressionType NoneCompressionAlgorithm::AlgorithmId; +constexpr StringRef NoneCompressionAlgorithm::Name; +constexpr int NoneCompressionAlgorithm::BestSpeedCompression; +constexpr int NoneCompressionAlgorithm::DefaultCompression; +constexpr int NoneCompressionAlgorithm::BestSizeCompression; + +bool NoneCompressionAlgorithm::Supported() { return true; }; + +void NoneCompressionAlgorithm::Compress( + ArrayRef Input, SmallVectorImpl &CompressedBuffer, + int Level) { + unsigned long CompressedSize = Input.size(); + CompressedBuffer.resize_for_overwrite(CompressedSize); + // SmallVectorImpl() + CompressedBuffer.assign(SmallVector(Input.begin(), Input.end())); + + // Tell MemorySanitizer that zlib output buffer is fully initialized. + // This avoids a false report when running LLVM with uninstrumented ZLib. + __msan_unpoison(CompressedBuffer.data(), CompressedSize); + if (CompressedSize < CompressedBuffer.size()) + CompressedBuffer.truncate(CompressedSize); +}; +Error NoneCompressionAlgorithm::Decompress(ArrayRef Input, + uint8_t *UncompressedBuffer, + size_t &UncompressedSize) { + // Tell MemorySanitizer that zlib output buffer is fully initialized. + // This avoids a false report when running LLVM with uninstrumented ZLib. + if (UncompressedSize < Input.size()) { + return make_error("decompressed buffer target size too small", + inconvertibleErrorCode()); + } + UncompressedSize = Input.size(); + memcpy(UncompressedBuffer, Input.data(), UncompressedSize); + + __msan_unpoison(UncompressedBuffer, UncompressedSize); + return Error::success(); +} + +constexpr SupportCompressionType ZlibCompressionAlgorithm::AlgorithmId; +constexpr StringRef ZlibCompressionAlgorithm::Name; +constexpr int ZlibCompressionAlgorithm::BestSpeedCompression; +constexpr int ZlibCompressionAlgorithm::DefaultCompression; +constexpr int ZlibCompressionAlgorithm::BestSizeCompression; + #if LLVM_ENABLE_ZLIB static StringRef convertZlibCodeToString(int Code) { @@ -45,10 +110,11 @@ } } -bool zlib::isAvailable() { return true; } +bool ZlibCompressionAlgorithm::Supported() { return true; }; -void zlib::compress(ArrayRef Input, - SmallVectorImpl &CompressedBuffer, int Level) { +void ZlibCompressionAlgorithm::Compress( + ArrayRef Input, SmallVectorImpl &CompressedBuffer, + int Level) { unsigned long CompressedSize = ::compressBound(Input.size()); CompressedBuffer.resize_for_overwrite(CompressedSize); int Res = ::compress2((Bytef *)CompressedBuffer.data(), &CompressedSize, @@ -61,10 +127,10 @@ __msan_unpoison(CompressedBuffer.data(), CompressedSize); if (CompressedSize < CompressedBuffer.size()) CompressedBuffer.truncate(CompressedSize); -} - -Error zlib::uncompress(ArrayRef Input, uint8_t *UncompressedBuffer, - size_t &UncompressedSize) { +}; +Error ZlibCompressionAlgorithm::Decompress(ArrayRef Input, + uint8_t *UncompressedBuffer, + size_t &UncompressedSize) { int Res = ::uncompress((Bytef *)UncompressedBuffer, (uLongf *)&UncompressedSize, (const Bytef *)Input.data(), Input.size()); @@ -74,42 +140,41 @@ return Res ? make_error(convertZlibCodeToString(Res), inconvertibleErrorCode()) : Error::success(); -} - -Error zlib::uncompress(ArrayRef Input, - SmallVectorImpl &UncompressedBuffer, - size_t UncompressedSize) { - UncompressedBuffer.resize_for_overwrite(UncompressedSize); - Error E = - zlib::uncompress(Input, UncompressedBuffer.data(), UncompressedSize); - if (UncompressedSize < UncompressedBuffer.size()) - UncompressedBuffer.truncate(UncompressedSize); - return E; -} +}; #else -bool zlib::isAvailable() { return false; } -void zlib::compress(ArrayRef Input, - SmallVectorImpl &CompressedBuffer, int Level) { - llvm_unreachable("zlib::compress is unavailable"); -} -Error zlib::uncompress(ArrayRef Input, uint8_t *UncompressedBuffer, - size_t &UncompressedSize) { - llvm_unreachable("zlib::uncompress is unavailable"); -} -Error zlib::uncompress(ArrayRef Input, - SmallVectorImpl &UncompressedBuffer, - size_t UncompressedSize) { - llvm_unreachable("zlib::uncompress is unavailable"); -} +bool ZlibCompressionAlgorithm::Supported() { return false; }; + +void ZlibCompressionAlgorithm::Compress( + ArrayRef Input, SmallVectorImpl &CompressedBuffer, + int Level) { + llvm_unreachable( + "method:\"compress\" is unsupported for compression algorithm:\"zlib\", " + "reason:\"llvm not compiled with zlib support\""); +}; +Error ZlibCompressionAlgorithm::Decompress(ArrayRef Input, + uint8_t *UncompressedBuffer, + size_t &UncompressedSize) { + llvm_unreachable( + "method:\"decompress\" is unsupported for compression " + "algorithm:\"zlib\", reason:\"llvm not compiled with zlib support\""); +}; + #endif +constexpr SupportCompressionType ZStdCompressionAlgorithm::AlgorithmId; +constexpr StringRef ZStdCompressionAlgorithm::Name; +constexpr int ZStdCompressionAlgorithm::BestSpeedCompression; +constexpr int ZStdCompressionAlgorithm::DefaultCompression; +constexpr int ZStdCompressionAlgorithm::BestSizeCompression; + #if LLVM_ENABLE_ZSTD -bool zstd::isAvailable() { return true; } +bool ZStdCompressionAlgorithm::Supported() { return true; }; -void zstd::compress(ArrayRef Input, - SmallVectorImpl &CompressedBuffer, int Level) { +void ZStdCompressionAlgorithm::Compress( + ArrayRef Input, SmallVectorImpl &CompressedBuffer, + int Level) { unsigned long CompressedBufferSize = ::ZSTD_compressBound(Input.size()); CompressedBuffer.resize_for_overwrite(CompressedBufferSize); unsigned long CompressedSize = @@ -122,10 +187,10 @@ __msan_unpoison(CompressedBuffer.data(), CompressedSize); if (CompressedSize < CompressedBuffer.size()) CompressedBuffer.truncate(CompressedSize); -} - -Error zstd::uncompress(ArrayRef Input, uint8_t *UncompressedBuffer, - size_t &UncompressedSize) { +}; +Error ZStdCompressionAlgorithm::Decompress(ArrayRef Input, + uint8_t *UncompressedBuffer, + size_t &UncompressedSize) { const size_t Res = ::ZSTD_decompress(UncompressedBuffer, UncompressedSize, (const uint8_t *)Input.data(), Input.size()); @@ -136,32 +201,47 @@ return ZSTD_isError(Res) ? make_error(ZSTD_getErrorName(Res), inconvertibleErrorCode()) : Error::success(); -} - -Error zstd::uncompress(ArrayRef Input, - SmallVectorImpl &UncompressedBuffer, - size_t UncompressedSize) { - UncompressedBuffer.resize_for_overwrite(UncompressedSize); - Error E = - zstd::uncompress(Input, UncompressedBuffer.data(), UncompressedSize); - if (UncompressedSize < UncompressedBuffer.size()) - UncompressedBuffer.truncate(UncompressedSize); - return E; -} +}; #else -bool zstd::isAvailable() { return false; } -void zstd::compress(ArrayRef Input, - SmallVectorImpl &CompressedBuffer, int Level) { - llvm_unreachable("zstd::compress is unavailable"); -} -Error zstd::uncompress(ArrayRef Input, uint8_t *UncompressedBuffer, - size_t &UncompressedSize) { - llvm_unreachable("zstd::uncompress is unavailable"); -} -Error zstd::uncompress(ArrayRef Input, - SmallVectorImpl &UncompressedBuffer, - size_t UncompressedSize) { - llvm_unreachable("zstd::uncompress is unavailable"); -} +bool ZStdCompressionAlgorithm::Supported() { return false; }; + +void ZStdCompressionAlgorithm::Compress( + ArrayRef Input, SmallVectorImpl &CompressedBuffer, + int Level) { + llvm_unreachable( + "method:\"compress\" is unsupported for compression algorithm:\"zstd\", " + "reason:\"llvm not compiled with zstd support\""); +}; +Error ZStdCompressionAlgorithm::Decompress(ArrayRef Input, + uint8_t *UncompressedBuffer, + size_t &UncompressedSize) { + llvm_unreachable( + "method:\"decompress\" is unsupported for compression " + "algorithm:\"zstd\", reason:\"llvm not compiled with zstd support\""); +}; + #endif + +llvm::compression::CompressionAlgorithm * +llvm::compression::CompressionAlgorithmFromId(uint8_t CompressionSchemeId) { + llvm::compression::CompressionAlgorithm *CompressionScheme = + new llvm::compression::UnknownCompressionAlgorithm(); + switch (CompressionSchemeId) { + case static_cast( + llvm::compression::NoneCompressionAlgorithm::AlgorithmId): + CompressionScheme = new llvm::compression::NoneCompressionAlgorithm(); + break; + case static_cast( + llvm::compression::ZlibCompressionAlgorithm::AlgorithmId): + CompressionScheme = new llvm::compression::ZlibCompressionAlgorithm(); + break; + case static_cast( + llvm::compression::ZStdCompressionAlgorithm::AlgorithmId): + CompressionScheme = new llvm::compression::ZStdCompressionAlgorithm(); + break; + default: + break; + } + return CompressionScheme; +} \ No newline at end of file diff --git a/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp b/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp --- a/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp +++ b/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp @@ -1118,7 +1118,7 @@ std::string CompressedNameStr; if (Error E = collectPGOFuncNameStrings(ReferencedNames, CompressedNameStr, - DoInstrProfNameCompression)) { + InstrProfNameCompressionScheme)) { report_fatal_error(Twine(toString(std::move(E))), false); } diff --git a/llvm/test/tools/llvm-cov/coverage-prefix-map.test b/llvm/test/tools/llvm-cov/coverage-prefix-map.test --- a/llvm/test/tools/llvm-cov/coverage-prefix-map.test +++ b/llvm/test/tools/llvm-cov/coverage-prefix-map.test @@ -20,7 +20,7 @@ # cd %S/Inputs/coverage_prefix_map cp -r . /tmp/coverage_prefix_map -clang -fprofile-instr-generate -mllvm -enable-name-compression=false -fcoverage-mapping -fcoverage-prefix-map=$PWD=. main.cc -o main +clang -fprofile-instr-generate -mllvm -name-compression=none -fcoverage-mapping -fcoverage-prefix-map=$PWD=. main.cc -o main LLVM_PROFILE_FILE="main.raw" ./main llvm-profdata merge main.raw -o main.profdata llvm-cov convert-for-testing ./main -o ./main.covmapping diff --git a/llvm/test/tools/llvm-cov/multiple-objects-not-all-instrumented.test b/llvm/test/tools/llvm-cov/multiple-objects-not-all-instrumented.test --- a/llvm/test/tools/llvm-cov/multiple-objects-not-all-instrumented.test +++ b/llvm/test/tools/llvm-cov/multiple-objects-not-all-instrumented.test @@ -7,6 +7,6 @@ Instructions for regenerating the test: clang -std=c++11 not_instrumented.cc -o not_instrumented -clang -std=c++11 -mllvm -enable-name-compression=false -fprofile-instr-generate -fcoverage-mapping instrumented.cc -o instrumented +clang -std=c++11 -mllvm -name-compression=none -fprofile-instr-generate -fcoverage-mapping instrumented.cc -o instrumented LLVM_PROFILE_FILE="instrumented.raw" ./instrumented llvm-profdata merge instrumented.raw -o instrumented.profdata diff --git a/llvm/test/tools/llvm-cov/multiple-objects.test b/llvm/test/tools/llvm-cov/multiple-objects.test --- a/llvm/test/tools/llvm-cov/multiple-objects.test +++ b/llvm/test/tools/llvm-cov/multiple-objects.test @@ -13,8 +13,8 @@ Instructions for regenerating the test: -clang -std=c++11 -mllvm -enable-name-compression=false -fprofile-instr-generate -fcoverage-mapping use_1.cc -o use_1 -clang -std=c++11 -mllvm -enable-name-compression=false -fprofile-instr-generate -fcoverage-mapping use_2.cc -o use_2 +clang -std=c++11 -mllvm -name-compression=none -fprofile-instr-generate -fcoverage-mapping use_1.cc -o use_1 +clang -std=c++11 -mllvm -name-compression=none -fprofile-instr-generate -fcoverage-mapping use_2.cc -o use_2 LLVM_PROFILE_FILE="use_1.raw" ./use_1 LLVM_PROFILE_FILE="use_2.raw" ./use_2 llvm-profdata merge use_{1,2}.raw -o merged.profdata diff --git a/llvm/test/tools/llvm-cov/multithreaded-report.test b/llvm/test/tools/llvm-cov/multithreaded-report.test --- a/llvm/test/tools/llvm-cov/multithreaded-report.test +++ b/llvm/test/tools/llvm-cov/multithreaded-report.test @@ -84,7 +84,7 @@ cp -r . /tmp/multithreaded_report -clang++ -std=c++11 -mllvm -enable-name-compression=false \ +clang++ -std=c++11 -mllvm -name-compression=none \ -fprofile-instr-generate -fcoverage-mapping \ /tmp/multithreaded_report/*.cc -o main diff --git a/llvm/test/tools/llvm-cov/sources-specified.test b/llvm/test/tools/llvm-cov/sources-specified.test --- a/llvm/test/tools/llvm-cov/sources-specified.test +++ b/llvm/test/tools/llvm-cov/sources-specified.test @@ -53,7 +53,7 @@ # cd %S/Inputs/sources_specified cp -r . /tmp/sources_specified -clang -mllvm -enable-name-compression=false -fprofile-instr-generate \ +clang -mllvm -name-compression=none -fprofile-instr-generate \ -fcoverage-mapping /tmp/sources_specified/main.cc -o main LLVM_PROFILE_FILE="main.raw" ./main diff --git a/llvm/test/tools/llvm-profdata/c-general.test b/llvm/test/tools/llvm-profdata/c-general.test --- a/llvm/test/tools/llvm-profdata/c-general.test +++ b/llvm/test/tools/llvm-profdata/c-general.test @@ -5,7 +5,7 @@ $ CFE=$SRC/tools/clang $ TESTDIR=$SRC/test/tools/llvm-profdata $ CFE_TESTDIR=$CFE/test/Profile -$ clang -o a.out -fprofile-instr-generate $CFE_TESTDIR/c-general.c -mllvm -enable-name-compression=false +$ clang -o a.out -fprofile-instr-generate $CFE_TESTDIR/c-general.c -mllvm -name-compression=none $ LLVM_PROFILE_FILE=$TESTDIR/Inputs/c-general.profraw ./a.out RUN: llvm-profdata show %p/Inputs/c-general.profraw -o - | FileCheck %s diff --git a/llvm/test/tools/llvm-profdata/memprof-merge.test b/llvm/test/tools/llvm-profdata/memprof-merge.test --- a/llvm/test/tools/llvm-profdata/memprof-merge.test +++ b/llvm/test/tools/llvm-profdata/memprof-merge.test @@ -21,7 +21,7 @@ ``` # Collect instrprof profile with name compression disabled since some buildbots # do not have zlib. -clang -mllvm -enable-name-compression=false -fprofile-generate source.c -o instr.out +clang -mllvm -name-compression=none -fprofile-generate source.c -o instr.out ./instr.out mv *.profraw basic.profraw diff --git a/llvm/tools/llvm-mc/llvm-mc.cpp b/llvm/tools/llvm-mc/llvm-mc.cpp --- a/llvm/tools/llvm-mc/llvm-mc.cpp +++ b/llvm/tools/llvm-mc/llvm-mc.cpp @@ -401,10 +401,18 @@ MAI->setRelaxELFRelocations(RelaxELFRel); if (CompressDebugSections != DebugCompressionType::None) { - if (!compression::zlib::isAvailable()) { - WithColor::error(errs(), ProgName) - << "build tools with zlib to enable -compress-debug-sections"; - return 1; + if (CompressDebugSections == DebugCompressionType::Z) { + if (!compression::ZlibCompressionAlgorithm().supported()) { + WithColor::error(errs(), ProgName) + << "build tools with zlib to enable -compress-debug-sections=zlib"; + return 1; + } + } else if (CompressDebugSections == DebugCompressionType::ZStd) { + if (!compression::ZStdCompressionAlgorithm().supported()) { + WithColor::error(errs(), ProgName) + << "build tools with zstd to enable -compress-debug-sections=zstd"; + return 1; + } } MAI->setCompressDebugSections(CompressDebugSections); } diff --git a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp --- a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp +++ b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp @@ -722,16 +722,30 @@ if (const auto *A = InputArgs.getLastArg(OBJCOPY_compress_debug_sections)) { Config.CompressionType = StringSwitch(A->getValue()) .Case("zlib", DebugCompressionType::Z) + .Case("zstd", DebugCompressionType::ZStd) .Default(DebugCompressionType::None); + if (Config.CompressionType == DebugCompressionType::None) return createStringError( errc::invalid_argument, "invalid or unsupported --compress-debug-sections format: %s", A->getValue()); - if (!compression::zlib::isAvailable()) - return createStringError( - errc::invalid_argument, - "LLVM was not compiled with LLVM_ENABLE_ZLIB: can not compress"); + switch (Config.CompressionType) { + case DebugCompressionType::None: + break; + case DebugCompressionType::Z: + if (!compression::ZlibCompressionAlgorithm().supported()) + return createStringError( + errc::invalid_argument, + "LLVM was not compiled with LLVM_ENABLE_ZLIB: can not compress"); + break; + case DebugCompressionType::ZStd: + if (!compression::ZStdCompressionAlgorithm().supported()) + return createStringError( + errc::invalid_argument, + "LLVM was not compiled with LLVM_ENABLE_ZSTD: can not compress"); + break; + } } Config.AddGnuDebugLink = InputArgs.getLastArgValue(OBJCOPY_add_gnu_debuglink); @@ -993,11 +1007,6 @@ "--decompress-debug-sections"); } - if (Config.DecompressDebugSections && !compression::zlib::isAvailable()) - return createStringError( - errc::invalid_argument, - "LLVM was not compiled with LLVM_ENABLE_ZLIB: cannot decompress"); - if (Config.ExtractPartition && Config.ExtractMainPartition) return createStringError(errc::invalid_argument, "cannot specify --extract-partition together with " diff --git a/llvm/tools/llvm-objcopy/ObjcopyOpts.td b/llvm/tools/llvm-objcopy/ObjcopyOpts.td --- a/llvm/tools/llvm-objcopy/ObjcopyOpts.td +++ b/llvm/tools/llvm-objcopy/ObjcopyOpts.td @@ -33,7 +33,7 @@ : Joined<["--"], "compress-debug-sections=">, MetaVarName<"format">, HelpText<"Compress DWARF debug sections using specified format. Supported " - "formats: zlib">; + "formats: zlib, zstd">; def : Flag<["--"], "compress-debug-sections">, Alias, AliasArgs<["zlib"]>; def decompress_debug_sections : Flag<["--"], "decompress-debug-sections">, diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp --- a/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -25,6 +25,7 @@ #include "llvm/ProfileData/SampleProfReader.h" #include "llvm/ProfileData/SampleProfWriter.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/Discriminator.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" @@ -829,7 +830,8 @@ } auto WriterOrErr = - SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]); + SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat], + new compression::ZlibCompressionAlgorithm()); if (std::error_code EC = WriterOrErr.getError()) exitWithErrorCode(EC, OutputFilename); diff --git a/llvm/tools/llvm-profgen/ProfileGenerator.cpp b/llvm/tools/llvm-profgen/ProfileGenerator.cpp --- a/llvm/tools/llvm-profgen/ProfileGenerator.cpp +++ b/llvm/tools/llvm-profgen/ProfileGenerator.cpp @@ -160,7 +160,9 @@ } void ProfileGeneratorBase::write() { - auto WriterOrErr = SampleProfileWriter::create(OutputFilename, OutputFormat); + auto WriterOrErr = + SampleProfileWriter::create(OutputFilename, OutputFormat, + new compression::ZlibCompressionAlgorithm()); if (std::error_code EC = WriterOrErr.getError()) exitWithError(EC, OutputFilename); diff --git a/llvm/unittests/ProfileData/CoverageMappingTest.cpp b/llvm/unittests/ProfileData/CoverageMappingTest.cpp --- a/llvm/unittests/ProfileData/CoverageMappingTest.cpp +++ b/llvm/unittests/ProfileData/CoverageMappingTest.cpp @@ -932,7 +932,11 @@ { raw_string_ostream OS(EncodedFilenames); CoverageFilenamesSectionWriter Writer(Paths); - Writer.write(OS, Compress); + Writer.write( + OS, Compress ? (compression::CompressionAlgorithm + *)new compression::ZlibCompressionAlgorithm() + : (compression::CompressionAlgorithm + *)new compression::NoneCompressionAlgorithm()); } std::vector ReadFilenames; @@ -956,7 +960,11 @@ { raw_string_ostream OS(EncodedFilenames); CoverageFilenamesSectionWriter Writer(Paths); - Writer.write(OS, Compress); + Writer.write( + OS, Compress ? (compression::CompressionAlgorithm + *)new compression::ZlibCompressionAlgorithm() + : (compression::CompressionAlgorithm + *)new compression::NoneCompressionAlgorithm()); } StringRef CompilationDir = "out"; diff --git a/llvm/unittests/ProfileData/InstrProfTest.cpp b/llvm/unittests/ProfileData/InstrProfTest.cpp --- a/llvm/unittests/ProfileData/InstrProfTest.cpp +++ b/llvm/unittests/ProfileData/InstrProfTest.cpp @@ -1146,19 +1146,31 @@ for (bool DoCompression : {false, true}) { // Compressing: std::string FuncNameStrings1; - EXPECT_THAT_ERROR(collectPGOFuncNameStrings( - FuncNames1, - (DoCompression && compression::zlib::isAvailable()), - FuncNameStrings1), - Succeeded()); + EXPECT_THAT_ERROR( + collectPGOFuncNameStrings( + FuncNames1, + (DoCompression && + compression::ZlibCompressionAlgorithm().supported()) + ? (compression::CompressionAlgorithm + *)new compression::ZlibCompressionAlgorithm() + : (compression::CompressionAlgorithm + *)new compression::NoneCompressionAlgorithm(), + FuncNameStrings1), + Succeeded()); // Compressing: std::string FuncNameStrings2; - EXPECT_THAT_ERROR(collectPGOFuncNameStrings( - FuncNames2, - (DoCompression && compression::zlib::isAvailable()), - FuncNameStrings2), - Succeeded()); + EXPECT_THAT_ERROR( + collectPGOFuncNameStrings( + FuncNames2, + (DoCompression && + compression::ZlibCompressionAlgorithm().supported()) + ? (compression::CompressionAlgorithm + *)new compression::ZlibCompressionAlgorithm() + : (compression::CompressionAlgorithm + *)new compression::NoneCompressionAlgorithm(), + FuncNameStrings2), + Succeeded()); for (int Padding = 0; Padding < 2; Padding++) { // Join with paddings : diff --git a/llvm/unittests/ProfileData/SampleProfTest.cpp b/llvm/unittests/ProfileData/SampleProfTest.cpp --- a/llvm/unittests/ProfileData/SampleProfTest.cpp +++ b/llvm/unittests/ProfileData/SampleProfTest.cpp @@ -50,7 +50,8 @@ std::error_code EC; std::unique_ptr OS( new raw_fd_ostream(Profile, EC, sys::fs::OF_None)); - auto WriterOrErr = SampleProfileWriter::create(OS, Format); + auto WriterOrErr = SampleProfileWriter::create( + OS, Format, new compression::ZlibCompressionAlgorithm()); ASSERT_TRUE(NoError(WriterOrErr.getError())); Writer = std::move(WriterOrErr.get()); } diff --git a/llvm/unittests/Support/CompressionTest.cpp b/llvm/unittests/Support/CompressionTest.cpp --- a/llvm/unittests/Support/CompressionTest.cpp +++ b/llvm/unittests/Support/CompressionTest.cpp @@ -22,31 +22,43 @@ namespace { -#if LLVM_ENABLE_ZLIB -static void testZlibCompression(StringRef Input, int Level) { +static void testCompressionAlgorithm( + StringRef Input, int Level, + compression::CompressionAlgorithm *CompressionScheme, + std::string ExpectedDestinationBufferTooSmallErrorMessage) { SmallVector Compressed; SmallVector Uncompressed; - zlib::compress(arrayRefFromStringRef(Input), Compressed, Level); + CompressionScheme->compress(arrayRefFromStringRef(Input), Compressed, Level); // Check that uncompressed buffer is the same as original. - Error E = zlib::uncompress(Compressed, Uncompressed, Input.size()); + Error E = + CompressionScheme->decompress(Compressed, Uncompressed, Input.size()); consumeError(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); - EXPECT_EQ("zlib error: Z_BUF_ERROR", llvm::toString(std::move(E))); + E = CompressionScheme->decompress(Compressed, Uncompressed, + Input.size() - 1); + EXPECT_EQ(ExpectedDestinationBufferTooSmallErrorMessage, + llvm::toString(std::move(E))); } } +#if LLVM_ENABLE_ZLIB +static void testZlibCompression(StringRef Input, int Level) { + testCompressionAlgorithm(Input, Level, new ZlibCompressionAlgorithm(), + "zlib error: Z_BUF_ERROR"); +} + TEST(CompressionTest, Zlib) { - testZlibCompression("", zlib::DefaultCompression); + compression::CompressionAlgorithm *CompressionScheme = + new compression::ZlibCompressionAlgorithm(); + testZlibCompression("", CompressionScheme->getDefaultLevel()); - testZlibCompression("hello, world!", zlib::NoCompression); - testZlibCompression("hello, world!", zlib::BestSizeCompression); - testZlibCompression("hello, world!", zlib::BestSpeedCompression); - testZlibCompression("hello, world!", zlib::DefaultCompression); + testZlibCompression("hello, world!", CompressionScheme->getBestSizeLevel()); + testZlibCompression("hello, world!", CompressionScheme->getBestSpeedLevel()); + testZlibCompression("hello, world!", CompressionScheme->getDefaultLevel()); const size_t kSize = 1024; char BinaryData[kSize]; @@ -54,38 +66,27 @@ BinaryData[i] = i & 255; StringRef BinaryDataStr(BinaryData, kSize); - testZlibCompression(BinaryDataStr, zlib::NoCompression); - testZlibCompression(BinaryDataStr, zlib::BestSizeCompression); - testZlibCompression(BinaryDataStr, zlib::BestSpeedCompression); - testZlibCompression(BinaryDataStr, zlib::DefaultCompression); + testZlibCompression(BinaryDataStr, CompressionScheme->getBestSizeLevel()); + testZlibCompression(BinaryDataStr, CompressionScheme->getBestSpeedLevel()); + testZlibCompression(BinaryDataStr, CompressionScheme->getDefaultLevel()); } #endif #if LLVM_ENABLE_ZSTD -static void testZstdCompression(StringRef Input, int Level) { - SmallVector Compressed; - SmallVector Uncompressed; - zstd::compress(arrayRefFromStringRef(Input), Compressed, Level); - // Check that uncompressed buffer is the same as original. - Error E = zstd::uncompress(Compressed, Uncompressed, Input.size()); - consumeError(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); - EXPECT_EQ("Destination buffer is too small", llvm::toString(std::move(E))); - } +static void testZStdCompression(StringRef Input, int Level) { + testCompressionAlgorithm(Input, Level, new ZStdCompressionAlgorithm(), + "Destination buffer is too small"); } TEST(CompressionTest, Zstd) { - testZstdCompression("", zstd::DefaultCompression); + compression::CompressionAlgorithm *CompressionScheme = + new compression::ZStdCompressionAlgorithm(); + testZStdCompression("", CompressionScheme->getDefaultLevel()); - testZstdCompression("hello, world!", zstd::NoCompression); - testZstdCompression("hello, world!", zstd::BestSizeCompression); - testZstdCompression("hello, world!", zstd::BestSpeedCompression); - testZstdCompression("hello, world!", zstd::DefaultCompression); + testZStdCompression("hello, world!", CompressionScheme->getBestSizeLevel()); + testZStdCompression("hello, world!", CompressionScheme->getBestSpeedLevel()); + testZStdCompression("hello, world!", CompressionScheme->getDefaultLevel()); const size_t kSize = 1024; char BinaryData[kSize]; @@ -93,10 +94,9 @@ BinaryData[i] = i & 255; StringRef BinaryDataStr(BinaryData, kSize); - testZstdCompression(BinaryDataStr, zstd::NoCompression); - testZstdCompression(BinaryDataStr, zstd::BestSizeCompression); - testZstdCompression(BinaryDataStr, zstd::BestSpeedCompression); - testZstdCompression(BinaryDataStr, zstd::DefaultCompression); + testZStdCompression(BinaryDataStr, CompressionScheme->getBestSizeLevel()); + testZStdCompression(BinaryDataStr, CompressionScheme->getBestSpeedLevel()); + testZStdCompression(BinaryDataStr, CompressionScheme->getDefaultLevel()); } #endif }