Skip to content

Commit 3fa231f

Browse files
authored
Add SimplifyTypeTests pass.
This pass figures 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. Reviewers: teresajohnson Reviewed By: teresajohnson Pull Request: #141327
1 parent d1b0b4b commit 3fa231f

File tree

8 files changed

+152
-0
lines changed

8 files changed

+152
-0
lines changed

llvm/include/llvm/Transforms/IPO/LowerTypeTests.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,11 @@ class LowerTypeTestsPass : public PassInfoMixin<LowerTypeTestsPass> {
223223
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
224224
};
225225

226+
class SimplifyTypeTestsPass : public PassInfoMixin<SimplifyTypeTestsPass> {
227+
public:
228+
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
229+
};
230+
226231
} // end namespace llvm
227232

228233
#endif // LLVM_TRANSFORMS_IPO_LOWERTYPETESTS_H

llvm/lib/Passes/PassBuilderPipelines.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,6 +1287,9 @@ PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level,
12871287
// and argument promotion.
12881288
MPM.addPass(DeadArgumentEliminationPass());
12891289

1290+
if (Phase == ThinOrFullLTOPhase::ThinLTOPostLink)
1291+
MPM.addPass(SimplifyTypeTestsPass());
1292+
12901293
if (Phase != ThinOrFullLTOPhase::ThinLTOPreLink)
12911294
MPM.addPass(CoroCleanupPass());
12921295

llvm/lib/Passes/PassRegistry.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ MODULE_PASS("jmc-instrumenter", JMCInstrumenterPass())
101101
MODULE_PASS("lower-emutls", LowerEmuTLSPass())
102102
MODULE_PASS("lower-global-dtors", LowerGlobalDtorsPass())
103103
MODULE_PASS("lower-ifunc", LowerIFuncPass())
104+
MODULE_PASS("simplify-type-tests", SimplifyTypeTestsPass())
104105
MODULE_PASS("lowertypetests", LowerTypeTestsPass())
105106
MODULE_PASS("fatlto-cleanup", FatLtoCleanup())
106107
MODULE_PASS("pgo-force-function-attrs", PGOForceFunctionAttrsPass(PGOOpt ? PGOOpt->ColdOptType : PGOOptions::ColdFuncOpt::Default))

