Skip to content

Commit 876d133

Browse files
committed
[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
1 parent 05afa55 commit 876d133

File tree

3 files changed

+255
-16
lines changed

3 files changed

+255
-16
lines changed

llvm/include/llvm/Transforms/Utils/KnowledgeRetention.h

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "llvm/IR/Attributes.h"
2020
#include "llvm/IR/Instruction.h"
2121
#include "llvm/IR/PassManager.h"
22+
#include "llvm/ADT/DenseMap.h"
2223

2324
namespace llvm {
2425

@@ -58,9 +59,41 @@ inline bool hasAttributeInAssume(CallInst &AssumeCI, Value *IsOn,
5859
AssumeCI, IsOn, Attribute::getNameFromAttrKind(Kind), ArgVal, AQR);
5960
}
6061

61-
/// TODO: Add an function to create/fill a map from the bundle when users intend
62-
/// to make many different queries on the same bundles. to be used for example
63-
/// in the Attributor.
62+
template<> struct DenseMapInfo<Attribute::AttrKind> {
63+
static constexpr auto MaxValue = std::numeric_limits<
64+
std::underlying_type<Attribute::AttrKind>::type>::max();
65+
static Attribute::AttrKind getEmptyKey() {
66+
return static_cast<Attribute::AttrKind>(MaxValue);
67+
}
68+
static Attribute::AttrKind getTombstoneKey() {
69+
return static_cast<Attribute::AttrKind>(MaxValue - 1);
70+
}
71+
static unsigned getHashValue(Attribute::AttrKind AK) {
72+
return hash_combine(AK);
73+
}
74+
static bool isEqual(Attribute::AttrKind LHS, Attribute::AttrKind RHS) {
75+
return LHS == RHS;
76+
}
77+
};
78+
79+
/// The map Key contains the Value on for which the attribute is valid and
80+
/// the Attribute that is valid for that value.
81+
/// If the Attribute is not on any value, the Value is nullptr.
82+
using RetainedKnowledgeKey = std::pair<Value *, Attribute::AttrKind>;
83+
84+
struct MinMax {
85+
unsigned Min;
86+
unsigned Max;
87+
};
88+
89+
using RetainedKnowledgeMap = DenseMap<RetainedKnowledgeKey, MinMax>;
90+
91+
/// Insert into the map all the informations contained in the operand bundles of
92+
/// the llvm.assume. This should be used instead of hasAttributeInAssume when
93+
/// many queries are going to be made on the same llvm.assume.
94+
/// String attributes are not inserted in the map.
95+
/// If the IR changes the map will be outdated.
96+
void fillMapFromAssume(CallInst &AssumeCI, RetainedKnowledgeMap &Result);
6497

6598
//===----------------------------------------------------------------------===//
6699
// Utilities for testing

llvm/lib/Transforms/Utils/KnowledgeRetention.cpp

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,18 @@ CallInst *llvm::BuildAssumeFromInst(const Instruction *I, Module *M) {
171171
return Builder.build();
172172
}
173173

174+
static bool BundleHasArguement(const CallBase::BundleOpInfo &BOI,
175+
unsigned Idx) {
176+
return BOI.End - BOI.Begin > Idx;
177+
}
178+
179+
static Value *getValueFromBundleOpInfo(IntrinsicInst &Assume,
180+
const CallBase::BundleOpInfo &BOI,
181+
unsigned Idx) {
182+
assert(BundleHasArguement(BOI, Idx) && "index out of range");
183+
return (Assume.op_begin() + BOI.Begin + Idx)->get();
184+
}
185+
174186
#ifndef NDEBUG
175187

176188
static bool isExistingAttribute(StringRef Name) {
@@ -219,12 +231,6 @@ bool llvm::hasAttributeInAssume(CallInst &AssumeCI, Value *IsOn,
219231
return LHS < BOI.Tag->getKey();
220232
}));
221233

