From 1577f10a637352b4fe7fb4a4c0fd672a96c84f58 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Fri, 9 Mar 2018 16:40:55 -0500 Subject: [PATCH] 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 --- gcc/cp/ChangeLog | 11 ++ gcc/cp/cp-tree.h | 13 ++ gcc/cp/expr.c | 20 ++- gcc/cp/lambda.c | 135 +++++++++++++++++++++ gcc/testsuite/g++.dg/abi/lambda-capture1.C | 11 ++ 5 files changed, 188 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/g++.dg/abi/lambda-capture1.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 29121cebc30..09bd3318482 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,14 @@ +2018-03-09 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. + 2018-03-09 Jason Merrill Jakub Jelinek diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 190286dcedd..4a406d2e9f5 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 &const_vars = *(hash_map*)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 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); +} -- 2.30.2