[clang] [Clang][counted_by] Add support for 'counted_by' on struct pointers (PR #137250)

Bill Wendling via cfe-commits cfe-commits at lists.llvm.org
Thu Apr 24 14:23:10 PDT 2025


https://github.com/bwendling created https://github.com/llvm/llvm-project/pull/137250

The 'counted_by' attribute is now available for pointers in structs.
It generates code for sanity checks as well as __builtin_dynamic_object_size()
calculations. For example:

  struct annotated_ptr {
    int count;
    char *buf __attribute__((counted_by(count)));
  };

If the pointer's type is 'void *', use the 'sized_by' attribute, which
works similarly to 'counted_by', but can handle the 'void' base type:

  struct annotated_ptr {
    int count;
    void *buf __attribute__((sized_by(count)));
  };

If the 'count' field member occurs after the pointer, use the
'-fexperimental-late-parse-attributes' flag during compilation.

Note that 'counted_by' cannot be applied to a pointer to an incomplete
type, because the size isn't known.

  struct foo;
  struct annotated_ptr {
    int count;
    struct foo *buf __attribute__((counted_by(count))); /* invalid */
  };

Signed-off-by: Bill Wendling <morbo at google.com>

>From 025166a60f44a1836fedc0c64b87aa69da945445 Mon Sep 17 00:00:00 2001
From: Bill Wendling <morbo at google.com>
Date: Fri, 10 Jan 2025 17:13:30 -0800
Subject: [PATCH 1/6] [Clang][counted_by] Add support for 'counted_by' on
 struct pointers

The 'counted_by' attribute is now available for structs. It generates
code for sanity checks as well as __builtin_dynamic_object_size()
calculations. For example:

  struct annotated_ptr {
    int count;
    void *buf __attribute__((counted_by(count)));
  };

The attribute can be applied to a 'void *'. In that case, use the
'sized_by' attribute instead:

  struct annotated_ptr {
    void *buf __attribute__((sized_by(count)));
    size_t count;
  };

The 'count' field member may occur after the pointer. If it does, use
the '-fexperimental-late-parse-attributes' flag (which will hopefully be
made the default in future Clang versions).

Note that 'counted_by' cannot be applied to a pointer to an incomplete
type, because the size isn't known.

  struct foo;
  struct annotated_ptr {
    int count;
    struct foo *buf __attribute__((counted_by(count))); /* invalid */
  };

Signed-off-by: Bill Wendling <morbo at google.com>
---
 clang/lib/CodeGen/CGBuiltin.cpp               | 272 ++++++++++++----
 clang/lib/CodeGen/CGExpr.cpp                  | 131 +++++---
 clang/lib/CodeGen/CodeGenFunction.h           |  19 +-
 .../CodeGen/attr-counted-by-for-pointers.c    | 302 ++++++++++++++++++
 4 files changed, 619 insertions(+), 105 deletions(-)
 create mode 100644 clang/test/CodeGen/attr-counted-by-for-pointers.c

diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index fe55dfffc1cbe..acf2abd9a5b92 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -909,7 +909,7 @@ class StructFieldAccess
   bool AddrOfSeen = false;
 
 public:
-  const ArraySubscriptExpr *ASE = nullptr;
+  const Expr *ArrayIndex = nullptr;
 
   const Expr *VisitMemberExpr(const MemberExpr *E) {
     if (AddrOfSeen && E->getType()->isArrayType())
@@ -919,12 +919,12 @@ class StructFieldAccess
   }
 
   const Expr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
-    if (ASE)
+    if (ArrayIndex)
       // We don't support multiple subscripts.
       return nullptr;
 
     AddrOfSeen = false; // '&ptr->array[idx]' is okay.
-    ASE = E;
+    ArrayIndex = E->getIdx();
     return Visit(E->getBase());
   }
   const Expr *VisitCastExpr(const CastExpr *E) {
@@ -1016,12 +1016,10 @@ GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FD) {
   return std::nullopt;
 }
 
-llvm::Value *
-CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
-                                         unsigned Type,
-                                         llvm::IntegerType *ResType) {
-  ASTContext &Ctx = getContext();
-
+llvm::Value *CodeGenFunction::emitCountedBySize(const Expr *E,
+                                                llvm::Value *EmittedE,
+                                                unsigned Type,
+                                                llvm::IntegerType *ResType) {
   // Note: If the whole struct is specificed in the __bdos (i.e. Visitor
   // returns a DeclRefExpr). The calculation of the whole size of the structure
   // with a flexible array member can be done in two ways:
@@ -1040,14 +1038,174 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
   // GCC does for consistency's sake.
 
   StructFieldAccess Visitor;
-  const MemberExpr *ME = dyn_cast_if_present<MemberExpr>(Visitor.Visit(E));
+  E = Visitor.Visit(E);
+  if (!E)
+    return nullptr;
+
+  const Expr *Idx = Visitor.ArrayIndex;
+  if (Idx) {
+    if (Idx->HasSideEffects(getContext()))
+      // We can't have side-effects.
+      return getDefaultBuiltinObjectSizeResult(Type, ResType);
+
+    if (const auto *IL = dyn_cast<IntegerLiteral>(Idx)) {
+      int64_t Val = IL->getValue().getSExtValue();
+      if (Val < 0)
+        return getDefaultBuiltinObjectSizeResult(Type, ResType);
+
+      // The index is 0, so we don't need to take it into account.
+      if (Val == 0)
+        Idx = nullptr;
+    }
+  }
+
+  if (const auto *ME = dyn_cast<MemberExpr>(E))
+    // This is either a flexible array member or a pointer into a struct with a
+    // flexible array member.
+    return emitCountedByMemberSize(ME, Idx, EmittedE, Type, ResType);
+
+  if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E);
+      ICE && ICE->getCastKind() == CK_LValueToRValue)
+    // This may be a pointer.
+    return emitCountedByPointerSize(ICE, Idx, EmittedE, Type, ResType);
+
+  return nullptr;
+}
+
+llvm::Value *CodeGenFunction::emitCountedByPointerSize(
+    const ImplicitCastExpr *E, const Expr *Idx, llvm::Value *EmittedE,
+    unsigned Type, llvm::IntegerType *ResType) {
+  assert(E->getCastKind() == CK_LValueToRValue &&
+         "must be an LValue to RValue cast");
+
+  const MemberExpr *ME = dyn_cast<MemberExpr>(E->getSubExpr());
   if (!ME)
     return nullptr;
 
+  const auto *ArrayBaseFD = dyn_cast<FieldDecl>(ME->getMemberDecl());
+  if (!ArrayBaseFD || !ArrayBaseFD->getType()->isPointerType() ||
+      !ArrayBaseFD->getType()->isCountAttributedType())
+    return nullptr;
+
+  // Get the 'count' FieldDecl.
+  const FieldDecl *CountFD = ArrayBaseFD->findCountedByField();
+  if (!CountFD)
+    // Can't find the field referenced by the "counted_by" attribute.
+    return nullptr;
+
+  // Calculate the array's object size using these formulae. (Note: if the
+  // calculation is negative, we return 0.):
+  //
+  //      struct p;
+  //      struct s {
+  //          /* ... */
+  //          struct p **array __attribute__((counted_by(count)));
+  //          int count;
+  //      };
+  //
+  // 1) 'ptr->array':
+  //
+  //    count = ptr->count;
+  //
+  //    array_base_size = sizeof (*ptr->array);
+  //    array_size = count * array_base_size;
+  //
+  //    result = array_size;
+  //
+  //    cmp = (result >= 0)
+  //    return cmp ? result : 0;
+  //
+  // 2) '&ptr->array[idx]':
+  //
+  //    count = ptr->count;
+  //    index = idx;
+  //
+  //    array_base_size = sizeof (*ptr->array_base_size);
+  //    array_size = count * array_base_size;
+  //
+  //    index_size = index * array_base_size;
+  //
+  //    result = array_size - index_size;
+  //
+  //    cmp = (result >= 0)
+  //    if (index)
+  //        cmp  = (cmp && index > 0)
+  //    return cmp ? result : 0;
+
+  bool IsSigned = CountFD->getType()->isSignedIntegerType();
+
+  //  array_base_size = sizeof (*ptr->array);
+  ASTContext &Ctx = getContext();
+  QualType ArrayBaseTy = ArrayBaseFD->getType()->getPointeeType();
+  CharUnits BaseSize = Ctx.getTypeSizeInChars(ArrayBaseTy);
+  if (BaseSize.isZero()) {
+    // This might be a __sized_by on a 'void *', which counts bytes, not
+    // elements.
+    auto *CAT = ArrayBaseFD->getType()->getAs<CountAttributedType>();
+    if (CAT->getKind() != CountAttributedType::SizedBy &&
+        CAT->getKind() != CountAttributedType::SizedByOrNull)
+      // Okay, not sure what it is now.
+      // FIXME: Should this be an assert?
+      return nullptr;
+
+    BaseSize = CharUnits::One();
+  }
+
+  auto *ArrayBaseSize =
+      llvm::ConstantInt::get(ResType, BaseSize.getQuantity(), IsSigned);
+
+  //  count = ptr->count;
+  Value *Count = EmitLoadOfCountedByField(ME, ArrayBaseFD, CountFD);
+  if (!Count)
+    return nullptr;
+  Count = Builder.CreateIntCast(Count, ResType, IsSigned, "count");
+
+  //  index = ptr->index;
+  Value *Index = nullptr;
+  if (Idx) {
+    bool IdxSigned = Idx->getType()->isSignedIntegerType();
+    Index = EmitScalarExpr(Idx);
+    Index = Builder.CreateIntCast(Index, ResType, IdxSigned, "index");
+  }
+
+  //  array_size = count * array_base_size;
+  Value *ArraySize = Builder.CreateMul(Count, ArrayBaseSize, "array_size",
+                                       !IsSigned, IsSigned);
+
+  // Option (1) 'ptr->array'
+  //   result = array_size
+  Value *Res = ArraySize;
+
+  if (Idx) { // Option (2) '&ptr->array[idx]'
+    //  index_size = index * array_base_size;
+    Value *IndexSize = Builder.CreateMul(ArrayBaseSize, Index, "index_size",
+                                         !IsSigned, IsSigned);
+
+    //  result = result - index_size;
+    Res = Builder.CreateSub(Res, IndexSize, "result", !IsSigned, IsSigned);
+  }
+
+  //  cmp = (array_size >= 0)
+  Value *Cmp = Builder.CreateIsNotNeg(Res);
+  if (Idx)
+    //  cmp = (cmp && index >= 0)
+    Cmp = Builder.CreateAnd(Builder.CreateIsNotNeg(Index), Cmp);
+
+  //  return cmp ? result : 0
+  return Builder.CreateSelect(Cmp, Res, ConstantInt::get(ResType, 0, IsSigned));
+}
+
+llvm::Value *
+CodeGenFunction::emitCountedByMemberSize(const MemberExpr *ME, const Expr *Idx,
+                                         llvm::Value *EmittedE, unsigned Type,
+                                         llvm::IntegerType *ResType) {
   const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl());
   if (!FD)
     return nullptr;
 
+  // Find the flexible array member and check that it has the __counted_by
+  // attribute.
+  ASTContext &Ctx = getContext();
   const RecordDecl *RD = FD->getDeclContext()->getOuterLexicalRecordContext();
   const FieldDecl *FlexibleArrayMemberFD = nullptr;
 
@@ -1062,32 +1220,14 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
       !FlexibleArrayMemberFD->getType()->isCountAttributedType())
     return nullptr;
 
+  // Get the 'count' FieldDecl.
   const FieldDecl *CountFD = FlexibleArrayMemberFD->findCountedByField();
   if (!CountFD)
     // Can't find the field referenced by the "counted_by" attribute.
     return nullptr;
 
-  const Expr *Idx = nullptr;
-  if (Visitor.ASE) {
-    Idx = Visitor.ASE->getIdx();
-
-    if (Idx->HasSideEffects(Ctx))
-      // We can't have side-effects.
-      return getDefaultBuiltinObjectSizeResult(Type, ResType);
-
-    if (const auto *IL = dyn_cast<IntegerLiteral>(Idx)) {
-      int64_t Val = IL->getValue().getSExtValue();
-      if (Val < 0)
-        return getDefaultBuiltinObjectSizeResult(Type, ResType);
-
-      // The index is 0, so we don't need to take it into account.
-      if (Val == 0)
-        Idx = nullptr;
-    }
-  }
-
-  // Calculate the flexible array member's object size using these formulae
-  // (note: if the calculation is negative, we return 0.):
+  // Calculate the flexible array member's object size using these formulae.
+  // (Note: if the calculation is negative, we return 0.):
   //
   //      struct p;
   //      struct s {
