Skip to content
Merged
79 changes: 70 additions & 9 deletions llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,10 @@ class ConstantOffsetExtractor {
/// \p GEP The given GEP
/// \p UserChainTail Outputs the tail of UserChain so that we can
/// garbage-collect unused instructions in UserChain.
/// \p PreservesNUW Outputs whether the extraction allows preserving the
/// GEP's nuw flag, if it has one.
static Value *Extract(Value *Idx, GetElementPtrInst *GEP,
User *&UserChainTail);
User *&UserChainTail, bool &PreservesNUW);

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

/// A helper function to check if reassociating through an entry in the user
/// chain would invalidate the GEP's nuw flag.
static bool allowsPreservingNUW(const User *U) {
if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(U)) {
// Binary operations need to be effectively add nuw.
auto Opcode = BO->getOpcode();
if (Opcode == BinaryOperator::Or) {
// Ors are only considered here if they are disjoint. The addition that
// they represent in this case is NUW.
assert(cast<PossiblyDisjointInst>(BO)->isDisjoint());
return true;
}
return Opcode == BinaryOperator::Add && BO->hasNoUnsignedWrap();
}
// UserChain can only contain ConstantInt, CastInst, or BinaryOperator.
// Among the possible CastInsts, only trunc without nuw is a problem: If it
// is distributed through an add nuw, wrapping may occur:
// "add nuw trunc(a), trunc(b)" is more poisonous than "trunc(add nuw a, b)"
if (const TruncInst *TI = dyn_cast<TruncInst>(U))
return TI->hasNoUnsignedWrap();
return isa<CastInst>(U) || isa<ConstantInt>(U);
}

Value *ConstantOffsetExtractor::Extract(Value *Idx, GetElementPtrInst *GEP,
User *&UserChainTail) {
User *&UserChainTail,
bool &PreservesNUW) {
ConstantOffsetExtractor Extractor(GEP->getIterator());
// Find a non-zero constant offset first.
APInt ConstantOffset =
Extractor.find(Idx, /* SignExtended */ false, /* ZeroExtended */ false,
GEP->isInBounds());
if (ConstantOffset == 0) {
UserChainTail = nullptr;
PreservesNUW = true;
return nullptr;
}

PreservesNUW = all_of(Extractor.UserChain, allowsPreservingNUW);

// Separates the constant offset from the GEP index.
Value *IdxWithoutConstOffset = Extractor.rebuildWithoutConstOffset();
UserChainTail = Extractor.UserChain.back();
Expand Down Expand Up @@ -1052,6 +1082,10 @@ bool SeparateConstOffsetFromGEP::splitGEP(GetElementPtrInst *GEP) {
}
}

// Track information for preserving GEP flags.
bool AllOffsetsNonNegative = AccumulativeByteOffset >= 0;
bool AllNUWPreserved = true;

// Remove the constant offset in each sequential index. The resultant GEP
// computes the variadic base.
// Notice that we don't remove struct field indices here. If LowerGEP is
Expand All @@ -1070,15 +1104,19 @@ bool SeparateConstOffsetFromGEP::splitGEP(GetElementPtrInst *GEP) {
// uses the variadic part as the new index.
Value *OldIdx = GEP->getOperand(I);
User *UserChainTail;
Value *NewIdx =
ConstantOffsetExtractor::Extract(OldIdx, GEP, UserChainTail);
bool PreservesNUW;
Value *NewIdx = ConstantOffsetExtractor::Extract(
OldIdx, GEP, UserChainTail, PreservesNUW);
if (NewIdx != nullptr) {
// Switches to the index with the constant offset removed.
GEP->setOperand(I, NewIdx);
// After switching to the new index, we can garbage-collect UserChain
// and the old index if they are not used.
RecursivelyDeleteTriviallyDeadInstructions(UserChainTail);
RecursivelyDeleteTriviallyDeadInstructions(OldIdx);
AllOffsetsNonNegative =
AllOffsetsNonNegative && isKnownNonNegative(NewIdx, *DL);
AllNUWPreserved &= PreservesNUW;
}
}
}
Expand All @@ -1099,12 +1137,35 @@ bool SeparateConstOffsetFromGEP::splitGEP(GetElementPtrInst *GEP) {
// inbounds keyword is not present, the offsets are added to the base
// address with silently-wrapping two's complement arithmetic".
// Therefore, the final code will be a semantically equivalent.
//
// TODO(jingyue): do some range analysis to keep as many inbounds as
// possible. GEPs with inbounds are more friendly to alias analysis.
// TODO(gep_nowrap): Preserve nuw at least.
GEPNoWrapFlags NewGEPFlags = GEPNoWrapFlags::none();
GEP->setNoWrapFlags(GEPNoWrapFlags::none());

// If the initial GEP was inbounds/nusw and all variable indices and the
// accumulated offsets are non-negative, they can be added in any order and
// the intermediate results are in bounds and don't overflow in a nusw sense.
// So, we can preserve the inbounds/nusw flag for both GEPs.
bool CanPreserveInBoundsNUSW = AllOffsetsNonNegative;

// If the initial GEP was NUW and all operations that we reassociate were NUW
// additions, the resulting GEPs are also NUW.
if (GEP->hasNoUnsignedWrap() && AllNUWPreserved) {
NewGEPFlags |= GEPNoWrapFlags::noUnsignedWrap();
// If the initial GEP additionally had NUSW (or inbounds, which implies
// NUSW), we know that the indices in the initial GEP must all have their
// signbit not set. For indices that are the result of NUW adds, the
// add-operands therefore also don't have their signbit set. Therefore, all
// indices of the resulting GEPs are non-negative -> we can preserve
// the inbounds/nusw flag.
CanPreserveInBoundsNUSW |= GEP->hasNoUnsignedSignedWrap();
}

if (CanPreserveInBoundsNUSW) {
if (GEP->isInBounds())
NewGEPFlags |= GEPNoWrapFlags::inBounds();
else if (GEP->hasNoUnsignedSignedWrap())
NewGEPFlags |= GEPNoWrapFlags::noUnsignedSignedWrap();
}

GEP->setNoWrapFlags(NewGEPFlags);

// Lowers a GEP to either GEPs with a single index or arithmetic operations.
if (LowerGEP) {
Expand Down
Loading
Loading