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]; 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/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2402,6 +2402,8 @@ "%0 attribute ignored when parsing type">, InGroup; 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: 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(CI)) + if (TargetDecl && TargetDecl->hasAttr()) + Call->setTailCallKind(llvm::CallInst::TCK_NoTail); + RValue Ret = [&] { switch (RetAI.getKind()) { case ABIArgInfo::InAlloca: 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()) + if (auto *MD = dyn_cast(&ND)) + if (MD->isVirtual()) { + S.Diag(ND.getLocation(), + diag::err_invalid_attribute_on_virtual_function) + << Attr; + ND.dropAttr(); + } } static void checkDLLAttributeRedeclaration(Sema &S, NamedDecl *OldDecl, 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(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(D)) { if (VD->hasLocalStorage()) { @@ -3419,6 +3428,9 @@ static void handleAlwaysInlineAttr(Sema &S, Decl *D, const AttributeList &Attr) { + if (checkAttrMutualExclusion(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(S, D, Attr); break; + case AttributeList::AT_NotTailCalled: + handleNotTailCalledAttr(S, D, Attr); + break; case AttributeList::AT_Used: handleUsedAttr(S, D, Attr); break; 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: 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/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/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(); +};