-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[SCCP] Propagate non-null pointers #106090
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
Conversation
Add NotConstant(Null) roots for nonnull arguments and the propagate them through nuw/inbounds GEPs. Having this functionality in SCCP is useful because it allows reliably eliminating null comparisons, independently of how deeply nested they are in selects/phis. This handles cases that would hit a cutoff in ValueTracking.
@llvm/pr-subscribers-function-specialization @llvm/pr-subscribers-llvm-transforms Author: Nikita Popov (nikic) ChangesAdd NotConstant(Null) roots for nonnull arguments and then propagate them through nuw/inbounds GEPs. Having this functionality in SCCP is useful because it allows reliably eliminating null comparisons, independently of how deeply nested they are in selects/phis. This handles cases that would hit a cutoff in ValueTracking otherwise. Full diff: https://github.com/llvm/llvm-project/pull/106090.diff 2 Files Affected:
diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
index c6f355a07d9c7f..c21b4c0b3a1c87 100644
--- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp
+++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
@@ -446,6 +446,12 @@ class SCCPInstVisitor : public InstVisitor<SCCPInstVisitor> {
return markConstant(ValueState[V], V, C);
}
+ bool markNotConstant(ValueLatticeElement &IV, Value *V, Constant *C);
+
+ bool markNotNull(ValueLatticeElement &IV, Value *V) {
+ return markNotConstant(IV, V, Constant::getNullValue(V->getType()));
+ }
+
/// markConstantRange - Mark the object as constant range with \p CR. If the
/// object is not a constant range with the range \p CR, add it to the
/// instruction work list so that the users of the instruction are updated
@@ -820,7 +826,11 @@ class SCCPInstVisitor : public InstVisitor<SCCPInstVisitor> {
return;
}
}
- // Assume nothing about the incoming arguments without range.
+ if (A->hasNonNullAttr()) {
+ markNotNull(ValueState[A], A);
+ return;
+ }
+ // Assume nothing about the incoming arguments without attributes.
markOverdefined(A);
}
@@ -905,6 +915,15 @@ bool SCCPInstVisitor::markConstant(ValueLatticeElement &IV, Value *V,
return true;
}
+bool SCCPInstVisitor::markNotConstant(ValueLatticeElement &IV, Value *V,
+ Constant *C) {
+ if (!IV.markNotConstant(C))
+ return false;
+ LLVM_DEBUG(dbgs() << "markNotConstant: " << *C << ": " << *V << '\n');
+ pushToWorkList(IV, V);
+ return true;
+}
+
bool SCCPInstVisitor::markConstantRange(ValueLatticeElement &IV, Value *V,
const ConstantRange &CR) {
if (!IV.markConstantRange(CR))
@@ -1574,6 +1593,19 @@ void SCCPInstVisitor::visitGetElementPtrInst(GetElementPtrInst &I) {
if (ValueState[&I].isOverdefined())
return (void)markOverdefined(&I);
+ const ValueLatticeElement &PtrState = getValueState(I.getPointerOperand());
+ if (PtrState.isUnknownOrUndef())
+ return;
+
+ // gep inbounds/nuw of non-null is non-null.
+ if (PtrState.isNotConstant() && PtrState.getNotConstant()->isNullValue()) {
+ if (I.hasNoUnsignedWrap() ||
+ (I.isInBounds() &&
+ !NullPointerIsDefined(I.getFunction(), I.getAddressSpace())))
+ return (void)markNotNull(ValueState[&I], &I);
+ return (void)markOverdefined(&I);
+ }
+
SmallVector<Constant *, 8> Operands;
Operands.reserve(I.getNumOperands());
diff --git a/llvm/test/Transforms/SCCP/pointer-nonnull.ll b/llvm/test/Transforms/SCCP/pointer-nonnull.ll
index 85367d8a56765e..a88f08ab25fe1d 100644
--- a/llvm/test/Transforms/SCCP/pointer-nonnull.ll
+++ b/llvm/test/Transforms/SCCP/pointer-nonnull.ll
@@ -14,8 +14,7 @@ define i1 @test_no_attr(ptr %p) {
define i1 @test_nonnull(ptr nonnull %p) {
; CHECK-LABEL: define i1 @test_nonnull(
; CHECK-SAME: ptr nonnull [[P:%.*]]) {
-; CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[P]], null
-; CHECK-NEXT: ret i1 [[CMP]]
+; CHECK-NEXT: ret i1 true
;
%cmp = icmp ne ptr %p, null
ret i1 %cmp
@@ -24,8 +23,7 @@ define i1 @test_nonnull(ptr nonnull %p) {
define i1 @test_nonnull_eq(ptr nonnull %p) {
; CHECK-LABEL: define i1 @test_nonnull_eq(
; CHECK-SAME: ptr nonnull [[P:%.*]]) {
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[P]], null
-; CHECK-NEXT: ret i1 [[CMP]]
+; CHECK-NEXT: ret i1 false
;
%cmp = icmp eq ptr %p, null
ret i1 %cmp
@@ -34,8 +32,7 @@ define i1 @test_nonnull_eq(ptr nonnull %p) {
define i1 @test_dereferenceable(ptr dereferenceable(4) %p) {
; CHECK-LABEL: define i1 @test_dereferenceable(
; CHECK-SAME: ptr dereferenceable(4) [[P:%.*]]) {
-; CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[P]], null
-; CHECK-NEXT: ret i1 [[CMP]]
+; CHECK-NEXT: ret i1 true
;
%cmp = icmp ne ptr %p, null
ret i1 %cmp
@@ -57,8 +54,7 @@ define i1 @test_gep_nuw(ptr nonnull %p, i64 %x) {
; CHECK-LABEL: define i1 @test_gep_nuw(
; CHECK-SAME: ptr nonnull [[P:%.*]], i64 [[X:%.*]]) {
; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr [[P]], i64 [[X]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[GEP]], null
-; CHECK-NEXT: ret i1 [[CMP]]
+; CHECK-NEXT: ret i1 true
;
%gep = getelementptr nuw i8, ptr %p, i64 %x
%cmp = icmp ne ptr %gep, null
@@ -69,8 +65,7 @@ define i1 @test_gep_inbounds(ptr nonnull %p, i64 %x) {
; CHECK-LABEL: define i1 @test_gep_inbounds(
; CHECK-SAME: ptr nonnull [[P:%.*]], i64 [[X:%.*]]) {
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 [[X]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[GEP]], null
-; CHECK-NEXT: ret i1 [[CMP]]
+; CHECK-NEXT: ret i1 true
;
%gep = getelementptr inbounds i8, ptr %p, i64 %x
%cmp = icmp ne ptr %gep, null
@@ -94,8 +89,7 @@ define i1 @test_select(i1 %c, ptr nonnull %p, i64 %x) {
; CHECK-SAME: i1 [[C:%.*]], ptr nonnull [[P:%.*]], i64 [[X:%.*]]) {
; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr [[P]], i64 [[X]]
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C]], ptr [[P]], ptr [[GEP]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[SEL]], null
-; CHECK-NEXT: ret i1 [[CMP]]
+; CHECK-NEXT: ret i1 true
;
%gep = getelementptr nuw i8, ptr %p, i64 %x
%sel = select i1 %c, ptr %p, ptr %gep
@@ -127,8 +121,7 @@ define i1 @test_phi(i1 %c, ptr nonnull %p, i64 %x) {
; CHECK-NEXT: br label %[[JOIN]]
; CHECK: [[JOIN]]:
; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ [[P]], %[[ENTRY]] ], [ [[GEP]], %[[IF]] ]
-; CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[PHI]], null
-; CHECK-NEXT: ret i1 [[CMP]]
+; CHECK-NEXT: ret i1 true
;
entry:
br i1 %c, label %if, label %join
|
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 have a look at https://github.com/dtcxzyw/llvm-opt-benchmark/pull/1247/files#r1731703928?
This patch seems to incorrectly remove the null pointer checking in https://github.com/abseil/abseil-cpp/blob/master/absl/time/internal/cctz/src/time_zone_format.cc#L623-L646.
Oh, ignore me. This function is internal :) |
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.
LGTM. Thank you!
Add NotConstant(Null) roots for nonnull arguments and then propagate them through nuw/inbounds GEPs.
Having this functionality in SCCP is useful because it allows reliably eliminating null comparisons, independently of how deeply nested they are in selects/phis. This handles cases that would hit a cutoff in ValueTracking otherwise.
The implementation is something of a MVP, there are a number of obvious extensions (e.g. allocas are also non-null).