This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] print_rtx: implement support for reuse IDs


On Thu, 2016-10-20 at 11:22 +0200, Bernd Schmidt wrote:
> > Recognizing by SCRATCH wouldn't catch everything, I believe. You
> > should
> > be able to check n_dups and dup_loc in recog_data to identify cases
> > where you need to ensure something is restored with pointer
> > equality.

Thanks.  I attemped to use those fields of recog_data, but it doesn't
seem to be exactly what's needed here.

Recall that we have:

      (cinsn (set (mem/v:BLK (scratch:DI) [0  A8])
                    (unspec:BLK [
                            (mem/v:BLK (scratch:DI) [0  A8])
                        ] UNSPEC_MEMORY_BLOCKAGE)) "test.c":2
                 (nil))

If I do a recog and then insn_extract on the insn in question, then
this code in insn-extract.c fires:

    case 695:  /* *memory_blockage */
      ro[0] = *(ro_loc[0] = &XEXP (pat, 0));
      recog_data.dup_loc[0] = &XVECEXP (XEXP (pat, 1), 0, 0);
      recog_data.dup_num[0] = 0;
      break;

and we have:

      (gdb) call debug (*recog_data.dup_loc[0])
      (mem/v:BLK (scratch:DI) [0  A8])

      (gdb) call debug (ro[0])
      (mem/v:BLK (scratch:DI) [0  A8])

      (gdb) p ro[0]
      $17 = (rtx) 0x7ffff19bca98
      (gdb) p recog_data.dup_loc[0]
      $18 = (rtx *) 0x7ffff190eeb8

i.e. it's recorded that the two "(mem/v:BLK (scratch:DI) [0  A8])" match.

However, this doesn't seem to help in terms of actually writing out
the data: it's identified a match_dup, but it's of the two MEM
instances; it's the SCRATCH instance within them that's shared.

Somehow we'd need to traverse the identified match_dup cases and figure
out which descendents within them have identical pointers.

So I came up with a different approach, which doesn't directly use
recog_data.  Instead, there's a new "rtx_reuse_manager" class,
which supports directly identifying the identical SCRATCH instances
during a dump.  This approach seems to me to be simpler, and it's more
flexible, as it can cope with other ways in which pointer-equality
could occur, outside of a match_dup.

On Thu, 2016-10-20 at 17:43 +0200, Bernd Schmidt wrote:
> On 10/20/2016 04:51 PM, David Malcolm wrote:
> >    (0|scratch:DI)
> > 
> > with the insn as a whole looking like:
> > 
> >        (cinsn (set (mem/v:BLK (0|scratch:DI) [0  A8])
> >                       (unspec:BLK [
> >                               (mem/v:BLK (reuse_rtx 0) [0  A8])
> >                           ] UNSPEC_MEMORY_BLOCKAGE)) "test.c":2
> >                    (nil))
> 
> LGTM. I'd try to expose match_dup though, it's the standard name for
> this sort of thing. Hopefully it won't have to be added to a lot of
> switch statements to shut up warnings.

The following patch implements the dumping side of the above proposed
format, via the new "rtx_reuse_manager" class.

I didn't expose match_dup to the host, instead introducing "reuse_rtx"
as a generic place for rtx pointer reuse.

Successfully bootstrapped&regrtested on x86_64-pc-linux-gnu (on top
of "[PATCH] Start adding target-specific selftests").

OK for trunk?

I've (separately) implemented support for loading this format, using
test dumps emitted by this patch and it resolves the issue I had with
the __RTL cc1 selftest.  The testcases for that loading support is
currently integrated with the function_reader code, so I'll save it for
a followup.

