diff mbox series

[4/9,SFN] introduce statement frontier notes, still disabled

Message ID 20170930090827.6604-4-aoliva@redhat.com
State New
Headers show
Series [1/9,SFN] adjust RTL insn-walking API | expand

Commit Message

Alexandre Oliva Sept. 30, 2017, 9:08 a.m. UTC
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.

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(-)

Comments

Richard Biener Oct. 9, 2017, 1:11 p.m. UTC | #1
On Sat, Sep 30, 2017 at 11:08 AM, Alexandre Oliva <aoliva@redhat.com> 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
>
Alexandre Oliva Oct. 13, 2017, 7:21 a.m. UTC | #2
On Oct  9, 2017, Richard Biener <richard.guenther@gmail.com> wrote:

> 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.

https://gcc.gnu.org/ml/gcc-patches/2017-09/msg02026.html


Thanks a lot for the reviews!

I've held off installing the approved patches and parts of this patch
for the time being, awaiting the second look you requested (copying the
C++ maintainers to get their attention), and also reviews for the
subsequent patches (2 LVU and 2 IEPM).  Please let me know if you'd
prefer me to go ahead and install the approved bits.
Richard Biener Oct. 13, 2017, 9:38 a.m. UTC | #3
On Fri, Oct 13, 2017 at 9:21 AM, Alexandre Oliva <aoliva@redhat.com> wrote:
> On Oct  9, 2017, Richard Biener <richard.guenther@gmail.com> wrote:
>
>> 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.
>
> https://gcc.gnu.org/ml/gcc-patches/2017-09/msg02026.html
>
>
> Thanks a lot for the reviews!
>
> I've held off installing the approved patches and parts of this patch
> for the time being, awaiting the second look you requested (copying the
> C++ maintainers to get their attention), and also reviews for the
> subsequent patches (2 LVU and 2 IEPM).  Please let me know if you'd
> prefer me to go ahead and install the approved bits.

If the [SFN] is self-contained you can install that part once the approval
for the FE parts is in.  You can of course wait a bit for more reviews
(stopped short on LVU because of that all-targets touching patch ... ;))

Richard.

> --
> Alexandre Oliva, freedom fighter    http://FSFLA.org/~lxoliva/
> You must be the change you wish to see in the world. -- Gandhi
> Be Free! -- http://FSFLA.org/   FSF Latin America board member
> Free Software Evangelist|Red Hat Brasil GNU Toolchain Engineer
Alexandre Oliva Oct. 17, 2017, 8:51 p.m. UTC | #4
On Oct 13, 2017, Richard Biener <richard.guenther@gmail.com> wrote:

> If the [SFN] is self-contained you can install that part once the approval
> for the FE parts is in.

It is, so I'll do that.

> You can of course wait a bit for more reviews
> (stopped short on LVU because of that all-targets touching patch ... ;))

:-)

I could minimize the amount of visible changes in target code by using
'&' rather than '*' to make the parameter changeable.  This enables
final_start_function to consume the initial debug insns, so that
e.g. parm bindings are integrated in the initial view.

The per-target changes could also be avoided entirely, by having both
final_start_function and final skip initial debug insns, but I think
that is wasteful, cpu-wise, and error prone in the long term,
maintenance-wise.

Just throwing some options out, in case touching the code of so many
targets turns out to be a blocker...
Jason Merrill Oct. 24, 2017, 6:07 p.m. UTC | #5
On Sat, Sep 30, 2017 at 5:08 AM, Alexandre Oliva <aoliva@redhat.com> wrote:
> 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?  */

There's no need; the result of this function isn't used for codegen
any more, just checking.

Why do you need a special case for this?  The existing code should
have the same effect.

> @@ -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?  */

Likewise.

> @@ -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;

Maybe instead of handling this here, add it to the sequence of codes
that are returned unchanged in cxx_eval_constant_expression, after
PREDICT_EXPR?

Jason
Alexandre Oliva Nov. 1, 2017, 7:13 p.m. UTC | #6
Hi, Jason,

Apologies for the delay in responding; somehow I missed your reply when
it arrived (probably because I had a cold back then :-), and only saw it
today.

