https://github.com/sebpop updated 
https://github.com/llvm/llvm-project/pull/159046

>From 860e601973a4ee4dae0cd743714c862b57aec5b2 Mon Sep 17 00:00:00 2001
From: Sebastian Pop <[email protected]>
Date: Tue, 16 Sep 2025 06:23:44 -0500
Subject: [PATCH] [clang] add array out-of-bounds access constraints using
 llvm.assume

Following C and C++ standards, generate llvm.assume statements for array
subscript bounds to provide optimization hints.

For this code:
```
int arr[10];
int example(int i) {
  return arr[i];
}
```
clang now generates an `assume(i < 10)`:
```
define i32 @example(i32 noundef %i) local_unnamed_addr #0 {
entry:
  %idxprom = zext nneg i32 %i to i64
  %bounds.constraint = icmp ult i32 %i, 10
  tail call void @llvm.assume(i1 %bounds.constraint)
  %arrayidx = getelementptr inbounds nuw i32, ptr @arr, i64 %idxprom
  %0 = load i32, ptr %arrayidx, align 4, !tbaa !2
  ret i32 %0
}
```
---
 clang/include/clang/Basic/CodeGenOptions.def  |   1 +
 clang/include/clang/Options/Options.td        |   5 +
 clang/lib/CodeGen/CGExpr.cpp                  | 177 +++++++++++++++++-
 clang/lib/CodeGen/CGExprScalar.cpp            |   5 +-
 clang/lib/CodeGen/CodeGenFunction.h           |   9 +
 .../CodeGen/array-bounds-constraints-safety.c | 106 +++++++++++
 .../array-bounds-constraints-strict-flex.c    |  35 ++++
 clang/test/CodeGen/array-bounds-constraints.c |  44 +++++
 8 files changed, 374 insertions(+), 8 deletions(-)
 create mode 100644 clang/test/CodeGen/array-bounds-constraints-safety.c
 create mode 100644 clang/test/CodeGen/array-bounds-constraints-strict-flex.c
 create mode 100644 clang/test/CodeGen/array-bounds-constraints.c

diff --git a/clang/include/clang/Basic/CodeGenOptions.def 
b/clang/include/clang/Basic/CodeGenOptions.def
index 49374efbeb28b..2f3d4870cac18 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -39,6 +39,7 @@ CODEGENOPT(ImplicitMapSyms, 1, 0, Benign) ///< 
-Wa,-mmapsyms=implicit
 CODEGENOPT(AsmVerbose        , 1, 0, Benign) ///< -dA, -fverbose-asm.
 CODEGENOPT(PreserveAsmComments, 1, 1, Benign) ///< -dA, 
-fno-preserve-as-comments.
 CODEGENOPT(AssumeSaneOperatorNew , 1, 1, Benign) ///< implicit 
__attribute__((malloc)) operator new
+CODEGENOPT(AssumeArrayBounds , 1, 0, Benign) ///< Generate llvm.assume for 
array bounds.
 CODEGENOPT(AssumeUniqueVTables , 1, 1, Benign) ///< Assume a class has only 
one vtable.
 CODEGENOPT(Autolink          , 1, 1, Benign) ///< -fno-autolink
 CODEGENOPT(AutoImport        , 1, 1, Benign) ///< -fno-auto-import
diff --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index 309bc94068a38..f52804bac94fb 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -1670,6 +1670,11 @@ defm assume_unique_vtables : 
BoolFOption<"assume-unique-vtables",
   BothFlags<[], [ClangOption, CLOption]>>;
 
 def fassume_sane_operator_new : Flag<["-"], "fassume-sane-operator-new">, 
