diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index f018130807519..fcf3dc25d95fc 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -525,6 +525,9 @@ static bool initTargetOptions(DiagnosticsEngine &Diags, Options.MCOptions.PPCUseFullRegisterNames = CodeGenOpts.PPCUseFullRegisterNames; Options.MisExpect = CodeGenOpts.MisExpect; + Options.MCOptions.PgoInstrumentation = CodeGenOpts.getProfileInstr() > 0; + Options.MCOptions.PgoUse = + CodeGenOpts.getProfileUse() > 0 || !CodeGenOpts.SampleProfileFile.empty(); return true; } diff --git a/clang/test/CodeGen/debug-dir-win-pe-magic-sections.c b/clang/test/CodeGen/debug-dir-win-pe-magic-sections.c new file mode 100644 index 0000000000000..ec0ce21e93fdd --- /dev/null +++ b/clang/test/CodeGen/debug-dir-win-pe-magic-sections.c @@ -0,0 +1,23 @@ +// This test checks if COFF file compiled with +// -fprofile-generate has magic section ".pgi" to indicate so. + +// REQUIRES: x86-registered-target + +// RUN: %clang --target=x86_64-pc-windows -fprofile-generate %s -c -o %t_x86 +// RUN: llvm-objdump -h %t_x86 | FileCheck --check-prefix=CHECK_PGI %s + +// CHECK_PGI: {{.*}}.pgi{{.*}} + +// This test checks if COFF file contains a magic ".pgu" section to indicate that +// it was compiled using profiling data. + +// RUN: llvm-profdata merge -output=%code.profdata %S/Inputs/thinlto_expect1.proftext +// RUN: %clang --target=x86_64-pc-windows -fprofile-use=%code.profdata -c %s -o %t.obj +// RUN: llvm-objdump -h %t.obj | FileCheck --check-prefix=CHECK_PGU %s + +// CHECK_PGU: {{.*}}.pgu{{.*}} + +int main(void) { + + return 0; +} diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index 71ee5ce468555..49937d3e40ec7 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -77,6 +77,12 @@ static unsigned char dosProgram[] = { static_assert(sizeof(dosProgram) % 8 == 0, "DOSProgram size must be multiple of 8"); +static char ltcg[] = "LTCG"; +static char pgi[] = "PGI"; +static char pgu[] = "PGU"; +static char pgiSectionName[] = ".pgi"; +static char pguSectionName[] = ".pgu"; + static const int dosStubSize = sizeof(dos_header) + sizeof(dosProgram); static_assert(dosStubSize % 8 == 0, "DOSStub size must be multiple of 8"); @@ -179,6 +185,23 @@ class ExtendedDllCharacteristicsChunk : public NonSectionChunk { uint32_t characteristics = 0; }; +class DebugDirStringChunk : public NonSectionChunk { +public: + DebugDirStringChunk(std::string str) : str(str.begin(), str.end()) { + while (this->str.size() % 4 != 0) + this->str.push_back(0); + } + size_t getSize() const override { return str.size(); } + + void writeTo(uint8_t *b) const override { + char *p = reinterpret_cast(b); + auto strReverse = str; + std::reverse(strReverse.begin(), strReverse.end()); + memcpy(p, strReverse.data(), strReverse.size()); + } + std::vector str; +}; + // PartialSection represents a group of chunks that contribute to an // OutputSection. Collating a collection of PartialSections of same name and // characteristics constitutes the OutputSection. @@ -1165,6 +1188,22 @@ void Writer::createMiscChunks() { llvm::TimeTraceScope timeScope("Misc chunks"); Configuration *config = &ctx.config; + auto searchForPgoMagicSection = [this](char sectionName[]) { + for (auto *obj : ctx.objFileInstances) { + for (auto &chunk : obj->getChunks()) { + if (chunk->kind() == Chunk::SectionKind && + chunk->getSectionName() == sectionName) { + return true; + } + } + } + return false; + }; + + bool writePgi = searchForPgoMagicSection(pgiSectionName); + bool writePgu = !writePgi && searchForPgoMagicSection(pguSectionName); + bool writeLTO = ctx.bitcodeFileInstances.size(); + for (MergeChunk *p : ctx.mergeChunkInstances) { if (p) { p->finalizeContents(); @@ -1181,7 +1220,7 @@ void Writer::createMiscChunks() { // Create Debug Information Chunks debugInfoSec = config->mingw ? buildidSec : rdataSec; if (config->buildIDHash != BuildIDHash::None || config->debug || - config->repro || config->cetCompat) { + config->repro || config->cetCompat || writePgi || writePgu || writeLTO) { debugDirectory = make(ctx, debugRecords, config->repro); debugDirectory->setAlignment(4); @@ -1206,6 +1245,19 @@ void Writer::createMiscChunks() { IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT)); } + if (writeLTO) { + debugRecords.emplace_back(COFF::IMAGE_DEBUG_TYPE_POGO, + make(ltcg)); + } + + if (writePgi) { + debugRecords.emplace_back(COFF::IMAGE_DEBUG_TYPE_POGO, + make(pgi)); + } else if (writePgu) { + debugRecords.emplace_back(COFF::IMAGE_DEBUG_TYPE_POGO, + make(pgu)); + } + // Align and add each chunk referenced by the debug data directory. for (std::pair r : debugRecords) { r.second->setAlignment(4); diff --git a/lld/test/COFF/debug_dir_magic_strings_from_section_pgi.s b/lld/test/COFF/debug_dir_magic_strings_from_section_pgi.s new file mode 100644 index 0000000000000..cf3e34e2b9a1a --- /dev/null +++ b/lld/test/COFF/debug_dir_magic_strings_from_section_pgi.s @@ -0,0 +1,15 @@ +// This test checks if lld puts magic string "PGI" when an object files contains +// .pgi section. + +// REQUIRES: x86 + +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-windows %s -o %t.main_x86.obj + +// RUN: lld-link -out:%t_x86.exe %t.main_x86.obj -entry:entry -subsystem:console -debug:symtab +// RUN: llvm-readobj --coff-debug-directory %t_x86.exe | FileCheck --check-prefix=CHECK_PGI %s +// CHECK_PGI: {{.*}}IGP{{.*}} + +#--- main.s +.section .pgi +.global entry +entry: diff --git a/lld/test/COFF/debug_dir_magic_strings_from_section_pgu.s b/lld/test/COFF/debug_dir_magic_strings_from_section_pgu.s new file mode 100644 index 0000000000000..d9f4139e23ad5 --- /dev/null +++ b/lld/test/COFF/debug_dir_magic_strings_from_section_pgu.s @@ -0,0 +1,15 @@ +// This test checks if lld puts magic string "PGU" when an object files contains +// .pgu section. + +// REQUIRES: x86 + +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-windows %s -o %t.main_x86.obj + +// RUN: lld-link -out:%t_x86.exe %t.main_x86.obj -entry:entry -subsystem:console -debug:symtab +// RUN: llvm-readobj --coff-debug-directory %t_x86.exe | FileCheck --check-prefix=CHECK_PGU %s +// CHECK_PGU: {{.*}}UGP{{.*}} + +#--- main.s +.section .pgu +.global entry +entry: diff --git a/lld/test/COFF/debug_dir_magic_strings_lto.ll b/lld/test/COFF/debug_dir_magic_strings_lto.ll new file mode 100644 index 0000000000000..560a2d1502e9a --- /dev/null +++ b/lld/test/COFF/debug_dir_magic_strings_lto.ll @@ -0,0 +1,12 @@ +; REQUIRES: x86 +; RUN: llvm-as -o %main.obj %s +; RUN: lld-link /out:%main.exe /entry:main /subsystem:console %main.obj +; RUN: llvm-readobj --coff-debug-directory %main.exe +; CHECK: {{.*}}GCTL{{.*}} + +target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows" + +define i32 @main() { + ret i32 0 +} diff --git a/llvm/include/llvm/MC/MCTargetOptions.h b/llvm/include/llvm/MC/MCTargetOptions.h index 7b0d81faf73d2..8437bb8e3c61a 100644 --- a/llvm/include/llvm/MC/MCTargetOptions.h +++ b/llvm/include/llvm/MC/MCTargetOptions.h @@ -112,6 +112,8 @@ class MCTargetOptions { // Whether or not to use full register names on PowerPC. bool PPCUseFullRegisterNames : 1; + bool PgoInstrumentation = false; + bool PgoUse = false; MCTargetOptions(); /// getABIName - If this returns a non-empty string this represents the diff --git a/llvm/lib/MC/WinCOFFObjectWriter.cpp b/llvm/lib/MC/WinCOFFObjectWriter.cpp index 62f53423126ea..6e6685008d16e 100644 --- a/llvm/lib/MC/WinCOFFObjectWriter.cpp +++ b/llvm/lib/MC/WinCOFFObjectWriter.cpp @@ -28,6 +28,7 @@ #include "llvm/MC/MCSectionCOFF.h" #include "llvm/MC/MCSymbol.h" #include "llvm/MC/MCSymbolCOFF.h" +#include "llvm/MC/MCTargetOptions.h" #include "llvm/MC/MCValue.h" #include "llvm/MC/MCWinCOFFObjectWriter.h" #include "llvm/MC/StringTableBuilder.h" @@ -981,6 +982,18 @@ static std::time_t getTime() { uint64_t WinCOFFWriter::writeObject(MCAssembler &Asm) { uint64_t StartOffset = W.OS.tell(); + const auto *Options = Asm.getContext().getTargetOptions(); + + if (Mode != DwoOnly && Options && Options->PgoInstrumentation) { + auto *Section = Asm.getContext().getCOFFSection(".pgi", 0); + defineSection(Asm, *Section); + } + + if (Mode != DwoOnly && Options && Options->PgoUse) { + auto *Section = Asm.getContext().getCOFFSection(".pgu", 0); + defineSection(Asm, *Section); + } + if (Sections.size() > INT32_MAX) report_fatal_error( "PE COFF object files can't have more than 2147483647 sections");