[llvm] 876d133 - [AssumeBundles] Add API to fill a map from operand bundles of an llvm.assume.

via llvm-commits llvm-commits at lists.llvm.org
Tue Mar 3 05:23:09 PST 2020


Author: Tyker
Date: 2020-03-03T14:22:52+01:00
New Revision: 876d13378931bee3dcefafff8729c40d5457ff31

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

LOG: [AssumeBundles] Add API to fill a map from operand bundles of an llvm.assume.

Summary: This patch adds a new way to query operand bundles of an llvm.assume that is much better suited to some users like the Attributor that need to do many queries on the operand bundles of llvm.assume. Some modifications of the IR like replaceAllUsesWith can cause information in the map to be outdated, so this API is more suited to analysis passes and passes that don't make modification that could invalidate the map.

Reviewers: jdoerfert, sstefan1, uenoku

Reviewed By: jdoerfert

Subscribers: hiraditya, llvm-commits

Tags: #llvm

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

Added: 
    

Modified: 
    llvm/include/llvm/Transforms/Utils/KnowledgeRetention.h
    llvm/lib/Transforms/Utils/KnowledgeRetention.cpp
    llvm/unittests/Transforms/Utils/KnowledgeRetentionTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Transforms/Utils/KnowledgeRetention.h b/llvm/include/llvm/Transforms/Utils/KnowledgeRetention.h
index 27d83373e074..c3baf8a43c0d 100644
--- a/llvm/include/llvm/Transforms/Utils/KnowledgeRetention.h
+++ b/llvm/include/llvm/Transforms/Utils/KnowledgeRetention.h
@@ -19,6 +19,7 @@
 #include "llvm/IR/Attributes.h"
 #include "llvm/IR/Instruction.h"
 #include "llvm/IR/PassManager.h"
+#include "llvm/ADT/DenseMap.h"
 
 namespace llvm {
 
@@ -58,9 +59,41 @@ inline bool hasAttributeInAssume(CallInst &AssumeCI, Value *IsOn,
       AssumeCI, IsOn, Attribute::getNameFromAttrKind(Kind), ArgVal, AQR);
 }
 
-/// TODO: Add an function to create/fill a map from the bundle when users intend
-/// to make many 
diff erent queries on the same bundles. to be used for example
-/// in the Attributor.
+template<> struct DenseMapInfo<Attribute::AttrKind> {
+  static constexpr auto MaxValue = std::numeric_limits<
+      std::underlying_type<Attribute::AttrKind>::type>::max();
+  static Attribute::AttrKind getEmptyKey() {
+    return static_cast<Attribute::AttrKind>(MaxValue);
+  }
+  static Attribute::AttrKind getTombstoneKey() {
+    return static_cast<Attribute::AttrKind>(MaxValue - 1);
+  }
+  static unsigned getHashValue(Attribute::AttrKind AK) {
+    return hash_combine(AK);
+  }
+  static bool isEqual(Attribute::AttrKind LHS, Attribute::AttrKind RHS) {
+    return LHS == RHS;
+  }
+};
+
+/// The map Key contains the Value on for which the attribute is valid and
+/// the Attribute that is valid for that value.
+/// If the Attribute is not on any value, the Value is nullptr.
+using RetainedKnowledgeKey = std::pair<Value *, Attribute::AttrKind>;
+
+struct MinMax {
+  unsigned Min;
+  unsigned Max;
+};
+
+using RetainedKnowledgeMap = DenseMap<RetainedKnowledgeKey, MinMax>;
+
+/// Insert into the map all the informations contained in the operand bundles of
+/// the llvm.assume. This should be used instead of hasAttributeInAssume when
+/// many queries are going to be made on the same llvm.assume.
+/// String attributes are not inserted in the map.
+/// If the IR changes the map will be outdated.
+void fillMapFromAssume(CallInst &AssumeCI, RetainedKnowledgeMap &Result);
 
 //===----------------------------------------------------------------------===//
 // Utilities for testing

diff  --git a/llvm/lib/Transforms/Utils/KnowledgeRetention.cpp b/llvm/lib/Transforms/Utils/KnowledgeRetention.cpp
index f3c9ee42b77f..f2f87f9200ed 100644
--- a/llvm/lib/Transforms/Utils/KnowledgeRetention.cpp
+++ b/llvm/lib/Transforms/Utils/KnowledgeRetention.cpp
@@ -171,6 +171,18 @@ CallInst *llvm::BuildAssumeFromInst(const Instruction *I, Module *M) {
   return Builder.build();
 }
 
