diff options
author | Jason Merrill <jason@redhat.com> | 2018-03-09 16:40:55 -0500 |
---|---|---|
committer | Jason Merrill <jason@gcc.gnu.org> | 2018-03-09 16:40:55 -0500 |
commit | 1577f10a637352b4fe7fb4a4c0fd672a96c84f58 (patch) | |
tree | afa0946e5089101e7d7a41bfed0009c943fdcfe8 /gcc/cp/lambda.c | |
parent | 5f2440b05799cb854b8d4a7622d50d7abcfecb0f (diff) | |
download | gcc-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.c | 135 |
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); |