diff options
Diffstat (limited to 'gcc/tree-ssa.c')
-rw-r--r-- | gcc/tree-ssa.c | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/gcc/tree-ssa.c b/gcc/tree-ssa.c index 51b16899121..db707fb35a9 100644 --- a/gcc/tree-ssa.c +++ b/gcc/tree-ssa.c @@ -243,6 +243,207 @@ flush_pending_stmts (edge e) redirect_edge_var_map_clear (e); } +/* Given a tree for an expression for which we might want to emit + locations or values in debug information (generally a variable, but + we might deal with other kinds of trees in the future), return the + tree that should be used as the variable of a DEBUG_BIND STMT or + VAR_LOCATION INSN or NOTE. Return NULL if VAR is not to be tracked. */ + +tree +target_for_debug_bind (tree var) +{ + if (!MAY_HAVE_DEBUG_STMTS) + return NULL_TREE; + + if (TREE_CODE (var) != VAR_DECL + && TREE_CODE (var) != PARM_DECL) + return NULL_TREE; + + if (DECL_HAS_VALUE_EXPR_P (var)) + return target_for_debug_bind (DECL_VALUE_EXPR (var)); + + if (DECL_IGNORED_P (var)) + return NULL_TREE; + + if (!is_gimple_reg (var)) + return NULL_TREE; + + return var; +} + +/* Called via walk_tree, look for SSA_NAMEs that have already been + released. */ + +static tree +find_released_ssa_name (tree *tp, int *walk_subtrees, void *data_) +{ + struct walk_stmt_info *wi = (struct walk_stmt_info *) data_; + + if (wi->is_lhs) + return NULL_TREE; + + if (TREE_CODE (*tp) == SSA_NAME) + { + if (SSA_NAME_IN_FREE_LIST (*tp)) + return *tp; + + *walk_subtrees = 0; + } + else if (IS_TYPE_OR_DECL_P (*tp)) + *walk_subtrees = 0; + + return NULL_TREE; +} + +/* Given a VAR whose definition STMT is to be moved to the iterator + position TOGSIP in the TOBB basic block, verify whether we're + moving it across any of the debug statements that use it, and + adjust them as needed. If TOBB is NULL, then the definition is + understood as being removed, and TOGSIP is unused. */ +void +propagate_var_def_into_debug_stmts (tree var, + basic_block tobb, + const gimple_stmt_iterator *togsip) +{ + imm_use_iterator imm_iter; + gimple stmt; + use_operand_p use_p; + tree value = NULL; + bool no_value = false; + + if (!MAY_HAVE_DEBUG_STMTS) + return; + + FOR_EACH_IMM_USE_STMT (stmt, imm_iter, var) + { + basic_block bb; + gimple_stmt_iterator si; + + if (!is_gimple_debug (stmt)) + continue; + + if (tobb) + { + bb = gimple_bb (stmt); + + if (bb != tobb) + { + gcc_assert (dom_info_available_p (CDI_DOMINATORS)); + if (dominated_by_p (CDI_DOMINATORS, bb, tobb)) + continue; + } + else + { + si = *togsip; + + if (gsi_end_p (si)) + continue; + + do + { + gsi_prev (&si); + if (gsi_end_p (si)) + break; + } + while (gsi_stmt (si) != stmt); + + if (gsi_end_p (si)) + continue; + } + } + + /* Here we compute (lazily) the value assigned to VAR, but we + remember if we tried before and failed, so that we don't try + again. */ + if (!value && !no_value) + { + gimple def_stmt = SSA_NAME_DEF_STMT (var); + + if (is_gimple_assign (def_stmt)) + { + if (!dom_info_available_p (CDI_DOMINATORS)) + { + struct walk_stmt_info wi; + + memset (&wi, 0, sizeof (wi)); + + /* When removing blocks without following reverse + dominance order, we may sometimes encounter SSA_NAMEs + that have already been released, referenced in other + SSA_DEFs that we're about to release. Consider: + + <bb X>: + v_1 = foo; + + <bb Y>: + w_2 = v_1 + bar; + # DEBUG w => w_2 + + If we deleted BB X first, propagating the value of + w_2 won't do us any good. It's too late to recover + their original definition of v_1: when it was + deleted, it was only referenced in other DEFs, it + couldn't possibly know it should have been retained, + and propagating every single DEF just in case it + might have to be propagated into a DEBUG STMT would + probably be too wasteful. + + When dominator information is not readily + available, we check for and accept some loss of + debug information. But if it is available, + there's no excuse for us to remove blocks in the + wrong order, so we don't even check for dead SSA + NAMEs. SSA verification shall catch any + errors. */ + if (!walk_gimple_op (def_stmt, find_released_ssa_name, &wi)) + no_value = true; + } + + if (!no_value) + value = gimple_assign_rhs_to_tree (def_stmt); + } + + if (!value) + no_value = true; + } + + if (no_value) + gimple_debug_bind_reset_value (stmt); + else + FOR_EACH_IMM_USE_ON_STMT (use_p, imm_iter) + SET_USE (use_p, unshare_expr (value)); + + update_stmt (stmt); + } +} + + +/* Given a STMT to be moved to the iterator position TOBSIP in the + TOBB basic block, verify whether we're moving it across any of the + debug statements that use it. If TOBB is NULL, then the definition + is understood as being removed, and TOBSIP is unused. */ + +void +propagate_defs_into_debug_stmts (gimple def, basic_block tobb, + const gimple_stmt_iterator *togsip) +{ + ssa_op_iter op_iter; + def_operand_p def_p; + + if (!MAY_HAVE_DEBUG_STMTS) + return; + + FOR_EACH_SSA_DEF_OPERAND (def_p, def, op_iter, SSA_OP_DEF) + { + tree var = DEF_FROM_PTR (def_p); + + if (TREE_CODE (var) != SSA_NAME) + continue; + + propagate_var_def_into_debug_stmts (var, tobb, togsip); + } +} + /* Return true if SSA_NAME is malformed and mark it visited. IS_VIRTUAL is true if this SSA_NAME was found inside a virtual @@ -636,6 +837,9 @@ verify_ssa (bool check_modified_stmt) goto err; } } + else if (gimple_debug_bind_p (stmt) + && !gimple_debug_bind_has_value_p (stmt)) + continue; /* Verify the single virtual operand and its constraints. */ has_err = false; @@ -1480,6 +1684,8 @@ warn_uninitialized_vars (bool warn_possibly_uninitialized) { struct walk_stmt_info wi; data.stmt = gsi_stmt (gsi); + if (is_gimple_debug (data.stmt)) + continue; memset (&wi, 0, sizeof (wi)); wi.info = &data; walk_gimple_op (gsi_stmt (gsi), warn_uninitialized_var, &wi); |