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 <[email protected]>
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(), "");

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

Reply via email to