Skip to content

Commit

Permalink
[GVN][NewGVN][Local] Handle attributes for function calls after CSE (l…
Browse files Browse the repository at this point in the history
…lvm#114011)

This patch intersects attributes of two calls to avoid introducing UB.
It also skips incompatible call pairs in GVN/NewGVN. However, I cannot
provide negative tests for these changes.

Fixes llvm#113997.
  • Loading branch information
dtcxzyw authored Nov 1, 2024
1 parent bef3b54 commit f16bff1
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 7 deletions.
14 changes: 12 additions & 2 deletions llvm/lib/Transforms/Scalar/GVN.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2189,6 +2189,16 @@ bool GVNPass::processAssumeIntrinsic(AssumeInst *IntrinsicI) {
return Changed;
}

// Return true iff V1 can be replaced with V2.
static bool canBeReplacedBy(Value *V1, Value *V2) {
if (auto *CB1 = dyn_cast<CallBase>(V1))
if (auto *CB2 = dyn_cast<CallBase>(V2))
return CB1->getAttributes()
.intersectWith(CB2->getContext(), CB2->getAttributes())
.has_value();
return true;
}

static void patchAndReplaceAllUsesWith(Instruction *I, Value *Repl) {
patchReplacementInstruction(I, Repl);
I->replaceAllUsesWith(Repl);
Expand Down Expand Up @@ -2734,7 +2744,7 @@ bool GVNPass::processInstruction(Instruction *I) {
// Perform fast-path value-number based elimination of values inherited from
// dominators.
Value *Repl = findLeader(I->getParent(), Num);
if (!Repl) {
if (!Repl || !canBeReplacedBy(I, Repl)) {
// Failure, just remember this instance for future use.
LeaderTable.insert(Num, I, I->getParent());
return false;
Expand Down Expand Up @@ -3000,7 +3010,7 @@ bool GVNPass::performScalarPRE(Instruction *CurInst) {

uint32_t TValNo = VN.phiTranslate(P, CurrentBlock, ValNo, *this);
Value *predV = findLeader(P, TValNo);
if (!predV) {
if (!predV || !canBeReplacedBy(CurInst, predV)) {
predMap.push_back(std::make_pair(static_cast<Value *>(nullptr), P));
PREPred = P;
++NumWithout;
Expand Down
30 changes: 25 additions & 5 deletions llvm/lib/Transforms/Scalar/NewGVN.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3854,6 +3854,16 @@ Value *NewGVN::findPHIOfOpsLeader(const Expression *E,
return nullptr;
}

// Return true iff V1 can be replaced with V2.
static bool canBeReplacedBy(Value *V1, Value *V2) {
if (auto *CB1 = dyn_cast<CallBase>(V1))
if (auto *CB2 = dyn_cast<CallBase>(V2))
return CB1->getAttributes()
.intersectWith(CB2->getContext(), CB2->getAttributes())
.has_value();
return true;
}

bool NewGVN::eliminateInstructions(Function &F) {
// This is a non-standard eliminator. The normal way to eliminate is
// to walk the dominator tree in order, keeping track of available
Expand Down Expand Up @@ -3963,6 +3973,9 @@ bool NewGVN::eliminateInstructions(Function &F) {
MembersLeft.insert(Member);
continue;
}
if (!canBeReplacedBy(Member, Leader))
continue;

LLVM_DEBUG(dbgs() << "Found replacement " << *(Leader) << " for "
<< *Member << "\n");
auto *I = cast<Instruction>(Member);
Expand Down Expand Up @@ -4069,8 +4082,11 @@ bool NewGVN::eliminateInstructions(Function &F) {
if (DominatingLeader != Def) {
// Even if the instruction is removed, we still need to update
// flags/metadata due to downstreams users of the leader.
if (!match(DefI, m_Intrinsic<Intrinsic::ssa_copy>()))
if (!match(DefI, m_Intrinsic<Intrinsic::ssa_copy>())) {
if (!canBeReplacedBy(DefI, DominatingLeader))
continue;
patchReplacementInstruction(DefI, DominatingLeader);
}

markInstructionForDeletion(DefI);
}
Expand Down Expand Up @@ -4112,17 +4128,21 @@ bool NewGVN::eliminateInstructions(Function &F) {
// Don't replace our existing users with ourselves.
if (U->get() == DominatingLeader)
continue;
LLVM_DEBUG(dbgs()
<< "Found replacement " << *DominatingLeader << " for "
<< *U->get() << " in " << *(U->getUser()) << "\n");

// If we replaced something in an instruction, handle the patching of
// metadata. Skip this if we are replacing predicateinfo with its
// original operand, as we already know we can just drop it.
auto *ReplacedInst = cast<Instruction>(U->get());
auto *PI = PredInfo->getPredicateInfoFor(ReplacedInst);
if (!PI || DominatingLeader != PI->OriginalOp)
if (!PI || DominatingLeader != PI->OriginalOp) {
if (!canBeReplacedBy(ReplacedInst, DominatingLeader))
continue;
patchReplacementInstruction(ReplacedInst, DominatingLeader);
}

LLVM_DEBUG(dbgs()
<< "Found replacement " << *DominatingLeader << " for "
<< *U->get() << " in " << *(U->getUser()) << "\n");
U->set(DominatingLeader);
// This is now a use of the dominating leader, which means if the
// dominating leader was dead, it's now live!
Expand Down
11 changes: 11 additions & 0 deletions llvm/lib/Transforms/Utils/Local.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3508,6 +3508,17 @@ void llvm::patchReplacementInstruction(Instruction *I, Value *Repl) {
else if (!isa<LoadInst>(I))
ReplInst->andIRFlags(I);

// Handle attributes.
if (auto *CB1 = dyn_cast<CallBase>(ReplInst)) {
if (auto *CB2 = dyn_cast<CallBase>(I)) {
bool Success = CB1->tryIntersectAttributes(CB2);
assert(Success && "We should not be trying to sink callbases "
"with non-intersectable attributes");
// For NDEBUG Compile.
(void)Success;
}
}

// FIXME: If both the original and replacement value are part of the
// same control-flow region (meaning that the execution of one
// guarantees the execution of the other), then we can combine the
Expand Down
33 changes: 33 additions & 0 deletions llvm/test/Transforms/GVN/pr113997.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
; RUN: opt -S -passes=gvn < %s | FileCheck %s

; Make sure attributes in function calls are intersected correctly.

define i1 @bucket(i32 noundef %x) {
; CHECK-LABEL: define i1 @bucket(
; CHECK-SAME: i32 noundef [[X:%.*]]) {
; CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i32 [[X]], 0
; CHECK-NEXT: [[CTPOP1:%.*]] = tail call range(i32 0, 33) i32 @llvm.ctpop.i32(i32 [[X]])
; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ult i32 [[CTPOP1]], 2
; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP1]], i1 [[CMP2]], i1 false
; CHECK-NEXT: br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
; CHECK: [[IF_ELSE]]:
; CHECK-NEXT: [[RES:%.*]] = icmp eq i32 [[CTPOP1]], 1
; CHECK-NEXT: ret i1 [[RES]]
; CHECK: [[IF_THEN]]:
; CHECK-NEXT: ret i1 false
;
%cmp1 = icmp sgt i32 %x, 0
%ctpop1 = tail call range(i32 1, 32) i32 @llvm.ctpop.i32(i32 %x)
%cmp2 = icmp samesign ult i32 %ctpop1, 2
%cond = select i1 %cmp1, i1 %cmp2, i1 false
br i1 %cond, label %if.then, label %if.else

if.else:
%ctpop2 = tail call range(i32 0, 33) i32 @llvm.ctpop.i32(i32 %x)
%res = icmp eq i32 %ctpop2, 1
ret i1 %res

if.then:
ret i1 false
}
33 changes: 33 additions & 0 deletions llvm/test/Transforms/NewGVN/pr113997.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
; RUN: opt -S -passes=newgvn < %s | FileCheck %s

; Make sure attributes in function calls are intersected correctly.

define i1 @bucket(i32 noundef %x) {
; CHECK-LABEL: define i1 @bucket(
; CHECK-SAME: i32 noundef [[X:%.*]]) {
; CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i32 [[X]], 0
; CHECK-NEXT: [[CTPOP1:%.*]] = tail call range(i32 0, 33) i32 @llvm.ctpop.i32(i32 [[X]])
; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ult i32 [[CTPOP1]], 2
; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP1]], i1 [[CMP2]], i1 false
; CHECK-NEXT: br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
; CHECK: [[IF_ELSE]]:
; CHECK-NEXT: [[RES:%.*]] = icmp eq i32 [[CTPOP1]], 1
; CHECK-NEXT: ret i1 [[RES]]
; CHECK: [[IF_THEN]]:
; CHECK-NEXT: ret i1 false
;
%cmp1 = icmp sgt i32 %x, 0
%ctpop1 = tail call range(i32 1, 32) i32 @llvm.ctpop.i32(i32 %x)
%cmp2 = icmp samesign ult i32 %ctpop1, 2
%cond = select i1 %cmp1, i1 %cmp2, i1 false
br i1 %cond, label %if.then, label %if.else

if.else:
%ctpop2 = tail call range(i32 0, 33) i32 @llvm.ctpop.i32(i32 %x)
%res = icmp eq i32 %ctpop2, 1
ret i1 %res

if.then:
ret i1 false
}

0 comments on commit f16bff1

Please sign in to comment.