summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Collingbourne <peter@pcc.me.uk>2016-12-14 01:17:59 +0000
committerPeter Collingbourne <peter@pcc.me.uk>2016-12-14 01:17:59 +0000
commit08850161ecdeeb7bb1308e8b27ee112152e56727 (patch)
treeddf95a8bfacc69a7530f32e44c7105887d7c7536
parentd5f6cc37da95b9a327bf7952993ff4b6e20eaf79 (diff)
LTO: Add support for multi-module bitcode files.
Differential Revision: https://reviews.llvm.org/D27313 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@289621 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/llvm/Bitcode/BitcodeReader.h2
-rw-r--r--include/llvm/LTO/LTO.h35
-rw-r--r--include/llvm/LTO/LTOBackend.h3
-rw-r--r--lib/LTO/LTO.cpp170
-rw-r--r--lib/LTO/LTOBackend.cpp8
-rw-r--r--test/LTO/Resolution/X86/empty-bitcode.test3
-rw-r--r--test/LTO/Resolution/X86/mixed_lto.ll6
-rw-r--r--test/LTO/Resolution/X86/multi-thinlto.ll6
8 files changed, 162 insertions, 71 deletions
diff --git a/include/llvm/Bitcode/BitcodeReader.h b/include/llvm/Bitcode/BitcodeReader.h
index 4e60101727d..ab2f25186d7 100644
--- a/include/llvm/Bitcode/BitcodeReader.h
+++ b/include/llvm/Bitcode/BitcodeReader.h
@@ -70,6 +70,8 @@ namespace llvm {
return StringRef((const char *)Buffer.begin(), Buffer.size());
}
+ StringRef getModuleIdentifier() const { return ModuleIdentifier; }
+
/// Read the bitcode module and prepare for lazy deserialization of function
/// bodies. If ShouldLazyLoadMetadata is true, lazily load metadata as well.
Expected<std::unique_ptr<Module>>
diff --git a/include/llvm/LTO/LTO.h b/include/llvm/LTO/LTO.h
index 28c05ed3fa1..bc435702157 100644
--- a/include/llvm/LTO/LTO.h
+++ b/include/llvm/LTO/LTO.h
@@ -31,6 +31,7 @@
namespace llvm {
+class BitcodeModule;
class Error;
class LLVMContext;
class MemoryBufferRef;
@@ -80,14 +81,16 @@ class InputFile {
// FIXME: Remove the LLVMContext once we have bitcode symbol tables.
LLVMContext Ctx;
+ struct InputModule;
+ std::vector<InputModule> Mods;
ModuleSymbolTable SymTab;
- std::unique_ptr<Module> Mod;
- MemoryBufferRef MBRef;
std::vector<StringRef> Comdats;
DenseMap<const Comdat *, unsigned> ComdatMap;
public:
+ ~InputFile();
+
/// Create an InputFile.
static Expected<std::unique_ptr<InputFile>> create(MemoryBufferRef Object);
@@ -217,11 +220,17 @@ public:
symbol_iterator(SymTab.symbols().end(), SymTab, this));
}
- StringRef getSourceFileName() const { return Mod->getSourceFileName(); }
- MemoryBufferRef getMemoryBufferRef() const { return MBRef; }
+ /// Returns the path to the InputFile.
+ StringRef getName() const;
+
+ /// Returns the source file path specified at compile time.
+ StringRef getSourceFileName() const;
// Returns a table with all the comdats used by this file.
ArrayRef<StringRef> getComdatTable() const { return Comdats; }
+
+private:
+ iterator_range<symbol_iterator> module_symbols(InputModule &IM);
};
/// This class wraps an output stream for a native object. Most clients should
@@ -311,6 +320,7 @@ public:
/// Until that is fixed, a Config argument is required.
LTO(Config Conf, ThinBackend Backend = nullptr,
unsigned ParallelCodeGenParallelismLevel = 1);
+ ~LTO();
/// Add an input file to the LTO link, using the provided symbol resolutions.
/// The symbol resolutions must appear in the enumeration order given by
@@ -357,7 +367,7 @@ private:
ThinBackend Backend;
ModuleSummaryIndex CombinedIndex;
- MapVector<StringRef, MemoryBufferRef> ModuleMap;
+ MapVector<StringRef, BitcodeModule> ModuleMap;
DenseMap<GlobalValue::GUID, StringRef> PrevailingModuleForGUID;
} ThinLTO;
@@ -405,10 +415,17 @@ private:
const InputFile::Symbol &Sym, SymbolResolution Res,
unsigned Partition);
- Error addRegularLTO(std::unique_ptr<InputFile> Input,
- ArrayRef<SymbolResolution> Res);
- Error addThinLTO(std::unique_ptr<InputFile> Input,
- ArrayRef<SymbolResolution> Res);
+ // These functions take a range of symbol resolutions [ResI, ResE) and consume
+ // the resolutions used by a single input module by incrementing ResI. After
+ // these functions return, [ResI, ResE) will refer to the resolution range for
+ // the remaining modules in the InputFile.
+ Error addModule(InputFile &Input, InputFile::InputModule &IM,
+ const SymbolResolution *&ResI, const SymbolResolution *ResE);
+ Error addRegularLTO(BitcodeModule BM, const SymbolResolution *&ResI,
+ const SymbolResolution *ResE);
+ Error addThinLTO(BitcodeModule BM, Module &M,
+ iterator_range<InputFile::symbol_iterator> Syms,
+ const SymbolResolution *&ResI, const SymbolResolution *ResE);
Error runRegularLTO(AddStreamFn AddStream);
Error runThinLTO(AddStreamFn AddStream, NativeObjectCache Cache,
diff --git a/include/llvm/LTO/LTOBackend.h b/include/llvm/LTO/LTOBackend.h
index 5a80ca58d87..933503afddc 100644
--- a/include/llvm/LTO/LTOBackend.h
+++ b/include/llvm/LTO/LTOBackend.h
@@ -27,6 +27,7 @@
namespace llvm {
+class BitcodeModule;
class Error;
class Module;
class Target;
@@ -43,7 +44,7 @@ Error thinBackend(Config &C, unsigned Task, AddStreamFn AddStream, Module &M,
ModuleSummaryIndex &CombinedIndex,
const FunctionImporter::ImportMapTy &ImportList,
const GVSummaryMapTy &DefinedGlobals,
- MapVector<StringRef, MemoryBufferRef> &ModuleMap);
+ MapVector<StringRef, BitcodeModule> &ModuleMap);
}
}
diff --git a/lib/LTO/LTO.cpp b/lib/LTO/LTO.cpp
index 8a68025121c..718cbf17048 100644
--- a/lib/LTO/LTO.cpp
+++ b/lib/LTO/LTO.cpp
@@ -214,6 +214,17 @@ void llvm::thinLTOInternalizeAndPromoteInIndex(
thinLTOInternalizeAndPromoteGUID(I.second, I.first, isExported);
}
+struct InputFile::InputModule {
+ BitcodeModule BM;
+ std::unique_ptr<Module> Mod;
+
+ // The range of ModuleSymbolTable entries for this input module.
+ size_t SymBegin, SymEnd;
+};
+
+// Requires a destructor for std::vector<InputModule>.
+InputFile::~InputFile() = default;
+
Expected<std::unique_ptr<InputFile>> InputFile::create(MemoryBufferRef Object) {
std::unique_ptr<InputFile> File(new InputFile);
@@ -221,23 +232,37 @@ Expected<std::unique_ptr<InputFile>> InputFile::create(MemoryBufferRef Object) {
IRObjectFile::findBitcodeInMemBuffer(Object);
if (!BCOrErr)
return errorCodeToError(BCOrErr.getError());
- File->MBRef = *BCOrErr;
- Expected<std::unique_ptr<Module>> MOrErr =
- getLazyBitcodeModule(*BCOrErr, File->Ctx,
- /*ShouldLazyLoadMetadata*/ true);
- if (!MOrErr)
- return MOrErr.takeError();
+ Expected<std::vector<BitcodeModule>> BMsOrErr =
+ getBitcodeModuleList(*BCOrErr);
+ if (!BMsOrErr)
+ return BMsOrErr.takeError();
- File->Mod = std::move(*MOrErr);
- File->SymTab.addModule(File->Mod.get());
+ if (BMsOrErr->empty())
+ return make_error<StringError>("Bitcode file does not contain any modules",
+ inconvertibleErrorCode());
- for (const auto &C : File->Mod->getComdatSymbolTable()) {
- auto P =
- File->ComdatMap.insert(std::make_pair(&C.second, File->Comdats.size()));
- assert(P.second);
- (void)P;
- File->Comdats.push_back(C.first());
+ // Create an InputModule for each module in the InputFile, and add it to the
+ // ModuleSymbolTable.
+ for (auto BM : *BMsOrErr) {
+ Expected<std::unique_ptr<Module>> MOrErr =
+ BM.getLazyModule(File->Ctx, /*ShouldLazyLoadMetadata*/ true);
+ if (!MOrErr)
+ return MOrErr.takeError();
+
+ size_t SymBegin = File->SymTab.symbols().size();
+ File->SymTab.addModule(MOrErr->get());
+ size_t SymEnd = File->SymTab.symbols().size();
+
+ for (const auto &C : (*MOrErr)->getComdatSymbolTable()) {
+ auto P = File->ComdatMap.insert(
+ std::make_pair(&C.second, File->Comdats.size()));
+ assert(P.second);
+ (void)P;
+ File->Comdats.push_back(C.first());
+ }
+
+ File->Mods.push_back({BM, std::move(*MOrErr), SymBegin, SymEnd});
}
return std::move(File);
@@ -258,6 +283,21 @@ Expected<int> InputFile::Symbol::getComdatIndex() const {
return -1;
}
+StringRef InputFile::getName() const {
+ return Mods[0].BM.getModuleIdentifier();
+}
+
+StringRef InputFile::getSourceFileName() const {
+ return Mods[0].Mod->getSourceFileName();
+}
+
+iterator_range<InputFile::symbol_iterator>
+InputFile::module_symbols(InputModule &IM) {
+ return llvm::make_range(
+ symbol_iterator(SymTab.symbols().data() + IM.SymBegin, SymTab, this),
+ symbol_iterator(SymTab.symbols().data() + IM.SymEnd, SymTab, this));
+}
+
LTO::RegularLTOState::RegularLTOState(unsigned ParallelCodeGenParallelismLevel,
Config &Conf)
: ParallelCodeGenParallelismLevel(ParallelCodeGenParallelismLevel),
@@ -275,6 +315,9 @@ LTO::LTO(Config Conf, ThinBackend Backend,
RegularLTO(ParallelCodeGenParallelismLevel, this->Conf),
ThinLTO(std::move(Backend)) {}
+// Requires a destructor for MapVector<BitcodeModule>.
+LTO::~LTO() = default;
+
// Add the given symbol to the GlobalResolutions map, and resolve its partition.
void LTO::addSymbolToGlobalRes(SmallPtrSet<GlobalValue *, 8> &Used,
const InputFile::Symbol &Sym,
@@ -297,7 +340,7 @@ void LTO::addSymbolToGlobalRes(SmallPtrSet<GlobalValue *, 8> &Used,
static void writeToResolutionFile(raw_ostream &OS, InputFile *Input,
ArrayRef<SymbolResolution> Res) {
- StringRef Path = Input->getMemoryBufferRef().getBufferIdentifier();
+ StringRef Path = Input->getName();
OS << Path << '\n';
auto ResI = Res.begin();
for (const InputFile::Symbol &Sym : Input->symbols()) {
@@ -323,34 +366,45 @@ Error LTO::add(std::unique_ptr<InputFile> Input,
if (Conf.ResolutionFile)
writeToResolutionFile(*Conf.ResolutionFile, Input.get(), Res);
+ const SymbolResolution *ResI = Res.begin();
+ for (InputFile::InputModule &IM : Input->Mods)
+ if (Error Err = addModule(*Input, IM, ResI, Res.end()))
+ return Err;
+
+ assert(ResI == Res.end());
+ return Error::success();
+}
+
+Error LTO::addModule(InputFile &Input, InputFile::InputModule &IM,
+ const SymbolResolution *&ResI,
+ const SymbolResolution *ResE) {
// FIXME: move to backend
- Module &M = *Input->Mod;
+ Module &M = *IM.Mod;
if (!Conf.OverrideTriple.empty())
M.setTargetTriple(Conf.OverrideTriple);
else if (M.getTargetTriple().empty())
M.setTargetTriple(Conf.DefaultTriple);
- Expected<bool> HasThinLTOSummary = hasGlobalValueSummary(Input->MBRef);
+ Expected<bool> HasThinLTOSummary = IM.BM.hasSummary();
if (!HasThinLTOSummary)
return HasThinLTOSummary.takeError();
if (*HasThinLTOSummary)
- return addThinLTO(std::move(Input), Res);
+ return addThinLTO(IM.BM, M, Input.module_symbols(IM), ResI, ResE);
else
- return addRegularLTO(std::move(Input), Res);
+ return addRegularLTO(IM.BM, ResI, ResE);
}
// Add a regular LTO object to the link.
-Error LTO::addRegularLTO(std::unique_ptr<InputFile> Input,
- ArrayRef<SymbolResolution> Res) {
+Error LTO::addRegularLTO(BitcodeModule BM, const SymbolResolution *&ResI,
+ const SymbolResolution *ResE) {
if (!RegularLTO.CombinedModule) {
RegularLTO.CombinedModule =
llvm::make_unique<Module>("ld-temp.o", RegularLTO.Ctx);
RegularLTO.Mover = llvm::make_unique<IRMover>(*RegularLTO.CombinedModule);
}
Expected<std::unique_ptr<Module>> MOrErr =
- getLazyBitcodeModule(Input->MBRef, RegularLTO.Ctx,
- /*ShouldLazyLoadMetadata*/ true);
+ BM.getLazyModule(RegularLTO.Ctx, /*ShouldLazyLoadMetadata*/ true);
if (!MOrErr)
return MOrErr.takeError();
@@ -371,13 +425,12 @@ Error LTO::addRegularLTO(std::unique_ptr<InputFile> Input,
if (GV.hasAppendingLinkage())
Keep.push_back(&GV);
- auto ResI = Res.begin();
for (const InputFile::Symbol &Sym :
make_range(InputFile::symbol_iterator(SymTab.symbols().begin(), SymTab,
nullptr),
InputFile::symbol_iterator(SymTab.symbols().end(), SymTab,
nullptr))) {
- assert(ResI != Res.end());
+ assert(ResI != ResE);
SymbolResolution Res = *ResI++;
addSymbolToGlobalRes(Used, Sym, Res, 0);
@@ -411,7 +464,6 @@ Error LTO::addRegularLTO(std::unique_ptr<InputFile> Input,
// FIXME: use proposed local attribute for FinalDefinitionInLinkageUnit.
}
- assert(ResI == Res.end());
return RegularLTO.Mover->move(std::move(*MOrErr), Keep,
[](GlobalValue &, IRMover::ValueAdder) {},
@@ -420,33 +472,36 @@ Error LTO::addRegularLTO(std::unique_ptr<InputFile> Input,
}
// Add a ThinLTO object to the link.
-Error LTO::addThinLTO(std::unique_ptr<InputFile> Input,
- ArrayRef<SymbolResolution> Res) {
- Module &M = *Input->Mod;
+// FIXME: This function should not need to take as many parameters once we have
+// a bitcode symbol table.
+Error LTO::addThinLTO(BitcodeModule BM, Module &M,
+ iterator_range<InputFile::symbol_iterator> Syms,
+ const SymbolResolution *&ResI,
+ const SymbolResolution *ResE) {
SmallPtrSet<GlobalValue *, 8> Used;
collectUsedGlobalVariables(M, Used, /*CompilerUsed*/ false);
- MemoryBufferRef MBRef = Input->MBRef;
- Expected<std::unique_ptr<object::ModuleSummaryIndexObjectFile>>
- SummaryObjOrErr = object::ModuleSummaryIndexObjectFile::create(MBRef);
- if (!SummaryObjOrErr)
- return SummaryObjOrErr.takeError();
- ThinLTO.CombinedIndex.mergeFrom((*SummaryObjOrErr)->takeIndex(),
+ Expected<std::unique_ptr<ModuleSummaryIndex>> SummaryOrErr = BM.getSummary();
+ if (!SummaryOrErr)
+ return SummaryOrErr.takeError();
+ ThinLTO.CombinedIndex.mergeFrom(std::move(*SummaryOrErr),
ThinLTO.ModuleMap.size());
- auto ResI = Res.begin();
- for (const InputFile::Symbol &Sym : Input->symbols()) {
- assert(ResI != Res.end());
+ for (const InputFile::Symbol &Sym : Syms) {
+ assert(ResI != ResE);
SymbolResolution Res = *ResI++;
addSymbolToGlobalRes(Used, Sym, Res, ThinLTO.ModuleMap.size() + 1);
if (Res.Prevailing && Sym.isGV())
ThinLTO.PrevailingModuleForGUID[Sym.getGV()->getGUID()] =
- MBRef.getBufferIdentifier();
+ BM.getModuleIdentifier();
}
- assert(ResI == Res.end());
- ThinLTO.ModuleMap[MBRef.getBufferIdentifier()] = MBRef;
+ if (!ThinLTO.ModuleMap.insert({BM.getModuleIdentifier(), BM}).second)
+ return make_error<StringError>(
+ "Expected at most one ThinLTO module per bitcode file",
+ inconvertibleErrorCode());
+
return Error::success();
}
@@ -543,11 +598,11 @@ public:
virtual ~ThinBackendProc() {}
virtual Error start(
- unsigned Task, MemoryBufferRef MBRef,
+ unsigned Task, BitcodeModule BM,
const FunctionImporter::ImportMapTy &ImportList,
const FunctionImporter::ExportSetTy &ExportList,
const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
- MapVector<StringRef, MemoryBufferRef> &ModuleMap) = 0;
+ MapVector<StringRef, BitcodeModule> &ModuleMap) = 0;
virtual Error wait() = 0;
};
@@ -572,16 +627,15 @@ public:
Error runThinLTOBackendThread(
AddStreamFn AddStream, NativeObjectCache Cache, unsigned Task,
- MemoryBufferRef MBRef, ModuleSummaryIndex &CombinedIndex,
+ BitcodeModule BM, ModuleSummaryIndex &CombinedIndex,
const FunctionImporter::ImportMapTy &ImportList,
const FunctionImporter::ExportSetTy &ExportList,
const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
const GVSummaryMapTy &DefinedGlobals,
- MapVector<StringRef, MemoryBufferRef> &ModuleMap) {
+ MapVector<StringRef, BitcodeModule> &ModuleMap) {
auto RunThinBackend = [&](AddStreamFn AddStream) {
LTOLLVMContext BackendContext(Conf);
- Expected<std::unique_ptr<Module>> MOrErr =
- parseBitcodeFile(MBRef, BackendContext);
+ Expected<std::unique_ptr<Module>> MOrErr = BM.parseModule(BackendContext);
if (!MOrErr)
return MOrErr.takeError();
@@ -589,7 +643,7 @@ public:
ImportList, DefinedGlobals, ModuleMap);
};
- auto ModuleID = MBRef.getBufferIdentifier();
+ auto ModuleID = BM.getModuleIdentifier();
if (!Cache || !CombinedIndex.modulePaths().count(ModuleID) ||
all_of(CombinedIndex.getModuleHash(ModuleID),
@@ -609,25 +663,25 @@ public:
}
Error start(
- unsigned Task, MemoryBufferRef MBRef,
+ unsigned Task, BitcodeModule BM,
const FunctionImporter::ImportMapTy &ImportList,
const FunctionImporter::ExportSetTy &ExportList,
const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
- MapVector<StringRef, MemoryBufferRef> &ModuleMap) override {
- StringRef ModulePath = MBRef.getBufferIdentifier();
+ MapVector<StringRef, BitcodeModule> &ModuleMap) override {
+ StringRef ModulePath = BM.getModuleIdentifier();
assert(ModuleToDefinedGVSummaries.count(ModulePath));
const GVSummaryMapTy &DefinedGlobals =
ModuleToDefinedGVSummaries.find(ModulePath)->second;
BackendThreadPool.async(
- [=](MemoryBufferRef MBRef, ModuleSummaryIndex &CombinedIndex,
+ [=](BitcodeModule BM, ModuleSummaryIndex &CombinedIndex,
const FunctionImporter::ImportMapTy &ImportList,
const FunctionImporter::ExportSetTy &ExportList,
const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>
&ResolvedODR,
const GVSummaryMapTy &DefinedGlobals,
- MapVector<StringRef, MemoryBufferRef> &ModuleMap) {
+ MapVector<StringRef, BitcodeModule> &ModuleMap) {
Error E = runThinLTOBackendThread(
- AddStream, Cache, Task, MBRef, CombinedIndex, ImportList,
+ AddStream, Cache, Task, BM, CombinedIndex, ImportList,
ExportList, ResolvedODR, DefinedGlobals, ModuleMap);
if (E) {
std::unique_lock<std::mutex> L(ErrMu);
@@ -637,7 +691,7 @@ public:
Err = std::move(E);
}
},
- MBRef, std::ref(CombinedIndex), std::ref(ImportList),
+ BM, std::ref(CombinedIndex), std::ref(ImportList),
std::ref(ExportList), std::ref(ResolvedODR), std::ref(DefinedGlobals),
std::ref(ModuleMap));
return Error::success();
@@ -703,12 +757,12 @@ public:
LinkedObjectsFileName(LinkedObjectsFileName) {}
Error start(
- unsigned Task, MemoryBufferRef MBRef,
+ unsigned Task, BitcodeModule BM,
const FunctionImporter::ImportMapTy &ImportList,
const FunctionImporter::ExportSetTy &ExportList,
const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
- MapVector<StringRef, MemoryBufferRef> &ModuleMap) override {
- StringRef ModulePath = MBRef.getBufferIdentifier();
+ MapVector<StringRef, BitcodeModule> &ModuleMap) override {
+ StringRef ModulePath = BM.getModuleIdentifier();
std::string NewModulePath =
getThinLTOOutputFile(ModulePath, OldPrefix, NewPrefix);
diff --git a/lib/LTO/LTOBackend.cpp b/lib/LTO/LTOBackend.cpp
index ad76e717981..9d4cbdde3ff 100644
--- a/lib/LTO/LTOBackend.cpp
+++ b/lib/LTO/LTOBackend.cpp
@@ -318,7 +318,7 @@ Error lto::thinBackend(Config &Conf, unsigned Task, AddStreamFn AddStream,
Module &Mod, ModuleSummaryIndex &CombinedIndex,
const FunctionImporter::ImportMapTy &ImportList,
const GVSummaryMapTy &DefinedGlobals,
- MapVector<StringRef, MemoryBufferRef> &ModuleMap) {
+ MapVector<StringRef, BitcodeModule> &ModuleMap) {
Expected<const Target *> TOrErr = initAndLookupTarget(Conf, Mod);
if (!TOrErr)
return TOrErr.takeError();
@@ -353,8 +353,10 @@ Error lto::thinBackend(Config &Conf, unsigned Task, AddStreamFn AddStream,
auto ModuleLoader = [&](StringRef Identifier) {
assert(Mod.getContext().isODRUniquingDebugTypes() &&
"ODR Type uniquing should be enabled on the context");
- return getLazyBitcodeModule(ModuleMap[Identifier], Mod.getContext(),
- /*ShouldLazyLoadMetadata=*/true);
+ auto I = ModuleMap.find(Identifier);
+ assert(I != ModuleMap.end());
+ return I->second.getLazyModule(Mod.getContext(),
+ /*ShouldLazyLoadMetadata=*/true);
};
FunctionImporter Importer(CombinedIndex, ModuleLoader);
diff --git a/test/LTO/Resolution/X86/empty-bitcode.test b/test/LTO/Resolution/X86/empty-bitcode.test
new file mode 100644
index 00000000000..c98c54499ef
--- /dev/null
+++ b/test/LTO/Resolution/X86/empty-bitcode.test
@@ -0,0 +1,3 @@
+RUN: llvm-cat -o %t.o
+RUN: not llvm-lto2 -o %t2 %t.o 2>&1 | FileCheck %s
+CHECK: Bitcode file does not contain any modules
diff --git a/test/LTO/Resolution/X86/mixed_lto.ll b/test/LTO/Resolution/X86/mixed_lto.ll
index 02e6186a3f5..0302ed990e0 100644
--- a/test/LTO/Resolution/X86/mixed_lto.ll
+++ b/test/LTO/Resolution/X86/mixed_lto.ll
@@ -13,6 +13,12 @@
; NM1-DAG: T main
; NM1-DAG: U g
+; Do the same test again, but with the regular and thin LTO modules in the same file.
+; RUN: llvm-cat -b -o %t4.o %t2.o %t1.o
+; RUN: llvm-lto2 -o %t5.o %t4.o -r %t4.o,main,px -r %t4.o,g, -r %t4.o,g,px
+; RUN: llvm-nm %t5.o.0 | FileCheck %s --check-prefix=NM0
+; RUN: llvm-nm %t5.o.1 | FileCheck %s --check-prefix=NM1
+
target triple = "x86_64-unknown-linux-gnu"
define i32 @g() {
ret i32 0
diff --git a/test/LTO/Resolution/X86/multi-thinlto.ll b/test/LTO/Resolution/X86/multi-thinlto.ll
new file mode 100644
index 00000000000..605c9694ced
--- /dev/null
+++ b/test/LTO/Resolution/X86/multi-thinlto.ll
@@ -0,0 +1,6 @@
+; RUN: opt -module-summary %s -o %t.o
+; RUN: llvm-cat -b -o %t2.o %t.o %t.o
+; RUN: not llvm-lto2 -o %t3.o %t2.o 2>&1 | FileCheck %s
+; CHECK: Expected at most one ThinLTO module per bitcode file
+
+target triple = "x86_64-unknown-linux-gnu"