aboutsummaryrefslogtreecommitdiff
path: root/llvm
diff options
context:
space:
mode:
authorArthur Eubanks <aeubanks@google.com>2022-06-30 15:18:04 -0700
committerArthur Eubanks <aeubanks@google.com>2022-07-26 08:01:08 -0700
commit2eade1dba4a8d6e1c6867e9127bcd88cf4e55976 (patch)
tree080ccfec588a3231d14022bc922f5c05e9a11adb /llvm
parente43621b09c9741d2e3a8a3d8ed216699af5ff8ba (diff)
downloadllvm-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.td6
-rw-r--r--llvm/include/llvm/IR/ModuleSummaryIndex.h6
-rw-r--r--llvm/include/llvm/LTO/legacy/LTOCodeGenerator.h4
-rw-r--r--llvm/include/llvm/Transforms/IPO/WholeProgramDevirt.h4
-rw-r--r--llvm/lib/Analysis/ModuleSummaryAnalysis.cpp3
-rw-r--r--llvm/lib/Analysis/TypeMetadataUtils.cpp4
-rw-r--r--llvm/lib/Bitcode/Reader/BitcodeReader.cpp2
-rw-r--r--llvm/lib/IR/ModuleSummaryIndex.cpp8
-rw-r--r--llvm/lib/LTO/LTO.cpp4
-rw-r--r--llvm/lib/LTO/LTOBackend.cpp3
-rw-r--r--llvm/lib/LTO/LTOCodeGenerator.cpp12
-rw-r--r--llvm/lib/LTO/ThinLTOCodeGenerator.cpp6
-rw-r--r--llvm/lib/Transforms/IPO/LowerTypeTests.cpp61
-rw-r--r--llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp8
-rw-r--r--llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp35
-rw-r--r--llvm/test/LTO/X86/public-type-test.ll35
-rw-r--r--llvm/test/ThinLTO/X86/devirt_vcall_vis_public.ll55
-rw-r--r--llvm/test/ThinLTO/X86/public-type-test.ll25
-rw-r--r--llvm/tools/llvm-lto/llvm-lto.cpp7
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";