[Clang][CodeGen] Preserve alignment information for pointer arithmetics (#152575)
Previously, the alignment of pointer arithmetics was inferred from the
pointee type, losing the alignment information from its operands:
503c0908c3/clang/lib/CodeGen/CGExpr.cpp (L1446-L1449)
This patch preserves alignment information for pointer arithmetics `P
+/- C`, to match the behavior of identical array subscript `&P[C]`:
https://godbolt.org/z/xx1hfTrx4.
Closes https://github.com/llvm/llvm-project/issues/152330. Although the
motivating case can be fixed by
https://github.com/llvm/llvm-project/pull/145733, the alignment cannot
be recovered without a dominating memory access with larger alignment.
This commit is contained in:
parent
41aba9ef3b
commit
5569bf26f0
@ -1325,6 +1325,57 @@ void CodeGenModule::EmitExplicitCastExprType(const ExplicitCastExpr *E,
|
||||
// LValue Expression Emission
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
static CharUnits getArrayElementAlign(CharUnits arrayAlign, llvm::Value *idx,
|
||||
CharUnits eltSize) {
|
||||
// If we have a constant index, we can use the exact offset of the
|
||||
// element we're accessing.
|
||||
if (auto *constantIdx = dyn_cast<llvm::ConstantInt>(idx)) {
|
||||
CharUnits offset = constantIdx->getZExtValue() * eltSize;
|
||||
return arrayAlign.alignmentAtOffset(offset);
|
||||
}
|
||||
|
||||
// Otherwise, use the worst-case alignment for any element.
|
||||
return arrayAlign.alignmentOfArrayElement(eltSize);
|
||||
}
|
||||
|
||||
/// Emit pointer + index arithmetic.
|
||||
static Address emitPointerArithmetic(CodeGenFunction &CGF,
|
||||
const BinaryOperator *BO,
|
||||
LValueBaseInfo *BaseInfo,
|
||||
TBAAAccessInfo *TBAAInfo,
|
||||
KnownNonNull_t IsKnownNonNull) {
|
||||
assert(BO->isAdditiveOp() && "Expect an addition or subtraction.");
|
||||
Expr *pointerOperand = BO->getLHS();
|
||||
Expr *indexOperand = BO->getRHS();
|
||||
bool isSubtraction = BO->getOpcode() == BO_Sub;
|
||||
|
||||
Address BaseAddr = Address::invalid();
|
||||
llvm::Value *index = nullptr;
|
||||
// In a subtraction, the LHS is always the pointer.
|
||||
// Note: do not change the evaluation order.
|
||||
if (!isSubtraction && !pointerOperand->getType()->isAnyPointerType()) {
|
||||
std::swap(pointerOperand, indexOperand);
|
||||
index = CGF.EmitScalarExpr(indexOperand);
|
||||
BaseAddr = CGF.EmitPointerWithAlignment(pointerOperand, BaseInfo, TBAAInfo,
|
||||
NotKnownNonNull);
|
||||
} else {
|
||||
BaseAddr = CGF.EmitPointerWithAlignment(pointerOperand, BaseInfo, TBAAInfo,
|
||||
NotKnownNonNull);
|
||||
index = CGF.EmitScalarExpr(indexOperand);
|
||||
}
|
||||
|
||||
llvm::Value *pointer = BaseAddr.getBasePointer();
|
||||
llvm::Value *Res = CGF.EmitPointerArithmetic(
|
||||
BO, pointerOperand, pointer, indexOperand, index, isSubtraction);
|
||||
QualType PointeeTy = BO->getType()->getPointeeType();
|
||||
CharUnits Align =
|
||||
getArrayElementAlign(BaseAddr.getAlignment(), index,
|
||||
CGF.getContext().getTypeSizeInChars(PointeeTy));
|
||||
return Address(Res, CGF.ConvertTypeForMem(PointeeTy), Align,
|
||||
CGF.CGM.getPointerAuthInfoForPointeeType(PointeeTy),
|
||||
/*Offset=*/nullptr, IsKnownNonNull);
|
||||
}
|
||||
|
||||
static Address EmitPointerWithAlignment(const Expr *E, LValueBaseInfo *BaseInfo,
|
||||
TBAAAccessInfo *TBAAInfo,
|
||||
KnownNonNull_t IsKnownNonNull,
|
||||
@ -1387,6 +1438,7 @@ static Address EmitPointerWithAlignment(const Expr *E, LValueBaseInfo *BaseInfo,
|
||||
if (CE->getCastKind() == CK_AddressSpaceConversion)
|
||||
Addr = CGF.Builder.CreateAddrSpaceCast(
|
||||
Addr, CGF.ConvertType(E->getType()), ElemTy);
|
||||
|
||||
return CGF.authPointerToPointerCast(Addr, CE->getSubExpr()->getType(),
|
||||
CE->getType());
|
||||
}
|
||||
@ -1447,6 +1499,12 @@ static Address EmitPointerWithAlignment(const Expr *E, LValueBaseInfo *BaseInfo,
|
||||
}
|
||||
}
|
||||
|
||||
// Pointer arithmetic: pointer +/- index.
|
||||
if (auto *BO = dyn_cast<BinaryOperator>(E)) {
|
||||
if (BO->isAdditiveOp())
|
||||
return emitPointerArithmetic(CGF, BO, BaseInfo, TBAAInfo, IsKnownNonNull);
|
||||
}
|
||||
|
||||
// TODO: conditional operators, comma.
|
||||
|
||||
// Otherwise, use the alignment of the type.
|
||||
@ -4236,21 +4294,6 @@ static Address emitArraySubscriptGEP(CodeGenFunction &CGF, Address addr,
|
||||
}
|
||||
}
|
||||
|
||||
static CharUnits getArrayElementAlign(CharUnits arrayAlign,
|
||||
llvm::Value *idx,
|
||||
CharUnits eltSize) {
|
||||
// If we have a constant index, we can use the exact offset of the
|
||||
// element we're accessing.
|
||||
if (auto constantIdx = dyn_cast<llvm::ConstantInt>(idx)) {
|
||||
CharUnits offset = constantIdx->getZExtValue() * eltSize;
|
||||
return arrayAlign.alignmentAtOffset(offset);
|
||||
|
||||
// Otherwise, use the worst-case alignment for any element.
|
||||
} else {
|
||||
return arrayAlign.alignmentOfArrayElement(eltSize);
|
||||
}
|
||||
}
|
||||
|
||||
static QualType getFixedSizeElementType(const ASTContext &ctx,
|
||||
const VariableArrayType *vla) {
|
||||
QualType eltType;
|
||||
|
||||
@ -4186,7 +4186,9 @@ Value *ScalarExprEmitter::EmitOverflowCheckedBinOp(const BinOpInfo &Ops) {
|
||||
return phi;
|
||||
}
|
||||
|
||||
/// This function is used for BO_Add/BO_Sub/BO_AddAssign/BO_SubAssign.
|
||||
/// BO_Add/BO_Sub are handled by EmitPointerWithAlignment to preserve alignment
|
||||
/// information.
|
||||
/// This function is used for BO_AddAssign/BO_SubAssign.
|
||||
static Value *emitPointerArithmetic(CodeGenFunction &CGF, const BinOpInfo &op,
|
||||
bool isSubtraction) {
|
||||
// Must have binary (not unary) expr here. Unary pointer
|
||||
|
||||
@ -55,7 +55,7 @@ int align3_x0 = __alignof(((struct s3*) 0)->x[0]);
|
||||
// CHECK: load i32, ptr %{{.*}}, align 1
|
||||
// CHECK: }
|
||||
// CHECK-LABEL: define{{.*}} i32 @f0_b
|
||||
// CHECK: load i32, ptr %{{.*}}, align 4
|
||||
// CHECK: load i32, ptr %{{.*}}, align 1
|
||||
// CHECK: }
|
||||
int f0_a(struct s0 *a) {
|
||||
return a->x[1];
|
||||
@ -100,7 +100,7 @@ int f1_d(struct s1 *a) {
|
||||
// CHECK: load i32, ptr %{{.*}}, align 1
|
||||
// CHECK: }
|
||||
// CHECK-LABEL: define{{.*}} i32 @f2_b
|
||||
// CHECK: load i32, ptr %{{.*}}, align 4
|
||||
// CHECK: load i32, ptr %{{.*}}, align 1
|
||||
// CHECK: }
|
||||
// CHECK-LABEL: define{{.*}} i32 @f2_c
|
||||
// CHECK: load i32, ptr %{{.*}}, align 1
|
||||
@ -125,7 +125,7 @@ int f2_d(struct s2 *a) {
|
||||
// CHECK: load i32, ptr %{{.*}}, align 1
|
||||
// CHECK: }
|
||||
// CHECK-LABEL: define{{.*}} i32 @f3_b
|
||||
// CHECK: load i32, ptr %{{.*}}, align 4
|
||||
// CHECK: load i32, ptr %{{.*}}, align 1
|
||||
// CHECK: }
|
||||
// CHECK-LABEL: define{{.*}} i32 @f3_c
|
||||
// CHECK: load i32, ptr %{{.*}}, align 1
|
||||
|
||||
83
clang/test/CodeGen/pointer-arithmetic-align.c
Normal file
83
clang/test/CodeGen/pointer-arithmetic-align.c
Normal file
@ -0,0 +1,83 @@
|
||||
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
|
||||
// RUN: %clang_cc1 -O1 -triple=x86_64-unknown-linux %s -emit-llvm -o - | FileCheck %s
|
||||
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
|
||||
struct a {
|
||||
uint64_t b;
|
||||
uint8_t block[16];
|
||||
};
|
||||
|
||||
// CHECK-LABEL: define dso_local void @ptradd_0(
|
||||
// CHECK-SAME: ptr noundef writeonly captures(none) initializes((8, 9)) [[CTX:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
|
||||
// CHECK-NEXT: [[ENTRY:.*:]]
|
||||
// CHECK-NEXT: [[BLOCK:%.*]] = getelementptr inbounds nuw i8, ptr [[CTX]], i64 8
|
||||
// CHECK-NEXT: store i8 0, ptr [[BLOCK]], align 8, !tbaa [[TBAA2:![0-9]+]]
|
||||
// CHECK-NEXT: ret void
|
||||
//
|
||||
void ptradd_0(struct a *ctx) {
|
||||
*(ctx->block + 0) = 0;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define dso_local void @ptradd_4(
|
||||
// CHECK-SAME: ptr noundef writeonly captures(none) initializes((12, 13)) [[CTX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// CHECK-NEXT: [[ENTRY:.*:]]
|
||||
// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[CTX]], i64 12
|
||||
// CHECK-NEXT: store i8 0, ptr [[ADD_PTR]], align 4, !tbaa [[TBAA2]]
|
||||
// CHECK-NEXT: ret void
|
||||
//
|
||||
void ptradd_4(struct a *ctx) {
|
||||
*(ctx->block + 4) = 0;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define dso_local void @ptradd_8(
|
||||
// CHECK-SAME: ptr noundef writeonly captures(none) initializes((16, 17)) [[CTX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// CHECK-NEXT: [[ENTRY:.*:]]
|
||||
// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[CTX]], i64 16
|
||||
// CHECK-NEXT: store i8 0, ptr [[ADD_PTR]], align 8, !tbaa [[TBAA2]]
|
||||
// CHECK-NEXT: ret void
|
||||
//
|
||||
void ptradd_8(struct a *ctx) {
|
||||
*(ctx->block + 8) = 0;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define dso_local void @ptradd_8_commuted(
|
||||
// CHECK-SAME: ptr noundef writeonly captures(none) initializes((16, 17)) [[CTX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// CHECK-NEXT: [[ENTRY:.*:]]
|
||||
// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[CTX]], i64 16
|
||||
// CHECK-NEXT: store i8 0, ptr [[ADD_PTR]], align 8, !tbaa [[TBAA2]]
|
||||
// CHECK-NEXT: ret void
|
||||
//
|
||||
void ptradd_8_commuted(struct a *ctx) {
|
||||
*(8 + ctx->block) = 0;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define dso_local void @ptrsub_4(
|
||||
// CHECK-SAME: ptr noundef writeonly captures(none) initializes((8, 9)) [[CTX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// CHECK-NEXT: [[ENTRY:.*:]]
|
||||
// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[CTX]], i64 8
|
||||
// CHECK-NEXT: store i8 0, ptr [[ADD_PTR]], align 4, !tbaa [[TBAA2]]
|
||||
// CHECK-NEXT: ret void
|
||||
//
|
||||
void ptrsub_4(struct a *ctx) {
|
||||
*(&ctx->block[4] - 4) = 0;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define dso_local void @neg_ptradd_var_index(
|
||||
// CHECK-SAME: ptr noundef writeonly captures(none) [[CTX:%.*]], i8 noundef zeroext [[IDX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// CHECK-NEXT: [[ENTRY:.*:]]
|
||||
// CHECK-NEXT: [[BLOCK:%.*]] = getelementptr inbounds nuw i8, ptr [[CTX]], i64 8
|
||||
// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i8 [[IDX]] to i64
|
||||
// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[BLOCK]], i64 [[IDX_EXT]]
|
||||
// CHECK-NEXT: store i8 0, ptr [[ADD_PTR]], align 1, !tbaa [[TBAA2]]
|
||||
// CHECK-NEXT: ret void
|
||||
//
|
||||
void neg_ptradd_var_index(struct a *ctx, uint8_t idx) {
|
||||
*(ctx->block + idx) = 0;
|
||||
}
|
||||
//.
|
||||
// CHECK: [[TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0}
|
||||
// CHECK: [[META3]] = !{!"omnipotent char", [[META4:![0-9]+]], i64 0}
|
||||
// CHECK: [[META4]] = !{!"Simple C/C++ TBAA"}
|
||||
//.
|
||||
@ -18,7 +18,7 @@ struct X { int z[17]; };
|
||||
// CHECK-NEXT: store i8 [[TMP0]], ptr [[ADD_PTR]], align 1
|
||||
// CHECK-NEXT: [[TMP1:%.*]] = load i8, ptr [[Y_ADDR_ASCAST]], align 1
|
||||
// CHECK-NEXT: [[ADD_PTR1:%.*]] = getelementptr inbounds i8, ptr [[AGG_RESULT_ASCAST]], i64 2
|
||||
// CHECK-NEXT: store i8 [[TMP1]], ptr [[ADD_PTR1]], align 1
|
||||
// CHECK-NEXT: store i8 [[TMP1]], ptr [[ADD_PTR1]], align 2
|
||||
// CHECK-NEXT: ret void
|
||||
//
|
||||
X foo(char x, char y) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user