[llvm] 898bbc2 - [Attributor] Lazily collect function information

Johannes Doerfert via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 15 20:28:43 PDT 2020


Author: Johannes Doerfert
Date: 2020-04-15T22:26:38-05:00
New Revision: 898bbc252a72b950cae138c3cad1e0ec4e17c902

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

LOG: [Attributor] Lazily collect function information

Before, we eagerly analyzed all the functions to collect information
about them, e.g. what instructions may read/write memory. This had
multiple drawbacks:
  - In CGSCC-mode we can end up looking at a callee which is not in the
    SCC but for which we need an initialized cache.
  - We end up looking at functions that we deem dead and never need to
    analyze in the first place.
  - We have a implicit dependence which is easy to break.

This patch moves the function analysis into the information cache and
makes it lazy. There is no real functional change expected except due to
the first reason above.

Added: 
    

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

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h
index aa7885915176..cc4390f41d49 100644
--- a/llvm/include/llvm/Transforms/IPO/Attributor.h
+++ b/llvm/include/llvm/Transforms/IPO/Attributor.h
@@ -583,21 +583,25 @@ struct InformationCache {
             }),
         AG(AG), CGSCC(CGSCC) {}
 
+  ~InformationCache() {
+    DeleteContainerSeconds(FuncInfoMap);
+  }
+
   /// A map type from opcodes to instructions with this opcode.
   using OpcodeInstMapTy = DenseMap<unsigned, SmallVector<Instruction *, 32>>;
 
   /// Return the map that relates "interesting" opcodes with all instructions
   /// with that opcode in \p F.
   OpcodeInstMapTy &getOpcodeInstMapForFunction(const Function &F) {
-    return FuncInstOpcodeMap[&F];
+    return getFunctionInfo(F).OpcodeInstMap;
   }
 
   /// A vector type to hold instructions.
-  using InstructionVectorTy = std::vector<Instruction *>;
+  using InstructionVectorTy = SmallVector<Instruction *, 4>;
 
   /// Return the instructions in \p F that may read or write memory.
   InstructionVectorTy &getReadOrWriteInstsForFunction(const Function &F) {
-    return FuncRWInstsMap[&F];
+    return getFunctionInfo(F).RWInsts;
   }
 
   /// Return MustBeExecutedContextExplorer
