[llvm] 6f0ca01 - [Attributor] Introduce AAGlobalValueInfo to track global value uses

Johannes Doerfert via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 29 23:06:33 PDT 2023


Author: Johannes Doerfert
Date: 2023-08-29T22:41:54-07:00
New Revision: 6f0ca01c570510dcfd1487b33c93f6aefa0bbf0b

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

LOG: [Attributor] Introduce AAGlobalValueInfo to track global value uses

GlobalValues are often interesting, especially if they have local
linkage. We now track all uses of those and refine potential callees
with it. Effectively, if an internal function cannot reach an indirect
call site, it cannot be a potential callee, even if it has its address
taken.

Added: 
    

Modified: 
    llvm/include/llvm/Transforms/IPO/Attributor.h
    llvm/lib/Transforms/IPO/AttributorAttributes.cpp
    llvm/test/Transforms/Attributor/callgraph.ll

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h
index abcdc9318a7a21..05dcd83d90c6a5 100644
--- a/llvm/include/llvm/Transforms/IPO/Attributor.h
+++ b/llvm/include/llvm/Transforms/IPO/Attributor.h
@@ -118,6 +118,7 @@
 #include "llvm/IR/Attributes.h"
 #include "llvm/IR/ConstantRange.h"
 #include "llvm/IR/Constants.h"
+#include "llvm/IR/GlobalValue.h"
 #include "llvm/IR/InstIterator.h"
 #include "llvm/IR/Instruction.h"
 #include "llvm/IR/Instructions.h"
@@ -3948,6 +3949,14 @@ struct AAIsDead
   using Base = StateWrapper<BitIntegerState<uint8_t, 3, 0>, AbstractAttribute>;
   AAIsDead(const IRPosition &IRP, Attributor &A) : Base(IRP) {}
 
