diff --git a/llvm/include/llvm/Bitcode/EmbedBitcodePass.h b/llvm/include/llvm/Bitcode/EmbedBitcodePass.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/Bitcode/EmbedBitcodePass.h @@ -0,0 +1,42 @@ +//===-- EmbedBitcodePass.h - Embeds bitcode into global ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// This file provides apass which embeds the bitcode into a global variable. +/// +//===----------------------------------------------------------------------===// +// +#ifndef LLVM_BITCODE_EMBEDBITCODEPASS_H +#define LLVM_BITCODE_EMBEDBITCODEPASS_H + +#include "llvm/IR/PassManager.h" + +namespace llvm { +class Module; +class ModulePass; +class Pass; + +/// Pass embeds the current module into a global variable. +class EmbedBitcodePass : public PassInfoMixin { + bool IsThinLTO; + bool EmitLLVMUSeLists; + bool EmitLTOSummary; + +public: + EmbedBitcodePass(bool IsThinLTO, bool EmitLLVMUSeLists, bool EmitLTOSummary) + : IsThinLTO(IsThinLTO), EmitLLVMUSeLists(EmitLLVMUSeLists), + EmitLTOSummary(EmitLTOSummary){}; + + PreservedAnalyses run(Module &M, ModuleAnalysisManager &); + + static bool isRequired() { return true; } +}; + +} // end namespace llvm. + +#endif diff --git a/llvm/include/llvm/Passes/PassBuilder.h b/llvm/include/llvm/Passes/PassBuilder.h --- a/llvm/include/llvm/Passes/PassBuilder.h +++ b/llvm/include/llvm/Passes/PassBuilder.h @@ -239,6 +239,21 @@ ModulePassManager buildPerModuleDefaultPipeline(OptimizationLevel Level, bool LTOPreLink = false); + /// Build a fat object default optimization pipeline. + /// + /// This builds a pipeline that runs the LTO/ThinLTO pipeline, and emits a + /// section containing the bitcode along size the object code generated by the + /// PerModuleDefaultPipeline. + /// + /// Note that \p Level cannot be `O0` here. The pipelines produced are + /// only intended for use when attempting to optimize code. If frontends + /// require some transformations for semantic reasons, they should explicitly + /// build them. + ModulePassManager buildFatLTODefaultPipeline(OptimizationLevel Level, + bool ThinLTOPreLink, + bool EmitUseList, + bool EmitSummary); + /// Build a pre-link, ThinLTO-targeting default optimization pipeline to /// a pass manager. /// @@ -576,6 +591,10 @@ void addVectorPasses(OptimizationLevel Level, FunctionPassManager &FPM, bool IsFullLTO); + void addPerModuleDefaultPipelinePasses(ModulePassManager &MPM, + OptimizationLevel Level, + bool LTOPreLink = false); + static std::optional> parsePipelineText(StringRef Text); diff --git a/llvm/lib/Bitcode/Writer/CMakeLists.txt b/llvm/lib/Bitcode/Writer/CMakeLists.txt --- a/llvm/lib/Bitcode/Writer/CMakeLists.txt +++ b/llvm/lib/Bitcode/Writer/CMakeLists.txt @@ -2,6 +2,7 @@ BitWriter.cpp BitcodeWriter.cpp BitcodeWriterPass.cpp + EmbedBitcodePass.cpp ValueEnumerator.cpp DEPENDS diff --git a/llvm/lib/Bitcode/Writer/EmbedBitcodePass.cpp b/llvm/lib/Bitcode/Writer/EmbedBitcodePass.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Bitcode/Writer/EmbedBitcodePass.cpp @@ -0,0 +1,59 @@ +//===- EmbedBitcodePass.cpp - Pass that embeds the bitcode into a global---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// EmbedBitcodePass implementation. +// +//===----------------------------------------------------------------------===// +// +#include "llvm/Bitcode/EmbedBitcodePass.h" + +#include "llvm/Bitcode/BitcodeWriter.h" +#include "llvm/Bitcode/BitcodeWriterPass.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/PassManager.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/TargetParser/Triple.h" +#include "llvm/Transforms/IPO/ThinLTOBitcodeWriter.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" + +using namespace llvm; + +PreservedAnalyses EmbedBitcodePass::run(Module &M, ModuleAnalysisManager &AM) { + if (M.getGlobalVariable("llvm.embedded.module", true)) + report_fatal_error("Can only embed the module once."); + + Triple T(M.getTargetTriple()); + if (T.getObjectFormat() != Triple::ELF) + report_fatal_error( + "Embed bitcode pass currently only supports ELF object format."); + + std::string Data; + raw_string_ostream OS(Data); + + if (IsThinLTO) + ThinLTOBitcodeWriterPass(OS, nullptr).run(M, AM); + else + BitcodeWriterPass(OS, EmitLLVMUSeLists, EmitLTOSummary).run(M, AM); + + ArrayRef ModuleData((const uint8_t *)OS.str().data(), + OS.str().size()); + Constant *ModuleConstant = ConstantDataArray::get(M.getContext(), ModuleData); + + llvm::GlobalVariable *EM = new llvm::GlobalVariable( + M, ModuleConstant->getType(), true, llvm::GlobalValue::PrivateLinkage, + ModuleConstant); + + EM->setSection(".llvm.lto"); + EM->setAlignment(Align(1)); + EM->setName("llvm.embedded.module"); + appendToCompilerUsed(M, {EM}); + + return PreservedAnalyses::all(); +} diff --git a/llvm/lib/Object/ObjectFile.cpp b/llvm/lib/Object/ObjectFile.cpp --- a/llvm/lib/Object/ObjectFile.cpp +++ b/llvm/lib/Object/ObjectFile.cpp @@ -79,7 +79,7 @@ bool ObjectFile::isSectionBitcode(DataRefImpl Sec) const { Expected NameOrErr = getSectionName(Sec); if (NameOrErr) - return *NameOrErr == ".llvmbc"; + return *NameOrErr == ".llvmbc" || *NameOrErr == ".llvm.lto"; consumeError(NameOrErr.takeError()); return false; } diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -73,6 +73,7 @@ #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/Analysis/TypeBasedAliasAnalysis.h" #include "llvm/Analysis/UniformityAnalysis.h" +#include "llvm/Bitcode/EmbedBitcodePass.h" #include "llvm/CodeGen/HardwareLoops.h" #include "llvm/CodeGen/TypePromotion.h" #include "llvm/IR/DebugInfo.h" diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp --- a/llvm/lib/Passes/PassBuilderPipelines.cpp +++ b/llvm/lib/Passes/PassBuilderPipelines.cpp @@ -24,6 +24,7 @@ #include "llvm/Analysis/ProfileSummaryInfo.h" #include "llvm/Analysis/ScopedNoAliasAA.h" #include "llvm/Analysis/TypeBasedAliasAnalysis.h" +#include "llvm/Bitcode/EmbedBitcodePass.h" #include "llvm/IR/PassManager.h" #include "llvm/Passes/OptimizationLevel.h" #include "llvm/Passes/PassBuilder.h" @@ -1403,15 +1404,22 @@ ModulePassManager PassBuilder::buildPerModuleDefaultPipeline(OptimizationLevel Level, bool LTOPreLink) { - if (Level == OptimizationLevel::O0) - return buildO0DefaultPipeline(Level, LTOPreLink); - ModulePassManager MPM; + addPerModuleDefaultPipelinePasses(MPM, Level, LTOPreLink); + return MPM; +} + +void PassBuilder::addPerModuleDefaultPipelinePasses(ModulePassManager &MPM, + OptimizationLevel Level, + bool LTOPreLink) { + assert(Level != OptimizationLevel::O0 && + "Must request optimizations for the default pipeline!"); // Convert @llvm.global.annotations to !annotation metadata. MPM.addPass(Annotation2MetadataPass()); - // Force any function attributes we want the rest of the pipeline to observe. + // Force any function attributes we want the rest of the pipeline to + // observe. MPM.addPass(ForceFunctionAttrsPass()); // Apply module pipeline start EP callback. @@ -1439,7 +1447,19 @@ if (LTOPreLink) addRequiredLTOPreLinkPasses(MPM); +} +ModulePassManager +PassBuilder::buildFatLTODefaultPipeline(OptimizationLevel Level, + bool ThinLTOPreLink, bool EmitUseList, + bool EmitSummary) { + assert(Level != OptimizationLevel::O0 && + "Must request optimizations for the default pipeline!"); + ModulePassManager MPM = ThinLTOPreLink + ? buildThinLTOPreLinkDefaultPipeline(Level) + : buildLTOPreLinkDefaultPipeline(Level); + MPM.addPass(EmbedBitcodePass(ThinLTOPreLink, EmitUseList, EmitSummary)); + addPerModuleDefaultPipelinePasses(MPM, Level, /*LTOPreLink=*/false); return MPM; } diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -58,6 +58,7 @@ MODULE_PASS("dot-callgraph", CallGraphDOTPrinterPass()) MODULE_PASS("elim-avail-extern", EliminateAvailableExternallyPass()) MODULE_PASS("extract-blocks", BlockExtractorPass({}, false)) +MODULE_PASS("embed-bitcode", EmbedBitcodePass(true, true, true)) MODULE_PASS("forceattrs", ForceFunctionAttrsPass()) MODULE_PASS("function-import", FunctionImportPass()) MODULE_PASS("globaldce", GlobalDCEPass()) diff --git a/llvm/test/Bitcode/embed-multiple.ll b/llvm/test/Bitcode/embed-multiple.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Bitcode/embed-multiple.ll @@ -0,0 +1,6 @@ +; RUN: not --crash opt --mtriple x86_64-unknown-linux-gnu < %s -passes=embed-bitcode -S 2>&1 | FileCheck %s + +@a = global i32 1 +@llvm.embedded.module = private constant [4 x i8] c"BC\C0\DE" + +; CHECK: LLVM ERROR: Can only embed the module once. diff --git a/llvm/test/Bitcode/embed-unsupported-object-format.ll b/llvm/test/Bitcode/embed-unsupported-object-format.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Bitcode/embed-unsupported-object-format.ll @@ -0,0 +1,5 @@ +; RUN: not --crash opt --mtriple powerpc64-unknown-aix < %s -passes=embed-bitcode -S 2>&1 | FileCheck %s + +@a = global i32 1 + +; CHECK: LLVM ERROR: Embed bitcode pass currently only supports ELF object format diff --git a/llvm/test/Bitcode/embed.ll b/llvm/test/Bitcode/embed.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Bitcode/embed.ll @@ -0,0 +1,7 @@ +; RUN: opt --mtriple x86_64-unknown-linux-gnu < %s -passes=embed-bitcode -S | FileCheck %s + +@a = global i32 1 + +; CHECK: @a = global i32 1 +; CHECK: @llvm.embedded.module = private constant {{.*}}, section ".llvm.lto", align 1 +; CHECK: @llvm.compiler.used = appending global [1 x ptr] [ptr @llvm.embedded.module], section "llvm.metadata"