Skip to content

Commit 2ec3765

Browse files
committed
[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
1 parent b142912 commit 2ec3765

File tree

4 files changed

+128
-1
lines changed

4 files changed

+128
-1
lines changed

clang/include/clang/Basic/Builtins.td

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,7 @@ def BuiltinAssumeAligned : Builtin {
859859

860860
def BuiltinAssumeDereferenceable : Builtin {
861861
let Spellings = ["__builtin_assume_dereferenceable"];
862-
let Attributes = [NoThrow, Const];
862+
let Attributes = [NoThrow, Const, Constexpr];
863863
let Prototype = "void(void const*, size_t)";
864864
}
865865

clang/lib/AST/ByteCode/InterpBuiltin.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2215,6 +2215,37 @@ static unsigned computePointerOffset(const ASTContext &ASTCtx,
22152215
return Result;
22162216
}
22172217

2218+
/// __builtin_assume_dereferenceable(Ptr, Size)
2219+
static bool interp__builtin_assume_dereferenceable(InterpState &S, CodePtr OpPC,
2220+
const InterpFrame *Frame,
2221+
const CallExpr *Call) {
2222+
assert(Call->getNumArgs() == 2);
2223+
2224+
APSInt ReqSize = popToAPSInt(S.Stk, *S.Ctx.classify(Call->getArg(1)));
2225+
if (ReqSize.getZExtValue() < 1)
2226+
return false;
2227+
2228+
const Pointer &Ptr = S.Stk.pop<Pointer>();
2229+
if (Ptr.isZero() || !Ptr.isLive() || !Ptr.isBlockPointer() || Ptr.isPastEnd())
2230+
return false;
2231+
2232+
const ASTContext &ASTCtx = S.getASTContext();
2233+
const Descriptor *DeclDesc = Ptr.getDeclDesc();
2234+
std::optional<unsigned> FullSize = computeFullDescSize(ASTCtx, DeclDesc);
2235+
if (!FullSize)
2236+
return false;
2237+
2238+
unsigned ByteOffset = computePointerOffset(ASTCtx, Ptr);
2239+
if (ByteOffset > *FullSize)
2240+
return false;
2241+
2242+
unsigned RemainingSpace = *FullSize - ByteOffset;
2243+
if (RemainingSpace < ReqSize.getZExtValue())
2244+
return false;
2245+
2246+
return true;
2247+
}
2248+
22182249
/// Does Ptr point to the last subobject?
22192250
static bool pointsToLastObject(const Pointer &Ptr) {
22202251
Pointer P = Ptr;
@@ -3749,6 +3780,9 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
37493780
case Builtin::BI__assume:
37503781
return interp__builtin_assume(S, OpPC, Frame, Call);
37513782

3783+
case Builtin::BI__builtin_assume_dereferenceable:
3784+
return interp__builtin_assume_dereferenceable(S, OpPC, Frame, Call);
3785+
37523786
case Builtin::BI__builtin_strcmp:
37533787
case Builtin::BIstrcmp:
37543788
case Builtin::BI__builtin_strncmp:

clang/lib/AST/ExprConstant.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19690,6 +19690,35 @@ class VoidExprEvaluator
1969019690
// The argument is not evaluated!
1969119691
return true;
1969219692

19693+
case Builtin::BI__builtin_assume_dereferenceable: {
19694+
assert(E->getType()->isVoidType());
19695+
assert(E->getNumArgs() == 2);
19696+
19697+
APSInt ReqSizeVal;
19698+
if (!::EvaluateInteger(E->getArg(1), ReqSizeVal, Info))
19699+
return false;
19700+
LValue Pointer;
19701+
if (!EvaluatePointer(E->getArg(0), Pointer, Info))
19702+
return false;
19703+
if (Pointer.Designator.Invalid)
19704+
return false;
19705+
if (Pointer.isNullPointer())
19706+
return false;
19707+
19708+
uint64_t ReqSize = ReqSizeVal.getZExtValue();
19709+
if (ReqSize < 1)
19710+
return false;
19711+
CharUnits EndOffset;
19712+
if (!determineEndOffset(Info, E->getExprLoc(), 0, Pointer, EndOffset))
19713+
return false;
19714+
19715+
uint64_t TotalSize =
19716+
(EndOffset - Pointer.getLValueOffset()).getQuantity();
19717+
if (TotalSize < ReqSize) {
19718+
return false;
19719+
}
19720+
return true;
19721+
}
1969319722
case Builtin::BI__builtin_operator_delete:
1969419723
return HandleOperatorDeleteCall(Info, E);
1969519724

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 -triple x86_64-unknown-unknown %s
2+
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 -triple x86_64-unknown-unknown %s -fexperimental-new-constant-interpreter
3+
4+
constexpr int arr[10] = {};
5+
6+
constexpr bool test_constexpr_valid() {
7+
__builtin_assume_dereferenceable(arr, 40);
8+
return true;
9+
}
10+
static_assert(test_constexpr_valid(), "");
11+
12+
constexpr bool test_constexpr_partial() {
13+
__builtin_assume_dereferenceable(&arr[5], 20);
14+
return true;
15+
}
16+
static_assert(test_constexpr_partial(), "");
17+
18+
constexpr bool test_constexpr_nullptr() {
19+
__builtin_assume_dereferenceable(nullptr, 4);
20+
return true;
21+
}
22+
static_assert(test_constexpr_nullptr(), ""); // expected-error {{not an integral constant expression}}
23+
24+
constexpr bool test_constexpr_too_large() {
25+
__builtin_assume_dereferenceable(arr, 100);
26+
return true;
27+
}
28+
static_assert(test_constexpr_too_large(), ""); // expected-error {{not an integral constant expression}}
29+
30+
constexpr int single_var = 42;
31+
constexpr bool test_single_var() {
32+
__builtin_assume_dereferenceable(&single_var, 4);
33+
return true;
34+
}
35+
static_assert(test_single_var(), "");
36+
37+
constexpr bool test_exact_boundary() {
38+
__builtin_assume_dereferenceable(&arr[9], 4);
39+
return true;
40+
}
41+
static_assert(test_exact_boundary(), "");
42+
43+
constexpr bool test_one_over() {
44+
__builtin_assume_dereferenceable(&arr[9], 5);
45+
return true;
46+
}
47+
static_assert(test_one_over(), ""); // expected-error {{not an integral constant expression}}
48+
49+
constexpr bool test_zero_size() {
50+
__builtin_assume_dereferenceable(arr, 0);
51+
return true;
52+
}
53+
static_assert(test_zero_size(), ""); // expected-error {{not an integral constant expression}}
54+
55+
struct S {
56+
int x;
57+
int y;
58+
};
59+
constexpr S s = {1, 2};
60+
constexpr bool test_struct_member() {
61+
__builtin_assume_dereferenceable(&s.x, 4);
62+
return true;
63+
}
64+
static_assert(test_struct_member(), "");

0 commit comments

Comments
 (0)