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);
+}

Reply via email to