diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -10396,8 +10396,7 @@ the ``inrange`` keyword is undefined, with the exception of comparisons in the case where both operands are in the range of the element selected by the ``inrange`` keyword, inclusive of the address one past the end of -that element. Note that the ``inrange`` keyword is currently only allowed -in constant ``getelementptr`` expressions. +that element. The getelementptr instruction is often confusing. For some more insight into how it works, see :doc:`the getelementptr FAQ `. diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -585,6 +585,9 @@ FUNC_CODE_INST_ATOMICRMW = 59, // ATOMICRMW: [ptrty, ptr, valty, val, // operation, align, vol, // ordering, synchscope] + FUNC_CODE_INST_GEP_WITH_INRANGE_INDICES = + 60, // GEP: [inbounds, (n)um indices, n+1 x operands, n x inrange] + }; enum UseListCodes { diff --git a/llvm/include/llvm/IR/Instructions.h b/llvm/include/llvm/IR/Instructions.h --- a/llvm/include/llvm/IR/Instructions.h +++ b/llvm/include/llvm/IR/Instructions.h @@ -27,8 +27,8 @@ #include "llvm/ADT/iterator_range.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/BasicBlock.h" -#include "llvm/IR/CallingConv.h" #include "llvm/IR/CFG.h" +#include "llvm/IR/CallingConv.h" #include "llvm/IR/Constant.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" @@ -42,6 +42,7 @@ #include "llvm/Support/AtomicOrdering.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/TrailingObjects.h" #include #include #include @@ -926,6 +927,21 @@ /// access elements of arrays and structs /// class GetElementPtrInst : public Instruction { + // All but the first bit (which is 'inbounds') of the SubclassOptionalData + // can be use for 'inrange' markers. + using SubclassOptionalDataInlineBits = Bitfield::Element; + // All but the last bit (which is 'has metadata') of the SubclassData + // can be use for [remaining] 'inrange' markers. + using SubclassDataInlineBits = Bitfield::Element; + // The rest of the markers will have to be stored in trailing objects. + + using trailing_obj = uintptr_t; + using trailing_obj_iterator = trailing_obj *; + using const_trailing_obj_iterator = const trailing_obj *; + + static constexpr unsigned NumBitsPerTrailingObject = + CHAR_BIT * sizeof(trailing_obj); + Type *SourceElementType; Type *ResultElementType; @@ -936,14 +952,41 @@ /// instruction, the second appends the new instruction to the specified /// BasicBlock. inline GetElementPtrInst(Type *PointeeType, Value *Ptr, - ArrayRef IdxList, unsigned Values, + ArrayRef IdxList, unsigned NumOps, const Twine &NameStr, Instruction *InsertBefore); inline GetElementPtrInst(Type *PointeeType, Value *Ptr, - ArrayRef IdxList, unsigned Values, + ArrayRef IdxList, unsigned NumOps, const Twine &NameStr, BasicBlock *InsertAtEnd); void init(Value *Ptr, ArrayRef IdxList, const Twine &NameStr); + static unsigned numTrailingObjects(unsigned NumIndices) { + unsigned NumBits = NumIndices; + static constexpr unsigned NumInlineBitsMax = + SubclassOptionalDataInlineBits::Bits + SubclassDataInlineBits::Bits; + ; + unsigned NumNonInlineBits = + NumBits > NumInlineBitsMax ? NumBits - NumInlineBitsMax : 0; + return divideCeil(NumNonInlineBits, NumBitsPerTrailingObject); + } + + unsigned getNumTrailingObjects() const { + return numTrailingObjects(getNumIndices()); + } + + inline trailing_obj_iterator trailing_obj_begin() { + return reinterpret_cast(op_end()); + } + inline const_trailing_obj_iterator trailing_obj_begin() const { + return reinterpret_cast(op_end()); + } + inline trailing_obj_iterator trailing_obj_end() { + return trailing_obj_begin() + getNumTrailingObjects(); + } + inline const_trailing_obj_iterator trailing_obj_end() const { + return trailing_obj_begin() + getNumTrailingObjects(); + } + protected: // Note: Instruction needs to be a friend here to call cloneImpl. friend class Instruction; @@ -955,11 +998,13 @@ ArrayRef IdxList, const Twine &NameStr = "", Instruction *InsertBefore = nullptr) { - unsigned Values = 1 + unsigned(IdxList.size()); + unsigned NumIndices = IdxList.size(); + unsigned NumOps = 1 + NumIndices; + unsigned Values = NumOps + numTrailingObjects(NumIndices); assert(PointeeType && "Must specify element type"); assert(cast(Ptr->getType()->getScalarType()) ->isOpaqueOrPointeeTypeMatches(PointeeType)); - return new (Values) GetElementPtrInst(PointeeType, Ptr, IdxList, Values, + return new (Values) GetElementPtrInst(PointeeType, Ptr, IdxList, NumOps, NameStr, InsertBefore); } @@ -967,11 +1012,13 @@ ArrayRef IdxList, const Twine &NameStr, BasicBlock *InsertAtEnd) { - unsigned Values = 1 + unsigned(IdxList.size()); + unsigned NumIndices = IdxList.size(); + unsigned NumOps = 1 + NumIndices; + unsigned Values = NumOps + numTrailingObjects(NumIndices); assert(PointeeType && "Must specify element type"); assert(cast(Ptr->getType()->getScalarType()) ->isOpaqueOrPointeeTypeMatches(PointeeType)); - return new (Values) GetElementPtrInst(PointeeType, Ptr, IdxList, Values, + return new (Values) GetElementPtrInst(PointeeType, Ptr, IdxList, NumOps, NameStr, InsertAtEnd); } @@ -1118,6 +1165,12 @@ /// Determine whether the GEP has the inbounds flag. bool isInBounds() const; + /// Set or clear the inrange flag on the index \p Idx of this GEP instruction. + void setIsInRange(unsigned Idx, bool b = true); + + /// Determine whether the index \p Idx of this GEP has the inrange flag. + bool isInRange(unsigned Idx) const; + /// Accumulate the constant address offset of this GEP if possible. /// /// This routine accepts an APInt into which it will accumulate the constant @@ -1145,12 +1198,12 @@ }; GetElementPtrInst::GetElementPtrInst(Type *PointeeType, Value *Ptr, - ArrayRef IdxList, unsigned Values, + ArrayRef IdxList, unsigned NumOps, const Twine &NameStr, Instruction *InsertBefore) : Instruction(getGEPReturnType(PointeeType, Ptr, IdxList), GetElementPtr, - OperandTraits::op_end(this) - Values, - Values, InsertBefore), + OperandTraits::op_end(this) - NumOps, + NumOps, InsertBefore), SourceElementType(PointeeType), ResultElementType(getIndexedType(PointeeType, IdxList)) { assert(cast(getType()->getScalarType()) @@ -1159,12 +1212,12 @@ } GetElementPtrInst::GetElementPtrInst(Type *PointeeType, Value *Ptr, - ArrayRef IdxList, unsigned Values, + ArrayRef IdxList, unsigned NumOps, const Twine &NameStr, BasicBlock *InsertAtEnd) : Instruction(getGEPReturnType(PointeeType, Ptr, IdxList), GetElementPtr, - OperandTraits::op_end(this) - Values, - Values, InsertAtEnd), + OperandTraits::op_end(this) - NumOps, + NumOps, InsertAtEnd), SourceElementType(PointeeType), ResultElementType(getIndexedType(PointeeType, IdxList)) { assert(cast(getType()->getScalarType()) diff --git a/llvm/include/llvm/IR/Operator.h b/llvm/include/llvm/IR/Operator.h --- a/llvm/include/llvm/IR/Operator.h +++ b/llvm/include/llvm/IR/Operator.h @@ -494,6 +494,7 @@ /// Returns the offset of the index with an inrange attachment, or None if /// none. Optional getInRangeIndex() const { + // FIXME: GetElementPtrInst support. if (SubclassOptionalData >> 1 == 0) return None; return (SubclassOptionalData >> 1) - 1; } diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -5067,7 +5067,7 @@ return false; case Instruction::GetElementPtr: // inbounds is handled above - // TODO: what about inrange on constexpr? + // TODO: what about inrange? return false; default: { const auto *CE = dyn_cast(Op); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -7442,6 +7442,7 @@ SmallVector Indices; bool AteExtraComma = false; + SmallVector InRangeIndices; // GEP returns a vector of pointers if at least one of parameters is a vector. // All vector parameters should have the same vector width. ElementCount GEPWidth = BaseType->isVectorTy() @@ -7453,6 +7454,8 @@ AteExtraComma = true; break; } + if (EatIfPresent(lltok::kw_inrange)) + InRangeIndices.emplace_back(Indices.size()); if (parseTypeAndValue(Val, EltLoc, PFS)) return true; if (!Val->getType()->isIntOrIntVectorTy()) @@ -7478,6 +7481,8 @@ Inst = GetElementPtrInst::Create(Ty, Ptr, Indices); if (InBounds) cast(Inst)->setIsInBounds(true); + for (unsigned InRangeInRangeIndice : InRangeIndices) + cast(Inst)->setIsInRange(InRangeInRangeIndice, true); return AteExtraComma ? InstExtraComma : InstNormal; } diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -4148,14 +4148,22 @@ } case bitc::FUNC_CODE_INST_INBOUNDS_GEP_OLD: case bitc::FUNC_CODE_INST_GEP_OLD: - case bitc::FUNC_CODE_INST_GEP: { // GEP: type, [n x operands] + case bitc::FUNC_CODE_INST_GEP: // GEP: inrange, type, [n x operands] + case bitc:: + FUNC_CODE_INST_GEP_WITH_INRANGE_INDICES: // GEP: inrange, n, type, [n+1 + // x operands, n x inrange] + { unsigned OpNum = 0; Type *Ty; bool InBounds; + Optional NumIndices; - if (BitCode == bitc::FUNC_CODE_INST_GEP) { + if (BitCode == bitc::FUNC_CODE_INST_GEP || + BitCode == bitc::FUNC_CODE_INST_GEP_WITH_INRANGE_INDICES) { InBounds = Record[OpNum++]; + if (BitCode == bitc::FUNC_CODE_INST_GEP_WITH_INRANGE_INDICES) + NumIndices = Record[OpNum++]; Ty = getTypeByID(Record[OpNum++]); } else { InBounds = BitCode == bitc::FUNC_CODE_INST_INBOUNDS_GEP_OLD; @@ -4176,7 +4184,16 @@ } SmallVector GEPIdx; - while (OpNum != Record.size()) { + if (NumIndices) + GEPIdx.reserve(*NumIndices); + + auto DoneParsingIndices = [&]() { + if (BitCode == bitc::FUNC_CODE_INST_GEP_WITH_INRANGE_INDICES) + return GEPIdx.size() == *NumIndices; + return OpNum == Record.size(); + }; + + while (!DoneParsingIndices()) { Value *Op; if (getValueTypePair(Record, OpNum, NextValueNo, Op)) return error("Invalid record"); @@ -4188,6 +4205,19 @@ InstructionList.push_back(I); if (InBounds) cast(I)->setIsInBounds(true); + if (BitCode == bitc::FUNC_CODE_INST_GEP_WITH_INRANGE_INDICES) { + constexpr unsigned NumInRangeBitsPerChunk = 6; + unsigned NumInRangeBitChunks = + divideCeil(*NumIndices, NumInRangeBitsPerChunk); + APInt InRangeIndices(NumInRangeBitsPerChunk * NumInRangeBitChunks, 0); + for (unsigned ChunkIndex : seq(0U, NumInRangeBitChunks)) + InRangeIndices.insertBits(Record[OpNum++], + NumInRangeBitsPerChunk * ChunkIndex, + NumInRangeBitsPerChunk); + for (unsigned Idx : seq(0U, *NumIndices)) + if (InRangeIndices[Idx]) + cast(I)->setIsInRange(Idx, true); + } break; } diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -124,6 +124,7 @@ FUNCTION_INST_RET_VAL_ABBREV, FUNCTION_INST_UNREACHABLE_ABBREV, FUNCTION_INST_GEP_ABBREV, + FUNCTION_INST_GEP_WITH_INRANGE_INDICES_ABBREV, }; /// Abstract class to manage the bitcode writing, subclassed for each bitcode @@ -2779,13 +2780,36 @@ break; } case Instruction::GetElementPtr: { - Code = bitc::FUNC_CODE_INST_GEP; - AbbrevToUse = FUNCTION_INST_GEP_ABBREV; auto &GEPInst = cast(I); + constexpr unsigned NumInRangeBitsPerChunk = 6; + unsigned NumInRangeChunks = + divideCeil(GEPInst.getNumIndices(), NumInRangeBitsPerChunk); + APInt InRangeIndices(NumInRangeBitsPerChunk * NumInRangeChunks, 0); + bool HadInRangeIndices = false; + for (unsigned Idx : seq(0U, GEPInst.getNumIndices())) + if (GEPInst.isInRange(Idx)) { + InRangeIndices.setBit(Idx); + HadInRangeIndices |= true; + } + if (HadInRangeIndices) { + Code = bitc::FUNC_CODE_INST_GEP_WITH_INRANGE_INDICES; + AbbrevToUse = FUNCTION_INST_GEP_WITH_INRANGE_INDICES_ABBREV; + } else { + Code = bitc::FUNC_CODE_INST_GEP; + AbbrevToUse = FUNCTION_INST_GEP_ABBREV; + } Vals.push_back(GEPInst.isInBounds()); + if (HadInRangeIndices) + Vals.push_back(GEPInst.getNumIndices()); Vals.push_back(VE.getTypeID(GEPInst.getSourceElementType())); for (unsigned i = 0, e = I.getNumOperands(); i != e; ++i) pushValueAndType(I.getOperand(i), InstID, Vals); + if (HadInRangeIndices) { + for (unsigned InRangeChunkIndex : seq(0U, NumInRangeChunks)) + Vals.push_back(InRangeIndices.extractBitsAsZExtValue( + NumInRangeBitsPerChunk, + NumInRangeBitsPerChunk * InRangeChunkIndex)); + } break; } case Instruction::ExtractValue: { @@ -3565,7 +3589,7 @@ { auto Abbv = std::make_shared(); Abbv->Add(BitCodeAbbrevOp(bitc::FUNC_CODE_INST_GEP)); - Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // inrange Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, // dest ty Log2_32_Ceil(VE.getTypes().size() + 1))); Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array)); @@ -3574,6 +3598,19 @@ FUNCTION_INST_GEP_ABBREV) llvm_unreachable("Unexpected abbrev ordering!"); } + { + auto Abbv = std::make_shared(); + Abbv->Add(BitCodeAbbrevOp(bitc::FUNC_CODE_INST_GEP_WITH_INRANGE_INDICES)); + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // inrange + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // num indices + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, // dest ty + Log2_32_Ceil(VE.getTypes().size() + 1))); + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array)); + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); + if (Stream.EmitBlockInfoAbbrev(bitc::FUNCTION_BLOCK_ID, Abbv) != + FUNCTION_INST_GEP_WITH_INRANGE_INDICES_ABBREV) + llvm_unreachable("Unexpected abbrev ordering!"); + } Stream.ExitBlock(); } diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -4303,6 +4303,9 @@ Out << ' '; for (unsigned i = 0, E = I.getNumOperands(); i != E; ++i) { if (i) Out << ", "; + if (const auto *GEP = dyn_cast(&I)) + if (i && GEP->isInRange(i - 1)) + Out << "inrange "; writeOperand(I.getOperand(i), PrintAllTypes); } } diff --git a/llvm/lib/IR/Instruction.cpp b/llvm/lib/IR/Instruction.cpp --- a/llvm/lib/IR/Instruction.cpp +++ b/llvm/lib/IR/Instruction.cpp @@ -162,10 +162,14 @@ cast(this)->setIsExact(false); break; - case Instruction::GetElementPtr: - cast(this)->setIsInBounds(false); + case Instruction::GetElementPtr: { + auto *GEPI = cast(this); + GEPI->setIsInBounds(false); + for (unsigned Idx : seq(0U, GEPI->getNumIndices())) + GEPI->setIsInRange(Idx, false); break; } + } // TODO: FastMathFlags! assert(!hasPoisonGeneratingFlags() && "must be kept in sync"); @@ -314,6 +318,7 @@ if (auto *SrcGEP = dyn_cast(V)) if (auto *DestGEP = dyn_cast(this)) DestGEP->setIsInBounds(SrcGEP->isInBounds() || DestGEP->isInBounds()); + // FIXME: What about `inrange`? } void Instruction::andIRFlags(const Value *V) { @@ -339,6 +344,7 @@ if (auto *SrcGEP = dyn_cast(V)) if (auto *DestGEP = dyn_cast(this)) DestGEP->setIsInBounds(SrcGEP->isInBounds() && DestGEP->isInBounds()); + // FIXME: What about `inrange`? } const char *Instruction::getOpcodeName(unsigned OpCode) { @@ -428,7 +434,7 @@ /// Return true if both instructions have the same special state. This must be /// kept in sync with FunctionComparator::cmpOperations in -/// lib/Transforms/IPO/MergeFunctions.cpp. +/// lib/Transforms/Utils/FunctionComparator.cpp. static bool haveSameSpecialState(const Instruction *I1, const Instruction *I2, bool IgnoreAlignment = false) { assert(I1->getOpcode() == I2->getOpcode() && @@ -489,6 +495,13 @@ if (const ShuffleVectorInst *SVI = dyn_cast(I1)) return SVI->getShuffleMask() == cast(I2)->getShuffleMask(); + if (const GetElementPtrInst *GEPI = dyn_cast(I1)) { + const GetElementPtrInst *OtherGEPI = cast(I2); + for (unsigned Idx : seq(0U, GEPI->getNumIndices())) + if (GEPI->isInRange(Idx) != OtherGEPI->isInRange(Idx)) + return false; + return true; + } return true; } @@ -524,7 +537,7 @@ } // Keep this in sync with FunctionComparator::cmpOperations in -// lib/Transforms/IPO/MergeFunctions.cpp. +// lib/Transforms/Utils/FunctionComparator.cpp. bool Instruction::isSameOperationAs(const Instruction *I, unsigned flags) const { bool IgnoreAlignment = flags & CompareIgnoringAlignment; diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp --- a/llvm/lib/IR/Instructions.cpp +++ b/llvm/lib/IR/Instructions.cpp @@ -1727,6 +1727,10 @@ ResultElementType(GEPI.ResultElementType) { std::copy(GEPI.op_begin(), GEPI.op_end(), op_begin()); SubclassOptionalData = GEPI.SubclassOptionalData; + setSubclassData( + GEPI.getSubclassData()); + std::copy(GEPI.trailing_obj_end(), GEPI.trailing_obj_end(), + trailing_obj_end()); } Type *GetElementPtrInst::getTypeAtIndex(Type *Ty, Value *Idx) { @@ -1815,6 +1819,70 @@ return cast(this)->isInBounds(); } +void GetElementPtrInst::setIsInRange(unsigned Idx, bool b) { + assert(Idx < getNumIndices() && "Must be a valid index for this GEP!"); + + auto Update = [b, Idx](auto &Storage) { + if (b) // Set bit Idx + Storage |= (1U << Idx); + else // Keep all bits except bit Idx + Storage &= ~(1U << Idx); + }; + + auto HandleInlineStorage = [&](auto InlineStorage) { + using InlineStorageTy = decltype(InlineStorage); + if (Idx <= InlineStorageTy::Bits) { + auto Storage = getSubclassData(); + Update(Storage); + setSubclassData(Storage); + return true; + } + Idx -= InlineStorageTy::Bits; + return false; + }; + + if (HandleInlineStorage(SubclassOptionalDataInlineBits{})) + return; + if (HandleInlineStorage(SubclassDataInlineBits{})) + return; + + // In which trailing object does this bit reside? + unsigned TrailingObjectIndex = Idx / NumBitsPerTrailingObject; + uintptr_t &Storage = trailing_obj_begin()[TrailingObjectIndex]; + // And which bit in that trailing object is that? + Idx %= NumBitsPerTrailingObject; + Update(Storage); +} + +bool GetElementPtrInst::isInRange(unsigned Idx) const { + assert(Idx < getNumIndices() && "Must be a valid index for this GEP!"); + + auto Test = [Idx](auto Storage) { return Storage & (1U << Idx); }; + + auto HandleInlineStorage = [&](auto InlineStorage) -> Optional { + using InlineStorageTy = decltype(InlineStorage); + if (Idx <= InlineStorageTy::Bits) { + auto Storage = getSubclassData(); + return Test(Storage); + } + Idx -= InlineStorageTy::Bits; + return None; + }; + + if (auto Res = HandleInlineStorage(SubclassOptionalDataInlineBits{})) + return *Res; + if (auto Res = HandleInlineStorage(SubclassDataInlineBits{})) + return *Res; + + // In which trailing object does this bit reside? + unsigned TrailingObjectIndex = Idx / NumBitsPerTrailingObject; + uintptr_t TrailingObject = trailing_obj_begin()[TrailingObjectIndex]; + // And which bit in that trailing object is that? + Idx %= NumBitsPerTrailingObject; + // Is bit Idx set in the trailing object? + return TrailingObject & (1U << Idx); +} + bool GetElementPtrInst::accumulateConstantOffset(const DataLayout &DL, APInt &Offset) const { // Delegate to the generic GEPOperator implementation. @@ -4566,7 +4634,8 @@ // unit that uses these classes. GetElementPtrInst *GetElementPtrInst::cloneImpl() const { - return new (getNumOperands()) GetElementPtrInst(*this); + return new (getNumOperands() + getNumTrailingObjects()) + GetElementPtrInst(*this); } UnaryOperator *UnaryOperator::cloneImpl() const { diff --git a/llvm/lib/IR/Operator.cpp b/llvm/lib/IR/Operator.cpp --- a/llvm/lib/IR/Operator.cpp +++ b/llvm/lib/IR/Operator.cpp @@ -35,8 +35,15 @@ return cast(this)->isExact(); case Instruction::GetElementPtr: { auto *GEP = cast(this); - // Note: inrange exists on constexpr only - return GEP->isInBounds() || GEP->getInRangeIndex() != None; + if (GEP->isInBounds()) + return true; + if (isa(this)) + return GEP->getInRangeIndex() != None; + auto *GEPI = dyn_cast(this); + for (unsigned Idx : seq(0U, GEPI->getNumIndices())) + if (GEPI->isInRange(Idx)) + return true; + return false; } default: return false; diff --git a/llvm/test/Assembler/getelementptr.ll b/llvm/test/Assembler/getelementptr.ll --- a/llvm/test/Assembler/getelementptr.ll +++ b/llvm/test/Assembler/getelementptr.ll @@ -75,3 +75,36 @@ ; CHECK-LABEL: define i32* @test9( ; CHECK: ret i32* getelementptr ([16 x i32], [16 x i32]* @array, i64 0, i64 -13) } + +; Verity that inrange on indices is handled correctly. +define void @test10({ { { i32 } } } *%a) { +; CHECK-LABEL: define void @test10( +; CHECK: %v0 = getelementptr { { { i32 } } }, { { { i32 } } }* %a, i32 0, i32 0, i32 0, i32 0 +; CHECK: %v1 = getelementptr { { { i32 } } }, { { { i32 } } }* %a, inrange i32 0, i32 0, i32 0, i32 0 +; CHECK: %v2 = getelementptr { { { i32 } } }, { { { i32 } } }* %a, i32 0, inrange i32 0, i32 0, i32 0 +; CHECK: %v3 = getelementptr { { { i32 } } }, { { { i32 } } }* %a, i32 0, i32 0, inrange i32 0, i32 0 +; CHECK: %v4 = getelementptr { { { i32 } } }, { { { i32 } } }* %a, i32 0, i32 0, i32 0, inrange i32 0 +; CHECK: %v5 = getelementptr { { { i32 } } }, { { { i32 } } }* %a, inrange i32 0, inrange i32 0, i32 0, i32 0 +; CHECK: %v6 = getelementptr { { { i32 } } }, { { { i32 } } }* %a, inrange i32 0, i32 0, inrange i32 0, i32 0 +; CHECK: %v7 = getelementptr { { { i32 } } }, { { { i32 } } }* %a, inrange i32 0, i32 0, i32 0, inrange i32 0 +; CHECK: %v8 = getelementptr { { { i32 } } }, { { { i32 } } }* %a, inrange i32 0, i32 0, i32 0, inrange i32 0 +; CHECK: %v9 = getelementptr { { { i32 } } }, { { { i32 } } }* %a, i32 0, inrange i32 0, i32 0, inrange i32 0 +; CHECK: %v10 = getelementptr { { { i32 } } }, { { { i32 } } }* %a, i32 0, i32 0, inrange i32 0, inrange i32 0 + + %v0 = getelementptr { { { i32 } } }, { { { i32 } } }* %a, i32 0, i32 0, i32 0, i32 0 + + %v1 = getelementptr { { { i32 } } }, { { { i32 } } }* %a, inrange i32 0, i32 0, i32 0, i32 0 + %v2 = getelementptr { { { i32 } } }, { { { i32 } } }* %a, i32 0, inrange i32 0, i32 0, i32 0 + %v3 = getelementptr { { { i32 } } }, { { { i32 } } }* %a, i32 0, i32 0, inrange i32 0, i32 0 + %v4 = getelementptr { { { i32 } } }, { { { i32 } } }* %a, i32 0, i32 0, i32 0, inrange i32 0 + + %v5 = getelementptr { { { i32 } } }, { { { i32 } } }* %a, inrange i32 0, inrange i32 0, i32 0, i32 0 + %v6 = getelementptr { { { i32 } } }, { { { i32 } } }* %a, inrange i32 0, i32 0, inrange i32 0, i32 0 + %v7 = getelementptr { { { i32 } } }, { { { i32 } } }* %a, inrange i32 0, i32 0, i32 0, inrange i32 0 + + %v8 = getelementptr { { { i32 } } }, { { { i32 } } }* %a, inrange i32 0, i32 0, i32 0, inrange i32 0 + %v9 = getelementptr { { { i32 } } }, { { { i32 } } }* %a, i32 0, inrange i32 0, i32 0, inrange i32 0 + %v10 = getelementptr { { { i32 } } }, { { { i32 } } }* %a, i32 0, i32 0, inrange i32 0, inrange i32 0 + + ret void +}