Skip to content

Commit f16f139

Browse files
committed
Basis of dropping uses in llvm.assume.
Summary: This patch adds the basic utilities to deal with dropable uses. dropable uses are uses that we rather drop than prevent transformations, for now they are limited to uses in llvm.assume. Reviewers: jdoerfert, sstefan1 Reviewed By: jdoerfert Subscribers: uenoku, lebedev.ri, mgorny, hiraditya, dexonsmith, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D73404
1 parent 61211fe commit f16f139

File tree

8 files changed

+142
-61
lines changed

8 files changed

+142
-61
lines changed

llvm/docs/LangRef.rst

+3-2
Original file line numberDiff line numberDiff line change
@@ -2119,8 +2119,9 @@ An assume operand bundle has the form:
21192119

21202120
"<tag>"([ <holds for value> [, <attribute argument>] ])
21212121

2122-
* The tag of the operand bundle is the name of attribute that can be assumed
2123-
to hold.
2122+
* The tag of the operand bundle is usually the name of attribute that can be
2123+
assumed to hold. It can also be `ignore`, this tag doesn't contain any
2124+
information and should be ignored.
21242125
* The first argument if present is the value for which the attribute hold.
21252126
* The second argument if present is an argument of the attribute.
21262127

llvm/include/llvm/IR/User.h

+5
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,11 @@ class User : public Value {
218218
NumUserOperands = NumOps;
219219
}
220220

221+
/// A droppable user is a user for which uses can be dropped without affecting
222+
/// correctness and should be dropped rather than preventing a transformation
223+
/// from happening.
224+
bool isDroppable() const;
225+
221226
// ---------------------------------------------------------------------------
222227
// Operand Iterator interface...
223228
//

llvm/include/llvm/IR/Value.h

