Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -7421,9 +7421,9 @@ :: - = getelementptr , * {, }* - = getelementptr inbounds , * {, }* - = getelementptr , , + = getelementptr , * {, [inbounds] }* + = getelementptr inbounds , * {, [inbounds] }* + = getelementptr , , [inbounds] Overview: """"""""" @@ -7519,26 +7519,30 @@ ret i32* %t5 } -If the ``inbounds`` keyword is present, the result value of the -``getelementptr`` is a :ref:`poison value ` if the base -pointer is not an *in bounds* address of an allocated object, or if any -of the addresses that would be formed by successive addition of the -offsets implied by the indices to the base address with infinitely -precise signed arithmetic are not an *in bounds* address of that -allocated object. The *in bounds* addresses for an allocated object are -all the addresses that point into the object, plus the address one byte -past the end. In cases where the base is a vector of pointers the -``inbounds`` keyword applies to each of the computations element-wise. - -If the ``inbounds`` keyword is not present, the offsets are added to the -base address with silently-wrapping two's complement arithmetic. If the -offsets have a different width from the pointer, they are sign-extended -or truncated to the width of the pointer. The result value of the -``getelementptr`` may be outside the object pointed to by the base -pointer. The result value may not necessarily be used to access memory -though, even if it happens to point into allocated storage. See the -:ref:`Pointer Aliasing Rules ` section for more -information. +If the ``inbounds`` keyword is present after the ``getelementptr`` keyword, the +result value of the ``getelementptr`` is a :ref:`poison value ` +if the base pointer is not an *in bounds* address of an allocated object, +or if any of the addresses that would be formed by successive addition +of the offsets implied by the indices to the base address with infinitely +precise signed arithmetic are not an *in bounds* address of that allocated +object. The *in bounds* addresses for an allocated object are all the addresses +that point into the object, plus the address one byte past the end. In cases +where the base is a vector of pointers the ``inbounds`` keyword applies to +each of the computations element-wise. + +If the ``inbounds`` keyword is not present after the ``getelementptr`` +keyword, the offsets are added to the base address with silently-wrapping +two's complement arithmetic. If the offsets have a different width from the +pointer, they are sign-extended or truncated to the width of the pointer. The +result value of the ``getelementptr`` may be outside the object pointed to +by the base pointer. The result value may not necessarily be used to access +memory though, even if it happens to point into allocated storage. See the +:ref:`Pointer Aliasing Rules ` section for more information. + +If the ``inbounds`` keyword is present before any index, loading from or +storing to any pointer derived from the ``getelementptr`` has undefined +behavior if the load or store would access memory outside of the bounds of +the element selected by the index marked as ``inbounds``. The getelementptr instruction is often confusing. For some more insight into how it works, see :doc:`the getelementptr FAQ `. Index: include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- include/llvm/Bitcode/LLVMBitCodes.h +++ include/llvm/Bitcode/LLVMBitCodes.h @@ -286,8 +286,9 @@ CST_CODE_CE_INBOUNDS_GEP = 20, // INBOUNDS_GEP: [n x operands] CST_CODE_BLOCKADDRESS = 21, // CST_CODE_BLOCKADDRESS [fnty, fnval, bb#] CST_CODE_DATA = 22, // DATA: [n x elements] - CST_CODE_INLINEASM = 23 // INLINEASM: [sideeffect|alignstack| + CST_CODE_INLINEASM = 23, // INLINEASM: [sideeffect|alignstack| // asmdialect,asmstr,conststr] + CST_CODE_CE_GEP_WITH_INBOUNDS_INDEX = 24, // [opty, flags, n x operands] }; /// CastOpcodes - These are values used in the bitcode files to encode which Index: include/llvm/IR/Constants.h =================================================================== --- include/llvm/IR/Constants.h +++ include/llvm/IR/Constants.h @@ -24,6 +24,7 @@ #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" #include "llvm/IR/Constant.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/OperandTraits.h" @@ -1075,22 +1076,26 @@ static Constant *getGetElementPtr(Type *Ty, Constant *C, ArrayRef IdxList, bool InBounds = false, + Optional InBoundsIndex = None, Type *OnlyIfReducedTy = nullptr) { return getGetElementPtr( Ty, C, makeArrayRef((Value * const *)IdxList.data(), IdxList.size()), - InBounds, OnlyIfReducedTy); + InBounds, InBoundsIndex, OnlyIfReducedTy); } static Constant *getGetElementPtr(Type *Ty, Constant *C, Constant *Idx, bool InBounds = false, + Optional InBoundsIndex = None, Type *OnlyIfReducedTy = nullptr) { // This form of the function only exists to avoid ambiguous overload // warnings about whether to convert Idx to ArrayRef or // ArrayRef. - return getGetElementPtr(Ty, C, cast(Idx), InBounds, OnlyIfReducedTy); + return getGetElementPtr(Ty, C, cast(Idx), InBounds, InBoundsIndex, + OnlyIfReducedTy); } static Constant *getGetElementPtr(Type *Ty, Constant *C, ArrayRef IdxList, bool InBounds = false, + Optional InBoundsIndex = None, Type *OnlyIfReducedTy = nullptr); /// Create an "inbounds" getelementptr. See the documentation for the Index: include/llvm/IR/Operator.h =================================================================== --- include/llvm/IR/Operator.h +++ include/llvm/IR/Operator.h @@ -379,6 +379,12 @@ bool isInBounds() const { return SubclassOptionalData & IsInBounds; } + /// Returns the offset of the index with an inbounds attachment, or None if + /// none. + Optional getInBoundsIndex() const { + if (SubclassOptionalData >> 1 == 0) return None; + return (SubclassOptionalData >> 1) - 1; + } inline op_iterator idx_begin() { return op_begin()+1; } inline const_op_iterator idx_begin() const { return op_begin()+1; } Index: lib/Analysis/ConstantFolding.cpp =================================================================== --- lib/Analysis/ConstantFolding.cpp +++ lib/Analysis/ConstantFolding.cpp @@ -689,8 +689,8 @@ /// If array indices are not pointer-sized integers, explicitly cast them so /// that they aren't implicitly casted by the getelementptr. Constant *CastGEPIndices(Type *SrcElemTy, ArrayRef Ops, - Type *ResultTy, const DataLayout &DL, - const TargetLibraryInfo *TLI) { + Type *ResultTy, Optional InBoundsIndex, + const DataLayout &DL, const TargetLibraryInfo *TLI) { Type *IntPtrTy = DL.getIntPtrType(ResultTy); bool Any = false; @@ -713,7 +713,8 @@ if (!Any) return nullptr; - Constant *C = ConstantExpr::getGetElementPtr(SrcElemTy, Ops[0], NewIdxs); + Constant *C = ConstantExpr::getGetElementPtr( + SrcElemTy, Ops[0], NewIdxs, /*InBounds=*/false, InBoundsIndex); if (auto *CE = dyn_cast(C)) { if (Constant *Folded = ConstantFoldConstantExpression(CE, DL, TLI)) C = Folded; @@ -744,13 +745,16 @@ ArrayRef Ops, const DataLayout &DL, const TargetLibraryInfo *TLI) { + const GEPOperator *InnermostGEP = GEP; + Type *SrcElemTy = GEP->getSourceElementType(); Type *ResElemTy = GEP->getResultElementType(); Type *ResTy = GEP->getType(); if (!SrcElemTy->isSized()) return nullptr; - if (Constant *C = CastGEPIndices(SrcElemTy, Ops, ResTy, DL, TLI)) + if (Constant *C = CastGEPIndices(SrcElemTy, Ops, ResTy, + GEP->getInBoundsIndex(), DL, TLI)) return C; Constant *Ptr = Ops[0]; @@ -793,6 +797,8 @@ // If this is a GEP of a GEP, fold it all into a single GEP. while (auto *GEP = dyn_cast(Ptr)) { + InnermostGEP = GEP; + SmallVector NestedOps(GEP->op_begin() + 1, GEP->op_end()); // Do not try the incorporate the sub-GEP if some index is not a number. @@ -897,8 +903,23 @@ if (Offset != 0) return nullptr; + // Preserve the inbounds index from the innermost GEP if possible. We must + // have calculated the same indices up to and including the inbounds index. + Optional InBoundsIndex; + if (Optional LastIBIndex = InnermostGEP->getInBoundsIndex()) + if (SrcElemTy == InnermostGEP->getSourceElementType() && + NewIdxs.size() > *LastIBIndex) { + InBoundsIndex = LastIBIndex; + for (unsigned I = 0; I <= *LastIBIndex; ++I) + if (NewIdxs[I] != InnermostGEP->getOperand(I + 1)) { + InBoundsIndex = None; + break; + } + } + // Create a GEP. - Constant *C = ConstantExpr::getGetElementPtr(SrcElemTy, Ptr, NewIdxs); + Constant *C = ConstantExpr::getGetElementPtr( + SrcElemTy, Ptr, NewIdxs, /*InBounds=*/false, InBoundsIndex); assert(C->getType()->getPointerElementType() == Ty && "Computed GetElementPtr has unexpected type!"); @@ -936,8 +957,9 @@ if (Constant *C = SymbolicallyEvaluateGEP(GEP, Ops, DL, TLI)) return C; - return ConstantExpr::getGetElementPtr(GEP->getSourceElementType(), - Ops[0], Ops.slice(1)); + return ConstantExpr::getGetElementPtr(GEP->getSourceElementType(), Ops[0], + Ops.slice(1), GEP->isInBounds(), + GEP->getInBoundsIndex()); } switch (Opcode) { Index: lib/AsmParser/LLParser.h =================================================================== --- lib/AsmParser/LLParser.h +++ lib/AsmParser/LLParser.h @@ -411,7 +411,8 @@ bool ParseValID(ValID &ID, PerFunctionState *PFS = nullptr); bool ParseGlobalValue(Type *Ty, Constant *&V); bool ParseGlobalTypeAndValue(Constant *&V); - bool ParseGlobalValueVector(SmallVectorImpl &Elts); + bool ParseGlobalValueVector(SmallVectorImpl &Elts, + Optional *InBoundsOp = nullptr); bool parseOptionalComdat(StringRef GlobalName, Comdat *&C); bool ParseMetadataAsValue(Value *&V, PerFunctionState &PFS); bool ParseValueAsMetadata(Metadata *&MD, const Twine &TypeMsg, Index: lib/AsmParser/LLParser.cpp =================================================================== --- lib/AsmParser/LLParser.cpp +++ lib/AsmParser/LLParser.cpp @@ -3157,7 +3157,9 @@ return true; } - if (ParseGlobalValueVector(Elts) || + Optional InBoundsOp; + if (ParseGlobalValueVector( + Elts, Opc == Instruction::GetElementPtr ? &InBoundsOp : nullptr) || ParseToken(lltok::rparen, "expected ')' in constantexpr")) return true; @@ -3196,8 +3198,16 @@ if (!GetElementPtrInst::getIndexedType(Ty, Indices)) return Error(ID.Loc, "invalid getelementptr indices"); - ID.ConstantVal = - ConstantExpr::getGetElementPtr(Ty, Elts[0], Indices, InBounds); + + if (InBoundsOp) { + if (*InBoundsOp == 0) + return Error(ID.Loc, + "inbounds keyword may not appear on pointer operand"); + --*InBoundsOp; + } + + ID.ConstantVal = ConstantExpr::getGetElementPtr(Ty, Elts[0], Indices, + InBounds, InBoundsOp); } else if (Opc == Instruction::Select) { if (Elts.size() != 3) return Error(ID.Loc, "expected three operands to select"); @@ -3280,8 +3290,9 @@ /// ParseGlobalValueVector /// ::= /*empty*/ -/// ::= TypeAndValue (',' TypeAndValue)* -bool LLParser::ParseGlobalValueVector(SmallVectorImpl &Elts) { +/// ::= [inbounds] TypeAndValue (',' [inbounds] TypeAndValue)* +bool LLParser::ParseGlobalValueVector(SmallVectorImpl &Elts, + Optional *InBoundsOp) { // Empty list. if (Lex.getKind() == lltok::rbrace || Lex.getKind() == lltok::rsquare || @@ -3289,14 +3300,14 @@ Lex.getKind() == lltok::rparen) return false; - Constant *C; - if (ParseGlobalTypeAndValue(C)) return true; - Elts.push_back(C); + do { + if (InBoundsOp && !*InBoundsOp && EatIfPresent(lltok::kw_inbounds)) + *InBoundsOp = Elts.size(); - while (EatIfPresent(lltok::comma)) { + Constant *C; if (ParseGlobalTypeAndValue(C)) return true; Elts.push_back(C); - } + } while (EatIfPresent(lltok::comma)); return false; } Index: lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- lib/Bitcode/Reader/BitcodeReader.cpp +++ lib/Bitcode/Reader/BitcodeReader.cpp @@ -3095,12 +3095,25 @@ } break; } - case bitc::CST_CODE_CE_INBOUNDS_GEP: - case bitc::CST_CODE_CE_GEP: { // CE_GEP: [n x operands] + case bitc::CST_CODE_CE_INBOUNDS_GEP: // [ty, n x operands] + case bitc::CST_CODE_CE_GEP: // [ty, n x operands] + case bitc::CST_CODE_CE_GEP_WITH_INBOUNDS_INDEX: { // [ty, flags, n x + // operands] unsigned OpNum = 0; Type *PointeeType = nullptr; - if (Record.size() % 2) + if (BitCode == bitc::CST_CODE_CE_GEP_WITH_INBOUNDS_INDEX || + Record.size() % 2) PointeeType = getTypeByID(Record[OpNum++]); + + bool InBounds = false; + Optional InBoundsIndex; + if (BitCode == bitc::CST_CODE_CE_GEP_WITH_INBOUNDS_INDEX) { + uint64_t Op = Record[OpNum++]; + InBounds = Op & 1; + InBoundsIndex = Op >> 1; + } else if (BitCode == bitc::CST_CODE_CE_INBOUNDS_GEP) + InBounds = true; + SmallVector Elts; while (OpNum != Record.size()) { Type *ElTy = getTypeByID(Record[OpNum++]); @@ -3121,8 +3134,7 @@ ArrayRef Indices(Elts.begin() + 1, Elts.end()); V = ConstantExpr::getGetElementPtr(PointeeType, Elts[0], Indices, - BitCode == - bitc::CST_CODE_CE_INBOUNDS_GEP); + InBounds, InBoundsIndex); break; } case bitc::CST_CODE_CE_SELECT: { // CE_SELECT: [opval#, opval#, opval#] Index: lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- lib/Bitcode/Writer/BitcodeWriter.cpp +++ lib/Bitcode/Writer/BitcodeWriter.cpp @@ -2200,9 +2200,12 @@ case Instruction::GetElementPtr: { Code = bitc::CST_CODE_CE_GEP; const auto *GO = cast(C); - if (GO->isInBounds()) - Code = bitc::CST_CODE_CE_INBOUNDS_GEP; Record.push_back(VE.getTypeID(GO->getSourceElementType())); + if (Optional Idx = GO->getInBoundsIndex()) { + Code = bitc::CST_CODE_CE_GEP_WITH_INBOUNDS_INDEX; + Record.push_back((*Idx << 1) | GO->isInBounds()); + } else if (GO->isInBounds()) + Code = bitc::CST_CODE_CE_INBOUNDS_GEP; for (unsigned i = 0, e = CE->getNumOperands(); i != e; ++i) { Record.push_back(VE.getTypeID(C->getOperand(i)->getType())); Record.push_back(VE.getValueID(C->getOperand(i))); Index: lib/IR/AsmWriter.cpp =================================================================== --- lib/IR/AsmWriter.cpp +++ lib/IR/AsmWriter.cpp @@ -1352,12 +1352,18 @@ Out << ' ' << getPredicateText(CE->getPredicate()); Out << " ("; + Optional InBoundsOp; if (const GEPOperator *GEP = dyn_cast(CE)) { TypePrinter.print(GEP->getSourceElementType(), Out); Out << ", "; + InBoundsOp = GEP->getInBoundsIndex(); + if (InBoundsOp) + ++*InBoundsOp; } for (User::const_op_iterator OI=CE->op_begin(); OI != CE->op_end(); ++OI) { + if (InBoundsOp && (OI - CE->op_begin()) == *InBoundsOp) + Out << "inbounds "; TypePrinter.print((*OI)->getType(), Out); Out << ' '; WriteAsOperandInternal(Out, *OI, &TypePrinter, Machine, Context); Index: lib/IR/ConstantFold.h =================================================================== --- lib/IR/ConstantFold.h +++ lib/IR/ConstantFold.h @@ -19,6 +19,8 @@ #ifndef LLVM_LIB_IR_CONSTANTFOLD_H #define LLVM_LIB_IR_CONSTANTFOLD_H +#include "llvm/ADT/Optional.h" + namespace llvm { template class ArrayRef; class Value; @@ -46,9 +48,8 @@ Constant *V2); Constant *ConstantFoldCompareInstruction(unsigned short predicate, Constant *C1, Constant *C2); - Constant *ConstantFoldGetElementPtr(Type *Ty, Constant *C, bool inBounds, - ArrayRef Idxs); - Constant *ConstantFoldGetElementPtr(Type *Ty, Constant *C, bool inBounds, + Constant *ConstantFoldGetElementPtr(Type *Ty, Constant *C, bool InBounds, + Optional InBoundsIndex, ArrayRef Idxs); } // End llvm namespace Index: lib/IR/ConstantFold.cpp =================================================================== --- lib/IR/ConstantFold.cpp +++ lib/IR/ConstantFold.cpp @@ -545,7 +545,10 @@ } 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 inbounds index, as this loses + // information. + !cast(CE)->getInBoundsIndex().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; @@ -2046,10 +2049,10 @@ return true; } -template -static Constant *ConstantFoldGetElementPtrImpl(Type *PointeeTy, Constant *C, - bool inBounds, - ArrayRef Idxs) { +Constant *llvm::ConstantFoldGetElementPtr(Type *PointeeTy, Constant *C, + bool InBounds, + Optional InBoundsIndex, + ArrayRef Idxs) { if (Idxs.empty()) return C; Constant *Idx0 = cast(Idxs[0]); if ((Idxs.size() == 1 && Idx0->isNullValue())) @@ -2150,9 +2153,18 @@ NewIndices.push_back(Combined); NewIndices.append(Idxs.begin() + 1, Idxs.end()); + + // The combined GEP normally inherits its index inbounds 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 IBIndex = cast(CE)->getInBoundsIndex(); + if (IBIndex && *IBIndex == CE->getNumOperands() - 2 && !Idx0->isNullValue()) + IBIndex = None; + return ConstantExpr::getGetElementPtr( cast(CE)->getSourceElementType(), CE->getOperand(0), - NewIndices, inBounds && cast(CE)->isInBounds()); + NewIndices, InBounds && cast(CE)->isInBounds(), + IBIndex); } } @@ -2177,8 +2189,9 @@ 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, InBoundsIndex); } } } @@ -2198,6 +2211,12 @@ Unknown = true; continue; } + if (InBoundsIndex && i == *InBoundsIndex + 1) { + // If an index is marked inbounds, we cannot apply this canonicalization + // to the following index, as that will cause the inbounds index to point + // to the wrong element. + continue; + } if (isa(Ty)) { // The verify makes sure that GEPs into a struct are in range. continue; @@ -2260,27 +2279,17 @@ if (!NewIdxs.empty()) { for (unsigned i = 0, e = Idxs.size(); i != e; ++i) if (!NewIdxs[i]) NewIdxs[i] = cast(Idxs[i]); - return ConstantExpr::getGetElementPtr(PointeeTy, C, NewIdxs, inBounds); + return ConstantExpr::getGetElementPtr(PointeeTy, C, NewIdxs, InBounds, + InBoundsIndex); } // 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(C)) if (!GV->hasExternalWeakLinkage() && isInBoundsIndices(Idxs)) - return ConstantExpr::getInBoundsGetElementPtr(PointeeTy, C, Idxs); + return ConstantExpr::getGetElementPtr(PointeeTy, C, Idxs, + /*InBounds=*/true, InBoundsIndex); return nullptr; } - -Constant *llvm::ConstantFoldGetElementPtr(Type *Ty, Constant *C, - bool inBounds, - ArrayRef Idxs) { - return ConstantFoldGetElementPtrImpl(Ty, C, inBounds, Idxs); -} - -Constant *llvm::ConstantFoldGetElementPtr(Type *Ty, Constant *C, - bool inBounds, - ArrayRef Idxs) { - return ConstantFoldGetElementPtrImpl(Ty, C, inBounds, Idxs); -} Index: lib/IR/Constants.cpp =================================================================== --- lib/IR/Constants.cpp +++ lib/IR/Constants.cpp @@ -1169,7 +1169,7 @@ 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->getInBoundsIndex(), OnlyIfReducedTy); } case Instruction::ICmp: case Instruction::FCmp: @@ -1893,6 +1893,7 @@ Constant *ConstantExpr::getGetElementPtr(Type *Ty, Constant *C, ArrayRef Idxs, bool InBounds, + Optional InBoundsIndex, Type *OnlyIfReducedTy) { if (!Ty) Ty = cast(C->getType()->getScalarType())->getElementType(); @@ -1901,7 +1902,8 @@ Ty == cast(C->getType()->getScalarType())->getContainedType(0u)); - if (Constant *FC = ConstantFoldGetElementPtr(Ty, C, InBounds, Idxs)) + if (Constant *FC = + ConstantFoldGetElementPtr(Ty, C, InBounds, InBoundsIndex, Idxs)) return FC; // Fold a few common cases. // Get the result type of the getelementptr! @@ -1937,9 +1939,12 @@ Idx = ConstantVector::getSplat(NumVecElts, Idx); ArgVec.push_back(Idx); } + + unsigned SubClassOptionalData = InBounds ? GEPOperator::IsInBounds : 0; + if (InBoundsIndex && *InBoundsIndex < 63) + SubClassOptionalData |= (*InBoundsIndex + 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); Index: test/Analysis/ConstantFolding/gep.ll =================================================================== --- /dev/null +++ test/Analysis/ConstantFolding/gep.ll @@ -0,0 +1,30 @@ +; RUN: opt -instcombine -S -o - %s | FileCheck %s +; Tests that we preserve the inbounds 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, inbounds i64 0, i64 2) to i32 (...)**) + %load = load i32 (...)*, i32 (...)** getelementptr (i32 (...)*, i32 (...)** bitcast (i8** getelementptr inbounds ([3 x i8*], [3 x i8*]* @vt, inbounds 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, inbounds 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, inbounds i64 1) to i32 (...)**), i64 3) + ret i32 (...)* %load +} Index: test/Assembler/getelementptr.ll =================================================================== --- test/Assembler/getelementptr.ll +++ test/Assembler/getelementptr.ll @@ -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 inbounds 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, inbounds i32 0, i64 1, i32 0) +@nestedarray.1 = alias i8*, getelementptr inbounds ([2 x [4 x i8*]], [2 x [4 x i8*]]* @nestedarray, inbounds 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, inbounds i32 0, i32 4) +@nestedarray.2 = alias i8*, getelementptr inbounds ([2 x [4 x i8*]], [2 x [4 x i8*]]* @nestedarray, i32 0, inbounds i32 0, i32 4) + +; CHECK: @nestedarray.3 = alias i8*, getelementptr inbounds ([2 x [4 x i8*]], [2 x [4 x i8*]]* @nestedarray, i32 0, inbounds 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, inbounds 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, inbounds i32 0), i32 1, i32 0) + +; CHECK: @nestedarray.5 = alias i8*, getelementptr inbounds ([2 x [4 x i8*]], [2 x [4 x i8*]]* @nestedarray, inbounds 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, inbounds i32 0, i32 0), i32 1, i32 0) + ; See if i92 indices work too. define i32 *@test({i32, i32}* %t, i92 %n) { ; CHECK: @test Index: test/Bitcode/compatibility.ll =================================================================== --- test/Bitcode/compatibility.ll +++ test/Bitcode/compatibility.ll @@ -1593,6 +1593,13 @@ 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, inbounds i32 1, i32 2) + ret i8** getelementptr inbounds ({ [4 x i8*], [4 x i8*] }, { [4 x i8*], [4 x i8*] }* null, i32 0, inbounds i32 1, i32 2) +} + ; CHECK: attributes #0 = { alignstack=4 } ; CHECK: attributes #1 = { alignstack=8 } ; CHECK: attributes #2 = { alwaysinline }