From b0f7fddd3505d5c389d4a637a6e33b7594db6842 Mon Sep 17 00:00:00 2001 From: Maksim Panchenko Date: Thu, 15 Nov 2018 16:02:16 -0800 Subject: [PATCH] [BOLT] Add method for better function size estimation Summary: Add BinaryContext::calculateEmittedSize() that ephemerally emits code to allow precise estimation of the function size. Relaxation and macro-op alignment adjustments are taken into account. (cherry picked from FBD13092139) --- bolt/src/BinaryContext.cpp | 80 +++++++++++++++++++++++++++++++++++++ bolt/src/BinaryContext.h | 7 ++++ bolt/src/BinaryFunction.cpp | 13 ++++-- bolt/src/BinaryFunction.h | 3 +- 4 files changed, 98 insertions(+), 5 deletions(-) diff --git a/bolt/src/BinaryContext.cpp b/bolt/src/BinaryContext.cpp index 4359f074abde..1b04d90e22b2 100644 --- a/bolt/src/BinaryContext.cpp +++ b/bolt/src/BinaryContext.cpp @@ -15,8 +15,13 @@ #include "llvm/ADT/Twine.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" #include "llvm/DebugInfo/DWARF/DWARFUnit.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCAsmLayout.h" #include "llvm/MC/MCContext.h" +#include "llvm/MC/MCELFStreamer.h" +#include "llvm/MC/MCObjectStreamer.h" #include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSymbol.h" #include "llvm/Support/CommandLine.h" @@ -1149,3 +1154,78 @@ BinaryContext::createInjectedBinaryFunction(const std::string &Name, setSymbolToFunctionMap(BF->getSymbol(), BF); return BF; } + +std::pair +BinaryContext::calculateEmittedSize(BinaryFunction &BF) { + // Adjust branch instruction to match the current layout. + BF.fixBranches(); + + // Create local MC context to isolate the effect of ephemeral code emission. + std::unique_ptr LocalMOFI = + llvm::make_unique(); + std::unique_ptr LocalCtx = + llvm::make_unique(AsmInfo.get(), MRI.get(), LocalMOFI.get()); + LocalMOFI->InitMCObjectFileInfo(*TheTriple, /*PIC=*/false, *LocalCtx); + auto *MAB = TheTarget->createMCAsmBackend(*STI, *MRI, MCTargetOptions()); + auto *MCE = TheTarget->createMCCodeEmitter(*MII, *MRI, *LocalCtx); + SmallString<256> Code; + raw_svector_ostream VecOS(Code); + + std::unique_ptr Streamer(TheTarget->createMCObjectStreamer( + *TheTriple, *LocalCtx, std::unique_ptr(MAB), VecOS, + std::unique_ptr(MCE), *STI, + /* RelaxAll */ false, + /* IncrementalLinkerCompatible */ false, + /* DWARFMustBeAtTheEnd */ false)); + + Streamer->InitSections(false); + + auto *Section = LocalMOFI->getTextSection(); + Section->setHasInstructions(true); + + auto *StartLabel = LocalCtx->getOrCreateSymbol("__hstart"); + auto *EndLabel = LocalCtx->getOrCreateSymbol("__hend"); + auto *ColdStartLabel = LocalCtx->getOrCreateSymbol("__cstart"); + auto *ColdEndLabel = LocalCtx->getOrCreateSymbol("__cend"); + + Streamer->SwitchSection(Section); + Streamer->EmitLabel(StartLabel); + BF.emitBody(*Streamer, /*EmitColdPart = */false, /*EmitCodeOnly = */true); + Streamer->EmitLabel(EndLabel); + + if (BF.isSplit()) { + auto *ColdSection = + LocalCtx->getELFSection(BF.getColdCodeSectionName(), + ELF::SHT_PROGBITS, + ELF::SHF_EXECINSTR | ELF::SHF_ALLOC); + ColdSection->setHasInstructions(true); + + Streamer->SwitchSection(ColdSection); + Streamer->EmitLabel(ColdStartLabel); + BF.emitBody(*Streamer, /*EmitColdPart = */true, /*EmitCodeOnly = */true); + Streamer->EmitLabel(ColdEndLabel); + } + + // To avoid calling MCObjectStreamer::flushPendingLabels() which is private. + Streamer->EmitBytes(StringRef("")); + + auto &Assembler = + static_cast(Streamer.get())->getAssembler(); + MCAsmLayout Layout(Assembler); + Assembler.layout(Layout); + + const auto HotSize = Layout.getSymbolOffset(*EndLabel) - + Layout.getSymbolOffset(*StartLabel); + const auto ColdSize = BF.isSplit() ? Layout.getSymbolOffset(*ColdEndLabel) - + Layout.getSymbolOffset(*ColdStartLabel) + : 0ULL; + + // Clean-up the effect of the code emission. + for (const auto &Symbol : Assembler.symbols()) { + auto *MutableSymbol = const_cast(&Symbol); + MutableSymbol->setUndefined(); + MutableSymbol->setIsRegistered(false); + } + + return std::make_pair(HotSize, ColdSize); +} diff --git a/bolt/src/BinaryContext.h b/bolt/src/BinaryContext.h index 7ce5a4a39858..fe7517da8bb6 100644 --- a/bolt/src/BinaryContext.h +++ b/bolt/src/BinaryContext.h @@ -669,6 +669,13 @@ public: static std::vector getSortedFunctions(std::map &BinaryFunctions); + /// Do the best effort to calculate the size of the function by emitting + /// its code, and relaxing branch instructions. + /// + /// Return the pair where the first size is for the main part, and the second + /// size is for the cold one. + std::pair calculateEmittedSize(BinaryFunction &BF); + /// Compute the native code size for a range of instructions. /// Note: this can be imprecise wrt the final binary since happening prior to /// relaxation, as well as wrt the original binary because of opcode diff --git a/bolt/src/BinaryFunction.cpp b/bolt/src/BinaryFunction.cpp index cb4bca682544..33c623056e7c 100644 --- a/bolt/src/BinaryFunction.cpp +++ b/bolt/src/BinaryFunction.cpp @@ -2639,8 +2639,9 @@ uint64_t BinaryFunction::getEditDistance() const { BasicBlocksLayout); } -void BinaryFunction::emitBody(MCStreamer &Streamer, bool EmitColdPart) { - if (EmitColdPart && hasConstantIsland()) +void BinaryFunction::emitBody(MCStreamer &Streamer, bool EmitColdPart, + bool EmitCodeOnly) { + if (!EmitCodeOnly && EmitColdPart && hasConstantIsland()) duplicateConstantIslands(); for (auto BB : layout()) { @@ -2672,6 +2673,9 @@ void BinaryFunction::emitBody(MCStreamer &Streamer, bool EmitColdPart) { for (auto I = BB->begin(), E = BB->end(); I != E; ++I) { auto &Instr = *I; + if (EmitCodeOnly && BC.MII->get(Instr.getOpcode()).isPseudo()) + continue; + // Handle pseudo instructions. if (BC.MIB->isEHLabel(Instr)) { const auto *Label = BC.MIB->getTargetSymbol(Instr); @@ -2695,7 +2699,7 @@ void BinaryFunction::emitBody(MCStreamer &Streamer, bool EmitColdPart) { Streamer.EmitNeverAlignCodeAtEnd(/*Alignment to avoid=*/64); } - if (opts::UpdateDebugSections && UnitLineTable.first) { + if (!EmitCodeOnly && opts::UpdateDebugSections && UnitLineTable.first) { LastLocSeen = emitLineInfo(Instr.getLoc(), LastLocSeen); } @@ -2704,7 +2708,8 @@ void BinaryFunction::emitBody(MCStreamer &Streamer, bool EmitColdPart) { } } - emitConstantIslands(Streamer, EmitColdPart); + if (!EmitCodeOnly) + emitConstantIslands(Streamer, EmitColdPart); } void BinaryFunction::emitBodyRaw(MCStreamer *Streamer) { diff --git a/bolt/src/BinaryFunction.h b/bolt/src/BinaryFunction.h index f52bf00d3d29..a2b23c1af7f3 100644 --- a/bolt/src/BinaryFunction.h +++ b/bolt/src/BinaryFunction.h @@ -2074,7 +2074,8 @@ public: /// Emit function code. The caller is responsible for emitting function /// symbol(s) and setting the section to emit the code to. - void emitBody(MCStreamer &Streamer, bool EmitColdPart); + void emitBody(MCStreamer &Streamer, bool EmitColdPart, + bool EmitCodeOnly = false); /// Emit function as a blob with relocations and labels for relocations. void emitBodyRaw(MCStreamer *Streamer);