From a4fcfa4da04771d28ea76ad876fa8645fd6032e6 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Fri, 2 Mar 2018 01:53:15 +0000 Subject: [PATCH] Add an option to disable tail-call optimization for escaping blocks. This makes it easier to debug crashes and hangs in block functions since users can easily find out where the block is called from. The option doesn't disable tail-calls from non-escaping blocks since non-escaping blocks are not as hard to debug as escaping blocks. rdar://problem/35758207 Differential Revision: https://reviews.llvm.org/D43841 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@326530 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Decl.h | 9 +++- include/clang/Driver/Options.td | 2 + include/clang/Frontend/CodeGenOptions.def | 2 + lib/CodeGen/CGCall.cpp | 19 +++++-- lib/Driver/ToolChains/Clang.cpp | 3 ++ lib/Frontend/CompilerInvocation.cpp | 2 + lib/Sema/SemaExpr.cpp | 4 ++ lib/Sema/SemaExprObjC.cpp | 5 ++ lib/Sema/SemaPseudoObject.cpp | 1 + .../disable-tail-call-escaping-block.m | 54 +++++++++++++++++++ test/Driver/fno-escaping-block-tail-calls.c | 7 +++ 11 files changed, 103 insertions(+), 5 deletions(-) create mode 100644 test/CodeGenObjC/disable-tail-call-escaping-block.m create mode 100644 test/Driver/fno-escaping-block-tail-calls.c diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 5b5ad34b0d..57c6f45336 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -3802,6 +3802,10 @@ private: 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 @@ protected: 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 @@ public: 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, diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index 6824858b99..d813bab99d 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -1430,6 +1430,8 @@ def fopenmp_cuda_mode : Flag<["-"], "fopenmp-cuda-mode">, Group, Flags< 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">; diff --git a/include/clang/Frontend/CodeGenOptions.def b/include/clang/Frontend/CodeGenOptions.def index 2b3ab5d745..edf266f743 100644 --- a/include/clang/Frontend/CodeGenOptions.def +++ b/include/clang/Frontend/CodeGenOptions.def @@ -62,6 +62,8 @@ CODEGENOPT(DebugPassManager, 1, 0) ///< Prints debug information for the new ///< 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 diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index d1b07f8681..08e0ec3eda 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -1887,10 +1887,21 @@ void CodeGenModule::ConstructAttributeList( } 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); diff --git a/lib/Driver/ToolChains/Clang.cpp b/lib/Driver/ToolChains/Clang.cpp index 60a04b6581..3c08e88bcd 100644 --- a/lib/Driver/ToolChains/Clang.cpp +++ b/lib/Driver/ToolChains/Clang.cpp @@ -3454,6 +3454,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, 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); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index a8b7dd9664..23f3c8fbbd 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -640,6 +640,8 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, 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) || diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 134415646d..8c22c0e643 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -4841,6 +4841,10 @@ bool Sema::GatherArgumentsForCall(SourceLocation CallLoc, FunctionDecl *FDecl, (!Param || !Param->hasAttr())) CFAudited = true; + if (Proto->getExtParameterInfo(i).isNoEscape()) + if (auto *BE = dyn_cast(Arg->IgnoreParenNoopCasts(Context))) + BE->getBlockDecl()->setDoesNotEscape(); + InitializedEntity Entity = Param ? InitializedEntity::InitializeParameter(Context, Param, ProtoArgType) diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index 6a725c485d..8d017af991 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -1613,6 +1613,11 @@ bool Sema::CheckMessageArgumentTypes(QualType ReceiverType, 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) && diff --git a/lib/Sema/SemaPseudoObject.cpp b/lib/Sema/SemaPseudoObject.cpp index f09a6f7158..5fa4342f18 100644 --- a/lib/Sema/SemaPseudoObject.cpp +++ b/lib/Sema/SemaPseudoObject.cpp @@ -993,6 +993,7 @@ ObjCSubscriptOpBuilder::buildAssignmentOperation(Scope *Sc, SourceLocation opcLoc, BinaryOperatorKind opcode, Expr *LHS, Expr *RHS) { + assert(false); assert(BinaryOperator::isAssignmentOp(opcode)); // There must be a method to do the Index'ed assignment. if (!findAtIndexSetter()) diff --git a/test/CodeGenObjC/disable-tail-call-escaping-block.m b/test/CodeGenObjC/disable-tail-call-escaping-block.m new file mode 100644 index 0000000000..48b0eda5a5 --- /dev/null +++ b/test/CodeGenObjC/disable-tail-call-escaping-block.m @@ -0,0 +1,54 @@ +// 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: store i8* bitcast (void (i8*)* @[[TEST_BLOCK_INVOKE6:.*]] 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: define internal void @[[TEST_BLOCK_INVOKE6]]({{.*}}) #[[ENABLEATTR]] { + +// CHECK: attributes #[[ENABLEATTR]] = {{{.*}}"disable-tail-calls"="false"{{.*}}} +// CHECK: attributes #[[DISABLEATTR]] = {{{.*}}"disable-tail-calls"="true"{{.*}}} + +typedef void (^BlockTy)(void); +typedef void (*NoEscapeFnTy)(__attribute__((noescape)) BlockTy); + +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 + +NoEscapeFnTy noescapefunc; + +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. + + noescapefunc(^{ (void)a; }); // enable tail-call optimization. +} diff --git a/test/Driver/fno-escaping-block-tail-calls.c b/test/Driver/fno-escaping-block-tail-calls.c new file mode 100644 index 0000000000..2d1b0a373e --- /dev/null +++ b/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" -- 2.40.0