[LLVM] [SeparateConstOffsetFromGEP] Fix sep-const-offset-from-gep invalid assumption (#183402)

`SeparateConstOffsetFromGEP` assumed the index of a GEP was non-negative
(and therefore previous sext/add could be reordered safely) if the GEP
was marked `inbounds`. This can only be assumed if the GEP is working
off of the base address for the object (counter example:
https://alive2.llvm.org/ce/z/FjGgWp).

This fix removes the general assumption of inbounds GEPs and replaces it
with new checks. The transform is valid when:

1. Value tracking shows the index is known non-negative.
2. The GEP is inbounds and the offset from the base ptr is 0.
3. The GEP is inbounds and the offset is within the threshold `(2^(N-1)
- C + 1) * stride`, where N is the bit width of the index, C is a
positive constant in the add, and stride is the type size of the GEP.
4. The GEP is inbounds and the object size is within the threshold
`(2^(N-1) - C + 1) * stride` for positive C or `(2^(N-1) + C) * stride`
for negative C.

Alive2 showing the constraints on the threshold:
https://alive2.llvm.org/ce/z/nBHM4m
This commit is contained in:
Meredith Julian 2026-03-11 12:14:32 -07:00 committed by GitHub
parent ef375ca232
commit cfbd53c981
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 328 additions and 59 deletions

View File

@ -227,16 +227,17 @@ private:
/// successful, returns C and update UserChain as a def-use chain from C to V;
/// otherwise, UserChain is empty.
///
/// \p V The given expression
/// \p SignExtended Whether V will be sign-extended in the computation of the
/// GEP index
/// \p ZeroExtended Whether V will be zero-extended in the computation of the
/// GEP index
/// \p NonNegative Whether V is guaranteed to be non-negative. For example,
/// an index of an inbounds GEP is guaranteed to be
/// non-negative. Levaraging this, we can better split
/// inbounds GEPs.
APInt find(Value *V, bool SignExtended, bool ZeroExtended, bool NonNegative);
/// \p V The given expression
/// \p GEP The base GEP instruction, used for determining relevant
/// types, flags, and non-negativity needed for safe
/// reassociation
/// \p Idx The original index of the GEP
/// \p SignExtended Whether V will be sign-extended in the computation of
/// the GEP index
/// \p ZeroExtended Whether V will be zero-extended in the computation of
/// the GEP index
APInt find(Value *V, GetElementPtrInst *GEP, Value *Idx, bool SignExtended,
bool ZeroExtended);
/// A helper function to look into both operands of a binary operator.
APInt findInEitherOperand(BinaryOperator *BO, bool SignExtended,
@ -290,10 +291,11 @@ private:
///
/// \p SignExtended Whether BO is surrounded by sext
/// \p ZeroExtended Whether BO is surrounded by zext
/// \p NonNegative Whether BO is known to be non-negative, e.g., an in-bound
/// array index.
bool CanTraceInto(bool SignExtended, bool ZeroExtended, BinaryOperator *BO,
bool NonNegative);
/// \p GEP The base GEP instruction, used for determining relevant
/// types and flags needed for safe reassociation.
/// \p Idx The original index of the GEP
bool canTraceInto(bool SignExtended, bool ZeroExtended, BinaryOperator *BO,
GetElementPtrInst *GEP, Value *Idx);
/// The path from the constant offset to the old GEP index. e.g., if the GEP
/// index is "a * b + (c + 5)". After running function find, UserChain[0] will
@ -474,10 +476,113 @@ FunctionPass *llvm::createSeparateConstOffsetFromGEPPass(bool LowerGEP) {
return new SeparateConstOffsetFromGEPLegacyPass(LowerGEP);
}
bool ConstantOffsetExtractor::CanTraceInto(bool SignExtended,
bool ZeroExtended,
BinaryOperator *BO,
bool NonNegative) {
// Checks if it is safe to reorder an add/sext result used in a GEP.
//
// An inbounds GEP does not guarantee that the index is non-negative.
// This helper checks first if the index is known non-negative. If the index is
// non-negative, the transform is always safe.
// Second, it checks whether the GEP is inbounds and directly based on a global
// or an alloca, which are required to prove futher transform validity.
// If the GEP:
// - Has a zero offset from the base, the index is non-negative (any negative
// value would produce poison/UB)
// - Has ObjectSize < (2^(N-1) - C + 1) * stride, where C is a constant from the
// add, stride is the element size of Idx, and N is bitwidth of Idx.
// This is because with this pattern:
// %add = add iN %val, C
// %sext = sext iN %add to i64
// %gep = getelementptr inbounds TYPE, %sext
// The worst-case is when %val sign-flips to produce the smallest magnitude
// negative value, at 2^(N-1)-1. In this case, the add/sext is -(2^(N-1)-C+1),
// and the sext/add is 2^(N-1)+C-1 (2^N difference). The original add/sext
// only produces a defined GEP when -(2^(N-1)-C+1) is inbounds. So, if
// ObjectSize < (2^(N-1) - C + 1) * stride, it is impossible for the
// worst-case sign-flip to be defined.
// Note that in this case the GEP is not neccesarily non-negative, but any
// negative results will still produce the same behavior in the reordered
// version with a defined GEP.
// This can also work for negative C, but the threshold is instead
// (2^(N-1)+C)*stride, since the sign-flip is done in reverse and is instead
// producing a large positive value that still needs to be inbounds to the
// object size. If C is negative, we cannot make any useful assumptions based
// on the offset, since it would need to be extremely large.
static bool canReorderAddSextToGEP(const GetElementPtrInst *GEP,
const Value *Idx, const BinaryOperator *Add,
const DataLayout &DL) {
if (isKnownNonNegative(Idx, DL))
return true;
if (!GEP->isInBounds())
return false;
const Value *Ptr = GEP->getPointerOperand();
int64_t Offset = 0;
const Value *Base =
GetPointerBaseWithConstantOffset(const_cast<Value *>(Ptr), Offset, DL);
// We need one of the operands to be a constant to be able to trace into the
// operator.
const ConstantInt *CI = dyn_cast<ConstantInt>(Add->getOperand(0));
if (!CI)
CI = dyn_cast<ConstantInt>(Add->getOperand(1));
if (!CI)
return false;
// Calculate the threshold
APInt Threshold;
unsigned N = Add->getType()->getIntegerBitWidth();
uint64_t Stride =
DL.getTypeAllocSize(GEP->getSourceElementType()).getFixedValue();
if (!CI->isNegative()) {
// (2^(N-1) - C + 1) * stride
Threshold = (APInt::getSignedMinValue(N).zext(128) -
CI->getValue().zextOrTrunc(128) + 1) *
APInt(128, Stride);
} else {
// (2^(N-1) + C) * stride
Threshold = (APInt::getSignedMinValue(N).zext(128) +
CI->getValue().zextOrTrunc(128)) *
APInt(128, Stride);
}
if (Base && (isa<AllocaInst>(Base) || isa<GlobalObject>(Base)) &&
!CI->isNegative()) {
// If the offset is zero from an alloca or global, inbounds is sufficient to
// prove non-negativity if one add operand is non-negative
if (Offset == 0)
return true;
// Check if the Offset < Threshold (positive CI only) otherwise
if (Offset < 0)
return true;
if (APInt(128, (uint64_t)Offset).ult(Threshold))
return true;
} else {
// If we can't determine the offset from the base object, we can still use
// the underlying object and type size constraints
Base = getUnderlyingObject(Ptr);
// Can only prove non-negativity if the base object is known
if (!(isa<AllocaInst>(Base) || isa<GlobalObject>(Base)))
return false;
}
// Check if the ObjectSize < Threshold (for both positive or negative C)
uint64_t ObjSize = 0;
if (const auto *AI = dyn_cast<AllocaInst>(Base)) {
if (auto AllocSize = AI->getAllocationSize(DL))
if (!AllocSize->isScalable())
ObjSize = AllocSize->getFixedValue();
} else if (const auto *GV = dyn_cast<GlobalVariable>(Base)) {
ObjSize = DL.getTypeAllocSize(GV->getValueType()).getFixedValue();
}
if (ObjSize > 0 && APInt(128, ObjSize).ult(Threshold))
return true;
return false;
}
bool ConstantOffsetExtractor::canTraceInto(bool SignExtended, bool ZeroExtended,
BinaryOperator *BO,
GetElementPtrInst *GEP, Value *Idx) {
// We only consider ADD, SUB and OR, because a non-zero constant found in
// expressions composed of these operations can be easily hoisted as a
// constant offset by reassociation.
@ -487,7 +592,6 @@ bool ConstantOffsetExtractor::CanTraceInto(bool SignExtended,
return false;
}
Value *LHS = BO->getOperand(0), *RHS = BO->getOperand(1);
// Do not trace into "or" unless it is equivalent to "add nuw nsw".
// This is the case if the or's disjoint flag is set.
if (BO->getOpcode() == Instruction::Or &&
@ -511,25 +615,27 @@ bool ConstantOffsetExtractor::CanTraceInto(bool SignExtended,
// 1 | 0 | sext(BO) == sext(A) op sext(B)
// 1 | 1 | zext(sext(BO)) ==
// | | zext(sext(A)) op zext(sext(B))
if (BO->getOpcode() == Instruction::Add && !ZeroExtended && NonNegative) {
if (BO->getOpcode() == Instruction::Add && !ZeroExtended && GEP) {
// If a + b >= 0 and (a >= 0 or b >= 0), then
// sext(a + b) = sext(a) + sext(b)
// even if the addition is not marked nsw.
//
// Leveraging this invariant, we can trace into an sext'ed inbound GEP
// index if the constant offset is non-negative.
// index under certain conditions (see canReorderAddSextToGEP).
//
// Verified in @sext_add in split-gep.ll.
if (ConstantInt *ConstLHS = dyn_cast<ConstantInt>(LHS)) {
if (!ConstLHS->isNegative())
return true;
}
if (ConstantInt *ConstRHS = dyn_cast<ConstantInt>(RHS)) {
if (!ConstRHS->isNegative())
return true;
}
if (canReorderAddSextToGEP(GEP, Idx, BO, DL))
return true;
}
// For a sext(add nuw), allow tracing through when the enclosing GEP is both
// inbounds and nuw.
bool GEPInboundsNUW =
GEP ? (GEP->isInBounds() && GEP->hasNoUnsignedWrap()) : false;
if (BO->getOpcode() == Instruction::Add && SignExtended && !ZeroExtended &&
GEPInboundsNUW && BO->hasNoUnsignedWrap())
return true;
// sext (add/sub nsw A, B) == add/sub nsw (sext A), (sext B)
// zext (add/sub nuw A, B) == add/sub nuw (zext A), (zext B)
if (BO->getOpcode() == Instruction::Add ||
@ -549,10 +655,9 @@ APInt ConstantOffsetExtractor::findInEitherOperand(BinaryOperator *BO,
// Save off the current height of the chain, in case we need to restore it.
size_t ChainLength = UserChain.size();
// BO being non-negative does not shed light on whether its operands are
// non-negative. Clear the NonNegative flag here.
APInt ConstantOffset = find(BO->getOperand(0), SignExtended, ZeroExtended,
/* NonNegative */ false);
// BO cannot use information from the base GEP at this point, so clear it.
APInt ConstantOffset =
find(BO->getOperand(0), nullptr, nullptr, SignExtended, ZeroExtended);
// If we found a constant offset in the left operand, stop and return that.
// This shortcut might cause us to miss opportunities of combining the
// constant offsets in both operands, e.g., (a + 4) + (b + 5) => (a + b) + 9.
@ -564,8 +669,8 @@ APInt ConstantOffsetExtractor::findInEitherOperand(BinaryOperator *BO,
// since visiting the LHS didn't pan out.
UserChain.resize(ChainLength);
ConstantOffset = find(BO->getOperand(1), SignExtended, ZeroExtended,
/* NonNegative */ false);
ConstantOffset =
find(BO->getOperand(1), nullptr, nullptr, SignExtended, ZeroExtended);
// If U is a sub operator, negate the constant offset found in the right
// operand.
if (BO->getOpcode() == Instruction::Sub)
@ -578,8 +683,9 @@ APInt ConstantOffsetExtractor::findInEitherOperand(BinaryOperator *BO,
return ConstantOffset;
}
APInt ConstantOffsetExtractor::find(Value *V, bool SignExtended,
bool ZeroExtended, bool NonNegative) {
APInt ConstantOffsetExtractor::find(Value *V, GetElementPtrInst *GEP,
Value *Idx, bool SignExtended,
bool ZeroExtended) {
// TODO(jingyue): We could trace into integer/pointer casts, such as
// inttoptr, ptrtoint, bitcast, and addrspacecast. We choose to handle only
// integers because it gives good enough results for our benchmarks.
@ -595,23 +701,22 @@ APInt ConstantOffsetExtractor::find(Value *V, bool SignExtended,
ConstantOffset = CI->getValue();
} else if (BinaryOperator *BO = dyn_cast<BinaryOperator>(V)) {
// Trace into subexpressions for more hoisting opportunities.
if (CanTraceInto(SignExtended, ZeroExtended, BO, NonNegative))
if (canTraceInto(SignExtended, ZeroExtended, BO, GEP, Idx))
ConstantOffset = findInEitherOperand(BO, SignExtended, ZeroExtended);
} else if (isa<TruncInst>(V)) {
ConstantOffset =
find(U->getOperand(0), SignExtended, ZeroExtended, NonNegative)
find(U->getOperand(0), GEP, Idx, SignExtended, ZeroExtended)
.trunc(BitWidth);
} else if (isa<SExtInst>(V)) {
ConstantOffset = find(U->getOperand(0), /* SignExtended */ true,
ZeroExtended, NonNegative).sext(BitWidth);
ConstantOffset =
find(U->getOperand(0), GEP, Idx, /* SignExtended */ true, ZeroExtended)
.sext(BitWidth);
} else if (isa<ZExtInst>(V)) {
// As an optimization, we can clear the SignExtended flag because
// sext(zext(a)) = zext(a). Verified in @sext_zext in split-gep.ll.
//
// Clear the NonNegative flag, because zext(a) >= 0 does not imply a >= 0.
ConstantOffset =
find(U->getOperand(0), /* SignExtended */ false,
/* ZeroExtended */ true, /* NonNegative */ false).zext(BitWidth);
ConstantOffset = find(U->getOperand(0), GEP, Idx, /* SignExtended */ false,
/* ZeroExtended */ true)
.zext(BitWidth);
}
// If we found a non-zero constant offset, add it to the path for
@ -780,9 +885,8 @@ Value *ConstantOffsetExtractor::Extract(Value *Idx, GetElementPtrInst *GEP,
bool &PreservesNUW) {
ConstantOffsetExtractor Extractor(GEP->getIterator());
// Find a non-zero constant offset first.
APInt ConstantOffset =
Extractor.find(Idx, /* SignExtended */ false, /* ZeroExtended */ false,
GEP->isInBounds());
APInt ConstantOffset = Extractor.find(Idx, GEP, Idx, /* SignExtended */ false,
/* ZeroExtended */ false);
if (ConstantOffset == 0) {
UserChainTail = nullptr;
PreservesNUW = true;
@ -798,10 +902,8 @@ Value *ConstantOffsetExtractor::Extract(Value *Idx, GetElementPtrInst *GEP,
}
APInt ConstantOffsetExtractor::Find(Value *Idx, GetElementPtrInst *GEP) {
// If Idx is an index of an inbound GEP, Idx is guaranteed to be non-negative.
return ConstantOffsetExtractor(GEP->getIterator())
.find(Idx, /* SignExtended */ false, /* ZeroExtended */ false,
GEP->isInBounds());
.find(Idx, GEP, Idx, /* SignExtended */ false, /* ZeroExtended */ false);
}
bool SeparateConstOffsetFromGEP::canonicalizeArrayIndicesToIndexSize(

View File

@ -10,6 +10,7 @@
@struct_array = global [1024 x %struct.S] zeroinitializer, align 16
@float_2d_array = global [32 x [32 x float]] zeroinitializer, align 4
@float_array = global [128 x float] zeroinitializer, align 4
; We should not extract any struct field indices, because fields in a struct
; may have different types.
@ -30,17 +31,16 @@ entry:
}
; We should be able to trace into sext(a + b) if a + b is non-negative
; (e.g., used as an index of an inbounds GEP) and one of a and b is
; non-negative.
; (e.g., used as an index of an inbounds GEP on a global base ptr) and one of a
; or b is non-negative.
define ptr @sext_add(i32 %i, i32 %j) {
; CHECK-LABEL: define ptr @sext_add(
; CHECK-SAME: i32 [[I:%.*]], i32 [[J:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[J]], -2
; CHECK-NEXT: [[TMP1:%.*]] = sext i32 [[TMP0]] to i64
; CHECK-NEXT: [[TMP2:%.*]] = sext i32 [[I]] to i64
; CHECK-NEXT: [[TMP3:%.*]] = getelementptr [32 x [32 x float]], ptr @float_2d_array, i64 0, i64 [[TMP2]], i64 [[TMP1]]
; CHECK-NEXT: [[P1:%.*]] = getelementptr i8, ptr [[TMP3]], i64 128
; CHECK-NEXT: [[TMP0:%.*]] = sext i32 [[I]] to i64
; CHECK-NEXT: [[TMP1:%.*]] = sext i32 [[J]] to i64
; CHECK-NEXT: [[TMP2:%.*]] = getelementptr [32 x [32 x float]], ptr @float_2d_array, i64 0, i64 [[TMP0]], i64 [[TMP1]]
; CHECK-NEXT: [[P1:%.*]] = getelementptr i8, ptr [[TMP2]], i64 120
; CHECK-NEXT: ret ptr [[P1]]
;
entry:
@ -48,11 +48,178 @@ entry:
%1 = sext i32 %0 to i64 ; inbound sext(i + 1) = sext(i) + 1
%2 = add i32 %j, -2
; However, inbound sext(j + -2) != sext(j) + -2, e.g., j = INT_MIN
; But j = INT_MIN would result in a very large positive result which would be
; OOB (and produce poison), so there is no counter example in this case
%3 = sext i32 %2 to i64
%p = getelementptr inbounds [32 x [32 x float]], ptr @float_2d_array, i64 0, i64 %1, i64 %3
ret ptr %p
}
; We should trace into sext(a + b) if a + b is an inbounds GEP on a known
; base ptr (alloca) if one of a or b is non-negative.
define ptr @sext_add_alloca(i32 %i) {
; CHECK-LABEL: define ptr @sext_add_alloca(
; CHECK-SAME: i32 [[I:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[ARR:%.*]] = alloca [32 x [32 x float]], align 4
; CHECK-NEXT: [[TMP0:%.*]] = sext i32 [[I]] to i64
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr [32 x [32 x float]], ptr [[ARR]], i64 0, i64 [[TMP0]], i64 0
; CHECK-NEXT: [[P1:%.*]] = getelementptr i8, ptr [[TMP1]], i64 128
; CHECK-NEXT: ret ptr [[P1]]
;
entry:
%arr = alloca [32 x [32 x float]], align 4
%0 = add i32 %i, 1
%1 = sext i32 %0 to i64
; inbound sext(i + 1) = sext(i) + 1 because inbounds on base ptr -> non-negative
%p = getelementptr inbounds [32 x [32 x float]], ptr %arr, i64 0, i64 %1, i64 0
ret ptr %p
}
; We cannot trace into sext(a + b) if a + b is an inbounds GEP but not on a
; known base ptr even if one of a or b is non-negative.
define ptr @sext_add_nonbase(i32 %i, ptr %unknown_arr) {
; CHECK-LABEL: define ptr @sext_add_nonbase(
; CHECK-SAME: i32 [[I:%.*]], ptr [[ARR:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[I]], 1
; CHECK-NEXT: [[TMP1:%.*]] = sext i32 [[TMP0]] to i64
; CHECK-NEXT: [[P1:%.*]] = getelementptr inbounds [32 x [32 x float]], ptr [[ARR]], i64 0, i64 [[TMP1]], i64 0
; CHECK-NEXT: ret ptr [[P1]]
;
entry:
%0 = add i32 %i, 1
%1 = sext i32 %0 to i64
; inbound sext(i + 1) != sext(i) + 1 because a wrapped result can still be inbounds if not at start of arr
%p = getelementptr inbounds [32 x [32 x float]], ptr %unknown_arr, i64 0, i64 %1, i64 0
ret ptr %p
}
; We can trace into sext(a + b) if a + b is an inbounds GEP and the known
; offset from a known base ptr is within a certain threshold relative to the
; bitwidth of the index (offset < (2^(n-1) - C + 1) * bitwidth).
define ptr @sext_add_nonzerooffset_inrange(i8 %i, i64 %size) {
; CHECK-LABEL: define ptr @sext_add_nonzerooffset_inrange(
; CHECK-SAME: i8 [[I:%.*]], i64 [[SIZE:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[ARR:%.*]] = alloca float, i64 %size, align 4
; CHECK-NEXT: [[OFFSETARR:%.*]] = getelementptr float, ptr [[ARR]], i64 127
; CHECK-NEXT: [[TMP0:%.*]] = sext i8 [[I]] to i64
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr float, ptr [[OFFSETARR]], i64 [[TMP0]]
; CHECK-NEXT: [[P2:%.*]] = getelementptr i8, ptr [[TMP1]], i64 4
; CHECK-NEXT: ret ptr [[P2]]
;
entry:
%arr = alloca float, i64 %size, align 4
%offsetarr = getelementptr float, ptr %arr, i64 127
%add = add i8 %i, 1
%sext = sext i8 %add to i64
%p = getelementptr inbounds float, ptr %offsetarr, i64 %sext
ret ptr %p
}
define ptr @sext_add_nonzerooffset_outofrange(i8 %i, i64 %size) {
; CHECK-LABEL: define ptr @sext_add_nonzerooffset_outofrange(
; CHECK-SAME: i8 [[I:%.*]], i64 [[SIZE:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[ARR:%.*]] = alloca float, i64 [[SIZE]], align 4
; CHECK-NEXT: [[ADD:%.*]] = add i8 [[I]], 1
; CHECK-NEXT: [[SEXT:%.*]] = sext i8 [[ADD]] to i64
; CHECK-NEXT: [[TMP0:%.*]] = getelementptr float, ptr [[ARR]], i64 [[SEXT]]
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr float, ptr [[TMP0]], i64 128
; CHECK-NEXT: ret ptr [[TMP1]]
;
entry:
%arr = alloca float, i64 %size, align 4
%offsetarr = getelementptr float, ptr %arr, i64 128
%add = add i8 %i, 1
%sext = sext i8 %add to i64
%p = getelementptr inbounds float, ptr %offsetarr, i64 %sext
ret ptr %p
}
; We can trace into sext(a + b) if a + b is an inbounds GEP and the size of the
; known base ptr is within a certain threshold relative to the bitwidth of the
; index (offset < (2^(n-1) - C + 1) * bitwidth).
define ptr @sext_add_unknownoffset_inrange(i8 %i, i64 %off) {
; CHECK-LABEL: define ptr @sext_add_unknownoffset_inrange(
; CHECK-SAME: i8 [[I:%.*]], i64 [[OFF:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[ARR:%.*]] = alloca float, i64 126, align 4
; CHECK-NEXT: [[OFFSETARR:%.*]] = getelementptr float, ptr [[ARR]], i64 [[OFF]]
; CHECK-NEXT: [[TMP0:%.*]] = sext i8 [[I]] to i64
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr float, ptr [[OFFSETARR]], i64 [[TMP0]]
; CHECK-NEXT: [[P2:%.*]] = getelementptr i8, ptr [[TMP1]], i64 8
; CHECK-NEXT: ret ptr [[P2]]
;
entry:
%arr = alloca float, i64 126, align 4
%offsetarr = getelementptr float, ptr %arr, i64 %off
%add = add i8 %i, 2
%sext = sext i8 %add to i64
%p = getelementptr inbounds float, ptr %offsetarr, i64 %sext
ret ptr %p
}
define ptr @sext_add_unknownoffset_inrange_neg(i8 %i, i64 %off) {
; CHECK-LABEL: define ptr @sext_add_unknownoffset_inrange_neg(
; CHECK-SAME: i8 [[I:%.*]], i64 [[OFF:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[ARR:%.*]] = alloca float, i64 125, align 4
; CHECK-NEXT: [[OFFSETARR:%.*]] = getelementptr float, ptr [[ARR]], i64 [[OFF]]
; CHECK-NEXT: [[TMP0:%.*]] = sext i8 [[I]] to i64
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr float, ptr [[OFFSETARR]], i64 [[TMP0]]
; CHECK-NEXT: [[P2:%.*]] = getelementptr i8, ptr [[TMP1]], i64 -8
; CHECK-NEXT: ret ptr [[P2]]
;
entry:
%arr = alloca float, i64 125, align 4
%offsetarr = getelementptr float, ptr %arr, i64 %off
%add = add i8 %i, -2
%sext = sext i8 %add to i64
%p = getelementptr inbounds float, ptr %offsetarr, i64 %sext
ret ptr %p
}
define ptr @sext_add_unknownoffset_outofrange(i8 %i, i64 %off) {
; CHECK-LABEL: define ptr @sext_add_unknownoffset_outofrange(
; CHECK-SAME: i8 [[I:%.*]], i64 [[OFF:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[ARR:%.*]] = alloca float, i64 127, align 4
; CHECK-NEXT: [[OFFSETARR:%.*]] = getelementptr float, ptr [[ARR]], i64 [[OFF]]
; CHECK-NEXT: [[ADD:%.*]] = add i8 [[I]], 2
; CHECK-NEXT: [[SEXT:%.*]] = sext i8 [[ADD]] to i64
; CHECK-NEXT: [[P:%.*]] = getelementptr inbounds float, ptr [[OFFSETARR]], i64 [[SEXT]]
; CHECK-NEXT: ret ptr [[P]]
;
entry:
%arr = alloca float, i64 127, align 4
%offsetarr = getelementptr float, ptr %arr, i64 %off
%add = add i8 %i, 2
%sext = sext i8 %add to i64
%p = getelementptr inbounds float, ptr %offsetarr, i64 %sext
ret ptr %p
}
; We can trace into sext(a + b) if a + b is non-negative (nsw flag) and one of
; a or b is non-negative, even if the gep is not inbounds
define ptr @sext_add_nsw(i32 %i, ptr %unknown_arr) {
; CHECK-LABEL: define ptr @sext_add_nsw(
; CHECK-SAME: i32 [[I:%.*]], ptr [[ARR:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = sext i32 [[I]] to i64
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr [32 x [32 x float]], ptr [[ARR]], i64 0, i64 [[TMP0]], i64 0
; CHECK-NEXT: [[P1:%.*]] = getelementptr i8, ptr [[TMP1]], i64 128
; CHECK-NEXT: ret ptr [[P1]]
;
entry:
%0 = add nsw i32 %i, 1
%1 = sext i32 %0 to i64
; sext(nsw i + 1) = sext(i) + 1
%p = getelementptr [32 x [32 x float]], ptr %unknown_arr, i64 0, i64 %1, i64 0
ret ptr %p
}
; We should be able to trace into sext/zext if it can be distributed to both
; operands, e.g., sext (add nsw a, b) == add nsw (sext a), (sext b)
;