[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