wg21.link/p0588 changes i from not captured to captured by the lambda
in the testcase, but an implementation is allowed to optimize away
that capture if it doesn't affect the semantics of the program.  Since
we didn't capture it in previous versions of GCC, the optimization is
very desirable to avoid an ABI change.

Tested x86_64-pc-linux-gnu (against the Boost::hana testsuite as
well), applying to trunk.
commit 419de4066a17e67f1f0e30b957c8fdf27cce8c93
Author: Jason Merrill <ja...@redhat.com>
Date:   Tue Mar 6 17:25:34 2018 -0500

            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.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index bf9897c9980..1ea5dc4248c 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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);
diff --git a/gcc/cp/expr.c b/gcc/cp/expr.c
index 2e679868970..15894fc0b59 100644
--- a/gcc/cp/expr.c
+++ b/gcc/cp/expr.c
@@ -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)
diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c
index 094979e81a3..de064ffc85e 100644
--- a/gcc/cp/lambda.c
+++ b/gcc/cp/lambda.c
@@ -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
index 00000000000..ad86eff876b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/lambda-capture1.C
@@ -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);
+}

Reply via email to