diff mbox

print_rtx: implement support for reuse IDs

Message ID 1477081644-19797-1-git-send-email-dmalcolm@redhat.com
State New
Headers show

Commit Message

David Malcolm Oct. 21, 2016, 8:27 p.m. UTC
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

Comments

Bernd Schmidt Oct. 25, 2016, 12:47 p.m. UTC | #1
On 10/21/2016 10:27 PM, David Malcolm wrote:
> Thanks.  I attemped to use those fields of recog_data, but it doesn't
> seem to be exactly what's needed here.

Yeah, I may have been confused. I'm not sure that just looking at 
SCRATCHes is the right thing either, but I think you're on the right 
track, and we can use something like your patch for now and extend it 
later if necessary.

> + public:
> +  rtx_reuse_manager ();
> +  ~rtx_reuse_manager ();
> +  static rtx_reuse_manager *get () { return singleton; }

OTOH, this setup looks a bit odd to me. Are you trying to avoid 
converting the print_rtx stuff to its own class, or avoid passing the 
reuse manager as an argument to a lot of functions?

Some of this setup might not even be necessary. We have a "used" flag on 
rtx objects which is used to unshare RTL, and I think could also be used 
for a similar purpose when dumping. So, before printing, call 
reset_insn_used_flags on everything, then have another pass to set bits 
on everything that could conceivably be shared, and when you find 
something that already has the bit set, enter it into a table. Finally, 
print everything out, using the table. I think this would be somewhat 
simpler than adding another header file and class definition.

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

Formatting rules suggest no braces around single statements, I think a 
more readable version of this would be:

   if (uses_rtx_reuse_p (*iter))
     {
       int *count = m_rtx_occurrence_count.get (*iter)
       if (count)
         {
           if ((*count)++ == 1)
             m_rtx_reuse_ids.put (*iter, m_next_id++);
         }
       else
	m_rtx_occurrence_count.put (*iter, 1);
     }


Bernd
diff mbox

Patch

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