@@ -617,9 +621,9 @@ struct InformationCache {
 
   /// Return true if \p Arg is involved in a must-tail call, thus the argument
   /// of the caller or callee.
-  bool isInvolvedInMustTailCall(const Argument &Arg) const {
-    return FunctionsCalledViaMustTail.count(Arg.getParent()) ||
-           FunctionsWithMustTailCall.count(Arg.getParent());
+  bool isInvolvedInMustTailCall(const Argument &Arg) {
+    FunctionInfo &FI = getFunctionInfo(*Arg.getParent());
+    return FI.CalledViaMustTail || FI.ContainsMustTailCall;
   }
 
   /// Return the analysis result from a pass \p AP for function \p F.
@@ -642,24 +646,40 @@ struct InformationCache {
   const RetainedKnowledgeMap &getKnowledgeMap() const { return KnowledgeMap; }
 
 private:
-  /// A map type from functions to opcode to instruction maps.
-  using FuncInstOpcodeMapTy = DenseMap<const Function *, OpcodeInstMapTy>;
+  struct FunctionInfo {
+    /// A nested map that remembers all instructions in a function with a
+    /// certain instruction opcode (Instruction::getOpcode()).
+    OpcodeInstMapTy OpcodeInstMap;
 
-  /// A map type from functions to their read or write instructions.
-  using FuncRWInstsMapTy = DenseMap<const Function *, InstructionVectorTy>;
+    /// A map from functions to their instructions that may read or write
+    /// memory.
+    InstructionVectorTy RWInsts;
 
-  /// A nested map that remembers all instructions in a function with a certain
-  /// instruction opcode (Instruction::getOpcode()).
-  FuncInstOpcodeMapTy FuncInstOpcodeMap;
+    /// Function is called by a `musttail` call.
+    bool CalledViaMustTail;
 
-  /// A map from functions to their instructions that may read or write memory.
-  FuncRWInstsMapTy FuncRWInstsMap;
+    /// Function contains a `musttail` call.
+    bool ContainsMustTailCall;
+  };
 
-  /// Functions called by a `musttail` call.
-  SmallPtrSet<Function *, 8> FunctionsCalledViaMustTail;
+  /// A map type from functions to informatio about it.
+  DenseMap<const Function *, FunctionInfo *> FuncInfoMap;
 
-  /// Functions containing a `musttail` call.
-  SmallPtrSet<Function *, 8> FunctionsWithMustTailCall;
+  /// Return information about the function \p F, potentially by creating it.
+  FunctionInfo &getFunctionInfo(const Function &F) {
+    FunctionInfo *&FI = FuncInfoMap[&F];
+    if (!FI) {
+      FI = new FunctionInfo();
+      initializeInformationCache(F, *FI);
+    }
+    return *FI;
+  }
+
+  /// Initialize the function information cache \p FI for the function \p F.
+  ///
+  /// This method needs to be called for all function that might be looked at
+  /// through the information cache interface *prior* to looking at them.
+  void initializeInformationCache(const Function &F, FunctionInfo &FI);
 
   /// The datalayout used in the module.
   const DataLayout &DL;
@@ -822,12 +842,6 @@ struct Attributor {
   /// various places.
   void identifyDefaultAbstractAttributes(Function &F);
 
-  /// Initialize the information cache for queries regarding function \p F.
-  ///
-  /// This method needs to be called for all function that might be looked at
-  /// through the information cache interface *prior* to looking at them.
-  void initializeInformationCache(Function &F);
-
   /// Determine whether the function \p F is IPO amendable
   ///
   /// If a function is exactly defined or it has alwaysinline attribute

diff  --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp
index 2c67edac7406..647518522c36 100644
--- a/llvm/lib/Transforms/IPO/Attributor.cpp
+++ b/llvm/lib/Transforms/IPO/Attributor.cpp
@@ -32,8 +32,7 @@ using namespace llvm;
 
 #define DEBUG_TYPE "attributor"
 
-STATISTIC(NumFnDeleted,
-          "Number of function deleted");
+STATISTIC(NumFnDeleted, "Number of function deleted");
 STATISTIC(NumFnWithExactDefinition,
           "Number of functions with exact definitions");
 STATISTIC(NumFnWithoutExactDefinition,
@@ -137,7 +136,6 @@ static bool addIfNotExistent(LLVMContext &Ctx, const Attribute &Attr,
   llvm_unreachable("Expected enum or string attribute!");
 }
 
-
 Argument *IRPosition::getAssociatedArgument() const {
   if (getPositionKind() == IRP_ARGUMENT)
     return cast<Argument>(&getAnchorValue());
@@ -1589,13 +1587,16 @@ ChangeStatus Attributor::rewriteFunctionSignatures(
   return Changed;
 }
 
-void Attributor::initializeInformationCache(Function &F) {
+void InformationCache::initializeInformationCache(const Function &CF,
+                                                  FunctionInfo &FI) {
+  // As we do not modify the function here we can remove the const
+  // withouth breaking implicit assumptions. At the end of the day, we could
+  // initialize the cache eagerly which would look the same to the users.
+  Function &F = const_cast<Function &>(CF);
 
   // Walk all instructions to find interesting instructions that might be
   // queried by abstract attributes during their initialization or update.
   // This has to happen before we create attributes.
-  auto &ReadOrWriteInsts = InfoCache.FuncRWInstsMap[&F];
-  auto &InstOpcodeMap = InfoCache.FuncInstOpcodeMap[&F];
 
   for (Instruction &I : instructions(&F)) {
     bool IsInterestingOpcode = false;
@@ -1617,11 +1618,11 @@ void Attributor::initializeInformationCache(Function &F) {
       // For `must-tail` calls we remember the caller and callee.
       if (IntrinsicInst *Assume = dyn_cast<IntrinsicInst>(&I)) {
         if (Assume->getIntrinsicID() == Intrinsic::assume)
-          fillMapFromAssume(*Assume, InfoCache.KnowledgeMap);
+          fillMapFromAssume(*Assume, KnowledgeMap);
       } else if (cast<CallInst>(I).isMustTailCall()) {
-        InfoCache.FunctionsWithMustTailCall.insert(&F);
-        InfoCache.FunctionsCalledViaMustTail.insert(
-            cast<CallInst>(I).getCalledFunction());
+        FI.ContainsMustTailCall = true;
+        if (const Function *Callee = cast<CallInst>(I).getCalledFunction())
+          getFunctionInfo(*Callee).CalledViaMustTail = true;
       }
       LLVM_FALLTHROUGH;
     case Instruction::CallBr:
@@ -1640,14 +1641,14 @@ void Attributor::initializeInformationCache(Function &F) {
       IsInterestingOpcode = true;
     }
     if (IsInterestingOpcode)
-      InstOpcodeMap[I.getOpcode()].push_back(&I);
+      FI.OpcodeInstMap[I.getOpcode()].push_back(&I);
     if (I.mayReadOrWriteMemory())
-      ReadOrWriteInsts.push_back(&I);
+      FI.RWInsts.push_back(&I);
   }
 
   if (F.hasFnAttribute(Attribute::AlwaysInline) &&
       isInlineViable(F).isSuccess())
-    InfoCache.InlineableFunctions.insert(&F);
+    InlineableFunctions.insert(&F);
 }
 
 void Attributor::recordDependence(const AbstractAttribute &FromAA,
@@ -1674,11 +1675,13 @@ void Attributor::identifyDefaultAbstractAttributes(Function &F) {
   // In non-module runs we need to look at the call sites of a function to
   // determine if it is part of a must-tail call edge. This will influence what
   // attributes we can derive.
-  if (!isModulePass() && !InfoCache.FunctionsCalledViaMustTail.count(&F))
+  InformationCache::FunctionInfo &FI = InfoCache.getFunctionInfo(F);
+  if (!isModulePass() && !FI.CalledViaMustTail) {
     for (const Use &U : F.uses())
       if (ImmutableCallSite ICS = ImmutableCallSite(U.getUser()))
         if (ICS.isCallee(&U) && ICS.isMustTailCall())
-          InfoCache.FunctionsCalledViaMustTail.insert(&F);
+          FI.CalledViaMustTail = true;
+  }
 
   IRPosition FPos = IRPosition::function(F);
 
@@ -1950,13 +1953,6 @@ static bool runAttributorOnFunctions(InformationCache &InfoCache,
   // while we identify default attribute opportunities.
   Attributor A(Functions, InfoCache, CGUpdater, DepRecInterval);
 
-  // Note: _Don't_ combine/fuse this loop with the one below because
-  // when A.identifyDefaultAbstractAttributes() is called for one
-  // function, it assumes that the information cach has been
-  // initialized for _all_ functions.
-  for (Function *F : Functions)
-    A.initializeInformationCache(*F);
-
   // Create shallow wrappers for all functions that are not IPO amendable
   if (AllowShallowWrappers)
     for (Function *F : Functions)


        


More information about the llvm-commits mailing list