Index: llvm/include/llvm/Analysis/ValueTracking.h =================================================================== --- llvm/include/llvm/Analysis/ValueTracking.h +++ llvm/include/llvm/Analysis/ValueTracking.h @@ -1017,12 +1017,6 @@ const Value *LHS, const Value *RHS, const Instruction *ContextI, const DataLayout &DL); - -/// If Ptr1 is provably equal to Ptr2 plus a constant offset, return that -/// offset. For example, Ptr1 might be &A[42], and Ptr2 might be &A[40]. In -/// this case offset would be -8. -std::optional isPointerOffset(const Value *Ptr1, const Value *Ptr2, - const DataLayout &DL); } // end namespace llvm #endif // LLVM_ANALYSIS_VALUETRACKING_H Index: llvm/include/llvm/IR/Value.h =================================================================== --- llvm/include/llvm/IR/Value.h +++ llvm/include/llvm/IR/Value.h @@ -769,6 +769,11 @@ /// align attribute of a function argument, or guaranteed by DataLayout. Align getPointerAlignment(const DataLayout &DL) const; + /// If this ptr is provably equal \p Other plus a constant offset, return + /// that offset in bytes. Essentially `ptr this` subtract `ptr Other`. + std::optional getPointerOffsetFrom(const Value *Other, + const DataLayout &DL) const; + /// Translate PHI node to its predecessor from the given basic block. /// /// If this value is a PHI node with CurBB as its parent, return the value in Index: llvm/lib/Analysis/ValueTracking.cpp =================================================================== --- llvm/lib/Analysis/ValueTracking.cpp +++ llvm/lib/Analysis/ValueTracking.cpp @@ -8224,74 +8224,3 @@ return CR; } - -static std::optional -getOffsetFromIndex(const GEPOperator *GEP, unsigned Idx, const DataLayout &DL) { - // Skip over the first indices. - gep_type_iterator GTI = gep_type_begin(GEP); - for (unsigned i = 1; i != Idx; ++i, ++GTI) - /*skip along*/; - - // Compute the offset implied by the rest of the indices. - int64_t Offset = 0; - for (unsigned i = Idx, e = GEP->getNumOperands(); i != e; ++i, ++GTI) { - ConstantInt *OpC = dyn_cast(GEP->getOperand(i)); - if (!OpC) - return std::nullopt; - if (OpC->isZero()) - continue; // No offset. - - // Handle struct indices, which add their field offset to the pointer. - if (StructType *STy = GTI.getStructTypeOrNull()) { - Offset += DL.getStructLayout(STy)->getElementOffset(OpC->getZExtValue()); - continue; - } - - // Otherwise, we have a sequential type like an array or fixed-length - // vector. Multiply the index by the ElementSize. - TypeSize Size = DL.getTypeAllocSize(GTI.getIndexedType()); - if (Size.isScalable()) - return std::nullopt; - Offset += Size.getFixedValue() * OpC->getSExtValue(); - } - - return Offset; -} - -std::optional llvm::isPointerOffset(const Value *Ptr1, - const Value *Ptr2, - const DataLayout &DL) { - APInt Offset1(DL.getIndexTypeSizeInBits(Ptr1->getType()), 0); - APInt Offset2(DL.getIndexTypeSizeInBits(Ptr2->getType()), 0); - Ptr1 = Ptr1->stripAndAccumulateConstantOffsets(DL, Offset1, true); - Ptr2 = Ptr2->stripAndAccumulateConstantOffsets(DL, Offset2, true); - - // Handle the trivial case first. - if (Ptr1 == Ptr2) - return Offset2.getSExtValue() - Offset1.getSExtValue(); - - const GEPOperator *GEP1 = dyn_cast(Ptr1); - const GEPOperator *GEP2 = dyn_cast(Ptr2); - - // Right now we handle the case when Ptr1/Ptr2 are both GEPs with an identical - // base. After that base, they may have some number of common (and - // potentially variable) indices. After that they handle some constant - // offset, which determines their offset from each other. At this point, we - // handle no other case. - if (!GEP1 || !GEP2 || GEP1->getOperand(0) != GEP2->getOperand(0) || - GEP1->getSourceElementType() != GEP2->getSourceElementType()) - return std::nullopt; - - // Skip any common indices and track the GEP types. - unsigned Idx = 1; - for (; Idx != GEP1->getNumOperands() && Idx != GEP2->getNumOperands(); ++Idx) - if (GEP1->getOperand(Idx) != GEP2->getOperand(Idx)) - break; - - auto IOffset1 = getOffsetFromIndex(GEP1, Idx, DL); - auto IOffset2 = getOffsetFromIndex(GEP2, Idx, DL); - if (!IOffset1 || !IOffset2) - return std::nullopt; - return *IOffset2 - *IOffset1 + Offset2.getSExtValue() - - Offset1.getSExtValue(); -} Index: llvm/lib/IR/Value.cpp =================================================================== --- llvm/lib/IR/Value.cpp +++ llvm/lib/IR/Value.cpp @@ -20,6 +20,7 @@ #include "llvm/IR/DebugInfo.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/DerivedUser.h" +#include "llvm/IR/GetElementPtrTypeIterator.h" #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" @@ -985,6 +986,78 @@ return Align(1); } +static std::optional +getOffsetFromIndex(const GEPOperator *GEP, unsigned Idx, const DataLayout &DL) { + // Skip over the first indices. + gep_type_iterator GTI = gep_type_begin(GEP); + for (unsigned i = 1; i != Idx; ++i, ++GTI) + /*skip along*/; + + // Compute the offset implied by the rest of the indices. + int64_t Offset = 0; + for (unsigned i = Idx, e = GEP->getNumOperands(); i != e; ++i, ++GTI) { + ConstantInt *OpC = dyn_cast(GEP->getOperand(i)); + if (!OpC) + return std::nullopt; + if (OpC->isZero()) + continue; // No offset. + + // Handle struct indices, which add their field offset to the pointer. + if (StructType *STy = GTI.getStructTypeOrNull()) { + Offset += DL.getStructLayout(STy)->getElementOffset(OpC->getZExtValue()); + continue; + } + + // Otherwise, we have a sequential type like an array or fixed-length + // vector. Multiply the index by the ElementSize. + TypeSize Size = DL.getTypeAllocSize(GTI.getIndexedType()); + if (Size.isScalable()) + return std::nullopt; + Offset += Size.getFixedValue() * OpC->getSExtValue(); + } + + return Offset; +} + +std::optional Value::getPointerOffsetFrom(const Value *Other, + const DataLayout &DL) const { + const Value *Ptr1 = Other; + const Value *Ptr2 = this; + APInt Offset1(DL.getIndexTypeSizeInBits(Ptr1->getType()), 0); + APInt Offset2(DL.getIndexTypeSizeInBits(Ptr2->getType()), 0); + Ptr1 = Ptr1->stripAndAccumulateConstantOffsets(DL, Offset1, true); + Ptr2 = Ptr2->stripAndAccumulateConstantOffsets(DL, Offset2, true); + + // Handle the trivial case first. + if (Ptr1 == Ptr2) + return Offset2.getSExtValue() - Offset1.getSExtValue(); + + const GEPOperator *GEP1 = dyn_cast(Ptr1); + const GEPOperator *GEP2 = dyn_cast(Ptr2); + + // Right now we handle the case when Ptr1/Ptr2 are both GEPs with an identical + // base. After that base, they may have some number of common (and + // potentially variable) indices. After that they handle some constant + // offset, which determines their offset from each other. At this point, we + // handle no other case. + if (!GEP1 || !GEP2 || GEP1->getOperand(0) != GEP2->getOperand(0) || + GEP1->getSourceElementType() != GEP2->getSourceElementType()) + return std::nullopt; + + // Skip any common indices and track the GEP types. + unsigned Idx = 1; + for (; Idx != GEP1->getNumOperands() && Idx != GEP2->getNumOperands(); ++Idx) + if (GEP1->getOperand(Idx) != GEP2->getOperand(Idx)) + break; + + auto IOffset1 = getOffsetFromIndex(GEP1, Idx, DL); + auto IOffset2 = getOffsetFromIndex(GEP2, Idx, DL); + if (!IOffset1 || !IOffset2) + return std::nullopt; + return *IOffset2 - *IOffset1 + Offset2.getSExtValue() - + Offset1.getSExtValue(); +} + const Value *Value::DoPHITranslation(const BasicBlock *CurBB, const BasicBlock *PredBB) const { auto *PN = dyn_cast(this); Index: llvm/lib/Target/AArch64/AArch64StackTagging.cpp =================================================================== --- llvm/lib/Target/AArch64/AArch64StackTagging.cpp +++ llvm/lib/Target/AArch64/AArch64StackTagging.cpp @@ -380,7 +380,7 @@ // Check to see if this store is to a constant offset from the start ptr. std::optional Offset = - isPointerOffset(StartPtr, NextStore->getPointerOperand(), *DL); + NextStore->getPointerOperand()->getPointerOffsetFrom(StartPtr, *DL); if (!Offset) break; @@ -398,7 +398,7 @@ // Check to see if this store is to a constant offset from the start ptr. std::optional Offset = - isPointerOffset(StartPtr, MSI->getDest(), *DL); + MSI->getDest()->getPointerOffsetFrom(StartPtr, *DL); if (!Offset) break; Index: llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp =================================================================== --- llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp +++ llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp @@ -412,7 +412,7 @@ // Check to see if this store is to a constant offset from the start ptr. std::optional Offset = - isPointerOffset(StartPtr, NextStore->getPointerOperand(), DL); + NextStore->getPointerOperand()->getPointerOffsetFrom(StartPtr, DL); if (!Offset) break; @@ -426,7 +426,7 @@ // Check to see if this store is to a constant offset from the start ptr. std::optional Offset = - isPointerOffset(StartPtr, MSI->getDest(), DL); + MSI->getDest()->getPointerOffsetFrom(StartPtr, DL); if (!Offset) break;