forked from OSchip/llvm-project
[Clang][Attributes] Allow not_tail_called attribute to be applied to virtual function.
It would be beneficial to allow not_tail_called attribute to be applied to virtual functions. I don't see any drawback of allowing this. Differential Revision: https://reviews.llvm.org/D96832
This commit is contained in:
parent
1959ead525
commit
4500f0a732
|
@ -4057,9 +4057,8 @@ 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``.
|
||||
bound calls. 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:
|
||||
|
||||
|
@ -4085,28 +4084,25 @@ However, it doesn't prevent tail-call optimization in this case:
|
|||
return (*fn)(a);
|
||||
}
|
||||
|
||||
Marking virtual functions as ``not_tail_called`` is an error:
|
||||
Generally, marking an overriding virtual function as ``not_tail_called`` is
|
||||
not useful, because this attribute is a property of the static type. Calls
|
||||
made through a pointer or reference to the base class type will respect
|
||||
the ``not_tail_called`` attribute of the base class's member function,
|
||||
regardless of the runtime destination of the call:
|
||||
|
||||
.. 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;
|
||||
struct Foo { virtual void f(); };
|
||||
struct Bar : Foo {
|
||||
[[clang::not_tail_called]] void f() override;
|
||||
};
|
||||
void callera(Bar& bar) {
|
||||
Foo& foo = bar;
|
||||
// not_tail_called has no effect on here, even though the
|
||||
// underlying method is f from Bar.
|
||||
foo.f();
|
||||
bar.f(); // No tail-call optimization on here.
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
|
@ -6432,16 +6432,6 @@ static void checkAttributesAfterMerging(Sema &S, NamedDecl &ND) {
|
|||
}
|
||||
}
|
||||
|
||||
// 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>();
|
||||
}
|
||||
|
||||
// Check the attributes on the function type, if any.
|
||||
if (const auto *FD = dyn_cast<FunctionDecl>(&ND)) {
|
||||
// Don't declare this variable in the second operand of the for-statement;
|
||||
|
|
|
@ -4,14 +4,28 @@ class Class1 {
|
|||
public:
|
||||
[[clang::not_tail_called]] int m1();
|
||||
int m2();
|
||||
[[clang::not_tail_called]] virtual int m3();
|
||||
virtual int m4();
|
||||
};
|
||||
|
||||
int foo1(int a, Class1 *c1) {
|
||||
class Class2: public Class1 {
|
||||
public:
|
||||
[[clang::not_tail_called]] int m4() override;
|
||||
};
|
||||
|
||||
int foo1(int a, Class1 *c1, Class2 &c2) {
|
||||
if (a)
|
||||
return c1->m1();
|
||||
c1->m3();
|
||||
Class1 &c = c2;
|
||||
c.m4();
|
||||
c2.m4();
|
||||
return c1->m2();
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define{{.*}} i32 @_Z4foo1iP6Class1(
|
||||
// CHECK-LABEL: define{{.*}} i32 @_Z4foo1iP6Class1R6Class2(
|
||||
// CHECK: %{{[a-z0-9]+}} = notail call i32 @_ZN6Class12m1Ev(%class.Class1*
|
||||
// CHECK: %{{[a-z0-9]+}} = notail call i32 %{{[0-9]+}}(%class.Class1*
|
||||
// CHECK-NOT: %{{[a-z0-9]+}} = notail call i32 %{{[0-9]+}}(%class.Class1*
|
||||
// CHECK: %{{[a-z0-9]+}} = notail call i32 %{{[0-9]+}}(%class.Class2*
|
||||
// CHECK: %{{[a-z0-9]+}} = call i32 @_ZN6Class12m2Ev(%class.Class1*
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
|
||||
// expected-no-diagnostics
|
||||
|
||||
class Base {
|
||||
public:
|
||||
[[clang::not_tail_called]] virtual int foo1(); // expected-error {{'not_tail_called' attribute cannot be applied to virtual functions}}
|
||||
[[clang::not_tail_called]] virtual int foo1();
|
||||
virtual int foo2();
|
||||
[[clang::not_tail_called]] int foo3();
|
||||
virtual ~Base() {}
|
||||
|
@ -11,6 +12,6 @@ public:
|
|||
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 foo2() override;
|
||||
[[clang::not_tail_called]] int foo4();
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue