mirror of https://github.com/microsoft/clang.git
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
This commit is contained in:
parent
e4e016d0a8
commit
361b11deda
|
@ -890,6 +890,13 @@ def ReturnsTwice : InheritableAttr {
|
||||||
let Documentation = [Undocumented];
|
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 {
|
def NoAlias : InheritableAttr {
|
||||||
let Spellings = [Declspec<"noalias">];
|
let Spellings = [Declspec<"noalias">];
|
||||||
let Subjects = SubjectList<[Function]>;
|
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.
|
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");
|
FuncAttrs.addAttribute("no-frame-pointer-elim-non-leaf");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DisableTailCalls =
|
||||||
|
CodeGenOpts.DisableTailCalls ||
|
||||||
|
(TargetDecl && TargetDecl->hasAttr<DisableTailCallsAttr>());
|
||||||
FuncAttrs.addAttribute("disable-tail-calls",
|
FuncAttrs.addAttribute("disable-tail-calls",
|
||||||
llvm::toStringRef(CodeGenOpts.DisableTailCalls));
|
llvm::toStringRef(DisableTailCalls));
|
||||||
|
|
||||||
FuncAttrs.addAttribute("less-precise-fpmad",
|
FuncAttrs.addAttribute("less-precise-fpmad",
|
||||||
llvm::toStringRef(CodeGenOpts.LessPreciseFPMAD));
|
llvm::toStringRef(CodeGenOpts.LessPreciseFPMAD));
|
||||||
FuncAttrs.addAttribute("no-infs-fp-math",
|
FuncAttrs.addAttribute("no-infs-fp-math",
|
||||||
|
|
|
@ -1583,6 +1583,15 @@ static void handleCommonAttr(Sema &S, Decl *D, const AttributeList &Attr) {
|
||||||
D->addAttr(CA);
|
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) {
|
static void handleNoReturnAttr(Sema &S, Decl *D, const AttributeList &attr) {
|
||||||
if (hasDeclarator(D)) return;
|
if (hasDeclarator(D)) return;
|
||||||
|
|
||||||
|
@ -1713,6 +1722,16 @@ static void handleNotTailCalledAttr(Sema &S, Decl *D,
|
||||||
Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex()));
|
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) {
|
static void handleUsedAttr(Sema &S, Decl *D, const AttributeList &Attr) {
|
||||||
if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
|
if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
|
||||||
if (VD->hasLocalStorage()) {
|
if (VD->hasLocalStorage()) {
|
||||||
|
@ -4933,7 +4952,7 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
|
||||||
handleHotAttr(S, D, Attr);
|
handleHotAttr(S, D, Attr);
|
||||||
break;
|
break;
|
||||||
case AttributeList::AT_Naked:
|
case AttributeList::AT_Naked:
|
||||||
handleSimpleAttribute<NakedAttr>(S, D, Attr);
|
handleNakedAttr(S, D, Attr);
|
||||||
break;
|
break;
|
||||||
case AttributeList::AT_NoReturn:
|
case AttributeList::AT_NoReturn:
|
||||||
handleNoReturnAttr(S, D, Attr);
|
handleNoReturnAttr(S, D, Attr);
|
||||||
|
@ -5056,6 +5075,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
|
||||||
case AttributeList::AT_NotTailCalled:
|
case AttributeList::AT_NotTailCalled:
|
||||||
handleNotTailCalledAttr(S, D, Attr);
|
handleNotTailCalledAttr(S, D, Attr);
|
||||||
break;
|
break;
|
||||||
|
case AttributeList::AT_DisableTailCalls:
|
||||||
|
handleDisableTailCallsAttr(S, D, Attr);
|
||||||
|
break;
|
||||||
case AttributeList::AT_Used:
|
case AttributeList::AT_Used:
|
||||||
handleUsedAttr(S, D, Attr);
|
handleUsedAttr(S, D, Attr);
|
||||||
break;
|
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 -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=CHECK -check-prefix=ENABLE
|
// 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() {
|
int f1() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// DISABLE: attributes [[ATTR]] = { {{.*}} "disable-tail-calls"="true" {{.*}} }
|
int f2() __attribute__((disable_tail_calls)) {
|
||||||
// ENABLE: attributes [[ATTR]] = { {{.*}} "disable-tail-calls"="false" {{.*}} }
|
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