diff options
author | Peter Collingbourne <peter@pcc.me.uk> | 2016-12-16 00:26:30 +0000 |
---|---|---|
committer | Peter Collingbourne <peter@pcc.me.uk> | 2016-12-16 00:26:30 +0000 |
commit | 1398a32e2857bab844a3b4c9f6d2cbf7770471b3 (patch) | |
tree | 390ac3a30dbe4dc7cdb4ccdb6551e37daacde4fe | |
parent | 1b48bac3300d440e6a2772f6feda40680d61729c (diff) | |
download | bcm5719-llvm-1398a32e2857bab844a3b4c9f6d2cbf7770471b3.tar.gz bcm5719-llvm-1398a32e2857bab844a3b4c9f6d2cbf7770471b3.zip |
IPO: Introduce ThinLTOBitcodeWriter pass.
This pass prepares a module containing type metadata for ThinLTO by splitting
it into regular and thin LTO parts if possible, and writing both parts to
a multi-module bitcode file. Modules that do not contain type metadata are
written unmodified as a single module.
All globals with type metadata are added to the regular LTO module, and
the rest are added to the thin LTO module.
Differential Revision: https://reviews.llvm.org/D27324
llvm-svn: 289899
-rw-r--r-- | llvm/include/llvm/InitializePasses.h | 1 | ||||
-rw-r--r-- | llvm/include/llvm/Transforms/IPO.h | 4 | ||||
-rw-r--r-- | llvm/lib/Transforms/IPO/CMakeLists.txt | 1 | ||||
-rw-r--r-- | llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp | 344 | ||||
-rw-r--r-- | llvm/test/Transforms/ThinLTOBitcodeWriter/no-type-md.ll | 13 | ||||
-rw-r--r-- | llvm/test/Transforms/ThinLTOBitcodeWriter/split-internal-typeid.ll | 40 | ||||
-rw-r--r-- | llvm/test/Transforms/ThinLTOBitcodeWriter/split-internal1.ll | 27 | ||||
-rw-r--r-- | llvm/test/Transforms/ThinLTOBitcodeWriter/split-internal2.ll | 32 | ||||
-rw-r--r-- | llvm/test/Transforms/ThinLTOBitcodeWriter/split.ll | 26 | ||||
-rw-r--r-- | llvm/test/Transforms/ThinLTOBitcodeWriter/unsplittable.ll | 21 | ||||
-rw-r--r-- | llvm/tools/opt/opt.cpp | 8 |
11 files changed, 516 insertions, 1 deletions
diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h index b00d26b8e87..5c90519dfb4 100644 --- a/llvm/include/llvm/InitializePasses.h +++ b/llvm/include/llvm/InitializePasses.h @@ -354,6 +354,7 @@ void initializeVirtRegRewriterPass(PassRegistry&); void initializeWholeProgramDevirtPass(PassRegistry &); void initializeWinEHPreparePass(PassRegistry&); void initializeWriteBitcodePassPass(PassRegistry &); +void initializeWriteThinLTOBitcodePass(PassRegistry &); void initializeXRayInstrumentationPass(PassRegistry &); } diff --git a/llvm/include/llvm/Transforms/IPO.h b/llvm/include/llvm/Transforms/IPO.h index 34cba185f9b..eef151af73f 100644 --- a/llvm/include/llvm/Transforms/IPO.h +++ b/llvm/include/llvm/Transforms/IPO.h @@ -28,6 +28,7 @@ class Pass; class Function; class BasicBlock; class GlobalValue; +class raw_ostream; //===----------------------------------------------------------------------===// // @@ -235,6 +236,9 @@ ModulePass *createGlobalSplitPass(); ModulePass *createSampleProfileLoaderPass(); ModulePass *createSampleProfileLoaderPass(StringRef Name); +/// Write ThinLTO-ready bitcode to Str. +ModulePass *createWriteThinLTOBitcodePass(raw_ostream &Str); + } // End llvm namespace #endif diff --git a/llvm/lib/Transforms/IPO/CMakeLists.txt b/llvm/lib/Transforms/IPO/CMakeLists.txt index fffa1c3356a..67f18a307b9 100644 --- a/llvm/lib/Transforms/IPO/CMakeLists.txt +++ b/llvm/lib/Transforms/IPO/CMakeLists.txt @@ -28,6 +28,7 @@ add_llvm_library(LLVMipo SampleProfile.cpp StripDeadPrototypes.cpp StripSymbols.cpp + ThinLTOBitcodeWriter.cpp WholeProgramDevirt.cpp ADDITIONAL_HEADER_DIRS diff --git a/llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp b/llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp new file mode 100644 index 00000000000..3680cfc813a --- /dev/null +++ b/llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp @@ -0,0 +1,344 @@ +//===- ThinLTOBitcodeWriter.cpp - Bitcode writing pass for ThinLTO --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This pass prepares a module containing type metadata for ThinLTO by splitting +// it into regular and thin LTO parts if possible, and writing both parts to +// a multi-module bitcode file. Modules that do not contain type metadata are +// written unmodified as a single module. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/IPO.h" +#include "llvm/Analysis/ModuleSummaryAnalysis.h" +#include "llvm/Analysis/TypeMetadataUtils.h" +#include "llvm/Bitcode/BitcodeWriter.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/PassManager.h" +#include "llvm/Pass.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Transforms/Utils/Cloning.h" +using namespace llvm; + +namespace { + +// Produce a unique identifier for this module by taking the MD5 sum of the +// names of the module's strong external symbols. This identifier is +// normally guaranteed to be unique, or the program would fail to link due to +// multiply defined symbols. +// +// If the module has no strong external symbols (such a module may still have a +// semantic effect if it performs global initialization), we cannot produce a +// unique identifier for this module, so we return the empty string, which +// causes the entire module to be written as a regular LTO module. +std::string getModuleId(Module *M) { + MD5 Md5; + bool ExportsSymbols = false; + auto AddGlobal = [&](GlobalValue &GV) { + if (GV.isDeclaration() || GV.getName().startswith("llvm.") || + !GV.hasExternalLinkage()) + return; + ExportsSymbols = true; + Md5.update(GV.getName()); + Md5.update(ArrayRef<uint8_t>{0}); + }; + + for (auto &F : *M) + AddGlobal(F); + for (auto &GV : M->globals()) + AddGlobal(GV); + for (auto &GA : M->aliases()) + AddGlobal(GA); + for (auto &IF : M->ifuncs()) + AddGlobal(IF); + + if (!ExportsSymbols) + return ""; + + MD5::MD5Result R; + Md5.final(R); + + SmallString<32> Str; + MD5::stringifyResult(R, Str); + return ("$" + Str).str(); +} + +// Promote each local-linkage entity defined by ExportM and used by ImportM by +// changing visibility and appending the given ModuleId. +void promoteInternals(Module &ExportM, Module &ImportM, StringRef ModuleId) { + auto PromoteInternal = [&](GlobalValue &ExportGV) { + if (!ExportGV.hasLocalLinkage()) + return; + + GlobalValue *ImportGV = ImportM.getNamedValue(ExportGV.getName()); + if (!ImportGV || ImportGV->use_empty()) + return; + + std::string NewName = (ExportGV.getName() + ModuleId).str(); + + ExportGV.setName(NewName); + ExportGV.setLinkage(GlobalValue::ExternalLinkage); + ExportGV.setVisibility(GlobalValue::HiddenVisibility); + + ImportGV->setName(NewName); + ImportGV->setVisibility(GlobalValue::HiddenVisibility); + }; + + for (auto &F : ExportM) + PromoteInternal(F); + for (auto &GV : ExportM.globals()) + PromoteInternal(GV); + for (auto &GA : ExportM.aliases()) + PromoteInternal(GA); + for (auto &IF : ExportM.ifuncs()) + PromoteInternal(IF); +} + +// Promote all internal (i.e. distinct) type ids used by the module by replacing +// them with external type ids formed using the module id. +// +// Note that this needs to be done before we clone the module because each clone +// will receive its own set of distinct metadata nodes. +void promoteTypeIds(Module &M, StringRef ModuleId) { + DenseMap<Metadata *, Metadata *> LocalToGlobal; + auto ExternalizeTypeId = [&](CallInst *CI, unsigned ArgNo) { + Metadata *MD = + cast<MetadataAsValue>(CI->getArgOperand(ArgNo))->getMetadata(); + + if (isa<MDNode>(MD) && cast<MDNode>(MD)->isDistinct()) { + Metadata *&GlobalMD = LocalToGlobal[MD]; + if (!GlobalMD) { + std::string NewName = + (to_string(LocalToGlobal.size()) + ModuleId).str(); + GlobalMD = MDString::get(M.getContext(), NewName); + } + + CI->setArgOperand(ArgNo, + MetadataAsValue::get(M.getContext(), GlobalMD)); + } + }; + + if (Function *TypeTestFunc = + M.getFunction(Intrinsic::getName(Intrinsic::type_test))) { + for (const Use &U : TypeTestFunc->uses()) { + auto CI = cast<CallInst>(U.getUser()); + ExternalizeTypeId(CI, 1); + } + } + + if (Function *TypeCheckedLoadFunc = + M.getFunction(Intrinsic::getName(Intrinsic::type_checked_load))) { + for (const Use &U : TypeCheckedLoadFunc->uses()) { + auto CI = cast<CallInst>(U.getUser()); + ExternalizeTypeId(CI, 2); + } + } + + for (GlobalObject &GO : M.global_objects()) { + SmallVector<MDNode *, 1> MDs; + GO.getMetadata(LLVMContext::MD_type, MDs); + + GO.eraseMetadata(LLVMContext::MD_type); + for (auto MD : MDs) { + auto I = LocalToGlobal.find(MD->getOperand(1)); + if (I == LocalToGlobal.end()) { + GO.addMetadata(LLVMContext::MD_type, *MD); + continue; + } + GO.addMetadata( + LLVMContext::MD_type, + *MDNode::get(M.getContext(), + ArrayRef<Metadata *>{MD->getOperand(0), I->second})); + } + } +} + +// Drop unused globals, and drop type information from function declarations. +// FIXME: If we made functions typeless then there would be no need to do this. +void simplifyExternals(Module &M) { + FunctionType *EmptyFT = + FunctionType::get(Type::getVoidTy(M.getContext()), false); + + for (auto I = M.begin(), E = M.end(); I != E;) { + Function &F = *I++; + if (F.isDeclaration() && F.use_empty()) { + F.eraseFromParent(); + continue; + } + + if (!F.isDeclaration() || F.getFunctionType() == EmptyFT) + continue; + + Function *NewF = + Function::Create(EmptyFT, GlobalValue::ExternalLinkage, "", &M); + NewF->setVisibility(F.getVisibility()); + NewF->takeName(&F); + F.replaceAllUsesWith(ConstantExpr::getBitCast(NewF, F.getType())); + F.eraseFromParent(); + } + + for (auto I = M.global_begin(), E = M.global_end(); I != E;) { + GlobalVariable &GV = *I++; + if (GV.isDeclaration() && GV.use_empty()) { + GV.eraseFromParent(); + continue; + } + } +} + +void filterModule( + Module *M, std::function<bool(const GlobalValue *)> ShouldKeepDefinition) { + for (Function &F : *M) { + if (ShouldKeepDefinition(&F)) + continue; + + F.deleteBody(); + F.clearMetadata(); + } + + for (GlobalVariable &GV : M->globals()) { + if (ShouldKeepDefinition(&GV)) + continue; + + GV.setInitializer(nullptr); + GV.setLinkage(GlobalValue::ExternalLinkage); + GV.clearMetadata(); + } + + for (Module::alias_iterator I = M->alias_begin(), E = M->alias_end(); + I != E;) { + GlobalAlias *GA = &*I++; + if (ShouldKeepDefinition(GA)) + continue; + + GlobalObject *GO; + if (I->getValueType()->isFunctionTy()) + GO = Function::Create(cast<FunctionType>(GA->getValueType()), + GlobalValue::ExternalLinkage, "", M); + else + GO = new GlobalVariable( + *M, GA->getValueType(), false, GlobalValue::ExternalLinkage, + (Constant *)nullptr, "", (GlobalVariable *)nullptr, + GA->getThreadLocalMode(), GA->getType()->getAddressSpace()); + GO->takeName(GA); + GA->replaceAllUsesWith(GO); + GA->eraseFromParent(); + } +} + +// If it's possible to split M into regular and thin LTO parts, do so and write +// a multi-module bitcode file with the two parts to OS. Otherwise, write only a +// regular LTO bitcode file to OS. +void splitAndWriteThinLTOBitcode(raw_ostream &OS, Module &M) { + std::string ModuleId = getModuleId(&M); + if (ModuleId.empty()) { + // We couldn't generate a module ID for this module, just write it out as a + // regular LTO module. + WriteBitcodeToFile(&M, OS); + return; + } + + promoteTypeIds(M, ModuleId); + + auto IsInMergedM = [&](const GlobalValue *GV) { + auto *GVar = dyn_cast<GlobalVariable>(GV->getBaseObject()); + if (!GVar) + return false; + + SmallVector<MDNode *, 1> MDs; + GVar->getMetadata(LLVMContext::MD_type, MDs); + return !MDs.empty(); + }; + + ValueToValueMapTy VMap; + std::unique_ptr<Module> MergedM(CloneModule(&M, VMap, IsInMergedM)); + + filterModule(&M, [&](const GlobalValue *GV) { return !IsInMergedM(GV); }); + + promoteInternals(*MergedM, M, ModuleId); + promoteInternals(M, *MergedM, ModuleId); + + simplifyExternals(*MergedM); + + SmallVector<char, 0> Buffer; + BitcodeWriter W(Buffer); + + // FIXME: Try to re-use BSI and PFI from the original module here. + ModuleSummaryIndex Index = buildModuleSummaryIndex(M, nullptr, nullptr); + W.writeModule(&M, /*ShouldPreserveUseListOrder=*/false, &Index, + /*GenerateHash=*/true); + + W.writeModule(MergedM.get()); + + OS << Buffer; +} + +// Returns whether this module needs to be split because it uses type metadata. +bool requiresSplit(Module &M) { + SmallVector<MDNode *, 1> MDs; + for (auto &GO : M.global_objects()) { + GO.getMetadata(LLVMContext::MD_type, MDs); + if (!MDs.empty()) + return true; + } + + return false; +} + +void writeThinLTOBitcode(raw_ostream &OS, Module &M, + const ModuleSummaryIndex *Index) { + // See if this module has any type metadata. If so, we need to split it. + if (requiresSplit(M)) + return splitAndWriteThinLTOBitcode(OS, M); + + // Otherwise we can just write it out as a regular module. + WriteBitcodeToFile(&M, OS, /*ShouldPreserveUseListOrder=*/false, Index, + /*GenerateHash=*/true); +} + +class WriteThinLTOBitcode : public ModulePass { + raw_ostream &OS; // raw_ostream to print on + +public: + static char ID; // Pass identification, replacement for typeid + WriteThinLTOBitcode() : ModulePass(ID), OS(dbgs()) { + initializeWriteThinLTOBitcodePass(*PassRegistry::getPassRegistry()); + } + + explicit WriteThinLTOBitcode(raw_ostream &o) + : ModulePass(ID), OS(o) { + initializeWriteThinLTOBitcodePass(*PassRegistry::getPassRegistry()); + } + + StringRef getPassName() const override { return "ThinLTO Bitcode Writer"; } + + bool runOnModule(Module &M) override { + const ModuleSummaryIndex *Index = + &(getAnalysis<ModuleSummaryIndexWrapperPass>().getIndex()); + writeThinLTOBitcode(OS, M, Index); + return true; + } + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesAll(); + AU.addRequired<ModuleSummaryIndexWrapperPass>(); + } +}; +} // anonymous namespace + +char WriteThinLTOBitcode::ID = 0; +INITIALIZE_PASS_BEGIN(WriteThinLTOBitcode, "write-thinlto-bitcode", + "Write ThinLTO Bitcode", false, true) +INITIALIZE_PASS_DEPENDENCY(ModuleSummaryIndexWrapperPass) +INITIALIZE_PASS_END(WriteThinLTOBitcode, "write-thinlto-bitcode", + "Write ThinLTO Bitcode", false, true) + +ModulePass *llvm::createWriteThinLTOBitcodePass(raw_ostream &Str) { + return new WriteThinLTOBitcode(Str); +} diff --git a/llvm/test/Transforms/ThinLTOBitcodeWriter/no-type-md.ll b/llvm/test/Transforms/ThinLTOBitcodeWriter/no-type-md.ll new file mode 100644 index 00000000000..f1ada67abe5 --- /dev/null +++ b/llvm/test/Transforms/ThinLTOBitcodeWriter/no-type-md.ll @@ -0,0 +1,13 @@ +; RUN: opt -thinlto-bc -o %t %s +; RUN: llvm-dis -o - %t | FileCheck %s +; RUN: llvm-bcanalyzer -dump %t | FileCheck --check-prefix=BCA %s + +; BCA: <GLOBALVAL_SUMMARY_BLOCK + +; CHECK: @g = global i8 42 +@g = global i8 42 + +; CHECK: define void @f() +define void @f() { + ret void +} diff --git a/llvm/test/Transforms/ThinLTOBitcodeWriter/split-internal-typeid.ll b/llvm/test/Transforms/ThinLTOBitcodeWriter/split-internal-typeid.ll new file mode 100644 index 00000000000..a43db9a3755 --- /dev/null +++ b/llvm/test/Transforms/ThinLTOBitcodeWriter/split-internal-typeid.ll @@ -0,0 +1,40 @@ +; RUN: opt -thinlto-bc -o %t %s +; RUN: llvm-modextract -b -n 0 -o %t0 %t +; RUN: llvm-modextract -b -n 1 -o %t1 %t +; RUN: not llvm-modextract -b -n 2 -o - %t 2>&1 | FileCheck --check-prefix=ERROR %s +; RUN: llvm-dis -o - %t0 | FileCheck --check-prefix=M0 %s +; RUN: llvm-dis -o - %t1 | FileCheck --check-prefix=M1 %s +; RUN: llvm-bcanalyzer -dump %t0 | FileCheck --check-prefix=BCA0 %s +; RUN: llvm-bcanalyzer -dump %t1 | FileCheck --check-prefix=BCA1 %s + +; ERROR: llvm-modextract: error: module index out of range; bitcode file contains 2 module(s) + +; BCA0: <GLOBALVAL_SUMMARY_BLOCK +; BCA1-NOT: <GLOBALVAL_SUMMARY_BLOCK + +; M0: @g = external global i8{{$}} +; M1: @g = global i8 42, !type !0, !type !1, !type !2 +@g = global i8 42, !type !1, !type !2, !type !4 + +; M0: define void @f() +; M1-NOT: @f() +define void @f() { + ; M0: llvm.type.test{{.*}}metadata !"1$f50b51a12bb012bebbeff978335e34cf" + %p = call i1 @llvm.type.test(i8* null, metadata !0) + ; M0: llvm.type.checked.load{{.*}}metadata !"2$f50b51a12bb012bebbeff978335e34cf" + %q = call {i8*, i1} @llvm.type.checked.load(i8* null, i32 0, metadata !3) + ret void +} + +declare i1 @llvm.type.test(i8*, metadata) +declare {i8*, i1} @llvm.type.checked.load(i8*, i32, metadata) + +!0 = distinct !{} +; M1: !0 = !{i32 0, !"1$f50b51a12bb012bebbeff978335e34cf"} +!1 = !{i32 0, !0} +; M1: !1 = !{i32 1, !"1$f50b51a12bb012bebbeff978335e34cf"} +!2 = !{i32 1, !0} + +!3 = distinct !{} +; M1: !2 = !{i32 0, !"2$f50b51a12bb012bebbeff978335e34cf"} +!4 = !{i32 0, !3} diff --git a/llvm/test/Transforms/ThinLTOBitcodeWriter/split-internal1.ll b/llvm/test/Transforms/ThinLTOBitcodeWriter/split-internal1.ll new file mode 100644 index 00000000000..6d18c4f6f65 --- /dev/null +++ b/llvm/test/Transforms/ThinLTOBitcodeWriter/split-internal1.ll @@ -0,0 +1,27 @@ +; RUN: opt -thinlto-bc -o %t %s +; RUN: llvm-modextract -b -n 0 -o %t0 %t +; RUN: llvm-modextract -b -n 1 -o %t1 %t +; RUN: not llvm-modextract -b -n 2 -o - %t 2>&1 | FileCheck --check-prefix=ERROR %s +; RUN: llvm-dis -o - %t0 | FileCheck --check-prefix=M0 %s +; RUN: llvm-dis -o - %t1 | FileCheck --check-prefix=M1 %s +; RUN: llvm-bcanalyzer -dump %t0 | FileCheck --check-prefix=BCA0 %s +; RUN: llvm-bcanalyzer -dump %t1 | FileCheck --check-prefix=BCA1 %s + +; ERROR: llvm-modextract: error: module index out of range; bitcode file contains 2 module(s) + +; BCA0: <GLOBALVAL_SUMMARY_BLOCK +; BCA1-NOT: <GLOBALVAL_SUMMARY_BLOCK + +; M0: @"g$581d7631532fa146ba4061179da39272" = external hidden global i8{{$}} +; M1: @"g$581d7631532fa146ba4061179da39272" = hidden global i8 42, !type !0 +@g = internal global i8 42, !type !0 + +; M0: define i8* @f() +; M1-NOT: @f() +define i8* @f() { + ; M0: ret i8* @"g$581d7631532fa146ba4061179da39272" + ret i8* @g +} + +; M1: !0 = !{i32 0, !"typeid"} +!0 = !{i32 0, !"typeid"} diff --git a/llvm/test/Transforms/ThinLTOBitcodeWriter/split-internal2.ll b/llvm/test/Transforms/ThinLTOBitcodeWriter/split-internal2.ll new file mode 100644 index 00000000000..fbe618f08e3 --- /dev/null +++ b/llvm/test/Transforms/ThinLTOBitcodeWriter/split-internal2.ll @@ -0,0 +1,32 @@ +; RUN: opt -thinlto-bc -o %t %s +; RUN: llvm-modextract -b -n 0 -o %t0 %t +; RUN: llvm-modextract -b -n 1 -o %t1 %t +; RUN: not llvm-modextract -b -n 2 -o - %t 2>&1 | FileCheck --check-prefix=ERROR %s +; RUN: llvm-dis -o - %t0 | FileCheck --check-prefix=M0 %s +; RUN: llvm-dis -o - %t1 | FileCheck --check-prefix=M1 %s +; RUN: llvm-bcanalyzer -dump %t0 | FileCheck --check-prefix=BCA0 %s +; RUN: llvm-bcanalyzer -dump %t1 | FileCheck --check-prefix=BCA1 %s + +; ERROR: llvm-modextract: error: module index out of range; bitcode file contains 2 module(s) + +; BCA0: <GLOBALVAL_SUMMARY_BLOCK +; BCA1-NOT: <GLOBALVAL_SUMMARY_BLOCK + +; M0: @g = external global void ()*{{$}} +; M1: @g = global void ()* @"f$13757e0fb71915e385efa4dc9d1e08fd", !type !0 +@g = global void ()* @f, !type !0 + +; M0: define hidden void @"f$13757e0fb71915e385efa4dc9d1e08fd"() +; M1: declare hidden void @"f$13757e0fb71915e385efa4dc9d1e08fd"() +define internal void @f() { + call void @f2() + ret void +} + +; M0: define internal void @f2() +define internal void @f2() { + ret void +} + +; M1: !0 = !{i32 0, !"typeid"} +!0 = !{i32 0, !"typeid"} diff --git a/llvm/test/Transforms/ThinLTOBitcodeWriter/split.ll b/llvm/test/Transforms/ThinLTOBitcodeWriter/split.ll new file mode 100644 index 00000000000..e08e92328b5 --- /dev/null +++ b/llvm/test/Transforms/ThinLTOBitcodeWriter/split.ll @@ -0,0 +1,26 @@ +; RUN: opt -thinlto-bc -o %t %s +; RUN: llvm-modextract -b -n 0 -o %t0 %t +; RUN: llvm-modextract -b -n 1 -o %t1 %t +; RUN: not llvm-modextract -b -n 2 -o - %t 2>&1 | FileCheck --check-prefix=ERROR %s +; RUN: llvm-dis -o - %t0 | FileCheck --check-prefix=M0 %s +; RUN: llvm-dis -o - %t1 | FileCheck --check-prefix=M1 %s +; RUN: llvm-bcanalyzer -dump %t0 | FileCheck --check-prefix=BCA0 %s +; RUN: llvm-bcanalyzer -dump %t1 | FileCheck --check-prefix=BCA1 %s + +; ERROR: llvm-modextract: error: module index out of range; bitcode file contains 2 module(s) + +; BCA0: <GLOBALVAL_SUMMARY_BLOCK +; BCA1-NOT: <GLOBALVAL_SUMMARY_BLOCK + +; M0: @g = external global i8{{$}} +; M1: @g = global i8 42, !type !0 +@g = global i8 42, !type !0 + +; M0: define i8* @f() +; M1-NOT: @f() +define i8* @f() { + ret i8* @g +} + +; M1: !0 = !{i32 0, !"typeid"} +!0 = !{i32 0, !"typeid"} diff --git a/llvm/test/Transforms/ThinLTOBitcodeWriter/unsplittable.ll b/llvm/test/Transforms/ThinLTOBitcodeWriter/unsplittable.ll new file mode 100644 index 00000000000..fbc97a00097 --- /dev/null +++ b/llvm/test/Transforms/ThinLTOBitcodeWriter/unsplittable.ll @@ -0,0 +1,21 @@ +; RUN: opt -thinlto-bc -o %t %s +; RUN: llvm-dis -o - %t | FileCheck %s +; RUN: llvm-bcanalyzer -dump %t | FileCheck --check-prefix=BCA %s + +; BCA-NOT: <GLOBALVAL_SUMMARY_BLOCK + +; CHECK: @llvm.global_ctors = appending global +@llvm.global_ctors = appending global [1 x { i32, void ()* }] [{ i32, void ()* } { i32 65535, void ()* @f }] + +; CHECK: @g = internal global i8 42, !type !0 +@g = internal global i8 42, !type !0 + +declare void @sink(i8*) + +; CHECK: define internal void @f() +define internal void @f() { + call void @sink(i8* @g) + ret void +} + +!0 = !{i32 0, !"typeid"} diff --git a/llvm/tools/opt/opt.cpp b/llvm/tools/opt/opt.cpp index 035629a8aa4..a93c06c1d13 100644 --- a/llvm/tools/opt/opt.cpp +++ b/llvm/tools/opt/opt.cpp @@ -99,6 +99,10 @@ static cl::opt<bool> OutputAssembly("S", cl::desc("Write output as LLVM assembly")); static cl::opt<bool> + OutputThinLTOBC("thinlto-bc", + cl::desc("Write output as ThinLTO-ready bitcode")); + +static cl::opt<bool> NoVerify("disable-verify", cl::desc("Do not run the verifier"), cl::Hidden); static cl::opt<bool> @@ -704,7 +708,9 @@ int main(int argc, char **argv) { if (EmitModuleHash) report_fatal_error("Text output is incompatible with -module-hash"); Passes.add(createPrintModulePass(*OS, "", PreserveAssemblyUseListOrder)); - } else + } else if (OutputThinLTOBC) + Passes.add(createWriteThinLTOBitcodePass(*OS)); + else Passes.add(createBitcodeWriterPass(*OS, PreserveBitcodeUseListOrder, EmitSummaryIndex, EmitModuleHash)); } |