aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp/lambda.c
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2018-03-09 16:40:55 -0500
committerJason Merrill <jason@gcc.gnu.org>2018-03-09 16:40:55 -0500
commit1577f10a637352b4fe7fb4a4c0fd672a96c84f58 (patch)
treeafa0946e5089101e7d7a41bfed0009c943fdcfe8 /gcc/cp/lambda.c
parent5f2440b05799cb854b8d4a7622d50d7abcfecb0f (diff)
downloadgcc-1577f10a637352b4fe7fb4a4c0fd672a96c84f58.zip
gcc-1577f10a637352b4fe7fb4a4c0fd672a96c84f58.tar.gz
gcc-1577f10a637352b4fe7fb4a4c0fd672a96c84f58.tar.bz2
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. From-SVN: r258398
Diffstat (limited to 'gcc/cp/lambda.c')
-rw-r--r--gcc/cp/lambda.c135
1 files changed, 135 insertions, 0 deletions
diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c
index 094979e..de064ff 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);