PR c++/84726 - unnecessary capture of constant vars.
authorJason Merrill <jason@redhat.com>
Fri, 9 Mar 2018 21:40:55 +0000 (16:40 -0500)
committerJason Merrill <jason@gcc.gnu.org>
Fri, 9 Mar 2018 21:40:55 +0000 (16:40 -0500)
* cp-tree.h (LAMBDA_CAPTURE_EXPLICIT_P)
(LAMBDA_EXPR_CAPTURE_OPTIMIZED): New.
* expr.c (mark_use): Set LAMBDA_EXPR_CAPTURE_OPTIMIZED.
* lambda.c (is_constant_capture_proxy)
(current_lambda_expr, var_to_maybe_prune, mark_const_cap_r)
(prune_lambda_captures): New.
(finish_lambda_function): Call prune_lambda_captures.

From-SVN: r258398

gcc/cp/ChangeLog
gcc/cp/cp-tree.h
gcc/cp/expr.c
gcc/cp/lambda.c
gcc/testsuite/g++.dg/abi/lambda-capture1.C [new file with mode: 0644]

index 29121ce..09bd331 100644 (file)
@@ -1,4 +1,15 @@
 2018-03-09  Jason Merrill  <jason@redhat.com>
+
+       PR c++/84726 - unnecessary capture of constant vars.
+       * cp-tree.h (LAMBDA_CAPTURE_EXPLICIT_P)
+       (LAMBDA_EXPR_CAPTURE_OPTIMIZED): New.
+       * expr.c (mark_use): Set LAMBDA_EXPR_CAPTURE_OPTIMIZED.
+       * lambda.c (is_constant_capture_proxy)
+       (current_lambda_expr, var_to_maybe_prune, mark_const_cap_r)
+       (prune_lambda_captures): New.
+       (finish_lambda_function): Call prune_lambda_captures.
+
+2018-03-09  Jason Merrill  <jason@redhat.com>
            Jakub Jelinek  <jakub@redhat.com>
 
        PR c++/84076
index 190286d..4a406d2 100644 (file)
@@ -352,6 +352,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
       TEMPLATE_PARM_PARAMETER_PACK (in TEMPLATE_PARM_INDEX)
       ATTR_IS_DEPENDENT (in the TREE_LIST for an attribute)
       ABI_TAG_IMPLICIT (in the TREE_LIST for the argument of abi_tag)
+      LAMBDA_CAPTURE_EXPLICIT_P (in a TREE_LIST in LAMBDA_EXPR_CAPTURE_LIST)
       CONSTRUCTOR_IS_DIRECT_INIT (in CONSTRUCTOR)
       LAMBDA_EXPR_CAPTURES_THIS_P (in LAMBDA_EXPR)
       DECLTYPE_FOR_LAMBDA_CAPTURE (in DECLTYPE_TYPE)
@@ -403,6 +404,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
       CONSTRUCTOR_MUTABLE_POISON (in CONSTRUCTOR)
       OVL_HIDDEN_P (in OVERLOAD)
       SWITCH_STMT_NO_BREAK_P (in SWITCH_STMT)
+      LAMBDA_EXPR_CAPTURE_OPTIMIZED (in LAMBDA_EXPR)
    3: (TREE_REFERENCE_EXPR) (in NON_LVALUE_EXPR) (commented-out).
       ICS_BAD_FLAG (in _CONV)
       FN_TRY_BLOCK_P (in TRY_BLOCK)
