This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] print_rtx: implement support for reuse IDs
- From: David Malcolm <dmalcolm at redhat dot com>
- To: Bernd Schmidt <bschmidt at redhat dot com>, "gcc-patches at gcc dot gnu dot org" <gcc-patches at gcc dot gnu dot org>
- Cc: David Malcolm <dmalcolm at redhat dot com>
- Date: Fri, 21 Oct 2016 16:27:24 -0400
- Subject: [PATCH] print_rtx: implement support for reuse IDs
- Authentication-results: sourceware.org; auth=none
- References: <a37f2232-f4b5-9ec1-1989-09e35cd42fbc@redhat.com>
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®rtested 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