[llvm] 32cd189 - [GVN] Look through select/phi when determining underlying object (#99509)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Jul 22 07:22:06 PDT 2024
Author: Nikita Popov
Date: 2024-07-22T16:22:01+02:00
New Revision: 32cd18975d2e1de5a783e9b1c3c21a234d5723b4
URL: https://github.com/llvm/llvm-project/commit/32cd18975d2e1de5a783e9b1c3c21a234d5723b4
DIFF: https://github.com/llvm/llvm-project/commit/32cd18975d2e1de5a783e9b1c3c21a234d5723b4.diff
LOG: [GVN] Look through select/phi when determining underlying object (#99509)
This addresses an optimization regression in Rust we have observed after
https://github.com/llvm/llvm-project/pull/82458. We now only perform
pointer replacement if they have the same underlying object. However,
getUnderlyingObject() by default only looks through linear chains, not
selects/phis. In particular, this means that we miss cases involving
involving pointer induction variables.
This patch fixes this by introducing a new helper
getUnderlyingObjectAggressive() which basically does what
getUnderlyingObjects() does, just specialized to the case where we must
arrive at a single underlying object in the end, and with a limit on the
number of inspected values.
Doing this more expensive underlying object check has no measurable
compile-time impact on CTMark.
Added:
Modified:
llvm/include/llvm/Analysis/ValueTracking.h
llvm/lib/Analysis/Loads.cpp
llvm/lib/Analysis/ValueTracking.cpp
llvm/test/Transforms/GVN/condprop.ll
Removed:
################################################################################
diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h
index 44e27234aa90d..8e5a8dc483d27 100644
--- a/llvm/include/llvm/Analysis/ValueTracking.h
+++ b/llvm/include/llvm/Analysis/ValueTracking.h
@@ -736,6 +736,10 @@ inline Value *getUnderlyingObject(Value *V, unsigned MaxLookup = 6) {
return const_cast<Value *>(getUnderlyingObject(VConst, MaxLookup));
}
+/// Like getUnderlyingObject(), but will try harder to find a single underlying
+/// object. In particular, this function also looks through selects and phis.
+const Value *getUnderlyingObjectAggressive(const Value *V);
+
/// This method is similar to getUnderlyingObject except that it can
/// look through phi and select instructions and return multiple objects.
///
diff --git a/llvm/lib/Analysis/Loads.cpp b/llvm/lib/Analysis/Loads.cpp
index 4f0b92f51777b..61c6aa5e5a3eb 100644
--- a/llvm/lib/Analysis/Loads.cpp
+++ b/llvm/lib/Analysis/Loads.cpp
@@ -750,9 +750,8 @@ static bool isPointerAlwaysReplaceable(const Value *From, const Value *To,
if (isa<Constant>(To) &&
isDereferenceablePointer(To, Type::getInt8Ty(To->getContext()), DL))
return true;
- if (getUnderlyingObject(From) == getUnderlyingObject(To))
- return true;
- return false;
+ return getUnderlyingObjectAggressive(From) ==
+ getUnderlyingObjectAggressive(To);
}
bool llvm::canReplacePointersInUseIfEqual(const Use &U, const Value *To,
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 8f1c42b7ede44..40fe1ffe13f1b 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -6620,6 +6620,48 @@ void llvm::getUnderlyingObjects(const Value *V,
} while (!Worklist.empty());
}
+const Value *llvm::getUnderlyingObjectAggressive(const Value *V) {
+ const unsigned MaxVisited = 8;
+
+ SmallPtrSet<const Value *, 8> Visited;
+ SmallVector<const Value *, 8> Worklist;
+ Worklist.push_back(V);
+ const Value *Object = nullptr;
+ // Used as fallback if we can't find a common underlying object through
+ // recursion.
+ bool First = true;
+ const Value *FirstObject = getUnderlyingObject(V);
+ do {
+ const Value *P = Worklist.pop_back_val();
+ P = First ? FirstObject : getUnderlyingObject(P);
+ First = false;
+
+ if (!Visited.insert(P).second)
+ continue;
+
+ if (Visited.size() == MaxVisited)
+ return FirstObject;
+
+ if (auto *SI = dyn_cast<SelectInst>(P)) {
+ Worklist.push_back(SI->getTrueValue());
+ Worklist.push_back(SI->getFalseValue());
+ continue;
+ }
+
+ if (auto *PN = dyn_cast<PHINode>(P)) {
+ append_range(Worklist, PN->incoming_values());
+ continue;
+ }
+
+ if (!Object)
+ Object = P;
+ else if (Object != P)
+ return FirstObject;
+ } while (!Worklist.empty());
+
+ return Object;
+}
+
/// This is the function that does the work of looking through basic
/// ptrtoint+arithmetic+inttoptr sequences.
static const Value *getUnderlyingObjectFromInt(const Value *V) {
diff --git a/llvm/test/Transforms/GVN/condprop.ll b/llvm/test/Transforms/GVN/condprop.ll
index 02f4bb97d7ebd..3babdd8173a21 100644
--- a/llvm/test/Transforms/GVN/condprop.ll
+++ b/llvm/test/Transforms/GVN/condprop.ll
@@ -813,7 +813,7 @@ define void @select_same_obj(i1 %c, ptr %p, i64 %x) {
; CHECK-NEXT: br i1 [[CMP]], label [[IF:%.*]], label [[EXIT:%.*]]
; CHECK: if:
; CHECK-NEXT: call void @use_ptr(ptr [[P]])
-; CHECK-NEXT: call void @use_ptr(ptr [[P3]])
+; CHECK-NEXT: call void @use_ptr(ptr [[P]])
; CHECK-NEXT: ret void
; CHECK: exit:
; CHECK-NEXT: ret void
@@ -902,7 +902,7 @@ define void @phi_same_obj(i1 %c, ptr %p, i64 %x) {
; CHECK-NEXT: br i1 [[CMP]], label [[IF2:%.*]], label [[EXIT:%.*]]
; CHECK: if2:
; CHECK-NEXT: call void @use_ptr(ptr [[P]])
-; CHECK-NEXT: call void @use_ptr(ptr [[P3]])
+; CHECK-NEXT: call void @use_ptr(ptr [[P]])
; CHECK-NEXT: ret void
; CHECK: exit:
; CHECK-NEXT: ret void
@@ -975,7 +975,7 @@ define void @phi_same_obj_cycle(i1 %c, ptr %p, i64 %x) {
; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[P_IV]], [[P]]
; CHECK-NEXT: br i1 [[CMP]], label [[IF:%.*]], label [[LOOP_LATCH]]
; CHECK: if:
-; CHECK-NEXT: call void @use_ptr(ptr [[P_IV]])
+; CHECK-NEXT: call void @use_ptr(ptr [[P]])
; CHECK-NEXT: call void @use_ptr(ptr [[P]])
; CHECK-NEXT: br label [[LOOP_LATCH]]
; CHECK: loop.latch:
More information about the llvm-commits
mailing list