Skip to content

Commit 720a911

Browse files
authored
[SeparateConstOffsetFromGEP] Preserve inbounds flag based on ValueTracking and NUW (#130617)
If we know that the initial GEP was inbounds, and we change it to a sequence of GEPs from the same base pointer where every offset is non-negative, then the new GEPs are inbounds. We can also preserve inbounds if the inbounds GEP and the involved additions are NUW. For SWDEV-516125.
1 parent c93af22 commit 720a911

File tree

4 files changed

+607
-22
lines changed

4 files changed

+607
-22
lines changed

llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,10 @@ class ConstantOffsetExtractor {
235235
/// \p GEP The given GEP
236236
/// \p UserChainTail Outputs the tail of UserChain so that we can
237237
/// garbage-collect unused instructions in UserChain.
238+
/// \p PreservesNUW Outputs whether the extraction allows preserving the
239+
/// GEP's nuw flag, if it has one.
238240
static Value *Extract(Value *Idx, GetElementPtrInst *GEP,
239-
User *&UserChainTail);
241+
User *&UserChainTail, bool &PreservesNUW);
240242

241243
/// Looks for a constant offset from the given GEP index without extracting
242244
/// it. It returns the numeric value of the extracted constant offset (0 if
@@ -778,17 +780,45 @@ Value *ConstantOffsetExtractor::removeConstOffset(unsigned ChainIndex) {
778780
return NewBO;
779781
}
780782

783+
/// A helper function to check if reassociating through an entry in the user
784+
/// chain would invalidate the GEP's nuw flag.
785+
static bool allowsPreservingNUW(const User *U) {
786+
if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(U)) {
787+
// Binary operations need to be effectively add nuw.
788+
auto Opcode = BO->getOpcode();
789+
if (Opcode == BinaryOperator::Or) {
790+
// Ors are only considered here if they are disjoint. The addition that
791+
// they represent in this case is NUW.
792+
assert(cast<PossiblyDisjointInst>(BO)->isDisjoint());
793+
return true;
794+
}
795+
return Opcode == BinaryOperator::Add && BO->hasNoUnsignedWrap();
796+
}
797+
// UserChain can only contain ConstantInt, CastInst, or BinaryOperator.
798+
// Among the possible CastInsts, only trunc without nuw is a problem: If it
799+
// is distributed through an add nuw, wrapping may occur:
800+
// "add nuw trunc(a), trunc(b)" is more poisonous than "trunc(add nuw a, b)"
801+
if (const TruncInst *TI = dyn_cast<TruncInst>(U))
802+
return TI->hasNoUnsignedWrap();
803+
return isa<CastInst>(U) || isa<ConstantInt>(U);
804+
}
805+
781806
Value *ConstantOffsetExtractor::Extract(Value *Idx, GetElementPtrInst *GEP,
782-
User *&UserChainTail) {
807+
User *&UserChainTail,
808+
bool &PreservesNUW) {
783809
ConstantOffsetExtractor Extractor(GEP->getIterator());
784810
// Find a non-zero constant offset first.
785811
APInt ConstantOffset =
786812
Extractor.find(Idx, /* SignExtended */ false, /* ZeroExtended */ false,
787813
GEP->isInBounds());
788814
if (ConstantOffset == 0) {
789815
UserChainTail = nullptr;
816+
PreservesNUW = true;
790817
return nullptr;
791818
}
819+
820+
PreservesNUW = all_of(Extractor.UserChain, allowsPreservingNUW);
821+
792822
// Separates the constant offset from the GEP index.
793823
Value *IdxWithoutConstOffset = Extractor.rebuildWithoutConstOffset();
794824
UserChainTail = Extractor.UserChain.back();
@@ -1052,6 +1082,10 @@ bool SeparateConstOffsetFromGEP::splitGEP(GetElementPtrInst *GEP) {
10521082
}
10531083
}
10541084

