Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -1020,6 +1020,12 @@ let Documentation = [Undocumented]; } +def NoTail : InheritableAttr { + let Spellings = [GNU<"notail">, CXX11<"clang", "notail">]; + let Subjects = SubjectList<[Function, ObjCMethod]>; + let Documentation = [NoTailDocs]; +} + 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 @@ -1612,3 +1612,10 @@ arguments, with arbitrary offsets. }]; } + +def NoTailDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +Tail call optimization is not performed on direct calls to a function marked ``notail``. + }]; +} Index: lib/CodeGen/CGCall.cpp =================================================================== --- lib/CodeGen/CGCall.cpp +++ lib/CodeGen/CGCall.cpp @@ -3492,6 +3492,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/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -1701,6 +1701,14 @@ Attr.getAttributeSpellingListIndex())); } +static void handleNoTailAttr(Sema &S, Decl *D, const AttributeList &Attr) { + if (checkAttrMutualExclusion(S, D, Attr)) + return; + + D->addAttr(::new (S.Context) NoTailAttr( + 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()) { @@ -3335,6 +3343,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())) @@ -4907,6 +4918,9 @@ case AttributeList::AT_ReturnsTwice: handleSimpleAttribute(S, D, Attr); break; + case AttributeList::AT_NoTail: + handleNoTailAttr(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__((notail)) { + return a + 1; +} + +int callee1(int) __attribute__((notail)); + +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,46 @@ +// RUN: %clang_cc1 -triple=x86_64-apple-darwin -std=c++11 %s -emit-llvm -o - | FileCheck %s + +class Base { +public: + [[clang::notail]] virtual int foo1() { + return 0; + } + virtual ~Base() {} +}; + +class Derived1 : public Base { +public: + int foo1() override { + return 1; + } +}; + +class Derived2 : public Derived1 { +public: + [[clang::notail]] int foo1() override { + return 2; + } +}; + +// CHECK-LABEL: define i32 @_Z4foo1iP4BaseP8Derived1 +// CHECK: [[BASEFUNC:%[a-z0-9]+]] = load i32 (%class.Base*)*, i32 (%class.Base*)** {{%[a-z0-9]+}} +// CHECK: {{%[a-z0-9]+}} = notail call i32 [[BASEFUNC]](%class.Base* {{%[a-z0-9]+}}) +// CHECK: [[DERIVEDFUNC1_1:%[a-z0-9]+]] = load i32 (%class.Derived1*)*, i32 (%class.Derived1*)** {{%[a-z0-9]+}} +// CHECK: {{%[a-z0-9]+}} = call i32 [[DERIVEDFUNC1_1]](%class.Derived1* {{%[a-z0-9]+}}) +// CHECK: [[DERIVEDFUNC1_2:%[a-z0-9]+]] = load i32 (%class.Derived1*)*, i32 (%class.Derived1*)** {{%[a-z0-9]+}} +// CHECK: {{%[a-z0-9]+}} = call i32 [[DERIVEDFUNC1_2]](%class.Derived1* {{%[a-z0-9]+}}) +// CHECK: [[DERIVEDFUNC2:%[a-z0-9]+]] = load i32 (%class.Derived2*)*, i32 (%class.Derived2*)** {{%[a-z0-9]+}} +// CHECK: {{%[a-z0-9]+}} = notail call i32 [[DERIVEDFUNC2]](%class.Derived2* {{%[a-z0-9]+}}) + +int foo1(int a, Base *b, Derived1 *d) { + if (a > 1) + return b->foo1(); // Tail-call is prevented. + if (a < 0) + return d->foo1(); // Tail-call is not prevented. + if (a == 0) { + Derived1 *i = new Derived2(); // Tail-call is not prevented. + return i->foo1(); + } + Derived2 *i = new Derived2(); // Tail-call is prevented. + return i->foo1(); +} Index: test/Sema/attr-notail.c =================================================================== --- /dev/null +++ test/Sema/attr-notail.c @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +int callee0() __attribute__((notail,always_inline)); // expected-error{{'notail' and 'always_inline' attributes are not compatible}} +int callee1() __attribute__((always_inline,notail)); // expected-error{{'always_inline' and 'notail' attributes are not compatible}} + +int foo(int a) { + return a ? callee0() : callee1(); +}