[llvm] eaf1b68 - [Attributor] Derive AACallEdges attribute

Kuter Dinel via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 17 14:29:40 PDT 2021


Author: Kuter Dinel
Date: 2021-06-18T03:29:22+03:00
New Revision: eaf1b6810ce0f40008b2b1d902750eafa3e198d3

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

LOG: [Attributor] Derive AACallEdges attribute

This attribute computes the optimistic live call edges using the attributor
liveness information. This attribute will be used for deriving a
inter-procedural function reachability attribute.

Reviewed By: jdoerfert

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

Added: 
    llvm/test/Transforms/Attributor/callgraph.ll

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

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h
index d02ec62fe107..5cad4c52d219 100644
--- a/llvm/include/llvm/Transforms/IPO/Attributor.h
+++ b/llvm/include/llvm/Transforms/IPO/Attributor.h
@@ -102,6 +102,7 @@
 #include "llvm/ADT/MapVector.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/iterator.h"
 #include "llvm/Analysis/AssumeBundleQueries.h"
 #include "llvm/Analysis/CFG.h"
 #include "llvm/Analysis/CGSCCPassManager.h"
@@ -115,6 +116,7 @@
 #include "llvm/IR/PassManager.h"
 #include "llvm/Support/Allocator.h"
 #include "llvm/Support/Casting.h"
+#include "llvm/Support/GraphWriter.h"
 #include "llvm/Support/TimeProfiler.h"
 #include "llvm/Transforms/Utils/CallGraphUpdater.h"
 
@@ -126,6 +128,7 @@ struct Attributor;
 struct AbstractAttribute;
 struct InformationCache;
 struct AAIsDead;
+struct AttributorCallGraph;
 
 class AAManager;
 class AAResults;
@@ -1760,6 +1763,7 @@ struct Attributor {
   ///}
 
   friend AADepGraph;
+  friend AttributorCallGraph;
 };
 
 /// An interface to query the internal state of an abstract attribute.
@@ -3855,6 +3859,159 @@ struct AANoUndef
   static const char ID;
 };
 
