Reimplement debug info compression by compressing the whole section, rather than a fragment.

To support compressing the debug_line section that contains multiple
fragments (due, I believe, to variation in choices of line table
encoding depending on the size of instruction ranges in the actual
program code) we needed to support compressing multiple MCFragments in a
single pass.

This patch implements that behavior by mutating the post-relaxed and
relocated section to be the compressed form of its former self,
including renaming the section.

This is a more flexible (and less invasive, to a degree) implementation
that will allow for other features such as "use compression only if it's
smaller than the uncompressed data".

Compressing debug_frame would be a possible further extension to this
work, but I've left it for now. The hurdle there is alignment sections -
which might require going as far as to refactor
MCAssembler.cpp:writeFragment to handle writing to a byte buffer or an
MCObjectWriter (there's already a virtual call there, so it shouldn't
add substantial compile-time cost) which could in turn involve
refactoring MCAsmBackend::writeNopData to use that same abstraction...
which involves touching all the backends. This would remove the limited
handling of fragment writing seen in
ELFObjectWriter.cpp:getUncompressedData which would be nice - but it's
more invasive.

I did discover that I (perhaps obviously) don't need to handle
relocations when I rewrite the fragments - since the relocations have
already been applied and computed (and stored into
ELFObjectWriter::Relocations) by this stage (necessarily, because we
need to have written any immediate values or assembly-time relocations
into the data already before we compress it, which we have). The test
case doesn't necessarily cover that in detail - I can add more test
coverage if that's preferred.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@205990 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
David Blaikie
2014-04-10 21:53:53 +00:00
parent 22f3236dd6
commit fe25521451
5 changed files with 151 additions and 8 deletions

View File

@ -17,6 +17,7 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCAsmLayout.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCContext.h"
@ -27,6 +28,7 @@
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCSectionELF.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Support/Compression.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/ELF.h"
@ -253,6 +255,8 @@ class ELFObjectWriter : public MCObjectWriter {
void CreateRelocationSections(MCAssembler &Asm, MCAsmLayout &Layout,
RelMapTy &RelMap);
void CompressDebugSections(MCAssembler &Asm, MCAsmLayout &Layout);
void WriteRelocations(MCAssembler &Asm, MCAsmLayout &Layout,
const RelMapTy &RelMap);
@ -1168,6 +1172,114 @@ void ELFObjectWriter::CreateRelocationSections(MCAssembler &Asm,
}
}
static SmallVector<char, 128> getUncompressedData(MCAsmLayout &Layout, MCSectionData::FragmentListType &Fragments) {
SmallVector<char, 128> UncompressedData;
for (const MCFragment &F : Fragments) {
const SmallVectorImpl<char> *Contents;
switch (F.getKind()) {
case MCFragment::FT_Data:
Contents = &cast<MCDataFragment>(F).getContents();
break;
case MCFragment::FT_Dwarf:
Contents = &cast<MCDwarfLineAddrFragment>(F).getContents();
break;
case MCFragment::FT_DwarfFrame:
Contents = &cast<MCDwarfCallFrameFragment>(F).getContents();
break;
default:
llvm_unreachable(
"Not expecting any other fragment types in a debug_* section");
}
UncompressedData.append(Contents->begin(), Contents->end());
}
return UncompressedData;
}
// Include the debug info compression header:
// "ZLIB" followed by 8 bytes representing the uncompressed size of the section,
// useful for consumers to preallocate a buffer to decompress into.
static void prependCompressionHeader(uint64_t Size, SmallVectorImpl<char> &CompressedContents) {
static const StringRef Magic = "ZLIB";
if (sys::IsLittleEndianHost)
Size = sys::SwapByteOrder(Size);
CompressedContents.insert(CompressedContents.begin(),
Magic.size() + sizeof(Size), 0);
std::copy(Magic.begin(), Magic.end(), CompressedContents.begin());
std::copy(reinterpret_cast<char *>(&Size),
reinterpret_cast<char *>(&Size + 1),
CompressedContents.begin() + Magic.size());
}
// Return a single fragment containing the compressed contents of the whole
// section. Null if the section was not compressed for any reason.
static std::unique_ptr<MCDataFragment> getCompressedFragment(MCAsmLayout &Layout, MCSectionData::FragmentListType &Fragments) {
std::unique_ptr<MCDataFragment> CompressedFragment(new MCDataFragment());
// Gather the uncompressed data from all the fragments, recording the
// alignment fragment, if seen, and any fixups.
SmallVector<char, 128> UncompressedData =
getUncompressedData(Layout, Fragments);
SmallVectorImpl<char> &CompressedContents = CompressedFragment->getContents();
zlib::Status Success = zlib::compress(
StringRef(UncompressedData.data(), UncompressedData.size()),
CompressedContents);
if (Success != zlib::StatusOK)
return nullptr;
prependCompressionHeader(UncompressedData.size(), CompressedContents);
return CompressedFragment;
}
static void CompressDebugSection(MCAssembler &Asm, MCAsmLayout &Layout,
const MCSectionELF &Section,
MCSectionData &SD) {
StringRef SectionName = Section.getSectionName();
MCSectionData::FragmentListType &Fragments = SD.getFragmentList();
std::unique_ptr<MCDataFragment> CompressedFragment =
getCompressedFragment(Layout, Fragments);
// Leave the section as-is if the fragments could not be compressed.
if (!CompressedFragment)
return;
// Invalidate the layout for the whole section since it will have new and
// different fragments now.
Layout.invalidateFragmentsFrom(&Fragments.front());
Fragments.clear();
// Complete the initialization of the new fragment
CompressedFragment->setParent(&SD);
CompressedFragment->setLayoutOrder(0);
Fragments.push_back(CompressedFragment.release());
// Rename from .debug_* to .zdebug_*
Asm.getContext().renameELFSection(&Section,
(".z" + SectionName.drop_front(1)).str());
}
void ELFObjectWriter::CompressDebugSections(MCAssembler &Asm,
MCAsmLayout &Layout) {
if (!Asm.getContext().getAsmInfo()->compressDebugSections())
return;
for (MCSectionData &SD : Asm) {
const MCSectionELF &Section = static_cast<const MCSectionELF&>(SD.getSection());
StringRef SectionName = Section.getSectionName();
// Compressing debug_frame requires handling alignment fragments which is
// more work (possibly generalizing MCAssembler.cpp:writeFragment to allow
// for writing to arbitrary buffers) for little benefit.
if (!SectionName.startswith(".debug_") || SectionName == ".debug_frame")
continue;
CompressDebugSection(Asm, Layout, Section, SD);
}
}
void ELFObjectWriter::WriteRelocations(MCAssembler &Asm, MCAsmLayout &Layout,
const RelMapTy &RelMap) {
for (MCAssembler::const_iterator it = Asm.begin(),
@ -1652,6 +1764,8 @@ void ELFObjectWriter::WriteObject(MCAssembler &Asm,
unsigned NumUserSections = Asm.size();
CompressDebugSections(Asm, const_cast<MCAsmLayout&>(Layout));
DenseMap<const MCSectionELF*, const MCSectionELF*> RelMap;
CreateRelocationSections(Asm, const_cast<MCAsmLayout&>(Layout), RelMap);