[clang] [Clang] Allow non-constant sizes for __builtin_assume_dereferenceable. (PR #156929)
Florian Hahn via cfe-commits
cfe-commits at lists.llvm.org
Thu Sep 4 10:00:10 PDT 2025
https://github.com/fhahn created https://github.com/llvm/llvm-project/pull/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);
}
>From b024720658c89da650cd2e8970e1ae7fc0b69859 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Thu, 4 Sep 2025 17:50:24 +0100
Subject: [PATCH] [Clang] Allow non-constant sizes for
__builtin_assume_dereferenceable.
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);
}
---
clang/docs/ReleaseNotes.rst | 1 +
clang/include/clang/Basic/Builtins.td | 2 +-
.../CodeGen/builtin-assume-dereferenceable.c | 59 +++++++++++++++++++
.../builtin-assume-dereferenceable.cpp | 9 ++-
4 files changed, 68 insertions(+), 3 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index bec001bf8e353..12974c128d5e7 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];
+}
More information about the cfe-commits
mailing list