gcc/ChangeLog:
	* config/i386/i386.c: Include print-rtl-reuse.h.
	(selftest::ix86_test_dumping_memory_blockage): New function.
	(selftest::ix86_run_selftests): Call it.
	* print-rtl-function.c: Include "print-rtl-reuse.h".
	(print_rtx_function): Create an rtx_reuse_manager and use it.
	* print-rtl-reuse.h: New file.
	* print-rtl.c: Include "print-rtl-reuse.h" and "rtl-iter.h".
	(rtx_reuse_manager::singleton): New global.
	(rtx_reuse_manager::rtx_reuse_manager): New ctor.
	(rtx_reuse_manager::~rtx_reuse_manager): New dtor.
	(uses_rtx_reuse_p): New function.
	(rtx_reuse_manager::preprocess): New function.
	(rtx_reuse_manager::has_reuse_id): New function.
	(rtx_reuse_manager::seen_def_p): New function.
	(rtx_reuse_manager::set_seen_def): New function.
	(print_rtx): If "in_rtx" has a reuse ID, print
	it as a prefix the first time in_rtx is seen, and print
	reuse_rtx subsequently.
	* rtl-tests.c: Include "print-rtl-reuse.h".
	(selftest::test_dumping_rtx_reuse): New function.
	(selftest::rtl_tests_c_tests): Call it.
---
 gcc/config/i386/i386.c   |  23 ++++++++
 gcc/print-rtl-function.c |   6 ++
 gcc/print-rtl-reuse.h    | 100 ++++++++++++++++++++++++++++++++
 gcc/print-rtl.c          | 144 +++++++++++++++++++++++++++++++++++++++++++++--
 gcc/rtl-tests.c          |  49 ++++++++++++++++
 5 files changed, 318 insertions(+), 4 deletions(-)
 create mode 100644 gcc/print-rtl-reuse.h

diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 8f6ceb4..b979cae 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -79,6 +79,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "dojump.h"
 #include "selftest.h"
 #include "selftest-rtl.h"
+#include "print-rtl-reuse.h"
 
 /* This file should be included last.  */
 #include "target-def.h"
@@ -50382,12 +50383,34 @@ ix86_test_dumping_hard_regs ()
   ASSERT_RTL_DUMP_EQ ("(reg:SI dx)", gen_raw_REG (SImode, 1));
 }
 
+/* Test dumping an insn with repeated references to the same SCRATCH,
+   to verify the rtx_reuse code.  */
+
+static void
+ix86_test_dumping_memory_blockage ()
+{
+  rtx pat = gen_memory_blockage ();
+  rtx_reuse_manager r;
+  r.preprocess (pat);
+
+  /* Verify that the repeated references to the SCRATCH show use
+     reuse IDS.  The first should be prefixed with a reuse ID,
+     and the second should be dumped as a "reuse_rtx" of that ID.  */
+  ASSERT_RTL_DUMP_EQ
+    ("(cinsn (set (mem/v:BLK (0|scratch:DI) [0  A8])\n"
+     "        (unspec:BLK [\n"
+     "                (mem/v:BLK (reuse_rtx 0) [0  A8])\n"
+     "            ] UNSPEC_MEMORY_BLOCKAGE))\n"
+     "     (nil))\n", pat);
+}
+
 /* Run all target-specific selftests.  */
 
 static void
 ix86_run_selftests (void)
 {
   ix86_test_dumping_hard_regs ();
+  ix86_test_dumping_memory_blockage ();
 }
 
 } // namespace selftest
diff --git a/gcc/print-rtl-function.c b/gcc/print-rtl-function.c
index 7ce1b90..d5ab24e 100644
--- a/gcc/print-rtl-function.c
+++ b/gcc/print-rtl-function.c
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "function.h"
 #include "basic-block.h"
 #include "print-rtl.h"
+#include "print-rtl-reuse.h"
 #include "langhooks.h"
 #include "memmodel.h"
 #include "emit-rtl.h"