@@ -1104,9 +1244,11 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
   //    flexible_array_member_size =
   //            count * flexible_array_member_base_size;
   //
-  //    if (flexible_array_member_size < 0)
-  //        return 0;
-  //    return flexible_array_member_size;
+  //    result = flexible_array_member_size;
+  //
+  //    cmp = (result >= 0)
+  //
+  //    return cmp ? result : 0;
   //
   // 2) '&ptr->array[idx]':
   //
@@ -1119,9 +1261,13 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
   //
   //    index_size = index * flexible_array_member_base_size;
   //
-  //    if (flexible_array_member_size < 0 || index < 0)
-  //        return 0;
-  //    return flexible_array_member_size - index_size;
+  //    result = flexible_array_member_size - index_size;
+  //
+  //    cmp = (result >= 0)
+  //    if (index != 0)
+  //        cmp = (cmp && index >= 0)
+  //
+  //    return cmp ? result : 0;
   //
   // 3) '&ptr->field':
   //
@@ -1135,9 +1281,11 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
   //    field_offset = offsetof (struct s, field);
   //    offset_diff = sizeof_struct - field_offset;
   //
-  //    if (flexible_array_member_size < 0)
-  //        return 0;
-  //    return offset_diff + flexible_array_member_size;
+  //    result = offset_diff + flexible_array_member_size;
+  //
+  //    cmp = (result >= 0)
+  //
+  //    return cmp ? result : 0;
   //
   // 4) '&ptr->field_array[idx]':
   //
@@ -1155,21 +1303,24 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
   //
   //    offset_diff = sizeof_struct - field_offset;
   //
-  //    if (flexible_array_member_size < 0 || index < 0)
-  //        return 0;
-  //    return offset_diff + flexible_array_member_size;
+  //    result = offset_diff + flexible_array_member_size;
+  //
+  //    cmp = (result >= 0)
+  //    if (index != 0)
+  //        cmp = (cmp && index >= 0)
+  //
+  //    return cmp ? result : 0;
 
-  QualType CountTy = CountFD->getType();
-  bool IsSigned = CountTy->isSignedIntegerType();
+  bool IsSigned = CountFD->getType()->isSignedIntegerType();
 
   QualType FlexibleArrayMemberTy = FlexibleArrayMemberFD->getType();
   QualType FieldTy = FD->getType();
 
   // Explicit cast because otherwise the CharWidth will promote an i32's into
-  // u64's leading to overflows..
+  // u64's leading to overflows.
   int64_t CharWidth = static_cast<int64_t>(CGM.getContext().getCharWidth());
 
-  //  size_t field_offset = offsetof (struct s, field);
+  //  field_offset = offsetof (struct s, field);
   Value *FieldOffset = nullptr;
   if (FlexibleArrayMemberFD != FD) {
     std::optional<int64_t> Offset = GetFieldOffset(Ctx, RD, FD);
@@ -1179,13 +1330,13 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
         llvm::ConstantInt::get(ResType, *Offset / CharWidth, IsSigned);
   }
 
-  //  size_t count = (size_t) ptr->count;
+  //  count = ptr->count;
   Value *Count = EmitLoadOfCountedByField(ME, FlexibleArrayMemberFD, CountFD);
   if (!Count)
     return nullptr;
   Count = Builder.CreateIntCast(Count, ResType, IsSigned, "count");
 
-  //  size_t index = (size_t) ptr->index;
+  //  index = ptr->index;
   Value *Index = nullptr;
   if (Idx) {
     bool IdxSigned = Idx->getType()->isSignedIntegerType();
@@ -1193,13 +1344,13 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
     Index = Builder.CreateIntCast(Index, ResType, IdxSigned, "index");
   }
 
-  //  size_t flexible_array_member_base_size = sizeof (*ptr->array);
+  //  flexible_array_member_base_size = sizeof (*ptr->array);
   const ArrayType *ArrayTy = Ctx.getAsArrayType(FlexibleArrayMemberTy);
   CharUnits BaseSize = Ctx.getTypeSizeInChars(ArrayTy->getElementType());
   auto *FlexibleArrayMemberBaseSize =
       llvm::ConstantInt::get(ResType, BaseSize.getQuantity(), IsSigned);
 
-  //  size_t flexible_array_member_size =
+  //  flexible_array_member_size =
   //          count * flexible_array_member_base_size;
   Value *FlexibleArrayMemberSize =
       Builder.CreateMul(Count, FlexibleArrayMemberBaseSize,
@@ -1208,19 +1359,19 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
   Value *Res = nullptr;
   if (FlexibleArrayMemberFD == FD) {
     if (Idx) { // Option (2) '&ptr->array[idx]'
-      //  size_t index_size = index * flexible_array_member_base_size;
+      //  index_size = index * flexible_array_member_base_size;
       Value *IndexSize = Builder.CreateMul(FlexibleArrayMemberBaseSize, Index,
                                            "index_size", !IsSigned, IsSigned);
 
-      //  return flexible_array_member_size - index_size;
+      //  result = flexible_array_member_size - index_size;
       Res = Builder.CreateSub(FlexibleArrayMemberSize, IndexSize, "result",
                               !IsSigned, IsSigned);
     } else { // Option (1) 'ptr->array'
-      //  return flexible_array_member_size;
+      //  result = flexible_array_member_size;
       Res = FlexibleArrayMemberSize;
     }
   } else {
-    //  size_t sizeof_struct = sizeof (struct s);
+    //  sizeof_struct = sizeof (struct s);
     llvm::StructType *StructTy = getTypes().getCGRecordLayout(RD).getLLVMType();
     const llvm::DataLayout &Layout = CGM.getDataLayout();
     TypeSize Size = Layout.getTypeSizeInBits(StructTy);
@@ -1228,7 +1379,7 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
         llvm::ConstantInt::get(ResType, Size.getKnownMinValue() / CharWidth);
 
     if (Idx) { // Option (4) '&ptr->field_array[idx]'
-      //  size_t field_base_size = sizeof (*ptr->field_array);
+      //  field_base_size = sizeof (*ptr->field_array);
       const ArrayType *ArrayTy = Ctx.getAsArrayType(FieldTy);
       CharUnits BaseSize = Ctx.getTypeSizeInChars(ArrayTy->getElementType());
       auto *FieldBaseSize =
@@ -1241,18 +1392,21 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
     }
     // Option (3) '&ptr->field', and Option (4) continuation.
 
-    //  size_t offset_diff = flexible_array_member_offset - field_offset;
+    //  offset_diff = flexible_array_member_offset - field_offset;
     Value *OffsetDiff = Builder.CreateSub(SizeofStruct, FieldOffset,
                                           "offset_diff", !IsSigned, IsSigned);
 
-    //  return offset_diff + flexible_array_member_size;
+    //  result = offset_diff + flexible_array_member_size;
     Res = Builder.CreateAdd(FlexibleArrayMemberSize, OffsetDiff, "result");
   }
 
+  //  cmp = (array_size >= 0)
   Value *Cmp = Builder.CreateIsNotNeg(Res);
   if (Idx)
+    //  cmp = (cmp && index >= 0)
     Cmp = Builder.CreateAnd(Builder.CreateIsNotNeg(Index), Cmp);
 
+  //  return cmp ? result : 0
   return Builder.CreateSelect(Cmp, Res, ConstantInt::get(ResType, 0, IsSigned));
 }
 
