ahatanak created this revision.
ahatanak added a reviewer: rjmccall.

This patch adds a command line option (-fdisable-tail-calls-esca
ping-blocks) that annotates escaping block invoke functions with attribute 
"disable-tail-calls". This is an option that helps users in debugging their 
code who spend a lot of time trying to figure out where a block came from.

The user who is asking for this command line option does not want to disable 
tail-call optimization for non-escaping blocks. For example, in the following 
code, we should not disable tail-call optimization for the block that is 
directly passed to function "noescapefunc":

  void foo3(void);
  
  void foo1() {
    noescapefunc(^{ foo3(); }); // do not disable tail-call.
    BlockTy b = ^{ foo3(); }; // disable tail-call.
    noescapefunc(b);
  }

Ideally, I think we want to avoid disabling tail-call optimization for block 
"b" too, as it doesn't escape. However, this patch doesn't do anything to avoid 
disabling tail-call optimization for the block, since that would require a more 
complex analysis.

rdar://problem/35758207


https://reviews.llvm.org/D43841

Files:
  include/clang/AST/DeclObjC.h
  include/clang/AST/Type.h
  include/clang/Driver/Options.td
  include/clang/Frontend/CodeGenOptions.def
  lib/AST/DeclObjC.cpp
  lib/AST/Type.cpp
  lib/CodeGen/CGCall.cpp
  lib/CodeGen/CodeGenFunction.h
  lib/CodeGen/CodeGenModule.h
  lib/Driver/ToolChains/Clang.cpp
  lib/Frontend/CompilerInvocation.cpp
  test/CodeGenObjC/disable-tail-call-escaping-block.m
  test/Driver/fdisable-tail-calls-escaping-blocks.c

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"
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: 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: 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/CodeGen/CodeGenModule.h
===================================================================
--- lib/CodeGen/CodeGenModule.h
+++ lib/CodeGen/CodeGenModule.h
@@ -466,6 +466,8 @@
   /// them if the constexpr evaluator gets aggressive.
   llvm::DenseMap<const BlockExpr *, llvm::Constant *> EmittedGlobalBlocks;
 
+  llvm::SmallPtrSet<const BlockDecl *, 1> NoEscapeBlocks;
+
   /// @name Cache for Blocks Runtime Globals
   /// @{
 
@@ -554,6 +556,16 @@
     return *ObjCData;
   }
 
+  bool isEscapingBlock(const Decl *D) {
+    if (const auto *BD = dyn_cast<BlockDecl>(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/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<BlockExpr>(
+                  (*Arg)->IgnoreParenNoopCasts(getContext())))
+            CGM.addNoEscapeBlock(BE->getBlockDecl());
       }
     }
 
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<DisableTailCallsAttr>() ||
-                        TargetDecl->hasAttr<AnyX86InterruptAttr>()));
+    bool DisableTailCalls = CodeGenOpts.DisableTailCalls;
+
+    if (TargetDecl &&
+        ((TargetDecl->hasAttr<DisableTailCallsAttr>() ||
+          TargetDecl->hasAttr<AnyX86InterruptAttr>()) ||
+         (CodeGenOpts.DisableTailCallsEscapingBlocks &&
+          isEscapingBlock(TargetDecl))))
+      DisableTailCalls = true;
+
     FuncAttrs.addAttribute("disable-tail-calls",
                            llvm::toStringRef(DisableTailCalls));
     GetCPUAndFeaturesAttributes(TargetDecl, FuncAttrs);
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/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<NoEscapeAttr>();
+}
+
 /// \brief A definition will return its interface declaration.
 /// An interface declaration will return its definition.
 /// Otherwise it will return itself.
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: 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<f_Group>;
 def foptimize_sibling_calls : Flag<["-"], "foptimize-sibling-calls">, Group<f_Group>;
+def fno_disable_tail_calls_escaping_blocks : Flag<["-"], "fno-disable-tail-calls-escaping-blocks">, Group<f_Group>, Flags<[CC1Option]>;
+def fdisable_tail_calls_escaping_blocks : Flag<["-"], "fdisable-tail-calls-escaping-blocks">, Group<f_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/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/AST/DeclObjC.h
===================================================================
--- include/clang/AST/DeclObjC.h
+++ include/clang/AST/DeclObjC.h
@@ -414,6 +414,8 @@
                        ArrayRef<ParmVarDecl*> Params,
                        ArrayRef<SourceLocation> SelLocs = llvm::None);
 
+  bool isNoEscapeParam(unsigned Idx) const;
+
   // Iterator access to parameter types.
   struct GetTypeFn {
     QualType operator()(const ParmVarDecl *PD) const { return PD->getType(); }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to