+struct AACallGraphNode;
+struct AACallEdges;
+
+/// An Iterator for call edges, creates AACallEdges attributes in a lazy way.
+/// This iterator becomes invalid if the underlying edge list changes.
+/// So This shouldn't outlive a iteration of Attributor.
+class AACallEdgeIterator
+    : public iterator_adaptor_base<AACallEdgeIterator,
+                                   SetVector<Function *>::iterator> {
+  AACallEdgeIterator(Attributor &A, SetVector<Function *>::iterator Begin)
+      : iterator_adaptor_base(Begin), A(A) {}
+
+public:
+  AACallGraphNode *operator*() const;
+
+private:
+  Attributor &A;
+  friend AACallEdges;
+  friend AttributorCallGraph;
+};
+
+struct AACallGraphNode {
+  AACallGraphNode(Attributor &A) : A(A) {}
+
+  virtual AACallEdgeIterator optimisticEdgesBegin() const = 0;
+  virtual AACallEdgeIterator optimisticEdgesEnd() const = 0;
+
+  /// Iterator range for exploring the call graph.
+  iterator_range<AACallEdgeIterator> optimisticEdgesRange() const {
+    return iterator_range<AACallEdgeIterator>(optimisticEdgesBegin(),
+                                              optimisticEdgesEnd());
+  }
+
+protected:
+  /// Reference to Attributor needed for GraphTraits implementation.
+  Attributor &A;
+};
+
+/// An abstract state for querying live call edges.
+/// This interface uses the Attributor's optimistic liveness
+/// information to compute the edges that are alive.
+struct AACallEdges : public StateWrapper<BooleanState, AbstractAttribute>,
+                     AACallGraphNode {
+  using Base = StateWrapper<BooleanState, AbstractAttribute>;
+
+  AACallEdges(const IRPosition &IRP, Attributor &A)
+      : Base(IRP), AACallGraphNode(A) {}
+
+  /// Get the optimistic edges.
+  virtual const SetVector<Function *> &getOptimisticEdges() const = 0;
+
+  /// Is there in this function call with a unknown Callee.
+  virtual bool hasUnknownCallee() const = 0;
+
+  /// Iterator for exploring the call graph.
+  AACallEdgeIterator optimisticEdgesBegin() const override {
+    return AACallEdgeIterator(A, getOptimisticEdges().begin());
+  }
+
+  /// Iterator for exploring the call graph.
+  AACallEdgeIterator optimisticEdgesEnd() const override {
+    return AACallEdgeIterator(A, getOptimisticEdges().end());
+  }
+
+  /// Create an abstract attribute view for the position \p IRP.
+  static AACallEdges &createForPosition(const IRPosition &IRP, Attributor &A);
+
+  /// See AbstractAttribute::getName()
+  const std::string getName() const override { return "AACallEdges"; }
+
+  /// See AbstractAttribute::getIdAddr()
+  const char *getIdAddr() const override { return &ID; }
+
+  /// This function should return true if the type of the \p AA is AACallEdges.
+  static bool classof(const AbstractAttribute *AA) {
+    return (AA->getIdAddr() == &ID);
+  }
+
+  /// Unique ID (due to the unique address)
+  static const char ID;
+};
+
+// Synthetic root node for the Attributor's internal call graph.
+struct AttributorCallGraph : public AACallGraphNode {
+  AttributorCallGraph(Attributor &A) : AACallGraphNode(A) {}
+
+  AACallEdgeIterator optimisticEdgesBegin() const override {
+    return AACallEdgeIterator(A, A.Functions.begin());
+  }
+
+  AACallEdgeIterator optimisticEdgesEnd() const override {
+    return AACallEdgeIterator(A, A.Functions.end());
+  }
+
+  /// Force populate the entire call graph.
+  void populateAll() const {
+    for (const AACallGraphNode *AA : optimisticEdgesRange()) {
+      // Nothing else to do here.
+      (void)AA;
+    }
+  }
+
+  void print();
+};
+
+template <> struct GraphTraits<AACallGraphNode *> {
+  using NodeRef = AACallGraphNode *;
+  using ChildIteratorType = AACallEdgeIterator;
+
+  static AACallEdgeIterator child_begin(AACallGraphNode *Node) {
+    return Node->optimisticEdgesBegin();
+  }
+
+  static AACallEdgeIterator child_end(AACallGraphNode *Node) {
+    return Node->optimisticEdgesEnd();
+  }
+};
+
+template <>
+struct GraphTraits<AttributorCallGraph *>
+    : public GraphTraits<AACallGraphNode *> {
+  using nodes_iterator = AACallEdgeIterator;
+
+  static AACallGraphNode *getEntryNode(AttributorCallGraph *G) {
+    return static_cast<AACallGraphNode *>(G);
+  }
+
+  static AACallEdgeIterator nodes_begin(const AttributorCallGraph *G) {
+    return G->optimisticEdgesBegin();
+  }
+
+  static AACallEdgeIterator nodes_end(const AttributorCallGraph *G) {
+    return G->optimisticEdgesEnd();
+  }
+};
+
+template <>
+struct DOTGraphTraits<AttributorCallGraph *> : public DefaultDOTGraphTraits {
+  DOTGraphTraits(bool Simple = false) : DefaultDOTGraphTraits(Simple) {}
+
+  std::string getNodeLabel(const AACallGraphNode *Node,
+                           const AttributorCallGraph *Graph) {
+    const AACallEdges *AACE = static_cast<const AACallEdges *>(Node);
+    return AACE->getAssociatedFunction()->getName().str();
+  }
+
+  static bool isNodeHidden(const AACallGraphNode *Node,
+                           const AttributorCallGraph *Graph) {
+    // Hide the synth root.
+    return static_cast<const AACallGraphNode *>(Graph) == Node;
+  }
+};
+
 /// Run options, used by the pass manager.
 enum AttributorRunOption {
   NONE = 0,

diff  --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp
index 143be7aef27c..414bd2e3db8a 100644
--- a/llvm/lib/Transforms/IPO/Attributor.cpp
+++ b/llvm/lib/Transforms/IPO/Attributor.cpp
@@ -148,6 +148,11 @@ static cl::opt<bool> EnableCallSiteSpecific(
     cl::desc("Allow the Attributor to do call site specific analysis"),
     cl::init(false));
 
+static cl::opt<bool>
+    PrintCallGraph("attributor-print-call-graph", cl::Hidden,
+                   cl::desc("Print Attributor's internal call graph"),
+                   cl::init(false));
+
 /// Logic operators for the change status enum class.
 ///
 ///{
@@ -1466,6 +1471,10 @@ ChangeStatus Attributor::cleanupIR() {
 
 ChangeStatus Attributor::run() {
   TimeTraceScope TimeScope("Attributor::run");
+  AttributorCallGraph ACallGraph(*this);
+
+  if (PrintCallGraph)
+    ACallGraph.populateAll();
 
   Phase = AttributorPhase::UPDATE;
   runTillFixpoint();
@@ -1486,6 +1495,9 @@ ChangeStatus Attributor::run() {
   Phase = AttributorPhase::CLEANUP;
   ChangeStatus CleanupChange = cleanupIR();
 
+  if (PrintCallGraph)
+    ACallGraph.print();
+
   return ManifestChange | CleanupChange;
 }
 

diff  --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
index ea627cac344e..9fbbb980f021 100644
--- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
+++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
@@ -30,9 +30,10 @@
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/NoFolder.h"
 #include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/raw_ostream.h"
 #include "llvm/Transforms/IPO/ArgumentPromotion.h"
 #include "llvm/Transforms/Utils/Local.h"
-
 #include <cassert>
 
 using namespace llvm;
@@ -134,6 +135,7 @@ PIPE_OPERATOR(AAPrivatizablePtr)
 PIPE_OPERATOR(AAUndefinedBehavior)
 PIPE_OPERATOR(AAPotentialValues)
 PIPE_OPERATOR(AANoUndef)
+PIPE_OPERATOR(AACallEdges)
 
 #undef PIPE_OPERATOR
 } // namespace llvm
@@ -8133,8 +8135,121 @@ struct AANoUndefCallSiteReturned final
   /// See AbstractAttribute::trackStatistics()
   void trackStatistics() const override { STATS_DECLTRACK_CSRET_ATTR(noundef) }
 };
+
+struct AACallEdgesFunction : public AACallEdges {
+  AACallEdgesFunction(const IRPosition &IRP, Attributor &A)
+      : AACallEdges(IRP, A) {}
+
+  /// See AbstractAttribute::updateImpl(...).
+  ChangeStatus updateImpl(Attributor &A) override {
+    ChangeStatus Change = ChangeStatus::UNCHANGED;
+    bool OldHasUnknownCallee = HasUnknownCallee;
+
+    auto AddCalledFunction = [&](Function *Fn) {
+      if (CalledFunctions.insert(Fn)) {
+        Change = ChangeStatus::CHANGED;
+        LLVM_DEBUG(dbgs() << "[AACallEdges] New call edge: " << Fn->getName()
+                          << "\n");
+      }
+    };
+
+    auto VisitValue = [&](Value &V, const Instruction *CtxI, bool &HasUnknown,
+                          bool Stripped) -> bool {
+      if (Function *Fn = dyn_cast<Function>(&V)) {
+        AddCalledFunction(Fn);
+      } else {
+        LLVM_DEBUG(dbgs() << "[AACallEdges] Unrecognized value: " << V << "\n");
+        HasUnknown = true;
+      }
+
+      // Explore all values.
+      return true;
+    };
+
+    // Process any value that we might call.
+    auto ProcessCalledOperand = [&](Value *V, Instruction *Ctx) {
+      if (!genericValueTraversal<AACallEdges, bool>(A, IRPosition::value(*V),
+                                                    *this, HasUnknownCallee,
+                                                    VisitValue, nullptr, false))
+        // If we haven't gone through all values, assume that there are unknown
+        // callees.
+        HasUnknownCallee = true;
+    };
+
+    auto ProcessCallInst = [&](Instruction &Inst) {
+      CallBase &CB = static_cast<CallBase &>(Inst);
+
+      // Process callee metadata if available.
+      if (auto *MD = Inst.getMetadata(LLVMContext::MD_callees)) {
+        for (auto &Op : MD->operands()) {
+          Function *Callee = mdconst::extract_or_null<Function>(Op);
+          if (Callee)
+            AddCalledFunction(Callee);
+        }
+        // Callees metadata grantees that the called function is one of its
+        // operands, So we are done.
+        return true;
+      }
+
+      // The most simple case.
+      ProcessCalledOperand(CB.getCalledOperand(), &Inst);
+
+      // Process callback functions.
+      SmallVector<const Use *, 4u> CallbackUses;
+      AbstractCallSite::getCallbackUses(CB, CallbackUses);
+      for (const Use *U : CallbackUses)
+        ProcessCalledOperand(U->get(), &Inst);
+
+      return true;
+    };
+
+    // Visit all callable instructions.
+    if (!A.checkForAllCallLikeInstructions(ProcessCallInst, *this))
+      // If we haven't looked at all call like instructions, assume that there
+      // are unknown callees.
+      HasUnknownCallee = true;
+    // Track changes.
+    if (OldHasUnknownCallee != HasUnknownCallee)
+      Change = ChangeStatus::CHANGED;
+
+    return Change;
+  }
+
+  virtual const SetVector<Function *> &getOptimisticEdges() const override {
+    return CalledFunctions;
+  };
+
+  virtual bool hasUnknownCallee() const override { return HasUnknownCallee; }
+
+  const std::string getAsStr() const override {
+    return "CallEdges[" + std::to_string(HasUnknownCallee) + "," +
+           std::to_string(CalledFunctions.size()) + "]";
+  }
+
+  void trackStatistics() const override {}
+
+  /// Optimistic set of functions that might be called by this function.
+  SetVector<Function *> CalledFunctions;
+
+  /// Does this function have a call to a function that we don't know about.
+  bool HasUnknownCallee;
+};
+
 } // namespace
 