+  /// See AbstractAttribute::isValidIRPositionForInit
+  static bool isValidIRPositionForInit(Attributor &A, const IRPosition &IRP) {
+    if (IRP.getPositionKind() == IRPosition::IRP_FUNCTION)
+      return isa<Function>(IRP.getAnchorValue()) &&
+             !cast<Function>(IRP.getAnchorValue()).isDeclaration();
+    return true;
+  }
+
   /// State encoding bits. A set bit in the state means the property holds.
   enum {
     HAS_NO_EFFECT = 1 << 0,
@@ -6139,6 +6148,45 @@ struct AAAddressSpace : public StateWrapper<BooleanState, AbstractAttribute> {
   static const char ID;
 };
 
+/// An abstract interface for llvm::GlobalValue information interference.
+struct AAGlobalValueInfo
+    : public StateWrapper<BooleanState, AbstractAttribute> {
+  AAGlobalValueInfo(const IRPosition &IRP, Attributor &A)
+      : StateWrapper<BooleanState, AbstractAttribute>(IRP) {}
+
+  /// See AbstractAttribute::isValidIRPositionForInit
+  static bool isValidIRPositionForInit(Attributor &A, const IRPosition &IRP) {
+    if (IRP.getPositionKind() != IRPosition::IRP_FLOAT)
+      return false;
+    auto *GV = dyn_cast<GlobalValue>(&IRP.getAnchorValue());
+    if (!GV)
+      return false;
+    return GV->hasLocalLinkage();
+  }
+
+  /// Create an abstract attribute view for the position \p IRP.
+  static AAGlobalValueInfo &createForPosition(const IRPosition &IRP,
+                                              Attributor &A);
+
+  /// Return true iff \p U is a potential use of the associated global value.
+  virtual bool isPotentialUse(const Use &U) const = 0;
+
+  /// See AbstractAttribute::getName()
+  const std::string getName() const override { return "AAGlobalValueInfo"; }
+
+  /// See AbstractAttribute::getIdAddr()
+  const char *getIdAddr() const override { return &ID; }
+
+  /// This function should return true if the type of the \p AA is
+  /// AAGlobalValueInfo
+  static bool classof(const AbstractAttribute *AA) {
+    return (AA->getIdAddr() == &ID);
+  }
+
+  /// Unique ID (due to the unique address)
+  static const char ID;
+};
+
 /// An abstract interface for indirect call information interference.
 struct AAIndirectCallInfo
     : public StateWrapper<BooleanState, AbstractAttribute> {

diff  --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
index ada4def7cfdb15..d91b4f158c8299 100644
--- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
+++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
@@ -193,6 +193,7 @@ PIPE_OPERATOR(AAAssumptionInfo)
 PIPE_OPERATOR(AAUnderlyingObjects)
 PIPE_OPERATOR(AAAddressSpace)
 PIPE_OPERATOR(AAIndirectCallInfo)
+PIPE_OPERATOR(AAGlobalValueInfo)
 
 #undef PIPE_OPERATOR
 
@@ -12056,6 +12057,134 @@ struct AAUnderlyingObjectsFunction final : AAUnderlyingObjectsImpl {
 };
 } // namespace
 
+/// ------------------------ Global Value Info  -------------------------------
+namespace {
+struct AAGlobalValueInfoFloating : public AAGlobalValueInfo {
+  AAGlobalValueInfoFloating(const IRPosition &IRP, Attributor &A)
+      : AAGlobalValueInfo(IRP, A) {}
+
+  /// See AbstractAttribute::initialize(...).
+  void initialize(Attributor &A) override {}
+
+  bool checkUse(Attributor &A, const Use &U, bool &Follow,
+                SmallVectorImpl<const Value *> &Worklist) {
+    Instruction *UInst = dyn_cast<Instruction>(U.getUser());
+    if (!UInst) {
+      Follow = true;
+      return true;
+    }
+
+    LLVM_DEBUG(dbgs() << "[AAGlobalValueInfo] Check use: " << *U.get() << " in "
+                      << *UInst << "\n");
+
+    if (auto *Cmp = dyn_cast<ICmpInst>(U.getUser())) {
+      int Idx = &Cmp->getOperandUse(0) == &U;
+      if (isa<Constant>(Cmp->getOperand(Idx)))
+        return true;
+      return U == &getAnchorValue();
+    }
+
+    // Explicitly catch return instructions.
+    if (isa<ReturnInst>(UInst)) {
+      auto CallSitePred = [&](AbstractCallSite ACS) {
+        Worklist.push_back(ACS.getInstruction());
+        return true;
+      };
+      bool UsedAssumedInformation = false;
+      // TODO: We should traverse the uses or add a "non-call-site" CB.
+      if (!A.checkForAllCallSites(CallSitePred, *UInst->getFunction(),
+                                  /*RequireAllCallSites=*/true, this,
+                                  UsedAssumedInformation))
+        return false;
+      return true;
+    }
+
+    // For now we only use special logic for call sites. However, the tracker
+    // itself knows about a lot of other non-capturing cases already.
+    auto *CB = dyn_cast<CallBase>(UInst);
+    if (!CB)
+      return false;
+    // Direct calls are OK uses.
+    if (CB->isCallee(&U))
+      return true;
+    // Non-argument uses are scary.
+    if (!CB->isArgOperand(&U))
+      return false;
+    // TODO: Iterate callees.
+    auto *Fn = dyn_cast<Function>(CB->getCalledOperand());
+    if (!Fn || !A.isFunctionIPOAmendable(*Fn))
+      return false;
+
+    unsigned ArgNo = CB->getArgOperandNo(&U);
+    Worklist.push_back(Fn->getArg(ArgNo));
+    return true;
+  }
+
+  ChangeStatus updateImpl(Attributor &A) override {
+    unsigned NumUsesBefore = Uses.size();
+
+    SmallPtrSet<const Value *, 8> Visited;
+    SmallVector<const Value *> Worklist;
+    Worklist.push_back(&getAnchorValue());
+
+    auto UsePred = [&](const Use &U, bool &Follow) -> bool {
+      Uses.insert(&U);
+      switch (DetermineUseCaptureKind(U, nullptr)) {
+      case UseCaptureKind::NO_CAPTURE:
+        return checkUse(A, U, Follow, Worklist);
+      case UseCaptureKind::MAY_CAPTURE:
+        return checkUse(A, U, Follow, Worklist);
+      case UseCaptureKind::PASSTHROUGH:
+        Follow = true;
+        return true;
+      }
+      return true;
+    };
+    auto EquivalentUseCB = [&](const Use &OldU, const Use &NewU) {
+      Uses.insert(&OldU);
+      return true;
+    };
+
+    while (!Worklist.empty()) {
+      const Value *V = Worklist.pop_back_val();
+      if (!Visited.insert(V).second)
+        continue;
+      if (!A.checkForAllUses(UsePred, *this, *V,
+                             /* CheckBBLivenessOnly */ true,
+                             DepClassTy::OPTIONAL,
+                             /* IgnoreDroppableUses */ true, EquivalentUseCB)) {
+        return indicatePessimisticFixpoint();
+      }
+    }
+
+    return Uses.size() == NumUsesBefore ? ChangeStatus::UNCHANGED
+                                        : ChangeStatus::CHANGED;
+  }
+
+  bool isPotentialUse(const Use &U) const override {
+    return !isValidState() || Uses.contains(&U);
+  }
+
+  /// See AbstractAttribute::manifest(...).
+  ChangeStatus manifest(Attributor &A) override {
+    return ChangeStatus::UNCHANGED;
+  }
+
+  /// See AbstractAttribute::getAsStr().
+  const std::string getAsStr(Attributor *A) const override {
+    return "[" + std::to_string(Uses.size()) + " uses]";
+  }
+
+  void trackStatistics() const override {
+    STATS_DECLTRACK_FLOATING_ATTR(GlobalValuesTracked);
+  }
+
+private:
+  /// Set of (transitive) uses of this GlobalValue.
+  SmallPtrSet<const Use *, 8> Uses;
+};
+} // namespace
+
 /// ------------------------ Indirect Call Info  -------------------------------
 namespace {
 struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
@@ -12085,11 +12214,30 @@ struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
 
   ChangeStatus updateImpl(Attributor &A) override {
     CallBase *CB = cast<CallBase>(getCtxI());
+    const Use &CalleeUse = CB->getCalledOperandUse();
     Value *FP = CB->getCalledOperand();
 
     SmallSetVector<Function *, 4> AssumedCalleesNow;
     bool AllCalleesKnownNow = AllCalleesKnown;
 
+    auto CheckPotentialCalleeUse = [&](Function &PotentialCallee,
+                                       bool &UsedAssumedInformation) {
+      const auto *GIAA = A.getAAFor<AAGlobalValueInfo>(
+          *this, IRPosition::value(PotentialCallee), DepClassTy::OPTIONAL);
+      if (!GIAA || GIAA->isPotentialUse(CalleeUse))
+        return true;
+      UsedAssumedInformation = !GIAA->isAtFixpoint();
+      return false;
+    };
+
+    auto AddPotentialCallees = [&]() {
+      for (auto *PotentialCallee : PotentialCallees) {
+        bool UsedAssumedInformation = false;
+        if (CheckPotentialCalleeUse(*PotentialCallee, UsedAssumedInformation))
+          AssumedCalleesNow.insert(PotentialCallee);
+      }
+    };
+
     // Use simplification to find potential callees, if !callees was present,
     // fallback to that set if necessary.
     bool UsedAssumedInformation = false;
@@ -12099,7 +12247,7 @@ struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
                                       UsedAssumedInformation)) {
       if (PotentialCallees.empty())
         return indicatePessimisticFixpoint();
-      AssumedCalleesNow.set_union(PotentialCallees);
+      AddPotentialCallees();
     }
 
     // Try to find a reason for \p Fn not to be a potential callee. If none was
@@ -12112,6 +12260,13 @@ struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
       if (CachedResult.has_value())
         return CachedResult.value();
 
+      bool UsedAssumedInformation = false;
+      if (!CheckPotentialCalleeUse(Fn, UsedAssumedInformation)) {
+        if (!UsedAssumedInformation)
+          CachedResult = false;
+        return false;
+      }
+
       int NumFnArgs = Fn.arg_size();
       int NumCBArgs = CB->arg_size();
 
@@ -12147,16 +12302,12 @@ struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
         continue;
       }
       if (!PotentialCallees.empty()) {
-        AssumedCalleesNow.set_union(PotentialCallees);
+        AddPotentialCallees();
         break;
       }
       AllCalleesKnownNow = false;
     }
 
