[llvm] r294897 - [ValueTracking] use nonnull argument attribute to eliminate null checks

Sanjay Patel via llvm-commits llvm-commits at lists.llvm.org
Sun Feb 12 07:35:35 PST 2017


Author: spatel
Date: Sun Feb 12 09:35:34 2017
New Revision: 294897

URL: http://llvm.org/viewvc/llvm-project?rev=294897&view=rev
Log:
[ValueTracking] use nonnull argument attribute to eliminate null checks

Enhancing value tracking's analysis of null-ness was suggested in D27855, so here's a first attempt at that.

This is part of solving:
https://llvm.org/bugs/show_bug.cgi?id=28430

Differential Revision: https://reviews.llvm.org/D28204

Modified:
    llvm/trunk/include/llvm/Analysis/ValueTracking.h
    llvm/trunk/lib/Analysis/ValueTracking.cpp
    llvm/trunk/test/Analysis/ValueTracking/known-nonnull-at.ll
    llvm/trunk/test/Transforms/InstCombine/call_nonnull_arg.ll

Modified: llvm/trunk/include/llvm/Analysis/ValueTracking.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Analysis/ValueTracking.h?rev=294897&r1=294896&r2=294897&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Analysis/ValueTracking.h (original)
+++ llvm/trunk/include/llvm/Analysis/ValueTracking.h Sun Feb 12 09:35:34 2017
@@ -88,8 +88,10 @@ template <typename T> class ArrayRef;
 
   /// Return true if the given value is known to be non-zero when defined. For
   /// vectors, return true if every element is known to be non-zero when
