ahatanak updated this revision to Diff 38967.
ahatanak added a comment.

Fixed test cases:

- Added a test case for the attribute being specified on a variable.
- Added a missing 's' to "virtual function" in the check strings.


http://reviews.llvm.org/D12922

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

Index: test/SemaCXX/attr-notail.cpp
===================================================================
--- /dev/null
+++ test/SemaCXX/attr-notail.cpp
@@ -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();
+};
Index: test/Sema/attr-notail.c
===================================================================
--- /dev/null
+++ test/Sema/attr-notail.c
@@ -0,0 +1,10 @@
+// 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}}
Index: test/CodeGenCXX/attr-notail.cpp
===================================================================
--- /dev/null
+++ test/CodeGenCXX/attr-notail.cpp
@@ -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*
Index: test/CodeGen/attr-no-tail.c
===================================================================
--- /dev/null
+++ test/CodeGen/attr-no-tail.c
@@ -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);
+}
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp
+++ lib/Sema/SemaDeclAttr.cpp
@@ -1701,6 +1701,15 @@
                                    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 @@
 
 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 @@
   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;
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -5370,6 +5370,16 @@
       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,
Index: lib/CodeGen/CGCall.cpp
===================================================================
--- lib/CodeGen/CGCall.cpp
+++ lib/CodeGen/CGCall.cpp
@@ -3493,6 +3493,10 @@
   // 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:
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -2402,6 +2402,8 @@
   "%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<
Index: include/clang/Basic/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -1620,3 +1620,52 @@
 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;
+  };
+
+  }];
+}
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -1029,6 +1029,12 @@
   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];
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to