[clang] [Clang] Make __builtin_assume_dereferenceable constexpr (PR #169869)
NagaChaitanya Vellanki via cfe-commits
cfe-commits at lists.llvm.org
Thu Nov 27 19:53:22 PST 2025
https://github.com/chaitanyav created https://github.com/llvm/llvm-project/pull/169869
Enable constant evaluation of __builtin_assume_dereferenceable. During evaluation, the pointer is validated and it is verified whether the requested bytes are dereferenceable.
Resolves:#168335
>From 2ec37654d31b0ea77fc16622cfb715a050edc263 Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Thu, 27 Nov 2025 19:18:31 -0800
Subject: [PATCH] [Clang] Make __builtin_assume_dereferenceable constexpr
Enable constant evaluation of __builtin_assume_dereferenceable.
During evaluation, we verify the pointer is valid and the requested
bytes are dereferenceable.
Resolves:#168335
---
clang/include/clang/Basic/Builtins.td | 2 +-
clang/lib/AST/ByteCode/InterpBuiltin.cpp | 34 ++++++++++
clang/lib/AST/ExprConstant.cpp | 29 +++++++++
...iltin-assume-dereferenceable-constexpr.cpp | 64 +++++++++++++++++++
4 files changed, 128 insertions(+), 1 deletion(-)
create mode 100644 clang/test/SemaCXX/builtin-assume-dereferenceable-constexpr.cpp
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 6d6104a3ddb8d..226ad1cc03078 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -859,7 +859,7 @@ def BuiltinAssumeAligned : Builtin {
def BuiltinAssumeDereferenceable : Builtin {
let Spellings = ["__builtin_assume_dereferenceable"];
- let Attributes = [NoThrow, Const];
+ let Attributes = [NoThrow, Const, Constexpr];
let Prototype = "void(void const*, size_t)";
}
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index d21f42d94d3a5..a29e7977b9d00 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2215,6 +2215,37 @@ static unsigned computePointerOffset(const ASTContext &ASTCtx,
return Result;
}
+/// __builtin_assume_dereferenceable(Ptr, Size)
+static bool interp__builtin_assume_dereferenceable(InterpState &S, CodePtr OpPC,
+ const InterpFrame *Frame,
+ const CallExpr *Call) {
+ assert(Call->getNumArgs() == 2);
+
+ APSInt ReqSize = popToAPSInt(S.Stk, *S.Ctx.classify(Call->getArg(1)));
+ if (ReqSize.getZExtValue() < 1)
+ return false;
+
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ if (Ptr.isZero() || !Ptr.isLive() || !Ptr.isBlockPointer() || Ptr.isPastEnd())
+ return false;
+
+ const ASTContext &ASTCtx = S.getASTContext();
+ const Descriptor *DeclDesc = Ptr.getDeclDesc();
+ std::optional<unsigned> FullSize = computeFullDescSize(ASTCtx, DeclDesc);
+ if (!FullSize)
+ return false;
+
+ unsigned ByteOffset = computePointerOffset(ASTCtx, Ptr);
+ if (ByteOffset > *FullSize)
+ return false;
+
+ unsigned RemainingSpace = *FullSize - ByteOffset;
+ if (RemainingSpace < ReqSize.getZExtValue())
+ return false;
+
+ return true;
+}
+
/// Does Ptr point to the last subobject?
static bool pointsToLastObject(const Pointer &Ptr) {
Pointer P = Ptr;
@@ -3749,6 +3780,9 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
case Builtin::BI__assume:
return interp__builtin_assume(S, OpPC, Frame, Call);
+ case Builtin::BI__builtin_assume_dereferenceable:
+ return interp__builtin_assume_dereferenceable(S, OpPC, Frame, Call);
+
case Builtin::BI__builtin_strcmp:
case Builtin::BIstrcmp:
case Builtin::BI__builtin_strncmp:
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index cab17ecdc7b29..2100fb91cbd93 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -19690,6 +19690,35 @@ class VoidExprEvaluator
// The argument is not evaluated!
return true;
+ case Builtin::BI__builtin_assume_dereferenceable: {
+ assert(E->getType()->isVoidType());
+ assert(E->getNumArgs() == 2);
+
+ APSInt ReqSizeVal;
+ if (!::EvaluateInteger(E->getArg(1), ReqSizeVal, Info))
+ return false;
+ LValue Pointer;
+ if (!EvaluatePointer(E->getArg(0), Pointer, Info))
+ return false;
+ if (Pointer.Designator.Invalid)
+ return false;
+ if (Pointer.isNullPointer())
+ return false;
+
+ uint64_t ReqSize = ReqSizeVal.getZExtValue();
+ if (ReqSize < 1)
+ return false;
+ CharUnits EndOffset;
+ if (!determineEndOffset(Info, E->getExprLoc(), 0, Pointer, EndOffset))
+ return false;
+
+ uint64_t TotalSize =
+ (EndOffset - Pointer.getLValueOffset()).getQuantity();
+ if (TotalSize < ReqSize) {
+ return false;
+ }
+ return true;
+ }
case Builtin::BI__builtin_operator_delete:
return HandleOperatorDeleteCall(Info, E);
diff --git a/clang/test/SemaCXX/builtin-assume-dereferenceable-constexpr.cpp b/clang/test/SemaCXX/builtin-assume-dereferenceable-constexpr.cpp
new file mode 100644
index 0000000000000..158eb78cbdabc
--- /dev/null
+++ b/clang/test/SemaCXX/builtin-assume-dereferenceable-constexpr.cpp
@@ -0,0 +1,64 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 -triple x86_64-unknown-unknown %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 -triple x86_64-unknown-unknown %s -fexperimental-new-constant-interpreter
+
+constexpr int arr[10] = {};
+
+constexpr bool test_constexpr_valid() {
+ __builtin_assume_dereferenceable(arr, 40);
+ return true;
+}
+static_assert(test_constexpr_valid(), "");
+
+constexpr bool test_constexpr_partial() {
+ __builtin_assume_dereferenceable(&arr[5], 20);
+ return true;
+}
+static_assert(test_constexpr_partial(), "");
+
+constexpr bool test_constexpr_nullptr() {
+ __builtin_assume_dereferenceable(nullptr, 4);
+ return true;
+}
+static_assert(test_constexpr_nullptr(), ""); // expected-error {{not an integral constant expression}}
+
+constexpr bool test_constexpr_too_large() {
+ __builtin_assume_dereferenceable(arr, 100);
+ return true;
+}
+static_assert(test_constexpr_too_large(), ""); // expected-error {{not an integral constant expression}}
+
+constexpr int single_var = 42;
+constexpr bool test_single_var() {
+ __builtin_assume_dereferenceable(&single_var, 4);
+ return true;
+}
+static_assert(test_single_var(), "");
+
+constexpr bool test_exact_boundary() {
+ __builtin_assume_dereferenceable(&arr[9], 4);
+ return true;
+}
+static_assert(test_exact_boundary(), "");
+
+constexpr bool test_one_over() {
+ __builtin_assume_dereferenceable(&arr[9], 5);
+ return true;
+}
+static_assert(test_one_over(), ""); // expected-error {{not an integral constant expression}}
+
+constexpr bool test_zero_size() {
+ __builtin_assume_dereferenceable(arr, 0);
+ return true;
+}
+static_assert(test_zero_size(), ""); // expected-error {{not an integral constant expression}}
+
+struct S {
+ int x;
+ int y;
+};
+constexpr S s = {1, 2};
+constexpr bool test_struct_member() {
+ __builtin_assume_dereferenceable(&s.x, 4);
+ return true;
+}
+static_assert(test_struct_member(), "");
More information about the cfe-commits
mailing list