Index: include/clang/AST/DeclObjC.h =================================================================== --- include/clang/AST/DeclObjC.h +++ include/clang/AST/DeclObjC.h @@ -414,6 +414,8 @@ ArrayRef Params, ArrayRef SelLocs = llvm::None); + bool isNoEscapeParam(unsigned Idx) const; + // Iterator access to parameter types. struct GetTypeFn { QualType operator()(const ParmVarDecl *PD) const { return PD->getType(); } Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -3503,6 +3503,8 @@ return llvm::makeArrayRef(param_type_begin(), param_type_end()); } + bool isNoEscapeParam(unsigned Idx) const; + ExtProtoInfo getExtProtoInfo() const { ExtProtoInfo EPI; EPI.ExtInfo = getExtInfo(); Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -1415,6 +1415,8 @@ HelpText<"Disable OpenMP code for SIMD-based constructs.">; def fno_optimize_sibling_calls : Flag<["-"], "fno-optimize-sibling-calls">, Group; def foptimize_sibling_calls : Flag<["-"], "foptimize-sibling-calls">, Group; +def fno_disable_tail_calls_escaping_blocks : Flag<["-"], "fno-disable-tail-calls-escaping-blocks">, Group, Flags<[CC1Option]>; +def fdisable_tail_calls_escaping_blocks : Flag<["-"], "fdisable-tail-calls-escaping-blocks">, Group, Flags<[CC1Option]>; 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(DisableTailCallsEscapingBlocks, 1, 0) ///< Do not emit tail calls for + ///< 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/AST/DeclObjC.cpp =================================================================== --- lib/AST/DeclObjC.cpp +++ lib/AST/DeclObjC.cpp @@ -854,6 +854,11 @@ setParamsAndSelLocs(C, Params, SelLocs); } +bool ObjCMethodDecl::isNoEscapeParam(unsigned Idx) const { + assert(Idx < param_size() && "Index too large"); + return (*(param_begin() + Idx))->hasAttr(); +} + /// \brief A definition will return its interface declaration. /// An interface declaration will return its definition. /// Otherwise it will return itself. Index: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -2813,6 +2813,10 @@ } } +bool FunctionProtoType::isNoEscapeParam(unsigned Idx) const { + return getExtParameterInfo(Idx).isNoEscape(); +} + bool FunctionProtoType::hasDependentExceptionSpec() const { if (Expr *NE = getNoexceptExpr()) return NE->isValueDependent(); Index: lib/CodeGen/CGCall.cpp =================================================================== --- lib/CodeGen/CGCall.cpp +++ lib/CodeGen/CGCall.cpp @@ -1871,10 +1871,15 @@ } if (!AttrOnCallSite) { - bool DisableTailCalls = - CodeGenOpts.DisableTailCalls || - (TargetDecl && (TargetDecl->hasAttr() || - TargetDecl->hasAttr())); + bool DisableTailCalls = CodeGenOpts.DisableTailCalls; + + if (TargetDecl && + ((TargetDecl->hasAttr() || + TargetDecl->hasAttr()) || + (CodeGenOpts.DisableTailCallsEscapingBlocks && + isEscapingBlock(TargetDecl)))) + DisableTailCalls = true; + FuncAttrs.addAttribute("disable-tail-calls", llvm::toStringRef(DisableTailCalls)); GetCPUAndFeaturesAttributes(TargetDecl, FuncAttrs); Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -3940,9 +3940,11 @@ #endif // First, use the argument types that the type info knows about + unsigned Idx = ParamsToSkip; + for (auto I = CallArgTypeInfo->param_type_begin() + ParamsToSkip, E = CallArgTypeInfo->param_type_end(); - I != E; ++I, ++Arg) { + I != E; ++I, ++Arg, ++Idx) { assert(Arg != ArgRange.end() && "Running over edge of argument list!"); assert((isGenericMethod || ((*I)->isVariablyModifiedType() || @@ -3955,6 +3957,11 @@ .getTypePtr())) && "type mismatch in call argument!"); ArgTypes.push_back(*I); + if (CGM.getCodeGenOpts().DisableTailCallsEscapingBlocks && + CallArgTypeInfo->isNoEscapeParam(Idx)) + if (const auto *BE = dyn_cast( + (*Arg)->IgnoreParenNoopCasts(getContext()))) + CGM.addNoEscapeBlock(BE->getBlockDecl()); } } Index: lib/CodeGen/CodeGenModule.h =================================================================== --- lib/CodeGen/CodeGenModule.h +++ lib/CodeGen/CodeGenModule.h @@ -466,6 +466,8 @@ /// them if the constexpr evaluator gets aggressive. llvm::DenseMap EmittedGlobalBlocks; + llvm::SmallPtrSet NoEscapeBlocks; + /// @name Cache for Blocks Runtime Globals /// @{ @@ -554,6 +556,16 @@ return *ObjCData; } + bool isEscapingBlock(const Decl *D) { + if (const auto *BD = dyn_cast(D)) + return !NoEscapeBlocks.count(BD); + return false; + } + + void addNoEscapeBlock(const BlockDecl *BD) { + NoEscapeBlocks.insert(BD); + } + // Version checking function, used to implement ObjC's @available: // i32 @__isOSVersionAtLeast(i32, i32, i32) llvm::Constant *IsOSVersionAtLeastFn = nullptr; Index: lib/Driver/ToolChains/Clang.cpp =================================================================== --- lib/Driver/ToolChains/Clang.cpp +++ lib/Driver/ToolChains/Clang.cpp @@ -3439,6 +3439,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_fdisable_tail_calls_escaping_blocks, + options::OPT_fno_disable_tail_calls_escaping_blocks)) + CmdArgs.push_back("-fdisable-tail-calls-escaping-blocks"); 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 @@ -639,6 +639,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.DisableTailCallsEscapingBlocks = + Args.hasArg(OPT_fdisable_tail_calls_escaping_blocks); Opts.FloatABI = Args.getLastArgValue(OPT_mfloat_abi); Opts.LessPreciseFPMAD = Args.hasArg(OPT_cl_mad_enable) || Args.hasArg(OPT_cl_unsafe_math_optimizations) || 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 -fdisable-tail-calls-escaping-blocks -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/fdisable-tail-calls-escaping-blocks.c =================================================================== --- /dev/null +++ test/Driver/fdisable-tail-calls-escaping-blocks.c @@ -0,0 +1,7 @@ +// RUN: %clang -### %s -fno-disable-tail-calls-escaping-blocks -fdisable-tail-calls-escaping-blocks 2> %t +// RUN: FileCheck --check-prefix=CHECK-DISABLE < %t %s +// CHECK-DISABLE: "-fdisable-tail-calls-escaping-blocks" + +// RUN: %clang -### %s -fdisable-tail-calls-escaping-blocks -fno-disable-tail-calls-escaping-blocks 2> %t +// RUN: FileCheck --check-prefix=CHECK-NO-DISABLE < %t %s +// CHECK-NO-DISABLE-NOT: "-fdisable-tail-calls-escaping-blocks"