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 @@ -770,6 +770,13 @@ let SimpleHandler = 1; } +def Trampoline : InheritableAttr { + let Spellings = [GNU<"trampoline">]; + let Subjects = SubjectList<[Function, ObjCMethod]>; + let Args = [StringArgument<"Name">]; + let Documentation = [TrampolineDocs]; +} + def XRayInstrument : InheritableAttr { let Spellings = [Clang<"xray_always_instrument">, Clang<"xray_never_instrument">]; 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 @@ -6900,6 +6900,31 @@ }]; } +def TrampolineDocs : Documentation { + let Category = DocCatFunction; + let Heading = "trampoline"; + let Content = [{ +The ``trampoline`` attribute indicates that the current function is a trampoline. +The argument is the mangled name of the symbol that debuggers should jump to instead +when entering the trampoline function. + +For example: + +.. code-block:: c + + int bar(void) { + return 42; + } + + __attribute__((trampoline("bar"))) + int foo(void) { + return bar(); + } + +Indicates to debuggers that stepping into ``foo`` should jump directly to ``bar`` instead. + }]; +} + def ReadOnlyPlacementDocs : Documentation { let Category = DocCatType; let Content = [{This attribute is attached to a structure, class or union declaration. @@ -6947,3 +6972,5 @@ its underlying representation to be a WebAssembly ``funcref``. }]; } + + diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3668,6 +3668,8 @@ MinSizeAttr *mergeMinSizeAttr(Decl *D, const AttributeCommonInfo &CI); SwiftNameAttr *mergeSwiftNameAttr(Decl *D, const SwiftNameAttr &SNA, StringRef Name); + TrampolineAttr *mergeTrampolineAttr(Decl *D, const TrampolineAttr &TA, + StringRef Name); OptimizeNoneAttr *mergeOptimizeNoneAttr(Decl *D, const AttributeCommonInfo &CI); InternalLinkageAttr *mergeInternalLinkageAttr(Decl *D, const ParsedAttr &AL); diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -68,6 +68,14 @@ return D->hasAttr() ? D->getMaxAlignment() : 0; } +static StringRef getTargetFuncName(const Decl *D) { + if (!D) + return {}; + if (auto *Trampoline = D->getAttr()) + return Trampoline->getName(); + return {}; +} + CGDebugInfo::CGDebugInfo(CodeGenModule &CGM) : CGM(CGM), DebugKind(CGM.getCodeGenOpts().getDebugInfo()), DebugTypeExtRefs(CGM.getCodeGenOpts().DebugTypeExtRefs), @@ -1918,10 +1926,11 @@ completeUnusedClass(*CD->getParent()); llvm::DINodeArray TParamsArray = CollectFunctionTemplateParams(Method, Unit); + StringRef TargetFuncName = getTargetFuncName(Method); llvm::DISubprogram *SP = DBuilder.createMethod( RecordTy, MethodName, MethodLinkageName, MethodDefUnit, MethodLine, MethodTy, VIndex, ThisAdjustment, ContainingType, Flags, SPFlags, - TParamsArray.get()); + TParamsArray.get(), nullptr, TargetFuncName); SPCache[Method->getCanonicalDecl()].reset(SP); @@ -3849,10 +3858,12 @@ if (Stub) { Flags |= getCallSiteRelatedAttrs(); SPFlags |= llvm::DISubprogram::SPFlagDefinition; + StringRef TargetFuncName = getTargetFuncName(FD); return DBuilder.createFunction( DContext, Name, LinkageName, Unit, Line, getOrCreateFunctionType(GD.getDecl(), FnType, Unit), 0, Flags, SPFlags, - TParamsArray.get(), getFunctionDeclaration(FD)); + TParamsArray.get(), getFunctionDeclaration(FD), nullptr, nullptr, + TargetFuncName); } llvm::DISubprogram *SP = DBuilder.createTempFunctionFwdDecl( @@ -3998,9 +4009,11 @@ if (It == TypeCache.end()) return nullptr; auto *InterfaceType = cast(It->second); + StringRef TargetFuncName = getTargetFuncName(D); llvm::DISubprogram *FD = DBuilder.createFunction( InterfaceType, getObjCMethodName(OMD), StringRef(), - InterfaceType->getFile(), LineNo, FnType, LineNo, Flags, SPFlags); + InterfaceType->getFile(), LineNo, FnType, LineNo, Flags, SPFlags, nullptr, + nullptr, nullptr, nullptr, TargetFuncName); DBuilder.finalizeSubprogram(FD); ObjCMethodCache[ID].push_back({FD, OMD->isDirectMethod()}); return FD; @@ -4182,6 +4195,8 @@ Annotations = CollectBTFDeclTagAnnotations(D); } + StringRef TargetFuncName = getTargetFuncName(D); + // FIXME: The function declaration we're constructing here is mostly reusing // declarations from CXXMethodDecl and not constructing new ones for arbitrary // FunctionDecls. When/if we fix this we can have FDContext be TheCU/null for @@ -4190,7 +4205,7 @@ llvm::DISubprogram *SP = DBuilder.createFunction( FDContext, Name, LinkageName, Unit, LineNo, DIFnType, ScopeLine, FlagsForDef, SPFlagsForDef, TParamsArray.get(), Decl, nullptr, - Annotations); + Annotations, TargetFuncName); Fn->setSubprogram(SP); // We might get here with a VarDecl in the case we're generating // code for the initialization of globals. Do not record these decls @@ -4251,9 +4266,11 @@ llvm::DINodeArray Annotations = CollectBTFDeclTagAnnotations(D); llvm::DISubroutineType *STy = getOrCreateFunctionType(D, FnType, Unit); - llvm::DISubprogram *SP = DBuilder.createFunction( - FDContext, Name, LinkageName, Unit, LineNo, STy, ScopeLine, Flags, - SPFlags, TParamsArray.get(), nullptr, nullptr, Annotations); + StringRef TargetFuncName = getTargetFuncName(D); + llvm::DISubprogram *SP = + DBuilder.createFunction(FDContext, Name, LinkageName, Unit, LineNo, STy, + ScopeLine, Flags, SPFlags, TParamsArray.get(), + nullptr, nullptr, Annotations, TargetFuncName); // Preserve btf_decl_tag attributes for parameters of extern functions // for BPF target. The parameters created in this loop are attached as diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2927,6 +2927,8 @@ NewAttr = S.mergeMinSizeAttr(D, *MA); else if (const auto *SNA = dyn_cast(Attr)) NewAttr = S.mergeSwiftNameAttr(D, *SNA, SNA->getName()); + else if (const auto *TA = dyn_cast(Attr)) + NewAttr = S.mergeTrampolineAttr(D, *TA, TA->getName()); else if (const auto *OA = dyn_cast(Attr)) NewAttr = S.mergeOptimizeNoneAttr(D, *OA); else if (const auto *InternalLinkageA = dyn_cast(Attr)) 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 @@ -4879,6 +4879,20 @@ return ::new (Context) SwiftNameAttr(Context, SNA, Name); } +TrampolineAttr *Sema::mergeTrampolineAttr(Decl *D, const TrampolineAttr &TA, + StringRef Name) { + if (const auto *PrevSNA = D->getAttr()) { + if (PrevSNA->getName() != Name && !PrevSNA->isImplicit()) { + Diag(PrevSNA->getLocation(), diag::err_attributes_are_not_compatible) + << PrevSNA << &TA; + Diag(TA.getLoc(), diag::note_conflicting_attribute); + } + + D->dropAttr(); + } + return ::new (Context) TrampolineAttr(Context, TA, Name); +} + OptimizeNoneAttr *Sema::mergeOptimizeNoneAttr(Decl *D, const AttributeCommonInfo &CI) { if (AlwaysInlineAttr *Inline = D->getAttr()) { @@ -6758,6 +6772,15 @@ D->addAttr(::new (S.Context) SwiftNameAttr(S.Context, AL, Name)); } +static void handleTrampoline(Sema &S, Decl *D, const ParsedAttr &AL) { + StringRef Name; + SourceLocation Loc; + if (!S.checkStringLiteralArgumentAttr(AL, 0, Name, &Loc)) + return; + + D->addAttr(::new (S.Context) TrampolineAttr(S.Context, AL, Name)); +} + static void handleSwiftAsyncName(Sema &S, Decl *D, const ParsedAttr &AL) { StringRef Name; SourceLocation Loc; @@ -9294,6 +9317,10 @@ handleSwiftAsyncError(S, D, AL); break; + case ParsedAttr::AT_Trampoline: + handleTrampoline(S, D, AL); + break; + // XRay attributes. case ParsedAttr::AT_XRayLogArgs: handleXRayLogArgsAttr(S, D, AL); diff --git a/clang/test/CodeGen/attr-trampoline-method.cpp b/clang/test/CodeGen/attr-trampoline-method.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/attr-trampoline-method.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -debug-info-kind=limited -emit-llvm -o - %s | FileCheck %s + +void bar(void) {} + +struct A { +__attribute__((trampoline("bar"))) +void foo(void) { + bar(); +} +}; + +int main() { + A().foo(); +} + +// CHECK: DISubprogram(name: "foo"{{.*}} targetFuncName: "bar" diff --git a/clang/test/CodeGen/attr-trampoline.c b/clang/test/CodeGen/attr-trampoline.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/attr-trampoline.c @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -debug-info-kind=limited -emit-llvm -o - %s | FileCheck %s + +void bar(void) {} + +__attribute__((trampoline("bar"))) +void foo(void) { + bar(); +} + +// CHECK: DISubprogram(name: "foo"{{.*}} targetFuncName: "bar" 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 @@ -185,6 +185,7 @@ // CHECK-NEXT: TargetClones (SubjectMatchRule_function) // CHECK-NEXT: TargetVersion (SubjectMatchRule_function) // CHECK-NEXT: TestTypestate (SubjectMatchRule_function_is_member) +// CHECK-NEXT: Trampoline (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: TrivialABI (SubjectMatchRule_record) // CHECK-NEXT: Uninitialized (SubjectMatchRule_variable_is_local) // CHECK-NEXT: UnsafeBufferUsage (SubjectMatchRule_function) diff --git a/llvm/include/llvm/IR/DIBuilder.h b/llvm/include/llvm/IR/DIBuilder.h --- a/llvm/include/llvm/IR/DIBuilder.h +++ b/llvm/include/llvm/IR/DIBuilder.h @@ -790,15 +790,16 @@ /// \param SPFlags Additional flags specific to subprograms. /// \param TParams Function template parameters. /// \param ThrownTypes Exception types this function may throw. - DISubprogram * - createMethod(DIScope *Scope, StringRef Name, StringRef LinkageName, - DIFile *File, unsigned LineNo, DISubroutineType *Ty, - unsigned VTableIndex = 0, int ThisAdjustment = 0, - DIType *VTableHolder = nullptr, - DINode::DIFlags Flags = DINode::FlagZero, - DISubprogram::DISPFlags SPFlags = DISubprogram::SPFlagZero, - DITemplateParameterArray TParams = nullptr, - DITypeArray ThrownTypes = nullptr); + /// \param TargetFuncName The name of the target function if this is + /// a trampoline. + DISubprogram *createMethod( + DIScope *Scope, StringRef Name, StringRef LinkageName, DIFile *File, + unsigned LineNo, DISubroutineType *Ty, unsigned VTableIndex = 0, + int ThisAdjustment = 0, DIType *VTableHolder = nullptr, + DINode::DIFlags Flags = DINode::FlagZero, + DISubprogram::DISPFlags SPFlags = DISubprogram::SPFlagZero, + DITemplateParameterArray TParams = nullptr, + DITypeArray ThrownTypes = nullptr, StringRef TargetFuncName = ""); /// Create common block entry for a Fortran common block. /// \param Scope Scope of this common block. diff --git a/llvm/lib/CodeGen/MachineOutliner.cpp b/llvm/lib/CodeGen/MachineOutliner.cpp --- a/llvm/lib/CodeGen/MachineOutliner.cpp +++ b/llvm/lib/CodeGen/MachineOutliner.cpp @@ -799,7 +799,8 @@ 0, /* Line 0 is reserved for compiler-generated code. */ DINode::DIFlags::FlagArtificial /* Compiler-generated code. */, /* Outlined code is optimized code by definition. */ - DISubprogram::SPFlagDefinition | DISubprogram::SPFlagOptimized); + DISubprogram::SPFlagDefinition | DISubprogram::SPFlagOptimized, nullptr, + nullptr, nullptr, nullptr, SP->getTargetFuncName()); // Don't add any new variables to the subprogram. DB.finalizeSubprogram(OutlinedSP); diff --git a/llvm/lib/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp --- a/llvm/lib/IR/DIBuilder.cpp +++ b/llvm/lib/IR/DIBuilder.cpp @@ -881,7 +881,7 @@ unsigned LineNo, DISubroutineType *Ty, unsigned VIndex, int ThisAdjustment, DIType *VTableHolder, DINode::DIFlags Flags, DISubprogram::DISPFlags SPFlags, DITemplateParameterArray TParams, - DITypeArray ThrownTypes) { + DITypeArray ThrownTypes, StringRef TargetFuncName) { assert(getNonCompileUnitScope(Context) && "Methods should have both a Context and a context that isn't " "the compile unit."); @@ -891,7 +891,7 @@ /*IsDistinct=*/IsDefinition, VMContext, cast(Context), Name, LinkageName, F, LineNo, Ty, LineNo, VTableHolder, VIndex, ThisAdjustment, Flags, SPFlags, IsDefinition ? CUNode : nullptr, TParams, nullptr, - nullptr, ThrownTypes); + nullptr, ThrownTypes, nullptr, TargetFuncName); if (IsDefinition) AllSubprograms.push_back(SP);