+AACallGraphNode *AACallEdgeIterator::operator*() const {
+  return static_cast<AACallGraphNode *>(const_cast<AACallEdges *>(
+      &A.getOrCreateAAFor<AACallEdges>(IRPosition::function(**I))));
+}
+
+void AttributorCallGraph::print() {
+  std::string Filename = "AttributorCallGraph.dot";
+  std::error_code EC;
+
+  raw_fd_ostream File(Filename, EC, sys::fs::OF_TextWithCRLF);
+  llvm::WriteGraph(File, this);
+}
+
 const char AAReturnedValues::ID = 0;
 const char AANoUnwind::ID = 0;
 const char AANoSync::ID = 0;
@@ -8158,6 +8273,7 @@ const char AAMemoryLocation::ID = 0;
 const char AAValueConstantRange::ID = 0;
 const char AAPotentialValues::ID = 0;
 const char AANoUndef::ID = 0;
+const char AACallEdges::ID = 0;
 
 // Macro magic to create the static generator function for attributes that
 // follow the naming scheme.
@@ -8277,6 +8393,7 @@ CREATE_ALL_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoFree)
 CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAHeapToStack)
 CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAReachability)
 CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAUndefinedBehavior)
+CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AACallEdges)
 
 CREATE_NON_RET_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAMemoryBehavior)
 

