forked from OSchip/llvm-project
[IR] Add helper to convert offset to GEP indices
We implement logic to convert a byte offset into a sequence of GEP indices for that offset in a number of places. This patch adds a DataLayout::getGEPIndicesForOffset() method, which implements the core logic. I've updated SROA, ConstantFolding and InstCombine to use it, and there's a few more places where it looks relevant. Differential Revision: https://reviews.llvm.org/D110043
This commit is contained in:
parent
4cf9bf6c9f
commit
dd0226561e
|
@ -579,6 +579,10 @@ public:
|
||||||
/// This is used to implement getelementptr.
|
/// This is used to implement getelementptr.
|
||||||
int64_t getIndexedOffsetInType(Type *ElemTy, ArrayRef<Value *> Indices) const;
|
int64_t getIndexedOffsetInType(Type *ElemTy, ArrayRef<Value *> Indices) const;
|
||||||
|
|
||||||
|
/// Get GEP indices to access Offset inside ElemTy. ElemTy is updated to be
|
||||||
|
/// the result element type and Offset to be the residual offset.
|
||||||
|
SmallVector<APInt> getGEPIndicesForOffset(Type *&ElemTy, APInt &Offset) const;
|
||||||
|
|
||||||
/// Returns a StructLayout object, indicating the alignment of the
|
/// Returns a StructLayout object, indicating the alignment of the
|
||||||
/// struct, its size, and the offsets of its fields.
|
/// struct, its size, and the offsets of its fields.
|
||||||
///
|
///
|
||||||
|
|
|
@ -985,8 +985,6 @@ Constant *SymbolicallyEvaluateGEP(const GEPOperator *GEP,
|
||||||
// we eliminate over-indexing of the notional static type array bounds.
|
// we eliminate over-indexing of the notional static type array bounds.
|
||||||
// This makes it easy to determine if the getelementptr is "inbounds".
|
// This makes it easy to determine if the getelementptr is "inbounds".
|
||||||
// Also, this helps GlobalOpt do SROA on GlobalVariables.
|
// Also, this helps GlobalOpt do SROA on GlobalVariables.
|
||||||
SmallVector<Constant *, 32> NewIdxs;
|
|
||||||
Type *Ty = PTy;
|
|
||||||
|
|
||||||
// For GEPs of GlobalValues, use the value type even for opaque pointers.
|
// For GEPs of GlobalValues, use the value type even for opaque pointers.
|
||||||
// Otherwise use an i8 GEP.
|
// Otherwise use an i8 GEP.
|
||||||
|
@ -997,69 +995,32 @@ Constant *SymbolicallyEvaluateGEP(const GEPOperator *GEP,
|
||||||
else
|
else
|
||||||
SrcElemTy = Type::getInt8Ty(Ptr->getContext());
|
SrcElemTy = Type::getInt8Ty(Ptr->getContext());
|
||||||
|
|
||||||
do {
|
if (!SrcElemTy->isSized())
|
||||||
if (!Ty->isStructTy()) {
|
return nullptr;
|
||||||
if (Ty->isPointerTy()) {
|
|
||||||
// The only pointer indexing we'll do is on the first index of the GEP.
|
|
||||||
if (!NewIdxs.empty())
|
|
||||||
break;
|
|
||||||
|
|
||||||
Ty = SrcElemTy;
|
Type *ElemTy = SrcElemTy;
|
||||||
|
SmallVector<APInt> Indices = DL.getGEPIndicesForOffset(ElemTy, Offset);
|
||||||
// Only handle pointers to sized types, not pointers to functions.
|
|
||||||
if (!Ty->isSized())
|
|
||||||
return nullptr;
|
|
||||||
} else {
|
|
||||||
Type *NextTy = GetElementPtrInst::getTypeAtIndex(Ty, (uint64_t)0);
|
|
||||||
if (!NextTy)
|
|
||||||
break;
|
|
||||||
Ty = NextTy;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine which element of the array the offset points into.
|
|
||||||
APInt ElemSize(BitWidth, DL.getTypeAllocSize(Ty));
|
|
||||||
if (ElemSize == 0) {
|
|
||||||
// The element size is 0. This may be [0 x Ty]*, so just use a zero
|
|
||||||
// index for this level and proceed to the next level to see if it can
|
|
||||||
// accommodate the offset.
|
|
||||||
NewIdxs.push_back(ConstantInt::get(IntIdxTy, 0));
|
|
||||||
} else {
|
|
||||||
// The element size is non-zero divide the offset by the element
|
|
||||||
// size (rounding down), to compute the index at this level.
|
|
||||||
bool Overflow;
|
|
||||||
APInt NewIdx = Offset.sdiv_ov(ElemSize, Overflow);
|
|
||||||
if (Overflow)
|
|
||||||
break;
|
|
||||||
Offset -= NewIdx * ElemSize;
|
|
||||||
NewIdxs.push_back(ConstantInt::get(IntIdxTy, NewIdx));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
auto *STy = cast<StructType>(Ty);
|
|
||||||
// If we end up with an offset that isn't valid for this struct type, we
|
|
||||||
// can't re-form this GEP in a regular form, so bail out. The pointer
|
|
||||||
// operand likely went through casts that are necessary to make the GEP
|
|
||||||
// sensible.
|
|
||||||
const StructLayout &SL = *DL.getStructLayout(STy);
|
|
||||||
if (Offset.isNegative() || Offset.uge(SL.getSizeInBytes()))
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Determine which field of the struct the offset points into. The
|
|
||||||
// getZExtValue is fine as we've already ensured that the offset is
|
|
||||||
// within the range representable by the StructLayout API.
|
|
||||||
unsigned ElIdx = SL.getElementContainingOffset(Offset.getZExtValue());
|
|
||||||
NewIdxs.push_back(ConstantInt::get(Type::getInt32Ty(Ty->getContext()),
|
|
||||||
ElIdx));
|
|
||||||
Offset -= APInt(BitWidth, SL.getElementOffset(ElIdx));
|
|
||||||
Ty = STy->getTypeAtIndex(ElIdx);
|
|
||||||
}
|
|
||||||
} while (Ty != ResElemTy);
|
|
||||||
|
|
||||||
// If we haven't used up the entire offset by descending the static
|
|
||||||
// type, then the offset is pointing into the middle of an indivisible
|
|
||||||
// member, so we can't simplify it.
|
|
||||||
if (Offset != 0)
|
if (Offset != 0)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
// Try to add additional zero indices to reach the desired result element
|
||||||
|
// type.
|
||||||
|
// TODO: Should we avoid extra zero indices if ResElemTy can't be reached and
|
||||||
|
// we'll have to insert a bitcast anyway?
|
||||||
|
while (ElemTy != ResElemTy) {
|
||||||
|
Type *NextTy = GetElementPtrInst::getTypeAtIndex(ElemTy, (uint64_t)0);
|
||||||
|
if (!NextTy)
|
||||||
|
break;
|
||||||
|
|
||||||
|
Indices.push_back(APInt::getZero(isa<StructType>(ElemTy) ? 32 : BitWidth));
|
||||||
|
ElemTy = NextTy;
|
||||||
|
}
|
||||||
|
|
||||||
|
SmallVector<Constant *, 32> NewIdxs;
|
||||||
|
for (const APInt &Index : Indices)
|
||||||
|
NewIdxs.push_back(ConstantInt::get(
|
||||||
|
Type::getIntNTy(Ptr->getContext(), Index.getBitWidth()), Index));
|
||||||
|
|
||||||
// Preserve the inrange index from the innermost GEP if possible. We must
|
// Preserve the inrange index from the innermost GEP if possible. We must
|
||||||
// have calculated the same indices up to and including the inrange index.
|
// have calculated the same indices up to and including the inrange index.
|
||||||
Optional<unsigned> InRangeIndex;
|
Optional<unsigned> InRangeIndex;
|
||||||
|
@ -1075,8 +1036,9 @@ Constant *SymbolicallyEvaluateGEP(const GEPOperator *GEP,
|
||||||
// Create a GEP.
|
// Create a GEP.
|
||||||
Constant *C = ConstantExpr::getGetElementPtr(SrcElemTy, Ptr, NewIdxs,
|
Constant *C = ConstantExpr::getGetElementPtr(SrcElemTy, Ptr, NewIdxs,
|
||||||
InBounds, InRangeIndex);
|
InBounds, InRangeIndex);
|
||||||
assert(cast<PointerType>(C->getType())->isOpaqueOrPointeeTypeMatches(Ty) &&
|
assert(
|
||||||
"Computed GetElementPtr has unexpected type!");
|
cast<PointerType>(C->getType())->isOpaqueOrPointeeTypeMatches(ElemTy) &&
|
||||||
|
"Computed GetElementPtr has unexpected type!");
|
||||||
|
|
||||||
// If we ended up indexing a member with a type that doesn't match
|
// If we ended up indexing a member with a type that doesn't match
|
||||||
// the type of what the original indices indexed, add a cast.
|
// the type of what the original indices indexed, add a cast.
|
||||||
|
|
|
@ -896,6 +896,68 @@ int64_t DataLayout::getIndexedOffsetInType(Type *ElemTy,
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void addElementIndex(SmallVectorImpl<APInt> &Indices, TypeSize ElemSize,
|
||||||
|
APInt &Offset) {
|
||||||
|
// Skip over scalable or zero size elements.
|
||||||
|
if (ElemSize.isScalable() || ElemSize == 0) {
|
||||||
|
Indices.push_back(APInt::getZero(Offset.getBitWidth()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
APInt Index = Offset.sdiv(ElemSize);
|
||||||
|
Offset -= Index * ElemSize;
|
||||||
|
if (Offset.isNegative()) {
|
||||||
|
// Prefer a positive remaining offset to allow struct indexing.
|
||||||
|
--Index;
|
||||||
|
Offset += ElemSize;
|
||||||
|
assert(Offset.isNonNegative() && "Remaining offset shouldn't be negative");
|
||||||
|
}
|
||||||
|
Indices.push_back(Index);
|
||||||
|
}
|
||||||
|
|
||||||
|
SmallVector<APInt> DataLayout::getGEPIndicesForOffset(Type *&ElemTy,
|
||||||
|
APInt &Offset) const {
|
||||||
|
assert(ElemTy->isSized() && "Element type must be sized");
|
||||||
|
SmallVector<APInt> Indices;
|
||||||
|
addElementIndex(Indices, getTypeAllocSize(ElemTy), Offset);
|
||||||
|
while (Offset != 0) {
|
||||||
|
if (auto *ArrTy = dyn_cast<ArrayType>(ElemTy)) {
|
||||||
|
ElemTy = ArrTy->getElementType();
|
||||||
|
addElementIndex(Indices, getTypeAllocSize(ElemTy), Offset);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto *VecTy = dyn_cast<VectorType>(ElemTy)) {
|
||||||
|
ElemTy = VecTy->getElementType();
|
||||||
|
unsigned ElemSizeInBits = getTypeSizeInBits(ElemTy).getFixedSize();
|
||||||
|
// GEPs over non-multiple of 8 size vector elements are invalid.
|
||||||
|
if (ElemSizeInBits % 8 != 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
addElementIndex(Indices, TypeSize::Fixed(ElemSizeInBits / 8), Offset);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto *STy = dyn_cast<StructType>(ElemTy)) {
|
||||||
|
const StructLayout *SL = getStructLayout(STy);
|
||||||
|
uint64_t IntOffset = Offset.getZExtValue();
|
||||||
|
if (IntOffset >= SL->getSizeInBytes())
|
||||||
|
break;
|
||||||
|
|
||||||
|
unsigned Index = SL->getElementContainingOffset(IntOffset);
|
||||||
|
Offset -= SL->getElementOffset(Index);
|
||||||
|
ElemTy = STy->getElementType(Index);
|
||||||
|
Indices.push_back(APInt(32, Index));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can't index into non-aggregate type.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Indices;
|
||||||
|
}
|
||||||
|
|
||||||
/// getPreferredAlign - Return the preferred alignment of the specified global.
|
/// getPreferredAlign - Return the preferred alignment of the specified global.
|
||||||
/// This includes an explicitly requested alignment (if the global has one).
|
/// This includes an explicitly requested alignment (if the global has one).
|
||||||
Align DataLayout::getPreferredAlign(const GlobalVariable *GV) const {
|
Align DataLayout::getPreferredAlign(const GlobalVariable *GV) const {
|
||||||
|
|
|
@ -1269,61 +1269,19 @@ Instruction *InstCombinerImpl::foldBinOpIntoSelectOrPhi(BinaryOperator &I) {
|
||||||
/// specified offset. If so, fill them into NewIndices and return the resultant
|
/// specified offset. If so, fill them into NewIndices and return the resultant
|
||||||
/// element type, otherwise return null.
|
/// element type, otherwise return null.
|
||||||
Type *
|
Type *
|
||||||
InstCombinerImpl::FindElementAtOffset(PointerType *PtrTy, int64_t Offset,
|
InstCombinerImpl::FindElementAtOffset(PointerType *PtrTy, int64_t IntOffset,
|
||||||
SmallVectorImpl<Value *> &NewIndices) {
|
SmallVectorImpl<Value *> &NewIndices) {
|
||||||
Type *Ty = PtrTy->getElementType();
|
Type *Ty = PtrTy->getElementType();
|
||||||
if (!Ty->isSized())
|
if (!Ty->isSized())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// Start with the index over the outer type. Note that the type size
|
APInt Offset(DL.getIndexTypeSizeInBits(PtrTy), IntOffset);
|
||||||
// might be zero (even if the offset isn't zero) if the indexed type
|
SmallVector<APInt> Indices = DL.getGEPIndicesForOffset(Ty, Offset);
|
||||||
// is something like [0 x {int, int}]
|
if (!Offset.isZero())
|
||||||
Type *IndexTy = DL.getIndexType(PtrTy);
|
return nullptr;
|
||||||
int64_t FirstIdx = 0;
|
|
||||||
if (int64_t TySize = DL.getTypeAllocSize(Ty)) {
|
|
||||||
FirstIdx = Offset/TySize;
|
|
||||||
Offset -= FirstIdx*TySize;
|
|
||||||
|
|
||||||
// Handle hosts where % returns negative instead of values [0..TySize).
|
|
||||||
if (Offset < 0) {
|
|
||||||
--FirstIdx;
|
|
||||||
Offset += TySize;
|
|
||||||
assert(Offset >= 0);
|
|
||||||
}
|
|
||||||
assert((uint64_t)Offset < (uint64_t)TySize && "Out of range offset");
|
|
||||||
}
|
|
||||||
|
|
||||||
NewIndices.push_back(ConstantInt::get(IndexTy, FirstIdx));
|
|
||||||
|
|
||||||
// Index into the types. If we fail, set OrigBase to null.
|
|
||||||
while (Offset) {
|
|
||||||
// Indexing into tail padding between struct/array elements.
|
|
||||||
if (uint64_t(Offset * 8) >= DL.getTypeSizeInBits(Ty))
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (StructType *STy = dyn_cast<StructType>(Ty)) {
|
|
||||||
const StructLayout *SL = DL.getStructLayout(STy);
|
|
||||||
assert(Offset < (int64_t)SL->getSizeInBytes() &&
|
|
||||||
"Offset must stay within the indexed type");
|
|
||||||
|
|
||||||
unsigned Elt = SL->getElementContainingOffset(Offset);
|
|
||||||
NewIndices.push_back(ConstantInt::get(Type::getInt32Ty(Ty->getContext()),
|
|
||||||
Elt));
|
|
||||||
|
|
||||||
Offset -= SL->getElementOffset(Elt);
|
|
||||||
Ty = STy->getElementType(Elt);
|
|
||||||
} else if (ArrayType *AT = dyn_cast<ArrayType>(Ty)) {
|
|
||||||
uint64_t EltSize = DL.getTypeAllocSize(AT->getElementType());
|
|
||||||
assert(EltSize && "Cannot index into a zero-sized array");
|
|
||||||
NewIndices.push_back(ConstantInt::get(IndexTy,Offset/EltSize));
|
|
||||||
Offset %= EltSize;
|
|
||||||
Ty = AT->getElementType();
|
|
||||||
} else {
|
|
||||||
// Otherwise, we can't index into the middle of this atomic type, bail.
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
for (const APInt &Index : Indices)
|
||||||
|
NewIndices.push_back(Builder.getInt(Index));
|
||||||
return Ty;
|
return Ty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1483,76 +1483,6 @@ static Value *getNaturalGEPWithType(IRBuilderTy &IRB, const DataLayout &DL,
|
||||||
return buildGEP(IRB, BasePtr, Indices, NamePrefix);
|
return buildGEP(IRB, BasePtr, Indices, NamePrefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recursively compute indices for a natural GEP.
|
|
||||||
///
|
|
||||||
/// This is the recursive step for getNaturalGEPWithOffset that walks down the
|
|
||||||
/// element types adding appropriate indices for the GEP.
|
|
||||||
static Value *getNaturalGEPRecursively(IRBuilderTy &IRB, const DataLayout &DL,
|
|
||||||
Value *Ptr, Type *Ty, APInt &Offset,
|
|
||||||
Type *TargetTy,
|
|
||||||
SmallVectorImpl<Value *> &Indices,
|
|
||||||
const Twine &NamePrefix) {
|
|
||||||
if (Offset == 0)
|
|
||||||
return getNaturalGEPWithType(IRB, DL, Ptr, Ty, TargetTy, Indices,
|
|
||||||
NamePrefix);
|
|
||||||
|
|
||||||
// We can't recurse through pointer types.
|
|
||||||
if (Ty->isPointerTy())
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
// We try to analyze GEPs over vectors here, but note that these GEPs are
|
|
||||||
// extremely poorly defined currently. The long-term goal is to remove GEPing
|
|
||||||
// over a vector from the IR completely.
|
|
||||||
if (VectorType *VecTy = dyn_cast<VectorType>(Ty)) {
|
|
||||||
unsigned ElementSizeInBits =
|
|
||||||
DL.getTypeSizeInBits(VecTy->getScalarType()).getFixedSize();
|
|
||||||
if (ElementSizeInBits % 8 != 0) {
|
|
||||||
// GEPs over non-multiple of 8 size vector elements are invalid.
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
APInt ElementSize(Offset.getBitWidth(), ElementSizeInBits / 8);
|
|
||||||
APInt NumSkippedElements = Offset.sdiv(ElementSize);
|
|
||||||
if (NumSkippedElements.ugt(cast<FixedVectorType>(VecTy)->getNumElements()))
|
|
||||||
return nullptr;
|
|
||||||
Offset -= NumSkippedElements * ElementSize;
|
|
||||||
Indices.push_back(IRB.getInt(NumSkippedElements));
|
|
||||||
return getNaturalGEPRecursively(IRB, DL, Ptr, VecTy->getElementType(),
|
|
||||||
Offset, TargetTy, Indices, NamePrefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ArrayType *ArrTy = dyn_cast<ArrayType>(Ty)) {
|
|
||||||
Type *ElementTy = ArrTy->getElementType();
|
|
||||||
APInt ElementSize(Offset.getBitWidth(),
|
|
||||||
DL.getTypeAllocSize(ElementTy).getFixedSize());
|
|
||||||
APInt NumSkippedElements = Offset.sdiv(ElementSize);
|
|
||||||
if (NumSkippedElements.ugt(ArrTy->getNumElements()))
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
Offset -= NumSkippedElements * ElementSize;
|
|
||||||
Indices.push_back(IRB.getInt(NumSkippedElements));
|
|
||||||
return getNaturalGEPRecursively(IRB, DL, Ptr, ElementTy, Offset, TargetTy,
|
|
||||||
Indices, NamePrefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
StructType *STy = dyn_cast<StructType>(Ty);
|
|
||||||
if (!STy)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
const StructLayout *SL = DL.getStructLayout(STy);
|
|
||||||
uint64_t StructOffset = Offset.getZExtValue();
|
|
||||||
if (StructOffset >= SL->getSizeInBytes())
|
|
||||||
return nullptr;
|
|
||||||
unsigned Index = SL->getElementContainingOffset(StructOffset);
|
|
||||||
Offset -= APInt(Offset.getBitWidth(), SL->getElementOffset(Index));
|
|
||||||
Type *ElementTy = STy->getElementType(Index);
|
|
||||||
if (Offset.uge(DL.getTypeAllocSize(ElementTy).getFixedSize()))
|
|
||||||
return nullptr; // The offset points into alignment padding.
|
|
||||||
|
|
||||||
Indices.push_back(IRB.getInt32(Index));
|
|
||||||
return getNaturalGEPRecursively(IRB, DL, Ptr, ElementTy, Offset, TargetTy,
|
|
||||||
Indices, NamePrefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a natural GEP from a base pointer to a particular offset and
|
/// Get a natural GEP from a base pointer to a particular offset and
|
||||||
/// resulting in a particular type.
|
/// resulting in a particular type.
|
||||||
///
|
///
|
||||||
|
@ -1577,18 +1507,15 @@ static Value *getNaturalGEPWithOffset(IRBuilderTy &IRB, const DataLayout &DL,
|
||||||
Type *ElementTy = Ty->getElementType();
|
Type *ElementTy = Ty->getElementType();
|
||||||
if (!ElementTy->isSized())
|
if (!ElementTy->isSized())
|
||||||
return nullptr; // We can't GEP through an unsized element.
|
return nullptr; // We can't GEP through an unsized element.
|
||||||
if (isa<ScalableVectorType>(ElementTy))
|
|
||||||
return nullptr;
|
|
||||||
APInt ElementSize(Offset.getBitWidth(),
|
|
||||||
DL.getTypeAllocSize(ElementTy).getFixedSize());
|
|
||||||
if (ElementSize == 0)
|
|
||||||
return nullptr; // Zero-length arrays can't help us build a natural GEP.
|
|
||||||
APInt NumSkippedElements = Offset.sdiv(ElementSize);
|
|
||||||
|
|
||||||
Offset -= NumSkippedElements * ElementSize;
|
SmallVector<APInt> IntIndices = DL.getGEPIndicesForOffset(ElementTy, Offset);
|
||||||
Indices.push_back(IRB.getInt(NumSkippedElements));
|
if (Offset != 0)
|
||||||
return getNaturalGEPRecursively(IRB, DL, Ptr, ElementTy, Offset, TargetTy,
|
return nullptr;
|
||||||
Indices, NamePrefix);
|
|
||||||
|
for (const APInt &Index : IntIndices)
|
||||||
|
Indices.push_back(IRB.getInt(Index));
|
||||||
|
return getNaturalGEPWithType(IRB, DL, Ptr, ElementTy, TargetTy, Indices,
|
||||||
|
NamePrefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute an adjusted pointer from Ptr by Offset bytes where the
|
/// Compute an adjusted pointer from Ptr by Offset bytes where the
|
||||||
|
|
|
@ -794,7 +794,7 @@ define i32 @test35() nounwind {
|
||||||
; Don't treat signed offsets as unsigned.
|
; Don't treat signed offsets as unsigned.
|
||||||
define i8* @test36() nounwind {
|
define i8* @test36() nounwind {
|
||||||
; CHECK-LABEL: @test36(
|
; CHECK-LABEL: @test36(
|
||||||
; CHECK-NEXT: ret i8* getelementptr ([11 x i8], [11 x i8]* @array, i64 0, i64 -1)
|
; CHECK-NEXT: ret i8* getelementptr ([11 x i8], [11 x i8]* @array, i64 -1, i64 10)
|
||||||
;
|
;
|
||||||
ret i8* getelementptr ([11 x i8], [11 x i8]* @array, i32 0, i64 -1)
|
ret i8* getelementptr ([11 x i8], [11 x i8]* @array, i32 0, i64 -1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,11 +71,10 @@ define <vscale x 4 x i32> @cast_alloca_to_svint32_t(<vscale x 4 x i32> %type.coe
|
||||||
define <vscale x 4 x i32> @cast_alloca_from_svint32_t() {
|
define <vscale x 4 x i32> @cast_alloca_from_svint32_t() {
|
||||||
; CHECK-LABEL: @cast_alloca_from_svint32_t(
|
; CHECK-LABEL: @cast_alloca_from_svint32_t(
|
||||||
; CHECK-NEXT: [[RETVAL_COERCE:%.*]] = alloca <vscale x 4 x i32>, align 16
|
; CHECK-NEXT: [[RETVAL_COERCE:%.*]] = alloca <vscale x 4 x i32>, align 16
|
||||||
; CHECK-NEXT: [[TMP1:%.*]] = bitcast <vscale x 4 x i32>* [[RETVAL_COERCE]] to i8*
|
; CHECK-NEXT: [[RETVAL_0__SROA_CAST:%.*]] = bitcast <vscale x 4 x i32>* [[RETVAL_COERCE]] to <16 x i32>*
|
||||||
; CHECK-NEXT: [[RETVAL_0__SROA_CAST:%.*]] = bitcast i8* [[TMP1]] to <16 x i32>*
|
|
||||||
; CHECK-NEXT: store <16 x i32> undef, <16 x i32>* [[RETVAL_0__SROA_CAST]], align 16
|
; CHECK-NEXT: store <16 x i32> undef, <16 x i32>* [[RETVAL_0__SROA_CAST]], align 16
|
||||||
; CHECK-NEXT: [[TMP2:%.*]] = load <vscale x 4 x i32>, <vscale x 4 x i32>* [[RETVAL_COERCE]], align 16
|
; CHECK-NEXT: [[TMP1:%.*]] = load <vscale x 4 x i32>, <vscale x 4 x i32>* [[RETVAL_COERCE]], align 16
|
||||||
; CHECK-NEXT: ret <vscale x 4 x i32> [[TMP2]]
|
; CHECK-NEXT: ret <vscale x 4 x i32> [[TMP1]]
|
||||||
;
|
;
|
||||||
%retval = alloca <16 x i32>
|
%retval = alloca <16 x i32>
|
||||||
%retval.coerce = alloca <vscale x 4 x i32>
|
%retval.coerce = alloca <vscale x 4 x i32>
|
||||||
|
|
Loading…
Reference in New Issue