@@ -1258,6 +1260,15 @@ enum cp_lambda_default_capture_mode_type {
 #define LAMBDA_EXPR_MUTABLE_P(NODE) \
   TREE_LANG_FLAG_1 (LAMBDA_EXPR_CHECK (NODE))
 
+/* True iff uses of a const variable capture were optimized away.  */
+#define LAMBDA_EXPR_CAPTURE_OPTIMIZED(NODE) \
+  TREE_LANG_FLAG_2 (LAMBDA_EXPR_CHECK (NODE))
+
+/* True if this TREE_LIST in LAMBDA_EXPR_CAPTURE_LIST is for an explicit
+   capture.  */
+#define LAMBDA_CAPTURE_EXPLICIT_P(NODE) \
+  TREE_LANG_FLAG_0 (TREE_LIST_CHECK (NODE))
+
 /* The source location of the lambda.  */
 #define LAMBDA_EXPR_LOCATION(NODE) \
   (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->locus)
@@ -6895,6 +6906,7 @@ extern void insert_capture_proxy          (tree);
 extern void insert_pending_capture_proxies     (void);
 extern bool is_capture_proxy                   (tree);
 extern bool is_normal_capture_proxy             (tree);
+extern bool is_constant_capture_proxy           (tree);
 extern void register_capture_members           (tree);
 extern tree lambda_expr_this_capture            (tree, bool);
 extern void maybe_generic_this_capture         (tree, tree);
@@ -6902,6 +6914,7 @@ extern tree maybe_resolve_dummy                   (tree, bool);
 extern tree current_nonlambda_function         (void);
 extern tree nonlambda_method_basetype          (void);
 extern tree current_nonlambda_scope            (void);
+extern tree current_lambda_expr                        (void);
 extern bool generic_lambda_fn_p                        (tree);
 extern tree do_dependent_capture               (tree, bool = false);
 extern bool lambda_fn_in_template_p            (tree);
index 2e67986..15894fc 100644 (file)
@@ -117,7 +117,15 @@ mark_use (tree expr, bool rvalue_p, bool read_p,
          tree cap = DECL_CAPTURED_VARIABLE (expr);
          if (TREE_CODE (TREE_TYPE (cap)) == TREE_CODE (TREE_TYPE (expr))
              && decl_constant_var_p (cap))
-           return RECUR (cap);
+           {
+             tree val = RECUR (cap);
+             if (!is_capture_proxy (val))
+               {
+                 tree l = current_lambda_expr ();
+                 LAMBDA_EXPR_CAPTURE_OPTIMIZED (l) = true;
+               }
+             return val;
+           }
        }
       if (outer_automatic_var_p (expr)
          && decl_constant_var_p (expr))
@@ -160,7 +168,15 @@ mark_use (tree expr, bool rvalue_p, bool read_p,
              tree cap = DECL_CAPTURED_VARIABLE (ref);
              if (TREE_CODE (TREE_TYPE (cap)) != REFERENCE_TYPE
                  && decl_constant_var_p (cap))
-               return RECUR (cap);
+               {
+                 tree val = RECUR (cap);
+                 if (!is_capture_proxy (val))
+                   {
+                     tree l = current_lambda_expr ();
+                     LAMBDA_EXPR_CAPTURE_OPTIMIZED (l) = true;
+                   }
+                 return val;
+               }
            }
          tree r = mark_rvalue_use (ref, loc, reject_builtin);
          if (r != ref)
index 094979e..de064ff 100644 (file)
@@ -291,6 +291,17 @@ is_normal_capture_proxy (tree decl)
   return DECL_NORMAL_CAPTURE_P (val);
 }
 
+/* Returns true iff DECL is a capture proxy for a normal capture
+   of a constant variable.  */
+
+bool
+is_constant_capture_proxy (tree decl)
+{
+  if (is_normal_capture_proxy (decl))
+    return decl_constant_var_p (DECL_CAPTURED_VARIABLE (decl));
+  return false;
+}
+
 /* VAR is a capture proxy created by build_capture_proxy; add it to the
    current function, which is the operator() for the appropriate lambda.  */
 
@@ -650,6 +661,7 @@ add_capture (tree lambda, tree id, tree orig_init, bool by_reference_p,
     return build_capture_proxy (member, initializer);
   /* For explicit captures we haven't started the function yet, so we wait
      and build the proxy from cp_parser_lambda_body.  */
+  LAMBDA_CAPTURE_EXPLICIT_P (LAMBDA_EXPR_CAPTURE_LIST (lambda)) = true;
   return NULL_TREE;
 }
 
@@ -840,6 +852,20 @@ lambda_expr_this_capture (tree lambda, bool add_capture_p)
   return result;
 }
 
+/* Return the innermost LAMBDA_EXPR we're currently in, if any.  */
+
+tree
+current_lambda_expr (void)
+{
+  tree type = current_class_type;
+  while (type && !LAMBDA_TYPE_P (type))
+    type = decl_type_context (TYPE_NAME (type));
+  if (type)
+    return CLASSTYPE_LAMBDA_EXPR (type);
+  else
+    return NULL_TREE;
+}
+
 /* Return the current LAMBDA_EXPR, if this is a resolvable dummy
    object.  NULL otherwise..  */
 
@@ -1374,11 +1400,120 @@ start_lambda_function (tree fco, tree lambda_expr)
   return body;
 }
 
