https://gcc.gnu.org/g:0389f4fda3a8474533d78d397f7421738971f4f8
commit r17-594-g0389f4fda3a8474533d78d397f7421738971f4f8 Author: Andrew Pinski <[email protected]> Date: Sun May 17 14:32:33 2026 -0700 tree: Move unshare_expr from gimplifier to generic tree We use unshare_expr in many places now outside of gimple even. So it makes sense to move the decl to tree.h. A few sources can now even not need to include gimplify.h; I have not checked all of them just a few which seemed like including gimplify.h didn't make sense. This also moves the implementations of unshare_expr, unshare_expr_without_location and copy_if_shared from gimplify.cc to tree.cc to keep the headers "clean". Bootstrapped and tested on x86_64-linux-gnu. Changes since v1: * v2: Move implementation too. gcc/ChangeLog: * cfgrtl.cc: Don't include gimplify.h or gimplify-me.h. * cgraphbuild.cc: Likewise. * emit-rtl.cc: Likewie. * tree-ssa-dom.cc: Likewise. * tree-ssa-dse.cc: Likewise. * tree-ssa-loop-im.cc: Likewise. * tree-ssa-loop-niter.cc: Likewise. * tree-ssa-loop-unswitch.cc: Likewise. * tree-ssa-math-opts.cc: Likewise. * tree-ssa-phiopt.cc: Likewise. * tree-ssa-phiprop.cc: Likewise. * tree-ssa-pre.cc: Likewise. * tree-ssa-propagate.cc: Likewise. * tree-ssa-sccvn.cc: Likewise. * gimplify.h (unshare_expr): Remove. (unshare_expr_without_location): Remove. (copy_if_shared): Remove. * tree.h (unshare_expr): New decl. (unshare_expr_without_location): Likewise. (copy_if_shared): Likewise. * gimplify.cc (mostly_copy_tree_r): Moved to tree.cc. (copy_if_shared_r): Likewise. (copy_if_shared): Likewise. (unshare_expr): Likewise. (prune_expr_location): Likewise. (unshare_expr_without_location): Likewise. * tree.cc (mostly_copy_tree_r): Moved from gimplify.cc (copy_if_shared_r): Likewise. (copy_if_shared): Likewise. (unshare_expr): Likewise. (prune_expr_location): Likewise. (unshare_expr_without_location): Likewise. Signed-off-by: Andrew Pinski <[email protected]> Diff: --- gcc/cfgrtl.cc | 1 - gcc/cgraphbuild.cc | 1 - gcc/emit-rtl.cc | 1 - gcc/gimplify.cc | 160 ------------------------------------------ gcc/gimplify.h | 3 - gcc/tree-ssa-dom.cc | 1 - gcc/tree-ssa-dse.cc | 1 - gcc/tree-ssa-loop-im.cc | 1 - gcc/tree-ssa-loop-niter.cc | 1 - gcc/tree-ssa-loop-unswitch.cc | 1 - gcc/tree-ssa-math-opts.cc | 2 - gcc/tree-ssa-phiopt.cc | 2 - gcc/tree-ssa-phiprop.cc | 1 - gcc/tree-ssa-pre.cc | 1 - gcc/tree-ssa-propagate.cc | 1 - gcc/tree-ssa-sccvn.cc | 1 - gcc/tree.cc | 159 +++++++++++++++++++++++++++++++++++++++++ gcc/tree.h | 9 +++ 18 files changed, 168 insertions(+), 179 deletions(-) diff --git a/gcc/cfgrtl.cc b/gcc/cfgrtl.cc index 7714e5548c2d..0ec72f08fa87 100644 --- a/gcc/cfgrtl.cc +++ b/gcc/cfgrtl.cc @@ -62,7 +62,6 @@ along with GCC; see the file COPYING3. If not see #include "tree-pass.h" #include "print-rtl.h" #include "rtl-iter.h" -#include "gimplify.h" #include "profile.h" #include "sreal.h" diff --git a/gcc/cgraphbuild.cc b/gcc/cgraphbuild.cc index e33a414310bc..3faf8395db5e 100644 --- a/gcc/cgraphbuild.cc +++ b/gcc/cgraphbuild.cc @@ -31,7 +31,6 @@ along with GCC; see the file COPYING3. If not see #include "gimple-walk.h" #include "ipa-utils.h" #include "except.h" -#include "gimplify.h" /* Context of record_reference. */ struct record_reference_ctx diff --git a/gcc/emit-rtl.cc b/gcc/emit-rtl.cc index e41ec2283b8f..7f5d267341ac 100644 --- a/gcc/emit-rtl.cc +++ b/gcc/emit-rtl.cc @@ -63,7 +63,6 @@ along with GCC; see the file COPYING3. If not see #include "rtx-vector-builder.h" #include "gimple.h" #include "gimple-ssa.h" -#include "gimplify.h" #include "bbitmap.h" struct target_rtl default_target_rtl; diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc index e4db4b1d9bd2..cdd039eb2bde 100644 --- a/gcc/gimplify.cc +++ b/gcc/gimplify.cc @@ -896,131 +896,6 @@ gimple_add_tmp_var (tree tmp) } } - - -/* This page contains routines to unshare tree nodes, i.e. to duplicate tree - nodes that are referenced more than once in GENERIC functions. This is - necessary because gimplification (translation into GIMPLE) is performed - by modifying tree nodes in-place, so gimplification of a shared node in a - first context could generate an invalid GIMPLE form in a second context. - - This is achieved with a simple mark/copy/unmark algorithm that walks the - GENERIC representation top-down, marks nodes with TREE_VISITED the first - time it encounters them, duplicates them if they already have TREE_VISITED - set, and finally removes the TREE_VISITED marks it has set. - - The algorithm works only at the function level, i.e. it generates a GENERIC - representation of a function with no nodes shared within the function when - passed a GENERIC function (except for nodes that are allowed to be shared). - - At the global level, it is also necessary to unshare tree nodes that are - referenced in more than one function, for the same aforementioned reason. - This requires some cooperation from the front-end. There are 2 strategies: - - 1. Manual unsharing. The front-end needs to call unshare_expr on every - expression that might end up being shared across functions. - - 2. Deep unsharing. This is an extension of regular unsharing. Instead - of calling unshare_expr on expressions that might be shared across - functions, the front-end pre-marks them with TREE_VISITED. This will - ensure that they are unshared on the first reference within functions - when the regular unsharing algorithm runs. The counterpart is that - this algorithm must look deeper than for manual unsharing, which is - specified by LANG_HOOKS_DEEP_UNSHARING. - - If there are only few specific cases of node sharing across functions, it is - probably easier for a front-end to unshare the expressions manually. On the - contrary, if the expressions generated at the global level are as widespread - as expressions generated within functions, deep unsharing is very likely the - way to go. */ - -/* Similar to copy_tree_r but do not copy SAVE_EXPR or TARGET_EXPR nodes. - These nodes model computations that must be done once. If we were to - unshare something like SAVE_EXPR(i++), the gimplification process would - create wrong code. However, if DATA is non-null, it must hold a pointer - set that is used to unshare the subtrees of these nodes. */ - -static tree -mostly_copy_tree_r (tree *tp, int *walk_subtrees, void *data) -{ - tree t = *tp; - enum tree_code code = TREE_CODE (t); - - /* Do not copy SAVE_EXPR, TARGET_EXPR or BIND_EXPR nodes themselves, but - copy their subtrees if we can make sure to do it only once. */ - if (code == SAVE_EXPR || code == TARGET_EXPR || code == BIND_EXPR) - { - if (data && !((hash_set<tree> *)data)->add (t)) - ; - else - *walk_subtrees = 0; - } - - /* Stop at types, decls, constants like copy_tree_r. */ - else if (TREE_CODE_CLASS (code) == tcc_type - || TREE_CODE_CLASS (code) == tcc_declaration - || TREE_CODE_CLASS (code) == tcc_constant) - *walk_subtrees = 0; - - /* Cope with the statement expression extension. */ - else if (code == STATEMENT_LIST) - ; - - /* Leave the bulk of the work to copy_tree_r itself. */ - else - copy_tree_r (tp, walk_subtrees, NULL); - - return NULL_TREE; -} - -/* Callback for walk_tree to unshare most of the shared trees rooted at *TP. - If *TP has been visited already, then *TP is deeply copied by calling - mostly_copy_tree_r. DATA is passed to mostly_copy_tree_r unmodified. */ - -static tree -copy_if_shared_r (tree *tp, int *walk_subtrees, void *data) -{ - tree t = *tp; - enum tree_code code = TREE_CODE (t); - - /* Skip types, decls, and constants. But we do want to look at their - types and the bounds of types. Mark them as visited so we properly - unmark their subtrees on the unmark pass. If we've already seen them, - don't look down further. */ - if (TREE_CODE_CLASS (code) == tcc_type - || TREE_CODE_CLASS (code) == tcc_declaration - || TREE_CODE_CLASS (code) == tcc_constant) - { - if (TREE_VISITED (t)) - *walk_subtrees = 0; - else - TREE_VISITED (t) = 1; - } - - /* If this node has been visited already, unshare it and don't look - any deeper. */ - else if (TREE_VISITED (t)) - { - walk_tree (tp, mostly_copy_tree_r, data, NULL); - *walk_subtrees = 0; - } - - /* Otherwise, mark the node as visited and keep looking. */ - else - TREE_VISITED (t) = 1; - - return NULL_TREE; -} - -/* Unshare most of the shared trees rooted at *TP. DATA is passed to the - copy_if_shared_r callback unmodified. */ - -void -copy_if_shared (tree *tp, void *data) -{ - walk_tree (tp, copy_if_shared_r, data, NULL); -} - /* Unshare all the trees in the body of FNDECL, as well as in the bodies of any nested functions. */ @@ -1089,41 +964,6 @@ unvisit_body (tree fndecl) unvisit_body (cgn->decl); } -/* Unconditionally make an unshared copy of EXPR. This is used when using - stored expressions which span multiple functions, such as BINFO_VTABLE, - as the normal unsharing process can't tell that they're shared. */ - -tree -unshare_expr (tree expr) -{ - walk_tree (&expr, mostly_copy_tree_r, NULL, NULL); - return expr; -} - -/* Worker for unshare_expr_without_location. */ - -static tree -prune_expr_location (tree *tp, int *walk_subtrees, void *) -{ - if (EXPR_P (*tp)) - SET_EXPR_LOCATION (*tp, UNKNOWN_LOCATION); - else - *walk_subtrees = 0; - return NULL_TREE; -} - -/* Similar to unshare_expr but also prune all expression locations - from EXPR. */ - -tree -unshare_expr_without_location (tree expr) -{ - walk_tree (&expr, mostly_copy_tree_r, NULL, NULL); - if (EXPR_P (expr)) - walk_tree (&expr, prune_expr_location, NULL, NULL); - return expr; -} - /* Return the EXPR_LOCATION of EXPR, if it (maybe recursively) has one, OR_ELSE otherwise. The location of a STATEMENT_LISTs comprising at least one DEBUG_BEGIN_STMT followed by exactly one diff --git a/gcc/gimplify.h b/gcc/gimplify.h index caa35b426bd6..18c7514c9a3e 100644 --- a/gcc/gimplify.h +++ b/gcc/gimplify.h @@ -62,9 +62,6 @@ extern tree get_initialized_tmp_var (tree, gimple_seq *, gimple_seq * = NULL, extern void declare_vars (tree, gimple *, bool); extern void gimple_add_tmp_var (tree); extern void gimple_add_tmp_var_fn (struct function *, tree); -extern void copy_if_shared (tree *, void * = NULL); -extern tree unshare_expr (tree); -extern tree unshare_expr_without_location (tree); extern tree voidify_wrapper_expr (tree, tree); extern tree build_and_jump (tree *); extern enum gimplify_status gimplify_self_mod_expr (tree *, gimple_seq *, diff --git a/gcc/tree-ssa-dom.cc b/gcc/tree-ssa-dom.cc index 3be7979f5289..37a29697319b 100644 --- a/gcc/tree-ssa-dom.cc +++ b/gcc/tree-ssa-dom.cc @@ -42,7 +42,6 @@ along with GCC; see the file COPYING3. If not see #include "tree-ssa-scopedtables.h" #include "tree-ssa-threadedge.h" #include "tree-ssa-dom.h" -#include "gimplify.h" #include "tree-cfgcleanup.h" #include "dbgcnt.h" #include "alloc-pool.h" diff --git a/gcc/tree-ssa-dse.cc b/gcc/tree-ssa-dse.cc index 58cfa9ec129b..2f97d6d1a1ab 100644 --- a/gcc/tree-ssa-dse.cc +++ b/gcc/tree-ssa-dse.cc @@ -37,7 +37,6 @@ along with GCC; see the file COPYING3. If not see #include "tree-ssa-dse.h" #include "builtins.h" #include "gimple-fold.h" -#include "gimplify.h" #include "tree-eh.h" #include "cfganal.h" #include "cgraph.h" diff --git a/gcc/tree-ssa-loop-im.cc b/gcc/tree-ssa-loop-im.cc index 4f7401e2d5d3..2651e3919e3a 100644 --- a/gcc/tree-ssa-loop-im.cc +++ b/gcc/tree-ssa-loop-im.cc @@ -30,7 +30,6 @@ along with GCC; see the file COPYING3. If not see #include "fold-const.h" #include "cfganal.h" #include "tree-eh.h" -#include "gimplify.h" #include "gimple-iterator.h" #include "tree-cfg.h" #include "tree-ssa-loop-manip.h" diff --git a/gcc/tree-ssa-loop-niter.cc b/gcc/tree-ssa-loop-niter.cc index c39401147aed..f179be1ffc7c 100644 --- a/gcc/tree-ssa-loop-niter.cc +++ b/gcc/tree-ssa-loop-niter.cc @@ -32,7 +32,6 @@ along with GCC; see the file COPYING3. If not see #include "fold-const.h" #include "calls.h" #include "intl.h" -#include "gimplify.h" #include "gimple-iterator.h" #include "tree-cfg.h" #include "tree-ssa-loop-ivopts.h" diff --git a/gcc/tree-ssa-loop-unswitch.cc b/gcc/tree-ssa-loop-unswitch.cc index 96680bc64ea3..a18c9ccfffa4 100644 --- a/gcc/tree-ssa-loop-unswitch.cc +++ b/gcc/tree-ssa-loop-unswitch.cc @@ -26,7 +26,6 @@ along with GCC; see the file COPYING3. If not see #include "tree-pass.h" #include "ssa.h" #include "fold-const.h" -#include "gimplify.h" #include "tree-cfg.h" #include "tree-ssa.h" #include "tree-ssa-loop-niter.h" diff --git a/gcc/tree-ssa-math-opts.cc b/gcc/tree-ssa-math-opts.cc index cd3fd2fc8fb4..72f6dbd1a990 100644 --- a/gcc/tree-ssa-math-opts.cc +++ b/gcc/tree-ssa-math-opts.cc @@ -102,8 +102,6 @@ along with GCC; see the file COPYING3. If not see #include "fold-const.h" #include "gimple-iterator.h" #include "gimple-fold.h" -#include "gimplify.h" -#include "gimplify-me.h" #include "stor-layout.h" #include "tree-cfg.h" #include "tree-dfa.h" diff --git a/gcc/tree-ssa-phiopt.cc b/gcc/tree-ssa-phiopt.cc index e654e8236360..18b5f284eee4 100644 --- a/gcc/tree-ssa-phiopt.cc +++ b/gcc/tree-ssa-phiopt.cc @@ -35,9 +35,7 @@ along with GCC; see the file COPYING3. If not see #include "fold-const.h" #include "stor-layout.h" #include "cfganal.h" -#include "gimplify.h" #include "gimple-iterator.h" -#include "gimplify-me.h" #include "tree-cfg.h" #include "tree-dfa.h" #include "domwalk.h" diff --git a/gcc/tree-ssa-phiprop.cc b/gcc/tree-ssa-phiprop.cc index 54f3ad8d9f84..7e7a4534eb2b 100644 --- a/gcc/tree-ssa-phiprop.cc +++ b/gcc/tree-ssa-phiprop.cc @@ -29,7 +29,6 @@ along with GCC; see the file COPYING3. If not see #include "gimple-pretty-print.h" #include "fold-const.h" #include "tree-eh.h" -#include "gimplify.h" #include "gimple-iterator.h" #include "stor-layout.h" #include "tree-ssa-loop.h" diff --git a/gcc/tree-ssa-pre.cc b/gcc/tree-ssa-pre.cc index 0e5eaf86bc7e..5ded68443ddc 100644 --- a/gcc/tree-ssa-pre.cc +++ b/gcc/tree-ssa-pre.cc @@ -37,7 +37,6 @@ along with GCC; see the file COPYING3. If not see #include "gimple-iterator.h" #include "gimple-fold.h" #include "tree-eh.h" -#include "gimplify.h" #include "tree-cfg.h" #include "tree-into-ssa.h" #include "tree-dfa.h" diff --git a/gcc/tree-ssa-propagate.cc b/gcc/tree-ssa-propagate.cc index a87c45d3a122..992b4243670e 100644 --- a/gcc/tree-ssa-propagate.cc +++ b/gcc/tree-ssa-propagate.cc @@ -30,7 +30,6 @@ #include "gimple-iterator.h" #include "gimple-fold.h" #include "tree-eh.h" -#include "gimplify.h" #include "tree-cfg.h" #include "tree-ssa.h" #include "tree-ssa-propagate.h" diff --git a/gcc/tree-ssa-sccvn.cc b/gcc/tree-ssa-sccvn.cc index e19beb439d20..79b6b4652946 100644 --- a/gcc/tree-ssa-sccvn.cc +++ b/gcc/tree-ssa-sccvn.cc @@ -42,7 +42,6 @@ along with GCC; see the file COPYING3. If not see #include "gimple-iterator.h" #include "gimple-fold.h" #include "tree-eh.h" -#include "gimplify.h" #include "flags.h" #include "dojump.h" #include "explow.h" diff --git a/gcc/tree.cc b/gcc/tree.cc index 8479ffab584f..e3df004be973 100644 --- a/gcc/tree.cc +++ b/gcc/tree.cc @@ -15847,6 +15847,165 @@ diagnose_versioned_decls (tree old_decl, tree new_decl) (old_target_attr, old_decl, new_target_attr, new_decl); } + +/* This page contains routines to unshare tree nodes, i.e. to duplicate tree + nodes that are referenced more than once in GENERIC functions. This is + necessary because gimplification (translation into GIMPLE) is performed + by modifying tree nodes in-place, so gimplification of a shared node in a + first context could generate an invalid GIMPLE form in a second context. + + This is achieved with a simple mark/copy/unmark algorithm that walks the + GENERIC representation top-down, marks nodes with TREE_VISITED the first + time it encounters them, duplicates them if they already have TREE_VISITED + set, and finally removes the TREE_VISITED marks it has set. + + The algorithm works only at the function level, i.e. it generates a GENERIC + representation of a function with no nodes shared within the function when + passed a GENERIC function (except for nodes that are allowed to be shared). + + At the global level, it is also necessary to unshare tree nodes that are + referenced in more than one function, for the same aforementioned reason. + This requires some cooperation from the front-end. There are 2 strategies: + + 1. Manual unsharing. The front-end needs to call unshare_expr on every + expression that might end up being shared across functions. + + 2. Deep unsharing. This is an extension of regular unsharing. Instead + of calling unshare_expr on expressions that might be shared across + functions, the front-end pre-marks them with TREE_VISITED. This will + ensure that they are unshared on the first reference within functions + when the regular unsharing algorithm runs. The counterpart is that + this algorithm must look deeper than for manual unsharing, which is + specified by LANG_HOOKS_DEEP_UNSHARING. + + If there are only few specific cases of node sharing across functions, it is + probably easier for a front-end to unshare the expressions manually. On the + contrary, if the expressions generated at the global level are as widespread + as expressions generated within functions, deep unsharing is very likely the + way to go. */ + +/* Similar to copy_tree_r but do not copy SAVE_EXPR or TARGET_EXPR nodes. + These nodes model computations that must be done once. If we were to + unshare something like SAVE_EXPR(i++), the gimplification process would + create wrong code. However, if DATA is non-null, it must hold a pointer + set that is used to unshare the subtrees of these nodes. */ + +static tree +mostly_copy_tree_r (tree *tp, int *walk_subtrees, void *data) +{ + tree t = *tp; + enum tree_code code = TREE_CODE (t); + + /* Do not copy SAVE_EXPR, TARGET_EXPR or BIND_EXPR nodes themselves, but + copy their subtrees if we can make sure to do it only once. */ + if (code == SAVE_EXPR || code == TARGET_EXPR || code == BIND_EXPR) + { + if (data && !((hash_set<tree> *)data)->add (t)) + ; + else + *walk_subtrees = 0; + } + + /* Stop at types, decls, constants like copy_tree_r. */ + else if (TREE_CODE_CLASS (code) == tcc_type + || TREE_CODE_CLASS (code) == tcc_declaration + || TREE_CODE_CLASS (code) == tcc_constant) + *walk_subtrees = 0; + + /* Cope with the statement expression extension. */ + else if (code == STATEMENT_LIST) + ; + + /* Leave the bulk of the work to copy_tree_r itself. */ + else + copy_tree_r (tp, walk_subtrees, NULL); + + return NULL_TREE; +} + +/* Callback for walk_tree to unshare most of the shared trees rooted at *TP. + If *TP has been visited already, then *TP is deeply copied by calling + mostly_copy_tree_r. DATA is passed to mostly_copy_tree_r unmodified. */ + +static tree +copy_if_shared_r (tree *tp, int *walk_subtrees, void *data) +{ + tree t = *tp; + enum tree_code code = TREE_CODE (t); + + /* Skip types, decls, and constants. But we do want to look at their + types and the bounds of types. Mark them as visited so we properly + unmark their subtrees on the unmark pass. If we've already seen them, + don't look down further. */ + if (TREE_CODE_CLASS (code) == tcc_type + || TREE_CODE_CLASS (code) == tcc_declaration + || TREE_CODE_CLASS (code) == tcc_constant) + { + if (TREE_VISITED (t)) + *walk_subtrees = 0; + else + TREE_VISITED (t) = 1; + } + + /* If this node has been visited already, unshare it and don't look + any deeper. */ + else if (TREE_VISITED (t)) + { + walk_tree (tp, mostly_copy_tree_r, data, NULL); + *walk_subtrees = 0; + } + + /* Otherwise, mark the node as visited and keep looking. */ + else + TREE_VISITED (t) = 1; + + return NULL_TREE; +} + +/* Unshare most of the shared trees rooted at *TP. DATA is passed to the + copy_if_shared_r callback unmodified. */ + +void +copy_if_shared (tree *tp, void *data) +{ + walk_tree (tp, copy_if_shared_r, data, NULL); +} + +/* Unconditionally make an unshared copy of EXPR. This is used when using + stored expressions which span multiple functions, such as BINFO_VTABLE, + as the normal unsharing process can't tell that they're shared. */ + +tree +unshare_expr (tree expr) +{ + walk_tree (&expr, mostly_copy_tree_r, NULL, NULL); + return expr; +} + +/* Worker for unshare_expr_without_location. */ + +static tree +prune_expr_location (tree *tp, int *walk_subtrees, void *) +{ + if (EXPR_P (*tp)) + SET_EXPR_LOCATION (*tp, UNKNOWN_LOCATION); + else + *walk_subtrees = 0; + return NULL_TREE; +} + +/* Similar to unshare_expr but also prune all expression locations + from EXPR. */ + +tree +unshare_expr_without_location (tree expr) +{ + walk_tree (&expr, mostly_copy_tree_r, NULL, NULL); + if (EXPR_P (expr)) + walk_tree (&expr, prune_expr_location, NULL, NULL); + return expr; +} + void tree_cc_finalize (void) { diff --git a/gcc/tree.h b/gcc/tree.h index 3b012d0fd6ad..05400ada20ba 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -7204,4 +7204,13 @@ extern bool disjoint_version_decls (tree, tree); /* Checks if two overlapping decls are not mergeable. */ extern bool diagnose_versioned_decls (tree, tree); +/* Unshare tree if needed. */ +extern tree unshare_expr (tree); + +/* Unshare tree if needed. + Removing the locations if an expr. */ +extern tree unshare_expr_without_location (tree); + +extern void copy_if_shared (tree *, void * = NULL); + #endif /* GCC_TREE_H */
