Index: llvm/include/llvm/IR/ModuleSummaryIndex.h =================================================================== --- llvm/include/llvm/IR/ModuleSummaryIndex.h +++ llvm/include/llvm/IR/ModuleSummaryIndex.h @@ -1018,6 +1018,11 @@ // and BitcodeWriter.cpp. static constexpr uint64_t BitcodeSummaryVersion = 8; + // Regular LTO module name for ASM writer + static constexpr const char *getRegularLTOModuleName() { + return "[Regular LTO]"; + } + bool haveGVs() const { return HaveGVs; } gvsummary_iterator begin() { return GlobalValueMap.begin(); } Index: llvm/lib/IR/AsmWriter.cpp =================================================================== --- llvm/lib/IR/AsmWriter.cpp +++ llvm/lib/IR/AsmWriter.cpp @@ -2683,7 +2683,8 @@ // Print module path entries. To print in order, add paths to a vector // indexed by module slot. std::vector> moduleVec; - std::string RegularLTOModuleName = "[Regular LTO]"; + std::string RegularLTOModuleName = + ModuleSummaryIndex::getRegularLTOModuleName(); moduleVec.resize(TheIndex->modulePaths().size()); for (auto &ModPath : TheIndex->modulePaths()) moduleVec[Machine.getModulePathSlot(ModPath.first())] = std::make_pair( Index: llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp =================================================================== --- llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp +++ llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp @@ -62,6 +62,8 @@ #include "llvm/Analysis/BasicAliasAnalysis.h" #include "llvm/Analysis/OptimizationRemarkEmitter.h" #include "llvm/Analysis/TypeMetadataUtils.h" +#include "llvm/Bitcode/BitcodeReader.h" +#include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/IR/CallSite.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DataLayout.h" @@ -86,6 +88,7 @@ #include "llvm/PassSupport.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MathExtras.h" @@ -115,12 +118,15 @@ static cl::opt ClReadSummary( "wholeprogramdevirt-read-summary", - cl::desc("Read summary from given YAML file before running pass"), + cl::desc( + "Read summary from given bitcode or YAML file before running pass"), cl::Hidden); static cl::opt ClWriteSummary( "wholeprogramdevirt-write-summary", - cl::desc("Write summary to given YAML file after running pass"), + cl::desc("Write summary to given bitcode or YAML file after running pass. " + "Output file format is deduced from extension: *.bc means writing " + "bitcode, otherwise YAML"), cl::Hidden); static cl::opt @@ -737,11 +743,23 @@ } // end namespace llvm +static Error checkCombinedSummaryForTesting(ModuleSummaryIndex *Summary) { + // Check that summary index contains regular LTO module + const auto &ModPaths = Summary->modulePaths(); + if (ModPaths.find(ModuleSummaryIndex::getRegularLTOModuleName()) == + ModPaths.end()) + return createStringError( + errc::invalid_argument, + "combined summary should contain Regular LTO module"); + return ErrorSuccess(); +} + bool DevirtModule::runForTesting( Module &M, function_ref AARGetter, function_ref OREGetter, function_ref LookupDomTree) { - ModuleSummaryIndex Summary(/*HaveGVs=*/false); + std::unique_ptr Summary = + std::make_unique(/*HaveGVs=*/false); // Handle the command-line summary arguments. This code is for testing // purposes only, so we handle errors directly. @@ -750,28 +768,41 @@ ": "); auto ReadSummaryFile = ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(ClReadSummary))); - - yaml::Input In(ReadSummaryFile->getBuffer()); - In >> Summary; - ExitOnErr(errorCodeToError(In.error())); + if (Expected> SummaryOrErr = + getModuleSummaryIndex(*ReadSummaryFile)) { + Summary = std::move(*SummaryOrErr); + ExitOnErr(checkCombinedSummaryForTesting(Summary.get())); + } else { + // Try YAML if we've failed with bitcode. + consumeError(SummaryOrErr.takeError()); + yaml::Input In(ReadSummaryFile->getBuffer()); + In >> *Summary; + ExitOnErr(errorCodeToError(In.error())); + } } bool Changed = - DevirtModule( - M, AARGetter, OREGetter, LookupDomTree, - ClSummaryAction == PassSummaryAction::Export ? &Summary : nullptr, - ClSummaryAction == PassSummaryAction::Import ? &Summary : nullptr) + DevirtModule(M, AARGetter, OREGetter, LookupDomTree, + ClSummaryAction == PassSummaryAction::Export ? Summary.get() + : nullptr, + ClSummaryAction == PassSummaryAction::Import ? Summary.get() + : nullptr) .run(); if (!ClWriteSummary.empty()) { ExitOnError ExitOnErr( "-wholeprogramdevirt-write-summary: " + ClWriteSummary + ": "); std::error_code EC; - raw_fd_ostream OS(ClWriteSummary, EC, sys::fs::OF_Text); - ExitOnErr(errorCodeToError(EC)); - - yaml::Output Out(OS); - Out << Summary; + if (StringRef(ClWriteSummary).endswith(".bc")) { + raw_fd_ostream OS(ClWriteSummary, EC, sys::fs::OF_None); + ExitOnErr(errorCodeToError(EC)); + WriteIndexToFile(*Summary, OS); + } else { + raw_fd_ostream OS(ClWriteSummary, EC, sys::fs::OF_Text); + ExitOnErr(errorCodeToError(EC)); + yaml::Output Out(OS); + Out << *Summary; + } } return Changed; Index: llvm/test/Transforms/WholeProgramDevirt/Inputs/devirt-bad-index.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/WholeProgramDevirt/Inputs/devirt-bad-index.ll @@ -0,0 +1,4 @@ +; ModuleID = '/tmp/devirt-index.bc' +source_filename = "/tmp/devirt-index.bc" + +^0 = module: (path: "/tmp/main.bc", hash: (3499594384, 1671013073, 3271036935, 1830411232, 59290952)) Index: llvm/test/Transforms/WholeProgramDevirt/Inputs/devirt-single-impl2-index.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/WholeProgramDevirt/Inputs/devirt-single-impl2-index.ll @@ -0,0 +1,14 @@ +; ModuleID = '/tmp/devirt-index.bc' +source_filename = "/tmp/devirt-index.bc" + +^0 = module: (path: "/tmp/main.bc", hash: (3499594384, 1671013073, 3271036935, 1830411232, 59290952)) +^1 = module: (path: "/tmp/foo.bc", hash: (1981453201, 1990260332, 4054522231, 886164300, 2116061388)) +^2 = module: (path: "/tmp/bar.bc", hash: (1315792037, 3870713320, 284974409, 169291533, 3565750560)) +^3 = module: (path: "[Regular LTO]", hash: (0, 0, 0, 0, 0)) +^4 = gv: (guid: 7004155349499253778, summaries: (variable: (module: ^2, flags: (linkage: linkonce_odr, notEligibleToImport: 0, live: 1, dsoLocal: 1, canAutoHide: 0), varFlags: (readonly: 0, writeonly: 0, constant: 1)))) +^5 = gv: (guid: 7112837063505133550, summaries: (variable: (module: ^2, flags: (linkage: linkonce_odr, notEligibleToImport: 0, live: 1, dsoLocal: 1, canAutoHide: 0), varFlags: (readonly: 0, writeonly: 0, constant: 1), refs: (^4)))) +^6 = gv: (guid: 12105754951942688208, summaries: (variable: (module: ^3, flags: (linkage: linkonce_odr, notEligibleToImport: 1, live: 1, dsoLocal: 1, canAutoHide: 1), varFlags: (readonly: 0, writeonly: 0, constant: 1), refs: (^5, ^7)))) +^7 = gv: (guid: 13351721993301222997, summaries: (function: (module: ^2, flags: (linkage: linkonce_odr, notEligibleToImport: 0, live: 1, dsoLocal: 1, canAutoHide: 1), insts: 1), function: (module: ^3, flags: (linkage: available_externally, notEligibleToImport: 1, live: 1, dsoLocal: 1, canAutoHide: 0), insts: 1))) +^8 = gv: (guid: 15822663052811949562, summaries: (function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 1, dsoLocal: 1, canAutoHide: 0), insts: 2, calls: ((callee: ^10))))) +^9 = gv: (guid: 16692224328168775211, summaries: (function: (module: ^1, flags: (linkage: external, notEligibleToImport: 0, live: 1, dsoLocal: 1, canAutoHide: 0), insts: 9, typeIdInfo: (typeTestAssumeConstVCalls: ((vFuncId: (guid: 7004155349499253778, offset: 0))))))) +^10 = gv: (guid: 17377440600225628772, summaries: (function: (module: ^2, flags: (linkage: external, notEligibleToImport: 0, live: 1, dsoLocal: 1, canAutoHide: 0), insts: 9, calls: ((callee: ^9)), refs: (^6)))) Index: llvm/test/Transforms/WholeProgramDevirt/devirt-single-impl2.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/WholeProgramDevirt/devirt-single-impl2.ll @@ -0,0 +1,44 @@ +; Check that we can run WPD export using opt -wholeprogramdevirt while +; loading/saving index from/to bitcode +; RUN: llvm-as %s -o %t.bc +; RUN: llvm-as %p/Inputs/devirt-single-impl2-index.ll -o %t.index.bc +; RUN: opt %s -S -wholeprogramdevirt -wholeprogramdevirt-read-summary=%t.index.bc \ +; RUN: -wholeprogramdevirt-summary-action=export \ +; RUN: -wholeprogramdevirt-write-summary=%t2.index.bc -o /dev/null +; RUN: llvm-dis %t2.index.bc -o - | FileCheck %s + +; Check couple of bad summary indexes +; RUN: llvm-as %p/Inputs/devirt-bad-index.ll -o %t-bad.index.bc +; RUN: not opt %s -S -wholeprogramdevirt -wholeprogramdevirt-read-summary=%t-bad1.index.bc \ +; RUN: -wholeprogramdevirt-summary-action=export -o /dev/null 2>&1 | FileCheck %s --check-prefix=MISSING-MODULE + +; Check single impl devirtulation in summary +; CHECK: typeid: (name: "_ZTS1A", summary: (typeTestRes: (kind: unsat, sizeM1BitWidth: 0), wpdResolutions: ((offset: 0, wpdRes: (kind: singleImpl, singleImplName: "_ZNK1A1fEv"))))) ; guid + +; MISSING-MODULE: combined summary should contain Regular LTO module + +source_filename = "ld-temp.o" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%struct.A = type { i32 (...)** } + +$_ZTV1A = comdat any + +@_ZTV1A = weak_odr hidden unnamed_addr constant { [3 x i8*] } { [3 x i8*] [i8* null, i8* bitcast ({ i8*, i8* }* @_ZTI1A to i8*), i8* bitcast (i32 (%struct.A*)* @_ZNK1A1fEv to i8*)] }, comdat, align 8, !type !0, !type !1 +@_ZTI1A = external hidden constant { i8*, i8* }, align 8 +define available_externally hidden i32 @_ZNK1A1fEv(%struct.A* %this) unnamed_addr align 2 { +entry: + ret i32 3 +} + +!llvm.ident = !{!2} +!llvm.module.flags = !{!3, !4, !5, !6} + +!0 = !{i64 16, !"_ZTS1A"} +!1 = !{i64 16, !"_ZTSM1AKFivE.virtual"} +!2 = !{!"clang version 10.0.0 (trunk 373596)"} +!3 = !{i32 1, !"wchar_size", i32 4} +!4 = !{i32 1, !"EnableSplitLTOUnit", i32 1} +!5 = !{i32 1, !"ThinLTO", i32 0} +!6 = !{i32 1, !"LTOPostLink", i32 1}