+/* Subroutine of prune_lambda_captures: CAP is a node in
+   LAMBDA_EXPR_CAPTURE_LIST.  Return the variable it captures for which we
+   might optimize away the capture, or NULL_TREE if there is no such
+   variable.  */
+
+static tree
+var_to_maybe_prune (tree cap)
+{
+  if (LAMBDA_CAPTURE_EXPLICIT_P (cap))
+    /* Don't prune explicit captures.  */
+    return NULL_TREE;
+
+  tree mem = TREE_PURPOSE (cap);
+  if (!DECL_P (mem) || !DECL_NORMAL_CAPTURE_P (mem))
+    /* Packs and init-captures aren't captures of constant vars.  */
+    return NULL_TREE;
+
+  tree init = TREE_VALUE (cap);
+  if (is_normal_capture_proxy (init))
+    init = DECL_CAPTURED_VARIABLE (init);
+  if (decl_constant_var_p (init))
+    return init;
+
+  return NULL_TREE;
+}
+
+/* walk_tree helper for prune_lambda_captures: Remember which capture proxies
+   for constant variables are actually used in the lambda body.
+
+   There will always be a DECL_EXPR for the capture proxy; remember it when we
+   see it, but replace it with any other use.  */
+
+static tree
+mark_const_cap_r (tree *t, int *walk_subtrees, void *data)
+{
+  hash_map<tree,tree*> &const_vars = *(hash_map<tree,tree*>*)data;
+
+  tree var = NULL_TREE;
+  if (TREE_CODE (*t) == DECL_EXPR)
+    {
+      tree decl = DECL_EXPR_DECL (*t);
+      if (is_constant_capture_proxy (decl))
+       var = DECL_CAPTURED_VARIABLE (decl);
+      *walk_subtrees = 0;
+    }
+  else if (is_constant_capture_proxy (*t))
+    var = DECL_CAPTURED_VARIABLE (*t);
+
+  if (var)
+    {
+      tree *&slot = const_vars.get_or_insert (var);
+      if (!slot || VAR_P (*t))
+       slot = t;
+    }
+
+  return NULL_TREE;
+}
+
+/* We're at the end of processing a lambda; go back and remove any captures of
+   constant variables for which we've folded away all uses.  */
+
+static void
+prune_lambda_captures (tree body)
+{
+  tree lam = current_lambda_expr ();
+  if (!LAMBDA_EXPR_CAPTURE_OPTIMIZED (lam))
+    /* No uses were optimized away.  */
+    return;
+  if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lam) == CPLD_NONE)
+    /* No default captures, and we don't prune explicit captures.  */
+    return;
+
+  hash_map<tree,tree*> const_vars;
+
+  cp_walk_tree_without_duplicates (&body, mark_const_cap_r, &const_vars);
+
+  tree *fieldp = &TYPE_FIELDS (LAMBDA_EXPR_CLOSURE (lam));
+  for (tree *capp = &LAMBDA_EXPR_CAPTURE_LIST (lam); *capp; )
+    {
+      tree cap = *capp;
+      if (tree var = var_to_maybe_prune (cap))
+       {
+         tree *use = *const_vars.get (var);
+         if (TREE_CODE (*use) == DECL_EXPR)
+           {
+             /* All uses of this capture were folded away, leaving only the
+                proxy declaration.  */
+
+             /* Splice the capture out of LAMBDA_EXPR_CAPTURE_LIST.  */
+             *capp = TREE_CHAIN (cap);
+
+             /* And out of TYPE_FIELDS.  */
+             tree field = TREE_PURPOSE (cap);
+             while (*fieldp != field)
+               fieldp = &DECL_CHAIN (*fieldp);
+             *fieldp = DECL_CHAIN (*fieldp);
+
+             /* And remove the capture proxy declaration.  */
+             *use = void_node;
+             continue;
+           }
+       }
+
+      capp = &TREE_CHAIN (cap);
+    }
+}
+
 void
 finish_lambda_function (tree body)
 {
   finish_function_body (body);
 
+  prune_lambda_captures (body);
+
   /* Finish the function and generate code for it if necessary.  */
   tree fn = finish_function (/*inline_p=*/true);
 
diff --git a/gcc/testsuite/g++.dg/abi/lambda-capture1.C b/gcc/testsuite/g++.dg/abi/lambda-capture1.C
new file mode 100644 (file)
index 0000000..ad86eff
--- /dev/null
@@ -0,0 +1,11 @@
+// PR c++/84726
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert (X, #X)
+
+int main()
+{ 
+  const int i = 42;
+  auto l = [=]{return i+i;};
+  SA(sizeof(l) == 1);
+}