Author: Florian Hahn Date: 2025-09-05T14:51:54Z New Revision: c8d065bf914d7c8feb06aa7978fe43b2a800b17f
URL: https://github.com/llvm/llvm-project/commit/c8d065bf914d7c8feb06aa7978fe43b2a800b17f DIFF: https://github.com/llvm/llvm-project/commit/c8d065bf914d7c8feb06aa7978fe43b2a800b17f.diff LOG: [Clang] Allow non-constant sizes for __builtin_assume_dereferenceable. (#156929) Update Clang's __builtin_assume_dereferenceable to support non-constant lengths. The corresponding assume bundle has been updated to support non-constant sizes in cad62df49a7. The current docs for the builtin don't mention the constant requirement for the size argument, so don't need to be updated: https://clang.llvm.org/docs/LanguageExtensions.html#builtin-assume-dereferenceable A number of patches landed recently to make the optimizer make better use of the dereferenceable assumptions, and once https://github.com/llvm/llvm-project/pull/156730 lands, it can be used to vectorize some early-exit loops, for example std::find with std::vector::iterator: https://godbolt.org/z/qo58PKG37 ``` #include <algorithm> #include <cstddef> #include <vector> auto find(std::vector<short>::iterator first, short s, unsigned size) { auto Addr = __builtin_assume_aligned(std::to_address(first), 2); __builtin_assume_dereferenceable(std::to_address(first), size * sizeof(short)); return std::find(first, first + size, s); } ``` PR: https://github.com/llvm/llvm-project/pull/156929 Added: Modified: clang/docs/ReleaseNotes.rst clang/include/clang/Basic/Builtins.td clang/test/CodeGen/builtin-assume-dereferenceable.c clang/test/SemaCXX/builtin-assume-dereferenceable.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 8522f4123cd97..8720262c33959 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -206,6 +206,7 @@ Non-comprehensive list of changes in this release Currently, the use of ``__builtin_dedup_pack`` is limited to template arguments and base specifiers, it also must be used within a template context. +- ``__builtin_assume_dereferenceable`` now accepts non-constant size operands. New Compiler Flags ------------------ diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 27fc6f008d743..27639f06529cb 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -854,7 +854,7 @@ def BuiltinAssumeAligned : Builtin { def BuiltinAssumeDereferenceable : Builtin { let Spellings = ["__builtin_assume_dereferenceable"]; let Attributes = [NoThrow, Const]; - let Prototype = "void(void const*, _Constant size_t)"; + let Prototype = "void(void const*, size_t)"; } def BuiltinFree : Builtin { diff --git a/clang/test/CodeGen/builtin-assume-dereferenceable.c b/clang/test/CodeGen/builtin-assume-dereferenceable.c index cadffd4a84c26..0dc4ba089ee3a 100644 --- a/clang/test/CodeGen/builtin-assume-dereferenceable.c +++ b/clang/test/CodeGen/builtin-assume-dereferenceable.c @@ -32,3 +32,62 @@ int test2(int *a) { __builtin_assume_dereferenceable(a, 32ull); return a[0]; } + +// CHECK-LABEL: @test3( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[N_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[A:%.*]], ptr [[A_ADDR]], align 8 +// CHECK-NEXT: store i32 [[N:%.*]], ptr [[N_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[N_ADDR]], align 4 +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[TMP0]], i64 [[CONV]]) ] +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[A_ADDR]], align 8 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 0 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 +// CHECK-NEXT: ret i32 [[TMP3]] +// +int test3(int *a, int n) { + __builtin_assume_dereferenceable(a, n); + return a[0]; +} + +// CHECK-LABEL: @test4( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[N_ADDR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: store ptr [[A:%.*]], ptr [[A_ADDR]], align 8 +// CHECK-NEXT: store i64 [[N:%.*]], ptr [[N_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i64, ptr [[N_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[TMP0]], i64 [[TMP1]]) ] +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[A_ADDR]], align 8 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 0 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 +// CHECK-NEXT: ret i32 [[TMP3]] +// +int test4(int *a, unsigned long long n) { + __builtin_assume_dereferenceable(a, n); + return a[0]; +} + +// CHECK-LABEL: @test5( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[N_ADDR:%.*]] = alloca float, align 4 +// CHECK-NEXT: store ptr [[A:%.*]], ptr [[A_ADDR]], align 8 +// CHECK-NEXT: store float [[N:%.*]], ptr [[N_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load float, ptr [[N_ADDR]], align 4 +// CHECK-NEXT: [[CONV:%.*]] = fptoui float [[TMP1]] to i64 +// CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[TMP0]], i64 [[CONV]]) ] +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[A_ADDR]], align 8 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 0 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 +// CHECK-NEXT: ret i32 [[TMP3]] +// +int test5(int *a, float n) { + __builtin_assume_dereferenceable(a, n); + return a[0]; +} diff --git a/clang/test/SemaCXX/builtin-assume-dereferenceable.cpp b/clang/test/SemaCXX/builtin-assume-dereferenceable.cpp index b79b7c059567e..2cbd7ac3507bf 100644 --- a/clang/test/SemaCXX/builtin-assume-dereferenceable.cpp +++ b/clang/test/SemaCXX/builtin-assume-dereferenceable.cpp @@ -18,12 +18,12 @@ int test3(int *a) { } int test4(int *a, unsigned size) { - a = __builtin_assume_dereferenceable(a, size); // expected-error {{argument to '__builtin_assume_dereferenceable' must be a constant integer}} + __builtin_assume_dereferenceable(a, size); return a[0]; } int test5(int *a, unsigned long long size) { - a = __builtin_assume_dereferenceable(a, size); // expected-error {{argument to '__builtin_assume_dereferenceable' must be a constant integer}} + __builtin_assume_dereferenceable(a, size); return a[0]; } @@ -53,3 +53,8 @@ constexpr void *l = __builtin_assume_dereferenceable(p, 4); // expected-error {{ void *foo() { return l; } + +int test10(int *a) { + __builtin_assume_dereferenceable(a, a); // expected-error {{cannot initialize a parameter of type '__size_t' (aka 'unsigned long') with an lvalue of type 'int *'}} + return a[0]; +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits