forked from OSchip/llvm-project
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:
parent
c9dd057e3c
commit
7828b1e604
|
@ -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]>;
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
};
|
||||
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"{{.*}} }
|
||||
|
|
|
@ -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"{{.*}} }
|
|
@ -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}}
|
|
@ -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; }
|
||||
};
|
Loading…
Reference in New Issue