diff options
45 files changed, 895 insertions, 154 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 3c1add6..01bd2b9 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,5 +1,104 @@ 2017-12-12 Alexandre Oliva <aoliva@redhat.com> + * cfgexpand.c (expand_gimple_basic_block): Handle begin stmt + markers. Integrate source bind into debug stmt expand loop. + (pass_expand::execute): Check debug marker limit. Avoid deep + TER and expand debug locations for debug bind insns only. + * cse.c (insn_live_p): Keep nonbind markers and debug bindings + followed by them. + * df-scan.c (df_insn_delete): Accept out-of-block debug insn. + * final.c (reemit_insn_block_notes): Take current block from + nonbind markers. Declare note where it's first set. + (final_scan_insn): Handle begin stmt notes. Emit is_stmt according to + begin stmt markers if enabled. + (notice_source_line): Handle nonbind markers. Fail if their + location is unknown or that of builtins. + (rest_of_handle_final): Convert begin stmt markers to notes if + var-tracking didn't run. + (rest_of_clean_state): Skip begin stmt markers. + * gimple-pretty-print.c (dump_gimple_debug): Handle begin stmt + markers. + * function.c (allocate_struct_function): Set begin_stmt_markers. + * function.h (struct function): Add debug_marker_count counter + and debug_nonbind_markers flag. + * gimple-iterator.c (gsi_remove): Adjust debug_marker_count. + * gimple-low.c (lower_function_body): Adjust + debug_nonbind_markers. + (lower_stmt): Drop or skip gimple debug stmts. + (lower_try_catch): Skip debug stmts. + * gimple.c (gimple_build_debug_begin_stmt): New. + (gimple_copy): Increment debug_marker_count if copying one. + * gimple.h (gimple_build_debug_begin_stmt): Declare. + * gimplify.c (rexpr_location): New. + (rexpr_has_location): New. + (warn_switch_unreachable_r): Handle gimple debug stmts. + (shortcut_cond_r): Call expr_location. + (find_goto): New. + (find_goto_label): New. + (shortcut_cond_expr): Call expr_has_location, expr_location, and + find_goto_label. + (gimplify_cond_expr): Call find_goto_label, expr_has_location, and + expr_location. + (gimplify_expr): Handle begin stmt markers. Reject debug expr decls. + * langhooks-def.h (LANG_HOOKS_EMITS_BEGIN_STMT): New. Add to... + (LANG_HOOKS_INITIALIZER): ... this. + * langhooks.h (struct lang_hooks): Add emits_begin_stmt. + * lra-contraints.c (inherit_reload_reg): Tolerate between-blocks + debug insns. + (update_ebb_live_info): Skip debug insn markers. + * lra.c (debug_insn_static_data): Rename to... + (debug_bind_static_data): ... this. + (debug_marker_static_data): New. + (lra_set_insn_recog_data): Select one of the above depending + on debug insn kind. + (lra_update_isn_regno_info): Don't assume debug insns have + freqs. + (push_insns): Skip debug insns. + * lto-streamer-in.c (input_function): Drop debug stmts + depending on active options. Adjust debug_nonbind_markers. + * params.def (PARAM_MAX_DEBUG_MARKER_COUNT): New. + * print-rtl.c (rtx_writer::print_rtx_operand_code_0): Handle + begin stmt marker notes. + (print_insn): Likewise. + * recog.c (extract_insn): Recognize rtl for debug markers. + * rtl.def (DEBUG_MARKER): New. + * tree-inline.c: Include params.h. + (remap_gimple_stmt): Handle nonbind markers. + (maybe_move_debug_stmts_to_successors): Likewise. + (copy_debug_stmt): Likewise. + * tree-iterator.c (append_to_statement_list_1): Append begin stmt + markers regardless of no side effects. + (tsi_link_before): Don't update container's side effects when adding + a begin stmt marker. + (tsi_link_after): Likewise. + (expr_first): Skip begin stmt markers. + (expr_last): Likewise. + * tree-pretty-print (dump_generic_node): Handle begin stmt markers. + * tree-ssa-threadedge.c (propagate_threaded_block_debug_info): + Disregard nonbind markers. + * tree.c (make_node_stat): Don't set side effects for begin stmt + markers. + (build1_stat): Likewise. + * tree.def (DEBUG_BEGIN_STMT): New. + * tree.h (GOTO_DESTINATION): Require a GOTO_EXPR. + * var-tracking.c (delete_debug_insns): Renamed to... + (delete_vta_debug_insns): ... this. + (reemit_marker_as_note): New. + (vt_initialize): Reemit markers. + (delete_vta_debug_insns): Likewise. + (vt_debug_insns_local): Reemit or delete markers. + (variable_tracking_main_1): Likewise. + * doc/generic.texi (DEBUG_BEGIN_STMT): Document. + * doc/gimple.texi (gimple_debug_begin_stmt_p): New. + (gimple_debug_nonbind_marker_p): New. + (gimple_build_debug_bind): Adjust. + (gimple_build_debug_begin_stmt): New. + * doc/invoke.texi (max-debug-marker-count): New param. + * doc/rtl.texi (debug_implicit_ptr, entry_value): New. + (debug_parameter_ref, debug_marker): New. + (NOTE_INSN_BEGIN_STMT): New. + (DEBUG_INSN): Describe begin stmt markers. + * cfgbuild.c (find_bb_boundaries): Don't purge dead edges if, without debug insns, we wouldn't, but clean up debug insns after a control flow insn nevertheless. diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 4ad83f3..6c31dbe 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,8 @@ +2017-12-12 Alexandre Oliva <aoliva@redhat.com> + + * c-semantics.c (pop_stmt_list): Move begin stmt marker into + subsequent statement list. + 2017-12-07 Martin Sebor <msebor@redhat.com> PR c/81544 diff --git a/gcc/c-family/c-semantics.c b/gcc/c-family/c-semantics.c index 3ceb714..cd872d8 100644 --- a/gcc/c-family/c-semantics.c +++ b/gcc/c-family/c-semantics.c @@ -76,6 +76,27 @@ pop_stmt_list (tree t) free_stmt_list (t); t = u; } + /* If the statement list contained a debug begin stmt and a + statement list, move the debug begin stmt into the statement + list and return it. */ + else if (!tsi_end_p (i) + && TREE_CODE (tsi_stmt (i)) == DEBUG_BEGIN_STMT) + { + u = tsi_stmt (i); + tsi_next (&i); + if (tsi_one_before_end_p (i) + && TREE_CODE (tsi_stmt (i)) == STATEMENT_LIST) + { + tree l = tsi_stmt (i); + tsi_prev (&i); + tsi_delink (&i); + tsi_delink (&i); + i = tsi_start (l); + free_stmt_list (t); + t = l; + tsi_link_before (&i, u, TSI_SAME_STMT); + } + } } return t; diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index 14d7f63..f981266 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,12 @@ +2017-12-12 Alexandre Oliva <aoliva@redhat.com> + + * c-objc-common.h (LANG_HOOKS_EMITS_BEGIN_STMT): Redefine as true. + * c-parser.c (add_debug_begin_stmt): New. + (c_parser_declaration_or_fndef): Call it. + (c_parser_compound_statement_nostart): Likewise. + (c_parser_statement_after_labels): Likewise. + * c-typeck (c_finish_stmt_expr): Skip begin stmts markers. + 2017-12-07 Joseph Myers <joseph@codesourcery.com> * c-decl.c (build_compound_literal): Add parameter alignas_align diff --git a/gcc/c/c-objc-common.h b/gcc/c/c-objc-common.h index bee06e9..27ceabc 100644 --- a/gcc/c/c-objc-common.h +++ b/gcc/c/c-objc-common.h @@ -60,6 +60,8 @@ along with GCC; see the file COPYING3. If not see #define LANG_HOOKS_BUILTIN_FUNCTION c_builtin_function #undef LANG_HOOKS_BUILTIN_FUNCTION_EXT_SCOPE #define LANG_HOOKS_BUILTIN_FUNCTION_EXT_SCOPE c_builtin_function_ext_scope +#undef LANG_HOOKS_EMITS_BEGIN_STMT +#define LANG_HOOKS_EMITS_BEGIN_STMT true /* Attribute hooks. */ #undef LANG_HOOKS_COMMON_ATTRIBUTE_TABLE diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index d398548..f1bae8a 100644 --- a/gcc/c/c-parser.c +++ b/gcc/c/c-parser.c @@ -1649,6 +1649,19 @@ c_parser_external_declaration (c_parser *parser) static void c_finish_omp_declare_simd (c_parser *, tree, tree, vec<c_token>); static void c_finish_oacc_routine (struct oacc_routine_data *, tree, bool); +/* Build and add a DEBUG_BEGIN_STMT statement with location LOC. */ + +static void +add_debug_begin_stmt (location_t loc) +{ + if (!MAY_HAVE_DEBUG_MARKER_STMTS) + return; + + tree stmt = build0 (DEBUG_BEGIN_STMT, void_type_node); + SET_EXPR_LOCATION (stmt, loc); + add_stmt (stmt); +} + /* Parse a declaration or function definition (C90 6.5, 6.7.1, C99 6.7, 6.9.1, C11 6.7, 6.9.1). If FNDEF_OK is true, a function definition is accepted; otherwise (old-style parameter declarations) only other @@ -1749,6 +1762,8 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, bool diagnosed_no_specs = false; location_t here = c_parser_peek_token (parser)->location; + add_debug_begin_stmt (c_parser_peek_token (parser)->location); + if (static_assert_ok && c_parser_next_token_is_keyword (parser, RID_STATIC_ASSERT)) { @@ -4911,6 +4926,7 @@ c_parser_compound_statement_nostart (c_parser *parser) location_t label_loc = UNKNOWN_LOCATION; /* Quiet warning. */ if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE)) { + add_debug_begin_stmt (c_parser_peek_token (parser)->location); c_parser_consume_token (parser); return; } @@ -5365,6 +5381,10 @@ c_parser_statement_after_labels (c_parser *parser, bool *if_p, parser->in_if_block = false; if (if_p != NULL) *if_p = false; + + if (c_parser_peek_token (parser)->type != CPP_OPEN_BRACE) + add_debug_begin_stmt (loc); + switch (c_parser_peek_token (parser)->type) { case CPP_OPEN_BRACE: diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c index 676dbbd..13b2684 100644 --- a/gcc/c/c-typeck.c +++ b/gcc/c/c-typeck.c @@ -10710,6 +10710,10 @@ c_finish_stmt_expr (location_t loc, tree body) } else i = tsi_last (last); + if (TREE_CODE (tsi_stmt (i)) == DEBUG_BEGIN_STMT) + do + tsi_prev (&i); + while (TREE_CODE (tsi_stmt (i)) == DEBUG_BEGIN_STMT); last_p = tsi_stmt_ptr (i); last = *last_p; } @@ -10729,7 +10733,9 @@ c_finish_stmt_expr (location_t loc, tree body) /* In the case that the BIND_EXPR is not necessary, return the expression out from inside it. */ - if (last == BIND_EXPR_BODY (body) + if ((last == BIND_EXPR_BODY (body) + /* Skip nested debug stmts. */ + || last == expr_first (BIND_EXPR_BODY (body))) && BIND_EXPR_VARS (body) == NULL) { /* Even if this looks constant, do not allow it in a constant diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c index 3ee242df..ce98264 100644 --- a/gcc/cfgexpand.c +++ b/gcc/cfgexpand.c @@ -5657,39 +5657,68 @@ expand_gimple_basic_block (basic_block bb, bool disable_tail_calls) if (new_bb) return new_bb; } - else if (gimple_debug_bind_p (stmt)) + else if (is_gimple_debug (stmt)) { location_t sloc = curr_insn_location (); gimple_stmt_iterator nsi = gsi; for (;;) { - tree var = gimple_debug_bind_get_var (stmt); - tree value; - rtx val; + tree var; + tree value = NULL_TREE; + rtx val = NULL_RTX; machine_mode mode; - if (TREE_CODE (var) != DEBUG_EXPR_DECL - && TREE_CODE (var) != LABEL_DECL - && !target_for_debug_bind (var)) - goto delink_debug_stmt; + if (!gimple_debug_nonbind_marker_p (stmt)) + { + if (gimple_debug_bind_p (stmt)) + { + var = gimple_debug_bind_get_var (stmt); - if (gimple_debug_bind_has_value_p (stmt)) - value = gimple_debug_bind_get_value (stmt); - else - value = NULL_TREE; + if (TREE_CODE (var) != DEBUG_EXPR_DECL + && TREE_CODE (var) != LABEL_DECL + && !target_for_debug_bind (var)) + goto delink_debug_stmt; - last = get_last_insn (); + if (DECL_P (var)) + mode = DECL_MODE (var); + else + mode = TYPE_MODE (TREE_TYPE (var)); - set_curr_insn_location (gimple_location (stmt)); + if (gimple_debug_bind_has_value_p (stmt)) + value = gimple_debug_bind_get_value (stmt); + + val = gen_rtx_VAR_LOCATION + (mode, var, (rtx)value, VAR_INIT_STATUS_INITIALIZED); + } + else if (gimple_debug_source_bind_p (stmt)) + { + var = gimple_debug_source_bind_get_var (stmt); + + value = gimple_debug_source_bind_get_value (stmt); + + mode = DECL_MODE (var); - if (DECL_P (var)) - mode = DECL_MODE (var); + val = gen_rtx_VAR_LOCATION (mode, var, (rtx)value, + VAR_INIT_STATUS_UNINITIALIZED); + } + else + gcc_unreachable (); + } + /* If this function was first compiled with markers + enabled, but they're now disable (e.g. LTO), drop + them on the floor. */ + else if (gimple_debug_nonbind_marker_p (stmt) + && !MAY_HAVE_DEBUG_MARKER_INSNS) + goto delink_debug_stmt; + else if (gimple_debug_begin_stmt_p (stmt)) + val = GEN_RTX_DEBUG_MARKER_BEGIN_STMT_PAT (); else - mode = TYPE_MODE (TREE_TYPE (var)); + gcc_unreachable (); - val = gen_rtx_VAR_LOCATION - (mode, var, (rtx)value, VAR_INIT_STATUS_INITIALIZED); + last = get_last_insn (); + + set_curr_insn_location (gimple_location (stmt)); emit_debug_insn (val); @@ -5697,9 +5726,14 @@ expand_gimple_basic_block (basic_block bb, bool disable_tail_calls) { /* We can't dump the insn with a TREE where an RTX is expected. */ - PAT_VAR_LOCATION_LOC (val) = const0_rtx; + if (GET_CODE (val) == VAR_LOCATION) + { + gcc_checking_assert (PAT_VAR_LOCATION_LOC (val) == (rtx)value); + PAT_VAR_LOCATION_LOC (val) = const0_rtx; + } maybe_dump_rtl_for_gimple_stmt (stmt, last); - PAT_VAR_LOCATION_LOC (val) = (rtx)value; + if (GET_CODE (val) == VAR_LOCATION) + PAT_VAR_LOCATION_LOC (val) = (rtx)value; } delink_debug_stmt: @@ -5715,42 +5749,12 @@ expand_gimple_basic_block (basic_block bb, bool disable_tail_calls) if (gsi_end_p (nsi)) break; stmt = gsi_stmt (nsi); - if (!gimple_debug_bind_p (stmt)) + if (!is_gimple_debug (stmt)) break; } set_curr_insn_location (sloc); } - else if (gimple_debug_source_bind_p (stmt)) - { - location_t sloc = curr_insn_location (); - tree var = gimple_debug_source_bind_get_var (stmt); - tree value = gimple_debug_source_bind_get_value (stmt); - rtx val; - machine_mode mode; - - last = get_last_insn (); - - set_curr_insn_location (gimple_location (stmt)); - - mode = DECL_MODE (var); - - val = gen_rtx_VAR_LOCATION (mode, var, (rtx)value, - VAR_INIT_STATUS_UNINITIALIZED); - - emit_debug_insn (val); - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - /* We can't dump the insn with a TREE where an RTX - is expected. */ - PAT_VAR_LOCATION_LOC (val) = const0_rtx; - maybe_dump_rtl_for_gimple_stmt (stmt, last); - PAT_VAR_LOCATION_LOC (val) = (rtx)value; - } - - set_curr_insn_location (sloc); - } else { gcall *call_stmt = dyn_cast <gcall *> (stmt); @@ -6383,6 +6387,11 @@ pass_expand::execute (function *fun) FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR_FOR_FN (fun)->succs) e->flags &= ~EDGE_EXECUTABLE; + /* If the function has too many markers, drop them while expanding. */ + if (cfun->debug_marker_count + >= PARAM_VALUE (PARAM_MAX_DEBUG_MARKER_COUNT)) + cfun->debug_nonbind_markers = false; + lab_rtx_for_bb = new hash_map<basic_block, rtx_code_label *>; FOR_BB_BETWEEN (bb, init_block->next_bb, EXIT_BLOCK_PTR_FOR_FN (fun), next_bb) diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index e492f0a..4f1be5b 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,20 @@ +2017-12-12 Alexandre Oliva <aoliva@redhat.com> + + * constexpr.c (check_constexpr_ctor_body_1): Skip begin stmt + markers. + (constexpr_fn_retval): Likewise. + (potential_constant_expression_1): Likewise. + (cxx_eval_statement_list): Check that a begin stmt marker is + not used as the value of a statement list. + (cxx_eval_constant_expression): Return begin stmt markers + unchanged. + * cp-array-notation.c (stmt_location): New. + (cp_expand_cond_array_notations): Use it. + * cp-objcp-common.h (LANG_HOOKS_EMITS_BEGIN_STMT): Redefine as true. + * parser.c (add_debug_begin_stmt): New. + (cp_parser_statement): Call it. + * pt.c (tsubst_copy): Handle begin stmt markers. + 2017-12-07 Martin Sebor <msebor@redhat.com> PR c/81544 diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 6dfecfc..0455be1 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -455,6 +455,7 @@ check_constexpr_ctor_body_1 (tree last, tree list) case USING_STMT: case STATIC_ASSERT: + case DEBUG_BEGIN_STMT: return true; default: @@ -694,6 +695,7 @@ constexpr_fn_retval (tree body) return constexpr_fn_retval (BIND_EXPR_BODY (body)); case USING_STMT: + case DEBUG_BEGIN_STMT: return NULL_TREE; case CALL_EXPR: @@ -3856,6 +3858,14 @@ cxx_eval_statement_list (const constexpr_ctx *ctx, tree t, if (returns (jump_target) || breaks (jump_target)) break; } + /* Make sure we don't use the "result" of a debug-only marker. That + would be wrong. We should be using the result of the previous + statement, or NULL if there isn't one. In practice, this should + never happen: the statement after the marker should override the + result of the marker, so its value shouldn't survive in R. Now, + should that ever change, we'll need some fixing here to stop + markers from modifying the generated executable code. */ + gcc_checking_assert (!r || TREE_CODE (r) != DEBUG_BEGIN_STMT); return r; } @@ -4081,6 +4091,11 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, } break; + case DEBUG_BEGIN_STMT: + /* ??? It might be nice to retain this information somehow, so + as to be able to step into a constexpr function call. */ + /* Fall through. */ + case FUNCTION_DECL: case TEMPLATE_DECL: case LABEL_DECL: @@ -5187,6 +5202,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, case CONTINUE_STMT: case REQUIRES_EXPR: case STATIC_ASSERT: + case DEBUG_BEGIN_STMT: return true; case PARM_DECL: diff --git a/gcc/cp/cp-objcp-common.h b/gcc/cp/cp-objcp-common.h index a1b1fcd..8d3bc87 100644 --- a/gcc/cp/cp-objcp-common.h +++ b/gcc/cp/cp-objcp-common.h @@ -107,6 +107,8 @@ extern void cp_register_dumps (gcc::dump_manager *); #define LANG_HOOKS_MISSING_NORETURN_OK_P cp_missing_noreturn_ok_p #undef LANG_HOOKS_BLOCK_MAY_FALLTHRU #define LANG_HOOKS_BLOCK_MAY_FALLTHRU cxx_block_may_fallthru +#undef LANG_HOOKS_EMITS_BEGIN_STMT +#define LANG_HOOKS_EMITS_BEGIN_STMT true /* Attribute hooks. */ #undef LANG_HOOKS_COMMON_ATTRIBUTE_TABLE diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 891f742..94e87c2 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -10684,6 +10684,19 @@ cp_parser_lambda_body (cp_parser* parser, tree lambda_expr) /* Statements [gram.stmt.stmt] */ +/* Build and add a DEBUG_BEGIN_STMT statement with location LOC. */ + +static void +add_debug_begin_stmt (location_t loc) +{ + if (!MAY_HAVE_DEBUG_MARKER_STMTS) + return; + + tree stmt = build0 (DEBUG_BEGIN_STMT, void_type_node); + SET_EXPR_LOCATION (stmt, loc); + add_stmt (stmt); +} + /* Parse a statement. statement: @@ -10759,6 +10772,7 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr, token = cp_lexer_peek_token (parser->lexer); /* Remember the location of the first token in the statement. */ statement_location = token->location; + add_debug_begin_stmt (statement_location); /* If this is a keyword, then that will often determine what kind of statement we have. */ if (token->type == CPP_KEYWORD) diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 252712e..0cf6509 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -15305,6 +15305,12 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl) case PREDICT_EXPR: return t; + case DEBUG_BEGIN_STMT: + /* ??? There's no point in copying it for now, but maybe some + day it will contain more information, such as a pointer back + to the containing function, inlined copy or so. */ + return t; + default: /* We shouldn't get here, but keep going if !flag_checking. */ if (flag_checking) @@ -6962,11 +6962,18 @@ insn_live_p (rtx_insn *insn, int *counts) { rtx_insn *next; + if (DEBUG_MARKER_INSN_P (insn)) + return true; + for (next = NEXT_INSN (insn); next; next = NEXT_INSN (next)) if (NOTE_P (next)) continue; else if (!DEBUG_INSN_P (next)) return true; + /* If we find an inspection point, such as a debug begin stmt, + we want to keep the earlier debug insn. */ + else if (DEBUG_MARKER_INSN_P (next)) + return true; else if (INSN_VAR_LOCATION_DECL (insn) == INSN_VAR_LOCATION_DECL (next)) return false; diff --git a/gcc/df-scan.c b/gcc/df-scan.c index 8ab3d71..429dab8 100644 --- a/gcc/df-scan.c +++ b/gcc/df-scan.c @@ -945,7 +945,7 @@ df_insn_delete (rtx_insn *insn) In any case, we expect BB to be non-NULL at least up to register allocation, so disallow a non-NULL BB up to there. Not perfect but better than nothing... */ - gcc_checking_assert (bb != NULL || reload_completed); + gcc_checking_assert (bb != NULL || DEBUG_INSN_P (insn) || reload_completed); df_grow_bb_info (df_scan); df_grow_reg_info (); diff --git a/gcc/doc/generic.texi b/gcc/doc/generic.texi index 75c448e..b01cdaa 100644 --- a/gcc/doc/generic.texi +++ b/gcc/doc/generic.texi @@ -2006,6 +2006,11 @@ case 2 ... 5: The first value will be @code{CASE_LOW}, while the second will be @code{CASE_HIGH}. +@item DEBUG_BEGIN_STMT + +Marks the beginning of a source statement, for purposes of debug +information generation. + @end table diff --git a/gcc/doc/gimple.texi b/gcc/doc/gimple.texi index fa98800..4926787 100644 --- a/gcc/doc/gimple.texi +++ b/gcc/doc/gimple.texi @@ -831,6 +831,16 @@ expression to a variable. Return true if g is any of the OpenMP codes. @end deftypefn +@deftypefn {GIMPLE function} gimple_debug_begin_stmt_p (gimple g) +Return true if g is a @code{GIMPLE_DEBUG} that marks the beginning of +a source statement. +@end deftypefn + +@deftypefn {GIMPLE function} gimple_debug_nonbind_marker_p (gimple g) +Return true if g is a @code{GIMPLE_DEBUG} that marks a program location, +without any variable binding. +@end deftypefn + @node Manipulating GIMPLE statements @section Manipulating GIMPLE statements @cindex Manipulating GIMPLE statements @@ -1530,10 +1540,11 @@ Set the conditional @code{COND_STMT} to be of the form 'if (1 == 1)'. @subsection @code{GIMPLE_DEBUG} @cindex @code{GIMPLE_DEBUG} @cindex @code{GIMPLE_DEBUG_BIND} +@cindex @code{GIMPLE_DEBUG_BEGIN_STMT} @deftypefn {GIMPLE function} gdebug *gimple_build_debug_bind (tree var, @ tree value, gimple stmt) -Build a @code{GIMPLE_DEBUG} statement with @code{GIMPLE_DEBUG_BIND} of +Build a @code{GIMPLE_DEBUG} statement with @code{GIMPLE_DEBUG_BIND} @code{subcode}. The effect of this statement is to tell debug information generation machinery that the value of user variable @code{var} is given by @code{value} at that point, and to remain with @@ -1604,6 +1615,17 @@ Return @code{TRUE} if @code{stmt} binds a user variable to a value, and @code{FALSE} if it unbinds the variable. @end deftypefn +@deftypefn {GIMPLE function} gimple gimple_build_debug_begin_stmt (tree block, location_t location) +Build a @code{GIMPLE_DEBUG} statement with +@code{GIMPLE_DEBUG_BEGIN_STMT} @code{subcode}. The effect of this +statement is to tell debug information generation machinery that the +user statement at the given @code{location} and @code{block} starts at +the point at which the statement is inserted. The intent is that side +effects (e.g. variable bindings) of all prior user statements are +observable, and that none of the side effects of subsequent user +statements are. +@end deftypefn + @node @code{GIMPLE_EH_FILTER} @subsection @code{GIMPLE_EH_FILTER} @cindex @code{GIMPLE_EH_FILTER} diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 1413095..189b3e4 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -10609,6 +10609,13 @@ debug information may end up not being used; setting this higher may enable the compiler to find more complex debug expressions, but compile time and memory use may grow. The default is 12. +@item max-debug-marker-count +Sets a threshold on the number of debug markers (e.g. begin stmt +markers) to avoid complexity explosion at inlining or expanding to RTL. +If a function has more such gimple stmts than the set limit, such stmts +will be dropped from the inlined copy of a function, and from its RTL +expansion. The default is 100000. + @item min-nondebug-insn-uid Use uids starting at this parameter for nondebug insns. The range below the parameter is reserved exclusively for debug insns created by diff --git a/gcc/doc/rtl.texi b/gcc/doc/rtl.texi index a58eedc..dd3c0d3 100644 --- a/gcc/doc/rtl.texi +++ b/gcc/doc/rtl.texi @@ -3448,6 +3448,25 @@ Stands for the value bound to the @code{DEBUG_EXPR_DECL} @var{decl}, that points back to it, within value expressions in @code{VAR_LOCATION} nodes. +@findex debug_implicit_ptr +@item (debug_implicit_ptr:@var{mode} @var{decl}) +Stands for the location of a @var{decl} that is no longer addressable. + +@findex entry_value +@item (entry_value:@var{mode} @var{decl}) +Stands for the value a @var{decl} had at the entry point of the +containing function. + +@findex debug_parameter_ref +@item (debug_parameter_ref:@var{mode} @var{decl}) +Refers to a parameter that was completely optimized out. + +@findex debug_marker +@item (debug_marker:@var{mode}) +Marks a program location. With @code{VOIDmode}, it stands for the +beginning of a statement, a recommended inspection point logically after +all prior side effects, and before any subsequent side effects. + @end table @node Insns @@ -3724,6 +3743,12 @@ can be computed by evaluating the RTL expression from that static point in the program up to the next such note for the same user variable. +@findex NOTE_INSN_BEGIN_STMT +@item NOTE_INSN_BEGIN_STMT +This note is used to generate @code{is_stmt} markers in line number +debuggign information. It indicates the beginning of a user +statement. + @end table These codes are printed symbolically when they appear in debugging dumps. @@ -3741,15 +3766,25 @@ binds a user variable tree to an RTL representation of the it stands for the value bound to the corresponding @code{DEBUG_EXPR_DECL}. -Throughout optimization passes, binding information is kept in -pseudo-instruction form, so that, unlike notes, it gets the same -treatment and adjustments that regular instructions would. It is the -variable tracking pass that turns these pseudo-instructions into var -location notes, analyzing control flow, value equivalences and changes -to registers and memory referenced in value expressions, propagating -the values of debug temporaries and determining expressions that can -be used to compute the value of each user variable at as many points -(ranges, actually) in the program as possible. +@code{GIMPLE_DEBUG_BEGIN_STMT} is expanded to RTL as a @code{DEBUG_INSN} +with a @code{VOIDmode} @code{DEBUG_MARKER} @code{PATTERN}. These +@code{DEBUG_INSN}s, that do not carry @code{VAR_LOCATION} information, +just @code{DEBUG_MARKER}s, can be detected by testing +@code{DEBUG_MARKER_INSN_P}, whereas those that do can be recognized as +@code{DEBUG_BIND_INSN_P}. + +Throughout optimization passes, @code{DEBUG_INSN}s are not reordered +with respect to each other, particularly during scheduling. Binding +information is kept in pseudo-instruction form, so that, unlike notes, +it gets the same treatment and adjustments that regular instructions +would. It is the variable tracking pass that turns these +pseudo-instructions into @code{NOTE_INSN_VAR_LOCATION} and +@code{NOTE_INSN_BEGIN_STMT} notes, +analyzing control flow, value equivalences and changes to registers and +memory referenced in value expressions, propagating the values of debug +temporaries and determining expressions that can be used to compute the +value of each user variable at as many points (ranges, actually) in the +program as possible. Unlike @code{NOTE_INSN_VAR_LOCATION}, the value expression in an @code{INSN_VAR_LOCATION} denotes a value at that specific point in the diff --git a/gcc/final.c b/gcc/final.c index afb6906..3bcb9c5 100644 --- a/gcc/final.c +++ b/gcc/final.c @@ -1652,7 +1652,6 @@ reemit_insn_block_notes (void) { tree cur_block = DECL_INITIAL (cfun->decl); rtx_insn *insn; - rtx_note *note; insn = get_insns (); for (; insn; insn = NEXT_INSN (insn)) @@ -1660,17 +1659,29 @@ reemit_insn_block_notes (void) tree this_block; /* Prevent lexical blocks from straddling section boundaries. */ - if (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_SWITCH_TEXT_SECTIONS) - { - for (tree s = cur_block; s != DECL_INITIAL (cfun->decl); - s = BLOCK_SUPERCONTEXT (s)) - { - rtx_note *note = emit_note_before (NOTE_INSN_BLOCK_END, insn); - NOTE_BLOCK (note) = s; - note = emit_note_after (NOTE_INSN_BLOCK_BEG, insn); - NOTE_BLOCK (note) = s; - } - } + if (NOTE_P (insn)) + switch (NOTE_KIND (insn)) + { + case NOTE_INSN_SWITCH_TEXT_SECTIONS: + { + for (tree s = cur_block; s != DECL_INITIAL (cfun->decl); + s = BLOCK_SUPERCONTEXT (s)) + { + rtx_note *note = emit_note_before (NOTE_INSN_BLOCK_END, insn); + NOTE_BLOCK (note) = s; + note = emit_note_after (NOTE_INSN_BLOCK_BEG, insn); + NOTE_BLOCK (note) = s; + } + } + break; + + case NOTE_INSN_BEGIN_STMT: + this_block = LOCATION_BLOCK (NOTE_MARKER_LOCATION (insn)); + goto set_cur_block_to_this_block; + + default: + continue; + } if (!active_insn_p (insn)) continue; @@ -1691,6 +1702,7 @@ reemit_insn_block_notes (void) this_block = choose_inner_scope (this_block, insn_scope (body->insn (i))); } + set_cur_block_to_this_block: if (! this_block) { if (INSN_LOCATION (insn) == UNKNOWN_LOCATION) @@ -1707,7 +1719,7 @@ reemit_insn_block_notes (void) } /* change_scope emits before the insn, not after. */ - note = emit_note (NOTE_INSN_DELETED); + rtx_note *note = emit_note (NOTE_INSN_DELETED); change_scope (note, cur_block, DECL_INITIAL (cfun->decl)); delete_insn (note); @@ -2413,6 +2425,17 @@ final_scan_insn (rtx_insn *insn, FILE *file, int optimize_p ATTRIBUTE_UNUSED, debug_hooks->var_location (insn); break; + case NOTE_INSN_BEGIN_STMT: + gcc_checking_assert (cfun->debug_nonbind_markers); + if (!DECL_IGNORED_P (current_function_decl) + && notice_source_line (insn, NULL)) + { + (*debug_hooks->source_line) (last_linenum, last_columnnum, + last_filename, last_discriminator, + true); + } + break; + default: gcc_unreachable (); break; @@ -2499,7 +2522,15 @@ final_scan_insn (rtx_insn *insn, FILE *file, int optimize_p ATTRIBUTE_UNUSED, rtx body = PATTERN (insn); int insn_code_number; const char *templ; - bool is_stmt; + bool is_stmt, *is_stmt_p; + + if (MAY_HAVE_DEBUG_MARKER_INSNS && cfun->debug_nonbind_markers) + { + is_stmt = false; + is_stmt_p = NULL; + } + else + is_stmt_p = &is_stmt; /* Reset this early so it is correct for ASM statements. */ current_insn_predicate = NULL_RTX; @@ -2602,7 +2633,7 @@ final_scan_insn (rtx_insn *insn, FILE *file, int optimize_p ATTRIBUTE_UNUSED, /* Output this line note if it is the first or the last line note in a row. */ if (!DECL_IGNORED_P (current_function_decl) - && notice_source_line (insn, &is_stmt)) + && notice_source_line (insn, is_stmt_p)) { if (flag_verbose_asm) asm_show_source (last_filename, last_linenum); @@ -3095,7 +3126,22 @@ notice_source_line (rtx_insn *insn, bool *is_stmt) const char *filename; int linenum, columnnum; - if (override_filename) + if (NOTE_MARKER_P (insn)) + { + location_t loc = NOTE_MARKER_LOCATION (insn); + expanded_location xloc = expand_location (loc); + if (xloc.line == 0) + { + gcc_checking_assert (LOCATION_LOCUS (loc) == UNKNOWN_LOCATION + || LOCATION_LOCUS (loc) == BUILTINS_LOCATION); + return false; + } + filename = xloc.file; + linenum = xloc.line; + columnnum = xloc.column; + force_source_line = true; + } + else if (override_filename) { filename = override_filename; linenum = override_linenum; @@ -3128,7 +3174,8 @@ notice_source_line (rtx_insn *insn, bool *is_stmt) last_linenum = linenum; last_columnnum = columnnum; last_discriminator = discriminator; - *is_stmt = true; + if (is_stmt) + *is_stmt = true; high_block_linenum = MAX (last_linenum, high_block_linenum); high_function_linenum = MAX (last_linenum, high_function_linenum); return true; @@ -3140,7 +3187,8 @@ notice_source_line (rtx_insn *insn, bool *is_stmt) output the line table entry with is_stmt false so the debugger does not treat this as a breakpoint location. */ last_discriminator = discriminator; - *is_stmt = false; + if (is_stmt) + *is_stmt = false; return true; } @@ -4493,6 +4541,10 @@ rest_of_handle_final (void) { const char *fnname = get_fnname_from_decl (current_function_decl); + /* Turn debug markers into notes. */ + if (!MAY_HAVE_DEBUG_BIND_INSNS && MAY_HAVE_DEBUG_MARKER_INSNS) + variable_tracking_main (); + assemble_start_function (current_function_decl, fnname); final_start_function (get_insns (), asm_out_file, optimize); final (get_insns (), asm_out_file, optimize); @@ -4680,6 +4732,7 @@ rest_of_clean_state (void) if (final_output && (!NOTE_P (insn) || (NOTE_KIND (insn) != NOTE_INSN_VAR_LOCATION + && NOTE_KIND (insn) != NOTE_INSN_BEGIN_STMT && NOTE_KIND (insn) != NOTE_INSN_CALL_ARG_LOCATION && NOTE_KIND (insn) != NOTE_INSN_BLOCK_BEG && NOTE_KIND (insn) != NOTE_INSN_BLOCK_END diff --git a/gcc/function.c b/gcc/function.c index 88f93db..cdb2fc8 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -4939,6 +4939,12 @@ allocate_struct_function (tree fndecl, bool abstract_p) if (!profile_flag && !flag_instrument_function_entry_exit) DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (fndecl) = 1; } + + /* Don't enable begin stmt markers if var-tracking at assignments is + disabled. The markers make little sense without the variable + binding annotations among them. */ + cfun->debug_nonbind_markers = lang_hooks.emits_begin_stmt + && MAY_HAVE_DEBUG_MARKER_STMTS; } /* This is like allocate_struct_function, but pushes a new cfun for FNDECL diff --git a/gcc/function.h b/gcc/function.h index 971ab66..b94abb6 100644 --- a/gcc/function.h +++ b/gcc/function.h @@ -281,6 +281,12 @@ struct GTY(()) function { /* Last statement uid. */ int last_stmt_uid; + /* Debug marker counter. Count begin stmt markers. We don't have + to keep it exact, it's more of a rough estimate to enable us to + decide whether they are too many to copy during inlining, or when + expanding to RTL. */ + int debug_marker_count; + /* Function sequence number for profiling, debugging, etc. */ int funcdef_no; @@ -381,6 +387,10 @@ struct GTY(()) function { /* Nonzero if the current function contains a #pragma GCC unroll. */ unsigned int has_unroll : 1; + + /* Set when the function was compiled with generation of debug + (begin stmt, inline entry, ...) markers enabled. */ + unsigned int debug_nonbind_markers : 1; }; /* Add the decl D to the local_decls list of FUN. */ diff --git a/gcc/gimple-iterator.c b/gcc/gimple-iterator.c index 1e87825..46d3c6a 100644 --- a/gcc/gimple-iterator.c +++ b/gcc/gimple-iterator.c @@ -568,6 +568,10 @@ gsi_remove (gimple_stmt_iterator *i, bool remove_permanently) if (remove_permanently) { + if (gimple_debug_nonbind_marker_p (stmt)) + /* We don't need this to be exact, but try to keep it at least + close. */ + cfun->debug_marker_count--; require_eh_edge_purge = remove_stmt_from_eh_lp (stmt); gimple_remove_stmt_histograms (cfun, stmt); } diff --git a/gcc/gimple-low.c b/gcc/gimple-low.c index 22db61b..95f3f45 100644 --- a/gcc/gimple-low.c +++ b/gcc/gimple-low.c @@ -110,6 +110,17 @@ lower_function_body (void) i = gsi_last (lowered_body); + /* If we had begin stmt markers from e.g. PCH, but this compilation + doesn't want them, lower_stmt will have cleaned them up; we can + now clear the flag that indicates we had them. */ + if (!MAY_HAVE_DEBUG_MARKER_STMTS && cfun->debug_nonbind_markers) + { + /* This counter needs not be exact, but before lowering it will + most certainly be. */ + gcc_assert (cfun->debug_marker_count == 0); + cfun->debug_nonbind_markers = false; + } + /* If the function falls off the end, we need a null return statement. If we've already got one in the return_statements vector, we don't need to do anything special. Otherwise build one by hand. */ @@ -296,6 +307,20 @@ lower_stmt (gimple_stmt_iterator *gsi, struct lower_data *data) } break; + case GIMPLE_DEBUG: + gcc_checking_assert (cfun->debug_nonbind_markers); + /* We can't possibly have debug bind stmts before lowering, we + first emit them when entering SSA. */ + gcc_checking_assert (gimple_debug_nonbind_marker_p (stmt)); + /* Propagate fallthruness. */ + /* If the function (e.g. from PCH) had debug stmts, but they're + disabled for this compilation, remove them. */ + if (!MAY_HAVE_DEBUG_MARKER_STMTS) + gsi_remove (gsi, true); + else + gsi_next (gsi); + return; + case GIMPLE_NOP: case GIMPLE_ASM: case GIMPLE_ASSIGN: @@ -503,6 +528,10 @@ lower_try_catch (gimple_stmt_iterator *gsi, struct lower_data *data) cannot_fallthru = false; break; + case GIMPLE_DEBUG: + gcc_checking_assert (gimple_debug_begin_stmt_p (stmt)); + break; + default: /* This case represents statements to be executed when an exception occurs. Those statements are implicitly followed diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c index e952f33..aee2ad8 100644 --- a/gcc/gimple-pretty-print.c +++ b/gcc/gimple-pretty-print.c @@ -1363,6 +1363,13 @@ dump_gimple_debug (pretty_printer *buffer, gdebug *gs, int spc, gimple_debug_source_bind_get_value (gs)); break; + case GIMPLE_DEBUG_BEGIN_STMT: + if (flags & TDF_RAW) + dump_gimple_fmt (buffer, spc, flags, "%G BEGIN_STMT", gs); + else + dump_gimple_fmt (buffer, spc, flags, "# DEBUG BEGIN_STMT"); + break; + default: gcc_unreachable (); } diff --git a/gcc/gimple.c b/gcc/gimple.c index c986a73..5a118e9 100644 --- a/gcc/gimple.c +++ b/gcc/gimple.c @@ -853,6 +853,27 @@ gimple_build_debug_source_bind (tree var, tree value, } +/* Build a new GIMPLE_DEBUG_BEGIN_STMT statement in BLOCK at + LOCATION. */ + +gdebug * +gimple_build_debug_begin_stmt (tree block, location_t location + MEM_STAT_DECL) +{ + gdebug *p + = as_a <gdebug *> ( + gimple_build_with_ops_stat (GIMPLE_DEBUG, + (unsigned)GIMPLE_DEBUG_BEGIN_STMT, 0 + PASS_MEM_STAT)); + + gimple_set_location (p, location); + gimple_set_block (p, block); + cfun->debug_marker_count++; + + return p; +} + + /* Build a GIMPLE_OMP_CRITICAL statement. BODY is the sequence of statements for which only one thread can execute. @@ -1915,6 +1936,9 @@ gimple_copy (gimple *stmt) gimple_set_modified (copy, true); } + if (gimple_debug_nonbind_marker_p (stmt)) + cfun->debug_marker_count++; + return copy; } diff --git a/gcc/gimple.h b/gcc/gimple.h index 26fed1d..7c36679 100644 --- a/gcc/gimple.h +++ b/gcc/gimple.h @@ -1453,6 +1453,7 @@ gswitch *gimple_build_switch (tree, tree, vec<tree> ); geh_dispatch *gimple_build_eh_dispatch (int); gdebug *gimple_build_debug_bind (tree, tree, gimple * CXX_MEM_STAT_INFO); gdebug *gimple_build_debug_source_bind (tree, tree, gimple * CXX_MEM_STAT_INFO); +gdebug *gimple_build_debug_begin_stmt (tree, location_t CXX_MEM_STAT_INFO); gomp_critical *gimple_build_omp_critical (gimple_seq, tree, tree); gomp_for *gimple_build_omp_for (gimple_seq, int, tree, size_t, gimple_seq); gomp_parallel *gimple_build_omp_parallel (gimple_seq, tree, tree, tree); diff --git a/gcc/gimplify.c b/gcc/gimplify.c index 6a15daf4..8e1d400 100644 --- a/gcc/gimplify.c +++ b/gcc/gimplify.c @@ -983,6 +983,48 @@ unshare_expr_without_location (tree 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 + EXPR is the location of the EXPR. */ + +static location_t +rexpr_location (tree expr, location_t or_else = UNKNOWN_LOCATION) +{ + if (!expr) + return or_else; + + if (EXPR_HAS_LOCATION (expr)) + return EXPR_LOCATION (expr); + + if (TREE_CODE (expr) != STATEMENT_LIST) + return or_else; + + tree_stmt_iterator i = tsi_start (expr); + + bool found = false; + while (!tsi_end_p (i) && TREE_CODE (tsi_stmt (i)) == DEBUG_BEGIN_STMT) + { + found = true; + tsi_next (&i); + } + + if (!found || !tsi_one_before_end_p (i)) + return or_else; + + return rexpr_location (tsi_stmt (i), or_else); +} + +/* Return TRUE iff EXPR (maybe recursively) has a location; see + rexpr_location for the potential recursion. */ + +static inline bool +rexpr_has_location (tree expr) +{ + return rexpr_location (expr) != UNKNOWN_LOCATION; +} + /* WRAPPER is a code such as BIND_EXPR or CLEANUP_POINT_EXPR which can both contain statements and have a value. Assign its value to a temporary @@ -1764,6 +1806,13 @@ warn_switch_unreachable_r (gimple_stmt_iterator *gsi_p, bool *handled_ops_p, /* Walk the sub-statements. */ *handled_ops_p = false; break; + + case GIMPLE_DEBUG: + /* Ignore these. We may generate them before declarations that + are never executed. If there's something to warn about, + there will be non-debug stmts too, and we'll catch those. */ + break; + case GIMPLE_CALL: if (gimple_call_internal_p (stmt, IFN_ASAN_MARK)) { @@ -3466,7 +3515,7 @@ shortcut_cond_r (tree pred, tree *true_label_p, tree *false_label_p, append_to_statement_list (t, &expr); /* Set the source location of the && on the second 'if'. */ - new_locus = EXPR_HAS_LOCATION (pred) ? EXPR_LOCATION (pred) : locus; + new_locus = rexpr_location (pred, locus); t = shortcut_cond_r (TREE_OPERAND (pred, 1), true_label_p, false_label_p, new_locus); append_to_statement_list (t, &expr); @@ -3489,7 +3538,7 @@ shortcut_cond_r (tree pred, tree *true_label_p, tree *false_label_p, append_to_statement_list (t, &expr); /* Set the source location of the || on the second 'if'. */ - new_locus = EXPR_HAS_LOCATION (pred) ? EXPR_LOCATION (pred) : locus; + new_locus = rexpr_location (pred, locus); t = shortcut_cond_r (TREE_OPERAND (pred, 1), true_label_p, false_label_p, new_locus); append_to_statement_list (t, &expr); @@ -3511,7 +3560,7 @@ shortcut_cond_r (tree pred, tree *true_label_p, tree *false_label_p, /* Keep the original source location on the first 'if'. Set the source location of the ? on the second 'if'. */ - new_locus = EXPR_HAS_LOCATION (pred) ? EXPR_LOCATION (pred) : locus; + new_locus = rexpr_location (pred, locus); expr = build3 (COND_EXPR, void_type_node, TREE_OPERAND (pred, 0), shortcut_cond_r (TREE_OPERAND (pred, 1), true_label_p, false_label_p, locus), @@ -3535,6 +3584,45 @@ shortcut_cond_r (tree pred, tree *true_label_p, tree *false_label_p, return expr; } +/* If EXPR is a GOTO_EXPR, return it. If it is a STATEMENT_LIST, skip + any of its leading DEBUG_BEGIN_STMTS and recurse on the subsequent + statement, if it is the last one. Otherwise, return NULL. */ + +static tree +find_goto (tree expr) +{ + if (!expr) + return NULL_TREE; + + if (TREE_CODE (expr) == GOTO_EXPR) + return expr; + + if (TREE_CODE (expr) != STATEMENT_LIST) + return NULL_TREE; + + tree_stmt_iterator i = tsi_start (expr); + + while (!tsi_end_p (i) && TREE_CODE (tsi_stmt (i)) == DEBUG_BEGIN_STMT) + tsi_next (&i); + + if (!tsi_one_before_end_p (i)) + return NULL_TREE; + + return find_goto (tsi_stmt (i)); +} + +/* Same as find_goto, except that it returns NULL if the destination + is not a LABEL_DECL. */ + +static inline tree +find_goto_label (tree expr) +{ + tree dest = find_goto (expr); + if (dest && TREE_CODE (GOTO_DESTINATION (dest)) == LABEL_DECL) + return dest; + return NULL_TREE; +} + /* Given a conditional expression EXPR with short-circuit boolean predicates using TRUTH_ANDIF_EXPR or TRUTH_ORIF_EXPR, break the predicate apart into the equivalent sequence of conditionals. */ @@ -3565,8 +3653,8 @@ shortcut_cond_expr (tree expr) location_t locus = EXPR_LOC_OR_LOC (expr, input_location); TREE_OPERAND (expr, 0) = TREE_OPERAND (pred, 1); /* Set the source location of the && on the second 'if'. */ - if (EXPR_HAS_LOCATION (pred)) - SET_EXPR_LOCATION (expr, EXPR_LOCATION (pred)); + if (rexpr_has_location (pred)) + SET_EXPR_LOCATION (expr, rexpr_location (pred)); then_ = shortcut_cond_expr (expr); then_se = then_ && TREE_SIDE_EFFECTS (then_); pred = TREE_OPERAND (pred, 0); @@ -3587,8 +3675,8 @@ shortcut_cond_expr (tree expr) location_t locus = EXPR_LOC_OR_LOC (expr, input_location); TREE_OPERAND (expr, 0) = TREE_OPERAND (pred, 1); /* Set the source location of the || on the second 'if'. */ - if (EXPR_HAS_LOCATION (pred)) - SET_EXPR_LOCATION (expr, EXPR_LOCATION (pred)); + if (rexpr_has_location (pred)) + SET_EXPR_LOCATION (expr, rexpr_location (pred)); else_ = shortcut_cond_expr (expr); else_se = else_ && TREE_SIDE_EFFECTS (else_); pred = TREE_OPERAND (pred, 0); @@ -3615,20 +3703,16 @@ shortcut_cond_expr (tree expr) /* If our arms just jump somewhere, hijack those labels so we don't generate jumps to jumps. */ - if (then_ - && TREE_CODE (then_) == GOTO_EXPR - && TREE_CODE (GOTO_DESTINATION (then_)) == LABEL_DECL) + if (tree then_goto = find_goto_label (then_)) { - true_label = GOTO_DESTINATION (then_); + true_label = GOTO_DESTINATION (then_goto); then_ = NULL; then_se = false; } - if (else_ - && TREE_CODE (else_) == GOTO_EXPR - && TREE_CODE (GOTO_DESTINATION (else_)) == LABEL_DECL) + if (tree else_goto = find_goto_label (else_)) { - false_label = GOTO_DESTINATION (else_); + false_label = GOTO_DESTINATION (else_goto); else_ = NULL; else_se = false; } @@ -3692,8 +3776,8 @@ shortcut_cond_expr (tree expr) { tree last = expr_last (expr); t = build_and_jump (&end_label); - if (EXPR_HAS_LOCATION (last)) - SET_EXPR_LOCATION (t, EXPR_LOCATION (last)); + if (rexpr_has_location (last)) + SET_EXPR_LOCATION (t, rexpr_location (last)); append_to_statement_list (t, &expr); } if (emit_false) @@ -3988,39 +4072,35 @@ gimplify_cond_expr (tree *expr_p, gimple_seq *pre_p, fallback_t fallback) gimple_push_condition (); have_then_clause_p = have_else_clause_p = false; - if (TREE_OPERAND (expr, 1) != NULL - && TREE_CODE (TREE_OPERAND (expr, 1)) == GOTO_EXPR - && TREE_CODE (GOTO_DESTINATION (TREE_OPERAND (expr, 1))) == LABEL_DECL - && (DECL_CONTEXT (GOTO_DESTINATION (TREE_OPERAND (expr, 1))) - == current_function_decl) + label_true = find_goto_label (TREE_OPERAND (expr, 1)); + if (label_true + && DECL_CONTEXT (GOTO_DESTINATION (label_true)) == current_function_decl /* For -O0 avoid this optimization if the COND_EXPR and GOTO_EXPR have different locations, otherwise we end up with incorrect location information on the branches. */ && (optimize || !EXPR_HAS_LOCATION (expr) - || !EXPR_HAS_LOCATION (TREE_OPERAND (expr, 1)) - || EXPR_LOCATION (expr) == EXPR_LOCATION (TREE_OPERAND (expr, 1)))) + || !rexpr_has_location (label_true) + || EXPR_LOCATION (expr) == rexpr_location (label_true))) { - label_true = GOTO_DESTINATION (TREE_OPERAND (expr, 1)); have_then_clause_p = true; + label_true = GOTO_DESTINATION (label_true); } else label_true = create_artificial_label (UNKNOWN_LOCATION); - if (TREE_OPERAND (expr, 2) != NULL - && TREE_CODE (TREE_OPERAND (expr, 2)) == GOTO_EXPR - && TREE_CODE (GOTO_DESTINATION (TREE_OPERAND (expr, 2))) == LABEL_DECL - && (DECL_CONTEXT (GOTO_DESTINATION (TREE_OPERAND (expr, 2))) - == current_function_decl) + label_false = find_goto_label (TREE_OPERAND (expr, 2)); + if (label_false + && DECL_CONTEXT (GOTO_DESTINATION (label_false)) == current_function_decl /* For -O0 avoid this optimization if the COND_EXPR and GOTO_EXPR have different locations, otherwise we end up with incorrect location information on the branches. */ && (optimize || !EXPR_HAS_LOCATION (expr) - || !EXPR_HAS_LOCATION (TREE_OPERAND (expr, 2)) - || EXPR_LOCATION (expr) == EXPR_LOCATION (TREE_OPERAND (expr, 2)))) + || !rexpr_has_location (label_false) + || EXPR_LOCATION (expr) == rexpr_location (label_false))) { - label_false = GOTO_DESTINATION (TREE_OPERAND (expr, 2)); have_else_clause_p = true; + label_false = GOTO_DESTINATION (label_false); } else label_false = create_artificial_label (UNKNOWN_LOCATION); @@ -11809,6 +11889,18 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, ret = GS_ALL_DONE; break; + case DEBUG_EXPR_DECL: + gcc_unreachable (); + + case DEBUG_BEGIN_STMT: + gimplify_seq_add_stmt (pre_p, + gimple_build_debug_begin_stmt + (TREE_BLOCK (*expr_p), + EXPR_LOCATION (*expr_p))); + ret = GS_ALL_DONE; + *expr_p = NULL; + break; + case SSA_NAME: /* Allow callbacks into the gimplifier during optimization. */ ret = GS_ALL_DONE; diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h index 44e7edf..9362d96 100644 --- a/gcc/langhooks-def.h +++ b/gcc/langhooks-def.h @@ -132,6 +132,7 @@ extern int lhd_type_dwarf_attribute (const_tree, int); #define LANG_HOOKS_EH_USE_CXA_END_CLEANUP false #define LANG_HOOKS_DEEP_UNSHARING false #define LANG_HOOKS_CUSTOM_FUNCTION_DESCRIPTORS false +#define LANG_HOOKS_EMITS_BEGIN_STMT false #define LANG_HOOKS_RUN_LANG_SELFTESTS lhd_do_nothing #define LANG_HOOKS_GET_SUBSTRING_LOCATION lhd_get_substring_location @@ -346,6 +347,7 @@ extern void lhd_end_section (void); LANG_HOOKS_EH_USE_CXA_END_CLEANUP, \ LANG_HOOKS_DEEP_UNSHARING, \ LANG_HOOKS_CUSTOM_FUNCTION_DESCRIPTORS, \ + LANG_HOOKS_EMITS_BEGIN_STMT, \ LANG_HOOKS_RUN_LANG_SELFTESTS, \ LANG_HOOKS_GET_SUBSTRING_LOCATION \ } diff --git a/gcc/langhooks.h b/gcc/langhooks.h index afc879f..dddb027 100644 --- a/gcc/langhooks.h +++ b/gcc/langhooks.h @@ -532,6 +532,9 @@ struct lang_hooks instead of trampolines. */ bool custom_function_descriptors; + /* True if this language emits begin stmt notes. */ + bool emits_begin_stmt; + /* Run all lang-specific selftests. */ void (*run_lang_selftests) (void); diff --git a/gcc/lra-constraints.c b/gcc/lra-constraints.c index a6600ef..4adf4bf 100644 --- a/gcc/lra-constraints.c +++ b/gcc/lra-constraints.c @@ -5265,10 +5265,11 @@ inherit_reload_reg (bool def_p, int original_regno, lra_update_insn_regno_info (as_a <rtx_insn *> (usage_insn)); if (lra_dump_file != NULL) { + basic_block bb = BLOCK_FOR_INSN (usage_insn); fprintf (lra_dump_file, " Inheritance reuse change %d->%d (bb%d):\n", original_regno, REGNO (new_reg), - BLOCK_FOR_INSN (usage_insn)->index); + bb ? bb->index : -1); dump_insn_slim (lra_dump_file, as_a <rtx_insn *> (usage_insn)); } } @@ -5807,6 +5808,13 @@ update_ebb_live_info (rtx_insn *head, rtx_insn *tail) if (NOTE_P (curr_insn) && NOTE_KIND (curr_insn) != NOTE_INSN_BASIC_BLOCK) continue; curr_bb = BLOCK_FOR_INSN (curr_insn); + if (!curr_bb) + { + gcc_assert (DEBUG_INSN_P (curr_insn)); + if (DEBUG_MARKER_INSN_P (curr_insn)) + continue; + curr_bb = prev_bb; + } if (curr_bb != prev_bb) { if (prev_bb != NULL) @@ -602,9 +602,9 @@ static struct lra_operand_data debug_operand_data = }; /* The following data are used as static insn data for all debug - insns. If structure lra_static_insn_data is changed, the + bind insns. If structure lra_static_insn_data is changed, the initializer should be changed too. */ -static struct lra_static_insn_data debug_insn_static_data = +static struct lra_static_insn_data debug_bind_static_data = { &debug_operand_data, 0, /* Duplication operands #. */ @@ -618,6 +618,22 @@ static struct lra_static_insn_data debug_insn_static_data = NULL /* Descriptions of operands in alternatives. */ }; +/* The following data are used as static insn data for all debug + marker insns. If structure lra_static_insn_data is changed, the + initializer should be changed too. */ +static struct lra_static_insn_data debug_marker_static_data = + { + &debug_operand_data, + 0, /* Duplication operands #. */ + -1, /* Commutative operand #. */ + 0, /* Operands #. There isn't any operand. */ + 0, /* Duplications #. */ + 0, /* Alternatives #. We are not interesting in alternatives + because we does not proceed debug_insns for reloads. */ + NULL, /* Hard registers referenced in machine description. */ + NULL /* Descriptions of operands in alternatives. */ + }; + /* Called once per compiler work to initialize some LRA data related to insns. */ static void @@ -947,12 +963,20 @@ lra_set_insn_recog_data (rtx_insn *insn) data->regs = NULL; if (DEBUG_INSN_P (insn)) { - data->insn_static_data = &debug_insn_static_data; data->dup_loc = NULL; data->arg_hard_regs = NULL; data->preferred_alternatives = ALL_ALTERNATIVES; - data->operand_loc = XNEWVEC (rtx *, 1); - data->operand_loc[0] = &INSN_VAR_LOCATION_LOC (insn); + if (DEBUG_BIND_INSN_P (insn)) + { + data->insn_static_data = &debug_bind_static_data; + data->operand_loc = XNEWVEC (rtx *, 1); + data->operand_loc[0] = &INSN_VAR_LOCATION_LOC (insn); + } + else if (DEBUG_MARKER_INSN_P (insn)) + { + data->insn_static_data = &debug_marker_static_data; + data->operand_loc = NULL; + } return data; } if (icode < 0) @@ -1597,7 +1621,7 @@ lra_update_insn_regno_info (rtx_insn *insn) return; data = lra_get_insn_recog_data (insn); static_data = data->insn_static_data; - freq = get_insn_freq (insn); + freq = NONDEBUG_INSN_P (insn) ? get_insn_freq (insn) : 0; invalidate_insn_data_regno_info (data, insn, freq); for (i = static_data->n_operands - 1; i >= 0; i--) add_regs_to_insn_regno_info (data, *data->operand_loc[i], insn, diff --git a/gcc/lto-streamer-in.c b/gcc/lto-streamer-in.c index 3db1d38..9b785ff 100644 --- a/gcc/lto-streamer-in.c +++ b/gcc/lto-streamer-in.c @@ -1128,7 +1128,10 @@ input_function (tree fn_decl, struct data_in *data_in, Similarly remove all IFN_*SAN_* internal calls */ if (!flag_wpa) { - if (!MAY_HAVE_DEBUG_STMTS && is_gimple_debug (stmt)) + if (is_gimple_debug (stmt) + && (gimple_debug_nonbind_marker_p (stmt) + ? !MAY_HAVE_DEBUG_MARKER_STMTS + : !MAY_HAVE_DEBUG_BIND_STMTS)) remove = true; if (is_gimple_call (stmt) && gimple_call_internal_p (stmt)) @@ -1182,6 +1185,13 @@ input_function (tree fn_decl, struct data_in *data_in, { gsi_next (&bsi); stmts[gimple_uid (stmt)] = stmt; + + /* Remember that the input function has begin stmt + markers, so that we know to expect them when emitting + debug info. */ + if (!cfun->debug_nonbind_markers + && gimple_debug_nonbind_marker_p (stmt)) + cfun->debug_nonbind_markers = true; } } } diff --git a/gcc/params.def b/gcc/params.def index 923ebc8..9d7f34f 100644 --- a/gcc/params.def +++ b/gcc/params.def @@ -976,6 +976,15 @@ DEFPARAM (PARAM_MAX_VARTRACK_REVERSE_OP_SIZE, "Max. size of loc list for which reverse ops should be added.", 50, 0, 0) +/* Set a threshold to discard debug markers (e.g. debug begin stmt + markers) when expanding a function to RTL, or inlining it into + another function. */ + +DEFPARAM (PARAM_MAX_DEBUG_MARKER_COUNT, + "max-debug-marker-count", + "Max. count of debug markers to expand or inline.", + 100000, 0, 0) + /* Set minimum insn uid for non-debug insns. */ DEFPARAM (PARAM_MIN_NONDEBUG_INSN_UID, diff --git a/gcc/print-rtl.c b/gcc/print-rtl.c index 5fe2380..0af739d 100644 --- a/gcc/print-rtl.c +++ b/gcc/print-rtl.c @@ -258,6 +258,16 @@ rtx_writer::print_rtx_operand_code_0 (const_rtx in_rtx ATTRIBUTE_UNUSED, fputc ('\t', m_outfile); break; + case NOTE_INSN_BEGIN_STMT: +#ifndef GENERATOR_FILE + { + expanded_location xloc + = expand_location (NOTE_MARKER_LOCATION (in_rtx)); + fprintf (m_outfile, " %s:%i", xloc.file, xloc.line); + } +#endif + break; + default: break; } @@ -1808,6 +1818,20 @@ print_insn (pretty_printer *pp, const rtx_insn *x, int verbose) case DEBUG_INSN: { + if (DEBUG_MARKER_INSN_P (x)) + { + switch (INSN_DEBUG_MARKER_KIND (x)) + { + case NOTE_INSN_BEGIN_STMT: + pp_string (pp, "debug begin stmt marker"); + break; + + default: + gcc_unreachable (); + } + break; + } + const char *name = "?"; char idbuf[32]; diff --git a/gcc/recog.c b/gcc/recog.c index cdcff8f..3da6e5b 100644 --- a/gcc/recog.c +++ b/gcc/recog.c @@ -2252,6 +2252,7 @@ extract_insn (rtx_insn *insn) case ADDR_VEC: case ADDR_DIFF_VEC: case VAR_LOCATION: + case DEBUG_MARKER: return; case SET: diff --git a/gcc/rtl.def b/gcc/rtl.def index ee7b7e1..0000808 100644 --- a/gcc/rtl.def +++ b/gcc/rtl.def @@ -766,6 +766,9 @@ DEF_RTL_EXPR(ENTRY_VALUE, "entry_value", "0", RTX_OBJ) been optimized away completely. */ DEF_RTL_EXPR(DEBUG_PARAMETER_REF, "debug_parameter_ref", "t", RTX_OBJ) +/* Used in marker DEBUG_INSNs to avoid being recognized as an insn. */ +DEF_RTL_EXPR(DEBUG_MARKER, "debug_marker", "", RTX_EXTRA) + /* All expressions from this point forward appear only in machine descriptions. */ #ifdef GENERATOR_FILE diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index e6e3f12..8604ba1 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -53,6 +53,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-ssa.h" #include "except.h" #include "debug.h" +#include "params.h" #include "value-prof.h" #include "cfgloop.h" #include "builtins.h" @@ -1355,7 +1356,9 @@ remap_gimple_stmt (gimple *stmt, copy_body_data *id) gimple_seq stmts = NULL; if (is_gimple_debug (stmt) - && !opt_for_fn (id->dst_fn, flag_var_tracking_assignments)) + && (gimple_debug_nonbind_marker_p (stmt) + ? !DECL_STRUCT_FUNCTION (id->dst_fn)->debug_nonbind_markers + : !opt_for_fn (id->dst_fn, flag_var_tracking_assignments))) return stmts; /* Begin by recognizing trees that we'll completely rewrite for the @@ -1638,6 +1641,20 @@ remap_gimple_stmt (gimple *stmt, copy_body_data *id) gimple_seq_add_stmt (&stmts, copy); return stmts; } + if (gimple_debug_nonbind_marker_p (stmt)) + { + /* If the inlined function has too many debug markers, + don't copy them. */ + if (id->src_cfun->debug_marker_count + > PARAM_VALUE (PARAM_MAX_DEBUG_MARKER_COUNT)) + return stmts; + + gdebug *copy = as_a <gdebug *> (gimple_copy (stmt)); + id->debug_stmts.safe_push (copy); + gimple_seq_add_stmt (&stmts, copy); + return stmts; + } + gcc_checking_assert (!is_gimple_debug (stmt)); /* Create a new deep copy of the statement. */ copy = gimple_copy (stmt); @@ -1733,7 +1750,8 @@ remap_gimple_stmt (gimple *stmt, copy_body_data *id) gimple_set_block (copy, *n); } - if (gimple_debug_bind_p (copy) || gimple_debug_source_bind_p (copy)) + if (gimple_debug_bind_p (copy) || gimple_debug_source_bind_p (copy) + || gimple_debug_nonbind_marker_p (copy)) { gimple_seq_add_stmt (&stmts, copy); return stmts; @@ -2555,6 +2573,8 @@ maybe_move_debug_stmts_to_successors (copy_body_data *id, basic_block new_bb) value = gimple_debug_source_bind_get_value (stmt); new_stmt = gimple_build_debug_source_bind (var, value, stmt); } + else if (gimple_debug_nonbind_marker_p (stmt)) + new_stmt = as_a <gdebug *> (gimple_copy (stmt)); else gcc_unreachable (); gsi_insert_before (&dsi, new_stmt, GSI_SAME_STMT); @@ -2825,6 +2845,9 @@ copy_debug_stmt (gdebug *stmt, copy_body_data *id) gimple_set_block (stmt, n ? *n : id->block); } + if (gimple_debug_nonbind_marker_p (stmt)) + return; + /* Remap all the operands in COPY. */ memset (&wi, 0, sizeof (wi)); wi.info = id; @@ -2833,8 +2856,10 @@ copy_debug_stmt (gdebug *stmt, copy_body_data *id) if (gimple_debug_source_bind_p (stmt)) t = gimple_debug_source_bind_get_var (stmt); - else + else if (gimple_debug_bind_p (stmt)) t = gimple_debug_bind_get_var (stmt); + else + gcc_unreachable (); if (TREE_CODE (t) == PARM_DECL && id->debug_map && (n = id->debug_map->get (t))) diff --git a/gcc/tree-iterator.c b/gcc/tree-iterator.c index c485413..10e510d 100644 --- a/gcc/tree-iterator.c +++ b/gcc/tree-iterator.c @@ -89,7 +89,7 @@ append_to_statement_list_1 (tree t, tree *list_p) void append_to_statement_list (tree t, tree *list_p) { - if (t && TREE_SIDE_EFFECTS (t)) + if (t && (TREE_SIDE_EFFECTS (t) || TREE_CODE (t) == DEBUG_BEGIN_STMT)) append_to_statement_list_1 (t, list_p); } @@ -137,7 +137,8 @@ tsi_link_before (tree_stmt_iterator *i, tree t, enum tsi_iterator_update mode) tail = head; } - TREE_SIDE_EFFECTS (i->container) = 1; + if (TREE_CODE (t) != DEBUG_BEGIN_STMT) + TREE_SIDE_EFFECTS (i->container) = 1; cur = i->ptr; @@ -213,7 +214,8 @@ tsi_link_after (tree_stmt_iterator *i, tree t, enum tsi_iterator_update mode) tail = head; } - TREE_SIDE_EFFECTS (i->container) = 1; + if (TREE_CODE (t) != DEBUG_BEGIN_STMT) + TREE_SIDE_EFFECTS (i->container) = 1; cur = i->ptr; @@ -279,8 +281,9 @@ tsi_delink (tree_stmt_iterator *i) i->ptr = next; } -/* Return the first expression in a sequence of COMPOUND_EXPRs, - or in a STATEMENT_LIST. */ +/* Return the first expression in a sequence of COMPOUND_EXPRs, or in + a STATEMENT_LIST, disregarding DEBUG_BEGIN_STMTs, recursing into a + STATEMENT_LIST if that's the first non-DEBUG_BEGIN_STMT. */ tree expr_first (tree expr) @@ -291,7 +294,20 @@ expr_first (tree expr) if (TREE_CODE (expr) == STATEMENT_LIST) { struct tree_statement_list_node *n = STATEMENT_LIST_HEAD (expr); - return n ? n->stmt : NULL_TREE; + if (!n) + return NULL_TREE; + while (TREE_CODE (n->stmt) == DEBUG_BEGIN_STMT) + { + n = n->next; + if (!n) + return NULL_TREE; + } + /* If the first non-debug stmt is not a statement list, we + already know it's what we're looking for. */ + if (TREE_CODE (n->stmt) != STATEMENT_LIST) + return n->stmt; + + return expr_first (n->stmt); } while (TREE_CODE (expr) == COMPOUND_EXPR) @@ -300,8 +316,9 @@ expr_first (tree expr) return expr; } -/* Return the last expression in a sequence of COMPOUND_EXPRs, - or in a STATEMENT_LIST. */ +/* Return the last expression in a sequence of COMPOUND_EXPRs, or in a + STATEMENT_LIST, disregarding DEBUG_BEGIN_STMTs, recursing into a + STATEMENT_LIST if that's the last non-DEBUG_BEGIN_STMT. */ tree expr_last (tree expr) @@ -312,7 +329,20 @@ expr_last (tree expr) if (TREE_CODE (expr) == STATEMENT_LIST) { struct tree_statement_list_node *n = STATEMENT_LIST_TAIL (expr); - return n ? n->stmt : NULL_TREE; + if (!n) + return NULL_TREE; + while (TREE_CODE (n->stmt) == DEBUG_BEGIN_STMT) + { + n = n->prev; + if (!n) + return NULL_TREE; + } + /* If the last non-debug stmt is not a statement list, we + already know it's what we're looking for. */ + if (TREE_CODE (n->stmt) != STATEMENT_LIST) + return n->stmt; + + return expr_last (n->stmt); } while (TREE_CODE (expr) == COMPOUND_EXPR) diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c index bf872b8..6519f3e 100644 --- a/gcc/tree-pretty-print.c +++ b/gcc/tree-pretty-print.c @@ -3230,6 +3230,10 @@ dump_generic_node (pretty_printer *pp, tree node, int spc, dump_flags_t flags, dump_block_node (pp, node, spc, flags); break; + case DEBUG_BEGIN_STMT: + pp_string (pp, "# DEBUG BEGIN STMT"); + break; + default: NIY; } diff --git a/gcc/tree-ssa-threadedge.c b/gcc/tree-ssa-threadedge.c index 70675e4..91793bf 100644 --- a/gcc/tree-ssa-threadedge.c +++ b/gcc/tree-ssa-threadedge.c @@ -712,6 +712,8 @@ propagate_threaded_block_debug_into (basic_block dest, basic_block src) gimple *stmt = gsi_stmt (si); if (!is_gimple_debug (stmt)) break; + if (gimple_debug_nonbind_marker_p (stmt)) + continue; i++; } @@ -739,6 +741,8 @@ propagate_threaded_block_debug_into (basic_block dest, basic_block src) var = gimple_debug_bind_get_var (stmt); else if (gimple_debug_source_bind_p (stmt)) var = gimple_debug_source_bind_get_var (stmt); + else if (gimple_debug_nonbind_marker_p (stmt)) + continue; else gcc_unreachable (); @@ -766,17 +770,23 @@ propagate_threaded_block_debug_into (basic_block dest, basic_block src) var = gimple_debug_bind_get_var (stmt); else if (gimple_debug_source_bind_p (stmt)) var = gimple_debug_source_bind_get_var (stmt); + else if (gimple_debug_nonbind_marker_p (stmt)) + continue; else gcc_unreachable (); - /* Discard debug bind overlaps. ??? Unlike stmts from src, + /* Discard debug bind overlaps. Unlike stmts from src, copied into a new block that will precede BB, debug bind stmts in bypassed BBs may actually be discarded if - they're overwritten by subsequent debug bind stmts, which - might be a problem once we introduce stmt frontier notes - or somesuch. Adding `&& bb == src' to the condition - below will preserve all potentially relevant debug - notes. */ + they're overwritten by subsequent debug bind stmts. We + want to copy binds for all modified variables, so that we + retain a bind to the shared def if there is one, or to a + newly introduced PHI node if there is one. Our bind will + end up reset if the value is dead, but that implies the + variable couldn't have survived, so it's fine. We are + not actually running the code that performed the binds at + this point, we're just adding binds so that they survive + the new confluence, so markers should not be copied. */ if (vars && vars->add (var)) continue; else if (!vars) @@ -787,8 +797,7 @@ propagate_threaded_block_debug_into (basic_block dest, basic_block src) break; if (i >= 0) continue; - - if (fewvars.length () < (unsigned) alloc_count) + else if (fewvars.length () < (unsigned) alloc_count) fewvars.quick_push (var); else { @@ -1040,7 +1040,8 @@ make_node (enum tree_code code MEM_STAT_DECL) switch (type) { case tcc_statement: - TREE_SIDE_EFFECTS (t) = 1; + if (code != DEBUG_BEGIN_STMT) + TREE_SIDE_EFFECTS (t) = 1; break; case tcc_declaration: @@ -4397,7 +4398,10 @@ build1 (enum tree_code code, tree type, tree node MEM_STAT_DECL) } if (TREE_CODE_CLASS (code) == tcc_statement) - TREE_SIDE_EFFECTS (t) = 1; + { + if (code != DEBUG_BEGIN_STMT) + TREE_SIDE_EFFECTS (t) = 1; + } else switch (code) { case VA_ARG_EXPR: diff --git a/gcc/tree.def b/gcc/tree.def index edcb7ef..137e63f 100644 --- a/gcc/tree.def +++ b/gcc/tree.def @@ -382,6 +382,9 @@ DEFTREECODE (RESULT_DECL, "result_decl", tcc_declaration, 0) DEBUG stmts. */ DEFTREECODE (DEBUG_EXPR_DECL, "debug_expr_decl", tcc_declaration, 0) +/* A stmt that marks the beginning of a source statement. */ +DEFTREECODE (DEBUG_BEGIN_STMT, "debug_begin_stmt", tcc_statement, 0) + /* A namespace declaration. Namespaces appear in DECL_CONTEXT of other _DECLs, providing a hierarchy of names. */ DEFTREECODE (NAMESPACE_DECL, "namespace_decl", tcc_declaration, 0) @@ -1223,7 +1223,7 @@ extern void protected_set_expr_location (tree, location_t); /* GOTO_EXPR accessor. This gives access to the label associated with a goto statement. */ -#define GOTO_DESTINATION(NODE) TREE_OPERAND ((NODE), 0) +#define GOTO_DESTINATION(NODE) TREE_OPERAND (GOTO_EXPR_CHECK (NODE), 0) /* ASM_EXPR accessors. ASM_STRING returns a STRING_CST for the instruction (e.g., "mov x, y"). ASM_OUTPUTS, ASM_INPUTS, and diff --git a/gcc/var-tracking.c b/gcc/var-tracking.c index b324a2c..8e500b1 100644 --- a/gcc/var-tracking.c +++ b/gcc/var-tracking.c @@ -9926,6 +9926,36 @@ vt_init_cfa_base (void) cselib_preserve_cfa_base_value (val, REGNO (cfa_base_rtx)); } +/* Reemit INSN, a MARKER_DEBUG_INSN, as a note. */ + +static rtx_insn * +reemit_marker_as_note (rtx_insn *insn, basic_block *bb) +{ + gcc_checking_assert (DEBUG_MARKER_INSN_P (insn)); + + enum insn_note kind = INSN_DEBUG_MARKER_KIND (insn); + + switch (kind) + { + case NOTE_INSN_BEGIN_STMT: + { + rtx_insn *note = NULL; + if (cfun->debug_nonbind_markers) + { + note = emit_note_before (kind, insn); + NOTE_MARKER_LOCATION (note) = INSN_LOCATION (insn); + if (bb) + BLOCK_FOR_INSN (note) = *bb; + } + delete_insn (insn); + return note; + } + + default: + gcc_unreachable (); + } +} + /* Allocate and initialize the data structures for variable tracking and parse the RTL to get the micro operations. */ @@ -10169,6 +10199,12 @@ vt_initialize (void) cselib_hook_called = false; adjust_insn (bb, insn); + if (DEBUG_MARKER_INSN_P (insn)) + { + insn = reemit_marker_as_note (insn, &save_bb); + continue; + } + if (MAY_HAVE_DEBUG_BIND_INSNS) { if (CALL_P (insn)) @@ -10245,10 +10281,11 @@ vt_initialize (void) static int debug_label_num = 1; -/* Get rid of all debug insns from the insn stream. */ +/* Remove from the insn stream all debug insns used for variable + tracking at assignments. */ static void -delete_debug_insns (void) +delete_vta_debug_insns (void) { basic_block bb; rtx_insn *insn, *next; @@ -10264,6 +10301,12 @@ delete_debug_insns (void) insn = next) if (DEBUG_INSN_P (insn)) { + if (DEBUG_MARKER_INSN_P (insn)) + { + insn = reemit_marker_as_note (insn, NULL); + continue; + } + tree decl = INSN_VAR_LOCATION_DECL (insn); if (TREE_CODE (decl) == LABEL_DECL && DECL_NAME (decl) @@ -10289,10 +10332,13 @@ delete_debug_insns (void) handled as well.. */ static void -vt_debug_insns_local (bool skipped ATTRIBUTE_UNUSED) +vt_debug_insns_local (bool skipped) { - /* ??? Just skip it all for now. */ - delete_debug_insns (); + /* ??? Just skip it all for now. If we skipped the global pass, + arrange for stmt markers to be dropped as well. */ + if (skipped) + cfun->debug_nonbind_markers = 0; + delete_vta_debug_insns (); } /* Free the data structures needed for variable tracking. */ @@ -10357,15 +10403,21 @@ variable_tracking_main_1 (void) { bool success; - if (flag_var_tracking_assignments < 0 + /* We won't be called as a separate pass if flag_var_tracking is not + set, but final may call us to turn debug markers into notes. */ + if ((!flag_var_tracking && MAY_HAVE_DEBUG_INSNS) + || flag_var_tracking_assignments < 0 /* Var-tracking right now assumes the IR doesn't contain any pseudos at this point. */ || targetm.no_register_allocation) { - delete_debug_insns (); + delete_vta_debug_insns (); return 0; } + if (!flag_var_tracking) + return 0; + if (n_basic_blocks_for_fn (cfun) > 500 && n_edges_for_fn (cfun) / n_basic_blocks_for_fn (cfun) >= 20) { @@ -10387,7 +10439,9 @@ variable_tracking_main_1 (void) { vt_finalize (); - delete_debug_insns (); + cfun->debug_nonbind_markers = 0; + + delete_vta_debug_insns (); /* This is later restored by our caller. */ flag_var_tracking_assignments = 0; |