[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
This commit is contained in:
parent
b9330e548e
commit
b98c405f95
@ -362,7 +362,7 @@ static bool isEqualImpl(SimpleValue LHS, SimpleValue RHS) {
|
||||
|
||||
if (LHSI->getOpcode() != RHSI->getOpcode())
|
||||
return false;
|
||||
if (LHSI->isIdenticalToWhenDefined(RHSI)) {
|
||||
if (LHSI->isIdenticalToWhenDefined(RHSI, /*IntersectAttrs=*/true)) {
|
||||
// Convergent calls implicitly depend on the set of threads that is
|
||||
// currently executing, so conservatively return false if they are in
|
||||
// different basic blocks.
|
||||
@ -551,7 +551,7 @@ bool DenseMapInfo<CallValue>::isEqual(CallValue LHS, CallValue RHS) {
|
||||
if (LHSI->isConvergent() && LHSI->getParent() != RHSI->getParent())
|
||||
return false;
|
||||
|
||||
return LHSI->isIdenticalTo(RHSI);
|
||||
return LHSI->isIdenticalToWhenDefined(RHSI, /*IntersectAttrs=*/true);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
@ -1307,6 +1307,23 @@ static void combineIRFlags(Instruction &From, Value *To) {
|
||||
(I->hasPoisonGeneratingFlags() && !programUndefinedIfPoison(I)))
|
||||
I->andIRFlags(&From);
|
||||
}
|
||||
if (isa<CallBase>(&From) && isa<CallBase>(To)) {
|
||||
// NB: Intersection of attrs between InVal.first and Inst is overly
|
||||
// conservative. Since we only CSE readonly functions that have the same
|
||||
// memory state, we can preserve (or possibly in some cases combine)
|
||||
// more attributes. Likewise this implies when checking equality of
|
||||
// callsite for CSEing, we can probably ignore more attributes.
|
||||
// Generally poison generating attributes need to be handled with more
|
||||
// care as they can create *new* UB if preserved/combined and violated.
|
||||
// Attributes that imply immediate UB on the other hand would have been
|
||||
// violated either way.
|
||||
bool Success =
|
||||
cast<CallBase>(To)->tryIntersectAttributes(cast<CallBase>(&From));
|
||||
assert(Success && "Failed to intersect attributes in callsites that "
|
||||
"passed identical check");
|
||||
// For NDEBUG Compile.
|
||||
(void)Success;
|
||||
}
|
||||
}
|
||||
|
||||
bool EarlyCSE::overridingStores(const ParseMemoryInst &Earlier,
|
||||
@ -1632,6 +1649,7 @@ bool EarlyCSE::processNode(DomTreeNode *Node) {
|
||||
LLVM_DEBUG(dbgs() << "Skipping due to debug counter\n");
|
||||
continue;
|
||||
}
|
||||
combineIRFlags(Inst, InVal.first);
|
||||
if (!Inst.use_empty())
|
||||
Inst.replaceAllUsesWith(InVal.first);
|
||||
salvageKnowledge(&Inst, &AC);
|
||||
|
@ -13,9 +13,8 @@ declare i8 @buz.fp(float, float)
|
||||
define i8 @same_parent_combine_diff_attrs(i8 %x, i8 %y) {
|
||||
; CHECK-LABEL: define i8 @same_parent_combine_diff_attrs(
|
||||
; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
|
||||
; CHECK-NEXT: [[C2:%.*]] = call i8 @baz(i8 noundef [[X]], i8 noundef [[Y]]) #[[ATTR0:[0-9]+]]
|
||||
; CHECK-NEXT: [[C1:%.*]] = call i8 @baz(i8 [[X]], i8 noundef [[Y]]) #[[ATTR0]]
|
||||
; CHECK-NEXT: [[R:%.*]] = call i8 @buz(i8 [[C1]], i8 [[C2]])
|
||||
; CHECK-NEXT: [[C1:%.*]] = call i8 @baz(i8 [[X]], i8 noundef [[Y]]) #[[ATTR0:[0-9]+]]
|
||||
; CHECK-NEXT: [[R:%.*]] = call i8 @buz(i8 [[C1]], i8 [[C1]])
|
||||
; CHECK-NEXT: ret i8 [[R]]
|
||||
;
|
||||
%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) {
|
||||
define i8 @same_parent_combine_diff_attrs_needs_intersect(i8 %x, i8 %y) {
|
||||
; CHECK-LABEL: define i8 @same_parent_combine_diff_attrs_needs_intersect(
|
||||
; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
|
||||
; CHECK-NEXT: [[C1:%.*]] = call nonnull ptr @baz.ptr(i8 noundef [[X]], i8 noundef [[Y]]) #[[ATTR0]]
|
||||
; CHECK-NEXT: [[C0:%.*]] = call ptr @baz.ptr(i8 [[X]], i8 noundef [[Y]]) #[[ATTR0]]
|
||||
; CHECK-NEXT: [[R:%.*]] = call i8 @buz.ptr(ptr [[C0]], ptr [[C1]])
|
||||
; CHECK-NEXT: [[C1:%.*]] = call ptr @baz.ptr(i8 [[X]], i8 noundef [[Y]]) #[[ATTR0]]
|
||||
; CHECK-NEXT: [[R:%.*]] = call i8 @buz.ptr(ptr [[C1]], ptr [[C1]])
|
||||
; CHECK-NEXT: ret i8 [[R]]
|
||||
;
|
||||
%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) {
|
||||
define i8 @same_parent_combine_diff_attrs_fmf(float %x, float %y) {
|
||||
; CHECK-LABEL: define i8 @same_parent_combine_diff_attrs_fmf(
|
||||
; CHECK-SAME: float [[X:%.*]], float [[Y:%.*]]) {
|
||||
; CHECK-NEXT: [[C1:%.*]] = call nnan nsz float @baz.fp(float noundef [[X]], float noundef [[Y]]) #[[ATTR1:[0-9]+]]
|
||||
; CHECK-NEXT: [[C0:%.*]] = call nnan float @baz.fp(float [[X]], float noundef [[Y]]) #[[ATTR1]]
|
||||
; CHECK-NEXT: [[R:%.*]] = call i8 @buz.fp(float [[C0]], float [[C1]])
|
||||
; CHECK-NEXT: [[C1:%.*]] = call nnan float @baz.fp(float [[X]], float noundef [[Y]]) #[[ATTR1:[0-9]+]]
|
||||
; CHECK-NEXT: [[R:%.*]] = call i8 @buz.fp(float [[C1]], float [[C1]])
|
||||
; CHECK-NEXT: ret i8 [[R]]
|
||||
;
|
||||
%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) {
|
||||
define i8 @same_parent_combine_diff_attrs_fmf2(float %x, float %y) {
|
||||
; CHECK-LABEL: define i8 @same_parent_combine_diff_attrs_fmf2(
|
||||
; CHECK-SAME: float [[X:%.*]], float [[Y:%.*]]) {
|
||||
; CHECK-NEXT: [[C1:%.*]] = call nnan float @baz.fp(float noundef [[X]], float noundef [[Y]]) #[[ATTR0]]
|
||||
; CHECK-NEXT: [[C0:%.*]] = call nnan nsz float @baz.fp(float [[X]], float noundef [[Y]]) #[[ATTR0]]
|
||||
; CHECK-NEXT: [[R:%.*]] = call i8 @buz.fp(float [[C0]], float [[C1]])
|
||||
; CHECK-NEXT: [[C1:%.*]] = call nnan float @baz.fp(float [[X]], float noundef [[Y]]) #[[ATTR0]]
|
||||
; CHECK-NEXT: [[R:%.*]] = call i8 @buz.fp(float [[C1]], float [[C1]])
|
||||
; CHECK-NEXT: ret i8 [[R]]
|
||||
;
|
||||
%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) {
|
||||
define i8 @same_parent_combine_diff_attrs_needs_intersect2(i8 %x, i8 %y) {
|
||||
; CHECK-LABEL: define i8 @same_parent_combine_diff_attrs_needs_intersect2(
|
||||
; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
|
||||
; CHECK-NEXT: [[C1:%.*]] = call nonnull ptr @baz.ptr(i8 noundef [[X]], i8 noundef [[Y]]) #[[ATTR1]]
|
||||
; CHECK-NEXT: [[C0:%.*]] = call ptr @baz.ptr(i8 [[X]], i8 noundef [[Y]]) #[[ATTR1]]
|
||||
; CHECK-NEXT: [[R:%.*]] = call i8 @buz.ptr(ptr [[C0]], ptr [[C1]])
|
||||
; CHECK-NEXT: [[C1:%.*]] = call ptr @baz.ptr(i8 [[X]], i8 noundef [[Y]]) #[[ATTR1]]
|
||||
; CHECK-NEXT: [[R:%.*]] = call i8 @buz.ptr(ptr [[C1]], ptr [[C1]])
|
||||
; CHECK-NEXT: ret i8 [[R]]
|
||||
;
|
||||
%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) {
|
||||
define i8 @same_parent_combine_diff_attrs_really_needs_intersect(i8 %x, i8 %y) {
|
||||
; CHECK-LABEL: define i8 @same_parent_combine_diff_attrs_really_needs_intersect(
|
||||
; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
|
||||
; CHECK-NEXT: [[C1:%.*]] = call nonnull ptr @baz.ptr(i8 noundef [[X]], i8 noundef [[Y]]) #[[ATTR1]]
|
||||
; CHECK-NEXT: [[C0:%.*]] = call ptr @baz.ptr(i8 [[X]], i8 noundef [[Y]]) #[[ATTR1]]
|
||||
; CHECK-NEXT: [[R:%.*]] = call i8 @buz.ptr(ptr [[C0]], ptr noundef [[C1]])
|
||||
; CHECK-NEXT: [[C1:%.*]] = call ptr @baz.ptr(i8 [[X]], i8 noundef [[Y]]) #[[ATTR1]]
|
||||
; CHECK-NEXT: [[R:%.*]] = call i8 @buz.ptr(ptr [[C1]], ptr noundef [[C1]])
|
||||
; CHECK-NEXT: ret i8 [[R]]
|
||||
;
|
||||
%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
|
||||
; CHECK-NEXT: [[C1:%.*]] = call nonnull ptr @baz.ptr(i8 [[X]], i8 noundef [[Y]]) #[[ATTR1]]
|
||||
; CHECK-NEXT: br i1 [[C]], label %[[T:.*]], label %[[F:.*]]
|
||||
; CHECK: [[T]]:
|
||||
; CHECK-NEXT: [[C0:%.*]] = call nonnull ptr @baz.ptr(i8 noundef [[X]], i8 noundef [[Y]]) #[[ATTR1]]
|
||||
; CHECK-NEXT: [[R:%.*]] = call i8 @buz.ptr(ptr [[C0]], ptr noundef [[C1]])
|
||||
; CHECK-NEXT: [[R:%.*]] = call i8 @buz.ptr(ptr [[C1]], ptr noundef [[C1]])
|
||||
; CHECK-NEXT: ret i8 [[R]]
|
||||
; CHECK: [[F]]:
|
||||
; CHECK-NEXT: ret i8 9
|
||||
|
Loading…
x
Reference in New Issue
Block a user