https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97172
Jakub Jelinek <jakub at gcc dot gnu.org> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |jakub at gcc dot gnu.org --- Comment #24 from Jakub Jelinek <jakub at gcc dot gnu.org> --- Completely untested routine that would allow unsharing expressions including SAVE_EXPRs/TARGET_EXPRs, obviously then the result couldn't be used in the IL for code generation, only for warnings. It is basically unshare_expr, which afterwards when it sees SAVE_EXPRs or TARGET_EXPRs for the first time copies those and unshares their subtrees and when it sees those second and following time just reuses what it did before. /* Helper function for fully_unshare_expr. Copy SAVE_EXPRs and TARGET_EXPRs including their subtrees, but do that only once. */ static tree fully_unshare_expr_r (tree *tp, int *walk_subtrees, void *data) { enum tree_code code = TREE_CODE (*tp); if (code == SAVE_EXPR || code == TARGET_EXPR) { hash_map<tree, tree> **map = (hash_map<tree, tree> **) data; *walk_subtrees = 0; if (*map == NULL) *map = new hash_map<tree, tree>; tree &cached = (*map)->get_or_insert (*tp); if (cached) { *tp = cached; return NULL_TREE; } tree t = copy_node (*tp); cached = t; int len = 1; if (code == TARGET_EXPR) len = (TREE_OPERAND (t, 3) == TREE_OPERAND (t, 1)) ? 3 : 4; for (int i = 0; i < len; i++) { TREE_OPERAND (t, i) = unshare_expr (TREE_OPERAND (t, i)); walk_tree (&TREE_OPERAND (t, i), fully_unshare_expr_r, data, NULL); } if (len == 3) TREE_OPERAND (t, 3) = TREE_OPERAND (t, 1); return NULL_TREE; } /* 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; return NULL_TREE; } /* Like unshare_expr, but unshare also SAVE_EXPRs and TARGET_EXPRs. After unsharing SAVE_EXPRs and TARGET_EXPRs, the returned expression shouldn't be gimplified into the IL, as that could mean evaluating side-effects multiple times. */ tree fully_unshare_expr (tree expr) { hash_map <tree, tree> *map = NULL; expr = unshare_expr (expr); walk_tree (&expr, fully_unshare_expr_r, &map, NULL); delete map; return expr; }