Commit 1577f10a by Jason Merrill Committed by Jason Merrill

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
parent 5f2440b0
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
......
......@@ -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);
......
// 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);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment