Skip to content

Commit 3427de7

Browse files
committed
[DeadStoreElimination] Optimize tautological assignments
If a store is immediately dominated by a condition that ensures that the value being stored in a memory location is already present at that memory location, consider the store a noop. Fixes #63419
1 parent 0871c4b commit 3427de7

File tree

2 files changed

+130
-0
lines changed

2 files changed

+130
-0
lines changed

llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1901,6 +1901,52 @@ struct DSEState {
19011901
return true;
19021902
}
19031903

1904+
// Check if there is a dominating condition, that implies that the value
1905+
// being stored in a ptr is already present in the ptr.
1906+
bool dominatingConditionImpliesValue(MemoryDef *Def) {
1907+
StoreInst *StoreI = dyn_cast<StoreInst>(Def->getMemoryInst());
1908+
BasicBlock *StoreBB = StoreI->getParent();
1909+
Value *StorePtr = StoreI->getPointerOperand();
1910+
Value *StoreVal = StoreI->getValueOperand();
1911+
1912+
DomTreeNode *IDom = DT.getNode(StoreBB)->getIDom();
1913+
if (!IDom)
1914+
return false;
1915+
1916+
auto *BI = dyn_cast<BranchInst>(IDom->getBlock()->getTerminator());
1917+
if (!BI || !BI->isConditional())
1918+
return false;
1919+
1920+
// In case both blocks are the same, we cannot optimize the FalseBB when the
1921+
// condition is true and vice versa. Return false in this case.
1922+
if (BI->getSuccessor(0) == BI->getSuccessor(1))
1923+
return false;
1924+
1925+
Instruction *ICmpL;
1926+
ICmpInst::Predicate Pred;
1927+
if (!match(BI->getCondition(),
1928+
m_c_ICmp(Pred,
1929+
m_CombineAnd(m_Load(m_Specific(StorePtr)),
1930+
m_Instruction(ICmpL)),
1931+
m_Specific(StoreVal))) ||
1932+
!ICmpInst::isEquality(Pred))
1933+
return false;
1934+
1935+
if (Pred == ICmpInst::ICMP_EQ && StoreBB != BI->getSuccessor(0))
1936+
return false;
1937+
1938+
if (Pred == ICmpInst::ICMP_NE && StoreBB != BI->getSuccessor(1))
1939+
return false;
1940+
1941+
MemoryAccess *LoadAcc = MSSA.getMemoryAccess(ICmpL);
1942+
MemoryAccess *ClobAcc =
1943+
MSSA.getSkipSelfWalker()->getClobberingMemoryAccess(Def, BatchAA);
1944+
if (!MSSA.dominates(ClobAcc, LoadAcc))
1945+
return false;
1946+
1947+
return true;
1948+
}
1949+
19041950
/// \returns true if \p Def is a no-op store, either because it
19051951
/// directly stores back a loaded value or stores zero to a calloced object.
19061952
bool storeIsNoop(MemoryDef *Def, const Value *DefUO) {
@@ -1931,6 +1977,9 @@ struct DSEState {
19311977
if (!Store)
19321978
return false;
19331979

1980+
if (dominatingConditionImpliesValue(Def))
1981+
return true;
1982+
19341983
if (auto *LoadI = dyn_cast<LoadInst>(Store->getOperand(0))) {
19351984
if (LoadI->getPointerOperand() == Store->getOperand(1)) {
19361985
// Get the defining access for the load.

llvm/test/Transforms/DeadStoreElimination/noop-stores.ll

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,3 +795,84 @@ join:
795795
store i8 %v, ptr %q, align 1
796796
ret void
797797
}
798+
799+
; Dominating condition implies value already exists, optimize store
800+
define void @remove_tautological_store(ptr %x, ptr %y, ptr %z) {
801+
; CHECK-LABEL: @remove_tautological_store(
802+
; CHECK-NEXT: entry:
803+
; CHECK-NEXT: [[VALX:%.*]] = load i32, ptr [[X:%.*]], align 4
804+
; CHECK-NEXT: [[CMPX:%.*]] = icmp eq i32 [[VALX]], 4
805+
; CHECK-NEXT: br i1 [[CMPX]], label [[IF_X:%.*]], label [[END:%.*]]
806+
; CHECK: if.x:
807+
; CHECK-NEXT: br label [[END]]
808+
; CHECK: end:
809+
; CHECK-NEXT: ret void
810+
;
811+
entry:
812+
%valx = load i32, ptr %x, align 4
813+
%cmpx = icmp eq i32 %valx, 4
814+
br i1 %cmpx, label %if.x, label %end
815+
816+
if.x:
817+
store i32 4, ptr %x, align 4
818+
br label %end
819+
820+
end:
821+
ret void
822+
}
823+
824+
; Dominating condition implies value already exists, optimize store
825+
; Should not optimize since value being stored is different from cond check
826+
define void @remove_tautological_store_fail_diff_value(ptr %x) {
827+
; CHECK-LABEL: @remove_tautological_store_fail_diff_value(
828+
; CHECK-NEXT: entry:
829+
; CHECK-NEXT: [[VALX:%.*]] = load i32, ptr [[X:%.*]], align 4
830+
; CHECK-NEXT: [[CMPX:%.*]] = icmp eq i32 [[VALX]], 4
831+
; CHECK-NEXT: br i1 [[CMPX]], label [[IF_X:%.*]], label [[END:%.*]]
832+
; CHECK: if.x:
833+
; CHECK-NEXT: store i32 5, ptr [[X]], align 4
834+
; CHECK-NEXT: br label [[END]]
835+
; CHECK: end:
836+
; CHECK-NEXT: ret void
837+
;
838+
entry:
839+
%valx = load i32, ptr %x, align 4
840+
%cmpx = icmp eq i32 %valx, 4
841+
br i1 %cmpx, label %if.x, label %end
842+
843+
if.x:
844+
store i32 5, ptr %x, align 4
845+
br label %end
846+
847+
end:
848+
ret void
849+
}
850+
851+
; Dominating condition implies value already exists, optimize store
852+
; Should not optimize since there is a clobbering acc after load
853+
define void @remove_tautological_store_fail_clobber(ptr %x) {
854+
; CHECK-LABEL: @remove_tautological_store_fail_clobber(
855+
; CHECK-NEXT: entry:
856+
; CHECK-NEXT: [[VALX:%.*]] = load i32, ptr [[X:%.*]], align 4
857+
; CHECK-NEXT: store i32 5, ptr [[X]], align 4
858+
; CHECK-NEXT: [[CMPX:%.*]] = icmp eq i32 [[VALX]], 4
859+
; CHECK-NEXT: br i1 [[CMPX]], label [[IF_X:%.*]], label [[END:%.*]]
860+
; CHECK: if.x:
861+
; CHECK-NEXT: store i32 4, ptr [[X]], align 4
862+
; CHECK-NEXT: br label [[END]]
863+
; CHECK: end:
864+
; CHECK-NEXT: ret void
865+
;
866+
entry:
867+
%valx = load i32, ptr %x, align 4
868+
store i32 5, ptr %x, align 4
869+
%cmpx = icmp eq i32 %valx, 4
870+
br i1 %cmpx, label %if.x, label %end
871+
872+
if.x:
873+
store i32 4, ptr %x, align 4
874+
br label %end
875+
876+
end:
877+
ret void
878+
}

0 commit comments

Comments
 (0)