-
Notifications
You must be signed in to change notification settings - Fork 15.1k
Add SimplifyTypeTests pass. #141327
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add SimplifyTypeTests pass. #141327
Changes from 5 commits
ab837f0
d7bb3c4
1344339
16d5799
b36c74c
b76d0d4
abfa556
d140ba4
7c0b3ea
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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(), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a comment on this conversion? Figured it out by adding up the chars myself but it would be good to make it explicit. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would just use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added an example, I think that should make it clear enough. |
||
| GV.getName().substr(9, GV.getName().size() - 21)); | ||
| auto MaySimplifyPtr = [&](Value *Ptr) { | ||
| if (auto *GV = dyn_cast<GlobalValue>(Ptr)) | ||
| if (auto *CFIGV = M.getNamedValue((GV->getName() + ".cfi").str())) | ||
| Ptr = CFIGV; | ||
| return isKnownTypeIdMember(MD, M.getDataLayout(), Ptr, 0); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are there cases where the GV will not have a ".cfi" extension? I notice the test has that extension. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, if it's a function defined outside the LTO unit the jump table entry will be named |
||
| }; | ||
| auto MaySimplifyInt = [&](Value *Op) { | ||
| auto *PtrAsInt = dyn_cast<ConstantExpr>(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<ICmpInst>(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<ConstantExpr>(U); | ||
| if (!CE || CE->getOpcode() != Instruction::PtrToInt) | ||
| continue; | ||
| for (Use &U : make_early_inc_range(CE->uses())) { | ||
| auto *CE = dyn_cast<ConstantExpr>(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<ICmpInst>(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(); | ||
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| ; Make sure that if a phi with identical inputs gets created it gets undone by CodeGenPrepare. | ||
|
|
||
| ; RUN: opt -codegenprepare -S < %s | FileCheck %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__ZTS1S_global_addr = external hidden global [0 x i8], code_model "small" | ||
| @__typeid__ZTS1S_align = external hidden global [0 x i8], !absolute_symbol !0 | ||
| @__typeid__ZTS1S_size_m1 = external hidden global [0 x i8], !absolute_symbol !1 | ||
|
|
||
| ; Check that we recover the third pair of zexts from the phi. | ||
|
|
||
| ; CHECK: define void @f4 | ||
| define void @f4(i1 noundef zeroext %0, ptr noundef %1, ptr noundef %2, ptr noundef %3) #1 { | ||
| br i1 %0, label %5, label %18 | ||
|
|
||
| 5: | ||
| %6 = load ptr, ptr %1, align 8 | ||
| %7 = ptrtoint ptr %6 to i64 | ||
| %8 = sub i64 %7, ptrtoint (ptr @__typeid__ZTS1S_global_addr to i64) | ||
| ; CHECK: zext {{.*}} @__typeid__ZTS1S_align | ||
| %9 = zext nneg i8 ptrtoint (ptr @__typeid__ZTS1S_align to i8) to i64 | ||
| %10 = lshr i64 %8, %9 | ||
| ; CHECK: zext {{.*}} @__typeid__ZTS1S_align | ||
| %11 = zext nneg i8 sub (i8 64, i8 ptrtoint (ptr @__typeid__ZTS1S_align to i8)) to i64 | ||
| %12 = shl i64 %8, %11 | ||
| %13 = or i64 %10, %12 | ||
| %14 = icmp ugt i64 %13, ptrtoint (ptr @__typeid__ZTS1S_size_m1 to i64) | ||
| br i1 %14, label %15, label %16 | ||
|
|
||
| 15: | ||
| tail call void @llvm.ubsantrap(i8 2) #5 | ||
| unreachable | ||
|
|
||
| 16: | ||
| %17 = load ptr, ptr %6, align 8 | ||
| tail call void %17(ptr noundef nonnull align 8 dereferenceable(8) %1) | ||
| br label %31 | ||
|
|
||
| 18: | ||
| %19 = load ptr, ptr %2, align 8 | ||
| %20 = ptrtoint ptr %19 to i64 | ||
| %21 = sub i64 %20, ptrtoint (ptr @__typeid__ZTS1S_global_addr to i64) | ||
| ; CHECK: zext {{.*}} @__typeid__ZTS1S_align | ||
| %22 = zext nneg i8 ptrtoint (ptr @__typeid__ZTS1S_align to i8) to i64 | ||
| %23 = lshr i64 %21, %22 | ||
| ; CHECK: zext {{.*}} @__typeid__ZTS1S_align | ||
| %24 = zext nneg i8 sub (i8 64, i8 ptrtoint (ptr @__typeid__ZTS1S_align to i8)) to i64 | ||
| %25 = shl i64 %21, %24 | ||
| %26 = or i64 %23, %25 | ||
| %27 = icmp ugt i64 %26, ptrtoint (ptr @__typeid__ZTS1S_size_m1 to i64) | ||
| br i1 %27, label %28, label %29 | ||
|
|
||
| 28: | ||
| tail call void @llvm.ubsantrap(i8 2) #5 | ||
| unreachable | ||
|
|
||
| 29: | ||
| %30 = load ptr, ptr %19, align 8 | ||
| tail call void %30(ptr noundef nonnull align 8 dereferenceable(8) %2) | ||
| br label %31 | ||
|
|
||
| 31: | ||
| %32 = phi i64 [ %24, %29 ], [ %11, %16 ] | ||
| %33 = phi i64 [ %22, %29 ], [ %9, %16 ] | ||
| %34 = load ptr, ptr %3, align 8 | ||
| %35 = ptrtoint ptr %34 to i64 | ||
| %36 = sub i64 %35, ptrtoint (ptr @__typeid__ZTS1S_global_addr to i64) | ||
| ; CHECK: zext {{.*}} @__typeid__ZTS1S_align | ||
| %37 = lshr i64 %36, %33 | ||
| ; CHECK: zext {{.*}} @__typeid__ZTS1S_align | ||
| %38 = shl i64 %36, %32 | ||
| %39 = or i64 %37, %38 | ||
| %40 = icmp ugt i64 %39, ptrtoint (ptr @__typeid__ZTS1S_size_m1 to i64) | ||
| br i1 %40, label %41, label %42 | ||
|
|
||
| 41: | ||
| tail call void @llvm.ubsantrap(i8 2) #5 | ||
| unreachable | ||
|
|
||
| 42: | ||
| %43 = load ptr, ptr %34, align 8 | ||
| tail call void %43(ptr noundef nonnull align 8 dereferenceable(8) %3) | ||
| ret void | ||
| } | ||
|
|
||
| declare i1 @llvm.type.test(ptr, metadata) | ||
| declare void @llvm.ubsantrap(i8 immarg) | ||
|
|
||
| !0 = !{i64 0, i64 256} | ||
| !1 = !{i64 0, i64 128} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a more extensive comment with what this is looking for and why? I don't look at lower type test output often so I don't recall offhand what e.g. it would have looked like without inlining vs with.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done