Skip to content

Commit ad90c0c

Browse files
nikicmahesh-attarde
authored andcommitted
[DropUnnecessaryAssumes] Add support for operand bundles (llvm#160311)
This extends the DropUnnecessaryAssumes pass to also handle operand bundle assumes. For this purpose, export the affected value analysis for operand bundles from AssumptionCache. If the bundle only affects ephemeral values, drop it. If all bundles on an assume are dropped, drop the whole assume.
1 parent 4d3fd48 commit ad90c0c

File tree

4 files changed

+177
-28
lines changed

4 files changed

+177
-28
lines changed

llvm/include/llvm/Analysis/AssumptionCache.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
namespace llvm {
2929

3030
class AssumeInst;
31+
struct OperandBundleUse;
3132
class Function;
3233
class raw_ostream;
3334
class TargetTransformInfo;
@@ -165,6 +166,11 @@ class AssumptionCache {
165166

166167
return AVI->second;
167168
}
169+
170+
/// Determine which values are affected by this assume operand bundle.
171+
static void
172+
findValuesAffectedByOperandBundle(OperandBundleUse Bundle,
173+
function_ref<void(Value *)> InsertAffected);
168174
};
169175

170176
/// A function analysis which provides an \c AssumptionCache.

llvm/lib/Analysis/AssumptionCache.cpp

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,22 @@ AssumptionCache::getOrInsertAffectedValues(Value *V) {
5353
return AffectedValues[AffectedValueCallbackVH(V, this)];
5454
}
5555

56+
void AssumptionCache::findValuesAffectedByOperandBundle(
57+
OperandBundleUse Bundle, function_ref<void(Value *)> InsertAffected) {
58+
auto AddAffectedVal = [&](Value *V) {
59+
if (isa<Argument, GlobalValue, Instruction>(V))
60+
InsertAffected(V);
61+
};
62+
63+
if (Bundle.getTagName() == "separate_storage") {
64+
assert(Bundle.Inputs.size() == 2 && "separate_storage must have two args");
65+
AddAffectedVal(getUnderlyingObject(Bundle.Inputs[0]));
66+
AddAffectedVal(getUnderlyingObject(Bundle.Inputs[1]));
67+
} else if (Bundle.Inputs.size() > ABA_WasOn &&
68+
Bundle.getTagName() != IgnoreBundleTag)
69+
AddAffectedVal(Bundle.Inputs[ABA_WasOn]);
70+
}
71+
5672
static void
5773
findAffectedValues(CallBase *CI, TargetTransformInfo *TTI,
5874
SmallVectorImpl<AssumptionCache::ResultElem> &Affected) {
@@ -69,17 +85,10 @@ findAffectedValues(CallBase *CI, TargetTransformInfo *TTI,
6985
}
7086
};
7187

72-
for (unsigned Idx = 0; Idx != CI->getNumOperandBundles(); Idx++) {
73-
OperandBundleUse Bundle = CI->getOperandBundleAt(Idx);
74-
if (Bundle.getTagName() == "separate_storage") {
75-
assert(Bundle.Inputs.size() == 2 &&
76-
"separate_storage must have two args");
77-
AddAffectedVal(getUnderlyingObject(Bundle.Inputs[0]), Idx);
78-
AddAffectedVal(getUnderlyingObject(Bundle.Inputs[1]), Idx);
79-
} else if (Bundle.Inputs.size() > ABA_WasOn &&
80-
Bundle.getTagName() != IgnoreBundleTag)
81-
AddAffectedVal(Bundle.Inputs[ABA_WasOn], Idx);
82-
}
88+
for (unsigned Idx = 0; Idx != CI->getNumOperandBundles(); Idx++)
89+
AssumptionCache::findValuesAffectedByOperandBundle(
90+
CI->getOperandBundleAt(Idx),
91+
[&](Value *V) { Affected.push_back({V, Idx}); });
8392

8493
Value *Cond = CI->getArgOperand(0);
8594
findValuesAffectedByCondition(Cond, /*IsAssume=*/true, InsertAffected);

llvm/lib/Transforms/Scalar/DropUnnecessaryAssumes.cpp

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@
1616
using namespace llvm;
1717
using namespace llvm::PatternMatch;
1818

19+
static bool affectedValuesAreEphemeral(ArrayRef<Value *> Affected) {
20+
// If all the affected uses have only one use (part of the assume), then
21+
// the assume does not provide useful information. Note that additional
22+
// users may appear as a result of inlining and CSE, so we should only
23+
// make this assumption late in the optimization pipeline.
24+
// TODO: Handle dead cyclic usages.
25+
// TODO: Handle multiple dead assumes on the same value.
26+
return all_of(Affected, match_fn(m_OneUse(m_Value())));
27+
}
28+
1929
PreservedAnalyses
2030
DropUnnecessaryAssumesPass::run(Function &F, FunctionAnalysisManager &FAM) {
2131
AssumptionCache &AC = FAM.getResult<AssumptionAnalysis>(F);
@@ -26,26 +36,64 @@ DropUnnecessaryAssumesPass::run(Function &F, FunctionAnalysisManager &FAM) {
2636
if (!Assume)
2737
continue;
2838

29-
// TODO: Handle assumes with operand bundles.
30-
if (Assume->hasOperandBundles())
39+
if (Assume->hasOperandBundles()) {
40+
// Handle operand bundle assumptions.
41+
SmallVector<WeakTrackingVH> DeadBundleArgs;
42+
SmallVector<OperandBundleDef> KeptBundles;
43+
unsigned NumBundles = Assume->getNumOperandBundles();
44+
for (unsigned I = 0; I != NumBundles; ++I) {
45+
auto IsDead = [](OperandBundleUse Bundle) {
46+
// "ignore" operand bundles are always dead.
47+
if (Bundle.getTagName() == "ignore")
48+
return true;
49+
50+
// Bundles without arguments do not affect any specific values.
51+
// Always keep them for now.
52+
if (Bundle.Inputs.empty())
53+
return false;
54+
55+
SmallVector<Value *> Affected;
56+
AssumptionCache::findValuesAffectedByOperandBundle(
57+
Bundle, [&](Value *A) { Affected.push_back(A); });
58+
59+
return affectedValuesAreEphemeral(Affected);
60+
};
61+
62+
OperandBundleUse Bundle = Assume->getOperandBundleAt(I);
63+
if (IsDead(Bundle))
64+
append_range(DeadBundleArgs, Bundle.Inputs);
65+
else
66+
KeptBundles.emplace_back(Bundle);
67+
}
68+
69+
if (KeptBundles.size() != NumBundles) {
70+
if (KeptBundles.empty()) {
71+
// All operand bundles are dead, remove the whole assume.
72+
Assume->eraseFromParent();
73+
} else {
74+
// Otherwise only drop the dead operand bundles.
75+
CallBase *NewAssume =
76+
CallBase::Create(Assume, KeptBundles, Assume->getIterator());
77+
AC.registerAssumption(cast<AssumeInst>(NewAssume));
78+
Assume->eraseFromParent();
79+
}
80+
81+
RecursivelyDeleteTriviallyDeadInstructionsPermissive(DeadBundleArgs);
82+
Changed = true;
83+
}
3184
continue;
85+
}
3286

3387
Value *Cond = Assume->getArgOperand(0);
3488
// Don't drop type tests, which have special semantics.
3589
if (match(Cond, m_Intrinsic<Intrinsic::type_test>()))
3690
continue;
3791

38-
SmallPtrSet<Value *, 8> Affected;
92+
SmallVector<Value *> Affected;
3993
findValuesAffectedByCondition(Cond, /*IsAssume=*/true,
40-
[&](Value *A) { Affected.insert(A); });
41-
42-
// If all the affected uses have only one use (part of the assume), then
43-
// the assume does not provide useful information. Note that additional
44-
// users may appear as a result of inlining and CSE, so we should only
45-
// make this assumption late in the optimization pipeline.
46-
// TODO: Handle dead cyclic usages.
47-
// TODO: Handle multiple dead assumes on the same value.
48-
if (!all_of(Affected, match_fn(m_OneUse(m_Value()))))
94+
[&](Value *A) { Affected.push_back(A); });
95+
96+
if (!affectedValuesAreEphemeral(Affected))
4997
continue;
5098

5199
Assume->eraseFromParent();

llvm/test/Transforms/DropUnnecessaryAssumes/basic.ll

Lines changed: 91 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,18 +63,17 @@ define i32 @multiple_live2(i32 %x, i32 %y) {
6363
ret i32 %y
6464
}
6565

66-
define void @operand_bundle_dead(ptr %x) {
67-
; CHECK-LABEL: define void @operand_bundle_dead(
66+
define void @operand_bundle_one_dead(ptr %x) {
67+
; CHECK-LABEL: define void @operand_bundle_one_dead(
6868
; CHECK-SAME: ptr [[X:%.*]]) {
69-
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[X]], i64 8) ]
7069
; CHECK-NEXT: ret void
7170
;
7271
call void @llvm.assume(i1 true) ["align"(ptr %x, i64 8)]
7372
ret void
7473
}
7574

76-
define ptr @operand_bundle_live(ptr %x) {
77-
; CHECK-LABEL: define ptr @operand_bundle_live(
75+
define ptr @operand_bundle_one_live(ptr %x) {
76+
; CHECK-LABEL: define ptr @operand_bundle_one_live(
7877
; CHECK-SAME: ptr [[X:%.*]]) {
7978
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[X]], i64 8) ]
8079
; CHECK-NEXT: ret ptr [[X]]
@@ -83,6 +82,93 @@ define ptr @operand_bundle_live(ptr %x) {
8382
ret ptr %x
8483
}
8584

85+
define void @operand_bundle_multiple_dead(ptr %x, ptr %y) {
86+
; CHECK-LABEL: define void @operand_bundle_multiple_dead(
87+
; CHECK-SAME: ptr [[X:%.*]], ptr [[Y:%.*]]) {
88+
; CHECK-NEXT: ret void
89+
;
90+
call void @llvm.assume(i1 true) ["align"(ptr %x, i64 8), "align"(ptr %y, i64 8)]
91+
ret void
92+
}
93+
94+
define ptr @operand_bundle_one_live_one_dead(ptr %x, ptr %y) {
95+
; CHECK-LABEL: define ptr @operand_bundle_one_live_one_dead(
96+
; CHECK-SAME: ptr [[X:%.*]], ptr [[Y:%.*]]) {
97+
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[Y]], i64 8) ]
98+
; CHECK-NEXT: ret ptr [[Y]]
99+
;
100+
call void @llvm.assume(i1 true) ["align"(ptr %x, i64 8), "align"(ptr %y, i64 8)]
101+
ret ptr %y
102+
}
103+
104+
define i64 @operand_bundle_ignore_unaffected_operands(ptr %x, i64 %align) {
105+
; CHECK-LABEL: define i64 @operand_bundle_ignore_unaffected_operands(
106+
; CHECK-SAME: ptr [[X:%.*]], i64 [[ALIGN:%.*]]) {
107+
; CHECK-NEXT: ret i64 [[ALIGN]]
108+
;
109+
call void @llvm.assume(i1 true) ["align"(ptr %x, i64 %align)]
110+
ret i64 %align
111+
}
112+
113+
define void @operand_bundle_remove_dead_insts(ptr %x) {
114+
; CHECK-LABEL: define void @operand_bundle_remove_dead_insts(
115+
; CHECK-SAME: ptr [[X:%.*]]) {
116+
; CHECK-NEXT: ret void
117+
;
118+
%gep = getelementptr i8, ptr %x, i64 8
119+
call void @llvm.assume(i1 true) ["align"(ptr %gep, i64 8)]
120+
ret void
121+
}
122+
123+
define void @operand_bundle_no_args() {
124+
; CHECK-LABEL: define void @operand_bundle_no_args() {
125+
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "cold"() ]
126+
; CHECK-NEXT: ret void
127+
;
128+
call void @llvm.assume(i1 true) ["cold"()]
129+
ret void
130+
}
131+
132+
; Can always drop ignore bundles, regardless of uses.
133+
define ptr @operand_bundle_ignore(ptr %x) {
134+
; CHECK-LABEL: define ptr @operand_bundle_ignore(
135+
; CHECK-SAME: ptr [[X:%.*]]) {
136+
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr [[X]]) ]
137+
; CHECK-NEXT: ret ptr [[X]]
138+
;
139+
call void @llvm.assume(i1 true) ["ignore"(), "ignore"(ptr %x), "nonnull"(ptr %x)]
140+
ret ptr %x
141+
}
142+
143+
define void @operand_bundle_separate_storage_both_dead(ptr %x, ptr %y) {
144+
; CHECK-LABEL: define void @operand_bundle_separate_storage_both_dead(
145+
; CHECK-SAME: ptr [[X:%.*]], ptr [[Y:%.*]]) {
146+
; CHECK-NEXT: ret void
147+
;
148+
call void @llvm.assume(i1 true) ["separate_storage"(ptr %x, ptr %y)]
149+
ret void
150+
}
151+
152+
define ptr @operand_bundle_separate_storage_one_live1(ptr %x, ptr %y) {
153+
; CHECK-LABEL: define ptr @operand_bundle_separate_storage_one_live1(
154+
; CHECK-SAME: ptr [[X:%.*]], ptr [[Y:%.*]]) {
155+
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "separate_storage"(ptr [[X]], ptr [[Y]]) ]
156+
; CHECK-NEXT: ret ptr [[Y]]
157+
;
158+
call void @llvm.assume(i1 true) ["separate_storage"(ptr %x, ptr %y)]
159+
ret ptr %y
160+
}
161+
162+
define ptr @operand_bundle_separate_storage_one_live2(ptr %x, ptr %y) {
163+
; CHECK-LABEL: define ptr @operand_bundle_separate_storage_one_live2(
164+
; CHECK-SAME: ptr [[X:%.*]], ptr [[Y:%.*]]) {
165+
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "separate_storage"(ptr [[X]], ptr [[Y]]) ]
166+
; CHECK-NEXT: ret ptr [[X]]
167+
;
168+
call void @llvm.assume(i1 true) ["separate_storage"(ptr %x, ptr %y)]
169+
ret ptr %x
170+
}
171+
86172
define void @type_test(ptr %x) {
87173
; CHECK-LABEL: define void @type_test(
88174
; CHECK-SAME: ptr [[X:%.*]]) {

0 commit comments

Comments
 (0)