@@ -1301,7 +1455,7 @@ CodeGenFunction::emitBuiltinObjectSize(const Expr *E, unsigned Type,
   if (IsDynamic)
     // Emit special code for a flexible array member with the "counted_by"
     // attribute.
-    if (Value *V = emitCountedByMemberSize(E, Ptr, Type, ResType))
+    if (Value *V = emitCountedBySize(E, Ptr, Type, ResType))
       return V;
 
   Function *F =
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index de7c577a23493..74ac4e4189b39 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -4291,6 +4291,53 @@ static std::optional<int64_t> getOffsetDifferenceInBits(CodeGenFunction &CGF,
   return std::make_optional<int64_t>(FD1Offset - FD2Offset);
 }
 
+/// EmitCountedByBoundsChecking - If the array being accessed has a "counted_by"
+/// attribute, generate bounds checking code. The "count" field is at the top
+/// level of the struct or in an anonymous struct, that's also at the top level.
+/// Future expansions may allow the "count" to reside at any place in the
+/// struct, but the value of "counted_by" will be a "simple" path to the count,
+/// i.e. "a.b.count", so we shouldn't need the full force of EmitLValue or
+/// similar to emit the correct GEP.
+void CodeGenFunction::EmitCountedByBoundsChecking(
+    const Expr *E, llvm::Value *Idx, Address Addr, QualType IdxTy,
+    QualType ArrayTy, bool Accessed, bool FlexibleArray) {
+  const auto *ME = dyn_cast<MemberExpr>(E->IgnoreImpCasts());
+  if (!ME || !ME->getMemberDecl()->getType()->isCountAttributedType())
+    return;
+
+  const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
+      getLangOpts().getStrictFlexArraysLevel();
+  if (FlexibleArray &&
+      !ME->isFlexibleArrayMemberLike(getContext(), StrictFlexArraysLevel))
+    return;
+
+  const FieldDecl *FD = cast<FieldDecl>(ME->getMemberDecl());
+  const FieldDecl *CountFD = FD->findCountedByField();
+  if (!CountFD)
+    return;
+
+  if (std::optional<int64_t> Diff =
+          getOffsetDifferenceInBits(*this, CountFD, FD)) {
+    // FIXME: The 'static_cast' is necessary, otherwise the result turns into a
+    // uint64_t, which messes things up if we have a negative offset difference.
+    Diff = *Diff / static_cast<int64_t>(CGM.getContext().getCharWidth());
+
+    // Create a GEP with the byte offset between the counted object and the
+    // count and use that to load the count value.
+    Addr = Builder.CreatePointerBitCastOrAddrSpaceCast(Addr, Int8PtrTy, Int8Ty);
+
+    llvm::Type *CountTy = ConvertType(CountFD->getType());
+    llvm::Value *Res =
+        Builder.CreateInBoundsGEP(Int8Ty, Addr.emitRawPointer(*this),
+                                  Builder.getInt32(*Diff), ".counted_by.gep");
+    Res = Builder.CreateAlignedLoad(CountTy, Res, getIntAlign(),
+                                    ".counted_by.load");
+
+    // Now emit the bounds checking.
+    EmitBoundsCheckImpl(E, Res, Idx, IdxTy, ArrayTy, Accessed);
+  }
+}
+
 LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
                                                bool Accessed) {
   // The index must always be an integer, which is not an aggregate.  Emit it
@@ -4417,46 +4464,10 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
       ArrayLV = EmitLValue(Array);
     auto *Idx = EmitIdxAfterBase(/*Promote*/true);
 
-    if (SanOpts.has(SanitizerKind::ArrayBounds)) {
-      // If the array being accessed has a "counted_by" attribute, generate
-      // bounds checking code. The "count" field is at the top level of the
-      // struct or in an anonymous struct, that's also at the top level. Future
-      // expansions may allow the "count" to reside at any place in the struct,
-      // but the value of "counted_by" will be a "simple" path to the count,
-      // i.e. "a.b.count", so we shouldn't need the full force of EmitLValue or
-      // similar to emit the correct GEP.
-      const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-          getLangOpts().getStrictFlexArraysLevel();
-
-      if (const auto *ME = dyn_cast<MemberExpr>(Array);
-          ME &&
-          ME->isFlexibleArrayMemberLike(getContext(), StrictFlexArraysLevel) &&
-          ME->getMemberDecl()->getType()->isCountAttributedType()) {
-        const FieldDecl *FAMDecl = cast<FieldDecl>(ME->getMemberDecl());
-        if (const FieldDecl *CountFD = FAMDecl->findCountedByField()) {
-          if (std::optional<int64_t> Diff =
-                  getOffsetDifferenceInBits(*this, CountFD, FAMDecl)) {
-            CharUnits OffsetDiff = CGM.getContext().toCharUnitsFromBits(*Diff);
-
-            // Create a GEP with a byte offset between the FAM and count and
-            // use that to load the count value.
-            Addr = Builder.CreatePointerBitCastOrAddrSpaceCast(
-                ArrayLV.getAddress(), Int8PtrTy, Int8Ty);
-
-            llvm::Type *CountTy = ConvertType(CountFD->getType());
-            llvm::Value *Res = Builder.CreateInBoundsGEP(
-                Int8Ty, Addr.emitRawPointer(*this),
-                Builder.getInt32(OffsetDiff.getQuantity()), ".counted_by.gep");
-            Res = Builder.CreateAlignedLoad(CountTy, Res, getIntAlign(),
-                                            ".counted_by.load");
-
-            // Now emit the bounds checking.
-            EmitBoundsCheckImpl(E, Res, Idx, E->getIdx()->getType(),
-                                Array->getType(), Accessed);
-          }
-        }
-      }
-    }
+    if (SanOpts.has(SanitizerKind::ArrayBounds))
+      EmitCountedByBoundsChecking(Array, Idx, ArrayLV.getAddress(),
+                                  E->getIdx()->getType(), Array->getType(),
+                                  Accessed, /*FlexibleArray=*/true);
 
     // Propagate the alignment from the array itself to the result.
     QualType arrayType = Array->getType();
@@ -4468,12 +4479,44 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
     EltTBAAInfo = CGM.getTBAAInfoForSubobject(ArrayLV, E->getType());
   } else {
     // The base must be a pointer; emit it with an estimate of its alignment.
-    Addr = EmitPointerWithAlignment(E->getBase(), &EltBaseInfo, &EltTBAAInfo);
+    Address BaseAddr =
+        EmitPointerWithAlignment(E->getBase(), &EltBaseInfo, &EltTBAAInfo);
     auto *Idx = EmitIdxAfterBase(/*Promote*/true);
     QualType ptrType = E->getBase()->getType();
-    Addr = emitArraySubscriptGEP(
-        *this, Addr, Idx, E->getType(), !getLangOpts().PointerOverflowDefined,
-        SignedIndices, E->getExprLoc(), &ptrType, E->getBase());
+    Addr = emitArraySubscriptGEP(*this, BaseAddr, Idx, E->getType(),
+                                 !getLangOpts().PointerOverflowDefined,
+                                 SignedIndices, E->getExprLoc(), &ptrType,
+                                 E->getBase());
+
+    if (SanOpts.has(SanitizerKind::ArrayBounds)) {
+      // FIXME: There *has* to be a better way to get the base GEP than
+      // crawling back through the LoadInst.
+      const Expr *Base = E->getBase();
+      while (true) {
+        if (const auto *CE = dyn_cast<CastExpr>(Base)) {
+          if (CE->getCastKind() == CK_LValueToRValue)
+            break;
+          Base = CE->getSubExpr();
+        } else if (const auto *PE = dyn_cast<ParenExpr>(Base)) {
+          Base = PE->getSubExpr();
+        } else {
+          break;
+        }
+      }
+
+      if (const auto *CE = dyn_cast<CastExpr>(Base);
+          CE && CE->getCastKind() == CK_LValueToRValue) {
+        // FIXME: This is a bit fragile. See if we can strengthen it a bit
+        // better.
+        if (const auto *ME = dyn_cast<MemberExpr>(CE->getSubExpr());
+            ME && ME->getMemberDecl()->getType()->isCountAttributedType()) {
+          LValue LV = EmitCheckedLValue(Base, TCK_MemberAccess);
+          EmitCountedByBoundsChecking(E->getBase(), Idx, LV.getAddress(),
+                                      E->getIdx()->getType(), ptrType, Accessed,
+                                      /*FlexibleArray=*/false);
+        }
+      }
+    }
   }
 
   LValue LV = MakeAddrLValue(Addr, E->getType(), EltBaseInfo, EltTBAAInfo);
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 2b1062d6d307c..a98fab9bb70f8 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3346,6 +3346,13 @@ class CodeGenFunction : public CodeGenTypeCache {
   llvm::Value *EmitLoadOfCountedByField(const Expr *Base, const FieldDecl *FD,
                                         const FieldDecl *CountDecl);
 
+  // Emit bounds checking for flexible array and pointer members with the
+  // counted_by attribute.
+  void EmitCountedByBoundsChecking(const Expr *E, llvm::Value *Idx,
+                                   Address Addr, QualType IdxTy,
+                                   QualType ArrayTy, bool Accessed,
+                                   bool FlexibleArray);
+
   llvm::Value *EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,
                                        bool isInc, bool isPre);
   ComplexPairTy EmitComplexPrePostIncDec(const UnaryOperator *E, LValue LV,
@@ -5373,10 +5380,18 @@ class CodeGenFunction : public CodeGenTypeCache {
                                      llvm::IntegerType *ResType,
                                      llvm::Value *EmittedE, bool IsDynamic);
 
-  llvm::Value *emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
-                                       unsigned Type,
+  llvm::Value *emitCountedBySize(const Expr *E, llvm::Value *EmittedE,
+                                 unsigned Type, llvm::IntegerType *ResType);
+
+  llvm::Value *emitCountedByMemberSize(const MemberExpr *E, const Expr *Idx,
+                                       llvm::Value *EmittedE, unsigned Type,
                                        llvm::IntegerType *ResType);
 
+  llvm::Value *emitCountedByPointerSize(const ImplicitCastExpr *E,
+                                        const Expr *Idx, llvm::Value *EmittedE,
+                                        unsigned Type,
+                                        llvm::IntegerType *ResType);
+
   void emitZeroOrPatternForAutoVarInit(QualType type, const VarDecl &D,
                                        Address Loc);
 
diff --git a/clang/test/CodeGen/attr-counted-by-for-pointers.c b/clang/test/CodeGen/attr-counted-by-for-pointers.c
new file mode 100644
index 0000000000000..bf215d506746c
--- /dev/null
+++ b/clang/test/CodeGen/attr-counted-by-for-pointers.c
@@ -0,0 +1,302 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 3
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -DWITH_ATTRS -Wall -fsanitize=array-bounds,object-size,local-bounds -fstrict-flex-arrays=3 -fexperimental-late-parse-attributes -emit-llvm -o - %s | FileCheck --check-prefix=SANITIZE-WITH-ATTR %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -DWITH_ATTRS -Wall -fstrict-flex-arrays=3 -fexperimental-late-parse-attributes -emit-llvm -o - %s | FileCheck --check-prefix=NO-SANITIZE-WITH-ATTR %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -Wall -fsanitize=array-bounds,object-size,local-bounds -fstrict-flex-arrays=3 -fexperimental-late-parse-attributes -emit-llvm -o - %s | FileCheck --check-prefix=SANITIZE-WITHOUT-ATTR %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -Wall -fstrict-flex-arrays=3 -fexperimental-late-parse-attributes -emit-llvm -o - %s | FileCheck --check-prefix=NO-SANITIZE-WITHOUT-ATTR %s
+
+#if !__has_attribute(counted_by)
+#error "has attribute broken"
+#endif
+
+#ifdef WITH_ATTRS
+#define __counted_by(member)   __attribute__((__counted_by__(member)))
+#define __sized_by(member)     __attribute__((__sized_by__(member)))
+#else
+#define __counted_by(member)
+#define __sized_by(member)
+#endif
+
+#define __bdos(P)              __builtin_dynamic_object_size(P, 0)
+
+typedef long unsigned int size_t;
+
+struct foo { size_t field; };
+struct annotated_ptr {
+  unsigned long flags;
+  struct foo **buf __counted_by(ptr_count);
+  int ptr_count;
+};
+
+// SANITIZE-WITH-ATTR-LABEL: define dso_local void @test1(
+// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// SANITIZE-WITH-ATTR-NEXT:  entry:
+// SANITIZE-WITH-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// SANITIZE-WITH-ATTR-NEXT:    [[DOTCOUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// SANITIZE-WITH-ATTR-NEXT:    [[DOTCOUNTED_BY_LOAD:%.*]] = load i32, ptr [[DOTCOUNTED_BY_GEP]], align 4
+// SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = zext i32 [[DOTCOUNTED_BY_LOAD]] to i64, !nosanitize [[META2:![0-9]+]]
+// SANITIZE-WITH-ATTR-NEXT:    [[TMP1:%.*]] = icmp ult i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    br i1 [[TMP1]], label [[CONT10:%.*]], 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]]) #[[ATTR3:[0-9]+]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    unreachable, !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR:       cont10:
+// SANITIZE-WITH-ATTR-NEXT:    [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
+// SANITIZE-WITH-ATTR-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA4:![0-9]+]]
+// SANITIZE-WITH-ATTR-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP2]], i64 [[IDXPROM]]
+// SANITIZE-WITH-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA12:![0-9]+]]
+// SANITIZE-WITH-ATTR-NEXT:    ret void
+//
+// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local void @test1(
+// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// NO-SANITIZE-WITH-ATTR-NEXT:  entry:
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2:![0-9]+]]
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
+// NO-SANITIZE-WITH-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA10:![0-9]+]]
+// NO-SANITIZE-WITH-ATTR-NEXT:    ret void
+//
+// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test1(
+// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// SANITIZE-WITHOUT-ATTR-NEXT:  entry:
+// SANITIZE-WITHOUT-ATTR-NEXT:    [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
+// SANITIZE-WITHOUT-ATTR-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2:![0-9]+]]
+// SANITIZE-WITHOUT-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// SANITIZE-WITHOUT-ATTR-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
+// SANITIZE-WITHOUT-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA10:![0-9]+]]
+// SANITIZE-WITHOUT-ATTR-NEXT:    ret void
+//
+// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test1(
+// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:  entry:
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2:![0-9]+]]
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA10:![0-9]+]]
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    ret void
+//
+void test1(struct annotated_ptr *p, int index, struct foo *value) {
+  p->buf[index] = value;
+}
+
+// SANITIZE-WITH-ATTR-LABEL: define dso_local void @test2(
+// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITH-ATTR-NEXT:  entry:
+// SANITIZE-WITH-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// SANITIZE-WITH-ATTR-NEXT:    [[DOTCOUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// SANITIZE-WITH-ATTR-NEXT:    [[DOTCOUNTED_BY_LOAD:%.*]] = load i32, ptr [[DOTCOUNTED_BY_GEP]], align 4
+// SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = zext i32 [[DOTCOUNTED_BY_LOAD]] to i64, !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    [[DOTNOT:%.*]] = icmp ugt i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    br i1 [[DOTNOT]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], label [[CONT10:%.*]], !prof [[PROF14:![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 @[[GLOB3:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR3]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    unreachable, !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR:       cont10:
+// SANITIZE-WITH-ATTR-NEXT:    [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
+// SANITIZE-WITH-ATTR-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA4]]
+// SANITIZE-WITH-ATTR-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP1]], i64 [[IDXPROM]]
+// SANITIZE-WITH-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA12]]
+// SANITIZE-WITH-ATTR-NEXT:    ret void
+//
+// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local void @test2(
+// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// NO-SANITIZE-WITH-ATTR-NEXT:  entry:
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2]]
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
+// NO-SANITIZE-WITH-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA10]]
+// NO-SANITIZE-WITH-ATTR-NEXT:    ret void
+//
+// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test2(
+// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITHOUT-ATTR-NEXT:  entry:
+// SANITIZE-WITHOUT-ATTR-NEXT:    [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
+// SANITIZE-WITHOUT-ATTR-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2]]
+// SANITIZE-WITHOUT-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// SANITIZE-WITHOUT-ATTR-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
+// SANITIZE-WITHOUT-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA10]]
+// SANITIZE-WITHOUT-ATTR-NEXT:    ret void
+//
+// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test2(
+// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:  entry:
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2]]
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA10]]
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    ret void
+//
+void test2(struct annotated_ptr *p, int index, struct foo *value) {
+  *&*&*&p->buf[index] = value;
+}
+
+// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -17179869184, 17179869177) i64 @test3(
+// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITH-ATTR-NEXT:  entry:
+// SANITIZE-WITH-ATTR-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// SANITIZE-WITH-ATTR-NEXT:    [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
+// SANITIZE-WITH-ATTR-NEXT:    [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
+// SANITIZE-WITH-ATTR-NEXT:    [[ARRAY_SIZE:%.*]] = shl nsw i64 [[COUNT]], 3
+// SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = icmp sgt i32 [[COUNTED_BY_LOAD]], -1
+// SANITIZE-WITH-ATTR-NEXT:    [[TMP1:%.*]] = select i1 [[TMP0]], i64 [[ARRAY_SIZE]], i64 0
+// SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP1]]
+//
+// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -17179869184, 17179869177) i64 @test3(
+// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] {
+// NO-SANITIZE-WITH-ATTR-NEXT:  entry:
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[ARRAY_SIZE:%.*]] = shl nsw i64 [[COUNT]], 3
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = icmp sgt i32 [[COUNTED_BY_LOAD]], -1
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP1:%.*]] = select i1 [[TMP0]], i64 [[ARRAY_SIZE]], i64 0
+// NO-SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP1]]
+//
+// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test3(
+// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITHOUT-ATTR-NEXT:  entry:
+// SANITIZE-WITHOUT-ATTR-NEXT:    ret i64 -1
+//
+// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test3(
+// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] {
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:  entry:
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    ret i64 -1
+//
+size_t test3(struct annotated_ptr *p, int index) {
+  return __bdos(p->buf);
+}
+
+// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 17179869177) i64 @test4(
+// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITH-ATTR-NEXT:  entry:
+// SANITIZE-WITH-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// SANITIZE-WITH-ATTR-NEXT:    [[DOTCOUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// SANITIZE-WITH-ATTR-NEXT:    [[DOTCOUNTED_BY_LOAD:%.*]] = load i32, ptr [[DOTCOUNTED_BY_GEP]], align 4
+// SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = zext i32 [[DOTCOUNTED_BY_LOAD]] to i64, !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    [[DOTNOT:%.*]] = icmp ugt i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    br i1 [[DOTNOT]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], label [[CONT8:%.*]], !prof [[PROF14]], !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 [[IDXPROM]]) #[[ATTR3]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    unreachable, !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR:       cont8:
+// SANITIZE-WITH-ATTR-NEXT:    [[COUNT:%.*]] = sext i32 [[DOTCOUNTED_BY_LOAD]] to i64
+// SANITIZE-WITH-ATTR-NEXT:    [[TMP1:%.*]] = sub nsw i64 [[COUNT]], [[IDXPROM]]
+// SANITIZE-WITH-ATTR-NEXT:    [[TMP2:%.*]] = tail call i64 @llvm.smax.i64(i64 [[TMP1]], i64 0)
+// SANITIZE-WITH-ATTR-NEXT:    [[TMP3:%.*]] = shl nuw nsw i64 [[TMP2]], 3
+// SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP3]]
+//
+// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -34359738360, 34359738361) i64 @test4(
+// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
+// NO-SANITIZE-WITH-ATTR-NEXT:  entry:
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = sub nsw i64 [[COUNT]], [[IDXPROM]]
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[RESULT:%.*]] = shl nsw i64 [[TMP0]], 3
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP1:%.*]] = icmp sgt i64 [[TMP0]], -1
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP2:%.*]] = icmp sgt i32 [[INDEX]], -1
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP3:%.*]] = and i1 [[TMP2]], [[TMP1]]
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP4:%.*]] = select i1 [[TMP3]], i64 [[RESULT]], i64 0
+// NO-SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP4]]
+//
+// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test4(
+// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITHOUT-ATTR-NEXT:  entry:
+// SANITIZE-WITHOUT-ATTR-NEXT:    ret i64 -1
+//
+// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test4(
+// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:  entry:
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    ret i64 -1
+//
+size_t test4(struct annotated_ptr *p, int index) {
+  return __bdos(&p->buf[index]);
+}
+
+struct annotated_sized_ptr {
+  unsigned long flags;
+  void *buf __sized_by(ptr_count);
+  int ptr_count;
+};
+
+// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 2147483648) i64 @test5(
+// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITH-ATTR-NEXT:  entry:
+// SANITIZE-WITH-ATTR-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// SANITIZE-WITH-ATTR-NEXT:    [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
+// SANITIZE-WITH-ATTR-NEXT:    [[NARROW:%.*]] = tail call i32 @llvm.smax.i32(i32 [[COUNTED_BY_LOAD]], i32 0)
+// SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = zext nneg i32 [[NARROW]] to i64
+// SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP0]]
+//
+// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 2147483648) i64 @test5(
+// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
+// NO-SANITIZE-WITH-ATTR-NEXT:  entry:
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[NARROW:%.*]] = tail call i32 @llvm.smax.i32(i32 [[COUNTED_BY_LOAD]], i32 0)
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = zext nneg i32 [[NARROW]] to i64
+// NO-SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP0]]
+//
+// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test5(
+// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITHOUT-ATTR-NEXT:  entry:
+// SANITIZE-WITHOUT-ATTR-NEXT:    ret i64 -1
+//
+// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test5(
+// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:  entry:
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    ret i64 -1
+//
+size_t test5(struct annotated_sized_ptr *p, int index) {
+  return __bdos(p->buf);
+}
+
+// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 2147483648) i64 @test6(
+// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITH-ATTR-NEXT:  entry:
+// SANITIZE-WITH-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// SANITIZE-WITH-ATTR-NEXT:    [[DOTCOUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// SANITIZE-WITH-ATTR-NEXT:    [[DOTCOUNTED_BY_LOAD:%.*]] = load i32, ptr [[DOTCOUNTED_BY_GEP]], align 4
+// SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = zext i32 [[DOTCOUNTED_BY_LOAD]] to i64, !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    [[DOTNOT:%.*]] = icmp ugt i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    br i1 [[DOTNOT]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], label [[CONT8:%.*]], !prof [[PROF14]], !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]]) #[[ATTR3]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    unreachable, !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR:       cont8:
+// SANITIZE-WITH-ATTR-NEXT:    [[COUNT:%.*]] = sext i32 [[DOTCOUNTED_BY_LOAD]] to i64
+// SANITIZE-WITH-ATTR-NEXT:    [[RESULT:%.*]] = sub nsw i64 [[COUNT]], [[IDXPROM]]
+// SANITIZE-WITH-ATTR-NEXT:    [[TMP1:%.*]] = tail call i64 @llvm.smax.i64(i64 [[RESULT]], i64 0)
+// SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP1]]
+//
+// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -4294967295, 4294967296) i64 @test6(
+// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
+// NO-SANITIZE-WITH-ATTR-NEXT:  entry:
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[RESULT:%.*]] = sub nsw i64 [[COUNT]], [[IDXPROM]]
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = icmp sgt i64 [[RESULT]], -1
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP1:%.*]] = icmp sgt i32 [[INDEX]], -1
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP2:%.*]] = and i1 [[TMP1]], [[TMP0]]
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0
+// NO-SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP3]]
+//
+// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test6(
+// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITHOUT-ATTR-NEXT:  entry:
+// SANITIZE-WITHOUT-ATTR-NEXT:    ret i64 -1
+//
+// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test6(
+// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:  entry:
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    ret i64 -1
+//
+size_t test6(struct annotated_sized_ptr *p, int index) {
+  return __bdos(&p->buf[index]);
+}

