diff options
author | Arthur Eubanks <aeubanks@google.com> | 2022-06-30 15:18:04 -0700 |
---|---|---|
committer | Arthur Eubanks <aeubanks@google.com> | 2022-07-26 08:01:08 -0700 |
commit | 2eade1dba4a8d6e1c6867e9127bcd88cf4e55976 (patch) | |
tree | 080ccfec588a3231d14022bc922f5c05e9a11adb /llvm | |
parent | e43621b09c9741d2e3a8a3d8ed216699af5ff8ba (diff) | |
download | llvm-2eade1dba4a8d6e1c6867e9127bcd88cf4e55976.zip llvm-2eade1dba4a8d6e1c6867e9127bcd88cf4e55976.tar.gz llvm-2eade1dba4a8d6e1c6867e9127bcd88cf4e55976.tar.bz2 |
[WPD] Use new llvm.public.type.test intrinsic for potentially publicly visible classes
Turning on opaque pointers has uncovered an issue with WPD where we currently pattern match away `assume(type.test)` in WPD so that a later LTT doesn't resolve the type test to undef and introduce an `assume(false)`. The pattern matching can fail in cases where we transform two `assume(type.test)`s into `assume(phi(type.test.1, type.test.2))`.
Currently we create `assume(type.test)` for all virtual calls that might be devirtualized. This is to support `-Wl,--lto-whole-program-visibility`.
To prevent this, all virtual calls that may not be in the same LTO module instead use a new `llvm.public.type.test` intrinsic in place of the `llvm.type.test`. Then when we know if `-Wl,--lto-whole-program-visibility` is passed or not, we can either replace all `llvm.public.type.test` with `llvm.type.test`, or replace all `llvm.public.type.test` with `true`. This prevents WPD from trying to pattern match away `assume(type.test)` for public virtual calls when failing the pattern matching will result in miscompiles.
Reviewed By: tejohnson
Differential Revision: https://reviews.llvm.org/D128955
Diffstat (limited to 'llvm')
-rw-r--r-- | llvm/include/llvm/IR/Intrinsics.td | 6 | ||||
-rw-r--r-- | llvm/include/llvm/IR/ModuleSummaryIndex.h | 6 | ||||
-rw-r--r-- | llvm/include/llvm/LTO/legacy/LTOCodeGenerator.h | 4 | ||||
-rw-r--r-- | llvm/include/llvm/Transforms/IPO/WholeProgramDevirt.h | 4 | ||||
-rw-r--r-- | llvm/lib/Analysis/ModuleSummaryAnalysis.cpp | 3 | ||||
-rw-r--r-- | llvm/lib/Analysis/TypeMetadataUtils.cpp | 4 | ||||
-rw-r--r-- | llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 2 | ||||
-rw-r--r-- | llvm/lib/IR/ModuleSummaryIndex.cpp | 8 | ||||
-rw-r--r-- | llvm/lib/LTO/LTO.cpp | 4 | ||||
-rw-r--r-- | llvm/lib/LTO/LTOBackend.cpp | 3 | ||||
-rw-r--r-- | llvm/lib/LTO/LTOCodeGenerator.cpp | 12 | ||||
-rw-r--r-- | llvm/lib/LTO/ThinLTOCodeGenerator.cpp | 6 | ||||
-rw-r--r-- | llvm/lib/Transforms/IPO/LowerTypeTests.cpp | 61 | ||||
-rw-r--r-- | llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp | 8 | ||||
-rw-r--r-- | llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp | 35 | ||||
-rw-r--r-- | llvm/test/LTO/X86/public-type-test.ll | 35 | ||||
-rw-r--r-- | llvm/test/ThinLTO/X86/devirt_vcall_vis_public.ll | 55 | ||||
-rw-r--r-- | llvm/test/ThinLTO/X86/public-type-test.ll | 25 | ||||
-rw-r--r-- | llvm/tools/llvm-lto/llvm-lto.cpp | 7 |
19 files changed, 255 insertions, 33 deletions
diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td index c523e37..d46fa4f 100644 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -1759,6 +1759,12 @@ def int_type_checked_load : DefaultAttrsIntrinsic<[llvm_ptr_ty, llvm_i1_ty], [llvm_ptr_ty, llvm_i32_ty, llvm_metadata_ty], [IntrNoMem, IntrWillReturn]>; +// Test whether a pointer is associated with a type metadata identifier. Used +// for public visibility classes that may later be refined to private +// visibility. +def int_public_type_test : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_ptr_ty, llvm_metadata_ty], + [IntrNoMem, IntrWillReturn, IntrSpeculatable]>; + // Create a branch funnel that implements an indirect call to a limited set of // callees. This needs to be a musttail call. def int_icall_branch_funnel : DefaultAttrsIntrinsic<[], [llvm_vararg_ty], []>; diff --git a/llvm/include/llvm/IR/ModuleSummaryIndex.h b/llvm/include/llvm/IR/ModuleSummaryIndex.h index f1dd299..468773a 100644 --- a/llvm/include/llvm/IR/ModuleSummaryIndex.h +++ b/llvm/include/llvm/IR/ModuleSummaryIndex.h @@ -1122,6 +1122,9 @@ private: /// every summary of a GV is synchronized. bool WithDSOLocalPropagation = false; + /// Indicates that we have whole program visibility. + bool WithWholeProgramVisibility = false; + /// Indicates that summary-based synthetic entry count propagation has run bool HasSyntheticEntryCounts = false; @@ -1280,6 +1283,9 @@ public: bool withDSOLocalPropagation() const { return WithDSOLocalPropagation; } void setWithDSOLocalPropagation() { WithDSOLocalPropagation = true; } + bool withWholeProgramVisibility() const { return WithWholeProgramVisibility; } + void setWithWholeProgramVisibility() { WithWholeProgramVisibility = true; } + bool isReadOnly(const GlobalVarSummary *GVS) const { return WithAttributePropagation && GVS->maybeReadOnly(); } diff --git a/llvm/include/llvm/LTO/legacy/LTOCodeGenerator.h b/llvm/include/llvm/LTO/legacy/LTOCodeGenerator.h index 96f82a9..fdd6ec7 100644 --- a/llvm/include/llvm/LTO/legacy/LTOCodeGenerator.h +++ b/llvm/include/llvm/LTO/legacy/LTOCodeGenerator.h @@ -102,6 +102,9 @@ struct LTOCodeGenerator { void setShouldInternalize(bool Value) { ShouldInternalize = Value; } void setShouldEmbedUselists(bool Value) { ShouldEmbedUselists = Value; } + void setSaveIRBeforeOptPath(std::string Value) { + SaveIRBeforeOptPath = Value; + } /// Restore linkage of globals /// @@ -237,6 +240,7 @@ private: bool ShouldRestoreGlobalsLinkage = false; std::unique_ptr<ToolOutputFile> DiagnosticOutputFile; std::unique_ptr<ToolOutputFile> StatsFile = nullptr; + std::string SaveIRBeforeOptPath; lto::Config Config; }; diff --git a/llvm/include/llvm/Transforms/IPO/WholeProgramDevirt.h b/llvm/include/llvm/Transforms/IPO/WholeProgramDevirt.h index 47c137e..a2296a0 100644 --- a/llvm/include/llvm/Transforms/IPO/WholeProgramDevirt.h +++ b/llvm/include/llvm/Transforms/IPO/WholeProgramDevirt.h @@ -239,7 +239,9 @@ struct VTableSlotSummary { StringRef TypeID; uint64_t ByteOffset; }; - +bool hasWholeProgramVisibility(bool WholeProgramVisibilityEnabledInLTO); +void updatePublicTypeTestCalls(Module &M, + bool WholeProgramVisibilityEnabledInLTO); void updateVCallVisibilityInModule( Module &M, bool WholeProgramVisibilityEnabledInLTO, const DenseSet<GlobalValue::GUID> &DynamicExportSymbols); diff --git a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp index c52b27a..efe6058 100644 --- a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp +++ b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp @@ -164,7 +164,8 @@ static void addIntrinsicToSummary( SetVector<FunctionSummary::ConstVCall> &TypeCheckedLoadConstVCalls, DominatorTree &DT) { switch (CI->getCalledFunction()->getIntrinsicID()) { - case Intrinsic::type_test: { + case Intrinsic::type_test: + case Intrinsic::public_type_test: { auto *TypeMDVal = cast<MetadataAsValue>(CI->getArgOperand(1)); auto *TypeId = dyn_cast<MDString>(TypeMDVal->getMetadata()); if (!TypeId) diff --git a/llvm/lib/Analysis/TypeMetadataUtils.cpp b/llvm/lib/Analysis/TypeMetadataUtils.cpp index 201e647..e128187 100644 --- a/llvm/lib/Analysis/TypeMetadataUtils.cpp +++ b/llvm/lib/Analysis/TypeMetadataUtils.cpp @@ -75,7 +75,9 @@ void llvm::findDevirtualizableCallsForTypeTest( SmallVectorImpl<DevirtCallSite> &DevirtCalls, SmallVectorImpl<CallInst *> &Assumes, const CallInst *CI, DominatorTree &DT) { - assert(CI->getCalledFunction()->getIntrinsicID() == Intrinsic::type_test); + assert(CI->getCalledFunction()->getIntrinsicID() == Intrinsic::type_test || + CI->getCalledFunction()->getIntrinsicID() == + Intrinsic::public_type_test); const Module *M = CI->getParent()->getParent()->getParent(); diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index 1d6c21b..1943b5d 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -7788,7 +7788,7 @@ static Expected<bool> getEnableSplitLTOUnitFlag(BitstreamCursor &Stream, case bitc::FS_FLAGS: { // [flags] uint64_t Flags = Record[0]; // Scan flags. - assert(Flags <= 0x7f && "Unexpected bits in flag"); + assert(Flags <= 0xff && "Unexpected bits in flag"); return Flags & 0x8; } diff --git a/llvm/lib/IR/ModuleSummaryIndex.cpp b/llvm/lib/IR/ModuleSummaryIndex.cpp index 0ca40a6..3e82987 100644 --- a/llvm/lib/IR/ModuleSummaryIndex.cpp +++ b/llvm/lib/IR/ModuleSummaryIndex.cpp @@ -105,11 +105,13 @@ uint64_t ModuleSummaryIndex::getFlags() const { Flags |= 0x20; if (withDSOLocalPropagation()) Flags |= 0x40; + if (withWholeProgramVisibility()) + Flags |= 0x80; return Flags; } void ModuleSummaryIndex::setFlags(uint64_t Flags) { - assert(Flags <= 0x7f && "Unexpected bits in flag"); + assert(Flags <= 0xff && "Unexpected bits in flag"); // 1 bit: WithGlobalValueDeadStripping flag. // Set on combined index only. if (Flags & 0x1) @@ -139,6 +141,10 @@ void ModuleSummaryIndex::setFlags(uint64_t Flags) { // Set on combined index only. if (Flags & 0x40) setWithDSOLocalPropagation(); + // 1 bit: WithWholeProgramVisibility flag. + // Set on combined index only. + if (Flags & 0x80) + setWithWholeProgramVisibility(); } // Collect for the given module the list of function it defines diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp index a9e04ba..cc7be24 100644 --- a/llvm/lib/LTO/LTO.cpp +++ b/llvm/lib/LTO/LTO.cpp @@ -1103,6 +1103,8 @@ Error LTO::runRegularLTO(AddStreamFn AddStream) { updateVCallVisibilityInModule(*RegularLTO.CombinedModule, Conf.HasWholeProgramVisibility, DynamicExportSymbols); + updatePublicTypeTestCalls(*RegularLTO.CombinedModule, + Conf.HasWholeProgramVisibility); if (Conf.PreOptModuleHook && !Conf.PreOptModuleHook(0, *RegularLTO.CombinedModule)) @@ -1482,6 +1484,8 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache, std::set<GlobalValue::GUID> ExportedGUIDs; + if (hasWholeProgramVisibility(Conf.HasWholeProgramVisibility)) + ThinLTO.CombinedIndex.setWithWholeProgramVisibility(); // If allowed, upgrade public vcall visibility to linkage unit visibility in // the summaries before whole program devirtualization below. updateVCallVisibilityInIndex(ThinLTO.CombinedIndex, diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp index e248e58..2e32469 100644 --- a/llvm/lib/LTO/LTOBackend.cpp +++ b/llvm/lib/LTO/LTOBackend.cpp @@ -40,6 +40,7 @@ #include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetMachine.h" +#include "llvm/Transforms/IPO/WholeProgramDevirt.h" #include "llvm/Transforms/Scalar/LoopPassManager.h" #include "llvm/Transforms/Utils/FunctionImportUtils.h" #include "llvm/Transforms/Utils/SplitModule.h" @@ -560,6 +561,8 @@ Error lto::thinBackend(const Config &Conf, unsigned Task, AddStreamFn AddStream, // the module, if applicable. Mod.setPartialSampleProfileRatio(CombinedIndex); + updatePublicTypeTestCalls(Mod, CombinedIndex.withWholeProgramVisibility()); + if (Conf.CodeGenOnly) { codegen(Conf, TM.get(), AddStream, Task, Mod, CombinedIndex); return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile)); diff --git a/llvm/lib/LTO/LTOCodeGenerator.cpp b/llvm/lib/LTO/LTOCodeGenerator.cpp index 2abf249..2f7c485 100644 --- a/llvm/lib/LTO/LTOCodeGenerator.cpp +++ b/llvm/lib/LTO/LTOCodeGenerator.cpp @@ -520,6 +520,8 @@ bool LTOCodeGenerator::optimize() { // linker option in the old LTO API, but this call allows it to be specified // via the internal option. Must be done before WPD invoked via the optimizer // pipeline run below. + updatePublicTypeTestCalls(*MergedModule, + /* WholeProgramVisibilityEnabledInLTO */ false); updateVCallVisibilityInModule(*MergedModule, /* WholeProgramVisibilityEnabledInLTO */ false, // FIXME: This needs linker information via a @@ -539,6 +541,16 @@ bool LTOCodeGenerator::optimize() { // Add an appropriate DataLayout instance for this module... MergedModule->setDataLayout(TargetMach->createDataLayout()); + if (!SaveIRBeforeOptPath.empty()) { + std::error_code EC; + raw_fd_ostream OS(SaveIRBeforeOptPath, EC, sys::fs::OF_None); + if (EC) + report_fatal_error(Twine("Failed to open ") + SaveIRBeforeOptPath + + " to save optimized bitcode\n"); + WriteBitcodeToFile(*MergedModule, OS, + /* ShouldPreserveUseListOrder */ true); + } + ModuleSummaryIndex CombinedIndex(false); TargetMach = createTargetMachine(); if (!opt(Config, TargetMach.get(), 0, *MergedModule, /*IsThinLTO=*/false, diff --git a/llvm/lib/LTO/ThinLTOCodeGenerator.cpp b/llvm/lib/LTO/ThinLTOCodeGenerator.cpp index a1041b3..2c723be 100644 --- a/llvm/lib/LTO/ThinLTOCodeGenerator.cpp +++ b/llvm/lib/LTO/ThinLTOCodeGenerator.cpp @@ -452,6 +452,10 @@ ProcessThinLTOModule(Module &TheModule, ModuleSummaryIndex &Index, bool DisableCodeGen, StringRef SaveTempsDir, bool Freestanding, unsigned OptLevel, unsigned count, bool DebugPassManager) { + // See comment at call to updateVCallVisibilityInIndex() for why + // WholeProgramVisibilityEnabledInLTO is false. + updatePublicTypeTestCalls(TheModule, + /* WholeProgramVisibilityEnabledInLTO */ false); // "Benchmark"-like optimization: single-source case bool SingleModule = (ModuleMap.size() == 1); @@ -1047,6 +1051,8 @@ void ThinLTOCodeGenerator::run() { // Currently there is no support for enabling whole program visibility via a // linker option in the old LTO API, but this call allows it to be specified // via the internal option. Must be done before WPD below. + if (hasWholeProgramVisibility(/* WholeProgramVisibilityEnabledInLTO */ false)) + Index->setWithWholeProgramVisibility(); updateVCallVisibilityInIndex(*Index, /* WholeProgramVisibilityEnabledInLTO */ false, // FIXME: This needs linker information via a diff --git a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp index 6bf25df..e3e4908 100644 --- a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp +++ b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp @@ -1778,35 +1778,48 @@ void LowerTypeTestsModule::replaceDirectCalls(Value *Old, Value *New) { Old->replaceUsesWithIf(New, isDirectCall); } +static void dropTypeTests(Module &M, Function &TypeTestFunc) { + for (Use &U : llvm::make_early_inc_range(TypeTestFunc.uses())) { + auto *CI = cast<CallInst>(U.getUser()); + // Find and erase llvm.assume intrinsics for this llvm.type.test call. + for (Use &CIU : llvm::make_early_inc_range(CI->uses())) + if (auto *Assume = dyn_cast<AssumeInst>(CIU.getUser())) + Assume->eraseFromParent(); + // If the assume was merged with another assume, we might have a use on a + // phi (which will feed the assume). Simply replace the use on the phi + // with "true" and leave the merged assume. + if (!CI->use_empty()) { + assert( + all_of(CI->users(), [](User *U) -> bool { return isa<PHINode>(U); })); + CI->replaceAllUsesWith(ConstantInt::getTrue(M.getContext())); + } + CI->eraseFromParent(); + } +} + bool LowerTypeTestsModule::lower() { Function *TypeTestFunc = M.getFunction(Intrinsic::getName(Intrinsic::type_test)); - if (DropTypeTests && TypeTestFunc) { - for (Use &U : llvm::make_early_inc_range(TypeTestFunc->uses())) { - auto *CI = cast<CallInst>(U.getUser()); - // Find and erase llvm.assume intrinsics for this llvm.type.test call. - for (Use &CIU : llvm::make_early_inc_range(CI->uses())) - if (auto *Assume = dyn_cast<AssumeInst>(CIU.getUser())) - Assume->eraseFromParent(); - // If the assume was merged with another assume, we might have a use on a - // phi (which will feed the assume). Simply replace the use on the phi - // with "true" and leave the merged assume. - if (!CI->use_empty()) { - assert(all_of(CI->users(), - [](User *U) -> bool { return isa<PHINode>(U); })); - CI->replaceAllUsesWith(ConstantInt::getTrue(M.getContext())); - } - CI->eraseFromParent(); + if (DropTypeTests) { + if (TypeTestFunc) + dropTypeTests(M, *TypeTestFunc); + // Normally we'd have already removed all @llvm.public.type.test calls, + // except for in the case where we originally were performing ThinLTO but + // decided not to in the backend. + Function *PublicTypeTestFunc = + M.getFunction(Intrinsic::getName(Intrinsic::public_type_test)); + if (PublicTypeTestFunc) + dropTypeTests(M, *PublicTypeTestFunc); + if (TypeTestFunc || PublicTypeTestFunc) { + // We have deleted the type intrinsics, so we no longer have enough + // information to reason about the liveness of virtual function pointers + // in GlobalDCE. + for (GlobalVariable &GV : M.globals()) + GV.eraseMetadata(LLVMContext::MD_vcall_visibility); + return true; } - - // We have deleted the type intrinsics, so we no longer have enough - // information to reason about the liveness of virtual function pointers - // in GlobalDCE. - for (GlobalVariable &GV : M.globals()) - GV.eraseMetadata(LLVMContext::MD_vcall_visibility); - - return true; + return false; } // If only some of the modules were split, we cannot correctly perform diff --git a/llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp b/llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp index a360a76..ef7af55 100644 --- a/llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp +++ b/llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp @@ -132,6 +132,14 @@ void promoteTypeIds(Module &M, StringRef ModuleId) { } } + if (Function *PublicTypeTestFunc = + M.getFunction(Intrinsic::getName(Intrinsic::public_type_test))) { + for (const Use &U : PublicTypeTestFunc->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()) { diff --git a/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp b/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp index ad00c11..18efe99 100644 --- a/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp +++ b/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp @@ -773,15 +773,14 @@ PreservedAnalyses WholeProgramDevirtPass::run(Module &M, return PreservedAnalyses::none(); } +namespace llvm { // Enable whole program visibility if enabled by client (e.g. linker) or // internal option, and not force disabled. -static bool hasWholeProgramVisibility(bool WholeProgramVisibilityEnabledInLTO) { +bool hasWholeProgramVisibility(bool WholeProgramVisibilityEnabledInLTO) { return (WholeProgramVisibilityEnabledInLTO || WholeProgramVisibility) && !DisableWholeProgramVisibility; } -namespace llvm { - /// If whole program visibility asserted, then upgrade all public vcall /// visibility metadata on vtable definitions to linkage unit visibility in /// Module IR (for regular or hybrid LTO). @@ -790,7 +789,7 @@ void updateVCallVisibilityInModule( const DenseSet<GlobalValue::GUID> &DynamicExportSymbols) { if (!hasWholeProgramVisibility(WholeProgramVisibilityEnabledInLTO)) return; - for (GlobalVariable &GV : M.globals()) + for (GlobalVariable &GV : M.globals()) { // Add linkage unit visibility to any variable with type metadata, which are // the vtable definitions. We won't have an existing vcall_visibility // metadata on vtable definitions with public visibility. @@ -800,6 +799,34 @@ void updateVCallVisibilityInModule( // linker, as we have no information on their eventual use. !DynamicExportSymbols.count(GV.getGUID())) GV.setVCallVisibilityMetadata(GlobalObject::VCallVisibilityLinkageUnit); + } +} + +void updatePublicTypeTestCalls(Module &M, + bool WholeProgramVisibilityEnabledInLTO) { + Function *PublicTypeTestFunc = + M.getFunction(Intrinsic::getName(Intrinsic::public_type_test)); + if (!PublicTypeTestFunc) + return; + if (hasWholeProgramVisibility(WholeProgramVisibilityEnabledInLTO)) { + Function *TypeTestFunc = + Intrinsic::getDeclaration(&M, Intrinsic::type_test); + for (Use &U : make_early_inc_range(PublicTypeTestFunc->uses())) { + auto *CI = cast<CallInst>(U.getUser()); + auto *NewCI = CallInst::Create( + TypeTestFunc, {CI->getArgOperand(0), CI->getArgOperand(1)}, None, "", + CI); + CI->replaceAllUsesWith(NewCI); + CI->eraseFromParent(); + } + } else { + auto *True = ConstantInt::getTrue(M.getContext()); + for (Use &U : make_early_inc_range(PublicTypeTestFunc->uses())) { + auto *CI = cast<CallInst>(U.getUser()); + CI->replaceAllUsesWith(True); + CI->eraseFromParent(); + } + } } /// If whole program visibility asserted, then upgrade all public vcall diff --git a/llvm/test/LTO/X86/public-type-test.ll b/llvm/test/LTO/X86/public-type-test.ll new file mode 100644 index 0000000..4cb2f6d --- /dev/null +++ b/llvm/test/LTO/X86/public-type-test.ll @@ -0,0 +1,35 @@ +; Test to ensure that the LTO API (legacy and new) lowers @llvm.public.type.test. + +; RUN: llvm-as < %s > %t1 +; RUN: llvm-lto -exported-symbol=_main %t1 -o %t2 --lto-save-before-opt --whole-program-visibility +; RUN: llvm-dis -o - %t2.0.preopt.bc | FileCheck %s --check-prefix=HIDDEN +; RUN: llvm-lto -exported-symbol=_main %t1 -o %t2 --lto-save-before-opt +; RUN: llvm-dis -o - %t2.0.preopt.bc | FileCheck %s --check-prefix=PUBLIC + +; RUN: llvm-lto2 run %t1 -save-temps -pass-remarks=. \ +; RUN: -whole-program-visibility \ +; RUN: -o %t2 \ +; RUN: -r=%t1,_main,px +; RUN: llvm-dis %t2.0.0.preopt.bc -o - | FileCheck %s --check-prefix=HIDDEN +; RUN: llvm-lto2 run %t1 -save-temps -pass-remarks=. \ +; RUN: -o %t2 \ +; RUN: -r=%t1,_main,px +; RUN: llvm-dis %t2.0.0.preopt.bc -o - | FileCheck %s --check-prefix=PUBLIC + +; PUBLIC-NOT: call {{.*}}@llvm.public.type.test +; PUBLIC-NOT: call {{.*}}@llvm.type.test +; HIDDEN-NOT: call {{.*}}@llvm.public.type.test +; HIDDEN: call {{.*}}@llvm.type.test + +target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.9" + +define i32 @main(ptr %vtable) { +entry: + %p = call i1 @llvm.public.type.test(ptr %vtable, metadata !"_ZTS1A") + call void @llvm.assume(i1 %p) + ret i32 0 +} + +declare void @llvm.assume(i1) +declare i1 @llvm.public.type.test(ptr, metadata) diff --git a/llvm/test/ThinLTO/X86/devirt_vcall_vis_public.ll b/llvm/test/ThinLTO/X86/devirt_vcall_vis_public.ll index 4451d17..7f8e612 100644 --- a/llvm/test/ThinLTO/X86/devirt_vcall_vis_public.ll +++ b/llvm/test/ThinLTO/X86/devirt_vcall_vis_public.ll @@ -10,6 +10,7 @@ ; RUN: -whole-program-visibility \ ; RUN: -o %t3 \ ; RUN: -r=%t2.o,test,px \ +; RUN: -r=%t2.o,test_public,px \ ; RUN: -r=%t2.o,_ZN1A1nEi,p \ ; RUN: -r=%t2.o,_ZN1B1fEi,p \ ; RUN: -r=%t2.o,_ZN1C1fEi,p \ @@ -17,6 +18,7 @@ ; RUN: -r=%t2.o,_ZTV1B,px \ ; RUN: -r=%t2.o,_ZTV1C,px \ ; RUN: -r=%t2.o,_ZTV1D,px 2>&1 | FileCheck %s --check-prefix=REMARK +; RUN: llvm-dis %t3.1.0.preopt.bc -o - | FileCheck %s --check-prefix=CHECK-TT ; RUN: llvm-dis %t3.1.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR ; Hybrid WPD @@ -26,6 +28,7 @@ ; RUN: -whole-program-visibility \ ; RUN: -o %t3 \ ; RUN: -r=%t.o,test,px \ +; RUN: -r=%t.o,test_public,px \ ; RUN: -r=%t.o,_ZN1A1nEi,p \ ; RUN: -r=%t.o,_ZN1B1fEi,p \ ; RUN: -r=%t.o,_ZN1C1fEi,p \ @@ -40,6 +43,7 @@ ; RUN: -r=%t.o,_ZTV1B,px \ ; RUN: -r=%t.o,_ZTV1C,px \ ; RUN: -r=%t.o,_ZTV1D,px 2>&1 | FileCheck %s --check-prefix=REMARK --dump-input=fail +; RUN: llvm-dis %t3.1.0.preopt.bc -o - | FileCheck %s --check-prefix=CHECK-TT ; RUN: llvm-dis %t3.1.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR ; Regular LTO WPD @@ -48,6 +52,7 @@ ; RUN: -whole-program-visibility \ ; RUN: -o %t5 \ ; RUN: -r=%t4.o,test,px \ +; RUN: -r=%t4.o,test_public,px \ ; RUN: -r=%t4.o,_ZN1A1nEi,p \ ; RUN: -r=%t4.o,_ZN1B1fEi,p \ ; RUN: -r=%t4.o,_ZN1C1fEi,p \ @@ -55,10 +60,13 @@ ; RUN: -r=%t4.o,_ZTV1B,px \ ; RUN: -r=%t4.o,_ZTV1C,px \ ; RUN: -r=%t4.o,_ZTV1D,px 2>&1 | FileCheck %s --check-prefix=REMARK +; RUN: llvm-dis %t5.0.0.preopt.bc -o - | FileCheck %s --check-prefix=CHECK-TT ; RUN: llvm-dis %t5.0.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR ; REMARK-DAG: single-impl: devirtualized a call to _ZN1A1nEi ; REMARK-DAG: single-impl: devirtualized a call to _ZN1D1mEi +; REMARK-DAG: single-impl: devirtualized a call to _ZN1A1nEi +; REMARK-DAG: single-impl: devirtualized a call to _ZN1D1mEi ; Try everything again but without -whole-program-visibility to confirm ; WPD fails @@ -67,6 +75,7 @@ ; RUN: llvm-lto2 run %t2.o -save-temps -pass-remarks=. \ ; RUN: -o %t3 \ ; RUN: -r=%t2.o,test,px \ +; RUN: -r=%t2.o,test_public,px \ ; RUN: -r=%t2.o,_ZN1A1nEi,p \ ; RUN: -r=%t2.o,_ZN1B1fEi,p \ ; RUN: -r=%t2.o,_ZN1C1fEi,p \ @@ -74,12 +83,14 @@ ; RUN: -r=%t2.o,_ZTV1B,px \ ; RUN: -r=%t2.o,_ZTV1C,px \ ; RUN: -r=%t2.o,_ZTV1D,px 2>&1 | FileCheck %s --implicit-check-not single-impl --allow-empty +; RUN: llvm-dis %t3.1.0.preopt.bc -o - | FileCheck %s --check-prefix=CHECK-TT ; RUN: llvm-dis %t3.1.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR ; Hybrid WPD ; RUN: llvm-lto2 run %t.o -save-temps -pass-remarks=. \ ; RUN: -o %t3 \ ; RUN: -r=%t.o,test,px \ +; RUN: -r=%t.o,test_public,px \ ; RUN: -r=%t.o,_ZN1A1nEi,p \ ; RUN: -r=%t.o,_ZN1B1fEi,p \ ; RUN: -r=%t.o,_ZN1C1fEi,p \ @@ -94,12 +105,14 @@ ; RUN: -r=%t.o,_ZTV1B,px \ ; RUN: -r=%t.o,_ZTV1C,px \ ; RUN: -r=%t.o,_ZTV1D,px 2>&1 | FileCheck %s --implicit-check-not single-impl --allow-empty +; RUN: llvm-dis %t3.1.0.preopt.bc -o - | FileCheck %s --check-prefix=CHECK-TT ; RUN: llvm-dis %t3.1.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR ; Regular LTO WPD ; RUN: llvm-lto2 run %t4.o -save-temps -pass-remarks=. \ ; RUN: -o %t5 \ ; RUN: -r=%t4.o,test,px \ +; RUN: -r=%t4.o,test_public,px \ ; RUN: -r=%t4.o,_ZN1A1nEi,p \ ; RUN: -r=%t4.o,_ZN1B1fEi,p \ ; RUN: -r=%t4.o,_ZN1C1fEi,p \ @@ -107,6 +120,7 @@ ; RUN: -r=%t4.o,_ZTV1B,px \ ; RUN: -r=%t4.o,_ZTV1C,px \ ; RUN: -r=%t4.o,_ZTV1D,px 2>&1 | FileCheck %s --implicit-check-not single-impl --allow-empty +; RUN: llvm-dis %t5.0.0.preopt.bc -o - | FileCheck %s --check-prefix=CHECK-TT ; RUN: llvm-dis %t5.0.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR ; Try index-based WPD again with both -whole-program-visibility and @@ -117,6 +131,7 @@ ; RUN: -disable-whole-program-visibility \ ; RUN: -o %t3 \ ; RUN: -r=%t2.o,test,px \ +; RUN: -r=%t2.o,test_public,px \ ; RUN: -r=%t2.o,_ZN1A1nEi,p \ ; RUN: -r=%t2.o,_ZN1B1fEi,p \ ; RUN: -r=%t2.o,_ZN1C1fEi,p \ @@ -124,8 +139,11 @@ ; RUN: -r=%t2.o,_ZTV1B,px \ ; RUN: -r=%t2.o,_ZTV1C,px \ ; RUN: -r=%t2.o,_ZTV1D,px 2>&1 | FileCheck %s --implicit-check-not single-impl --allow-empty +; RUN: llvm-dis %t3.1.0.preopt.bc -o - | FileCheck %s --check-prefix=CHECK-TT ; RUN: llvm-dis %t3.1.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-NODEVIRT-IR +; CHECK-TT-NOT: call {{.*}}@llvm.public.type.test + 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-grtev4-linux-gnu" @@ -175,7 +193,44 @@ entry: ; CHECK-IR-LABEL: ret i32 ; CHECK-IR-LABEL: } +; CHECK-IR-LABEL: define i32 @test_public +define i32 @test_public(ptr %obj, ptr %obj2, i32 %a) { +entry: + %vtable = load ptr, ptr %obj + %p = call i1 @llvm.public.type.test(ptr %vtable, metadata !"_ZTS1A") + call void @llvm.assume(i1 %p) + %fptrptr = getelementptr ptr, ptr %vtable, i32 1 + %fptr1 = load ptr, ptr %fptrptr, align 8 + + ; Check that the call was devirtualized. + ; CHECK-IR: %call = tail call i32 @_ZN1A1nEi + ; CHECK-NODEVIRT-IR: %call = tail call i32 %fptr1 + %call = tail call i32 %fptr1(ptr nonnull %obj, i32 %a) + + %fptr22 = load ptr, ptr %vtable, align 8 + + ; We still have to call it as virtual. + ; CHECK-IR: %call3 = tail call i32 %fptr22 + ; CHECK-NODEVIRT-IR: %call3 = tail call i32 %fptr22 + %call3 = tail call i32 %fptr22(ptr nonnull %obj, i32 %call) + + %vtable2 = load ptr, ptr %obj2 + %p2 = call i1 @llvm.public.type.test(ptr %vtable2, metadata !4) + call void @llvm.assume(i1 %p2) + + %fptr33 = load ptr, ptr %vtable2, align 8 + + ; Check that the call was devirtualized. + ; CHECK-IR: %call4 = tail call i32 @_ZN1D1mEi + ; CHECK-NODEVIRT-IR: %call4 = tail call i32 %fptr33 + %call4 = tail call i32 %fptr33(ptr nonnull %obj2, i32 %call3) + ret i32 %call4 +} +; CHECK-IR-LABEL: ret i32 +; CHECK-IR-LABEL: } + declare i1 @llvm.type.test(ptr, metadata) +declare i1 @llvm.public.type.test(ptr, metadata) declare void @llvm.assume(i1) define i32 @_ZN1B1fEi(ptr %this, i32 %a) #0 { diff --git a/llvm/test/ThinLTO/X86/public-type-test.ll b/llvm/test/ThinLTO/X86/public-type-test.ll new file mode 100644 index 0000000..5a4e19e --- /dev/null +++ b/llvm/test/ThinLTO/X86/public-type-test.ll @@ -0,0 +1,25 @@ +; Test to ensure that the legacy LTO API lowers @llvm.public.type.test. + +; RUN: opt -module-summary %s -o %t.bc +; RUN: llvm-lto --thinlto-action=run -exported-symbol=_main %t.bc --thinlto-save-temps=%t2 +; RUN: llvm-dis -o - %t20.2.internalized.bc | FileCheck %s --check-prefix=PUBLIC +; RUN: llvm-lto --thinlto-action=run -exported-symbol=_main %t.bc --thinlto-save-temps=%t2 --whole-program-visibility +; RUN: llvm-dis -o - %t20.2.internalized.bc | FileCheck %s --check-prefix=HIDDEN + +; PUBLIC-NOT: call {{.*}}@llvm.public.type.test +; PUBLIC-NOT: call {{.*}}@llvm.type.test +; HIDDEN-NOT: call {{.*}}@llvm.public.type.test +; HIDDEN: call {{.*}}@llvm.type.test + +target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.9" + +define i32 @main(ptr %vtable) { +entry: + %p = call i1 @llvm.public.type.test(ptr %vtable, metadata !"_ZTS1A") + call void @llvm.assume(i1 %p) + ret i32 0 +} + +declare void @llvm.assume(i1) +declare i1 @llvm.public.type.test(ptr, metadata) diff --git a/llvm/tools/llvm-lto/llvm-lto.cpp b/llvm/tools/llvm-lto/llvm-lto.cpp index c826661..64c8c19 100644 --- a/llvm/tools/llvm-lto/llvm-lto.cpp +++ b/llvm/tools/llvm-lto/llvm-lto.cpp @@ -261,6 +261,10 @@ static cl::opt<bool> cl::desc("Print pass management debugging information"), cl::cat(LTOCategory)); +static cl::opt<bool> + LTOSaveBeforeOpt("lto-save-before-opt", cl::init(false), + cl::desc("Save the IR before running optimizations")); + namespace { struct ModuleInfo { @@ -1069,6 +1073,9 @@ int main(int argc, char **argv) { CodeGen.setFileType(*FT); if (!OutputFilename.empty()) { + if (LTOSaveBeforeOpt) + CodeGen.setSaveIRBeforeOptPath(OutputFilename + ".0.preopt.bc"); + if (SaveLinkedModuleFile) { std::string ModuleFilename = OutputFilename; ModuleFilename += ".linked.bc"; |