llvm/lib/Transforms/IPO/LowerTypeTests.cpp

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@
1919
#include "llvm/ADT/DenseMap.h"
2020
#include "llvm/ADT/EquivalenceClasses.h"
2121
#include "llvm/ADT/PointerUnion.h"
22+
#include "llvm/ADT/STLExtras.h"
2223
#include "llvm/ADT/SetVector.h"
2324
#include "llvm/ADT/SmallVector.h"
2425
#include "llvm/ADT/Statistic.h"
2526
#include "llvm/ADT/StringRef.h"
2627
#include "llvm/ADT/TinyPtrVector.h"
28+
#include "llvm/Analysis/LoopInfo.h"
29+
#include "llvm/Analysis/PostDominators.h"
2730
#include "llvm/Analysis/TargetTransformInfo.h"
2831
#include "llvm/Analysis/TypeMetadataUtils.h"
2932
#include "llvm/Analysis/ValueTracking.h"
@@ -2472,3 +2475,94 @@ PreservedAnalyses LowerTypeTestsPass::run(Module &M,
24722475
return PreservedAnalyses::all();
24732476
return PreservedAnalyses::none();
24742477
}
2478+
2479+
PreservedAnalyses SimplifyTypeTestsPass::run(Module &M,
2480+
ModuleAnalysisManager &AM) {
2481+
bool Changed = false;
2482+
// Figure out whether inlining has exposed a constant address to a lowered
2483+
// type test, and remove the test if so and the address is known to pass the
2484+
// test. Unfortunately this pass ends up needing to reverse engineer what
2485+
// LowerTypeTests did; this is currently inherent to the design of ThinLTO
2486+
// importing where LowerTypeTests needs to run at the start.
2487+
//
2488+
// We look for things like:
2489+
//
2490+
// sub (i64 ptrtoint (ptr @_Z2fpv to i64), i64 ptrtoint (ptr
2491+
// @__typeid__ZTSFvvE_global_addr to i64))
2492+
//
2493+
// which gets replaced with 0 if _Z2fpv (more specifically _Z2fpv.cfi, the
2494+
// function referred to by the jump table) is a member of the type _ZTSFvv, as
2495+
// well as things like
2496+
//
2497+
// icmp eq ptr @_Z2fpv, @__typeid__ZTSFvvE_global_addr
2498+
//
2499+
// which gets replaced with true if _Z2fpv is a member.
2500+
for (auto &GV : M.globals()) {
2501+
if (!GV.getName().starts_with("__typeid_") ||
2502+
!GV.getName().ends_with("_global_addr"))
2503+
continue;
2504+
// __typeid_foo_global_addr -> foo
2505+
auto *MD = MDString::get(M.getContext(),
2506+
GV.getName().substr(9, GV.getName().size() - 21));
2507+
auto MaySimplifyPtr = [&](Value *Ptr) {
2508+
if (auto *GV = dyn_cast<GlobalValue>(Ptr))
2509+
if (auto *CFIGV = M.getNamedValue((GV->getName() + ".cfi").str()))
2510+
Ptr = CFIGV;
2511+
return isKnownTypeIdMember(MD, M.getDataLayout(), Ptr, 0);
2512+
};
2513+
auto MaySimplifyInt = [&](Value *Op) {
2514+
auto *PtrAsInt = dyn_cast<ConstantExpr>(Op);
2515+
if (!PtrAsInt || PtrAsInt->getOpcode() != Instruction::PtrToInt)
2516+
return false;
2517+
return MaySimplifyPtr(PtrAsInt->getOperand(0));
2518+
};
2519+
for (User *U : make_early_inc_range(GV.users())) {
2520+
if (auto *CI = dyn_cast<ICmpInst>(U)) {
2521+
if (CI->getPredicate() == CmpInst::ICMP_EQ &&
2522+
MaySimplifyPtr(CI->getOperand(0))) {
2523+
// This is an equality comparison (TypeTestResolution::Single case in
2524+
// lowerTypeTestCall). In this case we just replace the comparison
2525+
// with true.
2526+
CI->replaceAllUsesWith(ConstantInt::getTrue(M.getContext()));
2527+
CI->eraseFromParent();
2528+
Changed = true;
2529+
}
2530+
}
2531+
auto *CE = dyn_cast<ConstantExpr>(U);
2532+
if (!CE || CE->getOpcode() != Instruction::PtrToInt)
2533+
continue;
2534+
for (Use &U : make_early_inc_range(CE->uses())) {
2535+
auto *CE = dyn_cast<ConstantExpr>(U.getUser());
2536+
if (U.getOperandNo() == 1 && CE &&
2537+
CE->getOpcode() == Instruction::Sub &&
2538+
MaySimplifyInt(CE->getOperand(0))) {
2539+
// This is a computation of PtrOffset as generated by
2540+
// LowerTypeTestsModule::lowerTypeTestCall above. If
2541+
// isKnownTypeIdMember passes we just pretend it evaluated to 0. This
2542+
// should cause later passes to remove the range and alignment checks.
2543+
// The bitset checks won't be removed but those are uncommon.
2544+
CE->replaceAllUsesWith(ConstantInt::get(CE->getType(), 0));
2545+
Changed = true;
2546+
}
2547+
auto *CI = dyn_cast<ICmpInst>(U.getUser());
2548+
if (U.getOperandNo() == 1 && CI &&
2549+
CI->getPredicate() == CmpInst::ICMP_EQ &&
2550+
MaySimplifyInt(CI->getOperand(0))) {
2551+
// This is an equality comparison. Unlike in the case above it
2552+
// remained as an integer compare.
2553+
CI->replaceAllUsesWith(ConstantInt::getTrue(M.getContext()));
2554+
CI->eraseFromParent();
2555+
Changed = true;
2556+
}
2557+
}
2558+
}
2559+
}
2560+
2561+
if (!Changed)
2562+
return PreservedAnalyses::all();
2563+
PreservedAnalyses PA = PreservedAnalyses::none();
2564+
PA.preserve<DominatorTreeAnalysis>();
2565+
PA.preserve<PostDominatorTreeAnalysis>();
2566+
PA.preserve<LoopAnalysis>();
2567+
return PA;
2568+
}