Group<f_Group>;
+defm assume_array_bounds : BoolFOption<"assume-array-bounds",
+  CodeGenOpts<"AssumeArrayBounds">, DefaultFalse,
+  PosFlag<SetTrue, [], [ClangOption, CC1Option],
+          "Generate llvm.assume for array bounds to enable optimizations (may 
break code with intentional out-of-bounds access)">,
+  NegFlag<SetFalse, [], [ClangOption, CC1Option]>>;
 def fastcp : Flag<["-"], "fastcp">, Group<f_Group>;
 def fastf : Flag<["-"], "fastf">, Group<f_Group>;
 def fast : Flag<["-"], "fast">, Group<f_Group>;
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 6abe4331db552..35b3ea0f926ea 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1679,8 +1679,14 @@ bool CodeGenFunction::IsWrappedCXXThis(const Expr *Obj) {
 
 LValue CodeGenFunction::EmitCheckedLValue(const Expr *E, TypeCheckKind TCK) {
   LValue LV;
-  if (SanOpts.has(SanitizerKind::ArrayBounds) && isa<ArraySubscriptExpr>(E))
-    LV = EmitArraySubscriptExpr(cast<ArraySubscriptExpr>(E), /*Accessed*/true);
+  // This lvalue is about to be accessed (loaded from or stored to), so the
+  // array subscript must be within the strict bounds. Emitting the subscript 
as
+  // "accessed" lets both the array-bounds sanitizer and the array-bounds
+  // assumes (-fassume-array-bounds) use "index < size" rather than the weaker
+  // one-past-the-end "index <= size" that is required for plain address-of.
+  if (isa<ArraySubscriptExpr>(E) && (SanOpts.has(SanitizerKind::ArrayBounds) ||
+                                     CGM.getCodeGenOpts().AssumeArrayBounds))
+    LV = EmitArraySubscriptExpr(cast<ArraySubscriptExpr>(E), /*Accessed*/ 
true);
   else
     LV = EmitLValue(E);
   if (!isa<DeclRefExpr>(E) && !LV.isBitField() && LV.isSimple()) {
@@ -4976,6 +4982,143 @@ void CodeGenFunction::EmitCountedByBoundsChecking(
   }
 }
 
+/// Emit array bounds constraints using llvm.assume for optimization hints.
+///
+/// C Standard (ISO/IEC 9899:2011 - C11)
+/// Section J.2 (Undefined behavior): An array subscript is out of range, even
+/// if an object is apparently accessible with the given subscript (as in the
+/// lvalue expression a[1][7] given the declaration int a[4][5]) (6.5.6).
+///
+/// Section 6.5.6 (Additive operators): If both the pointer operand and the
+/// result point to elements of the same array object, or one past the last
+/// element of the array object, the evaluation shall not produce an overflow;
+/// otherwise, the behavior is undefined.
+///
+/// C++ Standard (ISO/IEC 14882 - 2017)
+/// Section 8.7 (Additive operators):
+/// 4 When an expression that has integral type is added to or subtracted from 
a
+///   pointer, the result has the type of the pointer operand. If the 
expression
+///   P points to element x[i] of an array object x with n elements,^86 the
+///   expressions P + J and J + P (where J has the value j) point to the
+///   (possibly-hypothetical) element x[i + j] if 0 ≤ i + j ≤ n; otherwise, the
+///   behavior is undefined. Likewise, the expression P - J points to the
+///   (possibly-hypothetical) element x[i − j] if 0 ≤ i − j ≤ n; otherwise, the
+///   behavior is undefined.
+/// ^86 A pointer past the last element of an array x of n elements is
+///     considered to be equivalent to a pointer to a hypothetical element x[n]
+///     for this purpose; see 6.9.2.
+///
+
+/// The standards allow &arr[size] (one-past-the-end) for iterators,
+/// but dereferencing one-past-the-end is UB. This function uses the Accessed
+/// parameter to distinguish: Accessed=true uses strict bounds (index < size),
+/// Accessed=false allows one-past-the-end (index <= size).
+///
+/// Code that intentionally dereferences out-of-bounds (UB) may break with
+/// optimizations. Disabled when -fsanitize=array-bounds is active.
+///
+void CodeGenFunction::EmitArrayBoundsConstraints(const ArraySubscriptExpr *E,
+                                                 llvm::Value *IndexVal,
+                                                 bool Accessed) {
+  // Disable with -fno-assume-array-bounds.
+  if (!CGM.getCodeGenOpts().AssumeArrayBounds)
+    return;
+
+  // Disable at -O0.
+  if (CGM.getCodeGenOpts().OptimizationLevel == 0)
+    return;
+
+  // Disable with array-bounds sanitizer.
+  if (SanOpts.has(SanitizerKind::ArrayBounds))
+    return;
+
+  // Use the provided IndexVal to avoid duplicating side effects.
+  // The caller has already emitted the index expression once.
+  if (!IndexVal)
+    return;
+
+  const Expr *Base = E->getBase();
+  const Expr *Idx = E->getIdx();
+  QualType BaseType = Base->getType();
+
+  if (const auto *ICE = dyn_cast<ImplicitCastExpr>(Base)) {
+    if (ICE->getCastKind() == CK_ArrayToPointerDecay) {
+      BaseType = ICE->getSubExpr()->getType();
+    }
+  }
+
+  // Handle both constant arrays and VLAs (variable-length arrays.)
+  const ConstantArrayType *CAT = getContext().getAsConstantArrayType(BaseType);
+  llvm::Value *VLASize = nullptr;
+
+  if (!CAT) {
+    if (const VariableArrayType *VAT =
+            getContext().getAsVariableArrayType(BaseType))
+      VLASize = getVLASize(VAT).NumElts;
+    else
+      return; // Not a constant or VLA.
+  }
+
+  llvm::APInt ArraySize;
+  if (CAT)
+    ArraySize = CAT->getSize();
+
+  // Don't generate assumes for flexible array members, whose declared size 
does
+  // not reflect the real number of elements. This covers true flexible array
+  // members ("data[]") as well as the trailing "data[1]" and "data[0]" idioms,
+  // and honors -fstrict-flex-arrays so that a declared size is trusted when 
the
+  // user asks for it.
+  if (const auto *ME = dyn_cast<MemberExpr>(Base->IgnoreParenImpCasts())) {
+    const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
+        getLangOpts().getStrictFlexArraysLevel();
+    if (ME->isFlexibleArrayMemberLike(getContext(), StrictFlexArraysLevel))
+      return;
+  }
+
+  QualType IdxType = Idx->getType();
+  llvm::Type *IndexType = ConvertType(IdxType);
+  llvm::Value *ArraySizeVal;
+
+  if (CAT)
+    // Constant array: use compile-time size.
+    ArraySizeVal =
+        llvm::ConstantInt::get(IndexType, ArraySize.getLimitedValue());
+  else
+    // VLA: use runtime size.
+    ArraySizeVal =
+        VLASize->getType() == IndexType
+            ? VLASize
+            : Builder.CreateIntCast(VLASize, IndexType, false, 
"vla.size.cast");
+
+  // Ensure index value has the same type as our constants.
+  if (IndexVal->getType() != IndexType) {
+    bool IsSigned = IdxType->isSignedIntegerOrEnumerationType();
+    IndexVal = Builder.CreateIntCast(IndexVal, IndexType, IsSigned, 
"idx.cast");
+  }
+
+  // Create the bounds constraint. The Accessed parameter indicates whether the
+  // array element will be dereferenced. Per C/C++ standards, &arr[size]
+  // (one-past-the-end) is legal, e.g. for iterators, but dereferencing it is
+  // undefined behavior:
+  //   Accessed == true:  element is dereferenced,  strict bound: index < size.
+  //   Accessed == false: address only, allow one-past-the-end: index <= size.
+  //
+  // The array size is non-negative, so for a signed index the check
+  // "0 <= index (< or <=) size" is equivalent to the same unsigned comparison
+  // of the (sign-extended) index against the size: a negative signed index
+  // wraps to a large unsigned value that fails the check. A single unsigned
+  // comparison therefore handles both signed and unsigned indices.
+  llvm::Value *BoundsConstraint =
+      Accessed ? Builder.CreateICmpULT(IndexVal, ArraySizeVal, "idx.ult.size")
+               : Builder.CreateICmpULE(IndexVal, ArraySizeVal, "idx.ule.size");
+
+  // Mark array bounds assumes with metadata so they can be dropped before
+  // vectorization by DropUnnecessaryAssumesPass to prevent IR bloat.
+  llvm::CallInst *Assume = Builder.CreateAssumption(BoundsConstraint);
+  llvm::MDNode *MD = llvm::MDNode::get(Assume->getContext(), {});
+  Assume->setMetadata("llvm.array.bounds", MD);
+}
+
 LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
                                                bool Accessed) {
   // The index must always be an integer, which is not an aggregate.  Emit it
@@ -5005,13 +5148,20 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const 
ArraySubscriptExpr *E,
   };
   IdxPre = nullptr;
 
+  // Array bounds constraints will be emitted after index evaluation to avoid
+  // duplicating side effects from the index expression.
+
   // If the base is a vector type, then we are forming a vector element lvalue
   // with this subscript.
   if (E->getBase()->getType()->isSubscriptableVectorType() &&
       !isa<ExtVectorElementExpr>(E->getBase())) {
     // Emit the vector as an lvalue to get its address.
     LValue LHS = EmitLValue(E->getBase());
-    auto *Idx = EmitIdxAfterBase(/*Promote*/false);
+    auto *Idx = EmitIdxAfterBase(/*Promote*/ false);
+
+    // Emit array bounds constraints for vector subscripts.
+    EmitArrayBoundsConstraints(E, Idx, Accessed);
+
     assert(LHS.isSimple() && "Can only subscript lvalue vectors here!");
     return LValue::MakeVectorElt(LHS.getAddress(), Idx, 
E->getBase()->getType(),
                                  LHS.getBaseInfo(), TBAAAccessInfo());
@@ -5056,7 +5206,10 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const 
ArraySubscriptExpr *E,
     // it.  It needs to be emitted first in case it's what captures
     // the VLA bounds.
     Addr = EmitPointerWithAlignment(E->getBase(), &EltBaseInfo, &EltTBAAInfo);
-    auto *Idx = EmitIdxAfterBase(/*Promote*/true);
+    auto *Idx = EmitIdxAfterBase(/*Promote*/ true);
+
+    // Emit array bounds constraints for VLA access.
+    EmitArrayBoundsConstraints(E, Idx, Accessed);
 
     // The element count here is the total number of non-VLA elements.
     llvm::Value *numElements = getVLASize(vla).NumElts;
@@ -5080,7 +5233,10 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const 
ArraySubscriptExpr *E,
 
     // Emit the base pointer.
     Addr = EmitPointerWithAlignment(E->getBase(), &EltBaseInfo, &EltTBAAInfo);
-    auto *Idx = EmitIdxAfterBase(/*Promote*/true);
+    auto *Idx = EmitIdxAfterBase(/*Promote*/ true);
+
+    // Emit array bounds constraints for ObjC interface access.
+    EmitArrayBoundsConstraints(E, Idx, Accessed);
 
     CharUnits InterfaceSize = getContext().getTypeSizeInChars(OIT);
     llvm::Value *InterfaceSizeVal =
@@ -5115,7 +5271,10 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const 
ArraySubscriptExpr *E,
       ArrayLV = EmitArraySubscriptExpr(ASE, /*Accessed*/ true);
     else
       ArrayLV = EmitLValue(Array);
-    auto *Idx = EmitIdxAfterBase(/*Promote*/true);
+    auto *Idx = EmitIdxAfterBase(/*Promote*/ true);
+
+    // Emit array bounds constraints for optimization.
+    EmitArrayBoundsConstraints(E, Idx, Accessed);
 
     if (SanOpts.has(SanitizerKind::ArrayBounds))
       EmitCountedByBoundsChecking(Array, Array->getType(), 
ArrayLV.getAddress(),
@@ -5159,7 +5318,11 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const 
ArraySubscriptExpr *E,
     // The base must be a pointer; emit it with an estimate of its alignment.
     Address BaseAddr =
         EmitPointerWithAlignment(E->getBase(), &EltBaseInfo, &EltTBAAInfo);
-    auto *Idx = EmitIdxAfterBase(/*Promote*/true);
+    auto *Idx = EmitIdxAfterBase(/*Promote*/ true);
+
+    // Emit array bounds constraints for pointer-based array access.
+    EmitArrayBoundsConstraints(E, Idx, Accessed);
+
     QualType ptrType = E->getBase()->getType();
     Addr = emitArraySubscriptGEP(*this, BaseAddr, Idx, E->getType(),
                                  !getLangOpts().PointerOverflowDefined,
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp 
b/clang/lib/CodeGen/CGExprScalar.cpp
index 18ed6570730f4..75f1d71adbdb0 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -2213,7 +2213,10 @@ Value 
*ScalarExprEmitter::VisitArraySubscriptExpr(ArraySubscriptExpr *E) {
   QualType IdxTy = E->getIdx()->getType();
 
   if (CGF.SanOpts.has(SanitizerKind::ArrayBounds))
-    CGF.EmitBoundsCheck(E, E->getBase(), Idx, IdxTy, /*Accessed*/true);
+    CGF.EmitBoundsCheck(E, E->getBase(), Idx, IdxTy, /*Accessed*/ true);
+
+  // Emit array bounds constraints for vector element access.
+  CGF.EmitArrayBoundsConstraints(E, Idx, /*Accessed=*/true);
 
   Value *Ret = Builder.CreateExtractElement(Base, Idx, "vecext");
 
diff --git a/clang/lib/CodeGen/CodeGenFunction.h 
b/clang/lib/CodeGen/CodeGenFunction.h
index 6d0718c243812..c3be1dce8d0f4 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3396,6 +3396,15 @@ class CodeGenFunction : public CodeGenTypeCache {
                            llvm::Value *BoundsVal, QualType BoundsType,
                            bool Accessed);
 
+  /// Emit array bounds constraints using llvm.assume for optimization hints.
+  /// Emits assume statements for array bounds without duplicating side 
effects.
+  /// Takes the already-emitted index value to avoid re-evaluating expressions
+  /// with side effects. The Accessed parameter distinguishes:
+  /// - dereferenced use strict bounds: index < size vs.
+  /// - address-only allows one-past: index <= size.
+  void EmitArrayBoundsConstraints(const ArraySubscriptExpr *E,
+                                  llvm::Value *IndexVal, bool Accessed);
+
   /// Returns debug info, with additional annotation if
   /// CGM.getCodeGenOpts().SanitizeAnnotateDebugInfo[Ordinal] is enabled for
   /// any of the ordinals.
diff --git a/clang/test/CodeGen/array-bounds-constraints-safety.c 
b/clang/test/CodeGen/array-bounds-constraints-safety.c
new file mode 100644
index 0000000000000..011d0e798f9b5
--- /dev/null
+++ b/clang/test/CodeGen/array-bounds-constraints-safety.c
@@ -0,0 +1,106 @@
+// Use -O1 -disable-llvm-optzns to check assumes before 
DropUnnecessaryAssumesPass
+// drops them (assumes with llvm.array.bounds metadata are dropped before 
vectorization).
+// RUN: %clang_cc1 -emit-llvm -O1 -disable-llvm-optzns -fassume-array-bounds 
%s -o - | FileCheck %s
+
+// Test that array bounds constraints are NOT applied to cases that might
+// break real-world code with intentional out-of-bounds access patterns.
+
+// CHECK-LABEL: define {{.*}} @test_zero_length_array
+struct ZeroLengthData {
+    int count;
+    int items[0];  // GNU C extension: zero-length array
+};
+
+int test_zero_length_array(struct ZeroLengthData *d, int i) {
+  // CHECK-NOT: call void @llvm.assume
+  // Zero-length array as last field should not generate bounds constraints.
+  return d->items[i];
+}
+
+// CHECK-LABEL: define {{.*}} @test_flexible_array_member
+struct Data {
+    int count;
+    int items[1];  // Flexible array member pattern (pre-C99 style)
+};
+
+int test_flexible_array_member(struct Data *d, int i) {
+  // CHECK-NOT: call void @llvm.assume
+  // Flexible array member pattern should NOT generate bounds constraints.
+  return d->items[i];
+}
+
+// CHECK-LABEL: define {{.*}} @test_not_flexible_array
+struct NotFlexible {
+    int items[1];  // Size 1 array but NOT the last field.
+    int count;     // Something comes after it.
+};
+
+int test_not_flexible_array(struct NotFlexible *s, int i) {
+  // CHECK: call void @llvm.assume{{.*}}!llvm.array.bounds
+  // This is NOT a flexible array pattern, so we generate assume.
+  return s->items[i];
+}
+
+// CHECK-LABEL: define {{.*}} @test_pointer_parameter
+int test_pointer_parameter(int *arr, int i) {
+  // CHECK-NOT: call void @llvm.assume
+  // Pointer parameters should NOT generate bounds constraints.
+  return arr[i];
+}
+
+// CHECK-LABEL: define {{.*}} @test_vla
+void init_vla(int *arr, int n);
+
+int test_vla(int n, int i) {
+  int arr[n];
+  init_vla(arr, n);
+  // CHECK: call void @llvm.assume{{.*}}!llvm.array.bounds
+  // For VLAs, generate bounds constraints using the runtime size.
+  return arr[i];
+}
+
+// CHECK-LABEL: define {{.*}} @test_one_past_end
+extern int extern_array[100];
+int *test_one_past_end(void) {
+  // One-past-the-end address is legal per C standard.
+  // We generate assume(i1 true) because 100 <= 100 is trivially true.
+  // CHECK: call void @llvm.assume(i1 true){{.*}}!llvm.array.bounds
+  return &extern_array[100];
+}
+
+// CHECK-LABEL: define {{.*}} @test_one_past_end_variable
+int *test_one_past_end_variable(int i) {
+  // Taking the address without dereferencing is allowed one-past-the-end, so
+  // the bound is inclusive: index <= size.
+  // CHECK: icmp ule i{{.*}} %{{.*}}, 100
+  // CHECK: call void @llvm.assume{{.*}}!llvm.array.bounds
+  return &extern_array[i];
+}
+
+// CHECK-LABEL: define {{.*}} @test_extern_array
+int test_extern_array(int i) {
+  // A real element access uses the strict bound: index < size.
+  // CHECK: icmp ult i{{.*}} %{{.*}}, 100
+  // CHECK: call void @llvm.assume{{.*}}!llvm.array.bounds
+  // Constant-size global array generates bounds constraints.
+  return extern_array[i];
+}
+
+// CHECK-LABEL: define {{.*}} @test_local_constant_array
+void init_array(int *arr);
+int test_local_constant_array(int i) {
+  int arr[10];
+  init_array(arr);
+  // CHECK: call void @llvm.assume{{.*}}!llvm.array.bounds
+  // Local constant-size array generates bounds constraints.
+  return arr[i];
+}
+
+// CHECK-LABEL: define {{.*}} @test_malloc_array
+int *my_malloc(int);
+int test_malloc_array(int i) {
+  // CHECK-NOT: call void @llvm.assume
+  // Dynamically allocated arrays via pointers do not get bounds constraints.
+  int *x = my_malloc(100 * sizeof(int));
+  return x[i];
+}
diff --git a/clang/test/CodeGen/array-bounds-constraints-strict-flex.c 
b/clang/test/CodeGen/array-bounds-constraints-strict-flex.c
new file mode 100644
index 0000000000000..747b828267463
--- /dev/null
+++ b/clang/test/CodeGen/array-bounds-constraints-strict-flex.c
@@ -0,0 +1,35 @@
+// Verify that -fstrict-flex-arrays controls whether a trailing array is 
treated
+// as a flexible array member for the purpose of the array bounds assumes
+// emitted by -fassume-array-bounds. Use -O1 -disable-llvm-optzns to check the
+// assumes before DropUnnecessaryAssumesPass drops them.
+// RUN: %clang_cc1 -emit-llvm -O1 -disable-llvm-optzns -fassume-array-bounds 
-fstrict-flex-arrays=0 %s -o - | FileCheck %s --check-prefixes=CHECK,LEVEL0
+// RUN: %clang_cc1 -emit-llvm -O1 -disable-llvm-optzns -fassume-array-bounds 
-fstrict-flex-arrays=3 %s -o - | FileCheck %s --check-prefixes=CHECK,LEVEL3
+
+struct TrailingOne {
+  int count;
+  int data[1];
+};
+
+// CHECK-LABEL: define {{.*}} @access_trailing_size_one
+int access_trailing_size_one(struct TrailingOne *s, int i) {
+  // At -fstrict-flex-arrays=0 the trailing "data[1]" is treated as a flexible
+  // array member, so no bounds assume is emitted.
+  // LEVEL0-NOT: call void @llvm.assume
+  // At -fstrict-flex-arrays=3 only "data[]" is flexible, so "data[1]" is a 
real
+  // one-element array and a bounds assume is emitted.
+  // LEVEL3: call void @llvm.assume{{.*}}!llvm.array.bounds
+  return s->data[i];
+}
+
+struct TrailingIncomplete {
+  int count;
+  int data[];
+};
+
+// CHECK-LABEL: define {{.*}} @access_trailing_incomplete
+int access_trailing_incomplete(struct TrailingIncomplete *s, int i) {
+  // A true flexible array member ("data[]") is never given a bounds assume,
+  // regardless of the -fstrict-flex-arrays level.
+  // CHECK-NOT: call void @llvm.assume
+  return s->data[i];
+}
diff --git a/clang/test/CodeGen/array-bounds-constraints.c 
b/clang/test/CodeGen/array-bounds-constraints.c
new file mode 100644
index 0000000000000..41a02ab4dc880
--- /dev/null
+++ b/clang/test/CodeGen/array-bounds-constraints.c
@@ -0,0 +1,44 @@
+// This test verifies that clang generates llvm.assume statements to inform the
+// optimizer that array subscripts are within bounds to enable better 
optimization.
+// Use -O1 -disable-llvm-optzns to check assumes before 
DropUnnecessaryAssumesPass
+// drops them (assumes with llvm.array.bounds metadata are dropped before 
vectorization).
+// RUN: %clang_cc1 -emit-llvm -O1 -disable-llvm-optzns -fassume-array-bounds 
%s -o - | FileCheck %s
+
+// Verify no assumes are generated.
+// RUN: %clang_cc1 -emit-llvm -O1 -disable-llvm-optzns 
-fno-assume-array-bounds %s -o - | FileCheck %s -check-prefix=NO-FLAG
+
+// CHECK-LABEL: define {{.*}} @test_simple_array
+// NO-FLAG-LABEL: define {{.*}} @test_simple_array
+void init_array(int *arr);
+int test_simple_array(int i) {
+  int arr[10];
+  init_array(arr);
+  // CHECK: call void @llvm.assume(i1 %{{.*}}){{.*}}!llvm.array.bounds
+  // NO-FLAG-NOT: call void @llvm.assume
+  return arr[i];
+}
+
+// CHECK-LABEL: define {{.*}} @test_multidimensional_array
+int test_multidimensional_array(int i, int j) {
+  int arr[5][8];
+  init_array(arr[0]);
+  // CHECK: call void @llvm.assume(i1 %{{.*}}){{.*}}!llvm.array.bounds
+  // CHECK: call void @llvm.assume(i1 %{{.*}}){{.*}}!llvm.array.bounds
+  return arr[i][j];
+}
+
+// CHECK-LABEL: define {{.*}} @test_unsigned_index
+int test_unsigned_index(unsigned int i) {
+  int arr[10];
+  init_array(arr);
+  // CHECK: call void @llvm.assume(i1 %{{.*}}){{.*}}!llvm.array.bounds
+  return arr[i];
+}
+
+// CHECK-LABEL: define {{.*}} @test_store
+void test_store(int i, int value) {
+  int arr[10];
+  // CHECK: call void @llvm.assume(i1 %{{.*}}){{.*}}!llvm.array.bounds
+  arr[i] = value;
+  init_array(arr);
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to