diff  --git a/llvm/test/Transforms/Attributor/callgraph.ll b/llvm/test/Transforms/Attributor/callgraph.ll
new file mode 100644
index 000000000000..f3dfab2234de
--- /dev/null
+++ b/llvm/test/Transforms/Attributor/callgraph.ll
@@ -0,0 +1,106 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -passes=attributor -attributor-print-call-graph -S < %s | FileCheck %s --check-prefixes=CHECK
+; RUN: FileCheck %s -input-file=AttributorCallGraph.dot --check-prefix=DOT
+
+
+define dso_local void @func1() {
+; CHECK-LABEL: @func1(
+; CHECK-NEXT:    br label [[TMP2:%.*]]
+; CHECK:       1:
+; CHECK-NEXT:    unreachable
+; CHECK:       2:
+; CHECK-NEXT:    call void @func3()
+; CHECK-NEXT:    ret void
+;
+  %1 = icmp ne i32 0, 0
+  br i1 %1, label %2, label %3
+
+2:                                                ; preds = %0
+  call void @func2()
+  br label %3
+
+3:                                                ; preds = %2, %0
+  call void () @func3()
+  ret void
+}
+
+declare void @func3()
+declare void @func4()
+
+define dso_local void @func2() {
+; CHECK-LABEL: @func2(
+; CHECK-NEXT:    call void @func4()
+; CHECK-NEXT:    ret void
+;
+  call void @func4()
+  ret void
+}
+
+
+define void @func5(i32 %0) {
+; CHECK-LABEL: @func5(
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp ne i32 [[TMP0:%.*]], 0
+; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], void ()* @func4, void ()* @func3
+; CHECK-NEXT:    call void [[TMP3]]()
+; CHECK-NEXT:    ret void
+;
+  %2 = icmp ne i32 %0, 0
+  %3 = select i1 %2, void ()* @func4, void ()* @func3
+  call void () %3()
+  ret void
+}
+
+define void @broker(void ()* %unknown) !callback !0 {
+; CHECK-LABEL: @broker(
+; CHECK-NEXT:    call void [[UNKNOWN:%.*]]()
+; CHECK-NEXT:    ret void
+;
+  call void %unknown()
+  ret void
+}
+
+define void @func6() {
+; CHECK-LABEL: @func6(
+; CHECK-NEXT:    call void @broker(void ()* nocapture nofree noundef @func3)
+; CHECK-NEXT:    ret void
+;
+  call void @broker(void ()* @func3)
+  ret void
+}
+
+define void @func7(void ()* %unknown) {
+; CHECK-LABEL: @func7(
+; CHECK-NEXT:    call void [[UNKNOWN:%.*]](), !callees !2
+; CHECK-NEXT:    ret void
+;
+  call void %unknown(), !callees !2
+  ret void
+}
+
+!0 = !{!1}
+!1 = !{i64 0, i1 false}
+!2 = !{void ()* @func3, void ()* @func4}
+
+; DOT-DAG: Node[[FUNC1:0x[a-z0-9]+]] [shape=record,label="{func1}"];
+; DOT-DAG: Node[[FUNC2:0x[a-z0-9]+]] [shape=record,label="{func2}"];
+; DOT-DAG: Node[[FUNC3:0x[a-z0-9]+]] [shape=record,label="{func3}"];
+; DOT-DAG: Node[[FUNC4:0x[a-z0-9]+]] [shape=record,label="{func4}"];
+; DOT-DAG: Node[[FUNC5:0x[a-z0-9]+]] [shape=record,label="{func5}"];
+; DOT-DAG: Node[[FUNC6:0x[a-z0-9]+]] [shape=record,label="{func6}"];
+; DOT-DAG: Node[[FUNC7:0x[a-z0-9]+]] [shape=record,label="{func7}"];
+
+; DOT-DAG: Node[[BROKER:0x[a-z0-9]+]] [shape=record,label="{broker}"];
+
+; DOT-DAG: Node[[FUNC1]] -> Node[[FUNC3]];
+; DOT-DAG: Node[[FUNC2]] -> Node[[FUNC4]];
+; DOT-DAG: Node[[FUNC5]] -> Node[[FUNC3]];
+; DOT-DAG: Node[[FUNC5]] -> Node[[FUNC4]];
+
+; DOT-DAG: Node[[FUNC6]] -> Node[[BROKER]];
+
+; This one gets added because of the callback metadata.
+; DOT-DAG: Node[[FUNC6]] -> Node[[FUNC3]];
+
+; These ones are added because of the callees metadata.
+; DOT-DAG: Node[[FUNC7]] -> Node[[FUNC3]];
+; DOT-DAG: Node[[FUNC7]] -> Node[[FUNC4]];


        


More information about the llvm-commits mailing list