This patch makes the into-SSA code handle LSM temporary variables as though they had been assignments to the original variable.
In the end, the easiest place to record the link seemed to be DECL_ABSTRACT_ORIGIN. These sorts of nameless temporaries shouldn't otherwise have debug info and so shouldn't be using DECL_ABSTRACT_ORIGIN for anything else. 2019-06-01 Richard Sandiford <richard.sandif...@arm.com> gcc/ * gimple.h (gimple_assign_load_decl_p): New function. * tree-ssa.h (inherit_target_for_debug_bind): Declare. * tree-ssa.c (inherit_target_for_debug_bind): New function. (target_for_debug_bind): Honor calls to inherit_target_for_debug_bind. * tree-into-ssa.c (maybe_register_def): Don't emit "VAR => DEF" for "DEF = VAR". * tree-ssa-loop-im.c: Include tree-ssa.h. (execute_sm_if_changed): Explain why we don't insert any debug statements here. (execute_sm): Call inherit_target_for_debug_bind on the temporary variable. gcc/testsuite/ * lib/gcc-gdb-test.exp (gdb-test): Add a syntax for specifying the Nth hit of a breakpoint. * gcc.dg/guality/loop-2.c: New test. * gcc.dg/guality/loop-3.c: Likewise. * gcc.dg/guality/loop-4.c: Likewise. Index: gcc/gimple.h =================================================================== --- gcc/gimple.h 2019-06-01 16:52:38.000000000 +0100 +++ gcc/gimple.h 2019-06-01 16:52:39.075271587 +0100 @@ -2782,6 +2782,18 @@ gimple_assign_load_p (const gimple *gs) } +/* Return true if GS is an assignment that loads DECL into its lhs. */ + +static inline bool +gimple_assign_load_decl_p (const gimple *gs, tree decl) +{ + const gassign *assign = dyn_cast <const gassign *> (gs); + return (assign + && gimple_assign_single_p (assign) + && get_base_address (gimple_assign_rhs1 (assign)) == decl); +} + + /* Return true if S is a type-cast assignment. */ static inline bool Index: gcc/tree-ssa.h =================================================================== --- gcc/tree-ssa.h 2019-06-01 16:52:38.000000000 +0100 +++ gcc/tree-ssa.h 2019-06-01 16:52:39.079271578 +0100 @@ -39,6 +39,7 @@ extern void redirect_edge_var_map_empty extern edge ssa_redirect_edge (edge, basic_block); extern void flush_pending_stmts (edge); extern void gimple_replace_ssa_lhs (gimple *, tree); +extern void inherit_target_for_debug_bind (tree, tree); extern tree target_for_debug_bind (tree); extern void insert_debug_temp_for_var_def (gimple_stmt_iterator *, tree); extern void insert_debug_temps_for_defs (gimple_stmt_iterator *); Index: gcc/tree-ssa.c =================================================================== --- gcc/tree-ssa.c 2019-06-01 16:52:38.000000000 +0100 +++ gcc/tree-ssa.c 2019-06-01 16:52:39.079271578 +0100 @@ -232,6 +232,25 @@ gimple_replace_ssa_lhs (gimple *stmt, tr gimple_set_lhs (stmt, nlhs); } +/* Record that assignments to new temporary variable VAR should be treated + for debug purposes like an assignment to ORIGIN. More specifically, + record that the value of target_for_debug_bind (VAR) should track the + value of target_for_debug_bind (ORIGIN). + + This can be useful when replacing all references to ORIGIN with VAR + in a particular region of code. */ + +void +inherit_target_for_debug_bind (tree var, tree origin) +{ + gcc_assert (VAR_P (var) + && DECL_IGNORED_P (var) + && DECL_ARTIFICIAL (var) + && DECL_NAMELESS (var) + && !DECL_ABSTRACT_ORIGIN (var)); + if (target_for_debug_bind (origin)) + DECL_ABSTRACT_ORIGIN (var) = origin; +} /* Given a tree for an expression for which we might want to emit locations or values in debug information (generally a variable, but @@ -252,6 +271,12 @@ target_for_debug_bind (tree var) return NULL_TREE; } + /* Honor choices made through inherit_target_for_debug_bind. */ + if (VAR_P (var) + && DECL_IGNORED_P (var) + && !DECL_IGNORED_P (DECL_ORIGIN (var))) + var = DECL_ORIGIN (var); + if ((!VAR_P (var) || VAR_DECL_IS_VIRTUAL_OPERAND (var)) && TREE_CODE (var) != PARM_DECL) return NULL_TREE; Index: gcc/tree-into-ssa.c =================================================================== --- gcc/tree-into-ssa.c 2019-06-01 16:52:38.000000000 +0100 +++ gcc/tree-into-ssa.c 2019-06-01 16:52:39.079271578 +0100 @@ -1916,7 +1916,12 @@ maybe_register_def (def_operand_p def_p, SET_DEF (def_p, def); tree tracked_var = target_for_debug_bind (sym); - if (tracked_var) + /* Don't emit "VAR => DEF" for "DEF = VAR". Although not + semantically wrong, it leads to more resets when the load + from VAR is (or might become) partly speculative. */ + if (tracked_var + && !(track_direct_refs_for_debug_p (tracked_var) + && gimple_assign_load_decl_p (stmt, tracked_var))) { gimple *note = gimple_build_debug_bind (tracked_var, def, stmt); /* If stmt ends the bb, insert the debug stmt on the single Index: gcc/tree-ssa-loop-im.c =================================================================== --- gcc/tree-ssa-loop-im.c 2019-06-01 16:52:38.000000000 +0100 +++ gcc/tree-ssa-loop-im.c 2019-06-01 16:52:39.079271578 +0100 @@ -48,6 +48,7 @@ Free Software Foundation; either version #include "alias.h" #include "builtins.h" #include "tree-dfa.h" +#include "tree-ssa.h" /* TODO: Support for predicated code motion. I.e. @@ -1998,6 +1999,24 @@ execute_sm_if_changed (edge ex, tree mem orig_ex->aux = (void *) p; } + /* ??? As things stand, the value of MEM on entry to the join block + can be found at MEM's DECL_RTL. It would therefore be accurate + to emit: + + # DEBUG MEM s=> MEM + + on entry to the block. However, there's no guarantee that later + optimizations will keep things that way, and they can't reasonably + be expected to find and correct the statement. + + E.g. if the store in THEN_BB is later deleted as dead, the debug + statement above would reestablish the connection between MEM and + its DECL_RTL even though the two are no longer the same. + + We therefore leave things so that the debug location on entry + to the join block comes from MEM's DECL_RTL if the flag is set + and TMP_VAR otherwise. */ + if (!loop_has_only_one_exit) for (gphi_iterator gpi = gsi_start_phis (old_dest); !gsi_end_p (gpi); gsi_next (&gpi)) @@ -2082,6 +2101,7 @@ execute_sm (struct loop *loop, vec<edge> tmp_var = create_tmp_reg (TREE_TYPE (ref->mem.ref), get_lsm_tmp_name (ref->mem.ref, ~0)); + inherit_target_for_debug_bind (tmp_var, ref->mem.ref); fmt_data.loop = loop; fmt_data.orig_loop = loop; Index: gcc/testsuite/lib/gcc-gdb-test.exp =================================================================== --- gcc/testsuite/lib/gcc-gdb-test.exp 2019-06-01 16:52:38.000000000 +0100 +++ gcc/testsuite/lib/gcc-gdb-test.exp 2019-06-01 16:52:39.075271587 +0100 @@ -18,6 +18,7 @@ # Call pass if variable has the desired value, otherwise fail. # # Argument 0 is the line number on which to put a breakpoint +# It can be suffixed by "*N" to test the Nth hit of the breakpoint. # Argument 1 is the name of the variable to be checked # possibly prefixed with type: to get the type of the variable # instead of the value of the variable (the default). @@ -54,12 +55,18 @@ proc gdb-test { useline args } { set var $arg1 } - set line [lindex $args 0] + set line_pieces [split [lindex $args 0] "*"] + set line [lindex $line_pieces 0] if { [string range $line 0 0] == "@" } { set line [string range $line 1 end] } else { set line [get-absolute-line $useline $line] } + if { [llength $line_pieces] > 1 } { + set count [lindex $line_pieces 1] + } else { + set count 1 + } set gdb_name $::env(GUALITY_GDB_NAME) set testname "$testcase line $line [lindex $args 1] == [lindex $args 2]" @@ -69,6 +76,9 @@ proc gdb-test { useline args } { set fd [open $cmd_file "w"] puts $fd "break $line" puts $fd "run" + if { $count > 1 } { + puts $fd "continue [expr { $count - 1 }]" + } puts $fd "$command $var" if { $command == "print" } { # For values, let gdb interpret them by printing them. Index: gcc/testsuite/gcc.dg/guality/loop-2.c =================================================================== --- /dev/null 2019-03-08 11:40:14.606883727 +0000 +++ gcc/testsuite/gcc.dg/guality/loop-2.c 2019-06-01 16:52:39.075271587 +0100 @@ -0,0 +1,41 @@ +/* { dg-do run } */ +/* Works without the --param except at -Os. */ +/* { dg-options "-fno-tree-vectorize --param allow-store-data-races=1 -g" } */ + +void __attribute__((noipa)) +get_start (int *x) +{ + *x = 42; +} + +void __attribute__((noipa)) +consume (int *x) +{ + *x += 1; +} + +void __attribute__((noipa)) +test (int *x, int *y, int n) +{ + int base; + get_start (&base); + base += 1; /* { dg-final { gdb-test . "base" "42" } } */ + get_start (&base); /* { dg-final { gdb-test . "base" "43" } } */ + for (int i = 0; i < n; ++i) + { + x[i] = base; /* { dg-final { gdb-test .*10 "base" "51" } } */ + base += y[i]; + } + y[0] = base; /* { dg-final { gdb-test . "base" "142" } } */ + consume (&base); /* { dg-final { gdb-test . "base" "142" } } */ + y[1] = 1; /* { dg-final { gdb-test . "base" "143" } } */ +} + +int +main (void) +{ + int x[100], y[100]; + for (int i = 0; i < 100; ++i) + y[i] = 1; + test (x, y, 100); +} Index: gcc/testsuite/gcc.dg/guality/loop-3.c =================================================================== --- /dev/null 2019-03-08 11:40:14.606883727 +0000 +++ gcc/testsuite/gcc.dg/guality/loop-3.c 2019-06-01 16:52:39.075271587 +0100 @@ -0,0 +1,38 @@ +/* { dg-do run } */ +/* { dg-options "-fno-tree-vectorize -fno-unroll-loops -g" } */ + +void __attribute__((noipa)) +get_start (int *x) +{ + *x = 42; +} + +void __attribute__((noipa)) +consume (int *x) +{ + *x += 1; +} + +void __attribute__((noipa)) +test (int *x, int *y) +{ + int base; + get_start (&base); + base += 1; /* { dg-final { gdb-test . "base" "42" } } */ + get_start (&base); /* { dg-final { gdb-test . "base" "43" } } */ + for (int i = 0; i < 100; ++i) + { + x[i] = base; /* { dg-final { gdb-test .*10 "base" "51" } } */ + base += y[i]; + } + y[0] = base; /* { dg-final { gdb-test . "base" "142" } } */ +} + +int +main (void) +{ + int x[100], y[100]; + for (int i = 0; i < 100; ++i) + y[i] = 1; + test (x, y); +} Index: gcc/testsuite/gcc.dg/guality/loop-4.c =================================================================== --- /dev/null 2019-03-08 11:40:14.606883727 +0000 +++ gcc/testsuite/gcc.dg/guality/loop-4.c 2019-06-01 16:52:39.075271587 +0100 @@ -0,0 +1,34 @@ +/* { dg-do run } */ +/* { dg-options "-fno-tree-vectorize -fno-unroll-loops -g" } */ + +void __attribute__((noipa)) +get_start (int *x) +{ + *x = 42; +} + +void __attribute__((noipa)) +test (int *x, int *y) +{ + int base; + get_start (&base); + base += 1; /* { dg-final { gdb-test . "base" "42" } } */ + get_start (&base); + for (int i = 0; i < 100; ++i) + { + x[i] = base; /* { dg-final { gdb-test .*10 "base" "51" } } */ + base += y[i]; + } + y[0] = base; /* { dg-final { gdb-test . "base" "142" } } */ + base += 1; + y[1] = base; /* { dg-final { gdb-test . "base" "143" } } */ +} + +int +main (void) +{ + int x[100], y[100]; + for (int i = 0; i < 100; ++i) + y[i] = 1; + test (x, y); +}