https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/169815
>From fe84b8ea3d85d7ea56e0e23d5d32a14309525535 Mon Sep 17 00:00:00 2001 From: Nikolas Klauser <[email protected]> Date: Wed, 9 Jul 2025 15:08:44 +0200 Subject: [PATCH] [clang] Add [[clang::returns]] --- clang/include/clang/Basic/Attr.td | 7 +++ clang/include/clang/Basic/AttrDocs.td | 16 ++++++ .../clang/Basic/DiagnosticSemaKinds.td | 5 ++ clang/lib/CodeGen/CGCall.cpp | 9 ++++ clang/lib/Sema/SemaDeclAttr.cpp | 51 +++++++++++++++++++ .../test/CodeGenCXX/attr-returns-argument.cpp | 41 +++++++++++++++ ...a-attribute-supported-attributes-list.test | 1 + clang/test/SemaCXX/attr-returns-argument.cpp | 18 +++++++ 8 files changed, 148 insertions(+) create mode 100644 clang/test/CodeGenCXX/attr-returns-argument.cpp create mode 100644 clang/test/SemaCXX/attr-returns-argument.cpp diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 8e5f7ef0bb82d..c90f29fb2011c 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2682,6 +2682,13 @@ def NoEscape : Attr { let Documentation = [NoEscapeDocs]; } +def ReturnsArgument : InheritableAttr { + let Spellings = [Clang<"returns_argument">]; + let Subjects = SubjectList<[Function]>; + let Args = [ParamIdxArgument<"ReturnedVal">]; + let Documentation = [ReturnsArgumentDocs]; +} + def MaybeUndef : InheritableAttr { let Spellings = [Clang<"maybe_undef">]; let Subjects = SubjectList<[ParmVar]>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index c1b1510f363d4..3e9853567599b 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -287,6 +287,22 @@ applies to copies of the block. For example: }]; } +def ReturnsArgumentDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``returns_argument`` attribute can be placed on a function to indicate when +an argument is returned identically. This allows the compiler to use the +returned value instead of the argument, potentially decreasing register +pressure. This attribute can currently only be applied to arguments that are +pointers or references to the same type as the returned pointer or reference. + +Sample usage: +.. code-block:: c + + [[clang::returns_argument(1)]] void* memcpy(void*, const void*, size_t); + }]; +} + def MaybeUndefDocs : Documentation { let Category = DocCatVariable; let Content = [{ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 4a145fd71eedd..d203380acccbd 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3439,6 +3439,9 @@ def err_attribute_only_once_per_parameter : Error< "%0 attribute can only be applied once per parameter">; def err_mismatched_uuid : Error<"uuid does not match previous declaration">; def note_previous_uuid : Note<"previous uuid specified here">; +def err_return_type_mismatch : Error< + "returned parameter has to reference the same type as the return type">; +def note_return_type : Note<"return type is %0">; def warn_attribute_pointers_only : Warning< "%0 attribute only applies to%select{| constant}1 pointer arguments">, InGroup<IgnoredAttributes>; @@ -3473,6 +3476,8 @@ def warn_attribute_return_pointers_refs_only : Warning< def warn_attribute_pointer_or_reference_only : Warning< "%0 attribute only applies to a pointer or reference (%1 is invalid)">, InGroup<IgnoredAttributes>; +def err_attribute_pointer_or_reference_only : Error< + warn_attribute_pointer_or_reference_only.Summary>; def err_attribute_no_member_pointers : Error< "%0 attribute cannot be used with pointers to members">; def err_attribute_invalid_implicit_this_argument : Error< diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index efacb3cc04c01..02471fbf718cb 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -3009,6 +3009,15 @@ void CodeGenModule::ConstructAttributeList(StringRef Name, getLLVMContext(), llvm::AttributeSet::get(getLLVMContext(), Attrs)); } } + + if (TargetDecl) { + if (const auto *Returns = TargetDecl->getAttr<ReturnsArgumentAttr>()) { + llvm::AttributeSet &Attrs = + ArgAttrs[Returns->getReturnedVal().getLLVMIndex()]; + Attrs = Attrs.addAttribute(getLLVMContext(), llvm::Attribute::Returned); + } + } + assert(ArgNo == FI.arg_size()); ArgNo = 0; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index e3af5023c74d0..d264c9481cc38 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -1375,6 +1375,54 @@ static void handleNoEscapeAttr(Sema &S, Decl *D, const ParsedAttr &AL) { D->addAttr(::new (S.Context) NoEscapeAttr(S.Context, AL)); } +static void handleReturnsArgumentAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + auto *FD = cast<FunctionDecl>(D); + + ParamIdx Index; + if (!S.checkFunctionOrMethodParameterIndex( + D, AL, 1, AL.getArgAsExpr(0), Index, /*CanIndexImplicitThis=*/true)) + return; + + QualType ArgType; + + if (const auto *MD = dyn_cast<CXXMethodDecl>(FD); + MD && MD->isInstance() && Index.getLLVMIndex() == 0) { + ArgType = MD->getThisType(); + } else { + ArgType = MD->getParamDecl(Index.getASTIndex())->getType(); + } + + if (!ArgType->isPointerOrReferenceType()) { + S.Diag(D->getBeginLoc(), diag::err_attribute_pointer_or_reference_only) + << AL << ArgType; + return; + } + + ArgType = ArgType->isPointerType() ? ArgType->getPointeeType() + : ArgType.getNonReferenceType(); + + QualType ReturnType = FD->getReturnType(); + + if (!ReturnType->isPointerOrReferenceType()) { + S.Diag(D->getBeginLoc(), diag::err_return_type_mismatch); + S.Diag(FD->getReturnTypeSourceRange().getBegin(), diag::note_return_type) + << FD->getReturnType(); + return; + } + + ReturnType = ReturnType->isPointerType() ? ReturnType->getPointeeType() + : ReturnType.getNonReferenceType(); + + if (!S.Context.hasSameType(ReturnType, ArgType)) { + S.Diag(D->getBeginLoc(), diag::err_return_type_mismatch); + S.Diag(FD->getReturnTypeSourceRange().getBegin(), diag::note_return_type) + << FD->getReturnType(); + return; + } + + D->addAttr(ReturnsArgumentAttr::Create(S.Context, Index, AL)); +} + static void handleAssumeAlignedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { Expr *E = AL.getArgAsExpr(0), *OE = AL.getNumArgs() > 1 ? AL.getArgAsExpr(1) : nullptr; @@ -7361,6 +7409,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_NoEscape: handleNoEscapeAttr(S, D, AL); break; + case ParsedAttr::AT_ReturnsArgument: + handleReturnsArgumentAttr(S, D, AL); + break; case ParsedAttr::AT_MaybeUndef: handleSimpleAttribute<MaybeUndefAttr>(S, D, AL); break; diff --git a/clang/test/CodeGenCXX/attr-returns-argument.cpp b/clang/test/CodeGenCXX/attr-returns-argument.cpp new file mode 100644 index 0000000000000..7d1f61c24f0d1 --- /dev/null +++ b/clang/test/CodeGenCXX/attr-returns-argument.cpp @@ -0,0 +1,41 @@ +// RUN: %clang_cc1 -triple=x86_64-apple-darwin -std=c++11 -emit-llvm -o - %s | FileCheck %s + +[[clang::returns_argument(1)]] int* test3(int* i) { + // CHECK: @_Z5test3Pi(ptr noundef returned + return i; +} + +[[clang::returns_argument(1)]] int& test4(int& i) { + // CHECK: @_Z5test4Ri(ptr noundef nonnull returned + return i; +} + +[[clang::returns_argument(1)]] int* test5(int* const i) { + // CHECK: _Z5test5Pi(ptr noundef returned + return i; +} + +[[clang::returns_argument(2)]] int* test6(int*, int* i) { + // CHECK: @_Z5test6PiS_(ptr noundef %{{.*}}, ptr noundef returned + return i; +} + +[[clang::returns_argument(1)]] int& test7(int* i) { + //CHECK: @_Z5test7Pi(ptr noundef returned + return *i; +} + +struct S { + [[clang::returns_argument(1)]] S& func1(); + [[clang::returns_argument(1)]] static S& func4(S*); +}; + +S& S::func1() { + // CHECK: @_ZN1S5func1Ev(ptr noundef nonnull returned + return *this; +} + +S& S::func4(S* i) { + // CHECK: @_ZN1S5func4EPS_(ptr noundef returned + return *i; +} diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index 747eb17446c87..59b2501f103a7 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -185,6 +185,7 @@ // CHECK-NEXT: ReqdWorkGroupSize (SubjectMatchRule_function) // CHECK-NEXT: Restrict (SubjectMatchRule_function) // CHECK-NEXT: ReturnTypestate (SubjectMatchRule_function, SubjectMatchRule_variable_is_parameter) +// CHECK-NEXT: ReturnsArgument (SubjectMatchRule_function) // CHECK-NEXT: ReturnsNonNull (SubjectMatchRule_objc_method, SubjectMatchRule_function) // CHECK-NEXT: ReturnsTwice (SubjectMatchRule_function) // CHECK-NEXT: SYCLExternal (SubjectMatchRule_function) diff --git a/clang/test/SemaCXX/attr-returns-argument.cpp b/clang/test/SemaCXX/attr-returns-argument.cpp new file mode 100644 index 0000000000000..ed0960bddf9e7 --- /dev/null +++ b/clang/test/SemaCXX/attr-returns-argument.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +[[clang::returns_argument(1)]] int test1(int); // expected-error {{'clang::returns_argument' attribute only applies to a pointer or reference ('int' is invalid)}} +[[clang::returns_argument(1)]] int* test2(long*); // expected-error {{returned parameter has to reference the same type as the return type}} \ + expected-note {{return type is 'int *'}} +[[clang::returns_argument(1)]] int* test3(int*); +[[clang::returns_argument(1)]] int& test4(int&); +[[clang::returns_argument(1)]] int* test5(int* const); +[[clang::returns_argument(1)]] int& test6(int*); +[[clang::returns_argument(2)]] int& test7(int*); // expected-error {{'clang::returns_argument' attribute parameter 1 is out of bounds}} + +struct S { + [[clang::returns_argument(1)]] S& func1(); + [[clang::returns_argument(2)]] S& func2(); // expected-error {{'clang::returns_argument' attribute parameter 1 is out of bounds}} + [[clang::returns_argument(1)]] int* func3(int*); // expected-error {{returned parameter has to reference the same type as the return type}} \ + expected-note {{return type is 'int *'}} + [[clang::returns_argument(1)]] static S& func4(S*); +}; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
