mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-01-31 12:41:49 +01:00
[LTO] Support for embedding bitcode section during LTO
Summary: This adds support for embedding bitcode in a binary during LTO. The libLTO gains supports the `-lto-embed-bitcode` flag. The option allows users of the LTO library to embed a bitcode section. For example, LLD can pass the option via `ld.lld -mllvm=-lto-embed-bitcode`. This feature allows doing something comparable to `clang -c -fembed-bitcode`, but on the (LTO) linker level. Having bitcode alongside native code has many use-cases. To give an example, the MacOS linker can create a `-bitcode_bundle` section containing bitcode. Also, having this feature built into LLVM is an alternative to 3rd party tools such as [[ https://github.com/travitch/whole-program-llvm | wllvm ]] or [[ https://github.com/SRI-CSL/gllvm | gllvm ]]. As with these tools, this feature simplifies creating "whole-program" llvm bitcode files, but in contrast to wllvm/gllvm it does not rely on a specific llvm frontend/driver. Patch by Josef Eisl <josef.eisl@oracle.com> Reviewers: #llvm, #clang, rsmith, pcc, alexshap, tejohnson Reviewed By: tejohnson Subscribers: tejohnson, mehdi_amini, inglorion, hiraditya, aheejin, steven_wu, dexonsmith, dang, cfe-commits, llvm-commits, #llvm, #clang Tags: #clang, #llvm Differential Revision: https://reviews.llvm.org/D68213
This commit is contained in:
parent
39015f8b4f
commit
3a2a2bb628
@ -17,6 +17,7 @@
|
||||
#include "llvm/IR/ModuleSummaryIndex.h"
|
||||
#include "llvm/MC/StringTableBuilder.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@ -151,6 +152,11 @@ class raw_ostream;
|
||||
const std::map<std::string, GVSummaryMapTy>
|
||||
*ModuleToSummariesForIndex = nullptr);
|
||||
|
||||
/// Save a copy of the llvm IR as data in the __LLVM,__bitcode section.
|
||||
void EmbedBitcodeInModule(Module &M, MemoryBufferRef Buf, bool EmbedBitcode,
|
||||
bool EmbedMarker,
|
||||
const std::vector<uint8_t> *CmdArgs);
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_BITCODE_BITCODEWRITER_H
|
||||
|
@ -24,9 +24,10 @@
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/Bitcode/BitcodeReader.h"
|
||||
#include "llvm/Bitcode/LLVMBitCodes.h"
|
||||
#include "llvm/Bitstream/BitCodes.h"
|
||||
#include "llvm/Bitstream/BitstreamWriter.h"
|
||||
#include "llvm/Bitcode/LLVMBitCodes.h"
|
||||
#include "llvm/Config/llvm-config.h"
|
||||
#include "llvm/IR/Attributes.h"
|
||||
#include "llvm/IR/BasicBlock.h"
|
||||
@ -4677,3 +4678,125 @@ void llvm::WriteThinLinkBitcodeToFile(const Module &M, raw_ostream &Out,
|
||||
|
||||
Out.write((char *)&Buffer.front(), Buffer.size());
|
||||
}
|
||||
|
||||
static const char *getSectionNameForBitcode(const Triple &T) {
|
||||
switch (T.getObjectFormat()) {
|
||||
case Triple::MachO:
|
||||
return "__LLVM,__bitcode";
|
||||
case Triple::COFF:
|
||||
case Triple::ELF:
|
||||
case Triple::Wasm:
|
||||
case Triple::UnknownObjectFormat:
|
||||
return ".llvmbc";
|
||||
case Triple::XCOFF:
|
||||
llvm_unreachable("XCOFF is not yet implemented");
|
||||
break;
|
||||
}
|
||||
llvm_unreachable("Unimplemented ObjectFormatType");
|
||||
}
|
||||
|
||||
static const char *getSectionNameForCommandline(const Triple &T) {
|
||||
switch (T.getObjectFormat()) {
|
||||
case Triple::MachO:
|
||||
return "__LLVM,__cmdline";
|
||||
case Triple::COFF:
|
||||
case Triple::ELF:
|
||||
case Triple::Wasm:
|
||||
case Triple::UnknownObjectFormat:
|
||||
return ".llvmcmd";
|
||||
case Triple::XCOFF:
|
||||
llvm_unreachable("XCOFF is not yet implemented");
|
||||
break;
|
||||
}
|
||||
llvm_unreachable("Unimplemented ObjectFormatType");
|
||||
}
|
||||
|
||||
void llvm::EmbedBitcodeInModule(llvm::Module &M, llvm::MemoryBufferRef Buf,
|
||||
bool EmbedBitcode, bool EmbedMarker,
|
||||
const std::vector<uint8_t> *CmdArgs) {
|
||||
// Save llvm.compiler.used and remove it.
|
||||
SmallVector<Constant *, 2> UsedArray;
|
||||
SmallPtrSet<GlobalValue *, 4> UsedGlobals;
|
||||
Type *UsedElementType = Type::getInt8Ty(M.getContext())->getPointerTo(0);
|
||||
GlobalVariable *Used = collectUsedGlobalVariables(M, UsedGlobals, true);
|
||||
for (auto *GV : UsedGlobals) {
|
||||
if (GV->getName() != "llvm.embedded.module" &&
|
||||
GV->getName() != "llvm.cmdline")
|
||||
UsedArray.push_back(
|
||||
ConstantExpr::getPointerBitCastOrAddrSpaceCast(GV, UsedElementType));
|
||||
}
|
||||
if (Used)
|
||||
Used->eraseFromParent();
|
||||
|
||||
// Embed the bitcode for the llvm module.
|
||||
std::string Data;
|
||||
ArrayRef<uint8_t> ModuleData;
|
||||
Triple T(M.getTargetTriple());
|
||||
// Create a constant that contains the bitcode.
|
||||
// In case of embedding a marker, ignore the input Buf and use the empty
|
||||
// ArrayRef. It is also legal to create a bitcode marker even Buf is empty.
|
||||
if (EmbedBitcode) {
|
||||
if (!isBitcode((const unsigned char *)Buf.getBufferStart(),
|
||||
(const unsigned char *)Buf.getBufferEnd())) {
|
||||
// If the input is LLVM Assembly, bitcode is produced by serializing
|
||||
// the module. Use-lists order need to be preserved in this case.
|
||||
llvm::raw_string_ostream OS(Data);
|
||||
llvm::WriteBitcodeToFile(M, OS, /* ShouldPreserveUseListOrder */ true);
|
||||
ModuleData =
|
||||
ArrayRef<uint8_t>((const uint8_t *)OS.str().data(), OS.str().size());
|
||||
} else
|
||||
// If the input is LLVM bitcode, write the input byte stream directly.
|
||||
ModuleData = ArrayRef<uint8_t>((const uint8_t *)Buf.getBufferStart(),
|
||||
Buf.getBufferSize());
|
||||
}
|
||||
llvm::Constant *ModuleConstant =
|
||||
llvm::ConstantDataArray::get(M.getContext(), ModuleData);
|
||||
llvm::GlobalVariable *GV = new llvm::GlobalVariable(
|
||||
M, ModuleConstant->getType(), true, llvm::GlobalValue::PrivateLinkage,
|
||||
ModuleConstant);
|
||||
GV->setSection(getSectionNameForBitcode(T));
|
||||
UsedArray.push_back(
|
||||
ConstantExpr::getPointerBitCastOrAddrSpaceCast(GV, UsedElementType));
|
||||
if (llvm::GlobalVariable *Old =
|
||||
M.getGlobalVariable("llvm.embedded.module", true)) {
|
||||
assert(Old->hasOneUse() &&
|
||||
"llvm.embedded.module can only be used once in llvm.compiler.used");
|
||||
GV->takeName(Old);
|
||||
Old->eraseFromParent();
|
||||
} else {
|
||||
GV->setName("llvm.embedded.module");
|
||||
}
|
||||
|
||||
// Skip if only bitcode needs to be embedded.
|
||||
if (EmbedMarker) {
|
||||
// Embed command-line options.
|
||||
ArrayRef<uint8_t> CmdData(const_cast<uint8_t *>(CmdArgs->data()),
|
||||
CmdArgs->size());
|
||||
llvm::Constant *CmdConstant =
|
||||
llvm::ConstantDataArray::get(M.getContext(), CmdData);
|
||||
GV = new llvm::GlobalVariable(M, CmdConstant->getType(), true,
|
||||
llvm::GlobalValue::PrivateLinkage,
|
||||
CmdConstant);
|
||||
GV->setSection(getSectionNameForCommandline(T));
|
||||
UsedArray.push_back(
|
||||
ConstantExpr::getPointerBitCastOrAddrSpaceCast(GV, UsedElementType));
|
||||
if (llvm::GlobalVariable *Old = M.getGlobalVariable("llvm.cmdline", true)) {
|
||||
assert(Old->hasOneUse() &&
|
||||
"llvm.cmdline can only be used once in llvm.compiler.used");
|
||||
GV->takeName(Old);
|
||||
Old->eraseFromParent();
|
||||
} else {
|
||||
GV->setName("llvm.cmdline");
|
||||
}
|
||||
}
|
||||
|
||||
if (UsedArray.empty())
|
||||
return;
|
||||
|
||||
// Recreate llvm.compiler.used.
|
||||
ArrayType *ATy = ArrayType::get(UsedElementType, UsedArray.size());
|
||||
auto *NewUsed = new GlobalVariable(
|
||||
M, ATy, false, llvm::GlobalValue::AppendingLinkage,
|
||||
llvm::ConstantArray::get(ATy, UsedArray), "llvm.compiler.used");
|
||||
NewUsed->setSection("llvm.metadata");
|
||||
}
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/Program.h"
|
||||
#include "llvm/Support/SmallVectorMemoryBuffer.h"
|
||||
#include "llvm/Support/TargetRegistry.h"
|
||||
#include "llvm/Support/ThreadPool.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
@ -312,11 +313,30 @@ bool opt(Config &Conf, TargetMachine *TM, unsigned Task, Module &Mod,
|
||||
return !Conf.PostOptModuleHook || Conf.PostOptModuleHook(Task, Mod);
|
||||
}
|
||||
|
||||
static cl::opt<bool> EmbedBitcode(
|
||||
"lto-embed-bitcode", cl::init(false),
|
||||
cl::desc("Embed LLVM bitcode in object files produced by LTO"));
|
||||
|
||||
static void EmitBitcodeSection(Module &M, Config &Conf) {
|
||||
if (!EmbedBitcode)
|
||||
return;
|
||||
SmallVector<char, 0> Buffer;
|
||||
raw_svector_ostream OS(Buffer);
|
||||
WriteBitcodeToFile(M, OS);
|
||||
|
||||
std::unique_ptr<MemoryBuffer> Buf(
|
||||
new SmallVectorMemoryBuffer(std::move(Buffer)));
|
||||
llvm::EmbedBitcodeInModule(M, Buf->getMemBufferRef(), /*EmbedBitcode*/ true,
|
||||
/*EmbedMarker*/ false, /*CmdArgs*/ nullptr);
|
||||
}
|
||||
|
||||
void codegen(Config &Conf, TargetMachine *TM, AddStreamFn AddStream,
|
||||
unsigned Task, Module &Mod) {
|
||||
if (Conf.PreCodeGenModuleHook && !Conf.PreCodeGenModuleHook(Task, Mod))
|
||||
return;
|
||||
|
||||
EmitBitcodeSection(Mod, Conf);
|
||||
|
||||
std::unique_ptr<ToolOutputFile> DwoOut;
|
||||
SmallString<1024> DwoFile(Conf.SplitDwarfOutput);
|
||||
if (!Conf.DwoDir.empty()) {
|
||||
|
8
test/LTO/X86/Inputs/start-lib1.ll
Normal file
8
test/LTO/X86/Inputs/start-lib1.ll
Normal file
@ -0,0 +1,8 @@
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
|
||||
declare void @bar()
|
||||
|
||||
define void @foo() {
|
||||
ret void
|
||||
}
|
6
test/LTO/X86/Inputs/start-lib2.ll
Normal file
6
test/LTO/X86/Inputs/start-lib2.ll
Normal file
@ -0,0 +1,6 @@
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
|
||||
define void @bar() {
|
||||
ret void
|
||||
}
|
28
test/LTO/X86/embed-bitcode.ll
Normal file
28
test/LTO/X86/embed-bitcode.ll
Normal file
@ -0,0 +1,28 @@
|
||||
; RUN: llvm-as %s -o %t1.o
|
||||
; RUN: llvm-as %p/Inputs/start-lib1.ll -o %t2.o
|
||||
; RUN: llvm-as %p/Inputs/start-lib2.ll -o %t3.o
|
||||
|
||||
; RUN: llvm-lto2 run -r %t1.o,_start,px -r %t2.o,foo,px -r %t3.o,bar,px -r %t2.o,bar,lx -o %t3 %t1.o %t2.o %t3.o
|
||||
; RUN: llvm-readelf -S %t3.0 | FileCheck %s --implicit-check-not=.llvmbc
|
||||
|
||||
; RUN: llvm-lto2 run -r %t1.o,_start,px -r %t2.o,foo,px -r %t3.o,bar,px -r %t2.o,bar,lx -lto-embed-bitcode=false -o %t3 %t1.o %t2.o %t3.o
|
||||
; RUN: llvm-readelf -S %t3.0 | FileCheck %s --implicit-check-not=.llvmbc
|
||||
|
||||
; RUN: llvm-lto2 run -r %t1.o,_start,px -r %t2.o,foo,px -r %t3.o,bar,px -r %t2.o,bar,lx -lto-embed-bitcode -o %t3 %t1.o %t2.o %t3.o
|
||||
; RUN: llvm-readelf -S %t3.0 | FileCheck %s --check-prefix=CHECK-ELF
|
||||
; RUN: llvm-objcopy -O binary -j .llvmbc %t3.0 %t-embedded.bc
|
||||
; RUN: llvm-dis %t-embedded.bc -o - | FileCheck %s --check-prefix=CHECK-LL
|
||||
|
||||
; CHECK-ELF: .text
|
||||
; CHECK-ELF: .llvmbc
|
||||
|
||||
; CHECK-LL: @_start
|
||||
; CHECK-LL: @foo
|
||||
; CHECK-LL: @bar
|
||||
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
|
||||
define void @_start() {
|
||||
ret void
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user