Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -890,6 +890,13 @@ let Documentation = [Undocumented]; } +def DisableTailCalls : InheritableAttr { + let Spellings = [GNU<"disable_tail_calls">, + CXX11<"clang", "disable_tail_calls">]; + let Subjects = SubjectList<[Function, ObjCMethod]>; + let Documentation = [DisableTailCallsDocs]; +} + def NoAlias : InheritableAttr { let Spellings = [Declspec<"noalias">]; let Subjects = SubjectList<[Function]>; Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -1669,3 +1669,19 @@ }]; } + +def DisableTailCallsDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``disable_tail_calls`` attribute instructs the backend to not perform tail call optimization inside the marked function. + +For example: + +.. code-block:: c++ + + int foo(int a) __attribute__((disable_tail_calls)) { + return callee(a); // This call is not tail-call optimized. + } + + }]; +} Index: lib/CodeGen/CGCall.cpp =================================================================== --- lib/CodeGen/CGCall.cpp +++ lib/CodeGen/CGCall.cpp @@ -1481,8 +1481,12 @@ FuncAttrs.addAttribute("no-frame-pointer-elim-non-leaf"); } + bool DisableTailCalls = + (TargetDecl && TargetDecl->hasAttr()) || + CodeGenOpts.DisableTailCalls; FuncAttrs.addAttribute("disable-tail-calls", - llvm::toStringRef(CodeGenOpts.DisableTailCalls)); + llvm::toStringRef(DisableTailCalls)); + FuncAttrs.addAttribute("less-precise-fpmad", llvm::toStringRef(CodeGenOpts.LessPreciseFPMAD)); FuncAttrs.addAttribute("no-infs-fp-math", Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -1581,6 +1581,14 @@ Attr.getAttributeSpellingListIndex())); } +static void handleNakedAttr(Sema &S, Decl *D, const AttributeList &Attr) { + if (checkAttrMutualExclusion(S, D, Attr)) + return; + + D->addAttr(::new (S.Context) NakedAttr(Attr.getRange(), S.Context, + Attr.getAttributeSpellingListIndex())); +} + static void handleNoReturnAttr(Sema &S, Decl *D, const AttributeList &attr) { if (hasDeclarator(D)) return; @@ -1710,6 +1718,15 @@ Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); } +static void handleDisableTailCallsAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + if (checkAttrMutualExclusion(S, D, Attr)) + return; + + D->addAttr(::new (S.Context) DisableTailCallsAttr( + 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()) { @@ -4883,7 +4900,7 @@ handleHotAttr(S, D, Attr); break; case AttributeList::AT_Naked: - handleSimpleAttribute(S, D, Attr); + handleNakedAttr(S, D, Attr); break; case AttributeList::AT_NoReturn: handleNoReturnAttr(S, D, Attr); @@ -5006,6 +5023,9 @@ case AttributeList::AT_NotTailCalled: handleNotTailCalledAttr(S, D, Attr); break; + case AttributeList::AT_DisableTailCalls: + handleDisableTailCallsAttr(S, D, Attr); + break; case AttributeList::AT_Used: handleUsedAttr(S, D, Attr); break; Index: test/CodeGen/attr-disable-tail-calls.c =================================================================== --- test/CodeGen/attr-disable-tail-calls.c +++ test/CodeGen/attr-disable-tail-calls.c @@ -1,11 +1,19 @@ -// RUN: %clang_cc1 -triple x86_64-apple-macosx10.7.0 %s -emit-llvm -mdisable-tail-calls -o - | FileCheck %s -check-prefix=CHECK -check-prefix=DISABLE -// RUN: %clang_cc1 -triple x86_64-apple-macosx10.7.0 %s -emit-llvm -o - | FileCheck %s -check-prefix=CHECK -check-prefix=ENABLE +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.7.0 %s -emit-llvm -mdisable-tail-calls -o - | FileCheck %s -check-prefix=DISABLE +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.7.0 %s -emit-llvm -o - | FileCheck %s -check-prefix=ENABLE -// CHECK: define i32 @f1() [[ATTR:#[0-9]+]] { +// DISABLE: define i32 @f1() [[ATTRTRUE:#[0-9]+]] { +// DISABLE: define i32 @f2() [[ATTRTRUE]] { +// ENABLE: define i32 @f1() [[ATTRFALSE:#[0-9]+]] { +// ENABLE: define i32 @f2() [[ATTRTRUE:#[0-9]+]] { int f1() { return 0; } -// DISABLE: attributes [[ATTR]] = { {{.*}} "disable-tail-calls"="true" {{.*}} } -// ENABLE: attributes [[ATTR]] = { {{.*}} "disable-tail-calls"="false" {{.*}} } +int f2() __attribute__((disable_tail_calls)) { + return 0; +} + +// DISABLE: attributes [[ATTRTRUE]] = { {{.*}}"disable-tail-calls"="true"{{.*}} } +// ENABLE: attributes [[ATTRFALSE]] = { {{.*}}"disable-tail-calls"="false"{{.*}} } +// ENABLE: attributes [[ATTRTRUE]] = { {{.*}}"disable-tail-calls"="true"{{.*}} } Index: test/Sema/disable-tail-calls-attr.c =================================================================== --- /dev/null +++ test/Sema/disable-tail-calls-attr.c @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +void __attribute__((disable_tail_calls,naked)) foo1(int a) { // expected-error {{'disable_tail_calls' and 'naked' attributes are not compatible}} + __asm__(""); +} + +void __attribute__((naked,disable_tail_calls)) foo2(int a) { // expected-error {{'naked' and 'disable_tail_calls' attributes are not compatible}} + __asm__(""); +} + +int g0 __attribute__((disable_tail_calls)); // expected-warning {{'disable_tail_calls' attribute only applies to functions}} + +int foo3(int a) __attribute__((disable_tail_calls("abc"))); // expected-error {{'disable_tail_calls' attribute takes no arguments}}