+static bool BundleHasArguement(const CallBase::BundleOpInfo &BOI,
+                               unsigned Idx) {
+  return BOI.End - BOI.Begin > Idx;
+}
+
+static Value *getValueFromBundleOpInfo(IntrinsicInst &Assume,
+                                const CallBase::BundleOpInfo &BOI,
+                                unsigned Idx) {
+  assert(BundleHasArguement(BOI, Idx) && "index out of range");
+  return (Assume.op_begin() + BOI.Begin + Idx)->get();
+}
+
 #ifndef NDEBUG
 
 static bool isExistingAttribute(StringRef Name) {
@@ -219,12 +231,6 @@ bool llvm::hasAttributeInAssume(CallInst &AssumeCI, Value *IsOn,
                             return LHS < BOI.Tag->getKey();
                           }));
 
-  auto getValueFromBundleOpInfo = [&Assume](const CallBase::BundleOpInfo &BOI,
-                                            unsigned Idx) {
-    assert(BOI.End - BOI.Begin > Idx && "index out of range");
-    return (Assume.op_begin() + BOI.Begin + Idx)->get();
-  };
-
   if (Lookup == Assume.bundle_op_info_end() ||
       Lookup->Tag->getKey() != AttrName)
     return false;
@@ -235,7 +241,7 @@ bool llvm::hasAttributeInAssume(CallInst &AssumeCI, Value *IsOn,
       if (Lookup == Assume.bundle_op_info_end() ||
           Lookup->Tag->getKey() != AttrName)
         return false;
-      if (getValueFromBundleOpInfo(*Lookup, BOIE_WasOn) == IsOn)
+      if (getValueFromBundleOpInfo(Assume, *Lookup, BOIE_WasOn) == IsOn)
         break;
       if (AQR == AssumeQuery::Highest &&
           Lookup == Assume.bundle_op_info_begin())
@@ -247,12 +253,41 @@ bool llvm::hasAttributeInAssume(CallInst &AssumeCI, Value *IsOn,
   if (Lookup->End - Lookup->Begin < BOIE_Argument)
     return true;
   if (ArgVal)
-    *ArgVal =
-        cast<ConstantInt>(getValueFromBundleOpInfo(*Lookup, BOIE_Argument))
-            ->getZExtValue();
+    *ArgVal = cast<ConstantInt>(
+                  getValueFromBundleOpInfo(Assume, *Lookup, BOIE_Argument))
+                  ->getZExtValue();
   return true;
 }
 
+void llvm::fillMapFromAssume(CallInst &AssumeCI, RetainedKnowledgeMap &Result) {
+  IntrinsicInst &Assume = cast<IntrinsicInst>(AssumeCI);
+  assert(Assume.getIntrinsicID() == Intrinsic::assume &&
+         "this function is intended to be used on llvm.assume");
+  for (auto &Bundles : Assume.bundle_op_infos()) {
+    std::pair<Value *, Attribute::AttrKind> Key{
+        nullptr, Attribute::getAttrKindFromName(Bundles.Tag->getKey())};
+    if (BundleHasArguement(Bundles, BOIE_WasOn))
+      Key.first = getValueFromBundleOpInfo(Assume, Bundles, BOIE_WasOn);
+
+    if (Key.first == nullptr && Key.second == Attribute::None)
+      continue;
+    if (!BundleHasArguement(Bundles, BOIE_Argument)) {
+      Result[Key] = {0, 0};
+      continue;
+    }
+    unsigned Val = cast<ConstantInt>(
+                       getValueFromBundleOpInfo(Assume, Bundles, BOIE_Argument))
+                       ->getZExtValue();
+    auto Lookup = Result.find(Key);
+    if (Lookup == Result.end()) {
+      Result[Key] = {Val, Val};
+      continue;
+    }
+    Lookup->second.Min = std::min(Val, Lookup->second.Min);
+    Lookup->second.Max = std::max(Val, Lookup->second.Max);
+  }
+}
+
 PreservedAnalyses AssumeBuilderPass::run(Function &F,
                                          FunctionAnalysisManager &AM) {
   for (Instruction &I : instructions(F))

diff  --git a/llvm/unittests/Transforms/Utils/KnowledgeRetentionTest.cpp b/llvm/unittests/Transforms/Utils/KnowledgeRetentionTest.cpp
index 5c84a25745e5..08f2c6441645 100644
--- a/llvm/unittests/Transforms/Utils/KnowledgeRetentionTest.cpp
+++ b/llvm/unittests/Transforms/Utils/KnowledgeRetentionTest.cpp
@@ -41,7 +41,7 @@ static void RunTest(
   }
 }
 
-void AssertMatchesExactlyAttributes(CallInst *Assume, Value *WasOn,
+static void AssertMatchesExactlyAttributes(CallInst *Assume, Value *WasOn,
                                     StringRef AttrToMatch) {
   Regex Reg(AttrToMatch);
   SmallVector<StringRef, 1> Matches;
@@ -57,7 +57,7 @@ void AssertMatchesExactlyAttributes(CallInst *Assume, Value *WasOn,
   }
 }
 
-void AssertHasTheRightValue(CallInst *Assume, Value *WasOn,
+static void AssertHasTheRightValue(CallInst *Assume, Value *WasOn,
                             Attribute::AttrKind Kind, unsigned Value, bool Both,
                             AssumeQuery AQ = AssumeQuery::Highest) {
   if (!Both) {
@@ -80,7 +80,7 @@ void AssertHasTheRightValue(CallInst *Assume, Value *WasOn,
   }
 }
 
-TEST(AssumeQueryAPI, Basic) {
+TEST(AssumeQueryAPI, hasAttributeInAssume) {
   StringRef Head =
       "declare void @llvm.assume(i1)\n"
       "declare void @func(i32*, i32*)\n"
@@ -216,3 +216,174 @@ TEST(AssumeQueryAPI, Basic) {
       }));
   RunTest(Head, Tail, Tests);
 }
+
+static void AssertFindExactlyAttributes(RetainedKnowledgeMap &Map, Value *WasOn,
+                                 StringRef AttrToMatch) {
+  Regex Reg(AttrToMatch);
+  SmallVector<StringRef, 1> Matches;
+  for (StringRef Attr : {
+#define GET_ATTR_NAMES
+#define ATTRIBUTE_ENUM(ENUM_NAME, DISPLAY_NAME) StringRef(#DISPLAY_NAME),
+#include "llvm/IR/Attributes.inc"
+       }) {
+    bool ShouldHaveAttr = Reg.match(Attr, &Matches) && Matches[0] == Attr;
+
+    if (ShouldHaveAttr != (Map.find(RetainedKnowledgeKey{WasOn, Attribute::getAttrKindFromName(Attr)}) != Map.end())) {
+      ASSERT_TRUE(false);
+    }
+  }
+}
+
+static void AssertMapHasRightValue(RetainedKnowledgeMap &Map,
+                                   RetainedKnowledgeKey Key, MinMax MM) {
+  auto LookupIt = Map.find(Key);
+  ASSERT_TRUE(LookupIt != Map.end());
+  ASSERT_TRUE(LookupIt->second.Min == MM.Min);
+  ASSERT_TRUE(LookupIt->second.Max == MM.Max);
+}
+
+TEST(AssumeQueryAPI, fillMapFromAssume) {
+    StringRef Head =
+      "declare void @llvm.assume(i1)\n"
+      "declare void @func(i32*, i32*)\n"
+      "declare void @func1(i32*, i32*, i32*, i32*)\n"
+      "declare void @func_many(i32*) \"no-jump-tables\" nounwind "
+      "\"less-precise-fpmad\" willreturn norecurse\n"
+      "define void @test(i32* %P, i32* %P1, i32* %P2, i32* %P3) {\n";
+  StringRef Tail = "ret void\n"
+                   "}";
+  std::vector<std::pair<StringRef, llvm::function_ref<void(Instruction *)>>>
+      Tests;
+  Tests.push_back(std::make_pair(
+      "call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align "
+      "8 noalias %P1)\n",
+      [](Instruction *I) {
+        CallInst *Assume = BuildAssumeFromInst(I);
+        Assume->insertBefore(I);
+
+        RetainedKnowledgeMap Map;
+        fillMapFromAssume(*Assume, Map);
+        AssertFindExactlyAttributes(Map, I->getOperand(0),
+                                       "(nonnull|align|dereferenceable)");
+        AssertFindExactlyAttributes(Map, I->getOperand(1),
+                                       "(noalias|align)");
+        AssertMapHasRightValue(
+            Map, {I->getOperand(0), Attribute::Dereferenceable}, {16, 16});
+        AssertMapHasRightValue(Map, {I->getOperand(0), Attribute::Alignment},
+                               {4, 4});
+        AssertMapHasRightValue(Map, {I->getOperand(0), Attribute::Alignment},
+                               {4, 4});
+      }));
+  Tests.push_back(std::make_pair(
+      "call void @func1(i32* nonnull align 32 dereferenceable(48) %P, i32* "
+      "nonnull "
+      "align 8 dereferenceable(28) %P, i32* nonnull align 64 "
+      "dereferenceable(4) "
+      "%P, i32* nonnull align 16 dereferenceable(12) %P)\n",
+      [](Instruction *I) {
+        CallInst *Assume = BuildAssumeFromInst(I);
+        Assume->insertBefore(I);
+
+        RetainedKnowledgeMap Map;
+        fillMapFromAssume(*Assume, Map);
+
+        AssertFindExactlyAttributes(Map, I->getOperand(0),
+                                       "(nonnull|align|dereferenceable)");
+        AssertFindExactlyAttributes(Map, I->getOperand(1),
+                                       "(nonnull|align|dereferenceable)");
+        AssertFindExactlyAttributes(Map, I->getOperand(2),
+                                       "(nonnull|align|dereferenceable)");
+        AssertFindExactlyAttributes(Map, I->getOperand(3),
+                                       "(nonnull|align|dereferenceable)");
+        AssertMapHasRightValue(
+            Map, {I->getOperand(0), Attribute::Dereferenceable}, {4, 48});
+        AssertMapHasRightValue(Map, {I->getOperand(0), Attribute::Alignment},
+                               {8, 64});
+      }));
+  Tests.push_back(std::make_pair(
+      "call void @func_many(i32* align 8 %P1) cold\n", [](Instruction *I) {
+        ShouldPreserveAllAttributes.setValue(true);
+        CallInst *Assume = BuildAssumeFromInst(I);
+        Assume->insertBefore(I);
+
+        RetainedKnowledgeMap Map;
+        fillMapFromAssume(*Assume, Map);
+
+        AssertFindExactlyAttributes(
+            Map, nullptr, "(nounwind|norecurse|willreturn|cold)");
+        ShouldPreserveAllAttributes.setValue(false);
+      }));
+  Tests.push_back(
+      std::make_pair("call void @llvm.assume(i1 true)\n", [](Instruction *I) {
+        RetainedKnowledgeMap Map;
+        fillMapFromAssume(*cast<CallInst>(I), Map);
+
+        AssertFindExactlyAttributes(Map, nullptr, "");
+        ASSERT_TRUE(Map.empty());
+      }));
+  Tests.push_back(std::make_pair(
+      "call void @func1(i32* readnone align 32 "
+      "dereferenceable(48) noalias %P, i32* "
+      "align 8 dereferenceable(28) %P1, i32* align 64 "
+      "dereferenceable(4) "
+      "%P2, i32* nonnull align 16 dereferenceable(12) %P3)\n",
+      [](Instruction *I) {
+        CallInst *Assume = BuildAssumeFromInst(I);
+        Assume->insertBefore(I);
+
+        RetainedKnowledgeMap Map;
+        fillMapFromAssume(*Assume, Map);
+
+        AssertFindExactlyAttributes(Map, I->getOperand(0),
+                                    "(readnone|align|dereferenceable|noalias)");
+        AssertFindExactlyAttributes(Map, I->getOperand(1),
+                                    "(align|dereferenceable)");
+        AssertFindExactlyAttributes(Map, I->getOperand(2),
+                                       "(align|dereferenceable)");
+        AssertFindExactlyAttributes(Map, I->getOperand(3),
+                                       "(nonnull|align|dereferenceable)");
+        AssertMapHasRightValue(Map, {I->getOperand(0), Attribute::Alignment},
+                               {32, 32});
+        AssertMapHasRightValue(
+            Map, {I->getOperand(0), Attribute::Dereferenceable}, {48, 48});
+        AssertMapHasRightValue(
+            Map, {I->getOperand(0), Attribute::NoAlias}, {0, 0});
+        AssertMapHasRightValue(
+            Map, {I->getOperand(1), Attribute::Dereferenceable}, {28, 28});
+        AssertMapHasRightValue(Map, {I->getOperand(1), Attribute::Alignment},
+                               {8, 8});
+        AssertMapHasRightValue(Map, {I->getOperand(2), Attribute::Alignment},
+                               {64, 64});
+        AssertMapHasRightValue(
+            Map, {I->getOperand(2), Attribute::Dereferenceable}, {4, 4});
+        AssertMapHasRightValue(Map, {I->getOperand(3), Attribute::Alignment},
+                               {16, 16});
+        AssertMapHasRightValue(
+            Map, {I->getOperand(3), Attribute::Dereferenceable}, {12, 12});
+      }));
+
+  /// Keep this test last as it modifies the function.
+  Tests.push_back(std::make_pair(
+      "call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align "
+      "8 noalias %P1)\n",
+      [](Instruction *I) {
+        CallInst *Assume = BuildAssumeFromInst(I);
+        Assume->insertBefore(I);
+
+        RetainedKnowledgeMap Map;
+        fillMapFromAssume(*Assume, Map);
+
+        Value *New = I->getFunction()->getArg(3);
+        Value *Old = I->getOperand(0);
+        AssertFindExactlyAttributes(Map, New, "");
+        AssertFindExactlyAttributes(Map, Old,
+                                       "(nonnull|align|dereferenceable)");
+        Old->replaceAllUsesWith(New);
+        Map.clear();
+        fillMapFromAssume(*Assume, Map);
+        AssertFindExactlyAttributes(Map, New,
+                                       "(nonnull|align|dereferenceable)");
+        AssertFindExactlyAttributes(Map, Old, "");
+      }));
+  RunTest(Head, Tail, Tests);
+}


        


More information about the llvm-commits mailing list