On Sat, Sep 30, 2017 at 11:08 AM, Alexandre Oliva <[email protected]> wrote:
> This patch completes the infrastructure for the introduction of
> statement frontiers in C-family languages.
>
> It brings in all the code remaining code needed to introduce and
> transform begin stmt trees, gimple stmts, insns and notes, and
> ultimately use them to generate the is_stmt column in DWARF2+ line
> number tables/programs, however none of it is activated: the option
> that would do so will be introduced in a subsequent patch.
>
> This patch depends on an earlier patch with not-quite-boilerplate
> changes towards SFN.
The middle-end changes are ok. The C FE change looks reasonable,
I'd appreciate a 2nd look at the C++ FE changes by a maintainer.
Thanks,
Richard.
> for gcc/c-family/ChangeLog
>
> * c-semantics.c (pop_stmt_list): Move begin stmt marker into
> subsequent statement list.
>
> for gcc/c/ChangeLog
>
> * 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.
>
> for gcc/cp/ChangeLog
>
> * constexpr.c (build_data_member_initialization): Skip begin stmt
> markers.
> (check_constexpr_ctor_body_1): Likewise.
> (build_constexpr_constructor_member_initializers): Likewise.
> (constexpr_fn_retval): Likewise.
> (cxx_eval_statement_list): Likewise.
> (potential_constant_expression_1): Likewise.
> * 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.
>
> for gcc/ChangeLog
>
> * 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.
> ---
> gcc/c-family/c-semantics.c | 21 ++++++
> gcc/c/c-objc-common.h | 2 +
> gcc/c/c-parser.c | 20 ++++++
> gcc/c/c-typeck.c | 8 ++-
> gcc/cfgexpand.c | 113 +++++++++++++++++---------------
> gcc/cp/constexpr.c | 11 ++++
> gcc/cp/cp-array-notation.c | 37 +++++++++--
> gcc/cp/cp-objcp-common.h | 2 +
> gcc/cp/parser.c | 14 ++++
> gcc/cp/pt.c | 6 ++
> gcc/cse.c | 7 ++
> gcc/df-scan.c | 2 +-
> gcc/doc/generic.texi | 5 ++
> gcc/doc/gimple.texi | 24 ++++++-
> gcc/doc/invoke.texi | 7 ++
> gcc/doc/rtl.texi | 53 ++++++++++++---
> gcc/final.c | 89 +++++++++++++++++++------
> gcc/function.c | 6 ++
> gcc/function.h | 10 +++
> gcc/gimple-iterator.c | 4 ++
> gcc/gimple-low.c | 29 +++++++++
> gcc/gimple-pretty-print.c | 7 ++
> gcc/gimple.c | 24 +++++++
> gcc/gimple.h | 1 +
> gcc/gimplify.c | 158
> +++++++++++++++++++++++++++++++++++----------
> gcc/langhooks-def.h | 2 +
> gcc/langhooks.h | 3 +
> gcc/lra-constraints.c | 10 ++-
> gcc/lra.c | 36 +++++++++--
> gcc/lto-streamer-in.c | 12 +++-
> gcc/params.def | 9 +++
> gcc/print-rtl.c | 24 +++++++
> gcc/recog.c | 1 +
> gcc/rtl.def | 3 +
> gcc/tree-inline.c | 31 ++++++++-
> gcc/tree-iterator.c | 48 +++++++++++---
> gcc/tree-pretty-print.c | 4 ++
> gcc/tree-ssa-threadedge.c | 25 ++++---
> gcc/tree.c | 8 ++-
> gcc/tree.def | 3 +
> gcc/tree.h | 2 +-
> gcc/var-tracking.c | 70 +++++++++++++++++---
> 42 files changed, 793 insertions(+), 158 deletions(-)
>
> 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/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 a36397b..aa70c91 100644
> --- a/gcc/c/c-parser.c
> +++ b/gcc/c/c-parser.c
> @@ -1640,6 +1640,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
> @@ -1740,6 +1753,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))
> {
> @@ -4949,6 +4964,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;
> }
> @@ -5403,6 +5419,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 73e7460..33bd4b8 100644
> --- a/gcc/c/c-typeck.c
> +++ b/gcc/c/c-typeck.c
> @@ -10740,6 +10740,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;
> }
> @@ -10759,7 +10763,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 5a46b5e..c854ffd 100644
> --- a/gcc/cfgexpand.c
> +++ b/gcc/cfgexpand.c
> @@ -5635,39 +5635,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 (VOIDmode);
> 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);
>
> @@ -5675,9 +5704,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:
> @@ -5693,42 +5727,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);
> @@ -6367,6 +6371,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/constexpr.c b/gcc/cp/constexpr.c
> index a89ee49..f9209ac 100644
> --- a/gcc/cp/constexpr.c
> +++ b/gcc/cp/constexpr.c
> @@ -306,6 +306,9 @@ build_data_member_initialization (tree t,
> vec<constructor_elt, va_gc> **vec)
> tree_stmt_iterator i;
> for (i = tsi_start (t); !tsi_end_p (i); tsi_next (&i))
> {
> + if (TREE_CODE (tsi_stmt (i)) == DEBUG_BEGIN_STMT)
> + /* ??? Can we retain this information somehow? */
> + continue;
> if (! build_data_member_initialization (tsi_stmt (i), vec))
> return false;
> }
> @@ -448,6 +451,7 @@ check_constexpr_ctor_body_1 (tree last, tree list)
>
> case USING_STMT:
> case STATIC_ASSERT:
> + case DEBUG_BEGIN_STMT:
> return true;
>
> default:
> @@ -586,6 +590,9 @@ build_constexpr_constructor_member_initializers (tree
> type, tree body)
> tree_stmt_iterator i;
> for (i = tsi_start (body); !tsi_end_p (i); tsi_next (&i))
> {
> + if (TREE_CODE (tsi_stmt (i)) == DEBUG_BEGIN_STMT)
> + /* ??? Can we retain this information somehow? */
> + continue;
> ok = build_data_member_initialization (tsi_stmt (i), &vec);
> if (!ok)
> break;
> @@ -673,6 +680,7 @@ constexpr_fn_retval (tree body)
> return constexpr_fn_retval (BIND_EXPR_BODY (body));
>
> case USING_STMT:
> + case DEBUG_BEGIN_STMT:
> return NULL_TREE;
>
> default:
> @@ -3783,6 +3791,8 @@ cxx_eval_statement_list (const constexpr_ctx *ctx, tree
> t,
> for (i = tsi_start (t); !tsi_end_p (i); tsi_next (&i))
> {
> tree stmt = tsi_stmt (i);
> + if (TREE_CODE (stmt) == DEBUG_BEGIN_STMT)
> + continue;
> r = cxx_eval_constant_expression (ctx, stmt, false,
> non_constant_p, overflow_p,
> jump_target);
> @@ -5119,6 +5129,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-array-notation.c b/gcc/cp/cp-array-notation.c
> index 31be7d6..17f0b35c 100644
> --- a/gcc/cp/cp-array-notation.c
> +++ b/gcc/cp/cp-array-notation.c
> @@ -780,6 +780,31 @@ error:
> return error_mark_node;
> }
>
> +/* Return a location associated with stmt. If it is an expresion,
> + that's the expression's location. If it is a STATEMENT_LIST,
> + instead of no location, use expr_first to skip any debug stmts and
> + take the location of the first nondebug stmt found. */
> +
> +static location_t
> +stmt_location (tree stmt)
> +{
> + location_t loc = UNKNOWN_LOCATION;
> +
> + if (!stmt)
> + return loc;
> +
> + loc = EXPR_LOCATION (stmt);
> +
> + if (loc != UNKNOWN_LOCATION || TREE_CODE (stmt) != STATEMENT_LIST)
> + return loc;
> +
> + stmt = expr_first (stmt);
> + if (stmt)
> + loc = EXPR_LOCATION (stmt);
> +
> + return loc;
> +}
> +
> /* Helper function for expand_conditonal_array_notations. Encloses the
> conditional statement passed in ORIG_STMT with a loop around it and
> replaces the condition in STMT with a ARRAY_REF tree-node to the array.
> @@ -835,10 +860,12 @@ cp_expand_cond_array_notations (tree orig_stmt)
> tree cond = IF_COND (orig_stmt);
> if (!find_rank (EXPR_LOCATION (cond), cond, cond, true, &cond_rank)
> || (yes_expr
> - && !find_rank (EXPR_LOCATION (yes_expr), yes_expr, yes_expr,
> true,
> + && !find_rank (stmt_location (yes_expr),
> + yes_expr, yes_expr, true,
> &yes_rank))
> || (no_expr
> - && !find_rank (EXPR_LOCATION (no_expr), no_expr, no_expr, true,
> + && !find_rank (stmt_location (no_expr),
> + no_expr, no_expr, true,
> &no_rank)))
> return error_mark_node;
>
> @@ -847,13 +874,15 @@ cp_expand_cond_array_notations (tree orig_stmt)
> return orig_stmt;
> else if (cond_rank != yes_rank && yes_rank != 0)
> {
> - error_at (EXPR_LOCATION (yes_expr), "rank mismatch with controlling"
> + error_at (stmt_location (yes_expr),
> + "rank mismatch with controlling"
> " expression of parent if-statement");
> return error_mark_node;
> }
> else if (cond_rank != no_rank && no_rank != 0)
> {
> - error_at (EXPR_LOCATION (no_expr), "rank mismatch with controlling "
> + error_at (stmt_location (no_expr),
> + "rank mismatch with controlling "
> "expression of parent if-statement");
> return error_mark_node;
> }
> diff --git a/gcc/cp/cp-objcp-common.h b/gcc/cp/cp-objcp-common.h
> index 3e4cc9c5..c1c82b6 100644
> --- a/gcc/cp/cp-objcp-common.h
> +++ b/gcc/cp/cp-objcp-common.h
> @@ -105,6 +105,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 6e817cb..1b31182 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -10661,6 +10661,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:
> @@ -10736,6 +10749,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 c29c779..4714b53 100644
> --- a/gcc/cp/pt.c
> +++ b/gcc/cp/pt.c
> @@ -15289,6 +15289,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)
> diff --git a/gcc/cse.c b/gcc/cse.c
> index d1577a6..bb9b317 100644
> --- a/gcc/cse.c
> +++ b/gcc/cse.c
> @@ -6967,11 +6967,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 a51cfd6e..a13bce9 100644
> --- a/gcc/doc/generic.texi
> +++ b/gcc/doc/generic.texi
> @@ -1930,6 +1930,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 635abd39..6c9c4789 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
> @@ -1528,10 +1538,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
> @@ -1602,6 +1613,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 f862b7f..108d730 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -10480,6 +10480,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 3b2b247..888ab02 100644
> --- a/gcc/doc/rtl.texi
> +++ b/gcc/doc/rtl.texi
> @@ -3413,6 +3413,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
> @@ -3689,6 +3708,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.
> @@ -3706,15 +3731,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 eff2ee6..49cfbfb 100644
> --- a/gcc/final.c
> +++ b/gcc/final.c
> @@ -1646,7 +1646,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))
> @@ -1654,17 +1653,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;
> @@ -1685,6 +1696,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)
> @@ -1701,7 +1713,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);
>
> @@ -2404,6 +2416,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;
> @@ -2490,7 +2513,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;
> @@ -2593,7 +2624,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);
> @@ -3086,7 +3117,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;
> @@ -3119,7 +3165,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;
> @@ -3131,7 +3178,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;
> }
>
> @@ -4483,6 +4531,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);
> @@ -4670,6 +4722,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 ae61d3d..468dc9a 100644
> --- a/gcc/function.c
> +++ b/gcc/function.c
> @@ -4933,6 +4933,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 76434cd..1186116 100644
> --- a/gcc/function.h
> +++ b/gcc/function.h
> @@ -284,6 +284,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;
>
> @@ -387,6 +393,10 @@ struct GTY(()) function {
>
> /* Set when the tail call has been identified. */
> unsigned int tail_call_marked : 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 fb75f99..2359760 100644
> --- a/gcc/gimple-iterator.c
> +++ b/gcc/gimple-iterator.c
> @@ -573,6 +573,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 ed8e51c..2702854 100644
> --- a/gcc/gimple-pretty-print.c
> +++ b/gcc/gimple-pretty-print.c
> @@ -1370,6 +1370,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 c4e6f81..dc9aa79 100644
> --- a/gcc/gimple.c
> +++ b/gcc/gimple.c
> @@ -836,6 +836,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.
> @@ -1874,6 +1895,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 8f289ac..68cd34f 100644
> --- a/gcc/gimple.h
> +++ b/gcc/gimple.h
> @@ -1454,6 +1454,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 e9dffc3..6c80a81 100644
> --- a/gcc/gimplify.c
> +++ b/gcc/gimplify.c
> @@ -982,6 +982,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
> @@ -1772,6 +1814,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))
> {
> @@ -3441,7 +3490,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);
> @@ -3464,7 +3513,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);
> @@ -3486,7 +3535,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),
> @@ -3510,6 +3559,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. */
> @@ -3540,8 +3628,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);
> @@ -3562,8 +3650,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);
> @@ -3590,20 +3678,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;
> }
> @@ -3667,8 +3751,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)
> @@ -3961,39 +4045,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);
> @@ -11789,6 +11869,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 61b081b..a3f02b2 100644
> --- a/gcc/langhooks-def.h
> +++ b/gcc/langhooks-def.h
> @@ -130,6 +130,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
>
> @@ -343,6 +344,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 b0c9829..3493ff3 100644
> --- a/gcc/langhooks.h
> +++ b/gcc/langhooks.h
> @@ -528,6 +528,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 4734c072..47527b0 100644
> --- a/gcc/lra-constraints.c
> +++ b/gcc/lra-constraints.c
> @@ -5253,10 +5253,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));
> }
> }
> @@ -5796,6 +5797,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)
> diff --git a/gcc/lra.c b/gcc/lra.c
> index 9037495..ac99d5e 100644
> --- a/gcc/lra.c
> +++ b/gcc/lra.c
> @@ -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
> @@ -949,12 +965,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)
> @@ -1600,7 +1624,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);
> uid = INSN_UID (insn);
> for (i = static_data->n_operands - 1; i >= 0; i--)
> diff --git a/gcc/lto-streamer-in.c b/gcc/lto-streamer-in.c
> index 51d9a7b..37fa14e 100644
> --- a/gcc/lto-streamer-in.c
> +++ b/gcc/lto-streamer-in.c
> @@ -1130,7 +1130,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))
> @@ -1184,6 +1187,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 136f927..9f63e63 100644
> --- a/gcc/params.def
> +++ b/gcc/params.def
> @@ -963,6 +963,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 79ec463..0d36a42 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;
> }
> @@ -1791,6 +1801,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 = "?";
>
> if (DECL_P (INSN_VAR_LOCATION_DECL (x)))
> diff --git a/gcc/recog.c b/gcc/recog.c
> index cfce029..566425d 100644
> --- a/gcc/recog.c
> +++ b/gcc/recog.c
> @@ -2251,6 +2251,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 4c2607a..ccdaa82 100644
> --- a/gcc/rtl.def
> +++ b/gcc/rtl.def
> @@ -761,6 +761,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_OBJ)
> +
> /* 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 a142488..a99e975 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"
> @@ -1354,7 +1355,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
> @@ -1637,6 +1640,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);
> @@ -1732,7 +1749,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;
> @@ -2606,6 +2624,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);
> @@ -2922,6 +2942,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;
> @@ -2930,8 +2953,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 1fe3e63..22c6667 100644
> --- a/gcc/tree-pretty-print.c
> +++ b/gcc/tree-pretty-print.c
> @@ -3291,6 +3291,10 @@ dump_generic_node (pretty_printer *pp, tree node, int
> spc, dump_flags_t flags,
> pp_string (pp, "_Cilk_sync");
> 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
> {
> diff --git a/gcc/tree.c b/gcc/tree.c
> index e379940..30cadd7 100644
> --- a/gcc/tree.c
> +++ b/gcc/tree.c
> @@ -1015,7 +1015,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:
> @@ -4412,7 +4413,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 9f80c4d..e30e950 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)
> diff --git a/gcc/tree.h b/gcc/tree.h
> index 2e8b3e9..62a85ea 100644
> --- a/gcc/tree.h
> +++ b/gcc/tree.h
> @@ -1225,7 +1225,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 974b4ea..d3850af 100644
> --- a/gcc/var-tracking.c
> +++ b/gcc/var-tracking.c
> @@ -9919,6 +9919,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. */
>
> @@ -10162,6 +10192,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))
> @@ -10238,10 +10274,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;
> @@ -10257,6 +10294,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)
> @@ -10282,10 +10325,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. */
> @@ -10350,15 +10396,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)
> {
> @@ -10380,7 +10432,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;
> --
> 2.9.5
>