llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: NagaChaitanya Vellanki (chaitanyav) <details> <summary>Changes</summary> 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 --- Full diff: https://github.com/llvm/llvm-project/pull/169869.diff 4 Files Affected: - (modified) clang/include/clang/Basic/Builtins.td (+1-1) - (modified) clang/lib/AST/ByteCode/InterpBuiltin.cpp (+34) - (modified) clang/lib/AST/ExprConstant.cpp (+29) - (added) clang/test/SemaCXX/builtin-assume-dereferenceable-constexpr.cpp (+64) ``````````diff 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(), ""); `````````` </details> https://github.com/llvm/llvm-project/pull/169869 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
