Files
clang-p2996/llvm/lib/Analysis/Local.cpp
Jannik Silvanus 7954c57124 [IR] Fix GEP offset computations for vector GEPs (#75448)
Vectors are always bit-packed and don't respect the elements' alignment
requirements. This is different from arrays. This means offsets of
vector GEPs need to be computed differently than offsets of array GEPs.

This PR fixes many places that rely on an incorrect pattern
that always relies on `DL.getTypeAllocSize(GTI.getIndexedType())`.
We replace these by usages of  `GTI.getSequentialElementStride(DL)`, 
which is a new helper function added in this PR.

This changes behavior for GEPs into vectors with element types for which
the (bit) size and alloc size is different. This includes two cases:

* Types with a bit size that is not a multiple of a byte, e.g. i1.
GEPs into such vectors are questionable to begin with, as some elements
  are not even addressable.
* Overaligned types, e.g. i16 with 32-bit alignment.

Existing tests are unaffected, but a miscompilation of a new test is fixed.

---------

Co-authored-by: Nikita Popov <github@npopov.com>
2024-01-04 10:08:21 +01:00

81 lines
3.0 KiB
C++

//===- Local.cpp - Functions to perform local transformations -------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This family of functions perform various local transformations to the
// program.
//
//===----------------------------------------------------------------------===//
#include "llvm/Analysis/Utils/Local.h"
#include "llvm/ADT/Twine.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/GetElementPtrTypeIterator.h"
#include "llvm/IR/IRBuilder.h"
using namespace llvm;
Value *llvm::emitGEPOffset(IRBuilderBase *Builder, const DataLayout &DL,
User *GEP, bool NoAssumptions) {
GEPOperator *GEPOp = cast<GEPOperator>(GEP);
Type *IntIdxTy = DL.getIndexType(GEP->getType());
Value *Result = nullptr;
// If the GEP is inbounds, we know that none of the addressing operations will
// overflow in a signed sense.
bool isInBounds = GEPOp->isInBounds() && !NoAssumptions;
auto AddOffset = [&](Value *Offset) {
if (Result)
Result = Builder->CreateAdd(Result, Offset, GEP->getName() + ".offs",
false /*NUW*/, isInBounds /*NSW*/);
else
Result = Offset;
};
gep_type_iterator GTI = gep_type_begin(GEP);
for (User::op_iterator i = GEP->op_begin() + 1, e = GEP->op_end(); i != e;
++i, ++GTI) {
Value *Op = *i;
if (Constant *OpC = dyn_cast<Constant>(Op)) {
if (OpC->isZeroValue())
continue;
// Handle a struct index, which adds its field offset to the pointer.
if (StructType *STy = GTI.getStructTypeOrNull()) {
uint64_t OpValue = OpC->getUniqueInteger().getZExtValue();
uint64_t Size = DL.getStructLayout(STy)->getElementOffset(OpValue);
if (!Size)
continue;
AddOffset(ConstantInt::get(IntIdxTy, Size));
continue;
}
}
// Splat the index if needed.
if (IntIdxTy->isVectorTy() && !Op->getType()->isVectorTy())
Op = Builder->CreateVectorSplat(
cast<VectorType>(IntIdxTy)->getElementCount(), Op);
// Convert to correct type.
if (Op->getType() != IntIdxTy)
Op = Builder->CreateIntCast(Op, IntIdxTy, true, Op->getName() + ".c");
TypeSize TSize = GTI.getSequentialElementStride(DL);
if (TSize != TypeSize::getFixed(1)) {
Value *Scale = Builder->CreateTypeSize(IntIdxTy->getScalarType(), TSize);
if (IntIdxTy->isVectorTy())
Scale = Builder->CreateVectorSplat(
cast<VectorType>(IntIdxTy)->getElementCount(), Scale);
// We'll let instcombine(mul) convert this to a shl if possible.
Op = Builder->CreateMul(Op, Scale, GEP->getName() + ".idx", false /*NUW*/,
isInBounds /*NSW*/);
}
AddOffset(Op);
}
return Result ? Result : Constant::getNullValue(IntIdxTy);
}