[clang] [Clang][objectsize] Generate object size calculation for sub-objects (PR #86858)
Bill Wendling via cfe-commits
cfe-commits at lists.llvm.org
Wed May 29 16:00:20 PDT 2024
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 <morbo at google.com>
Date: Thu, 21 Mar 2024 15:07:31 -0700
Subject: [PATCH 1/7] [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 <morbo at google.com>
Date: Mon, 25 Mar 2024 12:43:34 -0700
Subject: [PATCH 2/7] 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 <morbo at google.com>
Date: Mon, 25 Mar 2024 13:40:52 -0700
Subject: [PATCH 3/7] 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 <morbo at google.com>
Date: Mon, 25 Mar 2024 14:38:12 -0700
Subject: [PATCH 4/7] 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 <morbo at google.com>
Date: Wed, 27 Mar 2024 03:30:58 -0700
Subject: [PATCH 5/7] 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 <morbo at google.com>
Date: Fri, 17 May 2024 15:32:01 -0700
Subject: [PATCH 6/7] 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);
}
}
>From 98d48239dd0b82db53a6aafd4e2dc71de8a55944 Mon Sep 17 00:00:00 2001
From: Bill Wendling <morbo at google.com>
Date: Wed, 29 May 2024 16:00:00 -0700
Subject: [PATCH 7/7] Restrict 'VisitCastExpr' to no-op casts. Use
'IgnoreParenImpCasts' instead of using a 'VisitParenExpr', which is too
general.
---
clang/lib/CodeGen/CGBuiltin.cpp | 23 ++++++++++++-----------
1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index ed12ad44410c8..7a7368e2271da 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -1067,10 +1067,12 @@ namespace {
class ObjectSizeVisitor
: public ConstStmtVisitor<ObjectSizeVisitor, const Expr *> {
+ ASTContext &Ctx;
bool SkipASE;
public:
- ObjectSizeVisitor(bool SkipASE = false) : SkipASE(SkipASE) {}
+ ObjectSizeVisitor(ASTContext &Ctx, bool SkipASE = false)
+ : Ctx(Ctx), SkipASE(SkipASE) {}
const Expr *Visit(const Expr *E) {
return ConstStmtVisitor<ObjectSizeVisitor, const Expr *>::Visit(E);
@@ -1081,17 +1083,15 @@ class ObjectSizeVisitor
const Expr *VisitDeclRefExpr(const DeclRefExpr *E) { return E; }
const Expr *VisitMemberExpr(const MemberExpr *E) { return E; }
const Expr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
- return SkipASE ? Visit(E->getBase()) : E;
+ return SkipASE ? Visit(E->getBase()->IgnoreParenImpCasts()) : E;
}
const Expr *VisitCastExpr(const CastExpr *E) {
- return Visit(E->getSubExpr());
- }
- const Expr *VisitParenExpr(const ParenExpr *E) {
- return Visit(E->getSubExpr());
+ const Expr *NoopE = E->IgnoreParenNoopCasts(Ctx);
+ return NoopE != E ? Visit(NoopE->IgnoreParenImpCasts()) : nullptr;
}
const Expr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
- return Visit(E->getSubExpr());
+ return Visit(E->getSubExpr()->IgnoreParenImpCasts());
}
};
@@ -1129,7 +1129,8 @@ CodeGenFunction::tryToCalculateSubObjectSize(const Expr *E, unsigned Type,
// Only support sub-object calculation.
return nullptr;
- const Expr *ObjectRef = ObjectSizeVisitor().Visit(E);
+ ASTContext &Ctx = getContext();
+ const Expr *ObjectRef = ObjectSizeVisitor(Ctx).Visit(E);
if (!ObjectRef)
return nullptr;
@@ -1152,7 +1153,6 @@ CodeGenFunction::tryToCalculateSubObjectSize(const Expr *E, unsigned Type,
}
}
- ASTContext &Ctx = getContext();
if (!ArrayIdx || ArrayIdx->HasSideEffects(Ctx))
return nullptr;
@@ -1161,7 +1161,7 @@ CodeGenFunction::tryToCalculateSubObjectSize(const Expr *E, unsigned Type,
// its size, so return MAX_INT.
//
// Rerun the visitor to find the base expr: MemberExpr or DeclRefExpr.
- ObjectRef = ObjectSizeVisitor(true).Visit(ObjectRef);
+ ObjectRef = ObjectSizeVisitor(Ctx, true).Visit(ObjectRef);
if (!ObjectRef)
return nullptr;
@@ -1208,7 +1208,8 @@ CodeGenFunction::tryToCalculateSubObjectSize(const Expr *E, unsigned Type,
Value *Res = EmitScalarExpr(ArrayIdx);
- Res = Builder.CreateIntCast(Res, ResType, /*isSigned=*/true);
+ Res = Builder.CreateIntCast(Res, ResType,
+ ArrayIdx->getType()->isSignedIntegerType());
Res =
Builder.CreateSub(ArrayRefBaseSize, Builder.CreateMul(ArrayRefSize, Res));
More information about the cfe-commits
mailing list