55 changes: 32 additions & 23 deletions llvm/lib/IR/ConstantFold.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,10 @@ Constant *llvm::ConstantFoldCastInstruction(unsigned opc, Constant *V,
} else if (CE->getOpcode() == Instruction::GetElementPtr &&
// Do not fold addrspacecast (gep 0, .., 0). It might make the
// addrspacecast uncanonicalized.
opc != Instruction::AddrSpaceCast) {
opc != Instruction::AddrSpaceCast &&
// Do not fold bitcast (gep) with inrange index, as this loses
// information.
!cast<GEPOperator>(CE)->getInRangeIndex().hasValue()) {
// If all of the indexes in the GEP are null values, there is no pointer
// adjustment going on. We might as well cast the source pointer.
bool isAllNull = true;
Expand Down Expand Up @@ -2046,10 +2049,10 @@ static bool isIndexInRangeOfSequentialType(SequentialType *STy,
return true;
}

template<typename IndexTy>
static Constant *ConstantFoldGetElementPtrImpl(Type *PointeeTy, Constant *C,
bool inBounds,
ArrayRef<IndexTy> Idxs) {
Constant *llvm::ConstantFoldGetElementPtr(Type *PointeeTy, Constant *C,
bool InBounds,
Optional<unsigned> InRangeIndex,
ArrayRef<Value *> Idxs) {
if (Idxs.empty()) return C;
Constant *Idx0 = cast<Constant>(Idxs[0]);
if ((Idxs.size() == 1 && Idx0->isNullValue()))
Expand Down Expand Up @@ -2146,9 +2149,18 @@ static Constant *ConstantFoldGetElementPtrImpl(Type *PointeeTy, Constant *C,

NewIndices.push_back(Combined);
NewIndices.append(Idxs.begin() + 1, Idxs.end());

// The combined GEP normally inherits its index inrange attribute from
// the inner GEP, but if the inner GEP's last index was adjusted by the
// outer GEP, any inbounds attribute on that index is invalidated.
Optional<unsigned> IRIndex = cast<GEPOperator>(CE)->getInRangeIndex();
if (IRIndex && *IRIndex == CE->getNumOperands() - 2 && !Idx0->isNullValue())
IRIndex = None;

return ConstantExpr::getGetElementPtr(
cast<GEPOperator>(CE)->getSourceElementType(), CE->getOperand(0),
NewIndices, inBounds && cast<GEPOperator>(CE)->isInBounds());
NewIndices, InBounds && cast<GEPOperator>(CE)->isInBounds(),
IRIndex);
}
}

Expand All @@ -2173,8 +2185,9 @@ static Constant *ConstantFoldGetElementPtrImpl(Type *PointeeTy, Constant *C,
if (SrcArrayTy && DstArrayTy
&& SrcArrayTy->getElementType() == DstArrayTy->getElementType()
&& SrcPtrTy->getAddressSpace() == DstPtrTy->getAddressSpace())
return ConstantExpr::getGetElementPtr(
SrcArrayTy, (Constant *)CE->getOperand(0), Idxs, inBounds);
return ConstantExpr::getGetElementPtr(SrcArrayTy,
(Constant *)CE->getOperand(0),
Idxs, InBounds, InRangeIndex);
}
}
}
Expand All @@ -2194,6 +2207,12 @@ static Constant *ConstantFoldGetElementPtrImpl(Type *PointeeTy, Constant *C,
Unknown = true;
continue;
}
if (InRangeIndex && i == *InRangeIndex + 1) {
// If an index is marked inrange, we cannot apply this canonicalization to
// the following index, as that will cause the inrange index to point to
// the wrong element.
continue;
}
if (isa<StructType>(Ty)) {
// The verify makes sure that GEPs into a struct are in range.
continue;
Expand Down Expand Up @@ -2256,27 +2275,17 @@ static Constant *ConstantFoldGetElementPtrImpl(Type *PointeeTy, Constant *C,
if (!NewIdxs.empty()) {
for (unsigned i = 0, e = Idxs.size(); i != e; ++i)
if (!NewIdxs[i]) NewIdxs[i] = cast<Constant>(Idxs[i]);
return ConstantExpr::getGetElementPtr(PointeeTy, C, NewIdxs, inBounds);
return ConstantExpr::getGetElementPtr(PointeeTy, C, NewIdxs, InBounds,
InRangeIndex);
}

// If all indices are known integers and normalized, we can do a simple
// check for the "inbounds" property.
if (!Unknown && !inBounds)
if (!Unknown && !InBounds)
if (auto *GV = dyn_cast<GlobalVariable>(C))
if (!GV->hasExternalWeakLinkage() && isInBoundsIndices(Idxs))
return ConstantExpr::getInBoundsGetElementPtr(PointeeTy, C, Idxs);
return ConstantExpr::getGetElementPtr(PointeeTy, C, Idxs,
/*InBounds=*/true, InRangeIndex);

return nullptr;
}

Constant *llvm::ConstantFoldGetElementPtr(Type *Ty, Constant *C,
bool inBounds,
ArrayRef<Constant *> Idxs) {
return ConstantFoldGetElementPtrImpl(Ty, C, inBounds, Idxs);
}

Constant *llvm::ConstantFoldGetElementPtr(Type *Ty, Constant *C,
bool inBounds,
ArrayRef<Value *> Idxs) {
return ConstantFoldGetElementPtrImpl(Ty, C, inBounds, Idxs);
}
7 changes: 4 additions & 3 deletions llvm/lib/IR/ConstantFold.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#ifndef LLVM_LIB_IR_CONSTANTFOLD_H
#define LLVM_LIB_IR_CONSTANTFOLD_H

#include "llvm/ADT/Optional.h"

namespace llvm {
template <typename T> class ArrayRef;
class Value;
Expand Down Expand Up @@ -46,9 +48,8 @@ template <typename T> class ArrayRef;
Constant *V2);
Constant *ConstantFoldCompareInstruction(unsigned short predicate,
Constant *C1, Constant *C2);
Constant *ConstantFoldGetElementPtr(Type *Ty, Constant *C, bool inBounds,
ArrayRef<Constant *> Idxs);
Constant *ConstantFoldGetElementPtr(Type *Ty, Constant *C, bool inBounds,
Constant *ConstantFoldGetElementPtr(Type *Ty, Constant *C, bool InBounds,
Optional<unsigned> InRangeIndex,
ArrayRef<Value *> Idxs);
} // End llvm namespace

Expand Down
13 changes: 9 additions & 4 deletions llvm/lib/IR/Constants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1167,7 +1167,7 @@ Constant *ConstantExpr::getWithOperands(ArrayRef<Constant *> Ops, Type *Ty,
assert(SrcTy || (Ops[0]->getType() == getOperand(0)->getType()));
return ConstantExpr::getGetElementPtr(
SrcTy ? SrcTy : GEPO->getSourceElementType(), Ops[0], Ops.slice(1),
GEPO->isInBounds(), OnlyIfReducedTy);
GEPO->isInBounds(), GEPO->getInRangeIndex(), OnlyIfReducedTy);
}
case Instruction::ICmp:
case Instruction::FCmp:
Expand Down Expand Up @@ -1893,6 +1893,7 @@ Constant *ConstantExpr::getSelect(Constant *C, Constant *V1, Constant *V2,

Constant *ConstantExpr::getGetElementPtr(Type *Ty, Constant *C,
ArrayRef<Value *> Idxs, bool InBounds,
Optional<unsigned> InRangeIndex,
Type *OnlyIfReducedTy) {
if (!Ty)
Ty = cast<PointerType>(C->getType()->getScalarType())->getElementType();
Expand All @@ -1901,7 +1902,8 @@ Constant *ConstantExpr::getGetElementPtr(Type *Ty, Constant *C,
Ty ==
cast<PointerType>(C->getType()->getScalarType())->getContainedType(0u));

if (Constant *FC = ConstantFoldGetElementPtr(Ty, C, InBounds, Idxs))
if (Constant *FC =
ConstantFoldGetElementPtr(Ty, C, InBounds, InRangeIndex, Idxs))
return FC; // Fold a few common cases.

// Get the result type of the getelementptr!
Expand Down Expand Up @@ -1937,9 +1939,12 @@ Constant *ConstantExpr::getGetElementPtr(Type *Ty, Constant *C,
Idx = ConstantVector::getSplat(NumVecElts, Idx);
ArgVec.push_back(Idx);
}

unsigned SubClassOptionalData = InBounds ? GEPOperator::IsInBounds : 0;
if (InRangeIndex && *InRangeIndex < 63)
SubClassOptionalData |= (*InRangeIndex + 1) << 1;
const ConstantExprKeyType Key(Instruction::GetElementPtr, ArgVec, 0,
InBounds ? GEPOperator::IsInBounds : 0, None,
Ty);
SubClassOptionalData, None, Ty);

LLVMContextImpl *pImpl = C->getContext().pImpl;
return pImpl->ExprConstants.getOrCreate(ReqTy, Key);
Expand Down
30 changes: 30 additions & 0 deletions llvm/test/Analysis/ConstantFolding/gep.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
; RUN: opt -instcombine -S -o - %s | FileCheck %s
; Tests that we preserve the inrange attribute on indices where possible.

target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

%struct.A = type { i32 (...)** }

@vt = external global [3 x i8*]

; CHECK: define i32 (...)* @f0()
define i32 (...)* @f0() {
; CHECK-NEXT: load i32 (...)*, i32 (...)** bitcast (i8** getelementptr inbounds ([3 x i8*], [3 x i8*]* @vt, inrange i64 0, i64 2) to i32 (...)**)
%load = load i32 (...)*, i32 (...)** getelementptr (i32 (...)*, i32 (...)** bitcast (i8** getelementptr inbounds ([3 x i8*], [3 x i8*]* @vt, inrange i64 0, i64 1) to i32 (...)**), i64 1)
ret i32 (...)* %load
}

; CHECK: define i32 (...)* @f1()
define i32 (...)* @f1() {
; CHECK-NEXT: load i32 (...)*, i32 (...)** bitcast (i8** getelementptr inbounds ([3 x i8*], [3 x i8*]* @vt, i64 0, i64 2) to i32 (...)**)
%load = load i32 (...)*, i32 (...)** getelementptr (i32 (...)*, i32 (...)** bitcast (i8** getelementptr inbounds ([3 x i8*], [3 x i8*]* @vt, i64 0, inrange i64 1) to i32 (...)**), i64 1)
ret i32 (...)* %load
}

; CHECK: define i32 (...)* @f2()
define i32 (...)* @f2() {
; CHECK-NEXT: load i32 (...)*, i32 (...)** bitcast (i8** getelementptr ([3 x i8*], [3 x i8*]* @vt, i64 1, i64 1) to i32 (...)**)
%load = load i32 (...)*, i32 (...)** getelementptr (i32 (...)*, i32 (...)** bitcast (i8** getelementptr inbounds ([3 x i8*], [3 x i8*]* @vt, i64 0, inrange i64 1) to i32 (...)**), i64 3)
ret i32 (...)* %load
}
19 changes: 19 additions & 0 deletions llvm/test/Assembler/getelementptr.ll
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,25 @@
@PR23753_b = global i8* getelementptr (i8, i8* @PR23753_a, i64 ptrtoint (i8* @PR23753_a to i64))
; CHECK: @PR23753_b = global i8* getelementptr (i8, i8* @PR23753_a, i64 ptrtoint (i8* @PR23753_a to i64))

; Verify that inrange on an index inhibits over-indexed getelementptr folding.

@nestedarray = global [2 x [4 x i8*]] zeroinitializer

; CHECK: @nestedarray.1 = alias i8*, getelementptr inbounds ([2 x [4 x i8*]], [2 x [4 x i8*]]* @nestedarray, inrange i32 0, i64 1, i32 0)
@nestedarray.1 = alias i8*, getelementptr inbounds ([2 x [4 x i8*]], [2 x [4 x i8*]]* @nestedarray, inrange i32 0, i32 0, i32 4)

; CHECK: @nestedarray.2 = alias i8*, getelementptr inbounds ([2 x [4 x i8*]], [2 x [4 x i8*]]* @nestedarray, i32 0, inrange i32 0, i32 4)
@nestedarray.2 = alias i8*, getelementptr inbounds ([2 x [4 x i8*]], [2 x [4 x i8*]]* @nestedarray, i32 0, inrange i32 0, i32 4)

; CHECK: @nestedarray.3 = alias i8*, getelementptr inbounds ([2 x [4 x i8*]], [2 x [4 x i8*]]* @nestedarray, i32 0, inrange i32 0, i32 0)
@nestedarray.3 = alias i8*, getelementptr inbounds ([4 x i8*], [4 x i8*]* getelementptr inbounds ([2 x [4 x i8*]], [2 x [4 x i8*]]* @nestedarray, i32 0, inrange i32 0), i32 0, i32 0)

; CHECK: @nestedarray.4 = alias i8*, getelementptr inbounds ([2 x [4 x i8*]], [2 x [4 x i8*]]* @nestedarray, i32 0, i32 1, i32 0)
@nestedarray.4 = alias i8*, getelementptr inbounds ([4 x i8*], [4 x i8*]* getelementptr inbounds ([2 x [4 x i8*]], [2 x [4 x i8*]]* @nestedarray, i32 0, inrange i32 0), i32 1, i32 0)

; CHECK: @nestedarray.5 = alias i8*, getelementptr inbounds ([2 x [4 x i8*]], [2 x [4 x i8*]]* @nestedarray, inrange i32 0, i32 1, i32 0)
@nestedarray.5 = alias i8*, getelementptr inbounds ([4 x i8*], [4 x i8*]* getelementptr inbounds ([2 x [4 x i8*]], [2 x [4 x i8*]]* @nestedarray, inrange i32 0, i32 0), i32 1, i32 0)

; See if i92 indices work too.
define i32 *@test({i32, i32}* %t, i92 %n) {
; CHECK: @test
Expand Down
7 changes: 7 additions & 0 deletions llvm/test/Bitcode/compatibility.ll
Original file line number Diff line number Diff line change
Expand Up @@ -1611,6 +1611,13 @@ normal:
declare void @f.writeonly() writeonly
; CHECK: declare void @f.writeonly() #39

;; Constant Expressions

define i8** @constexpr() {
; CHECK: ret i8** getelementptr inbounds ({ [4 x i8*], [4 x i8*] }, { [4 x i8*], [4 x i8*] }* null, i32 0, inrange i32 1, i32 2)
ret i8** getelementptr inbounds ({ [4 x i8*], [4 x i8*] }, { [4 x i8*], [4 x i8*] }* null, i32 0, inrange i32 1, i32 2)
}

; CHECK: attributes #0 = { alignstack=4 }
; CHECK: attributes #1 = { alignstack=8 }
; CHECK: attributes #2 = { alwaysinline }
Expand Down