From 361b11dedac9bb7a4860c7895c2b74f052e0470c Mon Sep 17 00:00:00 2001 From: Akira Hatanaka <ahatanaka@apple.com> Date: Fri, 13 Nov 2015 00:42:21 +0000 Subject: [PATCH] Add support for function attribute 'disable_tail_calls'. The ``disable_tail_calls`` attribute instructs the backend to not perform tail call optimization inside the marked function. For example, int callee(int); int foo(int a) __attribute__((disable_tail_calls)) { return callee(a); // This call is not tail-call optimized. } Note that this attribute is different from 'not_tail_called', which prevents tail-call optimization to the marked function. rdar://problem/8973573 Differential Revision: http://reviews.llvm.org/D12547 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@252986 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/Attr.td | 7 ++++ include/clang/Basic/AttrDocs.td | 38 +++++++++++++++++++++ lib/CodeGen/CGCall.cpp | 6 +++- lib/Sema/SemaDeclAttr.cpp | 24 ++++++++++++- test/CodeGen/attr-disable-tail-calls.c | 18 +++++++--- test/CodeGenCXX/attr-disable-tail-calls.cpp | 35 +++++++++++++++++++ test/Sema/attr-disable-tail-calls.c | 13 +++++++ test/SemaCXX/attr-disable-tail-calls.cpp | 8 +++++ 8 files changed, 142 insertions(+), 7 deletions(-) create mode 100644 test/CodeGenCXX/attr-disable-tail-calls.cpp create mode 100644 test/Sema/attr-disable-tail-calls.c create mode 100644 test/SemaCXX/attr-disable-tail-calls.cpp diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index a32a8788e69..e020227612b 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -890,6 +890,13 @@ def ReturnsTwice : InheritableAttr { let Documentation = [Undocumented]; } +def DisableTailCalls : InheritableAttr { + let Spellings = [GNU<"disable_tail_calls">, + CXX11<"clang", "disable_tail_calls">]; + let Subjects = SubjectList<[Function, ObjCMethod]>; + let Documentation = [DisableTailCallsDocs]; +} + def NoAlias : InheritableAttr { let Spellings = [Declspec<"noalias">]; let Subjects = SubjectList<[Function]>; diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index b28ef6df0ed..9f933ba5249 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -1684,3 +1684,41 @@ this attribute affects all methods and static data members of that class. This can be used to contain the ABI of a C++ library by excluding unwanted class methods from the export tables. }]; } + +def DisableTailCallsDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``disable_tail_calls`` attribute instructs the backend to not perform tail call optimization inside the marked function. + +For example: + + .. code-block:: c + + int callee(int); + + int foo(int a) __attribute__((disable_tail_calls)) { + return callee(a); // This call is not tail-call optimized. + } + +Marking virtual functions as ``disable_tail_calls`` is legal. + + .. code-block: c++ + + int callee(int); + + class Base { + public: + [[clang::disable_tail_calls]] virtual int foo1() { + return callee(); // This call is not tail-call optimized. + } + }; + + class Derived1 : public Base { + public: + int foo1() override { + return callee(); // This call is tail-call optimized. + } + }; + + }]; +} diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index 430184d4398..5dea876451f 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -1481,8 +1481,12 @@ void CodeGenModule::ConstructAttributeList(const CGFunctionInfo &FI, FuncAttrs.addAttribute("no-frame-pointer-elim-non-leaf"); } + bool DisableTailCalls = + CodeGenOpts.DisableTailCalls || + (TargetDecl && TargetDecl->hasAttr<DisableTailCallsAttr>()); FuncAttrs.addAttribute("disable-tail-calls", - llvm::toStringRef(CodeGenOpts.DisableTailCalls)); + llvm::toStringRef(DisableTailCalls)); + FuncAttrs.addAttribute("less-precise-fpmad", llvm::toStringRef(CodeGenOpts.LessPreciseFPMAD)); FuncAttrs.addAttribute("no-infs-fp-math", diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index a9473d2e119..abdab9a0692 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -1583,6 +1583,15 @@ static void handleCommonAttr(Sema &S, Decl *D, const AttributeList &Attr) { D->addAttr(CA); } +static void handleNakedAttr(Sema &S, Decl *D, const AttributeList &Attr) { + if (checkAttrMutualExclusion<DisableTailCallsAttr>(S, D, Attr.getRange(), + Attr.getName())) + return; + + D->addAttr(::new (S.Context) NakedAttr(Attr.getRange(), S.Context, + Attr.getAttributeSpellingListIndex())); +} + static void handleNoReturnAttr(Sema &S, Decl *D, const AttributeList &attr) { if (hasDeclarator(D)) return; @@ -1713,6 +1722,16 @@ static void handleNotTailCalledAttr(Sema &S, Decl *D, Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); } +static void handleDisableTailCallsAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + if (checkAttrMutualExclusion<NakedAttr>(S, D, Attr.getRange(), + Attr.getName())) + return; + + D->addAttr(::new (S.Context) DisableTailCallsAttr( + Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); +} + static void handleUsedAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (const VarDecl *VD = dyn_cast<VarDecl>(D)) { if (VD->hasLocalStorage()) { @@ -4933,7 +4952,7 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, handleHotAttr(S, D, Attr); break; case AttributeList::AT_Naked: - handleSimpleAttribute<NakedAttr>(S, D, Attr); + handleNakedAttr(S, D, Attr); break; case AttributeList::AT_NoReturn: handleNoReturnAttr(S, D, Attr); @@ -5056,6 +5075,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_NotTailCalled: handleNotTailCalledAttr(S, D, Attr); break; + case AttributeList::AT_DisableTailCalls: + handleDisableTailCallsAttr(S, D, Attr); + break; case AttributeList::AT_Used: handleUsedAttr(S, D, Attr); break; diff --git a/test/CodeGen/attr-disable-tail-calls.c b/test/CodeGen/attr-disable-tail-calls.c index 81413492ff3..d47b14fe8ba 100644 --- a/test/CodeGen/attr-disable-tail-calls.c +++ b/test/CodeGen/attr-disable-tail-calls.c @@ -1,11 +1,19 @@ -// RUN: %clang_cc1 -triple x86_64-apple-macosx10.7.0 %s -emit-llvm -mdisable-tail-calls -o - | FileCheck %s -check-prefix=CHECK -check-prefix=DISABLE -// RUN: %clang_cc1 -triple x86_64-apple-macosx10.7.0 %s -emit-llvm -o - | FileCheck %s -check-prefix=CHECK -check-prefix=ENABLE +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.7.0 %s -emit-llvm -mdisable-tail-calls -o - | FileCheck %s -check-prefix=DISABLE +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.7.0 %s -emit-llvm -o - | FileCheck %s -check-prefix=ENABLE -// CHECK: define i32 @f1() [[ATTR:#[0-9]+]] { +// DISABLE: define i32 @f1() [[ATTRTRUE:#[0-9]+]] { +// DISABLE: define i32 @f2() [[ATTRTRUE]] { +// ENABLE: define i32 @f1() [[ATTRFALSE:#[0-9]+]] { +// ENABLE: define i32 @f2() [[ATTRTRUE:#[0-9]+]] { int f1() { return 0; } -// DISABLE: attributes [[ATTR]] = { {{.*}} "disable-tail-calls"="true" {{.*}} } -// ENABLE: attributes [[ATTR]] = { {{.*}} "disable-tail-calls"="false" {{.*}} } +int f2() __attribute__((disable_tail_calls)) { + return 0; +} + +// DISABLE: attributes [[ATTRTRUE]] = { {{.*}}"disable-tail-calls"="true"{{.*}} } +// ENABLE: attributes [[ATTRFALSE]] = { {{.*}}"disable-tail-calls"="false"{{.*}} } +// ENABLE: attributes [[ATTRTRUE]] = { {{.*}}"disable-tail-calls"="true"{{.*}} } diff --git a/test/CodeGenCXX/attr-disable-tail-calls.cpp b/test/CodeGenCXX/attr-disable-tail-calls.cpp new file mode 100644 index 00000000000..b9a3c2712f0 --- /dev/null +++ b/test/CodeGenCXX/attr-disable-tail-calls.cpp @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 -triple=x86_64-apple-darwin -std=c++11 -emit-llvm -o - %s | FileCheck %s + +class B { +public: + [[clang::disable_tail_calls]] virtual int m1() { return 1; } + virtual int m2() { return 2; } + int m3() { return 3; } + [[clang::disable_tail_calls]] int m4(); +}; + +class D : public B { +public: + int m1() override { return 11; } + [[clang::disable_tail_calls]] int m2() override { return 22; } +}; + +int foo1() { + B *b = new B; + D *d = new D; + int t = 0; + t += b->m1() + b->m2() + b->m3() + b->m4(); + t += d->m1() + d->m2(); + return t; +} + +// CHECK: define linkonce_odr i32 @_ZN1B2m3Ev(%class.B* %this) [[ATTRFALSE:#[0-9]+]] +// CHECK: declare i32 @_ZN1B2m4Ev(%class.B*) [[ATTRTRUE0:#[0-9]+]] +// CHECK: define linkonce_odr i32 @_ZN1B2m1Ev(%class.B* %this) unnamed_addr [[ATTRTRUE1:#[0-9]+]] +// CHECK: define linkonce_odr i32 @_ZN1B2m2Ev(%class.B* %this) unnamed_addr [[ATTRFALSE:#[0-9]+]] +// CHECK: define linkonce_odr i32 @_ZN1D2m1Ev(%class.D* %this) unnamed_addr [[ATTRFALSE:#[0-9]+]] +// CHECK: define linkonce_odr i32 @_ZN1D2m2Ev(%class.D* %this) unnamed_addr [[ATTRTRUE1:#[0-9]+]] + +// CHECK: attributes [[ATTRFALSE]] = { {{.*}}"disable-tail-calls"="false"{{.*}} } +// CHECK: attributes [[ATTRTRUE0]] = { {{.*}}"disable-tail-calls"="true"{{.*}} } +// CHECK: attributes [[ATTRTRUE1]] = { {{.*}}"disable-tail-calls"="true"{{.*}} } diff --git a/test/Sema/attr-disable-tail-calls.c b/test/Sema/attr-disable-tail-calls.c new file mode 100644 index 00000000000..4574d5e0b66 --- /dev/null +++ b/test/Sema/attr-disable-tail-calls.c @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +void __attribute__((disable_tail_calls,naked)) foo1(int a) { // expected-error {{'disable_tail_calls' and 'naked' attributes are not compatible}} expected-note {{conflicting attribute is here}} + __asm__(""); +} + +void __attribute__((naked,disable_tail_calls)) foo2(int a) { // expected-error {{'naked' and 'disable_tail_calls' attributes are not compatible}} expected-note {{conflicting attribute is here}} + __asm__(""); +} + +int g0 __attribute__((disable_tail_calls)); // expected-warning {{'disable_tail_calls' attribute only applies to functions and methods}} + +int foo3(int a) __attribute__((disable_tail_calls("abc"))); // expected-error {{'disable_tail_calls' attribute takes no arguments}} diff --git a/test/SemaCXX/attr-disable-tail-calls.cpp b/test/SemaCXX/attr-disable-tail-calls.cpp new file mode 100644 index 00000000000..d442aa6d440 --- /dev/null +++ b/test/SemaCXX/attr-disable-tail-calls.cpp @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s +// expected-no-diagnostics + +class B { +public: + [[clang::disable_tail_calls]] virtual int foo1() { return 1; } + [[clang::disable_tail_calls]] int foo2() { return 2; } +}; -- GitLab