+28
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,34 @@ class Value {
444444
/// This is logically equivalent to getNumUses() >= N.
445445
bool hasNUsesOrMore(unsigned N) const;
446446

447+
/// Return true if there is exactly one user of this value that cannot be
448+
/// dropped.
449+
///
450+
/// This is specialized because it is a common request and does not require
451+
/// traversing the whole use list.
452+
Use *getSingleUndroppableUse();
453+
454+
/// Return true if there this value.
455+
///
456+
/// This is specialized because it is a common request and does not require
457+
/// traversing the whole use list.
458+
bool hasNUndroppableUses(unsigned N) const;
459+
460+
/// Return true if this value has N users or more.
461+
///
462+
/// This is logically equivalent to getNumUses() >= N.
463+
bool hasNUndroppableUsesOrMore(unsigned N) const;
464+
465+
/// Remove every uses that can safely be removed.
466+
///
467+
/// This will remove for example uses in llvm.assume.
468+
/// This should be used when performing want to perform a tranformation but
469+
/// some Droppable uses pervent it.
470+
/// This function optionally takes a filter to only remove some droppable
471+
/// uses.
472+
void dropDroppableUses(llvm::function_ref<bool(const Use *)> ShouldDrop =
473+
[](const Use *) { return true; });
474+
447475
/// Check if this value is used in the specified basic block.
448476
bool isUsedInBasicBlock(const BasicBlock *BB) const;
449477

llvm/lib/IR/KnowledgeRetention.cpp

+19-43
Original file line numberDiff line numberDiff line change
@@ -197,51 +197,27 @@ bool llvm::hasAttributeInAssume(CallInst &AssumeCI, Value *IsOn,
197197
if (Assume.bundle_op_infos().empty())
198198
return false;
199199

200-
CallInst::bundle_op_iterator Lookup;
201-
202-
/// The right attribute can be found by binary search. After this finding the
203-
/// right WasOn needs to be done via linear search.
204-
/// Element have been ordered by argument value so the first we find is the
205-
/// one we need.
206-
if (AQR == AssumeQuery::Lowest)
207-
Lookup =
208-
llvm::lower_bound(Assume.bundle_op_infos(), AttrName,
209-
[](const CallBase::BundleOpInfo &BOI, StringRef RHS) {
210-
return BOI.Tag->getKey() < RHS;
211-
});
212-
else
213-
Lookup = std::prev(
214-
llvm::upper_bound(Assume.bundle_op_infos(), AttrName,
215-
[](StringRef LHS, const CallBase::BundleOpInfo &BOI) {
216-
return LHS < BOI.Tag->getKey();
217-
}));
218-
219-
if (Lookup == Assume.bundle_op_info_end() ||
220-
Lookup->Tag->getKey() != AttrName)
221-
return false;
222-
if (IsOn) {
223-
assert((Lookup->End - Lookup->Begin > BOIE_WasOn) &&
224-
"missing argument of attribute");
225-
while (true) {
226-
if (Lookup == Assume.bundle_op_info_end() ||
227-
Lookup->Tag->getKey() != AttrName)
228-
return false;
229-
if (getValueFromBundleOpInfo(Assume, *Lookup, BOIE_WasOn) == IsOn)
230-
break;
231-
if (AQR == AssumeQuery::Highest &&
232-
Lookup == Assume.bundle_op_info_begin())
233-
return false;
234-
Lookup = Lookup + (AQR == AssumeQuery::Lowest ? 1 : -1);
200+
auto Loop = [&](auto &&Range) {
201+
for (auto &BOI : Range) {
202+
if (BOI.Tag->getKey() != AttrName)
203+
continue;
204+
if (IsOn && (BOI.End - BOI.Begin <= BOIE_WasOn ||
205+
IsOn != getValueFromBundleOpInfo(Assume, BOI, BOIE_WasOn)))
206+
continue;
207+
if (ArgVal) {
208+
assert(BOI.End - BOI.Begin > BOIE_Argument);
209+
*ArgVal = cast<ConstantInt>(
210+
getValueFromBundleOpInfo(Assume, BOI, BOIE_Argument))
211+
->getZExtValue();
212+
}
213+
return true;
235214
}
236-
}
215+
return false;
216+
};
237217

238-
if (Lookup->End - Lookup->Begin < BOIE_Argument)
239-
return true;
240-
if (ArgVal)
241-
*ArgVal = cast<ConstantInt>(
242-
getValueFromBundleOpInfo(Assume, *Lookup, BOIE_Argument))
243-
->getZExtValue();
244-
return true;
218+
if (AQR == AssumeQuery::Lowest)
219+
return Loop(Assume.bundle_op_infos());
220+
return Loop(reverse(Assume.bundle_op_infos()));
245221
}
246222

247223
void llvm::fillMapFromAssume(CallInst &AssumeCI, RetainedKnowledgeMap &Result) {

llvm/lib/IR/User.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "llvm/IR/User.h"
1010
#include "llvm/IR/Constant.h"
1111
#include "llvm/IR/GlobalValue.h"
12+
#include "llvm/IR/IntrinsicInst.h"
1213

1314
namespace llvm {
1415
class BasicBlock;
@@ -105,6 +106,12 @@ MutableArrayRef<uint8_t> User::getDescriptor() {
105106
reinterpret_cast<uint8_t *>(DI) - DI->SizeInBytes, DI->SizeInBytes);
106107
}
107108

109+
bool User::isDroppable() const {
110+
if (const auto *Intr = dyn_cast<IntrinsicInst>(this))
111+
return Intr->getIntrinsicID() == Intrinsic::assume;
112+
return false;
113+
}
114+
108115
//===----------------------------------------------------------------------===//
109116
// User operator new Implementations
110117
//===----------------------------------------------------------------------===//

llvm/lib/IR/Value.cpp

+45
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,51 @@ bool Value::hasNUsesOrMore(unsigned N) const {
141141
return hasNItemsOrMore(use_begin(), use_end(), N);
142142
}
143143

144+
static bool isUnDroppableUser(const User *U) { return !U->isDroppable(); }
145+
146+
Use *Value::getSingleUndroppableUse() {
147+
Use *Result = nullptr;
148+
for (Use &U : uses()) {
149+
if (!U.getUser()->isDroppable()) {
150+
if (Result)
151+
return nullptr;
152+
Result = &U;
153+
}
154+
}
155+
return Result;
156+
}
157+
158+
bool Value::hasNUndroppableUses(unsigned int N) const {
159+
return hasNItems(user_begin(), user_end(), N, isUnDroppableUser);
160+
}
161+
162+
bool Value::hasNUndroppableUsesOrMore(unsigned int N) const {
163+
return hasNItemsOrMore(user_begin(), user_end(), N, isUnDroppableUser);
164+
}
165+
166+
void Value::dropDroppableUses(
167+
llvm::function_ref<bool(const Use *)> ShouldDrop) {
168+
SmallVector<Use *, 8> ToBeEdited;
169+
for (Use &U : uses())
170+
if (U.getUser()->isDroppable() && ShouldDrop(&U))
171+
ToBeEdited.push_back(&U);
172+
for (Use *U : ToBeEdited) {
173+
U->removeFromList();
174+
if (auto *Assume = dyn_cast<IntrinsicInst>(U->getUser())) {
175+
assert(Assume->getIntrinsicID() == Intrinsic::assume);
176+
unsigned OpNo = U->getOperandNo();
177+
if (OpNo == 0)
178+
Assume->setOperand(0, ConstantInt::getTrue(Assume->getContext()));
179+
else {
180+
Assume->setOperand(OpNo, UndefValue::get(U->get()->getType()));
181+
CallInst::BundleOpInfo &BOI = Assume->getBundleOpInfoForOperand(OpNo);
182+
BOI.Tag = getContext().pImpl->getOrInsertBundleTag("ignore");
183+
}
184+
} else
185+
llvm_unreachable("unkown droppable use");
186+
}
187+
}
188+
144189
bool Value::isUsedInBasicBlock(const BasicBlock *BB) const {
145190
// This can be computed either by scanning the instructions in BB, or by
146191
// scanning the use list of this Value. Both lists can be very long, but

llvm/lib/IR/Verifier.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -4322,7 +4322,8 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
43224322
break;
43234323
case Intrinsic::assume: {
43244324
for (auto &Elem : Call.bundle_op_infos()) {
4325-
Assert(Attribute::isExistingAttribute(Elem.Tag->getKey()),
4325+
Assert(Elem.Tag->getKey() == "ignore" ||
4326+
Attribute::isExistingAttribute(Elem.Tag->getKey()),
43264327
"tags must be valid attribute names");
43274328
Assert(Elem.End - Elem.Begin <= 2, "to many arguments");
43284329
Attribute::AttrKind Kind =

llvm/unittests/IR/KnowledgeRetentionTest.cpp

+33-15
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,17 @@ static void RunTest(
2525
StringRef Head, StringRef Tail,
2626
std::vector<std::pair<StringRef, llvm::function_ref<void(Instruction *)>>>
2727
&Tests) {
28-
std::string IR;
29-
IR.append(Head.begin(), Head.end());
30-
for (auto &Elem : Tests)
28+
for (auto &Elem : Tests) {
29+
std::string IR;
30+
IR.append(Head.begin(), Head.end());
3131
IR.append(Elem.first.begin(), Elem.first.end());
32-
IR.append(Tail.begin(), Tail.end());
33-
LLVMContext C;
34-
SMDiagnostic Err;
35-
std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);
36-
if (!Mod)
37-
Err.print("AssumeQueryAPI", errs());
38-
unsigned Idx = 0;
39-
for (Instruction &I : (*Mod->getFunction("test")->begin())) {
40-
if (Idx < Tests.size())
41-
Tests[Idx].second(&I);
42-
Idx++;
32+
IR.append(Tail.begin(), Tail.end());
33+
LLVMContext C;
34+
SMDiagnostic Err;
35+
std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);
36+
if (!Mod)
37+
Err.print("AssumeQueryAPI", errs());
38+
Elem.second(&*(Mod->getFunction("test")->begin()->begin()));
4339
}
4440
}
4541

@@ -199,7 +195,29 @@ TEST(AssumeQueryAPI, hasAttributeInAssume) {
199195
Attribute::AttrKind::Dereferenceable, 12, true);
200196
}));
201197

202-
/// Keep this test last as it modifies the function.
198+
Tests.push_back(std::make_pair(
199+
"call void @func1(i32* readnone align 32 "
200+
"dereferenceable(48) noalias %P, i32* "
201+
"align 8 dereferenceable(28) %P1, i32* align 64 "
202+
"dereferenceable(4) "
203+
"%P2, i32* nonnull align 16 dereferenceable(12) %P3)\n",
204+
[](Instruction *I) {
205+
CallInst *Assume = BuildAssumeFromInst(I);
206+
Assume->insertBefore(I);
207+
I->getOperand(1)->dropDroppableUses();
208+
I->getOperand(2)->dropDroppableUses();
209+
I->getOperand(3)->dropDroppableUses();
210+
AssertMatchesExactlyAttributes(
211+
Assume, I->getOperand(0),
212+
"(readnone|align|dereferenceable|noalias)");
213+
AssertMatchesExactlyAttributes(Assume, I->getOperand(1), "");
214+
AssertMatchesExactlyAttributes(Assume, I->getOperand(2), "");
215+
AssertMatchesExactlyAttributes(Assume, I->getOperand(3), "");
216+
AssertHasTheRightValue(Assume, I->getOperand(0),
217+
Attribute::AttrKind::Alignment, 32, true);
218+
AssertHasTheRightValue(Assume, I->getOperand(0),
219+
Attribute::AttrKind::Dereferenceable, 48, true);
220+
}));
203221
Tests.push_back(std::make_pair(
204222
"call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align "
205223
"8 noalias %P1)\n",

0 commit comments

Comments
 (0)