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