>From 734b79957167ccda2badbe31d4b4caf5aaab9da8 Mon Sep 17 00:00:00 2001
From: Bill Wendling <morbo at google.com>
Date: Tue, 8 Apr 2025 13:56:24 -0700
Subject: [PATCH 2/6] Remove outdated comments.

---
 clang/lib/CodeGen/CGExpr.cpp | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 74ac4e4189b39..ac15485c801a2 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -4489,8 +4489,6 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
                                  E->getBase());
 
     if (SanOpts.has(SanitizerKind::ArrayBounds)) {
-      // FIXME: There *has* to be a better way to get the base GEP than
-      // crawling back through the LoadInst.
       const Expr *Base = E->getBase();
       while (true) {
         if (const auto *CE = dyn_cast<CastExpr>(Base)) {
@@ -4505,9 +4503,7 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
       }
 
       if (const auto *CE = dyn_cast<CastExpr>(Base);
-          CE && CE->getCastKind() == CK_LValueToRValue) {
-        // FIXME: This is a bit fragile. See if we can strengthen it a bit
-        // better.
+          CE && CE->getCastKind() == CK_LValueToRValue)
         if (const auto *ME = dyn_cast<MemberExpr>(CE->getSubExpr());
             ME && ME->getMemberDecl()->getType()->isCountAttributedType()) {
           LValue LV = EmitCheckedLValue(Base, TCK_MemberAccess);
@@ -4515,7 +4511,6 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
                                       E->getIdx()->getType(), ptrType, Accessed,
                                       /*FlexibleArray=*/false);
         }
-      }
     }
   }
 

>From 27b2ad94c6a6b4750edffd1a4242fb343056ef47 Mon Sep 17 00:00:00 2001
From: Bill Wendling <morbo at google.com>
Date: Thu, 17 Apr 2025 13:58:35 -0700
Subject: [PATCH 3/6] Peel out "return positive or zero" into its own function.

---
 clang/lib/CodeGen/CGBuiltin.cpp | 37 +++++++++++++++------------------
 1 file changed, 17 insertions(+), 20 deletions(-)

diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 3662027a7ce9c..8dee079e65c64 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -1072,6 +1072,21 @@ llvm::Value *CodeGenFunction::emitCountedBySize(const Expr *E,
   return nullptr;
 }
 
+static llvm::Value *EmitPositiveResultOrZero(CodeGenFunction &CGF,
+                                             llvm::Value *Res,
+                                             llvm::Value *Index,
+                                             llvm::IntegerType *ResType,
+                                             bool IsSigned) {
+  //  cmp = (array_size >= 0)
+  Value *Cmp = CGF.Builder.CreateIsNotNeg(Res);
+  if (Index)
+    //  cmp = (cmp && index >= 0)
+    Cmp = CGF.Builder.CreateAnd(CGF.Builder.CreateIsNotNeg(Index), Cmp);
+
+  //  return cmp ? result : 0
+  return CGF.Builder.CreateSelect(Cmp, Res, ConstantInt::get(ResType, 0, IsSigned));
+}
+
 llvm::Value *CodeGenFunction::emitCountedByPointerSize(
     const ImplicitCastExpr *E, const Expr *Idx, llvm::Value *EmittedE,
     unsigned Type, llvm::IntegerType *ResType) {
@@ -1185,14 +1200,7 @@ llvm::Value *CodeGenFunction::emitCountedByPointerSize(
     Res = Builder.CreateSub(Res, IndexSize, "result", !IsSigned, IsSigned);
   }
 
-  //  cmp = (array_size >= 0)
-  Value *Cmp = Builder.CreateIsNotNeg(Res);
-  if (Idx)
-    //  cmp = (cmp && index >= 0)
-    Cmp = Builder.CreateAnd(Builder.CreateIsNotNeg(Index), Cmp);
-
-  //  return cmp ? result : 0
-  return Builder.CreateSelect(Cmp, Res, ConstantInt::get(ResType, 0, IsSigned));
+  return EmitPositiveResultOrZero(*this, Res, Index, ResType, IsSigned);
 }
 
 llvm::Value *