-    // If we can't specialize at all, give up now.
-    if (!AllCalleesKnownNow && AssumedCalleesNow.empty())
-      return indicatePessimisticFixpoint();
-
     if (AssumedCalleesNow == AssumedCallees &&
         AllCalleesKnown == AllCalleesKnownNow)
       return ChangeStatus::UNCHANGED;
@@ -12168,6 +12319,9 @@ struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
 
   /// See AbstractAttribute::manifest(...).
   ChangeStatus manifest(Attributor &A) override {
+    // If we can't specialize at all, give up now.
+    if (!AllCalleesKnown && AssumedCallees.empty())
+      return ChangeStatus::UNCHANGED;
 
     ChangeStatus Changed = ChangeStatus::UNCHANGED;
     CallBase *CB = cast<CallBase>(getCtxI());
@@ -12550,6 +12704,7 @@ const char AAAssumptionInfo::ID = 0;
 const char AAUnderlyingObjects::ID = 0;
 const char AAAddressSpace::ID = 0;
 const char AAIndirectCallInfo::ID = 0;
+const char AAGlobalValueInfo::ID = 0;
 
 // Macro magic to create the static generator function for attributes that
 // follow the naming scheme.
@@ -12688,6 +12843,8 @@ CREATE_ALL_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAUnderlyingObjects)
 
 CREATE_ABSTRACT_ATTRIBUTE_FOR_ONE_POSITION(IRP_CALL_SITE, CallSite,
                                            AAIndirectCallInfo)