1085+
// Track information for preserving GEP flags.
1086+
bool AllOffsetsNonNegative = AccumulativeByteOffset >= 0;
1087+
bool AllNUWPreserved = true;
1088+
10551089
// Remove the constant offset in each sequential index. The resultant GEP
10561090
// computes the variadic base.
10571091
// Notice that we don't remove struct field indices here. If LowerGEP is
@@ -1070,15 +1104,19 @@ bool SeparateConstOffsetFromGEP::splitGEP(GetElementPtrInst *GEP) {
10701104
// uses the variadic part as the new index.
10711105
Value *OldIdx = GEP->getOperand(I);
10721106
User *UserChainTail;
1073-
Value *NewIdx =
1074-
ConstantOffsetExtractor::Extract(OldIdx, GEP, UserChainTail);
1107+
bool PreservesNUW;
1108+
Value *NewIdx = ConstantOffsetExtractor::Extract(
1109+
OldIdx, GEP, UserChainTail, PreservesNUW);
10751110
if (NewIdx != nullptr) {
10761111
// Switches to the index with the constant offset removed.
10771112
GEP->setOperand(I, NewIdx);
10781113
// After switching to the new index, we can garbage-collect UserChain
10791114
// and the old index if they are not used.
10801115
RecursivelyDeleteTriviallyDeadInstructions(UserChainTail);
10811116
RecursivelyDeleteTriviallyDeadInstructions(OldIdx);
1117+
AllOffsetsNonNegative =
1118+
AllOffsetsNonNegative && isKnownNonNegative(NewIdx, *DL);
1119+
AllNUWPreserved &= PreservesNUW;
10821120
}
10831121
}
10841122
}
@@ -1099,12 +1137,35 @@ bool SeparateConstOffsetFromGEP::splitGEP(GetElementPtrInst *GEP) {
10991137
// inbounds keyword is not present, the offsets are added to the base
11001138
// address with silently-wrapping two's complement arithmetic".
11011139
// Therefore, the final code will be a semantically equivalent.
1102-
//
1103-
// TODO(jingyue): do some range analysis to keep as many inbounds as
1104-
// possible. GEPs with inbounds are more friendly to alias analysis.
1105-
// TODO(gep_nowrap): Preserve nuw at least.
11061140
GEPNoWrapFlags NewGEPFlags = GEPNoWrapFlags::none();
1107-
GEP->setNoWrapFlags(GEPNoWrapFlags::none());
1141+
1142+
// If the initial GEP was inbounds/nusw and all variable indices and the
1143+
// accumulated offsets are non-negative, they can be added in any order and
1144+
// the intermediate results are in bounds and don't overflow in a nusw sense.
1145+
// So, we can preserve the inbounds/nusw flag for both GEPs.
1146+
bool CanPreserveInBoundsNUSW = AllOffsetsNonNegative;
1147+
1148+
// If the initial GEP was NUW and all operations that we reassociate were NUW
1149+
// additions, the resulting GEPs are also NUW.
1150+
if (GEP->hasNoUnsignedWrap() && AllNUWPreserved) {
1151+
NewGEPFlags |= GEPNoWrapFlags::noUnsignedWrap();
1152+
// If the initial GEP additionally had NUSW (or inbounds, which implies
1153+
// NUSW), we know that the indices in the initial GEP must all have their
1154+
// signbit not set. For indices that are the result of NUW adds, the
1155+
// add-operands therefore also don't have their signbit set. Therefore, all
1156+
// indices of the resulting GEPs are non-negative -> we can preserve
1157+
// the inbounds/nusw flag.
1158+
CanPreserveInBoundsNUSW |= GEP->hasNoUnsignedSignedWrap();
1159+
}
1160+
1161+
if (CanPreserveInBoundsNUSW) {
1162+
if (GEP->isInBounds())
1163+
NewGEPFlags |= GEPNoWrapFlags::inBounds();
1164+
else if (GEP->hasNoUnsignedSignedWrap())
1165+
NewGEPFlags |= GEPNoWrapFlags::noUnsignedSignedWrap();
1166+
}
1167+
1168+
GEP->setNoWrapFlags(NewGEPFlags);
11081169

11091170
// Lowers a GEP to either GEPs with a single index or arithmetic operations.
11101171
if (LowerGEP) {

0 commit comments

Comments
 (0)