-  /// defined. Supports values with integer or pointer type and vectors of
-  /// integers.
+  /// defined. For pointers, if the context instruction and dominator tree are
+  /// specified, perform context-sensitive analysis and return true if the
+  /// pointer couldn't possibly be null at the specified instruction.
+  /// Supports values with integer or pointer type and vectors of integers.
   bool isKnownNonZero(const Value *V, const DataLayout &DL, unsigned Depth = 0,
                       AssumptionCache *AC = nullptr,
                       const Instruction *CxtI = nullptr,

Modified: llvm/trunk/lib/Analysis/ValueTracking.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/ValueTracking.cpp?rev=294897&r1=294896&r2=294897&view=diff
==============================================================================
--- llvm/trunk/lib/Analysis/ValueTracking.cpp (original)
+++ llvm/trunk/lib/Analysis/ValueTracking.cpp Sun Feb 12 09:35:34 2017
@@ -1824,10 +1824,12 @@ static bool rangeMetadataExcludesValue(c
   return true;
 }
 
-/// Return true if the given value is known to be non-zero when defined.
-/// For vectors return true if every element is known to be non-zero when
-/// defined. Supports values with integer or pointer type and vectors of
-/// integers.
+/// Return true if the given value is known to be non-zero when defined. For
+/// vectors, return true if every element is known to be non-zero when
+/// defined. For pointers, if the context instruction and dominator tree are
+/// specified, perform context-sensitive analysis and return true if the
+/// pointer couldn't possibly be null at the specified instruction.
+/// Supports values with integer or pointer type and vectors of integers.
 bool isKnownNonZero(const Value *V, unsigned Depth, const Query &Q) {
   if (auto *C = dyn_cast<Constant>(V)) {
     if (C->isNullValue())
@@ -1870,7 +1872,7 @@ bool isKnownNonZero(const Value *V, unsi
 
   // Check for pointer simplifications.
   if (V->getType()->isPointerTy()) {
-    if (isKnownNonNull(V))
+    if (isKnownNonNullAt(V, Q.CxtI, Q.DT))
       return true;
     if (const GEPOperator *GEP = dyn_cast<GEPOperator>(V))
       if (isGEPKnownNonNull(GEP, Depth, Q))
@@ -3473,6 +3475,16 @@ static bool isKnownNonNullFromDominating
     if (NumUsesExplored >= DomConditionsMaxUses)
       break;
     NumUsesExplored++;
+
+    // If the value is used as an argument to a call or invoke, then argument
+    // attributes may provide an answer about null-ness.
+    if (auto CS = ImmutableCallSite(U))
+      if (auto *CalledFunc = CS.getCalledFunction())
+        for (const Argument &Arg : CalledFunc->args())
+          if (CS.getArgOperand(Arg.getArgNo()) == V &&
+              Arg.hasNonNullAttr() && DT->dominates(CS.getInstruction(), CtxI))
+            return true;
+
     // Consider only compare instructions uniquely controlling a branch
     CmpInst::Predicate Pred;
     if (!match(const_cast<User *>(U),

Modified: llvm/trunk/test/Analysis/ValueTracking/known-nonnull-at.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Analysis/ValueTracking/known-nonnull-at.ll?rev=294897&r1=294896&r2=294897&view=diff
==============================================================================
--- llvm/trunk/test/Analysis/ValueTracking/known-nonnull-at.ll (original)
+++ llvm/trunk/test/Analysis/ValueTracking/known-nonnull-at.ll Sun Feb 12 09:35:34 2017
@@ -8,8 +8,7 @@ declare void @bar(i8* %a, i8* nonnull %b
 define i1 @caller1(i8* %x, i8* %y) {
 ; CHECK-LABEL: @caller1(
 ; CHECK-NEXT:    call void @bar(i8* %x, i8* %y)
-; CHECK-NEXT:    [[NULL_CHECK:%.*]] = icmp eq i8* %y, null
-; CHECK-NEXT:    ret i1 [[NULL_CHECK]]
+; CHECK-NEXT:    ret i1 false
 ;
   call void @bar(i8* %x, i8* %y)
   %null_check = icmp eq i8* %y, null
@@ -34,24 +33,68 @@ define i1 @caller2(i8* %x, i8* %y) {
 define i1 @caller3(i8* %x, i8* %y) {
 ; CHECK-LABEL: @caller3(
 ; CHECK-NEXT:    call void @bar(i8* %x, i8* %y)
-; CHECK-NEXT:    [[NULL_CHECK:%.*]] = icmp ne i8* %y, null
-; CHECK-NEXT:    ret i1 [[NULL_CHECK]]
+; CHECK-NEXT:    ret i1 true
 ;
   call void @bar(i8* %x, i8* %y)
   %null_check = icmp ne i8* %y, null
   ret i1 %null_check
 }
 
-; Don't know anything about 'y'.
+; FIXME: The call is guaranteed to execute, so 'y' must be nonnull throughout.
 
 define i1 @caller4(i8* %x, i8* %y) {
 ; CHECK-LABEL: @caller4(
-; CHECK-NEXT:    call void @bar(i8* %y, i8* %x)
 ; CHECK-NEXT:    [[NULL_CHECK:%.*]] = icmp ne i8* %y, null
+; CHECK-NEXT:    call void @bar(i8* %x, i8* %y)
 ; CHECK-NEXT:    ret i1 [[NULL_CHECK]]
 ;
-  call void @bar(i8* %y, i8* %x)
   %null_check = icmp ne i8* %y, null
+  call void @bar(i8* %x, i8* %y)
+  ret i1 %null_check
+}
+
+; The call to bar() does not dominate the null check, so no change.
+
+define i1 @caller5(i8* %x, i8* %y) {
+; CHECK-LABEL: @caller5(
+; CHECK-NEXT:    [[NULL_CHECK:%.*]] = icmp eq i8* %y, null
+; CHECK-NEXT:    br i1 [[NULL_CHECK]], label %t, label %f
+; CHECK:       t:
+; CHECK-NEXT:    ret i1 [[NULL_CHECK]]
+; CHECK:       f:
+; CHECK-NEXT:    call void @bar(i8* %x, i8* %y)
+; CHECK-NEXT:    ret i1 [[NULL_CHECK]]
+;
+  %null_check = icmp eq i8* %y, null
+  br i1 %null_check, label %t, label %f
+t:
   ret i1 %null_check
+f:
+  call void @bar(i8* %x, i8* %y)
+  ret i1 %null_check
+}
+
+; Make sure that an invoke works similarly to a call.
+
+declare i32 @esfp(...)
+
+define i1 @caller6(i8* %x, i8* %y) personality i8* bitcast (i32 (...)* @esfp to i8*){
+; CHECK-LABEL: @caller6(
+; CHECK-NEXT:    invoke void @bar(i8* %x, i8* nonnull %y)
+; CHECK-NEXT:    to label %cont unwind label %exc
+; CHECK:       cont:
+; CHECK-NEXT:    ret i1 false
+;
+  invoke void @bar(i8* %x, i8* nonnull %y)
+    to label %cont unwind label %exc
+
+cont:
+  %null_check = icmp eq i8* %y, null
+  ret i1 %null_check
+
+exc:
+  %lp = landingpad { i8*, i32 }
+    filter [0 x i8*] zeroinitializer
+  unreachable
 }
 

Modified: llvm/trunk/test/Transforms/InstCombine/call_nonnull_arg.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/InstCombine/call_nonnull_arg.ll?rev=294897&r1=294896&r2=294897&view=diff
==============================================================================
--- llvm/trunk/test/Transforms/InstCombine/call_nonnull_arg.ll (original)
+++ llvm/trunk/test/Transforms/InstCombine/call_nonnull_arg.ll Sun Feb 12 09:35:34 2017
@@ -31,7 +31,7 @@ dead:
   unreachable
 }
 
-; FIXME: The nonnull attribute in the 'bar' declaration could be 
+; The nonnull attribute in the 'bar' declaration is 
 ; propagated to the parameters of the 'baz' callsite. 
 
 declare void @bar(i8*, i8* nonnull)
@@ -40,7 +40,7 @@ declare void @baz(i8*, i8*)
 define void @deduce_nonnull_from_another_call(i8* %a, i8* %b) {
 ; CHECK-LABEL: @deduce_nonnull_from_another_call(
 ; CHECK-NEXT:    call void @bar(i8* %a, i8* %b)
-; CHECK-NEXT:    call void @baz(i8* %b, i8* %b)
+; CHECK-NEXT:    call void @baz(i8* nonnull %b, i8* nonnull %b)
 ; CHECK-NEXT:    ret void
 ;
   call void @bar(i8* %a, i8* %b)




More information about the llvm-commits mailing list