@@ -1247,7 +1255,6 @@ CodeGenFunction::emitCountedByMemberSize(const MemberExpr *ME, const Expr *Idx,
   //    result = flexible_array_member_size;
   //
   //    cmp = (result >= 0)
-  //
   //    return cmp ? result : 0;
   //
   // 2) '&ptr->array[idx]':
@@ -1266,7 +1273,6 @@ CodeGenFunction::emitCountedByMemberSize(const MemberExpr *ME, const Expr *Idx,
   //    cmp = (result >= 0)
   //    if (index != 0)
   //        cmp = (cmp && index >= 0)
-  //
   //    return cmp ? result : 0;
   //
   // 3) '&ptr->field':
@@ -1284,7 +1290,6 @@ CodeGenFunction::emitCountedByMemberSize(const MemberExpr *ME, const Expr *Idx,
   //    result = offset_diff + flexible_array_member_size;
   //
   //    cmp = (result >= 0)
-  //
   //    return cmp ? result : 0;
   //
   // 4) '&ptr->field_array[idx]':
@@ -1308,7 +1313,6 @@ CodeGenFunction::emitCountedByMemberSize(const MemberExpr *ME, const Expr *Idx,
   //    cmp = (result >= 0)
   //    if (index != 0)
   //        cmp = (cmp && index >= 0)
-  //
   //    return cmp ? result : 0;
 
   bool IsSigned = CountFD->getType()->isSignedIntegerType();
@@ -1400,14 +1404,7 @@ CodeGenFunction::emitCountedByMemberSize(const MemberExpr *ME, const Expr *Idx,
     Res = Builder.CreateAdd(FlexibleArrayMemberSize, OffsetDiff, "result");
   }
 
-  //  cmp = (array_size >= 0)
-  Value *Cmp = Builder.CreateIsNotNeg(Res);
-  if (Idx)
-    //  cmp = (cmp && index >= 0)
-    Cmp = Builder.CreateAnd(Builder.CreateIsNotNeg(Index), Cmp);
-
-  //  return cmp ? result : 0
-  return Builder.CreateSelect(Cmp, Res, ConstantInt::get(ResType, 0, IsSigned));
+  return EmitPositiveResultOrZero(*this, Res, Index, ResType, IsSigned);
 }
 
 /// Returns a Value corresponding to the size of the given expression.

>From 8fcac297455ef008da4a542fe6410402c2fe129a Mon Sep 17 00:00:00 2001
From: Bill Wendling <morbo at google.com>
Date: Thu, 17 Apr 2025 14:16:09 -0700
Subject: [PATCH 4/6] Refactor the 'count' and 'index' retrievals.

---
 clang/lib/CodeGen/CGBuiltin.cpp | 50 ++++++++++++++++++++-------------
 1 file changed, 30 insertions(+), 20 deletions(-)

diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 8dee079e65c64..3df2dd042add4 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -1087,6 +1087,28 @@ static llvm::Value *EmitPositiveResultOrZero(CodeGenFunction &CGF,
   return CGF.Builder.CreateSelect(Cmp, Res, ConstantInt::get(ResType, 0, IsSigned));
 }
 
+static std::pair<llvm::Value *, llvm::Value *>
+GetCountFieldAndIndex(CodeGenFunction &CGF, const MemberExpr *ME,
+                      const FieldDecl *ArrayFD, const FieldDecl *CountFD,
+                      const Expr *Idx, llvm::IntegerType *ResType,
+                      bool IsSigned) {
+  //  count = ptr->count;
+  Value *Count = CGF.EmitLoadOfCountedByField(ME, ArrayFD, CountFD);
+  if (!Count)
+    return std::make_pair<Value *>(nullptr, nullptr);
+  Count = CGF.Builder.CreateIntCast(Count, ResType, IsSigned, "count");
+
+  //  index = ptr->index;
+  Value *Index = nullptr;
+  if (Idx) {
+    bool IdxSigned = Idx->getType()->isSignedIntegerType();
+    Index = CGF.EmitScalarExpr(Idx);
+    Index = CGF.Builder.CreateIntCast(Index, ResType, IdxSigned, "index");
+  }
+
+  return std::make_pair(Count, Index);
+}
+
 llvm::Value *CodeGenFunction::emitCountedByPointerSize(
     const ImplicitCastExpr *E, const Expr *Idx, llvm::Value *EmittedE,
     unsigned Type, llvm::IntegerType *ResType) {
@@ -1170,18 +1192,12 @@ llvm::Value *CodeGenFunction::emitCountedByPointerSize(
       llvm::ConstantInt::get(ResType, BaseSize.getQuantity(), IsSigned);
 
   //  count = ptr->count;
-  Value *Count = EmitLoadOfCountedByField(ME, ArrayBaseFD, CountFD);
+  //  index = ptr->index;
+  Value *Count, *Index;
+  std::tie(Count, Index) = GetCountFieldAndIndex(
+      *this, ME, ArrayBaseFD, CountFD, Idx, ResType, IsSigned);
   if (!Count)
     return nullptr;
-  Count = Builder.CreateIntCast(Count, ResType, IsSigned, "count");
-
-  //  index = ptr->index;
-  Value *Index = nullptr;
-  if (Idx) {
-    bool IdxSigned = Idx->getType()->isSignedIntegerType();
-    Index = EmitScalarExpr(Idx);
-    Index = Builder.CreateIntCast(Index, ResType, IdxSigned, "index");
-  }
 
   //  array_size = count * array_base_size;
   Value *ArraySize = Builder.CreateMul(Count, ArrayBaseSize, "array_size",
@@ -1335,18 +1351,12 @@ CodeGenFunction::emitCountedByMemberSize(const MemberExpr *ME, const Expr *Idx,
   }
 
   //  count = ptr->count;
-  Value *Count = EmitLoadOfCountedByField(ME, FlexibleArrayMemberFD, CountFD);
+  //  index = ptr->index;
+  Value *Count, *Index;
+  std::tie(Count, Index) = GetCountFieldAndIndex(
+      *this, ME, FlexibleArrayMemberFD, CountFD, Idx, ResType, IsSigned);
   if (!Count)
     return nullptr;
-  Count = Builder.CreateIntCast(Count, ResType, IsSigned, "count");
-
-  //  index = ptr->index;
-  Value *Index = nullptr;
-  if (Idx) {
-    bool IdxSigned = Idx->getType()->isSignedIntegerType();
-    Index = EmitScalarExpr(Idx);
-    Index = Builder.CreateIntCast(Index, ResType, IdxSigned, "index");
-  }
 
   //  flexible_array_member_base_size = sizeof (*ptr->array);
   const ArrayType *ArrayTy = Ctx.getAsArrayType(FlexibleArrayMemberTy);

>From ad73d06615d471faf5bf3607f6d39815446134ed Mon Sep 17 00:00:00 2001
From: Bill Wendling <morbo at google.com>
Date: Thu, 17 Apr 2025 15:00:26 -0700
Subject: [PATCH 5/6] Add tests for deep casts.

---
 clang/lib/CodeGen/CGExpr.cpp                  |   2 +-
 .../CodeGen/attr-counted-by-for-pointers.c    | 203 ++++++++++++++----
 2 files changed, 164 insertions(+), 41 deletions(-)

diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 661b634be851f..f790ae914d1ae 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -4550,7 +4550,7 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
         if (const auto *ME = dyn_cast<MemberExpr>(CE->getSubExpr());
             ME && ME->getMemberDecl()->getType()->isCountAttributedType()) {
           LValue LV = EmitCheckedLValue(Base, TCK_MemberAccess);
-          EmitCountedByBoundsChecking(E->getBase(), Idx, LV.getAddress(),
+          EmitCountedByBoundsChecking(CE->getSubExpr(), Idx, LV.getAddress(),
                                       E->getIdx()->getType(), ptrType, Accessed,
                                       /*FlexibleArray=*/false);
         }
diff --git a/clang/test/CodeGen/attr-counted-by-for-pointers.c b/clang/test/CodeGen/attr-counted-by-for-pointers.c
index bf215d506746c..c1391fcd5f0b1 100644
--- a/clang/test/CodeGen/attr-counted-by-for-pointers.c
+++ b/clang/test/CodeGen/attr-counted-by-for-pointers.c
@@ -43,7 +43,7 @@ struct annotated_ptr {
 // SANITIZE-WITH-ATTR-NEXT:    [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
 // SANITIZE-WITH-ATTR-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA4:![0-9]+]]
 // SANITIZE-WITH-ATTR-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP2]], i64 [[IDXPROM]]
-// SANITIZE-WITH-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA12:![0-9]+]]
+// SANITIZE-WITH-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA13:![0-9]+]]
 // SANITIZE-WITH-ATTR-NEXT:    ret void
 //
 // NO-SANITIZE-WITH-ATTR-LABEL: define dso_local void @test1(
@@ -53,7 +53,7 @@ struct annotated_ptr {
 // NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2:![0-9]+]]
 // NO-SANITIZE-WITH-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
 // NO-SANITIZE-WITH-ATTR-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
-// NO-SANITIZE-WITH-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA10:![0-9]+]]
+// NO-SANITIZE-WITH-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA11:![0-9]+]]
 // NO-SANITIZE-WITH-ATTR-NEXT:    ret void
 //
 // SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test1(
@@ -63,7 +63,7 @@ struct annotated_ptr {
 // SANITIZE-WITHOUT-ATTR-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2:![0-9]+]]
 // SANITIZE-WITHOUT-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
 // SANITIZE-WITHOUT-ATTR-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
-// SANITIZE-WITHOUT-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA10:![0-9]+]]
+// SANITIZE-WITHOUT-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA11:![0-9]+]]
 // SANITIZE-WITHOUT-ATTR-NEXT:    ret void
 //
 // NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test1(
@@ -73,7 +73,7 @@ struct annotated_ptr {
 // NO-SANITIZE-WITHOUT-ATTR-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2:![0-9]+]]
 // NO-SANITIZE-WITHOUT-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
 // NO-SANITIZE-WITHOUT-ATTR-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
-// NO-SANITIZE-WITHOUT-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA10:![0-9]+]]
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA11:![0-9]+]]
 // NO-SANITIZE-WITHOUT-ATTR-NEXT:    ret void
 //
 void test1(struct annotated_ptr *p, int index, struct foo *value) {
@@ -87,16 +87,16 @@ void test1(struct annotated_ptr *p, int index, struct foo *value) {
 // SANITIZE-WITH-ATTR-NEXT:    [[DOTCOUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
 // SANITIZE-WITH-ATTR-NEXT:    [[DOTCOUNTED_BY_LOAD:%.*]] = load i32, ptr [[DOTCOUNTED_BY_GEP]], align 4
 // SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = zext i32 [[DOTCOUNTED_BY_LOAD]] to i64, !nosanitize [[META2]]
-// SANITIZE-WITH-ATTR-NEXT:    [[DOTNOT:%.*]] = icmp ugt i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]]
-// SANITIZE-WITH-ATTR-NEXT:    br i1 [[DOTNOT]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], label [[CONT10:%.*]], !prof [[PROF14:![0-9]+]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    [[TMP1:%.*]] = icmp ult i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    br i1 [[TMP1]], label [[CONT10:%.*]], 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 @[[GLOB3:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR3]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB4:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR3]], !nosanitize [[META2]]
 // SANITIZE-WITH-ATTR-NEXT:    unreachable, !nosanitize [[META2]]
 // SANITIZE-WITH-ATTR:       cont10:
 // SANITIZE-WITH-ATTR-NEXT:    [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
-// SANITIZE-WITH-ATTR-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA4]]
-// SANITIZE-WITH-ATTR-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP1]], i64 [[IDXPROM]]
-// SANITIZE-WITH-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA12]]
+// SANITIZE-WITH-ATTR-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA4]]
+// SANITIZE-WITH-ATTR-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP2]], i64 [[IDXPROM]]
+// SANITIZE-WITH-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA13]]
 // SANITIZE-WITH-ATTR-NEXT:    ret void
 //
 // NO-SANITIZE-WITH-ATTR-LABEL: define dso_local void @test2(
@@ -106,7 +106,7 @@ void test1(struct annotated_ptr *p, int index, struct foo *value) {
 // NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2]]
 // NO-SANITIZE-WITH-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
 // NO-SANITIZE-WITH-ATTR-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
-// NO-SANITIZE-WITH-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA10]]
+// NO-SANITIZE-WITH-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA11]]
 // NO-SANITIZE-WITH-ATTR-NEXT:    ret void
 //
 // SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test2(
@@ -116,7 +116,7 @@ void test1(struct annotated_ptr *p, int index, struct foo *value) {
 // SANITIZE-WITHOUT-ATTR-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2]]
 // SANITIZE-WITHOUT-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
 // SANITIZE-WITHOUT-ATTR-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
-// SANITIZE-WITHOUT-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA10]]
+// SANITIZE-WITHOUT-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA11]]
 // SANITIZE-WITHOUT-ATTR-NEXT:    ret void
 //
 // NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test2(
@@ -126,14 +126,103 @@ void test1(struct annotated_ptr *p, int index, struct foo *value) {
 // NO-SANITIZE-WITHOUT-ATTR-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2]]
 // NO-SANITIZE-WITHOUT-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
 // NO-SANITIZE-WITHOUT-ATTR-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
-// NO-SANITIZE-WITHOUT-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA10]]
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA11]]
 // NO-SANITIZE-WITHOUT-ATTR-NEXT:    ret void
 //
 void test2(struct annotated_ptr *p, int index, struct foo *value) {
+  ((struct foo **)((char *)p->buf))[index] = value;
+}
+
+// SANITIZE-WITH-ATTR-LABEL: define dso_local void @test3(
+// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITH-ATTR-NEXT:  entry:
+// SANITIZE-WITH-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// SANITIZE-WITH-ATTR-NEXT:    [[DOTCOUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// SANITIZE-WITH-ATTR-NEXT:    [[DOTCOUNTED_BY_LOAD:%.*]] = load i32, ptr [[DOTCOUNTED_BY_GEP]], align 4
+// SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = zext i32 [[DOTCOUNTED_BY_LOAD]] to i64, !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    [[DOTNOT:%.*]] = icmp ugt i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    br i1 [[DOTNOT]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], label [[CONT10:%.*]], !prof [[PROF15:![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 @[[GLOB5:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR3]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    unreachable, !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR:       cont10:
+// SANITIZE-WITH-ATTR-NEXT:    [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
+// SANITIZE-WITH-ATTR-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA4]]
+// SANITIZE-WITH-ATTR-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP1]], i64 [[IDXPROM]]
+// SANITIZE-WITH-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA13]]
+// SANITIZE-WITH-ATTR-NEXT:    ret void
+//
+// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local void @test3(
+// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// NO-SANITIZE-WITH-ATTR-NEXT:  entry:
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2]]
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
+// NO-SANITIZE-WITH-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA11]]
+// NO-SANITIZE-WITH-ATTR-NEXT:    ret void
+//
+// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test3(
+// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITHOUT-ATTR-NEXT:  entry:
+// SANITIZE-WITHOUT-ATTR-NEXT:    [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
+// SANITIZE-WITHOUT-ATTR-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2]]
+// SANITIZE-WITHOUT-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// SANITIZE-WITHOUT-ATTR-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
+// SANITIZE-WITHOUT-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA11]]
+// SANITIZE-WITHOUT-ATTR-NEXT:    ret void
+//
+// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test3(
+// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:  entry:
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2]]
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA11]]
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    ret void
+//
+void test3(struct annotated_ptr *p, int index, struct foo *value) {
   *&*&*&p->buf[index] = value;
 }
 
-// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -17179869184, 17179869177) i64 @test3(
+// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -17179869184, 17179869177) i64 @test4(
+// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITH-ATTR-NEXT:  entry:
+// SANITIZE-WITH-ATTR-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// SANITIZE-WITH-ATTR-NEXT:    [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
+// SANITIZE-WITH-ATTR-NEXT:    [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
+// SANITIZE-WITH-ATTR-NEXT:    [[ARRAY_SIZE:%.*]] = shl nsw i64 [[COUNT]], 3
+// SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = icmp sgt i32 [[COUNTED_BY_LOAD]], -1
+// SANITIZE-WITH-ATTR-NEXT:    [[TMP1:%.*]] = select i1 [[TMP0]], i64 [[ARRAY_SIZE]], i64 0
+// SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP1]]
+//
+// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -17179869184, 17179869177) i64 @test4(
+// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] {
+// NO-SANITIZE-WITH-ATTR-NEXT:  entry:
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[ARRAY_SIZE:%.*]] = shl nsw i64 [[COUNT]], 3
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = icmp sgt i32 [[COUNTED_BY_LOAD]], -1
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP1:%.*]] = select i1 [[TMP0]], i64 [[ARRAY_SIZE]], i64 0
+// NO-SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP1]]
+//
+// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test4(
+// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITHOUT-ATTR-NEXT:  entry:
+// SANITIZE-WITHOUT-ATTR-NEXT:    ret i64 -1
+//
+// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test4(
+// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] {
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:  entry:
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    ret i64 -1
+//
+size_t test4(struct annotated_ptr *p) {
+  return __bdos(p->buf);
+}
+
+// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -17179869184, 17179869177) i64 @test5(
 // SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
 // SANITIZE-WITH-ATTR-NEXT:  entry:
 // SANITIZE-WITH-ATTR-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
@@ -144,8 +233,8 @@ void test2(struct annotated_ptr *p, int index, struct foo *value) {
 // SANITIZE-WITH-ATTR-NEXT:    [[TMP1:%.*]] = select i1 [[TMP0]], i64 [[ARRAY_SIZE]], i64 0
 // SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP1]]
 //
-// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -17179869184, 17179869177) i64 @test3(
-// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] {
+// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -17179869184, 17179869177) i64 @test5(
+// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
 // NO-SANITIZE-WITH-ATTR-NEXT:  entry:
 // NO-SANITIZE-WITH-ATTR-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
 // NO-SANITIZE-WITH-ATTR-NEXT:    [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
@@ -155,21 +244,21 @@ void test2(struct annotated_ptr *p, int index, struct foo *value) {
 // NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP1:%.*]] = select i1 [[TMP0]], i64 [[ARRAY_SIZE]], i64 0
 // NO-SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP1]]
 //
-// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test3(
+// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test5(
 // SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
 // SANITIZE-WITHOUT-ATTR-NEXT:  entry:
 // SANITIZE-WITHOUT-ATTR-NEXT:    ret i64 -1
 //
-// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test3(
-// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] {
+// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test5(
+// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
 // NO-SANITIZE-WITHOUT-ATTR-NEXT:  entry:
 // NO-SANITIZE-WITHOUT-ATTR-NEXT:    ret i64 -1
 //
-size_t test3(struct annotated_ptr *p, int index) {
-  return __bdos(p->buf);
+size_t test5(struct annotated_ptr *p, int index) {
+  return __bdos((struct foo **)((char *)p->buf));
 }
 
-// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 17179869177) i64 @test4(
+// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 17179869177) i64 @test6(
 // SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
 // SANITIZE-WITH-ATTR-NEXT:  entry:
 // SANITIZE-WITH-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
@@ -177,9 +266,9 @@ size_t test3(struct annotated_ptr *p, int index) {
 // SANITIZE-WITH-ATTR-NEXT:    [[DOTCOUNTED_BY_LOAD:%.*]] = load i32, ptr [[DOTCOUNTED_BY_GEP]], align 4
 // SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = zext i32 [[DOTCOUNTED_BY_LOAD]] to i64, !nosanitize [[META2]]
 // SANITIZE-WITH-ATTR-NEXT:    [[DOTNOT:%.*]] = icmp ugt i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]]
-// SANITIZE-WITH-ATTR-NEXT:    br i1 [[DOTNOT]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], label [[CONT8:%.*]], !prof [[PROF14]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    br i1 [[DOTNOT]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], label [[CONT8:%.*]], !prof [[PROF15]], !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 [[IDXPROM]]) #[[ATTR3]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB6:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR3]], !nosanitize [[META2]]
 // SANITIZE-WITH-ATTR-NEXT:    unreachable, !nosanitize [[META2]]
 // SANITIZE-WITH-ATTR:       cont8:
 // SANITIZE-WITH-ATTR-NEXT:    [[COUNT:%.*]] = sext i32 [[DOTCOUNTED_BY_LOAD]] to i64
@@ -188,7 +277,7 @@ size_t test3(struct annotated_ptr *p, int index) {
 // SANITIZE-WITH-ATTR-NEXT:    [[TMP3:%.*]] = shl nuw nsw i64 [[TMP2]], 3
 // SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP3]]
 //
-// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -34359738360, 34359738361) i64 @test4(
+// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -34359738360, 34359738361) i64 @test6(
 // NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
 // NO-SANITIZE-WITH-ATTR-NEXT:  entry:
 // NO-SANITIZE-WITH-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
@@ -203,27 +292,61 @@ size_t test3(struct annotated_ptr *p, int index) {
 // NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP4:%.*]] = select i1 [[TMP3]], i64 [[RESULT]], i64 0
 // NO-SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP4]]
 //
-// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test4(
+// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test6(
 // SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
 // SANITIZE-WITHOUT-ATTR-NEXT:  entry:
 // SANITIZE-WITHOUT-ATTR-NEXT:    ret i64 -1
 //
-// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test4(
+// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test6(
 // NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
 // NO-SANITIZE-WITHOUT-ATTR-NEXT:  entry:
 // NO-SANITIZE-WITHOUT-ATTR-NEXT:    ret i64 -1
 //
-size_t test4(struct annotated_ptr *p, int index) {
+size_t test6(struct annotated_ptr *p, int index) {
   return __bdos(&p->buf[index]);
 }
 
+// SANITIZE-WITH-ATTR-LABEL: define dso_local i64 @test7(
+// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITH-ATTR-NEXT:  entry:
+// SANITIZE-WITH-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// SANITIZE-WITH-ATTR-NEXT:    [[DOTCOUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// SANITIZE-WITH-ATTR-NEXT:    [[DOTCOUNTED_BY_LOAD:%.*]] = load i32, ptr [[DOTCOUNTED_BY_GEP]], align 4
+// SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = zext i32 [[DOTCOUNTED_BY_LOAD]] to i64, !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    [[TMP1:%.*]] = icmp ult i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    br i1 [[TMP1]], label [[CONT10:%.*]], 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 @[[GLOB7:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR3]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    unreachable, !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR:       cont10:
+// SANITIZE-WITH-ATTR-NEXT:    ret i64 -1
+//
+// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local i64 @test7(
+// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] {
+// NO-SANITIZE-WITH-ATTR-NEXT:  entry:
+// NO-SANITIZE-WITH-ATTR-NEXT:    ret i64 -1
+//
+// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test7(
+// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITHOUT-ATTR-NEXT:  entry:
+// SANITIZE-WITHOUT-ATTR-NEXT:    ret i64 -1
+//
+// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test7(
+// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] {
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:  entry:
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    ret i64 -1
+//
+size_t test7(struct annotated_ptr *p, int index) {
+  return __bdos(((struct foo **)(char *)p->buf)[index]);
+}
+
 struct annotated_sized_ptr {
   unsigned long flags;
   void *buf __sized_by(ptr_count);
   int ptr_count;
 };
 
-// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 2147483648) i64 @test5(
+// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 2147483648) i64 @test8(
 // SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
 // SANITIZE-WITH-ATTR-NEXT:  entry:
 // SANITIZE-WITH-ATTR-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
@@ -232,7 +355,7 @@ struct annotated_sized_ptr {
 // SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = zext nneg i32 [[NARROW]] to i64
 // SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP0]]
 //
-// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 2147483648) i64 @test5(
+// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 2147483648) i64 @test8(
 // NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
 // NO-SANITIZE-WITH-ATTR-NEXT:  entry:
 // NO-SANITIZE-WITH-ATTR-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
@@ -241,21 +364,21 @@ struct annotated_sized_ptr {
 // NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = zext nneg i32 [[NARROW]] to i64
 // NO-SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP0]]
 //
-// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test5(
+// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test8(
 // SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
 // SANITIZE-WITHOUT-ATTR-NEXT:  entry:
 // SANITIZE-WITHOUT-ATTR-NEXT:    ret i64 -1
 //
-// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test5(
+// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test8(
 // NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
 // NO-SANITIZE-WITHOUT-ATTR-NEXT:  entry:
 // NO-SANITIZE-WITHOUT-ATTR-NEXT:    ret i64 -1
 //
-size_t test5(struct annotated_sized_ptr *p, int index) {
+size_t test8(struct annotated_sized_ptr *p, int index) {
   return __bdos(p->buf);
 }
 
-// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 2147483648) i64 @test6(
+// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 2147483648) i64 @test9(
 // SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
 // SANITIZE-WITH-ATTR-NEXT:  entry:
 // SANITIZE-WITH-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
@@ -263,9 +386,9 @@ size_t test5(struct annotated_sized_ptr *p, int index) {
 // SANITIZE-WITH-ATTR-NEXT:    [[DOTCOUNTED_BY_LOAD:%.*]] = load i32, ptr [[DOTCOUNTED_BY_GEP]], align 4
 // SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = zext i32 [[DOTCOUNTED_BY_LOAD]] to i64, !nosanitize [[META2]]
 // SANITIZE-WITH-ATTR-NEXT:    [[DOTNOT:%.*]] = icmp ugt i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]]
-// SANITIZE-WITH-ATTR-NEXT:    br i1 [[DOTNOT]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], label [[CONT8:%.*]], !prof [[PROF14]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    br i1 [[DOTNOT]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], label [[CONT8:%.*]], !prof [[PROF15]], !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]]) #[[ATTR3]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB9:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR3]], !nosanitize [[META2]]
 // SANITIZE-WITH-ATTR-NEXT:    unreachable, !nosanitize [[META2]]
 // SANITIZE-WITH-ATTR:       cont8:
 // SANITIZE-WITH-ATTR-NEXT:    [[COUNT:%.*]] = sext i32 [[DOTCOUNTED_BY_LOAD]] to i64
@@ -273,7 +396,7 @@ size_t test5(struct annotated_sized_ptr *p, int index) {
 // SANITIZE-WITH-ATTR-NEXT:    [[TMP1:%.*]] = tail call i64 @llvm.smax.i64(i64 [[RESULT]], i64 0)
 // SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP1]]
 //
-// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -4294967295, 4294967296) i64 @test6(
+// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -4294967295, 4294967296) i64 @test9(
 // NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
 // NO-SANITIZE-WITH-ATTR-NEXT:  entry:
 // NO-SANITIZE-WITH-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
@@ -287,16 +410,16 @@ size_t test5(struct annotated_sized_ptr *p, int index) {
 // NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0
 // NO-SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP3]]
 //
-// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test6(
+// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test9(
 // SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
 // SANITIZE-WITHOUT-ATTR-NEXT:  entry:
 // SANITIZE-WITHOUT-ATTR-NEXT:    ret i64 -1
 //
-// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test6(
+// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test9(
 // NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
 // NO-SANITIZE-WITHOUT-ATTR-NEXT:  entry:
 // NO-SANITIZE-WITHOUT-ATTR-NEXT:    ret i64 -1
 //
-size_t test6(struct annotated_sized_ptr *p, int index) {
+size_t test9(struct annotated_sized_ptr *p, int index) {
   return __bdos(&p->buf[index]);
 }

>From 4439836c3533edbb9bb37d41486d8c29f2d19524 Mon Sep 17 00:00:00 2001
From: Bill Wendling <morbo at google.com>
Date: Thu, 24 Apr 2025 11:40:41 -0700
Subject: [PATCH 6/6] Take casted arrays into account when calculating the
 size.

---
 clang/lib/CodeGen/CGBuiltin.cpp               | 209 +++++++++++-------
 clang/lib/CodeGen/CodeGenFunction.h           |   5 +-
 .../CodeGen/attr-counted-by-for-pointers.c    |  48 ++++
 3 files changed, 180 insertions(+), 82 deletions(-)

diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 3df2dd042add4..683fc488c76dc 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -910,6 +910,7 @@ class StructFieldAccess
 
 public:
   const Expr *ArrayIndex = nullptr;
+  QualType ArrayElementTy;
 
   const Expr *VisitMemberExpr(const MemberExpr *E) {
     if (AddrOfSeen && E->getType()->isArrayType())
@@ -925,6 +926,7 @@ class StructFieldAccess
 
     AddrOfSeen = false; // '&ptr->array[idx]' is okay.
     ArrayIndex = E->getIdx();
+    ArrayElementTy = E->getBase()->getType();
     return Visit(E->getBase());
   }
   const Expr *VisitCastExpr(const CastExpr *E) {
@@ -1059,15 +1061,17 @@ llvm::Value *CodeGenFunction::emitCountedBySize(const Expr *E,
     }
   }
 
+  // __counted_by on either a flexible array member or a pointer into a struct
+  // with a flexible array member.
   if (const auto *ME = dyn_cast<MemberExpr>(E))
-    // This is either a flexible array member or a pointer into a struct with a
-    // flexible array member.
-    return emitCountedByMemberSize(ME, Idx, EmittedE, Type, ResType);
+    return emitCountedByMemberSize(ME, Idx, EmittedE, Visitor.ArrayElementTy,
+                                   Type, ResType);
 
+  // __counted_by on a pointer in a struct.
   if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E);
       ICE && ICE->getCastKind() == CK_LValueToRValue)
-    // This may be a pointer.
-    return emitCountedByPointerSize(ICE, Idx, EmittedE, Type, ResType);
+    return emitCountedByPointerSize(ICE, Idx, EmittedE, Visitor.ArrayElementTy,
+                                    Type, ResType);
 
   return nullptr;
 }
@@ -1111,7 +1115,7 @@ GetCountFieldAndIndex(CodeGenFunction &CGF, const MemberExpr *ME,
 
 llvm::Value *CodeGenFunction::emitCountedByPointerSize(
     const ImplicitCastExpr *E, const Expr *Idx, llvm::Value *EmittedE,
-    unsigned Type, llvm::IntegerType *ResType) {
+    QualType CastedArrayElementTy, unsigned Type, llvm::IntegerType *ResType) {
   assert(E->getCastKind() == CK_LValueToRValue &&
          "must be an LValue to RValue cast");
 
@@ -1144,24 +1148,25 @@ llvm::Value *CodeGenFunction::emitCountedByPointerSize(
   //
   //    count = ptr->count;
   //
-  //    array_base_size = sizeof (*ptr->array);
-  //    array_size = count * array_base_size;
+  //    array_element_size = sizeof (*ptr->array);
+  //    array_size = count * array_element_size;
   //
   //    result = array_size;
   //
   //    cmp = (result >= 0)
   //    return cmp ? result : 0;
   //
-  // 2) '&ptr->array[idx]':
+  // 2) '&((cast) ptr->array)[idx]':
   //
   //    count = ptr->count;
   //    index = idx;
   //
-  //    array_base_size = sizeof (*ptr->array_base_size);
-  //    array_size = count * array_base_size;
+  //    array_element_size = sizeof (*ptr->array);
+  //    array_size = count * array_element_size;
   //
-  //    index_size = index * array_base_size;
+  //    casted_array_element_size = sizeof (*((cast) ptr->array));
   //
+  //    index_size = index * casted_array_element_size;
   //    result = array_size - index_size;
   //
   //    cmp = (result >= 0)
@@ -1169,27 +1174,41 @@ llvm::Value *CodeGenFunction::emitCountedByPointerSize(
   //        cmp  = (cmp && index > 0)
   //    return cmp ? result : 0;
 
-  bool IsSigned = CountFD->getType()->isSignedIntegerType();
+  auto GetElementBaseSize = [&](QualType ElementTy) {
+    CharUnits ElementSize =
+        getContext().getTypeSizeInChars(ElementTy->getPointeeType());
+
+    if (ElementSize.isZero()) {
+      // This might be a __sized_by on a 'void *', which counts bytes, not
+      // elements.
+      auto *CAT = ElementTy->getAs<CountAttributedType>();
+      if (!CAT || (CAT->getKind() != CountAttributedType::SizedBy &&
+                   CAT->getKind() != CountAttributedType::SizedByOrNull))
+        // Okay, not sure what it is now.
+        // FIXME: Should this be an assert?
+        return std::optional<CharUnits>();
+
+      ElementSize = CharUnits::One();
+    }
 
-  //  array_base_size = sizeof (*ptr->array);
-  ASTContext &Ctx = getContext();
-  QualType ArrayBaseTy = ArrayBaseFD->getType()->getPointeeType();
-  CharUnits BaseSize = Ctx.getTypeSizeInChars(ArrayBaseTy);
-  if (BaseSize.isZero()) {
-    // This might be a __sized_by on a 'void *', which counts bytes, not
-    // elements.
-    auto *CAT = ArrayBaseFD->getType()->getAs<CountAttributedType>();
-    if (CAT->getKind() != CountAttributedType::SizedBy &&
-        CAT->getKind() != CountAttributedType::SizedByOrNull)
-      // Okay, not sure what it is now.
-      // FIXME: Should this be an assert?
-      return nullptr;
+    return std::optional<CharUnits>(ElementSize);
+  };
 
-    BaseSize = CharUnits::One();
+  // Get the sizes of the original array element and the casted array element,
+  // if different.
+  std::optional<CharUnits> ArrayElementBaseSize =
+      GetElementBaseSize(ArrayBaseFD->getType());
+  if (!ArrayElementBaseSize)
+    return nullptr;
+
+  std::optional<CharUnits> CastedArrayElementBaseSize = ArrayElementBaseSize;
+  if (!CastedArrayElementTy.isNull() && CastedArrayElementTy->isPointerType()) {
+    CastedArrayElementBaseSize = GetElementBaseSize(CastedArrayElementTy);
+    if (!CastedArrayElementBaseSize)
+      return nullptr;
   }
 
-  auto *ArrayBaseSize =
-      llvm::ConstantInt::get(ResType, BaseSize.getQuantity(), IsSigned);
+  bool IsSigned = CountFD->getType()->isSignedIntegerType();
 
   //  count = ptr->count;
   //  index = ptr->index;
@@ -1199,30 +1218,38 @@ llvm::Value *CodeGenFunction::emitCountedByPointerSize(
   if (!Count)
     return nullptr;
 
-  //  array_size = count * array_base_size;
-  Value *ArraySize = Builder.CreateMul(Count, ArrayBaseSize, "array_size",
+  //  array_element_size = sizeof (*ptr->array)
+  auto *ArrayElementSize = llvm::ConstantInt::get(
+      ResType, ArrayElementBaseSize->getQuantity(), IsSigned);
+
+  //  casted_array_element_size = sizeof (*((cast) ptr->array));
+  auto *CastedArrayElementSize = llvm::ConstantInt::get(
+      ResType, CastedArrayElementBaseSize->getQuantity(), IsSigned);
+
+  //  array_size = count * array_element_size;
+  Value *ArraySize = Builder.CreateMul(Count, ArrayElementSize, "array_size",
                                        !IsSigned, IsSigned);
 
   // Option (1) 'ptr->array'
-  //   result = array_size
-  Value *Res = ArraySize;
+  //  result = array_size
+  Value *Result = ArraySize;
 
-  if (Idx) { // Option (2) '&ptr->array[idx]'
-    //  index_size = index * array_base_size;
-    Value *IndexSize = Builder.CreateMul(ArrayBaseSize, Index, "index_size",
-                                         !IsSigned, IsSigned);
+  if (Idx) { // Option (2) '&((cast) ptr->array)[idx]'
+    //  index_size = index * casted_array_element_size;
+    Value *IndexSize = Builder.CreateMul(Index, CastedArrayElementSize,
+                                         "index_size", !IsSigned, IsSigned);
 
     //  result = result - index_size;
-    Res = Builder.CreateSub(Res, IndexSize, "result", !IsSigned, IsSigned);
+    Result =
+        Builder.CreateSub(Result, IndexSize, "result", !IsSigned, IsSigned);
   }
 
-  return EmitPositiveResultOrZero(*this, Res, Index, ResType, IsSigned);
+  return EmitPositiveResultOrZero(*this, Result, Index, ResType, IsSigned);
 }
 
-llvm::Value *
-CodeGenFunction::emitCountedByMemberSize(const MemberExpr *ME, const Expr *Idx,
-                                         llvm::Value *EmittedE, unsigned Type,
-                                         llvm::IntegerType *ResType) {
+llvm::Value *CodeGenFunction::emitCountedByMemberSize(
+    const MemberExpr *ME, const Expr *Idx, llvm::Value *EmittedE,
+    QualType CastedArrayElementTy, unsigned Type, llvm::IntegerType *ResType) {
   const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl());
   if (!FD)
     return nullptr;
@@ -1264,25 +1291,27 @@ CodeGenFunction::emitCountedByMemberSize(const MemberExpr *ME, const Expr *Idx,
   //
   //    count = ptr->count;
   //
-  //    flexible_array_member_base_size = sizeof (*ptr->array);
+  //    flexible_array_member_element_size = sizeof (*ptr->array);
   //    flexible_array_member_size =
-  //            count * flexible_array_member_base_size;
+  //        count * flexible_array_member_element_size;
   //
   //    result = flexible_array_member_size;
   //
   //    cmp = (result >= 0)
   //    return cmp ? result : 0;
   //
-  // 2) '&ptr->array[idx]':
+  // 2) '&((cast) ptr->array)[idx]':
   //
   //    count = ptr->count;
   //    index = idx;
   //
-  //    flexible_array_member_base_size = sizeof (*ptr->array);
+  //    flexible_array_member_element_size = sizeof (*ptr->array);
   //    flexible_array_member_size =
-  //            count * flexible_array_member_base_size;
+  //        count * flexible_array_member_element_size;
   //
-  //    index_size = index * flexible_array_member_base_size;
+  //    casted_flexible_array_member_element_size =
+  //        sizeof (*((cast) ptr->array));
+  //    index_size = index * casted_flexible_array_member_element_size;
   //
   //    result = flexible_array_member_size - index_size;
   //
@@ -1296,9 +1325,9 @@ CodeGenFunction::emitCountedByMemberSize(const MemberExpr *ME, const Expr *Idx,
   //    count = ptr->count;
   //    sizeof_struct = sizeof (struct s);
   //
-  //    flexible_array_member_base_size = sizeof (*ptr->array);
+  //    flexible_array_member_element_size = sizeof (*ptr->array);
   //    flexible_array_member_size =
-  //            count * flexible_array_member_base_size;
+  //        count * flexible_array_member_element_size;
   //
   //    field_offset = offsetof (struct s, field);
   //    offset_diff = sizeof_struct - field_offset;
@@ -1308,19 +1337,19 @@ CodeGenFunction::emitCountedByMemberSize(const MemberExpr *ME, const Expr *Idx,
   //    cmp = (result >= 0)
   //    return cmp ? result : 0;
   //
-  // 4) '&ptr->field_array[idx]':
+  // 4) '&((cast) ptr->field_array)[idx]':
   //
   //    count = ptr->count;
   //    index = idx;
   //    sizeof_struct = sizeof (struct s);
   //
-  //    flexible_array_member_base_size = sizeof (*ptr->array);
+  //    flexible_array_member_element_size = sizeof (*ptr->array);
   //    flexible_array_member_size =
-  //            count * flexible_array_member_base_size;
+  //        count * flexible_array_member_element_size;
   //
-  //    field_base_size = sizeof (*ptr->field_array);
+  //    casted_field_element_size = sizeof (*((cast) ptr->field_array));
   //    field_offset = offsetof (struct s, field)
-  //    field_offset += index * field_base_size;
+  //    field_offset += index * casted_field_element_size;
   //
   //    offset_diff = sizeof_struct - field_offset;
   //
@@ -1334,7 +1363,6 @@ CodeGenFunction::emitCountedByMemberSize(const MemberExpr *ME, const Expr *Idx,
   bool IsSigned = CountFD->getType()->isSignedIntegerType();
 
   QualType FlexibleArrayMemberTy = FlexibleArrayMemberFD->getType();
-  QualType FieldTy = FD->getType();
 
   // Explicit cast because otherwise the CharWidth will promote an i32's into
   // u64's leading to overflows.
@@ -1358,31 +1386,43 @@ CodeGenFunction::emitCountedByMemberSize(const MemberExpr *ME, const Expr *Idx,
   if (!Count)
     return nullptr;
 
-  //  flexible_array_member_base_size = sizeof (*ptr->array);
+  //  flexible_array_member_element_size = sizeof (*ptr->array);
   const ArrayType *ArrayTy = Ctx.getAsArrayType(FlexibleArrayMemberTy);
   CharUnits BaseSize = Ctx.getTypeSizeInChars(ArrayTy->getElementType());
-  auto *FlexibleArrayMemberBaseSize =
+  auto *FlexibleArrayMemberElementSize =
       llvm::ConstantInt::get(ResType, BaseSize.getQuantity(), IsSigned);
 
-  //  flexible_array_member_size =
-  //          count * flexible_array_member_base_size;
+  //  flexible_array_member_size = count * flexible_array_member_element_size;
   Value *FlexibleArrayMemberSize =
-      Builder.CreateMul(Count, FlexibleArrayMemberBaseSize,
+      Builder.CreateMul(Count, FlexibleArrayMemberElementSize,
                         "flexible_array_member_size", !IsSigned, IsSigned);
 
-  Value *Res = nullptr;
+  Value *Result = nullptr;
   if (FlexibleArrayMemberFD == FD) {
-    if (Idx) { // Option (2) '&ptr->array[idx]'
-      //  index_size = index * flexible_array_member_base_size;
-      Value *IndexSize = Builder.CreateMul(FlexibleArrayMemberBaseSize, Index,
-                                           "index_size", !IsSigned, IsSigned);
+    if (Idx) { // Option (2) '&((cast) ptr->array)[idx]'
+      //  casted_flexible_array_member_element_size =
+      //      sizeof (*((cast) ptr->array));
+      llvm::ConstantInt *CastedFlexibleArrayMemberElementSize =
+          FlexibleArrayMemberElementSize;
+      if (!CastedArrayElementTy.isNull() &&
+          CastedArrayElementTy->isPointerType()) {
+        CharUnits BaseSize =
+            Ctx.getTypeSizeInChars(CastedArrayElementTy->getPointeeType());
+        CastedFlexibleArrayMemberElementSize =
+            llvm::ConstantInt::get(ResType, BaseSize.getQuantity(), IsSigned);
+      }
+
+      //  index_size = index * casted_flexible_array_member_element_size;
+      Value *IndexSize =
+          Builder.CreateMul(Index, CastedFlexibleArrayMemberElementSize,
+                            "index_size", !IsSigned, IsSigned);
 
       //  result = flexible_array_member_size - index_size;
-      Res = Builder.CreateSub(FlexibleArrayMemberSize, IndexSize, "result",
-                              !IsSigned, IsSigned);
+      Result = Builder.CreateSub(FlexibleArrayMemberSize, IndexSize, "result",
+                                 !IsSigned, IsSigned);
     } else { // Option (1) 'ptr->array'
       //  result = flexible_array_member_size;
-      Res = FlexibleArrayMemberSize;
+      Result = FlexibleArrayMemberSize;
     }
   } else {
     //  sizeof_struct = sizeof (struct s);
@@ -1392,29 +1432,36 @@ CodeGenFunction::emitCountedByMemberSize(const MemberExpr *ME, const Expr *Idx,
     Value *SizeofStruct =
         llvm::ConstantInt::get(ResType, Size.getKnownMinValue() / CharWidth);
 
-    if (Idx) { // Option (4) '&ptr->field_array[idx]'
-      //  field_base_size = sizeof (*ptr->field_array);
-      const ArrayType *ArrayTy = Ctx.getAsArrayType(FieldTy);
-      CharUnits BaseSize = Ctx.getTypeSizeInChars(ArrayTy->getElementType());
-      auto *FieldBaseSize =
+    if (Idx) { // Option (4) '&((cast) ptr->field_array)[idx]'
+      //  casted_field_element_size = sizeof (*((cast) ptr->field_array));
+      CharUnits BaseSize;
+      if (!CastedArrayElementTy.isNull() &&
+          CastedArrayElementTy->isPointerType()) {
+        BaseSize =
+            Ctx.getTypeSizeInChars(CastedArrayElementTy->getPointeeType());
+      } else {
+        const ArrayType *ArrayTy = Ctx.getAsArrayType(FD->getType());
+        BaseSize = Ctx.getTypeSizeInChars(ArrayTy->getElementType());
+      }
+
+      llvm::ConstantInt *CastedFieldElementSize =
           llvm::ConstantInt::get(ResType, BaseSize.getQuantity(), IsSigned);
 
-      //  field_offset += index * field_base_size;
-      Value *Mul = Builder.CreateMul(Index, FieldBaseSize, "field_offset",
-                                     !IsSigned, IsSigned);
+      //  field_offset += index * casted_field_element_size;
+      Value *Mul = Builder.CreateMul(Index, CastedFieldElementSize,
+                                     "field_offset", !IsSigned, IsSigned);
       FieldOffset = Builder.CreateAdd(FieldOffset, Mul);
     }
     // Option (3) '&ptr->field', and Option (4) continuation.
-
     //  offset_diff = flexible_array_member_offset - field_offset;
     Value *OffsetDiff = Builder.CreateSub(SizeofStruct, FieldOffset,
                                           "offset_diff", !IsSigned, IsSigned);
 
     //  result = offset_diff + flexible_array_member_size;
-    Res = Builder.CreateAdd(FlexibleArrayMemberSize, OffsetDiff, "result");
+    Result = Builder.CreateAdd(FlexibleArrayMemberSize, OffsetDiff, "result");
   }
 
-  return EmitPositiveResultOrZero(*this, Res, Index, ResType, IsSigned);
+  return EmitPositiveResultOrZero(*this, Result, Index, ResType, IsSigned);
 }
 
 /// Returns a Value corresponding to the size of the given expression.
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index c148296e0d096..ab5f4809d502f 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -5412,11 +5412,14 @@ class CodeGenFunction : public CodeGenTypeCache {
                                  unsigned Type, llvm::IntegerType *ResType);
 
   llvm::Value *emitCountedByMemberSize(const MemberExpr *E, const Expr *Idx,
-                                       llvm::Value *EmittedE, unsigned Type,
+                                       llvm::Value *EmittedE,
+                                       QualType CastedArrayElementTy,
+                                       unsigned Type,
                                        llvm::IntegerType *ResType);
 
   llvm::Value *emitCountedByPointerSize(const ImplicitCastExpr *E,
                                         const Expr *Idx, llvm::Value *EmittedE,
+                                        QualType CastedArrayElementTy,
                                         unsigned Type,
                                         llvm::IntegerType *ResType);
 
diff --git a/clang/test/CodeGen/attr-counted-by-for-pointers.c b/clang/test/CodeGen/attr-counted-by-for-pointers.c
index c1391fcd5f0b1..24076541168d4 100644
--- a/clang/test/CodeGen/attr-counted-by-for-pointers.c
+++ b/clang/test/CodeGen/attr-counted-by-for-pointers.c
@@ -423,3 +423,51 @@ size_t test8(struct annotated_sized_ptr *p, int index) {
 size_t test9(struct annotated_sized_ptr *p, int index) {
   return __bdos(&p->buf[index]);
 }
+
+// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 2147483648) i64 @test10(
+// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITH-ATTR-NEXT:  entry:
+// SANITIZE-WITH-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// SANITIZE-WITH-ATTR-NEXT:    [[DOTCOUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// SANITIZE-WITH-ATTR-NEXT:    [[DOTCOUNTED_BY_LOAD:%.*]] = load i32, ptr [[DOTCOUNTED_BY_GEP]], align 4
+// SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = zext i32 [[DOTCOUNTED_BY_LOAD]] to i64, !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    [[DOTNOT:%.*]] = icmp ugt i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    br i1 [[DOTNOT]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], label [[CONT8:%.*]], !prof [[PROF15]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR:       handler.out_of_bounds:
+// SANITIZE-WITH-ATTR-NEXT:    tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB11:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR3]], !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR-NEXT:    unreachable, !nosanitize [[META2]]
+// SANITIZE-WITH-ATTR:       cont8:
+// SANITIZE-WITH-ATTR-NEXT:    [[COUNT:%.*]] = sext i32 [[DOTCOUNTED_BY_LOAD]] to i64
+// SANITIZE-WITH-ATTR-NEXT:    [[INDEX_SIZE:%.*]] = shl nuw nsw i64 [[IDXPROM]], 2
+// SANITIZE-WITH-ATTR-NEXT:    [[RESULT:%.*]] = sub nsw i64 [[COUNT]], [[INDEX_SIZE]]
+// SANITIZE-WITH-ATTR-NEXT:    [[TMP1:%.*]] = tail call i64 @llvm.smax.i64(i64 [[RESULT]], i64 0)
+// SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP1]]
+//
+// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -10737418236, 10737418240) i64 @test10(
+// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
+// NO-SANITIZE-WITH-ATTR-NEXT:  entry:
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[INDEX_SIZE:%.*]] = shl nsw i64 [[IDXPROM]], 2
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[RESULT:%.*]] = sub nsw i64 [[COUNT]], [[INDEX_SIZE]]
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP0:%.*]] = icmp sgt i64 [[RESULT]], -1
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP1:%.*]] = icmp sgt i32 [[INDEX]], -1
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP2:%.*]] = and i1 [[TMP1]], [[TMP0]]
+// NO-SANITIZE-WITH-ATTR-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0
+// NO-SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP3]]
+//
+// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test10(
+// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SANITIZE-WITHOUT-ATTR-NEXT:  entry:
+// SANITIZE-WITHOUT-ATTR-NEXT:    ret i64 -1
+//
+// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test10(
+// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:  entry:
+// NO-SANITIZE-WITHOUT-ATTR-NEXT:    ret i64 -1
+//
+size_t test10(struct annotated_sized_ptr *p, int index) {
+  return __bdos(&((unsigned int *) p->buf)[index]);
+}



More information about the cfe-commits mailing list