llvm/test/Other/new-pm-thinlto-postlink-defaults.ll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@
159159
; CHECK-O-NEXT: Invalidating analysis: ShouldNotRunFunctionPassesAnalysis
160160
; CHECK-O-NEXT: Invalidating analysis: InlineAdvisorAnalysis
161161
; CHECK-O-NEXT: Running pass: DeadArgumentEliminationPass
162+
; CHECK-O-NEXT: Running pass: SimplifyTypeTestsPass
162163
; CHECK-O-NEXT: Running pass: CoroCleanupPass
163164
; CHECK-POSTLINK-O-NEXT: Running pass: GlobalOptPass
164165
; CHECK-POSTLINK-O-NEXT: Running pass: GlobalDCEPass

llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@
143143
; CHECK-O-NEXT: Invalidating analysis: ShouldNotRunFunctionPassesAnalysis
144144
; CHECK-O-NEXT: Invalidating analysis: InlineAdvisorAnalysis
145145
; CHECK-O-NEXT: Running pass: DeadArgumentEliminationPass
146+
; CHECK-O-NEXT: Running pass: SimplifyTypeTestsPass
146147
; CHECK-O-NEXT: Running pass: CoroCleanupPass
147148
; CHECK-O-NEXT: Running pass: GlobalOptPass
148149
; CHECK-O-NEXT: Running pass: GlobalDCEPass

llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@
152152
; CHECK-O-NEXT: Invalidating analysis: ShouldNotRunFunctionPassesAnalysis
153153
; CHECK-O-NEXT: Invalidating analysis: InlineAdvisorAnalysis
154154
; CHECK-O-NEXT: Running pass: DeadArgumentEliminationPass
155+
; CHECK-O-NEXT: Running pass: SimplifyTypeTestsPass
155156
; CHECK-O-NEXT: Running pass: CoroCleanupPass
156157
; CHECK-O-NEXT: Running pass: GlobalOptPass
157158
; CHECK-O-NEXT: Running pass: GlobalDCEPass
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
; Test that a lowered type test for a type is simplified to true
2+
; if the target is a constant member of that type.
3+
4+
; RUN: opt -S %s -passes=simplify-type-tests | FileCheck %s
5+
6+
; Test that the simplification does not occur if the type is wrong.
7+
8+
; RUN: sed -e 's/"_ZTSFvvE"/"wrongtype"/g' %s | opt -S -passes=simplify-type-tests | FileCheck --check-prefix=WRONGTYPE %s
9+
10+
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"
11+
target triple = "x86_64-unknown-linux-gnu"
12+
13+
@__typeid__ZTSFvvE_global_addr = external hidden global [0 x i8], code_model "small"
14+
15+
define void @_Z2fpv.cfi() !type !0 {
16+
ret void
17+
}
18+
19+
define i64 @main() {
20+
%1 = icmp eq ptr @_Z2fpv, @__typeid__ZTSFvvE_global_addr
21+
; CHECK: br i1 true
22+
; WRONGTYPE: br i1 %
23+
br i1 %1, label %3, label %2
24+
25+
2:
26+
tail call void @llvm.ubsantrap(i8 2)
27+
unreachable
28+
29+
3:
30+
; CHECK: br i1 true
31+
; WRONGTYPE: br i1 %
32+
%c = icmp eq i64 ptrtoint (ptr @_Z2fpv to i64), ptrtoint (ptr @__typeid__ZTSFvvE_global_addr to i64)
33+
br i1 %c, label %4, label %2
34+
35+
4:
36+
tail call void @_Z2fpv()
37+
; CHECK: ret i64 0
38+
; WRONGTYPE: ret i64 sub
39+
ret i64 sub (i64 ptrtoint (ptr @_Z2fpv to i64), i64 ptrtoint (ptr @__typeid__ZTSFvvE_global_addr to i64))
40+
}
41+
42+
declare void @llvm.ubsantrap(i8 immarg)
43+
44+
declare hidden void @_Z2fpv()
45+
46+
!0 = !{i64 0, !"_ZTSFvvE"}

0 commit comments

Comments
 (0)