@@ -191,6 +192,11 @@ print_rtx_function (FILE *outfile, function *fn, bool compact)
 {
   flag_compact = compact;
 
+  /* Support "reuse_rtx" in the dump.  */
+  rtx_reuse_manager r;
+  for (rtx_insn *insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    r.preprocess (insn);
+
   tree fdecl = fn->decl;
 
   const char *dname = lang_hooks.decl_printable_name (fdecl, 2);
diff --git a/gcc/print-rtl-reuse.h b/gcc/print-rtl-reuse.h
new file mode 100644
index 0000000..50eca9d
--- /dev/null
+++ b/gcc/print-rtl-reuse.h
@@ -0,0 +1,100 @@
+/* Track pointer reuse when printing RTL.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_PRINT_RTL_REUSE_H
+#define GCC_PRINT_RTL_REUSE_H
+
+#ifndef GENERATOR_FILE
+
+#include "bitmap.h"
+
+/* For some rtx codes (such as SCRATCH), instances are defined to only be
+   equal for pointer equality: two distinct SCRATCH instances are non-equal.
+   copy_rtx preserves this equality by reusing the SCRATCH instance.
+
+   For example, in this x86 instruction:
+
+      (cinsn (set (mem/v:BLK (scratch:DI) [0  A8])
+                    (unspec:BLK [
+                            (mem/v:BLK (scratch:DI) [0  A8])
+                        ] UNSPEC_MEMORY_BLOCKAGE)) "test.c":2
+                 (nil))
+
+   the two instances of "(scratch:DI)" are actually the same underlying
+   rtx pointer (and thus "equal"), and the insn will only be recognized
+   (as "*memory_blockage") if this pointer-equality is preserved.
+
+   To be able to preserve this pointer-equality when round-tripping
+   through dumping/loading the rtl, we need some syntax.  The first
+   time a reused rtx is encountered in the dump, we prefix it with
+   a reuse ID:
+
+      (0|scratch:DI)
+
+   Subsequent references to the rtx in the dump can be expressed using
+   "reuse_rtx" e.g.:
+
+      (reuse_rtx 0)
+
+   This class is responsible for tracking a set of reuse IDs during a dump.
+
+   It is a singleton, registering and unregistering itself for use by
+   print_rtx during its lifetime.
+
+   Dumping with reuse-support is done in two passes:
+
+   (a) a first pass in which "preprocess" is called on each top-level rtx
+       to be seen in the dump.  This traverses the rtx and its descendents,
+       identifying rtx that will be seen more than once in the actual dump,
+       and assigning them reuse IDs.
+
+   (b) the actual dump, via print_rtx etc.  print_rtx detect the presence
+       of a live rtx_reuse_manager and uses it if there is one.  Any rtx
+       that were assigned reuse IDs will be printed with it the first time
+       that they are seen, and then printed as "(reuse_rtx ID)" subsequently.
+
+   The first phase is needed since otherwise there would be no way to tell
+   if an rtx will be reused when first encountering it.  */
+
+class rtx_reuse_manager
+{
+ public:
+  rtx_reuse_manager ();
+  ~rtx_reuse_manager ();
+  static rtx_reuse_manager *get () { return singleton; }
+
+  /* The first pass.  */
+  void preprocess (const_rtx x);
+
+  /* The second pass (within print_rtx).  */
+  bool has_reuse_id (const_rtx x, int *out);
+  bool seen_def_p (int reuse_id);
+  void set_seen_def (int reuse_id);
+
+ private:
+  static rtx_reuse_manager *singleton;
+  hash_map<const_rtx, int> m_rtx_occurrence_count;
+  hash_map<const_rtx, int> m_rtx_reuse_ids;
+  bitmap_head m_defs_seen;
+  int m_next_id;
+};
+
+#endif /* #ifndef GENERATOR_FILE */
+
+#endif  // GCC_PRINT_RTL_REUSE_H
diff --git a/gcc/print-rtl.c b/gcc/print-rtl.c
index 46f3c4d..186e6ed 100644
--- a/gcc/print-rtl.c
+++ b/gcc/print-rtl.c
@@ -51,6 +51,8 @@ along with GCC; see the file COPYING3.  If not see
 #endif
 
 #include "print-rtl.h"
+#include "print-rtl-reuse.h"
+#include "rtl-iter.h"
 
 static FILE *outfile;
 
@@ -93,6 +95,114 @@ int flag_dump_unnumbered_links = 0;
 int flag_simple = 0;
 
 #ifndef GENERATOR_FILE
+
+/* Singleton pointer to the currently-live rtx_reuse_manager instance.  */
+
+rtx_reuse_manager *rtx_reuse_manager::singleton = NULL;
+
+/* rtx_reuse_manager's ctor.  Register this instance with the singleton,
+   and initialize.  */
+
+rtx_reuse_manager::rtx_reuse_manager ()
+: m_next_id (0)
+{
+  gcc_assert (singleton == NULL);
+  singleton = this;
+
+  bitmap_initialize (&m_defs_seen, NULL);
+}
+
+/* rtx_reuse_manager's dtor.  Unregister this instance with the
+   singleton.  */
+
+rtx_reuse_manager::~rtx_reuse_manager ()
+{
+  gcc_assert (singleton == this);
+  singleton = NULL;
+}
+
+/* Determine if X is of a kind suitable for dumping via reuse_rtx.  */
+
+static bool
+uses_rtx_reuse_p (const_rtx x)
+{
+  if (x == NULL)
+    return false;
+
+  switch (GET_CODE (x))
+    {
+    case DEBUG_EXPR:
+    case VALUE:
+    case SCRATCH:
+      return true;
+
+    /* We don't use reuse_rtx for consts.  */
+    CASE_CONST_UNIQUE:
+    default:
+      return false;
+    }
+}
+
+/* Traverse X and its descendents, determining if we see any rtx more than
+   once.  Any rtx suitable for "reuse_rtx" that is seen more than once is
+   assigned an ID.  */
+
+void
+rtx_reuse_manager::preprocess (const_rtx x)
+{
+  subrtx_iterator::array_type array;
+  FOR_EACH_SUBRTX (iter, array, x, NONCONST)
+    if (uses_rtx_reuse_p (*iter))
+      {
+	if (int *count = m_rtx_occurrence_count.get (*iter))
+	  {
+	    if (*count == 1)
+	      {
+		m_rtx_reuse_ids.put (*iter, m_next_id++);
+	      }
+	    (*count)++;
+	  }
+	else
+	  m_rtx_occurrence_count.put (*iter, 1);
+      }
+}
+
+/* Return true iff X has been assigned a reuse ID.  If it has,
+   and OUT is non-NULL, then write the reuse ID to *OUT.  */
+
+bool
+rtx_reuse_manager::has_reuse_id (const_rtx x, int *out)
+{
+  int *id = m_rtx_reuse_ids.get (x);
+  if (id)
+    {
+      if (out)
+	*out = *id;
+      return true;
+    }
+  else
+    return false;
+}
+
+/* Determine if set_seen_def has been called for the given reuse ID.  */
+
+bool
+rtx_reuse_manager::seen_def_p (int reuse_id)
+{
+  return bitmap_bit_p (&m_defs_seen, reuse_id);
+}
+
+/* Record that the definition of the given reuse ID has been seen.  */
+
+void
+rtx_reuse_manager::set_seen_def (int reuse_id)
+{
+  bitmap_set_bit (&m_defs_seen, reuse_id);
+}
+
+#endif /* #ifndef GENERATOR_FILE */
+
+#ifndef GENERATOR_FILE
 void
 print_mem_expr (FILE *outfile, const_tree expr)
 {
@@ -606,8 +716,34 @@ print_rtx (const_rtx in_rtx)
        return;
     }
 
+  fputc ('(', outfile);
+
   /* Print name of expression code.  */
 
+  /* Handle reuse.  */
+#ifndef GENERATOR_FILE
+  if (rtx_reuse_manager::get ())
+    {
+      int reuse_id;
+      if (rtx_reuse_manager::get ()->has_reuse_id (in_rtx, &reuse_id))
+	{
+	  /* Have we already seen the defn of this rtx?  */
+	  if (rtx_reuse_manager::get ()->seen_def_p (reuse_id))
+	    {
+	      fprintf (outfile, "reuse_rtx %i)", reuse_id);
+	      sawclose = 1;
+	      return;
+	    }
+	  else
+	    {
+	      /* First time we've seen this reused-rtx.  */
+	      fprintf (outfile, "%i|", reuse_id);
+	      rtx_reuse_manager::get ()->set_seen_def (reuse_id);
+	    }
+	}
+    }
+#endif /* #ifndef GENERATOR_FILE */
+
   /* In compact mode, prefix the code of insns with "c",
      giving "cinsn", "cnote" etc.  */
   if (flag_compact && is_a <const rtx_insn *, const struct rtx_def> (in_rtx))
@@ -616,14 +752,14 @@ print_rtx (const_rtx in_rtx)
 	 just "clabel".  */
       rtx_code code = GET_CODE (in_rtx);
       if (code == CODE_LABEL)
-	fprintf (outfile, "(clabel");
+	fprintf (outfile, "clabel");
       else
-	fprintf (outfile, "(c%s", GET_RTX_NAME (code));
+	fprintf (outfile, "c%s", GET_RTX_NAME (code));
     }
   else if (flag_simple && CONST_INT_P (in_rtx))
-    fputc ('(', outfile);
+    ; /* no code.  */
   else
-    fprintf (outfile, "(%s", GET_RTX_NAME (GET_CODE (in_rtx)));
+    fprintf (outfile, "%s", GET_RTX_NAME (GET_CODE (in_rtx)));
 
   if (! flag_simple)
     {
diff --git a/gcc/rtl-tests.c b/gcc/rtl-tests.c
index 10c0ddc..b1182c6 100644
--- a/gcc/rtl-tests.c
+++ b/gcc/rtl-tests.c
@@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "pretty-print.h"
 #include "cfgbuild.h"
 #include "print-rtl.h"
+#include "print-rtl-reuse.h"
 #include "selftest.h"
 #include "selftest-rtl.h"
 #include "function.h"
@@ -129,6 +130,53 @@ test_dumping_insns ()
   ASSERT_RTL_DUMP_EQ ("(clabel 0 42 (\"some_label\"))\n", label);
 }
 
+/* Manually exercise the rtx_reuse_manager code.  */
+
+static void
+test_dumping_rtx_reuse ()
+{
+  rtx_reuse_manager r;
+
+  rtx x = rtx_alloc (SCRATCH);
+  rtx y = rtx_alloc (SCRATCH);
+  rtx z = rtx_alloc (SCRATCH);
+
+  /* x and y will be seen more than once.  */
+  r.preprocess (x);
+  r.preprocess (x);
+  r.preprocess (y);
+  r.preprocess (y);
+
+  /* z will be only seen once.  */
+  r.preprocess (z);
+
+  /* Verify that x and y have been assigned reuse IDs.  */
+  int reuse_id_for_x;
+  ASSERT_TRUE (r.has_reuse_id (x, &reuse_id_for_x));
+  ASSERT_EQ (0, reuse_id_for_x);
+
+  int reuse_id_for_y;
+  ASSERT_TRUE (r.has_reuse_id (y, &reuse_id_for_y));
+  ASSERT_EQ (1, reuse_id_for_y);
+
+  /* z is only seen once and thus shouldn't get a reuse ID.  */
+  ASSERT_FALSE (r.has_reuse_id (z, NULL));
+
+  /* The first dumps of x and y should be prefixed by reuse ID;
+     all subsequent dumps of them should show up as "reuse_rtx".  */
+  ASSERT_RTL_DUMP_EQ ("(0|scratch)", x);
+  ASSERT_RTL_DUMP_EQ ("(reuse_rtx 0)", x);
+  ASSERT_RTL_DUMP_EQ ("(reuse_rtx 0)", x);
+
+  ASSERT_RTL_DUMP_EQ ("(1|scratch)", y);
+  ASSERT_RTL_DUMP_EQ ("(reuse_rtx 1)", y);
+  ASSERT_RTL_DUMP_EQ ("(reuse_rtx 1)", y);
+
+  /* z only appears once and thus shouldn't be prefixed with a
+     reuse ID.  */
+  ASSERT_RTL_DUMP_EQ ("(scratch)", z);
+}
+
 /* Unit testing of "single_set".  */
 
 static void
@@ -189,6 +237,7 @@ rtl_tests_c_tests ()
 {
   test_dumping_regs ();
   test_dumping_insns ();
+  test_dumping_rtx_reuse ();
   test_single_set ();
   test_uncond_jump ();
 
-- 
1.8.5.3


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]