r252369 - Add support for function attribute 'not_tail_called'.

Akira Hatanaka via cfe-commits cfe-commits at lists.llvm.org
Fri Nov 6 15:56:16 PST 2015


Author: ahatanak
Date: Fri Nov  6 17:56:15 2015
New Revision: 252369

URL: http://llvm.org/viewvc/llvm-project?rev=252369&view=rev
Log:
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

Added:
    cfe/trunk/test/CodeGen/attr-no-tail.c
    cfe/trunk/test/CodeGenCXX/attr-notail.cpp
    cfe/trunk/test/Sema/attr-notail.c
    cfe/trunk/test/SemaCXX/attr-notail.cpp
Modified:
    cfe/trunk/include/clang/Basic/Attr.td
    cfe/trunk/include/clang/Basic/AttrDocs.td
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/CodeGen/CGCall.cpp
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaDeclAttr.cpp

Modified: cfe/trunk/include/clang/Basic/Attr.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=252369&r1=252368&r2=252369&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Attr.td (original)
+++ cfe/trunk/include/clang/Basic/Attr.td Fri Nov  6 17:56:15 2015
@@ -1029,6 +1029,12 @@ def NoInstrumentFunction : InheritableAt
   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];

Modified: cfe/trunk/include/clang/Basic/AttrDocs.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/AttrDocs.td?rev=252369&r1=252368&r2=252369&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/AttrDocs.td (original)
+++ cfe/trunk/include/clang/Basic/AttrDocs.td Fri Nov  6 17:56:15 2015
@@ -1620,3 +1620,52 @@ function are loads and stores from objec
 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;
+  };
+
+  }];
+}

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=252369&r1=252368&r2=252369&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Nov  6 17:56:15 2015
@@ -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<

Modified: cfe/trunk/lib/CodeGen/CGCall.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCall.cpp?rev=252369&r1=252368&r2=252369&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGCall.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGCall.cpp Fri Nov  6 17:56:15 2015
@@ -3493,6 +3493,10 @@ RValue CodeGenFunction::EmitCall(const C
   // 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:

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=252369&r1=252368&r2=252369&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Fri Nov  6 17:56:15 2015
@@ -5373,6 +5373,16 @@ static void checkAttributesAfterMerging(
       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,

Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=252369&r1=252368&r2=252369&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Fri Nov  6 17:56:15 2015
@@ -1701,6 +1701,15 @@ static void handleDependencyAttr(Sema &S
                                    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::mergeOptimizeNon
 
 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
   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;

Added: cfe/trunk/test/CodeGen/attr-no-tail.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/attr-no-tail.c?rev=252369&view=auto
==============================================================================
--- cfe/trunk/test/CodeGen/attr-no-tail.c (added)
+++ cfe/trunk/test/CodeGen/attr-no-tail.c Fri Nov  6 17:56:15 2015
@@ -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);
+}

Added: cfe/trunk/test/CodeGenCXX/attr-notail.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/attr-notail.cpp?rev=252369&view=auto
==============================================================================
--- cfe/trunk/test/CodeGenCXX/attr-notail.cpp (added)
+++ cfe/trunk/test/CodeGenCXX/attr-notail.cpp Fri Nov  6 17:56:15 2015
@@ -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*

Added: cfe/trunk/test/Sema/attr-notail.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/attr-notail.c?rev=252369&view=auto
==============================================================================
--- cfe/trunk/test/Sema/attr-notail.c (added)
+++ cfe/trunk/test/Sema/attr-notail.c Fri Nov  6 17:56:15 2015
@@ -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}}

Added: cfe/trunk/test/SemaCXX/attr-notail.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/attr-notail.cpp?rev=252369&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/attr-notail.cpp (added)
+++ cfe/trunk/test/SemaCXX/attr-notail.cpp Fri Nov  6 17:56:15 2015
@@ -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();
+};




More information about the cfe-commits mailing list