commit 419de4066a17e67f1f0e30b957c8fdf27cce8c93
Author: Jason Merrill <jason@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.
@@ -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);
@@ -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)
@@ -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);
new file mode 100644
@@ -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);
+}