Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -3802,6 +3802,10 @@ bool BlockMissingReturnType : 1; bool IsConversionFromLambda : 1; + /// A bit that indicates this block is passed directly to a function as a + /// non-escaping parameter. + bool DoesNotEscape : 1; + /// A new[]'d array of pointers to ParmVarDecls for the formal /// parameters of this function. This is null if a prototype or if there are /// no formals. @@ -3821,7 +3825,7 @@ BlockDecl(DeclContext *DC, SourceLocation CaretLoc) : Decl(Block, DC, CaretLoc), DeclContext(Block), IsVariadic(false), CapturesCXXThis(false), BlockMissingReturnType(true), - IsConversionFromLambda(false) {} + IsConversionFromLambda(false), DoesNotEscape(false) {} public: static BlockDecl *Create(ASTContext &C, DeclContext *DC, SourceLocation L); @@ -3893,6 +3897,9 @@ bool isConversionFromLambda() const { return IsConversionFromLambda; } void setIsConversionFromLambda(bool val) { IsConversionFromLambda = val; } + bool doesNotEscape() const { return DoesNotEscape; } + void setDoesNotEscape() { DoesNotEscape = true; } + bool capturesVariable(const VarDecl *var) const; void setCaptures(ASTContext &Context, ArrayRef Captures, Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -1428,6 +1428,8 @@ def fno_openmp_cuda_mode : Flag<["-"], "fno-openmp-cuda-mode">, Group, Flags<[CC1Option, NoArgumentUnused]>; def fno_optimize_sibling_calls : Flag<["-"], "fno-optimize-sibling-calls">, Group; def foptimize_sibling_calls : Flag<["-"], "foptimize-sibling-calls">, Group; +def fno_escaping_block_tail_calls : Flag<["-"], "fno-escaping-block-tail-calls">, Group, Flags<[CC1Option]>; +def fescaping_block_tail_calls : Flag<["-"], "fescaping-block-tail-calls">, Group; def force__cpusubtype__ALL : Flag<["-"], "force_cpusubtype_ALL">; def force__flat__namespace : Flag<["-"], "force_flat_namespace">; def force__load : Separate<["-"], "force_load">; Index: include/clang/Frontend/CodeGenOptions.def =================================================================== --- include/clang/Frontend/CodeGenOptions.def +++ include/clang/Frontend/CodeGenOptions.def @@ -62,6 +62,8 @@ ///< pass manager. CODEGENOPT(DisableRedZone , 1, 0) ///< Set when -mno-red-zone is enabled. CODEGENOPT(DisableTailCalls , 1, 0) ///< Do not emit tail calls. +CODEGENOPT(NoEscapingBlockTailCalls, 1, 0) ///< Do not emit tail calls from + ///< escaping blocks. CODEGENOPT(EmitDeclMetadata , 1, 0) ///< Emit special metadata indicating what ///< Decl* various IR entities came from. ///< Only useful when running CodeGen as a Index: lib/CodeGen/CGCall.cpp =================================================================== --- lib/CodeGen/CGCall.cpp +++ lib/CodeGen/CGCall.cpp @@ -1887,10 +1887,21 @@ } if (!AttrOnCallSite) { - bool DisableTailCalls = - CodeGenOpts.DisableTailCalls || - (TargetDecl && (TargetDecl->hasAttr() || - TargetDecl->hasAttr())); + bool DisableTailCalls = false; + + if (CodeGenOpts.DisableTailCalls) + DisableTailCalls = true; + else if (TargetDecl) { + if (TargetDecl->hasAttr() || + TargetDecl->hasAttr()) + DisableTailCalls = true; + else if (CodeGenOpts.NoEscapingBlockTailCalls) { + if (const auto *BD = dyn_cast(TargetDecl)) + if (!BD->doesNotEscape()) + DisableTailCalls = true; + } + } + FuncAttrs.addAttribute("disable-tail-calls", llvm::toStringRef(DisableTailCalls)); GetCPUAndFeaturesAttributes(TargetDecl, FuncAttrs); Index: lib/Driver/ToolChains/Clang.cpp =================================================================== --- lib/Driver/ToolChains/Clang.cpp +++ lib/Driver/ToolChains/Clang.cpp @@ -3454,6 +3454,9 @@ if (!Args.hasFlag(options::OPT_foptimize_sibling_calls, options::OPT_fno_optimize_sibling_calls)) CmdArgs.push_back("-mdisable-tail-calls"); + if (Args.hasFlag(options::OPT_fno_escaping_block_tail_calls, + options::OPT_fescaping_block_tail_calls)) + CmdArgs.push_back("-fno-escaping-block-tail-calls"); Args.AddLastArg(CmdArgs, options::OPT_ffine_grained_bitfield_accesses, options::OPT_fno_fine_grained_bitfield_accesses); Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -640,6 +640,8 @@ Opts.DisableFree = Args.hasArg(OPT_disable_free); Opts.DiscardValueNames = Args.hasArg(OPT_discard_value_names); Opts.DisableTailCalls = Args.hasArg(OPT_mdisable_tail_calls); + Opts.NoEscapingBlockTailCalls = + Args.hasArg(OPT_fno_escaping_block_tail_calls); Opts.FloatABI = Args.getLastArgValue(OPT_mfloat_abi); Opts.LessPreciseFPMAD = Args.hasArg(OPT_cl_mad_enable) || Args.hasArg(OPT_cl_unsafe_math_optimizations) || Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -4841,6 +4841,10 @@ (!Param || !Param->hasAttr())) CFAudited = true; + if (Param && Param->hasAttr()) + if (auto *BE = dyn_cast(Arg->IgnoreParenNoopCasts(Context))) + BE->getBlockDecl()->setDoesNotEscape(); + InitializedEntity Entity = Param ? InitializedEntity::InitializeParameter(Context, Param, ProtoArgType) Index: lib/Sema/SemaExprObjC.cpp =================================================================== --- lib/Sema/SemaExprObjC.cpp +++ lib/Sema/SemaExprObjC.cpp @@ -1613,6 +1613,11 @@ ParmVarDecl *param = Method->parameters()[i]; assert(argExpr && "CheckMessageArgumentTypes(): missing expression"); + if (param->hasAttr()) + if (auto *BE = dyn_cast( + argExpr->IgnoreParenNoopCasts(Context))) + BE->getBlockDecl()->setDoesNotEscape(); + // Strip the unbridged-cast placeholder expression off unless it's // a consumed argument. if (argExpr->hasPlaceholderType(BuiltinType::ARCUnbridgedCast) && Index: test/CodeGenObjC/disable-tail-call-escaping-block.m =================================================================== --- /dev/null +++ test/CodeGenObjC/disable-tail-call-escaping-block.m @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin -fblocks -fno-escaping-block-tail-calls -emit-llvm -o - %s | FileCheck %s + +// CHECK-LABEL: define void @test( +// CHECK: store i8* bitcast (void (i8*)* @[[TEST_BLOCK_INVOKE0:.*]] to i8*) +// CHECK: store i8* bitcast (void (i8*)* @[[TEST_BLOCK_INVOKE1:.*]] to i8*) +// CHECK: store i8* bitcast (void (i8*)* @[[TEST_BLOCK_INVOKE2:.*]] to i8*) +// CHECK: store i8* bitcast (void (i8*)* @[[TEST_BLOCK_INVOKE3:.*]] to i8*) +// CHECK: store i8* bitcast (void (i8*)* @[[TEST_BLOCK_INVOKE4:.*]] to i8*) +// CHECK: store i8* bitcast (void (i8*)* @[[TEST_BLOCK_INVOKE5:.*]] to i8*) + +// CHECK: define internal void @[[TEST_BLOCK_INVOKE0]]({{.*}}) #[[DISABLEATTR:.*]] { +// CHECK: define internal void @[[TEST_BLOCK_INVOKE1]]({{.*}}) #[[ENABLEATTR:.*]] { +// CHECK: define internal void @[[TEST_BLOCK_INVOKE2]]({{.*}}) #[[DISABLEATTR]] { +// CHECK: define internal void @[[TEST_BLOCK_INVOKE3]]({{.*}}) #[[DISABLEATTR]] { +// CHECK: define internal void @[[TEST_BLOCK_INVOKE4]]({{.*}}) #[[ENABLEATTR]] { +// CHECK: define internal void @[[TEST_BLOCK_INVOKE5]]({{.*}}) #[[DISABLEATTR]] { + +// CHECK: attributes #[[ENABLEATTR]] = {{{.*}}"disable-tail-calls"="false"{{.*}}} +// CHECK: attributes #[[DISABLEATTR]] = {{{.*}}"disable-tail-calls"="true"{{.*}}} + +typedef void (^BlockTy)(void); + +void callee0(__attribute__((noescape)) BlockTy); +void callee1(BlockTy); + +__attribute__((objc_root_class)) +@interface C0 +-(void)m0:(__attribute__((noescape)) BlockTy)p; +-(void)m1:(BlockTy)p; +@end + +@implementation C0 +-(void)m0:(__attribute__((noescape)) BlockTy)p {} +-(void)m1:(BlockTy)p {} +@end + +void test(id a, C0 *c0) { + BlockTy b0 = ^{ (void)a; }; // disable tail-call optimization. + callee0(b0); + callee0(^{ (void)a; }); // enable tail-call optimization. + callee1(^{ (void)a; }); // disable tail-call optimization. + + BlockTy b1 = ^{ (void)a; }; // disable tail-call optimization. + [c0 m0:b1]; + [c0 m0:^{ (void)a; }]; // enable tail-call optimization. + [c0 m1:^{ (void)a; }]; // disable tail-call optimization. +} Index: test/Driver/fno-escaping-block-tail-calls.c =================================================================== --- /dev/null +++ test/Driver/fno-escaping-block-tail-calls.c @@ -0,0 +1,7 @@ +// RUN: %clang -### %s -fescaping-block-tail-calls -fno-escaping-block-tail-calls 2> %t +// RUN: FileCheck --check-prefix=CHECK-DISABLE < %t %s +// CHECK-DISABLE: "-fno-escaping-block-tail-calls" + +// RUN: %clang -### %s -fno-escaping-block-tail-calls -fescaping-block-tail-calls 2> %t +// RUN: FileCheck --check-prefix=CHECK-NO-DISABLE < %t %s +// CHECK-NO-DISABLE-NOT: "-fno-escaping-block-tail-calls"