+CREATE_ABSTRACT_ATTRIBUTE_FOR_ONE_POSITION(IRP_FLOAT, Floating,
+                                           AAGlobalValueInfo)
 
 CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAHeapToStack)
 CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAUndefinedBehavior)

diff  --git a/llvm/test/Transforms/Attributor/callgraph.ll b/llvm/test/Transforms/Attributor/callgraph.ll
index 272cf53ae9d8e0..547137046ca93f 100644
--- a/llvm/test/Transforms/Attributor/callgraph.ll
+++ b/llvm/test/Transforms/Attributor/callgraph.ll
@@ -18,7 +18,7 @@ define dso_local void @func1() {
   br i1 %1, label %2, label %3
 
 2:                                                ; preds = %0
-  call void @func2()
+  call void @func2(i1 false)
   br label %3
 
 3:                                                ; preds = %2, %0
@@ -27,14 +27,48 @@ define dso_local void @func1() {
 }
 
 declare void @func3()
-declare void @func4()
-
-define dso_local void @func2() {
-; CHECK-LABEL: @func2(
-; CHECK-NEXT:    call void @func4()
+define internal void @func4() {
+; CHECK-LABEL: @func4(
+; CHECK-NEXT:    call void @func3()
 ; CHECK-NEXT:    ret void
 ;
-  call void @func4()
+  call void @func3()
+  ret void
+}
+define internal void @internal_good() {
+; CHECK-LABEL: @internal_good(
+; CHECK-NEXT:    call void @void(ptr @func4)
+; CHECK-NEXT:    ret void
+;
+  call void @void(ptr @func4)
+  ret void
+}
+
+define dso_local void @func2(i1 %c) {
+; UPTO2-LABEL: @func2(
+; UPTO2-NEXT:    [[F:%.*]] = select i1 [[C:%.*]], ptr @internal_good, ptr @func4
+; UPTO2-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[F]], @func4
+; UPTO2-NEXT:    br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
+; UPTO2:       2:
+; UPTO2-NEXT:    call void @func4()
+; UPTO2-NEXT:    br label [[TMP6:%.*]]
+; UPTO2:       3:
+; UPTO2-NEXT:    br i1 true, label [[TMP4:%.*]], label [[TMP5:%.*]]
+; UPTO2:       4:
+; UPTO2-NEXT:    call void @internal_good()
+; UPTO2-NEXT:    br label [[TMP6]]
+; UPTO2:       5:
+; UPTO2-NEXT:    unreachable
+; UPTO2:       6:
+; UPTO2-NEXT:    ret void
+;
+; LIMI0-LABEL: @func2(
+; LIMI0-NEXT:    [[F:%.*]] = select i1 [[C:%.*]], ptr @internal_good, ptr @func4
+; LIMI0-NEXT:    call void [[F]](), !callees !0
+; LIMI0-NEXT:    ret void
+;
+  %f = select i1 %c, ptr @internal_good, ptr @func4
+  call void %f()
   ret void
 }
 
@@ -61,7 +95,7 @@ define void @func5(i32 %0) {
 ; LIMI0-LABEL: @func5(
 ; LIMI0-NEXT:    [[TMP2:%.*]] = icmp ne i32 [[TMP0:%.*]], 0
 ; LIMI0-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], ptr @func4, ptr @func3
-; LIMI0-NEXT:    call void [[TMP3]](), !callees !0
+; LIMI0-NEXT:    call void [[TMP3]](), !callees !1
 ; LIMI0-NEXT:    ret void
 ;
   %2 = icmp ne i32 %0, 0
@@ -151,7 +185,7 @@ define i32 @non_matching_fp1(i1 %c1, i1 %c2, i1 %c) {
 ; LIMI0-NEXT:    [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
 ; LIMI0-NEXT:    [[FP2:%.*]] = select i1 [[C2:%.*]], ptr @retFloatTakeFloat, ptr @void
 ; LIMI0-NEXT:    [[FP:%.*]] = select i1 [[C:%.*]], ptr [[FP1]], ptr [[FP2]]
-; LIMI0-NEXT:    [[CALL:%.*]] = call i32 [[FP]](i32 42), !callees !1
+; LIMI0-NEXT:    [[CALL:%.*]] = call i32 [[FP]](i32 42), !callees !2
 ; LIMI0-NEXT:    ret i32 [[CALL]]
 ;
   %fp1 = select i1 %c1, ptr @retI32, ptr @takeI32
@@ -214,7 +248,7 @@ define i32 @non_matching_fp1_noundef(i1 %c1, i1 %c2, i1 %c) {
 ; 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:    [[CALL:%.*]] = call i32 [[FP]](i32 42), !callees !3
 ; LIMI0-NEXT:    ret i32 [[CALL]]
 ;
   %fp1 = select i1 %c1, ptr @retI32, ptr @takeI32
@@ -489,6 +523,8 @@ define void @func6() {
   ret void
 }
 
+; Cannot be internal_good as it is internal and we see all uses.
+; Can be func4 since it escapes.
 define void @func7(ptr %unknown) {
 ; UPTO2-LABEL: @func7(
 ; UPTO2-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[UNKNOWN:%.*]], @func3
@@ -507,7 +543,7 @@ define void @func7(ptr %unknown) {
 ; UPTO2-NEXT:    ret void
 ;
 ; LIMI0-LABEL: @func7(
-; LIMI0-NEXT:    call void [[UNKNOWN:%.*]](), !callees !0
+; LIMI0-NEXT:    call void [[UNKNOWN:%.*]](), !callees !1
 ; LIMI0-NEXT:    ret void
 ;
   call void %unknown(), !callees !2
@@ -528,7 +564,7 @@ define void @undef_in_callees() {
 ;
 ; LIMI0-LABEL: @undef_in_callees(
 ; LIMI0-NEXT:  cond.end.i:
-; LIMI0-NEXT:    call void undef(ptr undef, i32 undef, ptr undef), !callees !5
+; LIMI0-NEXT:    call void undef(ptr undef, i32 undef, ptr undef), !callees !6
 ; LIMI0-NEXT:    ret void
 ;
 cond.end.i:
@@ -566,6 +602,9 @@ cond.end.i:
 ; These ones are added because of the callees metadata.
 ; DOT-DAG: Node[[FUNC7]] -> Node[[FUNC3]];
 ; DOT-DAG: Node[[FUNC7]] -> Node[[FUNC4]];
+
+; UTC_ARGS: --enable
+
 ;.
 ; UNLIM: [[META0:![0-9]+]] = !{!1}
 ; UNLIM: [[META1:![0-9]+]] = !{i64 0, i1 false}
@@ -577,12 +616,13 @@ cond.end.i:
 ; 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]+]] = !{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}
+; LIMI0: [[META0:![0-9]+]] = !{ptr @func4, ptr @internal_good}
+; LIMI0: [[META1:![0-9]+]] = !{ptr @func3, ptr @func4}
+; LIMI0: [[META2:![0-9]+]] = !{ptr @takeI32, ptr @retI32, ptr @void, ptr @retFloatTakeFloat}
+; LIMI0: [[META3:![0-9]+]] = !{ptr @takeI32, ptr @retI32, ptr @void}
+; LIMI0: [[META4:![0-9]+]] = !{!5}
+; LIMI0: [[META5:![0-9]+]] = !{i64 0, i1 false}
+; LIMI0: [[META6:![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