diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -683,6 +683,13 @@ let Documentation = [XRayDocs]; } +def PatchableFunctionEntry : InheritableAttr { + let Spellings = [GCC<"patchable_function_entry">]; + let Subjects = SubjectList<[Function, ObjCMethod]>; + let Args = [UnsignedArgument<"Size">, DefaultIntArgument<"Start", 0>]; + let Documentation = [PatchableFunctionEntryDocs]; +} + def TLSModel : InheritableAttr { let Spellings = [GCC<"tls_model">]; let Subjects = SubjectList<[TLSVar], ErrorDiag>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -3988,6 +3988,18 @@ }]; } +def PatchableFunctionEntryDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +``__attribute__((patchable_function_entry(N,M)))`` is used to generate M NOPs +before the function entry and N-M NOPs after the function entry. This attributes +takes precedence over command line option ``-fpatchable-function-entry=N,M``. +``M`` defaults to 0 if omitted. + +Currently, only M=0 is supported. +}]; +} + def TransparentUnionDocs : Documentation { let Category = DocCatDecl; let Content = [{ diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -799,8 +799,8 @@ FD->getBody()->getStmtClass() == Stmt::CoroutineBodyStmtClass) SanOpts.Mask &= ~SanitizerKind::Null; - // Apply xray attributes to the function (as a string, for now) if (D) { + // Apply xray attributes to the function (as a string, for now) if (const auto *XRayAttr = D->getAttr()) { if (CGM.getCodeGenOpts().XRayInstrumentationBundle.has( XRayInstrKind::Function)) { @@ -819,6 +819,11 @@ "xray-instruction-threshold", llvm::itostr(CGM.getCodeGenOpts().XRayInstructionThreshold)); } + + if (const auto *Attr = D->getAttr()) + Fn->addFnAttr( + "patchable-function-entry", + (Twine(Attr->getSize()) + "," + Twine(Attr->getStart())).str()); } // Add no-jump-tables value. diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4917,6 +4917,34 @@ XRayLogArgsAttr(S.Context, AL, ArgCount.getSourceIndex())); } +static void handlePatchableFunctionEntryAttr(Sema &S, Decl *D, + const ParsedAttr &AL) { + const llvm::Triple &T = S.Context.getTargetInfo().getTriple(); + if (!T.isAArch64() && T.getArch() != llvm::Triple::x86 && + T.getArch() != llvm::Triple::x86_64) { + S.Diag(getAttrLoc(AL), diag::err_attribute_unsupported) << AL; + return; + } + + uint32_t Size = 0, Start = 0; + if (AL.getNumArgs()) { + if (!checkUInt32Argument(S, AL, AL.getArgAsExpr(0), Size, 0, true)) + return; + if (AL.getNumArgs() == 2) { + Expr *Arg = AL.getArgAsExpr(1); + if (!checkUInt32Argument(S, AL, Arg, Start, 1, true)) + return; + if (Start) { + S.Diag(getAttrLoc(AL), diag::err_attribute_argument_out_of_range) + << &AL << 0 << 0 << Arg->getBeginLoc(); + return; + } + } + } + D->addAttr(::new (S.Context) + PatchableFunctionEntryAttr(S.Context, AL, Size, Start)); +} + static bool ArmMveAliasValid(unsigned BuiltinID, StringRef AliasName) { if (AliasName.startswith("__arm_")) AliasName = AliasName.substr(6); @@ -7376,6 +7404,10 @@ handleXRayLogArgsAttr(S, D, AL); break; + case ParsedAttr::AT_PatchableFunctionEntry: + handlePatchableFunctionEntryAttr(S, D, AL); + break; + // Move semantics attribute. case ParsedAttr::AT_Reinitializes: handleSimpleAttribute(S, D, AL); diff --git a/clang/test/CodeGen/patchable-function-entry.c b/clang/test/CodeGen/patchable-function-entry.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/patchable-function-entry.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -triple aarch64 -emit-llvm %s -o - | FileCheck %s + +// CHECK: define void @f0() #0 +__attribute__((patchable_function_entry(0))) void f0() {} + +// CHECK: define void @f00() #0 +__attribute__((patchable_function_entry(0, 0))) void f00() {} + +// CHECK: define void @f2() #1 +__attribute__((patchable_function_entry(2))) void f2() {} + +// CHECK: define void @f20() #1 +__attribute__((patchable_function_entry(2, 0))) void f20() {} + +// CHECK: define void @f20decl() #1 +__attribute__((patchable_function_entry(2, 0))) void f20decl(); +__attribute__((noinline)) void f20decl() {} + +// CHECK: attributes #0 = { {{.*}} "patchable-function-entry"="0,0" +// CHECK: attributes #1 = { {{.*}} "patchable-function-entry"="2,0" diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -128,6 +128,7 @@ // CHECK-NEXT: Owner (SubjectMatchRule_record_not_is_union) // CHECK-NEXT: ParamTypestate (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: PassObjectSize (SubjectMatchRule_variable_is_parameter) +// CHECK-NEXT: PatchableFunctionEntry (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: Pointer (SubjectMatchRule_record_not_is_union) // CHECK-NEXT: ReleaseHandle (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: RenderScriptKernel (SubjectMatchRule_function) diff --git a/clang/test/Sema/patchable-function-entry-attr.c b/clang/test/Sema/patchable-function-entry-attr.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/patchable-function-entry-attr.c @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -triple aarch64 -fsyntax-only -verify %s + +// expected-error@+1 {{'patchable_function_entry' attribute takes at least 1 argument}} +__attribute__((patchable_function_entry)) void f(); + +// expected-error@+1 {{'patchable_function_entry' attribute takes no more than 2 arguments}} +__attribute__((patchable_function_entry(0, 0, 0))) void f(); + +// expected-error@+1 {{'patchable_function_entry' attribute requires a non-negative integral compile time constant expression}} +__attribute__((patchable_function_entry(-1))) void f(); + +int i; +// expected-error@+1 {{'patchable_function_entry' attribute requires parameter 0 to be an integer constant}} +__attribute__((patchable_function_entry(i))) void f(); + +// expected-error@+1 {{'patchable_function_entry' attribute requires integer constant between 0 and 0 inclusive}} +__attribute__((patchable_function_entry(1, 1))) void f(); diff --git a/clang/test/Sema/patchable-function-entry-attr.cpp b/clang/test/Sema/patchable-function-entry-attr.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Sema/patchable-function-entry-attr.cpp @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple aarch64 -fsyntax-only -verify=silence %s +// RUN: %clang_cc1 -triple i386 -fsyntax-only -verify=silence %s +// RUN: %clang_cc1 -triple x86_64 -fsyntax-only -verify=silence %s +// RUN: %clang_cc1 -triple ppc64le -fsyntax-only -verify %s + +// silence-no-diagnostics + +// expected-error@+1 {{'patchable_function_entry' attribute is not supported for this target}} +[[gnu::patchable_function_entry(0)]] void f();