On Oct 24, 2017, Jason Merrill <jason@redhat.com> wrote:

> On Sat, Sep 30, 2017 at 5:08 AM, Alexandre Oliva <aoliva@redhat.com> wrote:
>> 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?  */

> There's no need; the result of this function isn't used for codegen
> any more, just checking.

> Why do you need a special case for this?  The existing code should
> have the same effect.

More than anything, it was to add the comment about retaining the begin
stmt markers.

Something in me wishes that, in spite of resolving the entire function
call to a constant expression, we could still express in debug info that
"we went through these statements", and maybe even single-step through
them towards the final result.

But if we all agree we don't want to do that, I'll be glad to drop the
???/FIXME markers to that effect.

>> @@ -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?  */

> Likewise.

>> @@ -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;

> Maybe instead of handling this here, add it to the sequence of codes
> that are returned unchanged in cxx_eval_constant_expression, after
> PREDICT_EXPR?

That would likely work, but there's a slight risk of codegen changes
then, if a debug begin stmt happens to be present (for some reason I
can't imagine) after the statement expression whose result we're
supposed to use and return: we'd then overwrite the expression value we
should use with the debug begin stmt.  Defensively, it seemed best to
avoid that risk, even though in theory it shouldn't ever occur.

There's another case that's more material, that also involves a debug
begin stmt at the end of a statement list: that of an empty statement
list.  It's not expected that cxx_eval_statement_list returns a debug
begin stmt marker, but that's what it would do with the proposed change.
Then we'd have to handle that up the call chain.  Maybe it doesn't
matter, maybe it's no big deal, but by just skipping it there we can
just not worry about it elsewhere.
Jason Merrill Nov. 1, 2017, 7:49 p.m. UTC | #7
On Wed, Nov 1, 2017 at 3:13 PM, Alexandre Oliva <aoliva@redhat.com> wrote:
> Hi, Jason,
>
> Apologies for the delay in responding; somehow I missed your reply when
> it arrived (probably because I had a cold back then :-), and only saw it
> today.
>
> On Oct 24, 2017, Jason Merrill <jason@redhat.com> wrote:
>
>> On Sat, Sep 30, 2017 at 5:08 AM, Alexandre Oliva <aoliva@redhat.com> wrote:
>>> 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?  */
>
>> There's no need; the result of this function isn't used for codegen
>> any more, just checking.
>
>> Why do you need a special case for this?  The existing code should
>> have the same effect.
>
> More than anything, it was to add the comment about retaining the begin
> stmt markers.
>
> Something in me wishes that, in spite of resolving the entire function
> call to a constant expression, we could still express in debug info that
> "we went through these statements", and maybe even single-step through
> them towards the final result.

Sure, but my point is that this function is not involved in evaluating
the call, so there would be no reason for a change here.  Most likely
you'd want something in cxx_eval_store_expression.

>>> @@ -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?  */
>
>> Likewise.

>>> @@ -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;
>
>> Maybe instead of handling this here, add it to the sequence of codes
>> that are returned unchanged in cxx_eval_constant_expression, after
>> PREDICT_EXPR?
>
> That would likely work, but there's a slight risk of codegen changes
> then, if a debug begin stmt happens to be present (for some reason I
> can't imagine) after the statement expression whose result we're
> supposed to use and return: we'd then overwrite the expression value we
> should use with the debug begin stmt.  Defensively, it seemed best to
> avoid that risk, even though in theory it shouldn't ever occur.

> There's another case that's more material, that also involves a debug
> begin stmt at the end of a statement list: that of an empty statement
> list.  It's not expected that cxx_eval_statement_list returns a debug
> begin stmt marker, but that's what it would do with the proposed change.
> Then we'd have to handle that up the call chain.  Maybe it doesn't
> matter, maybe it's no big deal, but by just skipping it there we can
> just not worry about it elsewhere.

I'm skeptical about adding defensive special cases everywhere to deal
with seemingly impossible situations.  If I'm not sure something's
actually impossible, I prefer to add an assert so things break more
loudly.

Jason
diff mbox series

Patch

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;