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