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

llvm-svn: 252986
This commit is contained in:
Akira Hatanaka 2015-11-13 00:42:21 +00:00
parent c9dd057e3c
commit 7828b1e604
8 changed files with 142 additions and 7 deletions

View File

@ -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]>;

View File

@ -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.
}
};
}];
}

View File

@ -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",

View File

@ -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;

View File

@ -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"{{.*}} }

View File

@ -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"{{.*}} }

View File

@ -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}}

View File

@ -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; }
};