https://github.com/bwendling updated https://github.com/llvm/llvm-project/pull/86858
>From 31af119d614ef2108b5404f9c9387ec45aa1bfef Mon Sep 17 00:00:00 2001 From: Bill Wendling <mo...@google.com> Date: Thu, 21 Mar 2024 15:07:31 -0700 Subject: [PATCH 1/6] [Clang][objectsize] Generate object size calculation for sub-objects The second argument of __builtin_dynamic_object_size controls whether it returns the size of the whole object or the closest surrounding object. For this struct: struct s { int foo; char bar[2][40]; int baz; int qux; }; int main(int argc, char **argv) { struct s f; #define report(x) printf(#x ": %zu\n", x) argc = 1; report(__builtin_dynamic_object_size(f.bar[argc], 0)); report(__builtin_dynamic_object_size(f.bar[argc], 1)); return 0; } should return: __builtin_dynamic_object_size(f.bar[argc], 0): 48 __builtin_dynamic_object_size(f.bar[argc], 1): 40 determined by the least significant bit of the TYPE. The LLVM IR isn't sufficient to determine what could be considered a "sub-object". However, the front-end does have enough information to determine the size of a sub-object and the offset into that sub-object. We try therefore to convert the intrinsic into a calculation in the front-end so that we can avoid the information issue.. --- clang/lib/CodeGen/CGBuiltin.cpp | 138 +++++++++- clang/lib/CodeGen/CodeGenFunction.h | 6 + clang/test/CodeGen/object-size-sub-object.c | 280 ++++++++++++++++++++ 3 files changed, 418 insertions(+), 6 deletions(-) create mode 100644 clang/test/CodeGen/object-size-sub-object.c diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 2eaceeba61770..be055f34c4492 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -26,6 +26,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/OSLog.h" #include "clang/AST/OperationKinds.h" +#include "clang/AST/StmtVisitor.h" #include "clang/Basic/TargetBuiltins.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/TargetOptions.h" @@ -1052,6 +1053,128 @@ CodeGenFunction::emitFlexibleArrayMemberSize(const Expr *E, unsigned Type, return Builder.CreateSelect(Cmp, Res, ConstantInt::get(ResType, 0, IsSigned)); } +namespace { + +struct ObjectSizeVisitor + : public ConstStmtVisitor<ObjectSizeVisitor, const Expr *> { + const Expr *Visit(const Expr *E) { + return ConstStmtVisitor<ObjectSizeVisitor, const Expr *>::Visit(E); + } + + const Expr *VisitStmt(const Stmt *S) { return nullptr; } + + const Expr *VisitDeclRefExpr(const DeclRefExpr *E) { return E; } + const Expr *VisitMemberExpr(const MemberExpr *E) { return E; } + const Expr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) { return E; } + + const Expr *VisitCastExpr(const CastExpr *E) { + return Visit(E->getSubExpr()); + } + const Expr *VisitParenExpr(const ParenExpr *E) { + return Visit(E->getSubExpr()); + } + const Expr *VisitUnaryAddrOf(const clang::UnaryOperator *E) { + return Visit(E->getSubExpr()); + } + const Expr *VisitUnaryDeref(const clang::UnaryOperator *E) { + return Visit(E->getSubExpr()); + } +}; + +} // end anonymous namespace + +/// tryToCalculateSubObjectSize - It may be possible to calculate the +/// sub-object size of an array and skip the generation of the llvm.objectsize +/// intrinsic. This avoids the complication in conveying the sub-object's +/// information to the backend. This calculation works for an N-dimentional +/// array. +/// +/// Note that this function supports only Row-Major arrays. The generalized +/// calculation of the offset of an element in Row-Major form: +/// +/// .- -. +/// d | d | +/// --- | ----- | +/// offset = \ | | | | +/// / | | | N_j | m_i +/// --- | | | | +/// i = 1 | j = i + 1 | +/// `- -' +/// +/// where d is the number of dimensions; m_i is the index of an element in +/// dimension i; and N_i is the size of dimention i. +/// +/// Examples: +/// 2D: offset = m_2 + (N_2 * m_1) +/// 3D: offset = m_3 + (N_3 * m_2) + (N_3 * N_2 * m_1) +llvm::Value * +CodeGenFunction::tryToCalculateSubObjectSize(const Expr *E, unsigned Type, + llvm::IntegerType *ResType) { + if ((Type & 0x01) != 1) + // Only support sub-object calculation. + return nullptr; + + const Expr *ObjectBase = ObjectSizeVisitor().Visit(E); + if (!ObjectBase) + return nullptr; + + // Collect the sizes and indices from the array. + ASTContext &Ctx = getContext(); + SmallVector<std::pair<Value *, Value *>, 4> Dims; + while (const auto *ASE = dyn_cast<ArraySubscriptExpr>(ObjectBase)) { + const Expr *Base = ASE; + const Expr *Idx = ASE->getIdx(); + + if (Idx->HasSideEffects(Ctx)) + return nullptr; + + uint64_t BaseSize = Ctx.getTypeSizeInChars(Base->getType()).getQuantity(); + Value *IdxSize = EmitScalarExpr(Idx); + + Dims.emplace_back(std::make_pair( + Builder.CreateIntCast(Builder.getInt64(BaseSize), ResType, + /*isSigned=*/true), + Builder.CreateIntCast(IdxSize, ResType, /*isSigned=*/true))); + + ObjectBase = ASE->getBase()->IgnoreParenImpCasts(); + } + + if (Dims.empty()) + return nullptr; + + // Rerun the visitor to find the base object: MemberExpr or DeclRefExpr. + ObjectBase = ObjectSizeVisitor().Visit(ObjectBase); + if (!ObjectBase) + return nullptr; + + if (const auto *ME = dyn_cast<MemberExpr>(ObjectBase)) { + const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel = + getLangOpts().getStrictFlexArraysLevel(); + const ValueDecl *Field = ME->getMemberDecl(); + if (Decl::isFlexibleArrayMemberLike( + Ctx, Field, Field->getType(), StrictFlexArraysLevel, + /*IgnoreTemplateOrMacroSubstitution=*/true)) + // FIXME: Support flexible array members? + return nullptr; + } + + uint64_t ObjectSize = + Ctx.getTypeSizeInChars(ObjectBase->getType()).getQuantity(); + + // Generate the calculation. + Value *Offset = Builder.CreateMul(Dims.front().first, Dims.front().second); + for (auto Dim : llvm::drop_begin(Dims)) + Offset = + Builder.CreateAdd(Offset, Builder.CreateMul(Dim.first, Dim.second)); + + Value *Res = + Builder.CreateSub(Builder.CreateIntCast(Builder.getInt64(ObjectSize), + ResType, /*isSigned=*/true), + Offset); + return Builder.CreateSelect(Builder.CreateIsNotNeg(Res), Res, + ConstantInt::get(ResType, 0, /*isSigned=*/true)); +} + /// Returns a Value corresponding to the size of the given expression. /// This Value may be either of the following: /// - A llvm::Argument (if E is a param with the pass_object_size attribute on @@ -1084,18 +1207,21 @@ CodeGenFunction::emitBuiltinObjectSize(const Expr *E, unsigned Type, } } + // LLVM can't handle Type=3 appropriately, and __builtin_object_size shouldn't + // evaluate E for side-effects. In either case, we shouldn't lower to + // @llvm.objectsize. + if (Type == 3 || (!EmittedE && E->HasSideEffects(getContext()))) + return getDefaultBuiltinObjectSizeResult(Type, ResType); + if (IsDynamic) { // Emit special code for a flexible array member with the "counted_by" // attribute. if (Value *V = emitFlexibleArrayMemberSize(E, Type, ResType)) return V; - } - // LLVM can't handle Type=3 appropriately, and __builtin_object_size shouldn't - // evaluate E for side-effects. In either case, we shouldn't lower to - // @llvm.objectsize. - if (Type == 3 || (!EmittedE && E->HasSideEffects(getContext()))) - return getDefaultBuiltinObjectSizeResult(Type, ResType); + if (Value *V = tryToCalculateSubObjectSize(E, Type, ResType)) + return V; + } Value *Ptr = EmittedE ? EmittedE : EmitScalarExpr(E); assert(Ptr->getType()->isPointerTy() && diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index e8f8aa601ed01..640f2cf2c51b5 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -4919,6 +4919,12 @@ class CodeGenFunction : public CodeGenTypeCache { llvm::Value *EmittedE, bool IsDynamic); + /// Try to calculate the sub-object size (i.e. \p Type's least significant + /// bit is set). It afoids the complication in conveying the sub-object + /// information to the backend. + llvm::Value *tryToCalculateSubObjectSize(const Expr *E, unsigned Type, + llvm::IntegerType *ResType); + /// Emits the size of E, as required by __builtin_object_size. This /// function is aware of pass_object_size parameters, and will act accordingly /// if E is a parameter with the pass_object_size attribute. diff --git a/clang/test/CodeGen/object-size-sub-object.c b/clang/test/CodeGen/object-size-sub-object.c new file mode 100644 index 0000000000000..964beab5bad93 --- /dev/null +++ b/clang/test/CodeGen/object-size-sub-object.c @@ -0,0 +1,280 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 4 +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fstrict-flex-arrays=3 -emit-llvm %s -o - | FileCheck %s + +typedef __SIZE_TYPE__ size_t; + +#define __bdos(a) __builtin_dynamic_object_size(a, 1) + +struct U { + double d; + int i; +}; + +struct test_struct { + struct test_struct *vptr; + char buf1[5]; + struct i { + char a; + int b[2][13]; + int c, d; + } z; + struct U *u_ptr; + unsigned _a : 1; + unsigned _b : 2; + struct { + struct { + char x_1; + char x_2[37]; + }; + }; + union { + struct { char _z[20]; } m; + struct { char _y[13]; } n; + } u; + char buf2[7]; +}; + +size_t ret; + +// CHECK-LABEL: define dso_local i64 @test1( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = call i64 @llvm.objectsize.i64.p0(ptr [[TMP0]], i1 false, i1 true, i1 true) +// CHECK-NEXT: ret i64 [[TMP1]] +// +size_t test1(struct test_struct *p, int idx) { + return __bdos(p); // 216 +} + +// CHECK-LABEL: define dso_local i64 @test2( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP2:%.*]] = mul i64 1, [[TMP1]] +// CHECK-NEXT: [[TMP3:%.*]] = sub i64 5, [[TMP2]] +// CHECK-NEXT: [[TMP4:%.*]] = icmp sgt i64 [[TMP3]], -1 +// CHECK-NEXT: [[TMP5:%.*]] = select i1 [[TMP4]], i64 [[TMP3]], i64 0 +// CHECK-NEXT: ret i64 [[TMP5]] +// +size_t test2(struct test_struct *p, int idx) { + return __bdos(&p->buf1[idx]); // 5 - idx +} + +// CHECK-LABEL: define dso_local i64 @test3( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP2:%.*]] = mul i64 1, [[TMP1]] +// CHECK-NEXT: [[TMP3:%.*]] = sub i64 116, [[TMP2]] +// CHECK-NEXT: [[TMP4:%.*]] = icmp sgt i64 [[TMP3]], -1 +// CHECK-NEXT: [[TMP5:%.*]] = select i1 [[TMP4]], i64 [[TMP3]], i64 0 +// CHECK-NEXT: ret i64 [[TMP5]] +// +size_t test3(struct test_struct *p, int idx) { + return __bdos(&((char *)&p->z)[idx]); // 116 - idx +} + +// CHECK-LABEL: define dso_local i64 @test4( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP2:%.*]] = mul i64 1, [[TMP1]] +// CHECK-NEXT: [[TMP3:%.*]] = sub i64 1, [[TMP2]] +// CHECK-NEXT: [[TMP4:%.*]] = icmp sgt i64 [[TMP3]], -1 +// CHECK-NEXT: [[TMP5:%.*]] = select i1 [[TMP4]], i64 [[TMP3]], i64 0 +// CHECK-NEXT: ret i64 [[TMP5]] +// +size_t test4(struct test_struct *p, int idx) { + return __bdos(&((char *)&p->z.a)[idx]); // 1 - idx +} + +// CHECK-LABEL: define dso_local i64 @test5( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP2:%.*]] = mul i64 1, [[TMP1]] +// CHECK-NEXT: [[TMP3:%.*]] = sub i64 104, [[TMP2]] +// CHECK-NEXT: [[TMP4:%.*]] = icmp sgt i64 [[TMP3]], -1 +// CHECK-NEXT: [[TMP5:%.*]] = select i1 [[TMP4]], i64 [[TMP3]], i64 0 +// CHECK-NEXT: ret i64 [[TMP5]] +// +size_t test5(struct test_struct *p, int idx) { + return __bdos(&((char *)&p->z.b)[idx]); // 104 - idx +} + +// CHECK-LABEL: define dso_local i64 @test6( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP2:%.*]] = mul i64 1, [[TMP1]] +// CHECK-NEXT: [[TMP3:%.*]] = sub i64 4, [[TMP2]] +// CHECK-NEXT: [[TMP4:%.*]] = icmp sgt i64 [[TMP3]], -1 +// CHECK-NEXT: [[TMP5:%.*]] = select i1 [[TMP4]], i64 [[TMP3]], i64 0 +// CHECK-NEXT: ret i64 [[TMP5]] +// +size_t test6(struct test_struct *p, int idx) { + return __bdos(&((char *)&p->z.c)[idx]); // 4 - idx +} + +// CHECK-LABEL: define dso_local i64 @test7( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP2:%.*]] = mul i64 1, [[TMP1]] +// CHECK-NEXT: [[TMP3:%.*]] = sub i64 4, [[TMP2]] +// CHECK-NEXT: [[TMP4:%.*]] = icmp sgt i64 [[TMP3]], -1 +// CHECK-NEXT: [[TMP5:%.*]] = select i1 [[TMP4]], i64 [[TMP3]], i64 0 +// CHECK-NEXT: ret i64 [[TMP5]] +// +size_t test7(struct test_struct *p, int idx) { + return __bdos(&((char *)&p->z.d)[idx]); // 4 - idx +} + +// CHECK-LABEL: define dso_local i64 @test8( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP2:%.*]] = mul i64 1, [[TMP1]] +// CHECK-NEXT: [[TMP3:%.*]] = sub i64 8, [[TMP2]] +// CHECK-NEXT: [[TMP4:%.*]] = icmp sgt i64 [[TMP3]], -1 +// CHECK-NEXT: [[TMP5:%.*]] = select i1 [[TMP4]], i64 [[TMP3]], i64 0 +// CHECK-NEXT: ret i64 [[TMP5]] +// +size_t test8(struct test_struct *p, int idx) { + return __bdos(&((char *)&p->u_ptr->d)[idx]); // 8 - idx +} + +// CHECK-LABEL: define dso_local i64 @test9( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP2:%.*]] = mul i64 1, [[TMP1]] +// CHECK-NEXT: [[TMP3:%.*]] = sub i64 1, [[TMP2]] +// CHECK-NEXT: [[TMP4:%.*]] = icmp sgt i64 [[TMP3]], -1 +// CHECK-NEXT: [[TMP5:%.*]] = select i1 [[TMP4]], i64 [[TMP3]], i64 0 +// CHECK-NEXT: ret i64 [[TMP5]] +// +size_t test9(struct test_struct *p, int idx) { + return __bdos(&((char *)&p->x_1)[idx]); // 1 - idx +} + +// CHECK-LABEL: define dso_local i64 @test10( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP2:%.*]] = mul i64 1, [[TMP1]] +// CHECK-NEXT: [[TMP3:%.*]] = sub i64 37, [[TMP2]] +// CHECK-NEXT: [[TMP4:%.*]] = icmp sgt i64 [[TMP3]], -1 +// CHECK-NEXT: [[TMP5:%.*]] = select i1 [[TMP4]], i64 [[TMP3]], i64 0 +// CHECK-NEXT: ret i64 [[TMP5]] +// +size_t test10(struct test_struct *p, int idx) { + return __bdos(&((char *)&p->x_2)[idx]); // 37 - idx +} + +// CHECK-LABEL: define dso_local i64 @test11( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP2:%.*]] = mul i64 1, [[TMP1]] +// CHECK-NEXT: [[TMP3:%.*]] = sub i64 20, [[TMP2]] +// CHECK-NEXT: [[TMP4:%.*]] = icmp sgt i64 [[TMP3]], -1 +// CHECK-NEXT: [[TMP5:%.*]] = select i1 [[TMP4]], i64 [[TMP3]], i64 0 +// CHECK-NEXT: ret i64 [[TMP5]] +// +size_t test11(struct test_struct *p, int idx) { + return __bdos(&p->u.m._z[idx]); // 20 - idx +} + +// CHECK-LABEL: define dso_local i64 @test12( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP2:%.*]] = mul i64 1, [[TMP1]] +// CHECK-NEXT: [[TMP3:%.*]] = sub i64 13, [[TMP2]] +// CHECK-NEXT: [[TMP4:%.*]] = icmp sgt i64 [[TMP3]], -1 +// CHECK-NEXT: [[TMP5:%.*]] = select i1 [[TMP4]], i64 [[TMP3]], i64 0 +// CHECK-NEXT: ret i64 [[TMP5]] +// +size_t test12(struct test_struct *p, int idx) { + return __bdos(&p->u.n._y[idx]); // 13 - idx +} + +// CHECK-LABEL: define dso_local i64 @test13( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP2:%.*]] = mul i64 1, [[TMP1]] +// CHECK-NEXT: [[TMP3:%.*]] = sub i64 7, [[TMP2]] +// CHECK-NEXT: [[TMP4:%.*]] = icmp sgt i64 [[TMP3]], -1 +// CHECK-NEXT: [[TMP5:%.*]] = select i1 [[TMP4]], i64 [[TMP3]], i64 0 +// CHECK-NEXT: ret i64 [[TMP5]] +// +size_t test13(struct test_struct *p, int idx) { + return __bdos(&p->buf2[idx]); // 7 - idx +} >From 81b5a841b66fbe2be74a8526a70582801b6d1701 Mon Sep 17 00:00:00 2001 From: Bill Wendling <mo...@google.com> Date: Mon, 25 Mar 2024 12:43:34 -0700 Subject: [PATCH 2/6] Make sure the field Decl we're looking as is the actual FAM before returning MAX_INT. --- clang/lib/CodeGen/CGBuiltin.cpp | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index be055f34c4492..1a25e347c8105 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -1147,15 +1147,27 @@ CodeGenFunction::tryToCalculateSubObjectSize(const Expr *E, unsigned Type, if (!ObjectBase) return nullptr; + // Check to see if the Decl is a flexible array member. We don't have any + // information on its size, so return MAX_INT. if (const auto *ME = dyn_cast<MemberExpr>(ObjectBase)) { - const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel = - getLangOpts().getStrictFlexArraysLevel(); - const ValueDecl *Field = ME->getMemberDecl(); - if (Decl::isFlexibleArrayMemberLike( - Ctx, Field, Field->getType(), StrictFlexArraysLevel, - /*IgnoreTemplateOrMacroSubstitution=*/true)) - // FIXME: Support flexible array members? - return nullptr; + if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl())) { + if (const RecordDecl *RD = FD->getType()->getAsRecordDecl()) { + const RecordDecl *OuterRD = RD->getOuterLexicalRecordContext(); + const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel = + getLangOpts().getStrictFlexArraysLevel(); + + if (OuterRD->hasFlexibleArrayMember()) { + const FieldDecl *LastFD = nullptr; + for (const FieldDecl *Field : OuterRD->fields()) + LastFD = Field; + + if (FD == LastFD && Decl::isFlexibleArrayMemberLike( + Ctx, FD, FD->getType(), StrictFlexArraysLevel, + /*IgnoreTemplateOrMacroSubstitution=*/true)) + return nullptr; + } + } + } } uint64_t ObjectSize = >From 3a59a35ce7edacb85ca0ec6e102a660b629b50eb Mon Sep 17 00:00:00 2001 From: Bill Wendling <mo...@google.com> Date: Mon, 25 Mar 2024 13:40:52 -0700 Subject: [PATCH 3/6] Make sure we're looking at the very last field in the struct, even if it's in a substruct. --- clang/lib/CodeGen/CGBuiltin.cpp | 45 +++++++----- clang/test/CodeGen/attr-counted-by.c | 102 +++++++++++++-------------- clang/test/CodeGen/object-size.c | 19 +++-- 3 files changed, 92 insertions(+), 74 deletions(-) diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 1a25e347c8105..0a26bf55533f0 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -1083,6 +1083,25 @@ struct ObjectSizeVisitor } // end anonymous namespace +/// Return the last FieldDecl in the struct. +static const FieldDecl *getLastDecl(const RecordDecl *RD) { + const Decl *LastDecl = nullptr; + for (const Decl *D : RD->decls()) + if (isa<FieldDecl>(D) || isa<RecordDecl>(D)) + LastDecl = D; + + if (const auto *LastRD = dyn_cast<RecordDecl>(LastDecl)) { + LastDecl = getLastDecl(LastRD); + } else if (const auto *LastFD = dyn_cast<FieldDecl>(LastDecl)) { + if (const RecordDecl *Rec = LastFD->getType()->getAsRecordDecl()) + // The last FieldDecl is a structure. Look into that struct to find its + // last FieldDecl. + LastDecl = getLastDecl(Rec); + } + + return dyn_cast_if_present<FieldDecl>(LastDecl); +} + /// tryToCalculateSubObjectSize - It may be possible to calculate the /// sub-object size of an array and skip the generation of the llvm.objectsize /// intrinsic. This avoids the complication in conveying the sub-object's @@ -1151,22 +1170,16 @@ CodeGenFunction::tryToCalculateSubObjectSize(const Expr *E, unsigned Type, // information on its size, so return MAX_INT. if (const auto *ME = dyn_cast<MemberExpr>(ObjectBase)) { if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl())) { - if (const RecordDecl *RD = FD->getType()->getAsRecordDecl()) { - const RecordDecl *OuterRD = RD->getOuterLexicalRecordContext(); - const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel = - getLangOpts().getStrictFlexArraysLevel(); - - if (OuterRD->hasFlexibleArrayMember()) { - const FieldDecl *LastFD = nullptr; - for (const FieldDecl *Field : OuterRD->fields()) - LastFD = Field; - - if (FD == LastFD && Decl::isFlexibleArrayMemberLike( - Ctx, FD, FD->getType(), StrictFlexArraysLevel, - /*IgnoreTemplateOrMacroSubstitution=*/true)) - return nullptr; - } - } + const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel = + getLangOpts().getStrictFlexArraysLevel(); + const RecordDecl *OuterRD = + FD->getParent()->getOuterLexicalRecordContext(); + const FieldDecl *LastFD = getLastDecl(OuterRD); + + if (LastFD == FD && Decl::isFlexibleArrayMemberLike( + Ctx, FD, FD->getType(), StrictFlexArraysLevel, + /*IgnoreTemplateOrMacroSubstitution=*/true)) + return ConstantInt::get(ResType, -1, /*isSigned=*/true); } } diff --git a/clang/test/CodeGen/attr-counted-by.c b/clang/test/CodeGen/attr-counted-by.c index 1fb39f9a34666..16e586baaced9 100644 --- a/clang/test/CodeGen/attr-counted-by.c +++ b/clang/test/CodeGen/attr-counted-by.c @@ -66,7 +66,7 @@ struct anon_struct { // SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp ult i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP1]], label [[CONT3:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF3:![0-9]+]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR: handler.out_of_bounds: -// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB2:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10:[0-9]+]], !nosanitize [[META2]] +// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB1:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10:[0-9]+]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]] // SANITIZE-WITH-ATTR: cont3: // SANITIZE-WITH-ATTR-NEXT: [[ARRAY:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 12 @@ -114,7 +114,7 @@ void test1(struct annotated *p, int index, int val) { // SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp ugt i64 [[TMP0]], [[INDEX]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP1]], label [[CONT3:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF3]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR: handler.out_of_bounds: -// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB4:[0-9]+]], i64 [[INDEX]]) #[[ATTR10]], !nosanitize [[META2]] +// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB3:[0-9]+]], i64 [[INDEX]]) #[[ATTR10]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]] // SANITIZE-WITH-ATTR: cont3: // SANITIZE-WITH-ATTR-NEXT: [[ARRAY:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 12 @@ -203,7 +203,7 @@ size_t test2_bdos(struct annotated *p) { // SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp ugt i64 [[TMP0]], [[INDEX]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP1]], label [[CONT3:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF3]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR: handler.out_of_bounds: -// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB5:[0-9]+]], i64 [[INDEX]]) #[[ATTR10]], !nosanitize [[META2]] +// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB4:[0-9]+]], i64 [[INDEX]]) #[[ATTR10]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]] // SANITIZE-WITH-ATTR: cont3: // SANITIZE-WITH-ATTR-NEXT: [[ARRAY:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 12 @@ -308,7 +308,7 @@ size_t test3_bdos(struct annotated *p) { // SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp ult i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP1]], label [[CONT4:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF3]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR: handler.out_of_bounds: -// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB6:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10]], !nosanitize [[META2]] +// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB5:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]] // SANITIZE-WITH-ATTR: cont4: // SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[DOT_COUNTED_BY_LOAD]], 2 @@ -325,7 +325,7 @@ size_t test3_bdos(struct annotated *p) { // SANITIZE-WITH-ATTR-NEXT: [[TMP7:%.*]] = icmp ult i64 [[IDXPROM13]], [[TMP6]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP7]], label [[CONT20:%.*]], label [[HANDLER_OUT_OF_BOUNDS16:%.*]], !prof [[PROF3]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR: handler.out_of_bounds16: -// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB7:[0-9]+]], i64 [[IDXPROM13]]) #[[ATTR10]], !nosanitize [[META2]] +// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB6:[0-9]+]], i64 [[IDXPROM13]]) #[[ATTR10]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]] // SANITIZE-WITH-ATTR: cont20: // SANITIZE-WITH-ATTR-NEXT: [[TMP8:%.*]] = icmp sgt i32 [[DOT_COUNTED_BY_LOAD7]], 3 @@ -342,7 +342,7 @@ size_t test3_bdos(struct annotated *p) { // SANITIZE-WITH-ATTR-NEXT: [[TMP13:%.*]] = icmp ult i64 [[IDXPROM30]], [[TMP12]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP13]], label [[CONT37:%.*]], label [[HANDLER_OUT_OF_BOUNDS33:%.*]], !prof [[PROF3]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR: handler.out_of_bounds33: -// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB8:[0-9]+]], i64 [[IDXPROM30]]) #[[ATTR10]], !nosanitize [[META2]] +// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB7:[0-9]+]], i64 [[IDXPROM30]]) #[[ATTR10]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]] // SANITIZE-WITH-ATTR: cont37: // SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX35:%.*]] = getelementptr inbounds [0 x i32], ptr [[ARRAY]], i64 0, i64 [[IDXPROM30]] @@ -405,33 +405,33 @@ size_t test3_bdos(struct annotated *p) { // SANITIZE-WITHOUT-ATTR-NEXT: entry: // SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAY:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 12 // SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64 -// SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX5:%.*]] = getelementptr inbounds [0 x i32], ptr [[ARRAY]], i64 0, i64 [[IDXPROM]] -// SANITIZE-WITHOUT-ATTR-NEXT: store i32 255, ptr [[ARRAYIDX5]], align 4, !tbaa [[TBAA2]] +// SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i32], ptr [[ARRAY]], i64 0, i64 [[IDXPROM]] +// SANITIZE-WITHOUT-ATTR-NEXT: store i32 255, ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA2]] // SANITIZE-WITHOUT-ATTR-NEXT: [[ADD:%.*]] = add nsw i32 [[INDEX]], 1 -// SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM17:%.*]] = sext i32 [[ADD]] to i64 -// SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX18:%.*]] = getelementptr inbounds [0 x i32], ptr [[ARRAY]], i64 0, i64 [[IDXPROM17]] -// SANITIZE-WITHOUT-ATTR-NEXT: store i32 255, ptr [[ARRAYIDX18]], align 4, !tbaa [[TBAA2]] -// SANITIZE-WITHOUT-ATTR-NEXT: [[ADD31:%.*]] = add nsw i32 [[INDEX]], 2 -// SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM32:%.*]] = sext i32 [[ADD31]] to i64 -// SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX33:%.*]] = getelementptr inbounds [0 x i32], ptr [[ARRAY]], i64 0, i64 [[IDXPROM32]] -// SANITIZE-WITHOUT-ATTR-NEXT: store i32 255, ptr [[ARRAYIDX33]], align 4, !tbaa [[TBAA2]] +// SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM6:%.*]] = sext i32 [[ADD]] to i64 +// SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX7:%.*]] = getelementptr inbounds [0 x i32], ptr [[ARRAY]], i64 0, i64 [[IDXPROM6]] +// SANITIZE-WITHOUT-ATTR-NEXT: store i32 255, ptr [[ARRAYIDX7]], align 4, !tbaa [[TBAA2]] +// SANITIZE-WITHOUT-ATTR-NEXT: [[ADD13:%.*]] = add nsw i32 [[INDEX]], 2 +// SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM14:%.*]] = sext i32 [[ADD13]] to i64 +// SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX15:%.*]] = getelementptr inbounds [0 x i32], ptr [[ARRAY]], i64 0, i64 [[IDXPROM14]] +// SANITIZE-WITHOUT-ATTR-NEXT: store i32 255, ptr [[ARRAYIDX15]], align 4, !tbaa [[TBAA2]] // SANITIZE-WITHOUT-ATTR-NEXT: ret void // // NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test4( -// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]], i32 noundef [[FAM_IDX:%.*]]) local_unnamed_addr #[[ATTR0]] { +// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr nocapture noundef writeonly [[P:%.*]], i32 noundef [[INDEX:%.*]], i32 noundef [[FAM_IDX:%.*]]) local_unnamed_addr #[[ATTR0]] { // NO-SANITIZE-WITHOUT-ATTR-NEXT: entry: // NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAY:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 12 // NO-SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64 -// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX3:%.*]] = getelementptr inbounds [0 x i32], ptr [[ARRAY]], i64 0, i64 [[IDXPROM]] -// NO-SANITIZE-WITHOUT-ATTR-NEXT: store i32 255, ptr [[ARRAYIDX3]], align 4, !tbaa [[TBAA2]] +// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i32], ptr [[ARRAY]], i64 0, i64 [[IDXPROM]] +// NO-SANITIZE-WITHOUT-ATTR-NEXT: store i32 255, ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA2]] // NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ADD:%.*]] = add nsw i32 [[INDEX]], 1 -// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM9:%.*]] = sext i32 [[ADD]] to i64 -// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX10:%.*]] = getelementptr inbounds [0 x i32], ptr [[ARRAY]], i64 0, i64 [[IDXPROM9]] -// NO-SANITIZE-WITHOUT-ATTR-NEXT: store i32 255, ptr [[ARRAYIDX10]], align 4, !tbaa [[TBAA2]] -// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ADD17:%.*]] = add nsw i32 [[INDEX]], 2 -// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM18:%.*]] = sext i32 [[ADD17]] to i64 -// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX19:%.*]] = getelementptr inbounds [0 x i32], ptr [[ARRAY]], i64 0, i64 [[IDXPROM18]] -// NO-SANITIZE-WITHOUT-ATTR-NEXT: store i32 255, ptr [[ARRAYIDX19]], align 4, !tbaa [[TBAA2]] +// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM2:%.*]] = sext i32 [[ADD]] to i64 +// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX3:%.*]] = getelementptr inbounds [0 x i32], ptr [[ARRAY]], i64 0, i64 [[IDXPROM2]] +// NO-SANITIZE-WITHOUT-ATTR-NEXT: store i32 255, ptr [[ARRAYIDX3]], align 4, !tbaa [[TBAA2]] +// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ADD5:%.*]] = add nsw i32 [[INDEX]], 2 +// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM6:%.*]] = sext i32 [[ADD5]] to i64 +// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX7:%.*]] = getelementptr inbounds [0 x i32], ptr [[ARRAY]], i64 0, i64 [[IDXPROM6]] +// NO-SANITIZE-WITHOUT-ATTR-NEXT: store i32 255, ptr [[ARRAYIDX7]], align 4, !tbaa [[TBAA2]] // NO-SANITIZE-WITHOUT-ATTR-NEXT: ret void // void test4(struct annotated *p, int index, int fam_idx) { @@ -471,13 +471,13 @@ void test4(struct annotated *p, int index, int fam_idx) { // NO-SANITIZE-WITH-ATTR-NEXT: [[TMP7:%.*]] = select i1 [[TMP6]], i64 [[TMP3]], i64 0 // NO-SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP7]] // -// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test4_bdos( -// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] { +// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local noundef i64 @test4_bdos( +// SANITIZE-WITHOUT-ATTR-SAME: ptr nocapture noundef readnone [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR2]] { // SANITIZE-WITHOUT-ATTR-NEXT: entry: // SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1 // -// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test4_bdos( -// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readnone [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] { +// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local noundef i64 @test4_bdos( +// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr nocapture noundef readnone [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] { // NO-SANITIZE-WITHOUT-ATTR-NEXT: entry: // NO-SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1 // @@ -494,7 +494,7 @@ size_t test4_bdos(struct annotated *p, int index) { // SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = icmp ugt i64 [[DOT_COUNTED_BY_LOAD]], [[IDXPROM]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP0]], label [[CONT3:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF3]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR: handler.out_of_bounds: -// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB9:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10]], !nosanitize [[META2]] +// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB8:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]] // SANITIZE-WITH-ATTR: cont3: // SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16 @@ -590,7 +590,7 @@ size_t test5_bdos(struct anon_struct *p) { // SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = icmp ugt i64 [[DOT_COUNTED_BY_LOAD]], [[IDXPROM]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP0]], label [[CONT3:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF3]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR: handler.out_of_bounds: -// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB10:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10]], !nosanitize [[META2]] +// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB9:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]] // SANITIZE-WITH-ATTR: cont3: // SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16 @@ -683,7 +683,7 @@ size_t test6_bdos(struct anon_struct *p) { // SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = icmp ult i64 [[IDXPROM]], [[TMP1]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP2]], label [[CONT7:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF3]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR: handler.out_of_bounds: -// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB12:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10]], !nosanitize [[META2]] +// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB11:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]] // SANITIZE-WITH-ATTR: cont7: // SANITIZE-WITH-ATTR-NEXT: [[INTS:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 9 @@ -756,7 +756,7 @@ size_t test7_bdos(struct union_of_fams *p) { // SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp ult i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP1]], label [[CONT9:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF3]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR: handler.out_of_bounds: -// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB13:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10]], !nosanitize [[META2]] +// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB12:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]] // SANITIZE-WITH-ATTR: cont9: // SANITIZE-WITH-ATTR-NEXT: [[INTS:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 9 @@ -1095,10 +1095,10 @@ int test12_a, test12_b; // SANITIZE-WITH-ATTR-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[DOTCOUNTED_BY_LOAD]], 0 // SANITIZE-WITH-ATTR-NEXT: br i1 [[DOTNOT]], label [[HANDLER_OUT_OF_BOUNDS4:%.*]], label [[HANDLER_TYPE_MISMATCH6:%.*]], !prof [[PROF10:![0-9]+]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR: handler.out_of_bounds4: -// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB19:[0-9]+]], i64 0) #[[ATTR10]], !nosanitize [[META2]] +// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB20:[0-9]+]], i64 0) #[[ATTR10]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]] // SANITIZE-WITH-ATTR: handler.type_mismatch6: -// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_type_mismatch_v1_abort(ptr nonnull @[[GLOB20:[0-9]+]], i64 ptrtoint (ptr getelementptr inbounds ([[STRUCT_ANON_5:%.*]], ptr @test12_foo, i64 1, i32 0, i32 0, i32 0) to i64)) #[[ATTR10]], !nosanitize [[META2]] +// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_type_mismatch_v1_abort(ptr nonnull @[[GLOB21:[0-9]+]], i64 ptrtoint (ptr getelementptr inbounds ([[STRUCT_ANON_5:%.*]], ptr @test12_foo, i64 1, i32 0, i32 0, i32 0) to i64)) #[[ATTR10]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]] // // NO-SANITIZE-WITH-ATTR-LABEL: define dso_local noundef i32 @test12( @@ -1188,7 +1188,7 @@ struct test13_bar { // SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = icmp ugt i64 [[TMP1]], [[INDEX]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP2]], label [[CONT5:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF3]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR: handler.out_of_bounds: -// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB23:[0-9]+]], i64 [[INDEX]]) #[[ATTR10]], !nosanitize [[META2]] +// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB24:[0-9]+]], i64 [[INDEX]]) #[[ATTR10]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]] // SANITIZE-WITH-ATTR: cont5: // SANITIZE-WITH-ATTR-NEXT: [[REVMAP:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 16 @@ -1249,7 +1249,7 @@ struct test14_foo { // SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP0]], label [[TRAP:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF3]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR: handler.out_of_bounds: // SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[IDX]] to i64 -// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB24:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10]], !nosanitize [[META2]] +// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB25:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]] // SANITIZE-WITH-ATTR: trap: // SANITIZE-WITH-ATTR-NEXT: tail call void @llvm.trap() #[[ATTR10]] @@ -1305,7 +1305,7 @@ int test14(int idx) { // SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP0]], label [[TRAP:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF3]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR: handler.out_of_bounds: // SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[IDX]] to i64 -// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB25:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10]], !nosanitize [[META2]] +// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB27:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]] // SANITIZE-WITH-ATTR: trap: // SANITIZE-WITH-ATTR-NEXT: tail call void @llvm.trap() #[[ATTR10]] @@ -1326,7 +1326,7 @@ int test14(int idx) { // SANITIZE-WITHOUT-ATTR-NEXT: br i1 [[TMP0]], label [[TRAP:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF8]], !nosanitize [[META9]] // SANITIZE-WITHOUT-ATTR: handler.out_of_bounds: // SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[IDX]] to i64 -// SANITIZE-WITHOUT-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB10:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR8]], !nosanitize [[META9]] +// SANITIZE-WITHOUT-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB11:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR8]], !nosanitize [[META9]] // SANITIZE-WITHOUT-ATTR-NEXT: unreachable, !nosanitize [[META9]] // SANITIZE-WITHOUT-ATTR: trap: // SANITIZE-WITHOUT-ATTR-NEXT: tail call void @llvm.trap() #[[ATTR8]] @@ -1359,13 +1359,13 @@ int test15(int idx) { // NO-SANITIZE-WITH-ATTR-NEXT: entry: // NO-SANITIZE-WITH-ATTR-NEXT: ret i64 -1 // -// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test19( -// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local noundef i64 @test19( +// SANITIZE-WITHOUT-ATTR-SAME: ptr nocapture noundef readnone [[P:%.*]]) local_unnamed_addr #[[ATTR2]] { // SANITIZE-WITHOUT-ATTR-NEXT: entry: // SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1 // -// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test19( -// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readnone [[P:%.*]]) local_unnamed_addr #[[ATTR1]] { +// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local noundef i64 @test19( +// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr nocapture noundef readnone [[P:%.*]]) local_unnamed_addr #[[ATTR1]] { // NO-SANITIZE-WITHOUT-ATTR-NEXT: entry: // NO-SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1 // @@ -1487,7 +1487,7 @@ struct tests_foo { // SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = icmp ugt i32 [[DOTCOUNTED_BY_LOAD]], 10 // SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP0]], label [[CONT4:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF3]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR: handler.out_of_bounds: -// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB26:[0-9]+]], i64 10) #[[ATTR10]], !nosanitize [[META2]] +// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB28:[0-9]+]], i64 10) #[[ATTR10]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]] // SANITIZE-WITH-ATTR: cont4: // SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX2:%.*]] = getelementptr inbounds i8, ptr [[VAR]], i64 84 @@ -1528,7 +1528,7 @@ int test24(int c, struct tests_foo *var) { // SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp ugt i32 [[DOTCOUNTED_BY_LOAD]], 10 // SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP1]], label [[CONT5:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF3]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR: handler.out_of_bounds: -// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB27:[0-9]+]], i64 10) #[[ATTR10]], !nosanitize [[META2]] +// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB29:[0-9]+]], i64 10) #[[ATTR10]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]] // SANITIZE-WITH-ATTR: cont5: // SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 44 @@ -1580,7 +1580,7 @@ struct test26_foo { // SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp ult i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP1]], label [[CONT5:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF3]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR: handler.out_of_bounds: -// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB28:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10]], !nosanitize [[META2]] +// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB30:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]] // SANITIZE-WITH-ATTR: cont5: // SANITIZE-WITH-ATTR-NEXT: [[ARR:%.*]] = getelementptr inbounds i8, ptr [[FOO]], i64 8 @@ -1651,7 +1651,7 @@ struct test27_foo { // SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp ult i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP1]], label [[CONT3:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF3]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR: handler.out_of_bounds: -// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB30:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10]], !nosanitize [[META2]] +// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB32:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]] // SANITIZE-WITH-ATTR: cont3: // SANITIZE-WITH-ATTR-NEXT: [[ENTRIES:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 24 @@ -1717,7 +1717,7 @@ struct test28_foo { // SANITIZE-WITH-ATTR-NEXT: [[TMP4:%.*]] = icmp ult i64 [[IDXPROM]], [[TMP3]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP4]], label [[CONT17:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF3]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR: handler.out_of_bounds: -// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB31:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10]], !nosanitize [[META2]] +// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB34:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]] // SANITIZE-WITH-ATTR: cont17: // SANITIZE-WITH-ATTR-NEXT: [[ARR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 12 @@ -1779,7 +1779,7 @@ struct annotated_struct_array { // SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = zext i32 [[IDX1]] to i64 // SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP0]], label [[CONT3:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF3]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR: handler.out_of_bounds: -// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB33:[0-9]+]], i64 [[TMP1]]) #[[ATTR10]], !nosanitize [[META2]] +// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB36:[0-9]+]], i64 [[TMP1]]) #[[ATTR10]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]] // SANITIZE-WITH-ATTR: cont3: // SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [10 x ptr], ptr [[ANN]], i64 0, i64 [[TMP1]] @@ -1791,7 +1791,7 @@ struct annotated_struct_array { // SANITIZE-WITH-ATTR-NEXT: [[TMP4:%.*]] = icmp ult i64 [[IDXPROM15]], [[TMP3]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP4]], label [[CONT20:%.*]], label [[HANDLER_OUT_OF_BOUNDS16:%.*]], !prof [[PROF3]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR: handler.out_of_bounds16: -// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB34:[0-9]+]], i64 [[IDXPROM15]]) #[[ATTR10]], !nosanitize [[META2]] +// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB37:[0-9]+]], i64 [[IDXPROM15]]) #[[ATTR10]], !nosanitize [[META2]] // SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]] // SANITIZE-WITH-ATTR: cont20: // SANITIZE-WITH-ATTR-NEXT: [[ARRAY:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 12 @@ -1826,7 +1826,7 @@ struct annotated_struct_array { // SANITIZE-WITHOUT-ATTR-NEXT: [[TMP1:%.*]] = zext i32 [[IDX1]] to i64 // SANITIZE-WITHOUT-ATTR-NEXT: br i1 [[TMP0]], label [[CONT21:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF8]], !nosanitize [[META9]] // SANITIZE-WITHOUT-ATTR: handler.out_of_bounds: -// SANITIZE-WITHOUT-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB12:[0-9]+]], i64 [[TMP1]]) #[[ATTR8]], !nosanitize [[META9]] +// SANITIZE-WITHOUT-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB13:[0-9]+]], i64 [[TMP1]]) #[[ATTR8]], !nosanitize [[META9]] // SANITIZE-WITHOUT-ATTR-NEXT: unreachable, !nosanitize [[META9]] // SANITIZE-WITHOUT-ATTR: cont21: // SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [10 x ptr], ptr [[ANN]], i64 0, i64 [[TMP1]] diff --git a/clang/test/CodeGen/object-size.c b/clang/test/CodeGen/object-size.c index b39b15fcc65b9..8fe743617d685 100644 --- a/clang/test/CodeGen/object-size.c +++ b/clang/test/CodeGen/object-size.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -no-enable-noundef-analysis -triple x86_64-apple-darwin -emit-llvm %s -o - 2>&1 | FileCheck %s -// RUN: %clang_cc1 -no-enable-noundef-analysis -DDYNAMIC -triple x86_64-apple-darwin -emit-llvm %s -o - 2>&1 | FileCheck %s +// RUN: %clang_cc1 -no-enable-noundef-analysis -triple x86_64-apple-darwin -emit-llvm %s -o - 2>&1 | FileCheck --check-prefixes=CHECK,NON-DYNAMIC %s +// RUN: %clang_cc1 -no-enable-noundef-analysis -DDYNAMIC -triple x86_64-apple-darwin -emit-llvm %s -o - 2>&1 | FileCheck --check-prefixes=CHECK,DYNAMIC %s #ifndef DYNAMIC #define OBJECT_SIZE_BUILTIN __builtin_object_size @@ -283,7 +283,8 @@ void test23(struct Test23Ty *p) { // CHECK: call i64 @llvm.objectsize.i64.p0(ptr %{{.*}}, i1 false, i1 true, i1 gi = OBJECT_SIZE_BUILTIN(&p->t[5], 0); - // CHECK: call i64 @llvm.objectsize.i64.p0(ptr %{{.*}}, i1 false, i1 true, i1 + // NON-DYNAMIC: call i64 @llvm.objectsize.i64.p0(ptr %{{.*}}, i1 false, i1 true, i1 + // DYNAMIC: store i32 -1 gi = OBJECT_SIZE_BUILTIN(&p->t[5], 1); // CHECK: call i64 @llvm.objectsize.i64.p0(ptr %{{.*}}, i1 true, i1 true, i1 gi = OBJECT_SIZE_BUILTIN(&p->t[5], 2); @@ -512,16 +513,20 @@ void test31(void) { // CHECK: call i64 @llvm.objectsize.i64.p0(ptr %{{.*}}, i1 false, i1 true, i1 gi = OBJECT_SIZE_BUILTIN(ds1[9].snd, 1); - // CHECK: call i64 @llvm.objectsize.i64.p0(ptr %{{.*}}, i1 false, i1 true, i1 + // NON-DYNAMIC: call i64 @llvm.objectsize.i64.p0(ptr %{{.*}}, i1 false, i1 true, i1 + // DYNAMIC: store i32 -1 gi = OBJECT_SIZE_BUILTIN(&ss[9].snd[0], 1); - // CHECK: call i64 @llvm.objectsize.i64.p0(ptr %{{.*}}, i1 false, i1 true, i1 + // NON-DYNAMIC: call i64 @llvm.objectsize.i64.p0(ptr %{{.*}}, i1 false, i1 true, i1 + // DYNAMIC: store i32 -1 gi = OBJECT_SIZE_BUILTIN(&ds1[9].snd[0], 1); - // CHECK: call i64 @llvm.objectsize.i64.p0(ptr %{{.*}}, i1 false, i1 true, i1 + // NON-DYNAMIC: call i64 @llvm.objectsize.i64.p0(ptr %{{.*}}, i1 false, i1 true, i1 + // DYNAMIC: store i32 -1 gi = OBJECT_SIZE_BUILTIN(&ds0[9].snd[0], 1); - // CHECK: call i64 @llvm.objectsize.i64.p0(ptr %{{.*}}, i1 false, i1 true, i1 + // NON-DYNAMIC: call i64 @llvm.objectsize.i64.p0(ptr %{{.*}}, i1 false, i1 true, i1 + // DYNAMIC: store i32 -1 gi = OBJECT_SIZE_BUILTIN(&dsv[9].snd[0], 1); } >From 0b66243c7622567c38145e4e0529b7cd4fa04990 Mon Sep 17 00:00:00 2001 From: Bill Wendling <mo...@google.com> Date: Mon, 25 Mar 2024 14:38:12 -0700 Subject: [PATCH 4/6] Follow the flexible array member through a pointer. --- clang/lib/CodeGen/CGBuiltin.cpp | 11 ++++++--- clang/test/CodeGen/object-size-sub-object.c | 25 +++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 0a26bf55533f0..141e917e915e2 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -1093,7 +1093,11 @@ static const FieldDecl *getLastDecl(const RecordDecl *RD) { if (const auto *LastRD = dyn_cast<RecordDecl>(LastDecl)) { LastDecl = getLastDecl(LastRD); } else if (const auto *LastFD = dyn_cast<FieldDecl>(LastDecl)) { - if (const RecordDecl *Rec = LastFD->getType()->getAsRecordDecl()) + QualType Ty = LastFD->getType(); + if (Ty->isPointerType()) + Ty = Ty->getPointeeType(); + + if (const RecordDecl *Rec = Ty->getAsRecordDecl()) // The last FieldDecl is a structure. Look into that struct to find its // last FieldDecl. LastDecl = getLastDecl(Rec); @@ -1166,8 +1170,9 @@ CodeGenFunction::tryToCalculateSubObjectSize(const Expr *E, unsigned Type, if (!ObjectBase) return nullptr; - // Check to see if the Decl is a flexible array member. We don't have any - // information on its size, so return MAX_INT. + // Check to see if the Decl is a flexible array member. Processing of the + // 'counted_by' attribute is done by now. So we don't have any information on + // its size, so return MAX_INT. if (const auto *ME = dyn_cast<MemberExpr>(ObjectBase)) { if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl())) { const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel = diff --git a/clang/test/CodeGen/object-size-sub-object.c b/clang/test/CodeGen/object-size-sub-object.c index 964beab5bad93..6c5650c53da06 100644 --- a/clang/test/CodeGen/object-size-sub-object.c +++ b/clang/test/CodeGen/object-size-sub-object.c @@ -278,3 +278,28 @@ size_t test12(struct test_struct *p, int idx) { size_t test13(struct test_struct *p, int idx) { return __bdos(&p->buf2[idx]); // 7 - idx } + +// Referencing a flexible array member through a pointer. +struct stest14 { + unsigned long flags; + int count; + struct { + char a; + int array[]; + } *z; +}; + +// CHECK-LABEL: define dso_local i64 @test14( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: ret i64 -1 +// +size_t test14(struct stest14 *p, int idx) { + return __bdos(&p->z->array[idx]); // -1 +} >From d2c4328e9751fbc95951d0d0d584f9044e7b1e4a Mon Sep 17 00:00:00 2001 From: Bill Wendling <mo...@google.com> Date: Wed, 27 Mar 2024 03:30:58 -0700 Subject: [PATCH 5/6] Don't calculate from the last index to the end of the entire N-dimentional array taken as a whole. Instead treat it as referencing the sub-type of the array the indices point to. --- clang/lib/CodeGen/CGBuiltin.cpp | 123 +++++++++++--------- clang/test/CodeGen/object-size-sub-object.c | 4 +- 2 files changed, 66 insertions(+), 61 deletions(-) diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 141e917e915e2..b49311459fda6 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -1055,8 +1055,13 @@ CodeGenFunction::emitFlexibleArrayMemberSize(const Expr *E, unsigned Type, namespace { -struct ObjectSizeVisitor +class ObjectSizeVisitor : public ConstStmtVisitor<ObjectSizeVisitor, const Expr *> { + bool SkipASE; + +public: + ObjectSizeVisitor(bool SkipASE = false) : SkipASE(SkipASE) {} + const Expr *Visit(const Expr *E) { return ConstStmtVisitor<ObjectSizeVisitor, const Expr *>::Visit(E); } @@ -1065,7 +1070,9 @@ struct ObjectSizeVisitor const Expr *VisitDeclRefExpr(const DeclRefExpr *E) { return E; } const Expr *VisitMemberExpr(const MemberExpr *E) { return E; } - const Expr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) { return E; } + const Expr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) { + return SkipASE ? Visit(E->getBase()) : E; + } const Expr *VisitCastExpr(const CastExpr *E) { return Visit(E->getSubExpr()); @@ -1083,7 +1090,7 @@ struct ObjectSizeVisitor } // end anonymous namespace -/// Return the last FieldDecl in the struct. +/// getLastDecl - Return the last FieldDecl in the struct. static const FieldDecl *getLastDecl(const RecordDecl *RD) { const Decl *LastDecl = nullptr; for (const Decl *D : RD->decls()) @@ -1111,25 +1118,6 @@ static const FieldDecl *getLastDecl(const RecordDecl *RD) { /// intrinsic. This avoids the complication in conveying the sub-object's /// information to the backend. This calculation works for an N-dimentional /// array. -/// -/// Note that this function supports only Row-Major arrays. The generalized -/// calculation of the offset of an element in Row-Major form: -/// -/// .- -. -/// d | d | -/// --- | ----- | -/// offset = \ | | | | -/// / | | | N_j | m_i -/// --- | | | | -/// i = 1 | j = i + 1 | -/// `- -' -/// -/// where d is the number of dimensions; m_i is the index of an element in -/// dimension i; and N_i is the size of dimention i. -/// -/// Examples: -/// 2D: offset = m_2 + (N_2 * m_1) -/// 3D: offset = m_3 + (N_3 * m_2) + (N_3 * N_2 * m_1) llvm::Value * CodeGenFunction::tryToCalculateSubObjectSize(const Expr *E, unsigned Type, llvm::IntegerType *ResType) { @@ -1137,43 +1125,43 @@ CodeGenFunction::tryToCalculateSubObjectSize(const Expr *E, unsigned Type, // Only support sub-object calculation. return nullptr; - const Expr *ObjectBase = ObjectSizeVisitor().Visit(E); - if (!ObjectBase) + const Expr *ObjectRef = ObjectSizeVisitor().Visit(E); + if (!ObjectRef) return nullptr; - // Collect the sizes and indices from the array. - ASTContext &Ctx = getContext(); - SmallVector<std::pair<Value *, Value *>, 4> Dims; - while (const auto *ASE = dyn_cast<ArraySubscriptExpr>(ObjectBase)) { - const Expr *Base = ASE; - const Expr *Idx = ASE->getIdx(); - - if (Idx->HasSideEffects(Ctx)) - return nullptr; + QualType ObjectRefType = ObjectRef->getType(); + if (ObjectRefType->isPointerType()) + ObjectRefType = ObjectRefType->getPointeeType(); - uint64_t BaseSize = Ctx.getTypeSizeInChars(Base->getType()).getQuantity(); - Value *IdxSize = EmitScalarExpr(Idx); + // Collect the base and index from the array. + QualType ObjectBaseRefTy; + const Expr *ArrayIdx = nullptr; - Dims.emplace_back(std::make_pair( - Builder.CreateIntCast(Builder.getInt64(BaseSize), ResType, - /*isSigned=*/true), - Builder.CreateIntCast(IdxSize, ResType, /*isSigned=*/true))); + if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(ObjectRef)) { + ArrayIdx = ASE->getIdx()->IgnoreParenImpCasts(); - ObjectBase = ASE->getBase()->IgnoreParenImpCasts(); + const Expr *ArrayRefBase = ASE->getBase()->IgnoreParenImpCasts(); + if (isa<ArraySubscriptExpr>(ArrayRefBase)) { + ObjectBaseRefTy = ArrayRefBase->getType(); + if (ObjectBaseRefTy->isPointerType()) + ObjectBaseRefTy = ObjectBaseRefTy->getPointeeType(); + } } - if (Dims.empty()) - return nullptr; - - // Rerun the visitor to find the base object: MemberExpr or DeclRefExpr. - ObjectBase = ObjectSizeVisitor().Visit(ObjectBase); - if (!ObjectBase) + ASTContext &Ctx = getContext(); + if (!ArrayIdx || ArrayIdx->HasSideEffects(Ctx)) return nullptr; // Check to see if the Decl is a flexible array member. Processing of the // 'counted_by' attribute is done by now. So we don't have any information on // its size, so return MAX_INT. - if (const auto *ME = dyn_cast<MemberExpr>(ObjectBase)) { + // + // Rerun the visitor to find the base expr: MemberExpr or DeclRefExpr. + ObjectRef = ObjectSizeVisitor(true).Visit(ObjectRef); + if (!ObjectRef) + return nullptr; + + if (const auto *ME = dyn_cast<MemberExpr>(ObjectRef)) { if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl())) { const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel = getLangOpts().getStrictFlexArraysLevel(); @@ -1188,19 +1176,38 @@ CodeGenFunction::tryToCalculateSubObjectSize(const Expr *E, unsigned Type, } } - uint64_t ObjectSize = - Ctx.getTypeSizeInChars(ObjectBase->getType()).getQuantity(); + if (ObjectBaseRefTy.isNull()) { + ObjectBaseRefTy = ObjectRef->getType(); + if (ObjectBaseRefTy->isPointerType()) + ObjectBaseRefTy = ObjectBaseRefTy->getPointeeType(); + } + + // Generate the calculation: + // + // S Object[n_1][n_2]...[n_m]; /* M-dimentional array */ + // + // ObjectRef = Object[n_1]...[n_x]; /* 0 < x < m */ + // ObjectBaseRef = Object[n_1]...[n_{x-1}]; + // + // ArrayRefSize = sizeof( typeof( ObjectRef ) ); + // ArrayRefBaseSize = sizeof( typeof( ObjectBaseRef ) ); + // + // Size = ArrayRefSize - (ArrayRefBaseSize * ArrayIdx); + // return Size > 0 ? Size : 0; + // + Value *ArrayRefSize = ConstantInt::get( + ResType, Ctx.getTypeSizeInChars(ObjectRefType).getQuantity(), + /*isSigned=*/true); + Value *ArrayRefBaseSize = ConstantInt::get( + ResType, Ctx.getTypeSizeInChars(ObjectBaseRefTy).getQuantity(), + /*isSigned=*/true); + + Value *Res = EmitScalarExpr(ArrayIdx); - // Generate the calculation. - Value *Offset = Builder.CreateMul(Dims.front().first, Dims.front().second); - for (auto Dim : llvm::drop_begin(Dims)) - Offset = - Builder.CreateAdd(Offset, Builder.CreateMul(Dim.first, Dim.second)); + Res = Builder.CreateIntCast(Res, ResType, /*isSigned=*/true); + Res = + Builder.CreateSub(ArrayRefBaseSize, Builder.CreateMul(ArrayRefSize, Res)); - Value *Res = - Builder.CreateSub(Builder.CreateIntCast(Builder.getInt64(ObjectSize), - ResType, /*isSigned=*/true), - Offset); return Builder.CreateSelect(Builder.CreateIsNotNeg(Res), Res, ConstantInt::get(ResType, 0, /*isSigned=*/true)); } diff --git a/clang/test/CodeGen/object-size-sub-object.c b/clang/test/CodeGen/object-size-sub-object.c index 6c5650c53da06..162ed457ff6ab 100644 --- a/clang/test/CodeGen/object-size-sub-object.c +++ b/clang/test/CodeGen/object-size-sub-object.c @@ -48,7 +48,7 @@ size_t ret; // CHECK-NEXT: ret i64 [[TMP1]] // size_t test1(struct test_struct *p, int idx) { - return __bdos(p); // 216 + return __bdos(p); // -1 } // CHECK-LABEL: define dso_local i64 @test2( @@ -296,8 +296,6 @@ struct stest14 { // CHECK-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 // CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 // CHECK-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 -// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 -// CHECK-NEXT: [[TMP1:%.*]] = sext i32 [[TMP0]] to i64 // CHECK-NEXT: ret i64 -1 // size_t test14(struct stest14 *p, int idx) { >From 206b30563de580faf92eee7caf3714951d19446e Mon Sep 17 00:00:00 2001 From: Bill Wendling <mo...@google.com> Date: Fri, 17 May 2024 15:32:01 -0700 Subject: [PATCH 6/6] Remove aggressive 'dereference' from the visitor, and simplify 'getLastDecl' so that it only looks at 'FieldDecls'. --- clang/lib/CodeGen/CGBuiltin.cpp | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index a619c96ef30a3..ed12ad44410c8 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -1093,34 +1093,28 @@ class ObjectSizeVisitor const Expr *VisitUnaryAddrOf(const clang::UnaryOperator *E) { return Visit(E->getSubExpr()); } - const Expr *VisitUnaryDeref(const clang::UnaryOperator *E) { - return Visit(E->getSubExpr()); - } }; } // end anonymous namespace /// getLastDecl - Return the last FieldDecl in the struct. static const FieldDecl *getLastDecl(const RecordDecl *RD) { - const Decl *LastDecl = nullptr; - for (const Decl *D : RD->decls()) - if (isa<FieldDecl>(D) || isa<RecordDecl>(D)) - LastDecl = D; - - if (const auto *LastRD = dyn_cast<RecordDecl>(LastDecl)) { - LastDecl = getLastDecl(LastRD); - } else if (const auto *LastFD = dyn_cast<FieldDecl>(LastDecl)) { - QualType Ty = LastFD->getType(); + const FieldDecl *LastFieldDecl = nullptr; + for (const FieldDecl *FD : RD->fields()) + LastFieldDecl = FD; + + if (LastFieldDecl) { + QualType Ty = LastFieldDecl->getType(); if (Ty->isPointerType()) Ty = Ty->getPointeeType(); if (const RecordDecl *Rec = Ty->getAsRecordDecl()) // The last FieldDecl is a structure. Look into that struct to find its // last FieldDecl. - LastDecl = getLastDecl(Rec); + LastFieldDecl = getLastDecl(Rec); } - return dyn_cast_if_present<FieldDecl>(LastDecl); + return LastFieldDecl; } /// tryToCalculateSubObjectSize - It may be possible to calculate the @@ -1177,11 +1171,11 @@ CodeGenFunction::tryToCalculateSubObjectSize(const Expr *E, unsigned Type, getLangOpts().getStrictFlexArraysLevel(); const RecordDecl *OuterRD = FD->getParent()->getOuterLexicalRecordContext(); - const FieldDecl *LastFD = getLastDecl(OuterRD); - if (LastFD == FD && Decl::isFlexibleArrayMemberLike( - Ctx, FD, FD->getType(), StrictFlexArraysLevel, - /*IgnoreTemplateOrMacroSubstitution=*/true)) + if (getLastDecl(OuterRD) == FD && + Decl::isFlexibleArrayMemberLike( + Ctx, FD, FD->getType(), StrictFlexArraysLevel, + /*IgnoreTemplateOrMacroSubstitution=*/true)) return ConstantInt::get(ResType, -1, /*isSigned=*/true); } } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits