Skip to content

Commit c9e93c8

Browse files
committed
Add Query API for llvm.assume holding attributes
Reviewers: jdoerfert, sstefan1, uenoku Reviewed By: jdoerfert Subscribers: mgorny, hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D72885
1 parent 8ee0e1d commit c9e93c8

File tree

4 files changed

+353
-8
lines changed

4 files changed

+353
-8
lines changed

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#ifndef LLVM_TRANSFORMS_UTILS_ASSUMEBUILDER_H
1717
#define LLVM_TRANSFORMS_UTILS_ASSUMEBUILDER_H
1818

19+
#include "llvm/IR/Attributes.h"
1920
#include "llvm/IR/Instruction.h"
2021
#include "llvm/IR/PassManager.h"
2122

@@ -30,6 +31,41 @@ inline CallInst *BuildAssumeFromInst(Instruction *I) {
3031
return BuildAssumeFromInst(I, I->getModule());
3132
}
3233

34+
/// It is possible to have multiple Value for the argument of an attribute in
35+
/// the same llvm.assume on the same llvm::Value. This is rare but need to be
36+
/// dealt with.
37+
enum class AssumeQuery {
38+
Highest, ///< Take the highest value available.
39+
Lowest, ///< Take the lowest value available.
40+
};
41+
42+
/// Query the operand bundle of an llvm.assume to find a single attribute of
43+
/// the specified kind applied on a specified Value.
44+
///
45+
/// This has a non-constant complexity. It should only be used when a single
46+
/// attribute is going to be queried.
47+
///
48+
/// Return true iff the queried attribute was found.
49+
/// If ArgVal is set. the argument will be stored to ArgVal.
50+
bool hasAttributeInAssume(CallInst &AssumeCI, Value *IsOn, StringRef AttrName,
51+
uint64_t *ArgVal = nullptr,
52+
AssumeQuery AQR = AssumeQuery::Highest);
53+
inline bool hasAttributeInAssume(CallInst &AssumeCI, Value *IsOn,
54+
Attribute::AttrKind Kind,
55+
uint64_t *ArgVal = nullptr,
56+
AssumeQuery AQR = AssumeQuery::Highest) {
57+
return hasAttributeInAssume(
58+
AssumeCI, IsOn, Attribute::getNameFromAttrKind(Kind), ArgVal, AQR);
59+
}
60+
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.
64+
65+
//===----------------------------------------------------------------------===//
66+
// Utilities for testing
67+
//===----------------------------------------------------------------------===//
68+
3369
/// This pass will try to build an llvm.assume for every instruction in the
3470
/// function. Its main purpose is testing.
3571
struct AssumeBuilderPass : public PassInfoMixin<AssumeBuilderPass> {

llvm/lib/Transforms/Utils/KnowledgeRetention.cpp

Lines changed: 101 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515

1616
using namespace llvm;
1717

18-
namespace {
19-
2018
cl::opt<bool> ShouldPreserveAllAttributes(
2119
"assume-preserve-all", cl::init(false), cl::Hidden,
2220
cl::desc("enable preservation of all attrbitues. even those that are "
2321
"unlikely to be usefull"));
2422

23+
namespace {
24+
2525
struct AssumedKnowledge {
2626
const char *Name;
2727
Value *Argument;
@@ -59,22 +59,33 @@ template <> struct DenseMapInfo<AssumedKnowledge> {
5959

6060
namespace {
6161

62+
/// Index of elements in the operand bundle.
63+
/// If the element exist it is guaranteed to be what is specified in this enum
64+
/// but it may not exist.
65+
enum BundleOpInfoElem {
66+
BOIE_WasOn = 0,
67+
BOIE_Argument = 1,
68+
};
69+
6270
/// Deterministically compare OperandBundleDef.
6371
/// The ordering is:
64-
/// - by the name of the attribute, (doesn't change)
65-
/// - then by the Value of the argument, (doesn't change)
72+
/// - by the attribute's name aka operand bundle tag, (doesn't change)
73+
/// - then by the numeric Value of the argument, (doesn't change)
6674
/// - lastly by the Name of the current Value it WasOn. (may change)
6775
/// This order is deterministic and allows looking for the right kind of
6876
/// attribute with binary search. However finding the right WasOn needs to be
69-
/// done via linear search because values can get remplaced.
77+
/// done via linear search because values can get replaced.
7078
bool isLowerOpBundle(const OperandBundleDef &LHS, const OperandBundleDef &RHS) {
7179
auto getTuple = [](const OperandBundleDef &Op) {
7280
return std::make_tuple(
7381
Op.getTag(),
74-
Op.input_size() < 2
82+
Op.input_size() <= BOIE_Argument
7583
? 0
76-
: cast<ConstantInt>(*std::next(Op.input_begin()))->getZExtValue(),
77-
Op.input_size() < 1 ? StringRef("") : (*Op.input_begin())->getName());
84+
: cast<ConstantInt>(*(Op.input_begin() + BOIE_Argument))
85+
->getZExtValue(),
86+
Op.input_size() <= BOIE_WasOn
87+
? StringRef("")
88+
: (*(Op.input_begin() + BOIE_WasOn))->getName());
7889
};
7990
return getTuple(LHS) < getTuple(RHS);
8091
}
@@ -160,6 +171,88 @@ CallInst *llvm::BuildAssumeFromInst(const Instruction *I, Module *M) {
160171
return Builder.build();
161172
}
162173

174+
#ifndef NDEBUG
175+
176+
static bool isExistingAttribute(StringRef Name) {
177+
return StringSwitch<bool>(Name)
178+
#define GET_ATTR_NAMES
179+
#define ATTRIBUTE_ALL(ENUM_NAME, DISPLAY_NAME) .Case(#DISPLAY_NAME, true)
180+
#include "llvm/IR/Attributes.inc"
181+
.Default(false);
182+
}
183+
184+
#endif
185+
186+
bool llvm::hasAttributeInAssume(CallInst &AssumeCI, Value *IsOn,
187+
StringRef AttrName, uint64_t *ArgVal,
188+
AssumeQuery AQR) {
189+
IntrinsicInst &Assume = cast<IntrinsicInst>(AssumeCI);
190+
assert(Assume.getIntrinsicID() == Intrinsic::assume &&
191+
"this function is intended to be used on llvm.assume");
192+
assert(isExistingAttribute(AttrName) && "this attribute doesn't exist");
193+
assert((ArgVal == nullptr || Attribute::doesAttrKindHaveArgument(
194+
Attribute::getAttrKindFromName(AttrName))) &&
195+
"requested value for an attribute that has no argument");
196+
if (Assume.bundle_op_infos().empty())
197+
return false;
198+
199+
CallInst::bundle_op_iterator Lookup;
200+
201+
/// The right attribute can be found by binary search. After this finding the
202+
/// right WasOn needs to be done via linear search.
203+
/// Element have been ordered by argument value so the first we find is the
204+
/// one we need.
205+
if (AQR == AssumeQuery::Lowest)
206+
Lookup =
207+
llvm::lower_bound(Assume.bundle_op_infos(), AttrName,
208+
[](const CallBase::BundleOpInfo &BOI, StringRef RHS) {
209+
assert(isExistingAttribute(BOI.Tag->getKey()) &&
210+
"this attribute doesn't exist");
211+
return BOI.Tag->getKey() < RHS;
212+
});
213+
else
214+
Lookup = std::prev(
215+
llvm::upper_bound(Assume.bundle_op_infos(), AttrName,
216+
[](StringRef LHS, const CallBase::BundleOpInfo &BOI) {
217+
assert(isExistingAttribute(BOI.Tag->getKey()) &&
218+
"this attribute doesn't exist");
219+
return LHS < BOI.Tag->getKey();
220+
}));
221+
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+
228+
if (Lookup == Assume.bundle_op_info_end() ||
229+
Lookup->Tag->getKey() != AttrName)
230+
return false;
231+
if (IsOn) {
232+
if (Lookup->End - Lookup->Begin < BOIE_WasOn)
233+
return false;
234+
while (true) {
235+
if (Lookup == Assume.bundle_op_info_end() ||
236+
Lookup->Tag->getKey() != AttrName)
237+
return false;
238+
if (getValueFromBundleOpInfo(*Lookup, BOIE_WasOn) == IsOn)
239+
break;
240+
if (AQR == AssumeQuery::Highest &&
241+
Lookup == Assume.bundle_op_info_begin())
242+
return false;
243+
Lookup = Lookup + (AQR == AssumeQuery::Lowest ? 1 : -1);
244+
}
245+
}
246+
247+
if (Lookup->End - Lookup->Begin < BOIE_Argument)
248+
return true;
249+
if (ArgVal)
250+
*ArgVal =
251+
cast<ConstantInt>(getValueFromBundleOpInfo(*Lookup, BOIE_Argument))
252+
->getZExtValue();
253+
return true;
254+
}
255+
163256
PreservedAnalyses AssumeBuilderPass::run(Function &F,
164257
FunctionAnalysisManager &AM) {
165258
for (Instruction &I : instructions(F))

llvm/unittests/Transforms/Utils/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ add_llvm_unittest(UtilsTests
1515
CodeMoverUtilsTest.cpp
1616
FunctionComparatorTest.cpp
1717
IntegerDivisionTest.cpp
18+
KnowledgeRetentionTest.cpp
1819
LocalTest.cpp
1920
LoopRotationUtilsTest.cpp
2021
LoopUtilsTest.cpp

0 commit comments

Comments
 (0)