222-
auto getValueFromBundleOpInfo = [&Assume](const CallBase::BundleOpInfo &BOI,
223-
unsigned Idx) {
224-
assert(BOI.End - BOI.Begin > Idx && "index out of range");
225-
return (Assume.op_begin() + BOI.Begin + Idx)->get();
226-
};
227-
228234
if (Lookup == Assume.bundle_op_info_end() ||
229235
Lookup->Tag->getKey() != AttrName)
230236
return false;
@@ -235,7 +241,7 @@ bool llvm::hasAttributeInAssume(CallInst &AssumeCI, Value *IsOn,
235241
if (Lookup == Assume.bundle_op_info_end() ||
236242
Lookup->Tag->getKey() != AttrName)
237243
return false;
238-
if (getValueFromBundleOpInfo(*Lookup, BOIE_WasOn) == IsOn)
244+
if (getValueFromBundleOpInfo(Assume, *Lookup, BOIE_WasOn) == IsOn)
239245
break;
240246
if (AQR == AssumeQuery::Highest &&
241247
Lookup == Assume.bundle_op_info_begin())
@@ -247,12 +253,41 @@ bool llvm::hasAttributeInAssume(CallInst &AssumeCI, Value *IsOn,
247253
if (Lookup->End - Lookup->Begin < BOIE_Argument)
248254
return true;
249255
if (ArgVal)
250-
*ArgVal =
251-
cast<ConstantInt>(getValueFromBundleOpInfo(*Lookup, BOIE_Argument))
252-
->getZExtValue();
256+
*ArgVal = cast<ConstantInt>(
257+
getValueFromBundleOpInfo(Assume, *Lookup, BOIE_Argument))
258+
->getZExtValue();
253259
return true;
254260
}
255261

262+
void llvm::fillMapFromAssume(CallInst &AssumeCI, RetainedKnowledgeMap &Result) {
263+
IntrinsicInst &Assume = cast<IntrinsicInst>(AssumeCI);
264+
assert(Assume.getIntrinsicID() == Intrinsic::assume &&
265+
"this function is intended to be used on llvm.assume");
266+
for (auto &Bundles : Assume.bundle_op_infos()) {
267+
std::pair<Value *, Attribute::AttrKind> Key{
268+
nullptr, Attribute::getAttrKindFromName(Bundles.Tag->getKey())};
269+
if (BundleHasArguement(Bundles, BOIE_WasOn))
270+
Key.first = getValueFromBundleOpInfo(Assume, Bundles, BOIE_WasOn);
271+
272+
if (Key.first == nullptr && Key.second == Attribute::None)
273+
continue;
274+
if (!BundleHasArguement(Bundles, BOIE_Argument)) {
275+
Result[Key] = {0, 0};
276+
continue;
277+
}
278+
unsigned Val = cast<ConstantInt>(
279+
getValueFromBundleOpInfo(Assume, Bundles, BOIE_Argument))
280+
->getZExtValue();
281+
auto Lookup = Result.find(Key);
282+
if (Lookup == Result.end()) {
283+
Result[Key] = {Val, Val};
284+
continue;
285+
}
286+
Lookup->second.Min = std::min(Val, Lookup->second.Min);
287+
Lookup->second.Max = std::max(Val, Lookup->second.Max);
288+
}
289+
}
290+
256291
PreservedAnalyses AssumeBuilderPass::run(Function &F,
257292
FunctionAnalysisManager &AM) {
258293
for (Instruction &I : instructions(F))

llvm/unittests/Transforms/Utils/KnowledgeRetentionTest.cpp

Lines changed: 174 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ static void RunTest(
4141
}
4242
}
4343

44-
void AssertMatchesExactlyAttributes(CallInst *Assume, Value *WasOn,
44+
static void AssertMatchesExactlyAttributes(CallInst *Assume, Value *WasOn,
4545
StringRef AttrToMatch) {
4646
Regex Reg(AttrToMatch);
4747
SmallVector<StringRef, 1> Matches;
@@ -57,7 +57,7 @@ void AssertMatchesExactlyAttributes(CallInst *Assume, Value *WasOn,
5757
}
5858
}
5959

60-
void AssertHasTheRightValue(CallInst *Assume, Value *WasOn,
60+
static void AssertHasTheRightValue(CallInst *Assume, Value *WasOn,
6161
Attribute::AttrKind Kind, unsigned Value, bool Both,
6262
AssumeQuery AQ = AssumeQuery::Highest) {
6363
if (!Both) {
@@ -80,7 +80,7 @@ void AssertHasTheRightValue(CallInst *Assume, Value *WasOn,
8080
}
8181
}
8282

83-
TEST(AssumeQueryAPI, Basic) {
83+
TEST(AssumeQueryAPI, hasAttributeInAssume) {
8484
StringRef Head =
8585
"declare void @llvm.assume(i1)\n"
8686
"declare void @func(i32*, i32*)\n"
@@ -216,3 +216,174 @@ TEST(AssumeQueryAPI, Basic) {
216216
}));
217217
RunTest(Head, Tail, Tests);
218218
}
219+
220+
static void AssertFindExactlyAttributes(RetainedKnowledgeMap &Map, Value *WasOn,
221+
StringRef AttrToMatch) {
222+
Regex Reg(AttrToMatch);
223+
SmallVector<StringRef, 1> Matches;
224+
for (StringRef Attr : {
225+
#define GET_ATTR_NAMES
226+
#define ATTRIBUTE_ENUM(ENUM_NAME, DISPLAY_NAME) StringRef(#DISPLAY_NAME),
227+
#include "llvm/IR/Attributes.inc"
228+
}) {
229+
bool ShouldHaveAttr = Reg.match(Attr, &Matches) && Matches[0] == Attr;
230+
231+
if (ShouldHaveAttr != (Map.find(RetainedKnowledgeKey{WasOn, Attribute::getAttrKindFromName(Attr)}) != Map.end())) {
232+
ASSERT_TRUE(false);
233+
}
234+
}
235+
}
236+
237+
static void AssertMapHasRightValue(RetainedKnowledgeMap &Map,
238+
RetainedKnowledgeKey Key, MinMax MM) {
239+
auto LookupIt = Map.find(Key);
240+
ASSERT_TRUE(LookupIt != Map.end());
241+
ASSERT_TRUE(LookupIt->second.Min == MM.Min);
242+
ASSERT_TRUE(LookupIt->second.Max == MM.Max);
243+
}
244+
245+
TEST(AssumeQueryAPI, fillMapFromAssume) {
246+
StringRef Head =
247+
"declare void @llvm.assume(i1)\n"
248+
"declare void @func(i32*, i32*)\n"
249+
"declare void @func1(i32*, i32*, i32*, i32*)\n"
250+
"declare void @func_many(i32*) \"no-jump-tables\" nounwind "
251+
"\"less-precise-fpmad\" willreturn norecurse\n"
252+
"define void @test(i32* %P, i32* %P1, i32* %P2, i32* %P3) {\n";
253+
StringRef Tail = "ret void\n"
254+
"}";
255+
std::vector<std::pair<StringRef, llvm::function_ref<void(Instruction *)>>>
256+
Tests;
257+
Tests.push_back(std::make_pair(
258+
"call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align "
259+
"8 noalias %P1)\n",
260+
[](Instruction *I) {
261+
CallInst *Assume = BuildAssumeFromInst(I);
262+
Assume->insertBefore(I);
263+
264+
RetainedKnowledgeMap Map;
265+
fillMapFromAssume(*Assume, Map);
266+
AssertFindExactlyAttributes(Map, I->getOperand(0),
267+
"(nonnull|align|dereferenceable)");
268+
AssertFindExactlyAttributes(Map, I->getOperand(1),
269+
"(noalias|align)");
270+
AssertMapHasRightValue(
271+
Map, {I->getOperand(0), Attribute::Dereferenceable}, {16, 16});
272+
AssertMapHasRightValue(Map, {I->getOperand(0), Attribute::Alignment},
273+
{4, 4});
274+
AssertMapHasRightValue(Map, {I->getOperand(0), Attribute::Alignment},
275+
{4, 4});
276+
}));
277+
Tests.push_back(std::make_pair(
278+
"call void @func1(i32* nonnull align 32 dereferenceable(48) %P, i32* "
279+
"nonnull "
280+
"align 8 dereferenceable(28) %P, i32* nonnull align 64 "
281+
"dereferenceable(4) "
282+
"%P, i32* nonnull align 16 dereferenceable(12) %P)\n",
283+
[](Instruction *I) {
284+
CallInst *Assume = BuildAssumeFromInst(I);
285+
Assume->insertBefore(I);
286+
287+
RetainedKnowledgeMap Map;
288+
fillMapFromAssume(*Assume, Map);
289+
290+
AssertFindExactlyAttributes(Map, I->getOperand(0),
291+
"(nonnull|align|dereferenceable)");
292+
AssertFindExactlyAttributes(Map, I->getOperand(1),
293+
"(nonnull|align|dereferenceable)");
294+
AssertFindExactlyAttributes(Map, I->getOperand(2),
295+
"(nonnull|align|dereferenceable)");
296+
AssertFindExactlyAttributes(Map, I->getOperand(3),
297+
"(nonnull|align|dereferenceable)");
298+
AssertMapHasRightValue(
299+
Map, {I->getOperand(0), Attribute::Dereferenceable}, {4, 48});
300+
AssertMapHasRightValue(Map, {I->getOperand(0), Attribute::Alignment},
301+
{8, 64});
302+
}));
303+
Tests.push_back(std::make_pair(
304+
"call void @func_many(i32* align 8 %P1) cold\n", [](Instruction *I) {
305+
ShouldPreserveAllAttributes.setValue(true);
306+
CallInst *Assume = BuildAssumeFromInst(I);
307+
Assume->insertBefore(I);
308+
309+
RetainedKnowledgeMap Map;
310+
fillMapFromAssume(*Assume, Map);
311+
312+
AssertFindExactlyAttributes(
313+
Map, nullptr, "(nounwind|norecurse|willreturn|cold)");
314+
ShouldPreserveAllAttributes.setValue(false);
315+
}));
316+
Tests.push_back(
317+
std::make_pair("call void @llvm.assume(i1 true)\n", [](Instruction *I) {
318+
RetainedKnowledgeMap Map;
319+
fillMapFromAssume(*cast<CallInst>(I), Map);
320+
321+
AssertFindExactlyAttributes(Map, nullptr, "");
322+
ASSERT_TRUE(Map.empty());
323+
}));
324+
Tests.push_back(std::make_pair(
325+
"call void @func1(i32* readnone align 32 "
326+
"dereferenceable(48) noalias %P, i32* "
327+
"align 8 dereferenceable(28) %P1, i32* align 64 "
328+
"dereferenceable(4) "
329+
"%P2, i32* nonnull align 16 dereferenceable(12) %P3)\n",
330+
[](Instruction *I) {
331+
CallInst *Assume = BuildAssumeFromInst(I);
332+
Assume->insertBefore(I);
333+
334+
RetainedKnowledgeMap Map;
335+
fillMapFromAssume(*Assume, Map);
336+
337+
AssertFindExactlyAttributes(Map, I->getOperand(0),
338+
"(readnone|align|dereferenceable|noalias)");
339+
AssertFindExactlyAttributes(Map, I->getOperand(1),
340+
"(align|dereferenceable)");
341+
AssertFindExactlyAttributes(Map, I->getOperand(2),
342+
"(align|dereferenceable)");
343+
AssertFindExactlyAttributes(Map, I->getOperand(3),
344+
"(nonnull|align|dereferenceable)");
345+
AssertMapHasRightValue(Map, {I->getOperand(0), Attribute::Alignment},
346+
{32, 32});
347+
AssertMapHasRightValue(
348+
Map, {I->getOperand(0), Attribute::Dereferenceable}, {48, 48});
349+
AssertMapHasRightValue(
350+
Map, {I->getOperand(0), Attribute::NoAlias}, {0, 0});
351+
AssertMapHasRightValue(
352+
Map, {I->getOperand(1), Attribute::Dereferenceable}, {28, 28});
353+
AssertMapHasRightValue(Map, {I->getOperand(1), Attribute::Alignment},
354+
{8, 8});
355+
AssertMapHasRightValue(Map, {I->getOperand(2), Attribute::Alignment},
356+
{64, 64});
357+
AssertMapHasRightValue(
358+
Map, {I->getOperand(2), Attribute::Dereferenceable}, {4, 4});
359+
AssertMapHasRightValue(Map, {I->getOperand(3), Attribute::Alignment},
360+
{16, 16});
361+
AssertMapHasRightValue(
362+
Map, {I->getOperand(3), Attribute::Dereferenceable}, {12, 12});
363+
}));
364+
365+
/// Keep this test last as it modifies the function.
366+
Tests.push_back(std::make_pair(
367+
"call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align "
368+
"8 noalias %P1)\n",
369+
[](Instruction *I) {
370+
CallInst *Assume = BuildAssumeFromInst(I);
371+
Assume->insertBefore(I);
372+
373+
RetainedKnowledgeMap Map;
374+
fillMapFromAssume(*Assume, Map);
375+
376+
Value *New = I->getFunction()->getArg(3);
377+
Value *Old = I->getOperand(0);
378+
AssertFindExactlyAttributes(Map, New, "");
379+
AssertFindExactlyAttributes(Map, Old,
380+
"(nonnull|align|dereferenceable)");
381+
Old->replaceAllUsesWith(New);
382+
Map.clear();
383+
fillMapFromAssume(*Assume, Map);
384+
AssertFindExactlyAttributes(Map, New,
385+
"(nonnull|align|dereferenceable)");
386+
AssertFindExactlyAttributes(Map, Old, "");
387+
}));
388+
RunTest(Head, Tail, Tests);
389+
}

0 commit comments

Comments
 (0)