Index: include/llvm/Bitcode/BitcodeReader.h =================================================================== --- include/llvm/Bitcode/BitcodeReader.h +++ include/llvm/Bitcode/BitcodeReader.h @@ -51,6 +51,7 @@ struct BitcodeLTOInfo { bool IsThinLTO; bool HasSummary; + bool EnableSplitLTOUnit; }; /// Represents a module in a bitcode file. Index: include/llvm/IR/ModuleSummaryIndex.h =================================================================== --- include/llvm/IR/ModuleSummaryIndex.h +++ include/llvm/IR/ModuleSummaryIndex.h @@ -815,6 +815,9 @@ /// union. bool HaveGVs; + // True if the index was created for a module compiled with -fsplit-lto-unit. + bool EnableSplitLTOUnit; + std::set CfiFunctionDefs; std::set CfiFunctionDecls; @@ -834,7 +837,9 @@ public: // See HaveGVs variable comment. - ModuleSummaryIndex(bool HaveGVs) : HaveGVs(HaveGVs), Saver(Alloc) {} + ModuleSummaryIndex(bool HaveGVs, bool EnableSplitLTOUnit = false) + : HaveGVs(HaveGVs), EnableSplitLTOUnit(EnableSplitLTOUnit), Saver(Alloc) { + } bool haveGVs() const { return HaveGVs; } @@ -921,6 +926,9 @@ SkipModuleByDistributedBackend = true; } + bool enableSplitLTOUnit() const { return EnableSplitLTOUnit; } + void setEnableSplitLTOUnit() { EnableSplitLTOUnit = true; } + bool isGlobalValueLive(const GlobalValueSummary *GVS) const { return !WithGlobalValueDeadStripping || GVS->isLive(); } Index: include/llvm/LTO/LTO.h =================================================================== --- include/llvm/LTO/LTO.h +++ include/llvm/LTO/LTO.h @@ -387,6 +387,9 @@ Error runThinLTO(AddStreamFn AddStream, NativeObjectCache Cache); mutable bool CalledGetMaxTasks = false; + + // Use Optional to distinguish false from not yet initialized. + Optional EnableSplitLTOUnit; }; /// The resolution for a symbol. The linker must provide a SymbolResolution for Index: lib/Analysis/ModuleSummaryAnalysis.cpp =================================================================== --- lib/Analysis/ModuleSummaryAnalysis.cpp +++ lib/Analysis/ModuleSummaryAnalysis.cpp @@ -464,7 +464,11 @@ std::function GetBFICallback, ProfileSummaryInfo *PSI) { assert(PSI); - ModuleSummaryIndex Index(/*HaveGVs=*/true); + bool EnableSplitLTOUnit = false; + if (auto *MD = mdconst::extract_or_null( + M.getModuleFlag("EnableSplitLTOUnit"))) + EnableSplitLTOUnit = MD->getZExtValue(); + ModuleSummaryIndex Index(/*HaveGVs=*/true, EnableSplitLTOUnit); // Identify the local values in the llvm.used and llvm.compiler.used sets, // which should not be exported as they would then require renaming and Index: lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- lib/Bitcode/Reader/BitcodeReader.cpp +++ lib/Bitcode/Reader/BitcodeReader.cpp @@ -5294,15 +5294,24 @@ break; case bitc::FS_FLAGS: { // [flags] uint64_t Flags = Record[0]; - // Scan flags (set only on the combined index). - assert(Flags <= 0x3 && "Unexpected bits in flag"); + // Scan flags. + assert(Flags <= 0x7 && "Unexpected bits in flag"); // 1 bit: WithGlobalValueDeadStripping flag. + // Set on combined index only. if (Flags & 0x1) TheIndex.setWithGlobalValueDeadStripping(); // 1 bit: SkipModuleByDistributedBackend flag. + // Set on combined index only. if (Flags & 0x2) TheIndex.setSkipModuleByDistributedBackend(); + // 1 bit: DisableSplitLTOUnit flag. + // Set on per module indexes. It is up to the client to validate + // the consistency of this flag across modules being linked. + // We recorded the reverse of the EnableSplitLTOUnit flag, for backwards + // compatibility with older bitcode when splitting was enabled by default. + if (!(Flags & 0x4)) + TheIndex.setEnableSplitLTOUnit(); break; } case bitc::FS_VALUE_GUID: { // [valueid, refguid] @@ -5907,6 +5916,47 @@ return std::move(Index); } +static Expected getEnableSplitLTOUnitFlag(BitstreamCursor &Stream, + unsigned ID) { + if (Stream.EnterSubBlock(ID)) + return error("Invalid record"); + SmallVector Record; + + while (true) { + BitstreamEntry Entry = Stream.advanceSkippingSubblocks(); + + switch (Entry.Kind) { + case BitstreamEntry::SubBlock: // Handled for us already. + case BitstreamEntry::Error: + return error("Malformed block"); + case BitstreamEntry::EndBlock: + // If no flags record found, conservatively return false. + return false; + case BitstreamEntry::Record: + // The interesting case. + break; + } + + // Look for the FS_FLAGS record. + Record.clear(); + auto BitCode = Stream.readRecord(Entry.ID, Record); + switch (BitCode) { + default: // Default behavior: ignore. + break; + case bitc::FS_FLAGS: { // [flags] + uint64_t Flags = Record[0]; + // Scan flags. + assert(Flags <= 0x7 && "Unexpected bits in flag"); + + // We recorded the reverse of the EnableSplitLTOUnit flag, for backwards + // compatibility with older bitcode when splitting was enabled by default. + return !(Flags & 0x4); + } + } + } + llvm_unreachable("Exit infinite loop"); +} + // Check if the given bitcode buffer contains a global value summary block. Expected BitcodeModule::getLTOInfo() { BitstreamCursor Stream(Buffer); @@ -5922,14 +5972,27 @@ case BitstreamEntry::Error: return error("Malformed block"); case BitstreamEntry::EndBlock: - return BitcodeLTOInfo{/*IsThinLTO=*/false, /*HasSummary=*/false}; + return BitcodeLTOInfo{/*IsThinLTO=*/false, /*HasSummary=*/false, + /*EnableSplitLTOUnit=*/false}; case BitstreamEntry::SubBlock: - if (Entry.ID == bitc::GLOBALVAL_SUMMARY_BLOCK_ID) - return BitcodeLTOInfo{/*IsThinLTO=*/true, /*HasSummary=*/true}; + if (Entry.ID == bitc::GLOBALVAL_SUMMARY_BLOCK_ID) { + Expected EnableSplitLTOUnit = + getEnableSplitLTOUnitFlag(Stream, Entry.ID); + if (!EnableSplitLTOUnit) + return EnableSplitLTOUnit.takeError(); + return BitcodeLTOInfo{/*IsThinLTO=*/true, /*HasSummary=*/true, + *EnableSplitLTOUnit}; + } - if (Entry.ID == bitc::FULL_LTO_GLOBALVAL_SUMMARY_BLOCK_ID) - return BitcodeLTOInfo{/*IsThinLTO=*/false, /*HasSummary=*/true}; + if (Entry.ID == bitc::FULL_LTO_GLOBALVAL_SUMMARY_BLOCK_ID) { + Expected EnableSplitLTOUnit = + getEnableSplitLTOUnitFlag(Stream, Entry.ID); + if (!EnableSplitLTOUnit) + return EnableSplitLTOUnit.takeError(); + return BitcodeLTOInfo{/*IsThinLTO=*/false, /*HasSummary=*/true, + *EnableSplitLTOUnit}; + } // Ignore other sub-blocks. if (Stream.SkipBlock()) Index: lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- lib/Bitcode/Writer/BitcodeWriter.cpp +++ lib/Bitcode/Writer/BitcodeWriter.cpp @@ -3620,6 +3620,15 @@ Stream.EmitRecord(bitc::FS_VERSION, ArrayRef{INDEX_VERSION}); + // Write the index flags. + uint64_t Flags = 0; + // Bits 1 and 2 are set only in the combined index, skip them. + // We record the reverse of the EnableSplitLTOUnit flag, for backwards + // compatibility with older bitcode when splitting was enabled by default. + if (!Index->enableSplitLTOUnit()) + Flags |= 0x4; + Stream.EmitRecord(bitc::FS_FLAGS, ArrayRef{Flags}); + if (Index->begin() == Index->end()) { Stream.ExitBlock(); return; @@ -3734,6 +3743,10 @@ Flags |= 0x1; if (Index.skipModuleByDistributedBackend()) Flags |= 0x2; + // We record the reverse of the EnableSplitLTOUnit flag, for backwards + // compatibility with older bitcode when splitting was enabled by default. + if (!Index.enableSplitLTOUnit()) + Flags |= 0x4; Stream.EmitRecord(bitc::FS_FLAGS, ArrayRef{Flags}); for (const auto &GVI : valueIds()) { Index: lib/LTO/LTO.cpp =================================================================== --- lib/LTO/LTO.cpp +++ lib/LTO/LTO.cpp @@ -534,6 +534,14 @@ if (!LTOInfo) return LTOInfo.takeError(); + if (EnableSplitLTOUnit.hasValue()) { + if (EnableSplitLTOUnit.getValue() != LTOInfo->EnableSplitLTOUnit) + return make_error("Expect all bitcode to be compiled with " + "same value of -f[no-]split-lto-unit", + inconvertibleErrorCode()); + } else + EnableSplitLTOUnit = LTOInfo->EnableSplitLTOUnit; + BitcodeModule BM = Input.Mods[ModI]; auto ModSyms = Input.module_symbols(ModI); addModuleToGlobalRes(ModSyms, {ResI, ResE}, Index: lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp =================================================================== --- lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp +++ lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp @@ -419,6 +419,14 @@ // Returns whether this module needs to be split because it uses type metadata. bool requiresSplit(Module &M) { + // First check if the LTO Unit splitting has been enabled. + bool EnableSplitLTOUnit = false; + if (auto *MD = mdconst::extract_or_null( + M.getModuleFlag("EnableSplitLTOUnit"))) + EnableSplitLTOUnit = MD->getZExtValue(); + if (!EnableSplitLTOUnit) + return false; + for (auto &GO : M.global_objects()) { if (GO.hasMetadata(LLVMContext::MD_type)) return true; Index: test/Bitcode/thinlto-alias.ll =================================================================== --- test/Bitcode/thinlto-alias.ll +++ test/Bitcode/thinlto-alias.ll @@ -18,6 +18,7 @@ ; CHECK-NEXT: Index: test/Bitcode/thinlto-alias2.ll =================================================================== --- test/Bitcode/thinlto-alias2.ll +++ test/Bitcode/thinlto-alias2.ll @@ -4,6 +4,7 @@ ; CHECK: ; CHECK-NEXT: Index: test/Bitcode/thinlto-deadstrip-flag.ll =================================================================== --- test/Bitcode/thinlto-deadstrip-flag.ll +++ test/Bitcode/thinlto-deadstrip-flag.ll @@ -5,14 +5,14 @@ ; RUN: llvm-lto2 run %t.o -o %t.out -thinlto-distributed-indexes \ ; RUN: -r %t.o,glob,plx ; RUN: llvm-bcanalyzer -dump %t.o.thinlto.bc | FileCheck %s --check-prefix=WITHDEAD -; WITHDEAD: +; WITHDEAD: ; Ensure dead stripping performed flag is not set on distributed index ; when option used to disable dead stripping computation. ; RUN: llvm-lto2 run %t.o -o %t.out -thinlto-distributed-indexes \ ; RUN: -r %t.o,glob,plx -compute-dead=false ; RUN: llvm-bcanalyzer -dump %t.o.thinlto.bc | FileCheck %s --check-prefix=NODEAD -; NODEAD: +; NODEAD: target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" Index: test/Bitcode/thinlto-function-summary-callgraph-cast.ll =================================================================== --- test/Bitcode/thinlto-function-summary-callgraph-cast.ll +++ test/Bitcode/thinlto-function-summary-callgraph-cast.ll @@ -5,6 +5,7 @@ ; CHECK: ; "another_caller" has only references but no calls. Index: test/Bitcode/thinlto-function-summary-callgraph-pgo.ll =================================================================== --- test/Bitcode/thinlto-function-summary-callgraph-pgo.ll +++ test/Bitcode/thinlto-function-summary-callgraph-pgo.ll @@ -16,6 +16,7 @@ ; CHECK-NEXT: ; CHECK-NEXT: Index: test/Bitcode/thinlto-function-summary-callgraph-profile-summary.ll =================================================================== --- test/Bitcode/thinlto-function-summary-callgraph-profile-summary.ll +++ test/Bitcode/thinlto-function-summary-callgraph-profile-summary.ll @@ -46,6 +46,7 @@ ; CHECK-NEXT: ; op4=hot1 op6=cold op8=hot2 op10=hot4 op12=none1 op14=hot3 op16=none2 op18=none3 op20=123 ; CHECK-NEXT: Index: test/Bitcode/thinlto-function-summary-callgraph-relbf.ll =================================================================== --- test/Bitcode/thinlto-function-summary-callgraph-relbf.ll +++ test/Bitcode/thinlto-function-summary-callgraph-relbf.ll @@ -12,6 +12,7 @@ ; CHECK-NEXT: Index: test/Bitcode/thinlto-function-summary-callgraph-sample-profile-summary.ll =================================================================== --- test/Bitcode/thinlto-function-summary-callgraph-sample-profile-summary.ll +++ test/Bitcode/thinlto-function-summary-callgraph-sample-profile-summary.ll @@ -29,6 +29,7 @@ ; CHECK-LABEL: ; op4=none1 op6=hot1 op8=cold1 op10=none2 op12=hot2 op14=cold2 op16=none3 op18=hot3 op20=cold3 op22=123 ; CHECK-NEXT: Index: test/Bitcode/thinlto-function-summary-callgraph.ll =================================================================== --- test/Bitcode/thinlto-function-summary-callgraph.ll +++ test/Bitcode/thinlto-function-summary-callgraph.ll @@ -17,6 +17,7 @@ ; CHECK-NEXT: Index: test/Bitcode/thinlto-function-summary.ll =================================================================== --- test/Bitcode/thinlto-function-summary.ll +++ test/Bitcode/thinlto-function-summary.ll @@ -19,6 +19,7 @@ ; BC-NEXT: &1 | FileCheck %s --check-prefix=NOERROR --allow-empty + +; Linking bitcode both without EnableSplitLTOUnit set should work +; RUN: opt -thinlto-bc -thinlto-split-lto-unit=false -o %t1 %s +; RUN: llvm-bcanalyzer -dump %t1 | FileCheck %s --check-prefix=NOSPLITLTOUNIT +; RUN: llvm-dis -o - %t1 | FileCheck %s --check-prefix=NOENABLESPLITFLAG +; RUN: opt -thinlto-bc -thinlto-split-lto-unit=false -o %t2 %s +; RUN: llvm-bcanalyzer -dump %t2 | FileCheck %s --check-prefix=NOSPLITLTOUNIT +; RUN: llvm-dis -o - %t2 | FileCheck %s --check-prefix=NOENABLESPLITFLAG +; RUN: llvm-lto2 run -o %t3 %t1 %t2 2>&1 | FileCheck %s --check-prefix=NOERROR --allow-empty + +; Linking bitcode with different values of EnableSplitLTOUnit should fail +; RUN: opt -thinlto-bc -thinlto-split-lto-unit -o %t1 %s +; RUN: llvm-bcanalyzer -dump %t1 | FileCheck %s --check-prefix=SPLITLTOUNIT +; RUN: llvm-dis -o - %t1 | FileCheck %s --check-prefix=ENABLESPLITFLAG +; RUN: opt -thinlto-bc -thinlto-split-lto-unit=false -o %t2 %s +; RUN: llvm-bcanalyzer -dump %t2 | FileCheck %s --check-prefix=NOSPLITLTOUNIT +; RUN: llvm-dis -o - %t2 | FileCheck %s --check-prefix=NOENABLESPLITFLAG +; RUN: not llvm-lto2 run -o %t3 %t1 %t2 2>&1 | FileCheck %s --check-prefix=ERROR + +; Linking bitcode with different values of EnableSplitLTOUnit should fail +; (reverse order) +; RUN: opt -thinlto-bc -thinlto-split-lto-unit=false -o %t1 %s +; RUN: llvm-bcanalyzer -dump %t1 | FileCheck %s --check-prefix=NOSPLITLTOUNIT +; RUN: llvm-dis -o - %t1 | FileCheck %s --check-prefix=NOENABLESPLITFLAG +; RUN: opt -thinlto-bc -thinlto-split-lto-unit -o %t2 %s +; RUN: llvm-bcanalyzer -dump %t2 | FileCheck %s --check-prefix=SPLITLTOUNIT +; RUN: llvm-dis -o - %t2 | FileCheck %s --check-prefix=ENABLESPLITFLAG +; RUN: not llvm-lto2 run -o %t3 %t1 %t2 2>&1 | FileCheck %s --check-prefix=ERROR + +; ERROR: Expect all bitcode to be compiled with same value of -f[no-]split-lto-unit +; NOERROR-NOT: Expect all bitcode to be compiled with same value of -f[no-]split-lto-unit + +; The flag should be set when splitting is disabled (for backwards compatibility +; with older bitcode where it was always enabled). +; SPLITLTOUNIT: +; NOSPLITLTOUNIT: + +; Check that the corresponding module flag is set when expected. +; ENABLESPLITFLAG: !{i32 1, !"EnableSplitLTOUnit", i32 1} +; NOENABLESPLITFLAG-NOT: !{i32 1, !"EnableSplitLTOUnit", i32 1} + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" Index: test/Transforms/ThinLTOBitcodeWriter/unsplittable.ll =================================================================== --- test/Transforms/ThinLTOBitcodeWriter/unsplittable.ll +++ test/Transforms/ThinLTOBitcodeWriter/unsplittable.ll @@ -28,7 +28,8 @@ ret void } -; CHECK: !llvm.module.flags = !{![[FLAG:[0-9]+]]} -; CHECK: ![[FLAG]] = !{i32 1, !"ThinLTO", i32 0} +; CHECK: !llvm.module.flags = !{![[FLAG1:[0-9]+]], ![[FLAG2:[0-9]+]]} +; CHECK: ![[FLAG1]] = !{i32 1, !"EnableSplitLTOUnit", i32 1} +; CHECK: ![[FLAG2]] = !{i32 1, !"ThinLTO", i32 0} !0 = !{i32 0, !"typeid"} Index: test/tools/gold/X86/v1.12/thinlto_emit_linked_objects.ll =================================================================== --- test/tools/gold/X86/v1.12/thinlto_emit_linked_objects.ll +++ test/tools/gold/X86/v1.12/thinlto_emit_linked_objects.ll @@ -35,14 +35,14 @@ ; should not be set. ; RUN: llvm-bcanalyzer --dump %t1.o.thinlto.bc | FileCheck %s -check-prefixes=CHECK-BC1 ; CHECK-BC1: +; CHECK-BC1: ; CHECK-BC1: +; CHECK-BC2: ; CHECK-BC2: + SplitLTOUnit("thinlto-split-lto-unit", + cl::desc("Enable splitting of a ThinLTO LTOUnit"), + cl::init(true)); + static cl::opt ThinLinkBitcodeFile( "thin-link-bitcode-file", cl::value_desc("filename"), cl::desc( @@ -596,6 +603,9 @@ if (CheckBitcodeOutputToConsole(Out->os(), !Quiet)) NoOutput = true; + if (OutputThinLTOBC) + M->addModuleFlag(Module::Error, "EnableSplitLTOUnit", SplitLTOUnit); + if (PassPipeline.getNumOccurrences() > 0) { OutputKind OK = OK_NoOutput; if (!NoOutput)