[llvm] e5313ef - [Attributor] Filter potential callees based on `noundef` arguments

Johannes Doerfert via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 25 14:36:58 PDT 2023


Author: Johannes Doerfert
Date: 2023-08-25T14:36:43-07:00
New Revision: e5313ef55fc25b74cb9d3cd2bd74eb452274a2f2

URL: https://github.com/llvm/llvm-project/commit/e5313ef55fc25b74cb9d3cd2bd74eb452274a2f2
DIFF: https://github.com/llvm/llvm-project/commit/e5313ef55fc25b74cb9d3cd2bd74eb452274a2f2.diff

LOG: [Attributor] Filter potential callees based on `noundef` arguments

If a potential callee has more arguments than the call site, the values
passed will be `poison`. If the potential callee would exhibit UB for
such `undef` argument, e.g., they are marked `noundef`, we can rule the
potential callee out.

Added: 
    

Modified: 
    llvm/lib/Analysis/ValueTracking.cpp
    llvm/lib/Transforms/IPO/AttributorAttributes.cpp
    llvm/test/Transforms/Attributor/callgraph.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 06162c6c3c1823..90b4abe2664427 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -7167,6 +7167,8 @@ static bool programUndefinedIfUndefOrPoison(const Value *V,
     Begin = Inst->getIterator();
     Begin++;
   } else if (const auto *Arg = dyn_cast<Argument>(V)) {
+    if (Arg->getParent()->isDeclaration())
+      return false;
     BB = &Arg->getParent()->getEntryBlock();
     Begin = BB->begin();
   } else {

diff  --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
index 8c6fa1ab53082e..79f216bedbc505 100644
--- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
+++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
@@ -10230,7 +10230,8 @@ struct AANoUndefFloating : public AANoUndefImpl {
   /// See AbstractAttribute::initialize(...).
   void initialize(Attributor &A) override {
     AANoUndefImpl::initialize(A);
-    if (!getState().isAtFixpoint())
+    if (!getState().isAtFixpoint() && getAnchorScope() &&
+        !getAnchorScope()->isDeclaration())
       if (Instruction *CtxI = getCtxI())
         followUsesInMBEC(*this, A, getState(), *CtxI);
   }
@@ -12091,6 +12092,36 @@ struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
       AssumedCalleesNow.set_union(PotentialCallees);
     }
 
+    // Try to find a reason for \p Fn not to be a potential callee. If none was
+    // found, add it to the assumed callees set.
+    auto CheckPotentialCallee = [&](Function &Fn) {
+      if (!PotentialCallees.empty() && !PotentialCallees.count(&Fn))
+        return false;
+
+      auto &CachedResult = FilterResults[&Fn];
+      if (CachedResult.has_value())
+        return CachedResult.value();
+
+      int NumFnArgs = Fn.arg_size();
+      int NumCBArgs = CB->arg_size();
+
+      // Check if any excess argument (which we fill up with poison) is known to
+      // be UB on undef.
+      for (int I = NumCBArgs; I < NumFnArgs; ++I) {
+        bool IsKnown = false;
+        if (AA::hasAssumedIRAttr<Attribute::NoUndef>(
+                A, this, IRPosition::argument(*Fn.getArg(I)),
+                DepClassTy::OPTIONAL, IsKnown)) {
+          if (IsKnown)
+            CachedResult = false;
+          return false;
+        }
+      }
+
+      CachedResult = true;
+      return true;
+    };
+
     // Check simplification result, prune known UB callees, also restrict it to
     // the !callees set, if present.
     for (auto &VAC : Values) {
@@ -12101,7 +12132,7 @@ struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
         continue;
       // TODO: Check for known UB, e.g., poison + noundef.
       if (auto *VACFn = dyn_cast<Function>(VAC.getValue())) {
-        if (PotentialCallees.empty() || PotentialCallees.count(VACFn))
+        if (CheckPotentialCallee(*VACFn))
           AssumedCalleesNow.insert(VACFn);
         continue;
       }
@@ -12283,6 +12314,9 @@ struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
   }
 
 private:
+  /// Map to remember filter results.
+  DenseMap<Function *, std::optional<bool>> FilterResults;
+
   /// If the !callee metadata was present, this set will contain all potential
   /// callees (superset).
   SmallSetVector<Function *, 4> PotentialCallees;

diff  --git a/llvm/test/Transforms/Attributor/callgraph.ll b/llvm/test/Transforms/Attributor/callgraph.ll
index 5904fc364e6e1c..bc14ac342aeabb 100644
--- a/llvm/test/Transforms/Attributor/callgraph.ll
+++ b/llvm/test/Transforms/Attributor/callgraph.ll
@@ -85,6 +85,8 @@ define i32 @musttailCall(i32 %0) {
 declare i32 @retI32()
 declare void @takeI32(i32)
 declare float @retFloatTakeFloat(float)
+; This callee is always filtered out because of the noundef argument
+declare float @retFloatTakeFloatFloatNoundef(float, float noundef)
 declare void @void()
 
 define i32 @non_matching_fp1(i1 %c1, i1 %c2, i1 %c) {
@@ -158,6 +160,69 @@ define i32 @non_matching_fp1(i1 %c1, i1 %c2, i1 %c) {
   ret i32 %call
 }
 
+define i32 @non_matching_fp1_noundef(i1 %c1, i1 %c2, i1 %c) {
+; UNLIM-LABEL: @non_matching_fp1_noundef(
+; UNLIM-NEXT:    [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
+; UNLIM-NEXT:    [[FP2:%.*]] = select i1 [[C2:%.*]], ptr @retFloatTakeFloatFloatNoundef, ptr @void
+; UNLIM-NEXT:    [[FP:%.*]] = select i1 [[C:%.*]], ptr [[FP1]], ptr [[FP2]]
+; UNLIM-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[FP]], @takeI32
+; UNLIM-NEXT:    br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
+; UNLIM:       2:
+; UNLIM-NEXT:    [[CALL1:%.*]] = call i32 @takeI32(i32 42)
+; UNLIM-NEXT:    br label [[TMP9:%.*]]
+; UNLIM:       3:
+; UNLIM-NEXT:    [[TMP4:%.*]] = icmp eq ptr [[FP]], @retI32
+; UNLIM-NEXT:    br i1 [[TMP4]], label [[TMP5:%.*]], label [[TMP6:%.*]]
+; UNLIM:       5:
+; UNLIM-NEXT:    [[CALL2:%.*]] = call i32 @retI32(i32 42)
+; UNLIM-NEXT:    br label [[TMP9]]
+; UNLIM:       6:
+; UNLIM-NEXT:    br i1 true, label [[TMP7:%.*]], label [[TMP8:%.*]]
+; UNLIM:       7:
+; UNLIM-NEXT:    [[CALL3:%.*]] = call i32 @void(i32 42)
+; UNLIM-NEXT:    br label [[TMP9]]
+; UNLIM:       8:
+; UNLIM-NEXT:    unreachable
+; UNLIM:       9:
+; UNLIM-NEXT:    [[CALL_PHI:%.*]] = phi i32 [ [[CALL1]], [[TMP2]] ], [ [[CALL2]], [[TMP5]] ], [ [[CALL3]], [[TMP7]] ]
+; UNLIM-NEXT:    ret i32 [[CALL_PHI]]
+;
+; LIMI2-LABEL: @non_matching_fp1_noundef(
+; LIMI2-NEXT:    [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
+; LIMI2-NEXT:    [[FP2:%.*]] = select i1 [[C2:%.*]], ptr @retFloatTakeFloatFloatNoundef, ptr @void
+; LIMI2-NEXT:    [[FP:%.*]] = select i1 [[C:%.*]], ptr [[FP1]], ptr [[FP2]]
+; LIMI2-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[FP]], @takeI32
+; LIMI2-NEXT:    br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
+; LIMI2:       2:
+; LIMI2-NEXT:    [[CALL1:%.*]] = call i32 @takeI32(i32 42)
+; LIMI2-NEXT:    br label [[TMP7:%.*]]
+; LIMI2:       3:
+; LIMI2-NEXT:    [[TMP4:%.*]] = icmp eq ptr [[FP]], @retI32
+; LIMI2-NEXT:    br i1 [[TMP4]], label [[TMP5:%.*]], label [[TMP6:%.*]]
+; LIMI2:       5:
+; LIMI2-NEXT:    [[CALL2:%.*]] = call i32 @retI32(i32 42)
+; LIMI2-NEXT:    br label [[TMP7]]
+; LIMI2:       6:
+; LIMI2-NEXT:    [[CALL3:%.*]] = call i32 [[FP]](i32 42), !callees !1
+; LIMI2-NEXT:    br label [[TMP7]]
+; LIMI2:       7:
+; LIMI2-NEXT:    [[CALL_PHI:%.*]] = phi i32 [ [[CALL1]], [[TMP2]] ], [ [[CALL2]], [[TMP5]] ], [ [[CALL3]], [[TMP6]] ]
+; LIMI2-NEXT:    ret i32 [[CALL_PHI]]
+;
+; LIMI0-LABEL: @non_matching_fp1_noundef(
+; LIMI0-NEXT:    [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
+; LIMI0-NEXT:    [[FP2:%.*]] = select i1 [[C2:%.*]], ptr @retFloatTakeFloatFloatNoundef, ptr @void
+; LIMI0-NEXT:    [[FP:%.*]] = select i1 [[C:%.*]], ptr [[FP1]], ptr [[FP2]]
+; LIMI0-NEXT:    [[CALL:%.*]] = call i32 [[FP]](i32 42), !callees !2
+; LIMI0-NEXT:    ret i32 [[CALL]]
+;
+  %fp1 = select i1 %c1, ptr @retI32, ptr @takeI32
+  %fp2 = select i1 %c2, ptr @retFloatTakeFloatFloatNoundef, ptr @void
+  %fp = select i1 %c, ptr %fp1, ptr %fp2
+  %call = call i32 %fp(i32 42)
+  ret i32 %call
+}
+
 define void @non_matching_fp2(i1 %c1, i1 %c2, i1 %c, ptr %unknown) {
 ; UNLIM-LABEL: @non_matching_fp2(
 ; UNLIM-NEXT:    [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
@@ -298,12 +363,12 @@ define void @undef_in_callees() {
 ;
 ; LIMI2-LABEL: @undef_in_callees(
 ; LIMI2-NEXT:  cond.end.i:
-; LIMI2-NEXT:    call void undef(ptr undef, i32 undef, ptr undef), !callees !3
+; LIMI2-NEXT:    call void undef(ptr undef, i32 undef, ptr undef), !callees !4
 ; LIMI2-NEXT:    ret void
 ;
 ; LIMI0-LABEL: @undef_in_callees(
 ; LIMI0-NEXT:  cond.end.i:
-; LIMI0-NEXT:    call void undef(ptr undef, i32 undef, ptr undef), !callees !4
+; LIMI0-NEXT:    call void undef(ptr undef, i32 undef, ptr undef), !callees !5
 ; LIMI0-NEXT:    ret void
 ;
 cond.end.i:
@@ -347,15 +412,17 @@ cond.end.i:
 ; UNLIM: [[META2:![0-9]+]] = distinct !{ptr undef, ptr null}
 ;.
 ; LIMI2: [[META0:![0-9]+]] = !{ptr @void, ptr @retFloatTakeFloat}
-; LIMI2: [[META1:![0-9]+]] = !{!2}
-; LIMI2: [[META2:![0-9]+]] = !{i64 0, i1 false}
-; LIMI2: [[META3:![0-9]+]] = distinct !{ptr undef, ptr null}
+; LIMI2: [[META1:![0-9]+]] = !{ptr @void}
+; LIMI2: [[META2:![0-9]+]] = !{!3}
+; LIMI2: [[META3:![0-9]+]] = !{i64 0, i1 false}
+; LIMI2: [[META4:![0-9]+]] = distinct !{ptr undef, ptr null}
 ;.
 ; LIMI0: [[META0:![0-9]+]] = !{ptr @func3, ptr @func4}
 ; LIMI0: [[META1:![0-9]+]] = !{ptr @takeI32, ptr @retI32, ptr @void, ptr @retFloatTakeFloat}
-; LIMI0: [[META2:![0-9]+]] = !{!3}
-; LIMI0: [[META3:![0-9]+]] = !{i64 0, i1 false}
-; LIMI0: [[META4:![0-9]+]] = distinct !{ptr undef, ptr null}
+; LIMI0: [[META2:![0-9]+]] = !{ptr @takeI32, ptr @retI32, ptr @void}
+; LIMI0: [[META3:![0-9]+]] = !{!4}
+; LIMI0: [[META4:![0-9]+]] = !{i64 0, i1 false}
+; LIMI0: [[META5:![0-9]+]] = distinct !{ptr undef, ptr null}
 ;.
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; DOT: {{.*}}


        


More information about the llvm-commits mailing list