Skip to content

Commit 23d953a

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 23d953a

File tree

2 files changed

+378
-0
lines changed

2 files changed

+378
-0
lines changed

llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1901,6 +1901,51 @@ 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+
auto *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, it is not possible to determine
1921+
// if optimization is possible. (We would not want to optimize a store
1922+
// in the FalseBB if condition is true and vice versa.)
1923+
if (BI->getSuccessor(0) == BI->getSuccessor(1))
1924+
return false;
1925+
1926+
Instruction *ICmpL;
1927+
ICmpInst::Predicate Pred;
1928+
if (!match(BI->getCondition(),
1929+
m_c_ICmp(Pred,
1930+
m_CombineAnd(m_Load(m_Specific(StorePtr)),
1931+
m_Instruction(ICmpL)),
1932+
m_Specific(StoreVal))) ||
1933+
!ICmpInst::isEquality(Pred))
1934+
return false;
1935+
1936+
if (Pred == ICmpInst::ICMP_EQ && StoreBB != BI->getSuccessor(0))
1937+
return false;
1938+
1939+
if (Pred == ICmpInst::ICMP_NE && StoreBB != BI->getSuccessor(1))
1940+
return false;
1941+
1942+
MemoryAccess *LoadAcc = MSSA.getMemoryAccess(ICmpL);
1943+
MemoryAccess *ClobAcc =
1944+
MSSA.getSkipSelfWalker()->getClobberingMemoryAccess(Def, BatchAA);
1945+
1946+
return MSSA.dominates(ClobAcc, LoadAcc);
1947+
}
1948+
19041949
/// \returns true if \p Def is a no-op store, either because it
19051950
/// directly stores back a loaded value or stores zero to a calloced object.
19061951
bool storeIsNoop(MemoryDef *Def, const Value *DefUO) {
@@ -1931,6 +1976,9 @@ struct DSEState {
19311976
if (!Store)
19321977
return false;
19331978

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

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

Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,3 +795,333 @@ 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_eq(ptr %x) {
801+
; CHECK-LABEL: @remove_tautological_store_eq(
802+
; CHECK-NEXT: entry:
803+
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[X:%.*]], align 4
804+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[VAL]], 4
805+
; CHECK-NEXT: br i1 [[CMP]], label [[IF_EQ:%.*]], label [[END:%.*]]
806+
; CHECK: if.eq:
807+
; CHECK-NEXT: br label [[END]]
808+
; CHECK: end:
809+
; CHECK-NEXT: ret void
810+
;
811+
entry:
812+
%val = load i32, ptr %x, align 4
813+
%cmp = icmp eq i32 %val, 4
814+
br i1 %cmp, label %if.eq, label %end
815+
816+
if.eq:
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+
define void @remove_tautological_store_var(ptr %x, ptr %y) {
826+
; CHECK-LABEL: @remove_tautological_store_var(
827+
; CHECK-NEXT: entry:
828+
; CHECK-NEXT: [[VALX:%.*]] = load i32, ptr [[X:%.*]], align 4
829+
; CHECK-NEXT: [[VALY:%.*]] = load i32, ptr [[Y:%.*]], align 4
830+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[VALX]], [[VALY]]
831+
; CHECK-NEXT: br i1 [[CMP]], label [[IF_EQ:%.*]], label [[END:%.*]]
832+
; CHECK: if.eq:
833+
; CHECK-NEXT: br label [[END]]
834+
; CHECK: end:
835+
; CHECK-NEXT: ret void
836+
;
837+
entry:
838+
%valx = load i32, ptr %x, align 4
839+
%valy = load i32, ptr %y, align 4
840+
%cmp = icmp eq i32 %valx, %valy
841+
br i1 %cmp, label %if.eq, label %end
842+
843+
if.eq:
844+
store i32 %valy, 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+
define void @remove_tautological_store_ne(ptr %x) {
853+
; CHECK-LABEL: @remove_tautological_store_ne(
854+
; CHECK-NEXT: entry:
855+
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[X:%.*]], align 4
856+
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[VAL]], 4
857+
; CHECK-NEXT: br i1 [[CMP]], label [[IF_NE:%.*]], label [[END:%.*]]
858+
; CHECK: if.ne:
859+
; CHECK-NEXT: br label [[END]]
860+
; CHECK: end:
861+
; CHECK-NEXT: ret void
862+
;
863+
entry:
864+
%val = load i32, ptr %x, align 4
865+
%cmp = icmp ne i32 %val, 4
866+
br i1 %cmp, label %if.ne, label %end
867+
868+
if.ne:
869+
br label %end
870+
871+
end:
872+
store i32 4, ptr %x, align 4
873+
ret void
874+
}
875+
876+
; Dominating condition implies value already exists, optimize store
877+
; Optimizes unordered atomic stores
878+
define void @remove_tautological_store_atomic_unordered(ptr %x) {
879+
; CHECK-LABEL: @remove_tautological_store_atomic_unordered(
880+
; CHECK-NEXT: entry:
881+
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[X:%.*]], align 4
882+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[VAL]], 4
883+
; CHECK-NEXT: br i1 [[CMP]], label [[IF_EQ:%.*]], label [[END:%.*]]
884+
; CHECK: if.eq:
885+
; CHECK-NEXT: br label [[END]]
886+
; CHECK: end:
887+
; CHECK-NEXT: ret void
888+
;
889+
entry:
890+
%val = load i32, ptr %x, align 4
891+
%cmp = icmp eq i32 %val, 4
892+
br i1 %cmp, label %if.eq, label %end
893+
894+
if.eq:
895+
store atomic i32 4, ptr %x unordered, align 4
896+
br label %end
897+
898+
end:
899+
ret void
900+
}
901+
902+
; Dominating condition implies value already exists, optimize store
903+
; Should not optimize ordered atomic stores
904+
define void @remove_tautological_store_atomic_monotonic(ptr %x) {
905+
; CHECK-LABEL: @remove_tautological_store_atomic_monotonic(
906+
; CHECK-NEXT: entry:
907+
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[X:%.*]], align 4
908+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[VAL]], 4
909+
; CHECK-NEXT: br i1 [[CMP]], label [[IF_EQ:%.*]], label [[END:%.*]]
910+
; CHECK: if.eq:
911+
; CHECK-NEXT: store atomic i32 4, ptr [[X]] monotonic, align 4
912+
; CHECK-NEXT: br label [[END]]
913+
; CHECK: end:
914+
; CHECK-NEXT: ret void
915+
;
916+
entry:
917+
%val = load i32, ptr %x, align 4
918+
%cmp = icmp eq i32 %val, 4
919+
br i1 %cmp, label %if.eq, label %end
920+
921+
if.eq:
922+
store atomic i32 4, ptr %x monotonic, align 4
923+
br label %end
924+
925+
end:
926+
ret void
927+
}
928+
929+
; Dominating condition implies value already exists, optimize store
930+
; Should not optimize since the store is in incorrect branch
931+
define void @remove_tautological_store_eq_wrong_branch(ptr %x, ptr %y) {
932+
; CHECK-LABEL: @remove_tautological_store_eq_wrong_branch(
933+
; CHECK-NEXT: entry:
934+
; CHECK-NEXT: [[VALX:%.*]] = load i32, ptr [[X:%.*]], align 4
935+
; CHECK-NEXT: [[VALY:%.*]] = load i32, ptr [[Y:%.*]], align 4
936+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[VALX]], [[VALY]]
937+
; CHECK-NEXT: br i1 [[CMP]], label [[IF_EQ:%.*]], label [[END:%.*]]
938+
; CHECK: if.eq:
939+
; CHECK-NEXT: br label [[END]]
940+
; CHECK: end:
941+
; CHECK-NEXT: store i32 [[VALY]], ptr [[X]], align 4
942+
; CHECK-NEXT: ret void
943+
;
944+
entry:
945+
%valx = load i32, ptr %x, align 4
946+
%valy = load i32, ptr %y, align 4
947+
%cmp = icmp eq i32 %valx, %valy
948+
br i1 %cmp, label %if.eq, label %end
949+
950+
if.eq:
951+
br label %end
952+
953+
end:
954+
store i32 %valy, ptr %x, align 4
955+
ret void
956+
}
957+
958+
; Dominating condition implies value already exists, optimize store
959+
; Should not optimize since the store is in incorrect branch
960+
define void @remove_tautological_store_ne_wrong_branch(ptr %x) {
961+
; CHECK-LABEL: @remove_tautological_store_ne_wrong_branch(
962+
; CHECK-NEXT: entry:
963+
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[X:%.*]], align 4
964+
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[VAL]], 4
965+
; CHECK-NEXT: br i1 [[CMP]], label [[IF_NE:%.*]], label [[END:%.*]]
966+
; CHECK: if.ne:
967+
; CHECK-NEXT: store i32 4, ptr [[X]], align 4
968+
; CHECK-NEXT: br label [[END]]
969+
; CHECK: end:
970+
; CHECK-NEXT: ret void
971+
;
972+
entry:
973+
%val = load i32, ptr %x, align 4
974+
%cmp = icmp ne i32 %val, 4
975+
br i1 %cmp, label %if.ne, label %end
976+
977+
if.ne:
978+
store i32 4, ptr %x, align 4
979+
br label %end
980+
981+
end:
982+
ret void
983+
}
984+
985+
; Dominating condition implies value already exists, optimize store
986+
; Should not optimize since we cannot determine if we should when both
987+
; branches are the same
988+
define void @remove_tautological_store_same_branch(ptr %x) {
989+
; CHECK-LABEL: @remove_tautological_store_same_branch(
990+
; CHECK-NEXT: entry:
991+
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[X:%.*]], align 4
992+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[VAL]], 4
993+
; CHECK-NEXT: br i1 [[CMP]], label [[IF_EQ:%.*]], label [[IF_EQ]]
994+
; CHECK: if.eq:
995+
; CHECK-NEXT: store i32 4, ptr [[X]], align 4
996+
; CHECK-NEXT: br label [[END:%.*]]
997+
; CHECK: end:
998+
; CHECK-NEXT: ret void
999+
;
1000+
entry:
1001+
%val = load i32, ptr %x, align 4
1002+
%cmp = icmp eq i32 %val, 4
1003+
br i1 %cmp, label %if.eq, label %if.eq
1004+
1005+
if.eq:
1006+
store i32 4, ptr %x, align 4
1007+
br label %end
1008+
1009+
end:
1010+
ret void
1011+
}
1012+
1013+
; Dominating condition implies value already exists, optimize store
1014+
; Should not optimize since value being stored is different from cond check
1015+
define void @remove_tautological_store_wrong_value(ptr %x) {
1016+
; CHECK-LABEL: @remove_tautological_store_wrong_value(
1017+
; CHECK-NEXT: entry:
1018+
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[X:%.*]], align 4
1019+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[VAL]], 4
1020+
; CHECK-NEXT: br i1 [[CMP]], label [[IF_EQ:%.*]], label [[END:%.*]]
1021+
; CHECK: if.eq:
1022+
; CHECK-NEXT: store i32 5, ptr [[X]], align 4
1023+
; CHECK-NEXT: br label [[END]]
1024+
; CHECK: end:
1025+
; CHECK-NEXT: ret void
1026+
;
1027+
entry:
1028+
%val = load i32, ptr %x, align 4
1029+
%cmp = icmp eq i32 %val, 4
1030+
br i1 %cmp, label %if.eq, label %end
1031+
1032+
if.eq:
1033+
store i32 5, ptr %x, align 4
1034+
br label %end
1035+
1036+
end:
1037+
ret void
1038+
}
1039+
1040+
; Dominating condition implies value already exists, optimize store
1041+
; Should not optimize since there is a clobbering acc after load
1042+
define void @remove_tautological_store_clobber(ptr %x) {
1043+
; CHECK-LABEL: @remove_tautological_store_clobber(
1044+
; CHECK-NEXT: entry:
1045+
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[X:%.*]], align 4
1046+
; CHECK-NEXT: store i32 5, ptr [[X]], align 4
1047+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[VAL]], 4
1048+
; CHECK-NEXT: br i1 [[CMP]], label [[IF_EQ:%.*]], label [[END:%.*]]
1049+
; CHECK: if.eq:
1050+
; CHECK-NEXT: store i32 4, ptr [[X]], align 4
1051+
; CHECK-NEXT: br label [[END]]
1052+
; CHECK: end:
1053+
; CHECK-NEXT: ret void
1054+
;
1055+
entry:
1056+
%val = load i32, ptr %x, align 4
1057+
store i32 5, ptr %x, align 4
1058+
%cmp = icmp eq i32 %val, 4
1059+
br i1 %cmp, label %if.eq, label %end
1060+
1061+
if.eq:
1062+
store i32 4, ptr %x, align 4
1063+
br label %end
1064+
1065+
end:
1066+
ret void
1067+
}
1068+
1069+
; Dominating condition implies value already exists, optimize store
1070+
; Should not optimize since the condition does not dominate the store
1071+
define void @remove_tautological_store_no_dom(ptr %x) {
1072+
; CHECK-LABEL: @remove_tautological_store_no_dom(
1073+
; CHECK-NEXT: entry:
1074+
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[X:%.*]], align 4
1075+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[VAL]], 4
1076+
; CHECK-NEXT: br i1 [[CMP]], label [[IF_EQ:%.*]], label [[IF_ELSE:%.*]]
1077+
; CHECK: if.eq:
1078+
; CHECK-NEXT: br label [[END:%.*]]
1079+
; CHECK: if.else:
1080+
; CHECK-NEXT: br label [[END]]
1081+
; CHECK: end:
1082+
; CHECK-NEXT: store i32 4, ptr [[X]], align 4
1083+
; CHECK-NEXT: ret void
1084+
;
1085+
entry:
1086+
%val = load i32, ptr %x, align 4
1087+
store i32 5, ptr %x, align 4
1088+
%cmp = icmp eq i32 %val, 4
1089+
br i1 %cmp, label %if.eq, label %if.else
1090+
1091+
if.eq:
1092+
br label %end
1093+
1094+
if.else:
1095+
br label %end
1096+
1097+
end:
1098+
store i32 4, ptr %x, align 4
1099+
ret void
1100+
}
1101+
1102+
; Dominating condition implies value already exists, optimize store
1103+
; Should not optimize volatile stores
1104+
define void @remove_tautological_store_volatile(ptr %x) {
1105+
; CHECK-LABEL: @remove_tautological_store_volatile(
1106+
; CHECK-NEXT: entry:
1107+
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[X:%.*]], align 4
1108+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[VAL]], 4
1109+
; CHECK-NEXT: br i1 [[CMP]], label [[IF_EQ:%.*]], label [[END:%.*]]
1110+
; CHECK: if.eq:
1111+
; CHECK-NEXT: store volatile i32 4, ptr [[X]], align 4
1112+
; CHECK-NEXT: br label [[END]]
1113+
; CHECK: end:
1114+
; CHECK-NEXT: ret void
1115+
;
1116+
entry:
1117+
%val = load i32, ptr %x, align 4
1118+
%cmp = icmp eq i32 %val, 4
1119+
br i1 %cmp, label %if.eq, label %end
1120+
1121+
if.eq:
1122+
store volatile i32 4, ptr %x, align 4
1123+
br label %end
1124+
1125+
end:
1126+
ret void
1127+
}

0 commit comments

Comments
 (0)