mirror of https://github.com/microsoft/clang.git
Add support for function attribute 'not_tail_called'.
This attribute is used to prevent tail-call optimizations to the marked function. For example, in the following piece of code, foo1 will not be tail-call optimized: int __attribute__((not_tail_called)) foo1(int); int foo2(int a) { return foo1(a); // Tail-call optimization is not performed. } The attribute has effect only on statically bound calls. It has no effect on indirect calls. Also, virtual functions and objective-c methods cannot be marked as 'not_tail_called'. rdar://problem/22667622 Differential Revision: http://reviews.llvm.org/D12922 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@252369 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
67fafbb08d
commit
08bdfb571a
|
@ -1029,6 +1029,12 @@ def NoInstrumentFunction : InheritableAttr {
|
|||
let Documentation = [Undocumented];
|
||||
}
|
||||
|
||||
def NotTailCalled : InheritableAttr {
|
||||
let Spellings = [GNU<"not_tail_called">, CXX11<"clang", "not_tail_called">];
|
||||
let Subjects = SubjectList<[Function]>;
|
||||
let Documentation = [NotTailCalledDocs];
|
||||
}
|
||||
|
||||
def NoThrow : InheritableAttr {
|
||||
let Spellings = [GCC<"nothrow">, Declspec<"nothrow">];
|
||||
let Documentation = [Undocumented];
|
||||
|
|
|
@ -1620,3 +1620,52 @@ function are loads and stores from objects pointed to by its pointer-typed
|
|||
arguments, with arbitrary offsets.
|
||||
}];
|
||||
}
|
||||
|
||||
def NotTailCalledDocs : Documentation {
|
||||
let Category = DocCatFunction;
|
||||
let Content = [{
|
||||
The ``not_tail_called`` attribute prevents tail-call optimization on statically bound calls. It has no effect on indirect calls. Virtual functions, objective-c methods, and functions marked as ``always_inline`` cannot be marked as ``not_tail_called``.
|
||||
|
||||
For example, it prevents tail-call optimization in the following case:
|
||||
.. code-block: c
|
||||
int __attribute__((not_tail_called)) foo1(int);
|
||||
|
||||
int foo2(int a) {
|
||||
return foo1(a); // No tail-call optimization on direct calls.
|
||||
}
|
||||
|
||||
However, it doesn't prevent tail-call optimization in this case:
|
||||
.. code-block: c
|
||||
int __attribute__((not_tail_called)) foo1(int);
|
||||
|
||||
int foo2(int a) {
|
||||
int (*fn)(int) = &foo1;
|
||||
|
||||
// not_tail_called has no effect on an indirect call even if the call can be
|
||||
// resolved at compile time.
|
||||
return (*fn)(a);
|
||||
}
|
||||
|
||||
Marking virtual functions as ``not_tail_called`` is an error:
|
||||
.. code-block: c++
|
||||
class Base {
|
||||
public:
|
||||
// not_tail_called on a virtual function is an error.
|
||||
[[clang::not_tail_called]] virtual int foo1();
|
||||
|
||||
virtual int foo2();
|
||||
|
||||
// Non-virtual functions can be marked ``not_tail_called``.
|
||||
[[clang::not_tail_called]] int foo3();
|
||||
};
|
||||
|
||||
class Derived1 : public Base {
|
||||
public:
|
||||
int foo1() override;
|
||||
|
||||
// not_tail_called on a virtual function is an error.
|
||||
[[clang::not_tail_called]] int foo2() override;
|
||||
};
|
||||
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -2409,6 +2409,8 @@ def warn_attribute_not_on_decl : Warning<
|
|||
"%0 attribute ignored when parsing type">, InGroup<IgnoredAttributes>;
|
||||
def err_base_specifier_attribute : Error<
|
||||
"%0 attribute cannot be applied to a base specifier">;
|
||||
def err_invalid_attribute_on_virtual_function : Error<
|
||||
"%0 attribute cannot be applied to virtual functions">;
|
||||
|
||||
// Availability attribute
|
||||
def warn_availability_unknown_platform : Warning<
|
||||
|
|
|
@ -3493,6 +3493,10 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
|
|||
// lexical order, so deactivate it and run it manually here.
|
||||
CallArgs.freeArgumentMemory(*this);
|
||||
|
||||
if (llvm::CallInst *Call = dyn_cast<llvm::CallInst>(CI))
|
||||
if (TargetDecl && TargetDecl->hasAttr<NotTailCalledAttr>())
|
||||
Call->setTailCallKind(llvm::CallInst::TCK_NoTail);
|
||||
|
||||
RValue Ret = [&] {
|
||||
switch (RetAI.getKind()) {
|
||||
case ABIArgInfo::InAlloca:
|
||||
|
|
|
@ -5373,6 +5373,16 @@ static void checkAttributesAfterMerging(Sema &S, NamedDecl &ND) {
|
|||
ND.setInvalidDecl();
|
||||
}
|
||||
}
|
||||
|
||||
// Virtual functions cannot be marked as 'notail'.
|
||||
if (auto *Attr = ND.getAttr<NotTailCalledAttr>())
|
||||
if (auto *MD = dyn_cast<CXXMethodDecl>(&ND))
|
||||
if (MD->isVirtual()) {
|
||||
S.Diag(ND.getLocation(),
|
||||
diag::err_invalid_attribute_on_virtual_function)
|
||||
<< Attr;
|
||||
ND.dropAttr<NotTailCalledAttr>();
|
||||
}
|
||||
}
|
||||
|
||||
static void checkDLLAttributeRedeclaration(Sema &S, NamedDecl *OldDecl,
|
||||
|
|
|
@ -1701,6 +1701,15 @@ static void handleDependencyAttr(Sema &S, Scope *Scope, Decl *D,
|
|||
Attr.getAttributeSpellingListIndex()));
|
||||
}
|
||||
|
||||
static void handleNotTailCalledAttr(Sema &S, Decl *D,
|
||||
const AttributeList &Attr) {
|
||||
if (checkAttrMutualExclusion<AlwaysInlineAttr>(S, D, Attr))
|
||||
return;
|
||||
|
||||
D->addAttr(::new (S.Context) NotTailCalledAttr(
|
||||
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()) {
|
||||
|
@ -3419,6 +3428,9 @@ OptimizeNoneAttr *Sema::mergeOptimizeNoneAttr(Decl *D, SourceRange Range,
|
|||
|
||||
static void handleAlwaysInlineAttr(Sema &S, Decl *D,
|
||||
const AttributeList &Attr) {
|
||||
if (checkAttrMutualExclusion<NotTailCalledAttr>(S, D, Attr))
|
||||
return;
|
||||
|
||||
if (AlwaysInlineAttr *Inline = S.mergeAlwaysInlineAttr(
|
||||
D, Attr.getRange(), Attr.getName(),
|
||||
Attr.getAttributeSpellingListIndex()))
|
||||
|
@ -4991,6 +5003,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
|
|||
case AttributeList::AT_ReturnsTwice:
|
||||
handleSimpleAttribute<ReturnsTwiceAttr>(S, D, Attr);
|
||||
break;
|
||||
case AttributeList::AT_NotTailCalled:
|
||||
handleNotTailCalledAttr(S, D, Attr);
|
||||
break;
|
||||
case AttributeList::AT_Used:
|
||||
handleUsedAttr(S, D, Attr);
|
||||
break;
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.7.0 %s -emit-llvm -o - | FileCheck %s
|
||||
|
||||
// CHECK: %{{[a-z0-9]+}} = notail call i32 @callee0(i32 %
|
||||
// CHECK: %{{[a-z0-9]+}} = notail call i32 @callee1(i32 %
|
||||
|
||||
// Check that indirect calls do not have the notail marker.
|
||||
// CHECK: store i32 (i32)* @callee1, i32 (i32)** [[ALLOCA1:%[A-Za-z0-9]+]], align 8
|
||||
// CHECK: [[INDIRFUNC:%[0-9]+]] = load i32 (i32)*, i32 (i32)** [[ALLOCA1]], align 8
|
||||
// CHECK: %{{[a-z0-9]+}} = call i32 [[INDIRFUNC]](i32 %6)
|
||||
|
||||
// CHECK: %{{[a-z0-9]+}} = call i32 @callee2(i32 %
|
||||
|
||||
int callee0(int a) __attribute__((not_tail_called)) {
|
||||
return a + 1;
|
||||
}
|
||||
|
||||
int callee1(int) __attribute__((not_tail_called));
|
||||
|
||||
int callee2(int);
|
||||
|
||||
typedef int (*FuncTy)(int);
|
||||
|
||||
int foo0(int a) {
|
||||
if (a > 1)
|
||||
return callee0(a);
|
||||
if (a == 1)
|
||||
return callee1(a);
|
||||
if (a < 0) {
|
||||
FuncTy F = callee1;
|
||||
return (*F)(a);
|
||||
}
|
||||
return callee2(a);
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// RUN: %clang_cc1 -triple=x86_64-apple-darwin -std=c++11 -emit-llvm -o - %s | FileCheck %s
|
||||
|
||||
class Class1 {
|
||||
public:
|
||||
[[clang::not_tail_called]] int m1();
|
||||
int m2();
|
||||
};
|
||||
|
||||
int foo1(int a, Class1 *c1) {
|
||||
if (a)
|
||||
return c1->m1();
|
||||
return c1->m2();
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define i32 @_Z4foo1iP6Class1(
|
||||
// CHECK: %{{[a-z0-9]+}} = notail call i32 @_ZN6Class12m1Ev(%class.Class1*
|
||||
// CHECK: %{{[a-z0-9]+}} = call i32 @_ZN6Class12m2Ev(%class.Class1*
|
|
@ -0,0 +1,12 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify %s
|
||||
|
||||
int callee0() __attribute__((not_tail_called,always_inline)); // expected-error{{'not_tail_called' and 'always_inline' attributes are not compatible}}
|
||||
int callee1() __attribute__((always_inline,not_tail_called)); // expected-error{{'always_inline' and 'not_tail_called' attributes are not compatible}}
|
||||
|
||||
int foo(int a) {
|
||||
return a ? callee0() : callee1();
|
||||
}
|
||||
|
||||
int g0 __attribute__((not_tail_called)); // expected-warning {{'not_tail_called' attribute only applies to functions}}
|
||||
|
||||
int foo2(int a) __attribute__((not_tail_called("abc"))); // expected-error {{'not_tail_called' attribute takes no arguments}}
|
|
@ -0,0 +1,16 @@
|
|||
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
|
||||
|
||||
class Base {
|
||||
public:
|
||||
[[clang::not_tail_called]] virtual int foo1(); // expected-error {{'not_tail_called' attribute cannot be applied to virtual functions}}
|
||||
virtual int foo2();
|
||||
[[clang::not_tail_called]] int foo3();
|
||||
virtual ~Base() {}
|
||||
};
|
||||
|
||||
class Derived1 : public Base {
|
||||
public:
|
||||
int foo1() override;
|
||||
[[clang::not_tail_called]] int foo2() override; // expected-error {{'not_tail_called' attribute cannot be applied to virtual functions}}
|
||||
[[clang::not_tail_called]] int foo4();
|
||||
};
|
Loading…
Reference in New Issue