Author: Yingwei Zheng Date: 2025-08-25T20:08:32+08:00 New Revision: 5569bf26f0097c2d8b088f0a7cf46451da752dea
URL: https://github.com/llvm/llvm-project/commit/5569bf26f0097c2d8b088f0a7cf46451da752dea DIFF: https://github.com/llvm/llvm-project/commit/5569bf26f0097c2d8b088f0a7cf46451da752dea.diff LOG: [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: https://github.com/llvm/llvm-project/blob/503c0908c3450d228debd64baecf41df8f58476e/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. Added: clang/test/CodeGen/pointer-arithmetic-align.c Modified: clang/lib/CodeGen/CGExpr.cpp clang/lib/CodeGen/CGExprScalar.cpp clang/test/CodeGen/packed-arrays.c clang/test/CodeGenCXX/sret_cast_with_nonzero_alloca_as.cpp Removed: ################################################################################ diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 2329fa20a2530..0f9de6fe3e0cb 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -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; diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index fcbfa5e31d149..1e23f45546748 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -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 diff --git a/clang/test/CodeGen/packed-arrays.c b/clang/test/CodeGen/packed-arrays.c index 097fa7fc0feb7..51629b66d0687 100644 --- a/clang/test/CodeGen/packed-arrays.c +++ b/clang/test/CodeGen/packed-arrays.c @@ -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 diff --git a/clang/test/CodeGen/pointer-arithmetic-align.c b/clang/test/CodeGen/pointer-arithmetic-align.c new file mode 100644 index 0000000000000..745ab84635c1b --- /dev/null +++ b/clang/test/CodeGen/pointer-arithmetic-align.c @@ -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"} +//. diff --git a/clang/test/CodeGenCXX/sret_cast_with_nonzero_alloca_as.cpp b/clang/test/CodeGenCXX/sret_cast_with_nonzero_alloca_as.cpp index a0ee54dc16ba3..a1a6ada4f3fd6 100644 --- a/clang/test/CodeGenCXX/sret_cast_with_nonzero_alloca_as.cpp +++ b/clang/test/CodeGenCXX/sret_cast_with_nonzero_alloca_as.cpp @@ -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) { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits