diff --git a/llvm/include/llvm/Transforms/IPO/LowerTypeTests.h b/llvm/include/llvm/Transforms/IPO/LowerTypeTests.h index 02adcd8bfd45d..40f2d2c4fadf7 100644 --- a/llvm/include/llvm/Transforms/IPO/LowerTypeTests.h +++ b/llvm/include/llvm/Transforms/IPO/LowerTypeTests.h @@ -223,6 +223,11 @@ class LowerTypeTestsPass : public PassInfoMixin { PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); }; +class SimplifyTypeTestsPass : public PassInfoMixin { +public: + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); +}; + } // end namespace llvm #endif // LLVM_TRANSFORMS_IPO_LOWERTYPETESTS_H diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp index f3654600c5abb..70b4eb68ffa2f 100644 --- a/llvm/lib/Passes/PassBuilderPipelines.cpp +++ b/llvm/lib/Passes/PassBuilderPipelines.cpp @@ -1288,6 +1288,9 @@ PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level, // and argument promotion. MPM.addPass(DeadArgumentEliminationPass()); + if (Phase == ThinOrFullLTOPhase::ThinLTOPostLink) + MPM.addPass(SimplifyTypeTestsPass()); + if (Phase != ThinOrFullLTOPhase::ThinLTOPreLink) MPM.addPass(CoroCleanupPass()); diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index be93a7b1f5ba6..cfbf52ffcee61 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -101,6 +101,7 @@ MODULE_PASS("jmc-instrumenter", JMCInstrumenterPass()) MODULE_PASS("lower-emutls", LowerEmuTLSPass()) MODULE_PASS("lower-global-dtors", LowerGlobalDtorsPass()) MODULE_PASS("lower-ifunc", LowerIFuncPass()) +MODULE_PASS("simplify-type-tests", SimplifyTypeTestsPass()) MODULE_PASS("lowertypetests", LowerTypeTestsPass()) MODULE_PASS("fatlto-cleanup", FatLtoCleanup()) MODULE_PASS("pgo-force-function-attrs", PGOForceFunctionAttrsPass(PGOOpt ? PGOOpt->ColdOptType : PGOOptions::ColdFuncOpt::Default)) diff --git a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp index 63f8a6e1b6d44..26238acbb3f4d 100644 --- a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp +++ b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp @@ -19,6 +19,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/EquivalenceClasses.h" #include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" @@ -2478,3 +2479,76 @@ PreservedAnalyses LowerTypeTestsPass::run(Module &M, return PreservedAnalyses::all(); return PreservedAnalyses::none(); } + +PreservedAnalyses SimplifyTypeTestsPass::run(Module &M, + ModuleAnalysisManager &AM) { + bool Changed = false; + // Figure out whether inlining has exposed a constant address to a lowered + // type test, and remove the test if so and the address is known to pass the + // test. Unfortunately this pass ends up needing to reverse engineer what + // LowerTypeTests did; this is currently inherent to the design of ThinLTO + // importing where LowerTypeTests needs to run at the start. + for (auto &GV : M.globals()) { + if (!GV.getName().starts_with("__typeid_") || + !GV.getName().ends_with("_global_addr")) + continue; + auto *MD = MDString::get(M.getContext(), + GV.getName().substr(9, GV.getName().size() - 21)); + auto MaySimplifyPtr = [&](Value *Ptr) { + if (auto *GV = dyn_cast(Ptr)) + if (auto *CFIGV = M.getNamedValue((GV->getName() + ".cfi").str())) + Ptr = CFIGV; + return isKnownTypeIdMember(MD, M.getDataLayout(), Ptr, 0); + }; + auto MaySimplifyInt = [&](Value *Op) { + auto *PtrAsInt = dyn_cast(Op); + if (!PtrAsInt || PtrAsInt->getOpcode() != Instruction::PtrToInt) + return false; + return MaySimplifyPtr(PtrAsInt->getOperand(0)); + }; + for (User *U : make_early_inc_range(GV.users())) { + if (auto *CI = dyn_cast(U)) { + if (CI->getPredicate() == CmpInst::ICMP_EQ && + MaySimplifyPtr(CI->getOperand(0))) { + // This is an equality comparison (TypeTestResolution::Single case in + // lowerTypeTestCall). In this case we just replace the comparison + // with true. + CI->replaceAllUsesWith(ConstantInt::getTrue(M.getContext())); + CI->eraseFromParent(); + Changed = true; + } + } + auto *CE = dyn_cast(U); + if (!CE || CE->getOpcode() != Instruction::PtrToInt) + continue; + for (Use &U : make_early_inc_range(CE->uses())) { + auto *CE = dyn_cast(U.getUser()); + if (U.getOperandNo() == 1 && CE && + CE->getOpcode() == Instruction::Sub && + MaySimplifyInt(CE->getOperand(0))) { + // This is a computation of PtrOffset as generated by + // LowerTypeTestsModule::lowerTypeTestCall above. If + // isKnownTypeIdMember passes we just pretend it evaluated to 0. This + // should cause later passes to remove the range and alignment checks. + // The bitset checks won't be removed but those are uncommon. + CE->replaceAllUsesWith(ConstantInt::get(CE->getType(), 0)); + Changed = true; + } + auto *CI = dyn_cast(U.getUser()); + if (U.getOperandNo() == 1 && CI && + CI->getPredicate() == CmpInst::ICMP_EQ && + MaySimplifyInt(CI->getOperand(0))) { + // This is an equality comparison. Unlike in the case above it + // remained as an integer compare. + CI->replaceAllUsesWith(ConstantInt::getTrue(M.getContext())); + CI->eraseFromParent(); + Changed = true; + } + } + } + } + + if (!Changed) + return PreservedAnalyses::all(); + return PreservedAnalyses::none(); +} diff --git a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll index ed13402e1c4b1..62bb02d9b3c40 100644 --- a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll @@ -159,6 +159,7 @@ ; CHECK-O-NEXT: Invalidating analysis: ShouldNotRunFunctionPassesAnalysis ; CHECK-O-NEXT: Invalidating analysis: InlineAdvisorAnalysis ; CHECK-O-NEXT: Running pass: DeadArgumentEliminationPass +; CHECK-O-NEXT: Running pass: SimplifyTypeTestsPass ; CHECK-O-NEXT: Running pass: CoroCleanupPass ; CHECK-POSTLINK-O-NEXT: Running pass: GlobalOptPass ; CHECK-POSTLINK-O-NEXT: Running pass: GlobalDCEPass diff --git a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll index c82c34f7ff01e..0da7a9f73bdce 100644 --- a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll @@ -143,6 +143,7 @@ ; CHECK-O-NEXT: Invalidating analysis: ShouldNotRunFunctionPassesAnalysis ; CHECK-O-NEXT: Invalidating analysis: InlineAdvisorAnalysis ; CHECK-O-NEXT: Running pass: DeadArgumentEliminationPass +; CHECK-O-NEXT: Running pass: SimplifyTypeTestsPass ; CHECK-O-NEXT: Running pass: CoroCleanupPass ; CHECK-O-NEXT: Running pass: GlobalOptPass ; CHECK-O-NEXT: Running pass: GlobalDCEPass diff --git a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll index d375747547d61..38b7890682783 100644 --- a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll @@ -152,6 +152,7 @@ ; CHECK-O-NEXT: Invalidating analysis: ShouldNotRunFunctionPassesAnalysis ; CHECK-O-NEXT: Invalidating analysis: InlineAdvisorAnalysis ; CHECK-O-NEXT: Running pass: DeadArgumentEliminationPass +; CHECK-O-NEXT: Running pass: SimplifyTypeTestsPass ; CHECK-O-NEXT: Running pass: CoroCleanupPass ; CHECK-O-NEXT: Running pass: GlobalOptPass ; CHECK-O-NEXT: Running pass: GlobalDCEPass diff --git a/llvm/test/Transforms/SimplifyTypeTests/basic.ll b/llvm/test/Transforms/SimplifyTypeTests/basic.ll new file mode 100644 index 0000000000000..a649f6936da1b --- /dev/null +++ b/llvm/test/Transforms/SimplifyTypeTests/basic.ll @@ -0,0 +1,40 @@ +; RUN: opt -S %s -passes=simplify-type-tests | FileCheck %s +; RUN: sed -e 's/"_ZTSFvvE"/"wrongtype"/g' %s | opt -S -passes=simplify-type-tests | FileCheck --check-prefix=WRONGTYPE %s + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@__typeid__ZTSFvvE_global_addr = external hidden global [0 x i8], code_model "small" + +define void @_Z2fpv.cfi() !type !0 { + ret void +} + +define i64 @main() { + %1 = icmp eq ptr @_Z2fpv, @__typeid__ZTSFvvE_global_addr + ; CHECK: br i1 true + ; WRONGTYPE: br i1 % + br i1 %1, label %3, label %2 + +2: + tail call void @llvm.ubsantrap(i8 2) + unreachable + +3: + ; CHECK: br i1 true + ; WRONGTYPE: br i1 % + %c = icmp eq i64 ptrtoint (ptr @_Z2fpv to i64), ptrtoint (ptr @__typeid__ZTSFvvE_global_addr to i64) + br i1 %c, label %4, label %2 + +4: + tail call void @_Z2fpv() + ; CHECK: ret i64 0 + ; WRONGTYPE: ret i64 sub + ret i64 sub (i64 ptrtoint (ptr @_Z2fpv to i64), i64 ptrtoint (ptr @__typeid__ZTSFvvE_global_addr to i64)) +} + +declare void @llvm.ubsantrap(i8 immarg) + +declare hidden void @_Z2fpv() + +!0 = !{i64 0, !"_ZTSFvvE"}