Skip to content

Commit b98c405

Browse files
committed
[EarlyCSE] De-Duplicate callsites with differing attrs
We only do this if the attributes of the two callsites are compatible (intersectable) which is probably not in fact necessary. Closes #110929
1 parent b9330e5 commit b98c405

File tree

2 files changed

+33
-22
lines changed

2 files changed

+33
-22
lines changed

llvm/lib/Transforms/Scalar/EarlyCSE.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ static bool isEqualImpl(SimpleValue LHS, SimpleValue RHS) {
362362

363363
if (LHSI->getOpcode() != RHSI->getOpcode())
364364
return false;
365-
if (LHSI->isIdenticalToWhenDefined(RHSI)) {
365+
if (LHSI->isIdenticalToWhenDefined(RHSI, /*IntersectAttrs=*/true)) {
366366
// Convergent calls implicitly depend on the set of threads that is
367367
// currently executing, so conservatively return false if they are in
368368
// different basic blocks.
@@ -551,7 +551,7 @@ bool DenseMapInfo<CallValue>::isEqual(CallValue LHS, CallValue RHS) {
551551
if (LHSI->isConvergent() && LHSI->getParent() != RHSI->getParent())
552552
return false;
553553

554-
return LHSI->isIdenticalTo(RHSI);
554+
return LHSI->isIdenticalToWhenDefined(RHSI, /*IntersectAttrs=*/true);
555555
}
556556

557557
//===----------------------------------------------------------------------===//
@@ -1307,6 +1307,23 @@ static void combineIRFlags(Instruction &From, Value *To) {
13071307
(I->hasPoisonGeneratingFlags() && !programUndefinedIfPoison(I)))
13081308
I->andIRFlags(&From);
13091309
}
1310+
if (isa<CallBase>(&From) && isa<CallBase>(To)) {
1311+
// NB: Intersection of attrs between InVal.first and Inst is overly
1312+
// conservative. Since we only CSE readonly functions that have the same
1313+
// memory state, we can preserve (or possibly in some cases combine)
1314+
// more attributes. Likewise this implies when checking equality of
1315+
// callsite for CSEing, we can probably ignore more attributes.
1316+
// Generally poison generating attributes need to be handled with more
1317+
// care as they can create *new* UB if preserved/combined and violated.
1318+
// Attributes that imply immediate UB on the other hand would have been
1319+
// violated either way.
1320+
bool Success =
1321+
cast<CallBase>(To)->tryIntersectAttributes(cast<CallBase>(&From));
1322+
assert(Success && "Failed to intersect attributes in callsites that "
1323+
"passed identical check");
1324+
// For NDEBUG Compile.
1325+
(void)Success;
1326+
}
13101327
}
13111328

13121329
bool EarlyCSE::overridingStores(const ParseMemoryInst &Earlier,
@@ -1632,6 +1649,7 @@ bool EarlyCSE::processNode(DomTreeNode *Node) {
16321649
LLVM_DEBUG(dbgs() << "Skipping due to debug counter\n");
16331650
continue;
16341651
}
1652+
combineIRFlags(Inst, InVal.first);
16351653
if (!Inst.use_empty())
16361654
Inst.replaceAllUsesWith(InVal.first);
16371655
salvageKnowledge(&Inst, &AC);

llvm/test/Transforms/EarlyCSE/replace-calls-def-attrs.ll

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@ declare i8 @buz.fp(float, float)
1313
define i8 @same_parent_combine_diff_attrs(i8 %x, i8 %y) {
1414
; CHECK-LABEL: define i8 @same_parent_combine_diff_attrs(
1515
; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
16-
; CHECK-NEXT: [[C2:%.*]] = call i8 @baz(i8 noundef [[X]], i8 noundef [[Y]]) #[[ATTR0:[0-9]+]]
17-
; CHECK-NEXT: [[C1:%.*]] = call i8 @baz(i8 [[X]], i8 noundef [[Y]]) #[[ATTR0]]
18-
; CHECK-NEXT: [[R:%.*]] = call i8 @buz(i8 [[C1]], i8 [[C2]])
16+
; CHECK-NEXT: [[C1:%.*]] = call i8 @baz(i8 [[X]], i8 noundef [[Y]]) #[[ATTR0:[0-9]+]]
17+
; CHECK-NEXT: [[R:%.*]] = call i8 @buz(i8 [[C1]], i8 [[C1]])
1918
; CHECK-NEXT: ret i8 [[R]]
2019
;
2120
%c1 = call i8 @baz(i8 noundef %x, i8 noundef %y) readnone
@@ -28,9 +27,8 @@ define i8 @same_parent_combine_diff_attrs(i8 %x, i8 %y) {
2827
define i8 @same_parent_combine_diff_attrs_needs_intersect(i8 %x, i8 %y) {
2928
; CHECK-LABEL: define i8 @same_parent_combine_diff_attrs_needs_intersect(
3029
; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
31-
; CHECK-NEXT: [[C1:%.*]] = call nonnull ptr @baz.ptr(i8 noundef [[X]], i8 noundef [[Y]]) #[[ATTR0]]
32-
; CHECK-NEXT: [[C0:%.*]] = call ptr @baz.ptr(i8 [[X]], i8 noundef [[Y]]) #[[ATTR0]]
33-
; CHECK-NEXT: [[R:%.*]] = call i8 @buz.ptr(ptr [[C0]], ptr [[C1]])
30+
; CHECK-NEXT: [[C1:%.*]] = call ptr @baz.ptr(i8 [[X]], i8 noundef [[Y]]) #[[ATTR0]]
31+
; CHECK-NEXT: [[R:%.*]] = call i8 @buz.ptr(ptr [[C1]], ptr [[C1]])
3432
; CHECK-NEXT: ret i8 [[R]]
3533
;
3634
%c1 = call nonnull ptr @baz.ptr(i8 noundef %x, i8 noundef %y) readnone
@@ -43,9 +41,8 @@ define i8 @same_parent_combine_diff_attrs_needs_intersect(i8 %x, i8 %y) {
4341
define i8 @same_parent_combine_diff_attrs_fmf(float %x, float %y) {
4442
; CHECK-LABEL: define i8 @same_parent_combine_diff_attrs_fmf(
4543
; CHECK-SAME: float [[X:%.*]], float [[Y:%.*]]) {
46-
; CHECK-NEXT: [[C1:%.*]] = call nnan nsz float @baz.fp(float noundef [[X]], float noundef [[Y]]) #[[ATTR1:[0-9]+]]
47-
; CHECK-NEXT: [[C0:%.*]] = call nnan float @baz.fp(float [[X]], float noundef [[Y]]) #[[ATTR1]]
48-
; CHECK-NEXT: [[R:%.*]] = call i8 @buz.fp(float [[C0]], float [[C1]])
44+
; CHECK-NEXT: [[C1:%.*]] = call nnan float @baz.fp(float [[X]], float noundef [[Y]]) #[[ATTR1:[0-9]+]]
45+
; CHECK-NEXT: [[R:%.*]] = call i8 @buz.fp(float [[C1]], float [[C1]])
4946
; CHECK-NEXT: ret i8 [[R]]
5047
;
5148
%c1 = call nnan nsz float @baz.fp(float noundef %x, float noundef %y) readonly
@@ -58,9 +55,8 @@ define i8 @same_parent_combine_diff_attrs_fmf(float %x, float %y) {
5855
define i8 @same_parent_combine_diff_attrs_fmf2(float %x, float %y) {
5956
; CHECK-LABEL: define i8 @same_parent_combine_diff_attrs_fmf2(
6057
; CHECK-SAME: float [[X:%.*]], float [[Y:%.*]]) {
61-
; CHECK-NEXT: [[C1:%.*]] = call nnan float @baz.fp(float noundef [[X]], float noundef [[Y]]) #[[ATTR0]]
62-
; CHECK-NEXT: [[C0:%.*]] = call nnan nsz float @baz.fp(float [[X]], float noundef [[Y]]) #[[ATTR0]]
63-
; CHECK-NEXT: [[R:%.*]] = call i8 @buz.fp(float [[C0]], float [[C1]])
58+
; CHECK-NEXT: [[C1:%.*]] = call nnan float @baz.fp(float [[X]], float noundef [[Y]]) #[[ATTR0]]
59+
; CHECK-NEXT: [[R:%.*]] = call i8 @buz.fp(float [[C1]], float [[C1]])
6460
; CHECK-NEXT: ret i8 [[R]]
6561
;
6662
%c1 = call nnan float @baz.fp(float noundef %x, float noundef %y) readnone
@@ -73,9 +69,8 @@ define i8 @same_parent_combine_diff_attrs_fmf2(float %x, float %y) {
7369
define i8 @same_parent_combine_diff_attrs_needs_intersect2(i8 %x, i8 %y) {
7470
; CHECK-LABEL: define i8 @same_parent_combine_diff_attrs_needs_intersect2(
7571
; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
76-
; CHECK-NEXT: [[C1:%.*]] = call nonnull ptr @baz.ptr(i8 noundef [[X]], i8 noundef [[Y]]) #[[ATTR1]]
77-
; CHECK-NEXT: [[C0:%.*]] = call ptr @baz.ptr(i8 [[X]], i8 noundef [[Y]]) #[[ATTR1]]
78-
; CHECK-NEXT: [[R:%.*]] = call i8 @buz.ptr(ptr [[C0]], ptr [[C1]])
72+
; CHECK-NEXT: [[C1:%.*]] = call ptr @baz.ptr(i8 [[X]], i8 noundef [[Y]]) #[[ATTR1]]
73+
; CHECK-NEXT: [[R:%.*]] = call i8 @buz.ptr(ptr [[C1]], ptr [[C1]])
7974
; CHECK-NEXT: ret i8 [[R]]
8075
;
8176
%c1 = call nonnull ptr @baz.ptr(i8 noundef %x, i8 noundef %y) readonly
@@ -88,9 +83,8 @@ define i8 @same_parent_combine_diff_attrs_needs_intersect2(i8 %x, i8 %y) {
8883
define i8 @same_parent_combine_diff_attrs_really_needs_intersect(i8 %x, i8 %y) {
8984
; CHECK-LABEL: define i8 @same_parent_combine_diff_attrs_really_needs_intersect(
9085
; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
91-
; CHECK-NEXT: [[C1:%.*]] = call nonnull ptr @baz.ptr(i8 noundef [[X]], i8 noundef [[Y]]) #[[ATTR1]]
92-
; CHECK-NEXT: [[C0:%.*]] = call ptr @baz.ptr(i8 [[X]], i8 noundef [[Y]]) #[[ATTR1]]
93-
; CHECK-NEXT: [[R:%.*]] = call i8 @buz.ptr(ptr [[C0]], ptr noundef [[C1]])
86+
; CHECK-NEXT: [[C1:%.*]] = call ptr @baz.ptr(i8 [[X]], i8 noundef [[Y]]) #[[ATTR1]]
87+
; CHECK-NEXT: [[R:%.*]] = call i8 @buz.ptr(ptr [[C1]], ptr noundef [[C1]])
9488
; CHECK-NEXT: ret i8 [[R]]
9589
;
9690
%c1 = call nonnull ptr @baz.ptr(i8 noundef %x, i8 noundef %y) readonly
@@ -160,8 +154,7 @@ define i8 @diff_parent_combine_diff_attrs_preserves_return_attrs(i1 %c, i8 %x, i
160154
; CHECK-NEXT: [[C1:%.*]] = call nonnull ptr @baz.ptr(i8 [[X]], i8 noundef [[Y]]) #[[ATTR1]]
161155
; CHECK-NEXT: br i1 [[C]], label %[[T:.*]], label %[[F:.*]]
162156
; CHECK: [[T]]:
163-
; CHECK-NEXT: [[C0:%.*]] = call nonnull ptr @baz.ptr(i8 noundef [[X]], i8 noundef [[Y]]) #[[ATTR1]]
164-
; CHECK-NEXT: [[R:%.*]] = call i8 @buz.ptr(ptr [[C0]], ptr noundef [[C1]])
157+
; CHECK-NEXT: [[R:%.*]] = call i8 @buz.ptr(ptr [[C1]], ptr noundef [[C1]])
165158
; CHECK-NEXT: ret i8 [[R]]
166159
; CHECK: [[F]]:
167160
; CHECK-NEXT: ret i8 9

0 commit comments

Comments
 (0)