From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 3995 invoked by alias); 5 Oct 2016 15:45:32 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Received: (qmail 3330 invoked by uid 89); 5 Oct 2016 15:45:26 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=2.3 required=5.0 tests=BAYES_50,KAM_STOCKGEN autolearn=no version=3.3.2 spammy=readers, DECL_NAME, decl_name, dfmode X-HELO: eggs.gnu.org Received: from eggs.gnu.org (HELO eggs.gnu.org) (208.118.235.92) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 05 Oct 2016 15:44:55 +0000 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1broN7-0000MZ-P7 for gcc-patches@gcc.gnu.org; Wed, 05 Oct 2016 11:44:52 -0400 Received: from mx1.redhat.com ([209.132.183.28]:46122) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1broN7-0000LR-2i for gcc-patches@gcc.gnu.org; Wed, 05 Oct 2016 11:44:41 -0400 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 8F4728B978 for ; Wed, 5 Oct 2016 15:44:40 +0000 (UTC) Received: from c64.redhat.com (vpn-236-9.phx2.redhat.com [10.3.236.9]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u95FiV5S027350; Wed, 5 Oct 2016 11:44:39 -0400 From: David Malcolm To: gcc-patches@gcc.gnu.org Cc: David Malcolm Subject: [PATCH 10/16] Introduce class function_reader (v3) Date: Wed, 05 Oct 2016 15:45:00 -0000 Message-Id: <1475684110-2521-11-git-send-email-dmalcolm@redhat.com> In-Reply-To: <1475684110-2521-1-git-send-email-dmalcolm@redhat.com> References: <1475684110-2521-1-git-send-email-dmalcolm@redhat.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 X-IsSubscribed: yes X-SW-Source: 2016-10/txt/msg00267.txt.bz2 This patch implements class function_reader, which implements the dump-parsing functionality used in the rest of the kit. Changed in v3: * rather than hardcoding the selftest input dumps as escaped C strings in the source code, move them out into separate files, using selftest::locate_file * update to use the new "print_rtx_function" output format, parsing "(cfg)" and "(crtl)" directives to reconstruct the CFG and various metadata. * eliminated regno_remapper in favor of using the absence of a trailing register name to detect pseudos * significantly cleaner handling of print_rtx's various "warts", moving the parsing from read-rtl.c to read-rtl-function.c, by making read_rtx_operand virtual. Split the implementation up into smaller functions to make it easier to grok. * more test coverage * lots of other cleanups (some FIXMEs and TODOs remain) Blurb from original version: This patch generalizes the RTL-reading capabilities so that they can be run on the host as well as the build machine. The available rtx in rtl.def changes dramatically between these two configurations, so a fair amount of #ifdef GENERATOR_FILE is required to express this. This patch introduces a function_reader subclass of rtx_reader, capable of reading an RTL function dump (or part of one), reconstructing a cfun with a CFG and basic blocks containing insns. gcc/ChangeLog: * Makefile.in (OBJS): Add errors.o, read-md.o, read-rtl.o, read-rtl-function.o, and selftest-rtl.o. * errors.c: Use consistent pattern for bconfig.h vs config.h includes. (progname): Wrap with #ifdef GENERATOR_FILE. (error): Likewise. Add "error: " to message. (fatal): Likewise. (internal_error): Likewise. (trim_filename): Likewise. (fancy_abort): Likewise. * errors.h (struct file_location): Move here from read-md.h. (file_location::file_location): Likewise. (error_at): New decl. * function-tests.c (selftest::verify_three_block_rtl_cfg): Remove "static". * function.c (instantiate_decls): Guard call to instantiate_decls_1 with if (DECL_INITIAL (fndecl)). * print-rtl.c (print_rtx): Print "(nil)" rather than an empty string for NULL strings. Print "(nil)" for NULL basic blocks. * read-md.c (read_skip_construct): Provide forward decl. (read_skip_spaces): Support '/'. (read_name): Rename to... (read_name_1): ...this new static function, adding "out_loc" param, and converting "missing name or number" to returning false, rather than failing. (read_name): Reimplement in terms of read_name_1. (read_name_or_nil): New function. (read_string): Handle "(nil)" by returning NULL. */ * read-md.h (struct file_location): Move to errors.h. (file_location::file_location): Likewise. Include errors.h. (rtx_reader::read_rtx_operand): Make virtual. (rtx_reader::read_until): New decl. (rtx_reader::handle_any_trailing_information): New virtual func. (rtx_reader::postprocess): New virtual func. (rtx_reader::m_in_call_function_usage): New field. (read_name): Convert return type from void to file_location. (read_name_or_nil): New decl. * read-rtl-function.c: New file. * read-rtl-function.h: New file. * read-rtl.c: Potentially include config.h rather than bconfig.h. For host, include function.h and emit-rtl.h. (apply_subst_iterator): Wrap with #ifdef GENERATOR_FILE. (bind_subst_iter_and_attr): Likewise. (add_condition_to_string): Likewise. (add_condition_to_rtx): Likewise. (apply_attribute_uses): Likewise. (add_current_iterators): Likewise. (apply_iterators): Likewise. (initialize_iterators): Guard usage of apply_subst_iterator with #ifdef GENERATOR_FILE. (read_conditions): Wrap with #ifdef GENERATOR_FILE. (read_mapping): Likewise. (add_define_attr_for_define_subst): Likewise. (add_define_subst_attr): Likewise. (read_subst_mapping): Likewise. (check_code_iterator): Likewise. (check_code_iterator): Likewise. (read_rtx): Likewise. Move one-time initialization logic to... (one_time_initialization): New function. (rtx_reader::read_until): New method. (read_flags): New function. (parse_reg_note_name): New function. (rtx_reader::read_rtx_code): Initialize "iterator" to NULL. Call one_time_initialization. Wrap iterator lookup within #ifdef GENERATOR_FILE. Add parsing support for RTL dumps, mirroring the special-cases in print_rtx, by calling read_flags, reading REG_NOTE names, INSN_UID values, and calling handle_any_trailing_information. (rtx_reader::read_rtx_operand): Handle case 'e'. When on host, reallocate XSTR and XTMPL fields in the GC-managed heap. (rtx_reader::read_nested_rtx): Call the postprocess vfunc on the return_rtx. (rtx_reader::rtx_reader): Initialize m_in_call_function_usage. * rtl.h (read_rtx): Wrap decl with #ifdef GENERATOR_FILE. * selftest-rtl.c: New file. * selftest-rtl.h: New file. * selftest-run-tests.c (selftest::run_tests): Run read_rtl_function_c_tests. * selftest.h (selftest::read_rtl_function_c_tests): New decl. * tree-dfa.c (ssa_default_def): Return NULL_TREE for rtl function dumps. gcc/testsuite/ChangeLog: * selftests: New subdirectory. * selftests/aarch64: New subdirectory. * selftests/aarch64/times-two.rtl: New file. * selftests/asr_div1.rtl: New file. * selftests/cfg-test.rtl: New file. * selftests/const-int.rtl: New file. * selftests/copy-hard-reg-into-frame.rtl: New file. * selftests/example-labels.rtl: New file. * selftests/insn-with-mode.rtl: New file. * selftests/jump-to-label-ref.rtl: New file. * selftests/jump-to-return.rtl: New file. * selftests/jump-to-simple-return.rtl: New file. * selftests/note-insn-deleted.rtl: New file. * selftests/note_insn_basic_block.rtl: New file. * selftests/symbol-ref.rtl: New file. * selftests/test-regno-renumbering.rtl: New file. * selftests/x86_64: New subdirectory. * selftests/x86_64/call-insn.rtl: New file. * selftests/x86_64/times-two.rtl: New file. --- gcc/Makefile.in | 5 + gcc/errors.c | 23 +- gcc/errors.h | 14 + gcc/function-tests.c | 2 +- gcc/function.c | 3 +- gcc/print-rtl.c | 4 +- gcc/read-md.c | 52 +- gcc/read-md.h | 27 +- gcc/read-rtl-function.c | 2402 ++++++++++++++++++++ gcc/read-rtl-function.h | 37 + gcc/read-rtl.c | 205 +- gcc/rtl.h | 2 + gcc/selftest-rtl.c | 90 + gcc/selftest-rtl.h | 67 + gcc/selftest-run-tests.c | 1 + gcc/selftest.h | 1 + gcc/testsuite/selftests/aarch64/times-two.rtl | 42 + gcc/testsuite/selftests/asr_div1.rtl | 23 + gcc/testsuite/selftests/cfg-test.rtl | 38 + gcc/testsuite/selftests/const-int.rtl | 16 + .../selftests/copy-hard-reg-into-frame.rtl | 12 + gcc/testsuite/selftests/example-labels.rtl | 6 + gcc/testsuite/selftests/insn-with-mode.rtl | 5 + gcc/testsuite/selftests/jump-to-label-ref.rtl | 11 + gcc/testsuite/selftests/jump-to-return.rtl | 10 + gcc/testsuite/selftests/jump-to-simple-return.rtl | 11 + gcc/testsuite/selftests/note-insn-deleted.rtl | 5 + gcc/testsuite/selftests/note_insn_basic_block.rtl | 5 + gcc/testsuite/selftests/symbol-ref.rtl | 7 + gcc/testsuite/selftests/test-regno-renumbering.rtl | 6 + gcc/testsuite/selftests/x86_64/call-insn.rtl | 12 + gcc/testsuite/selftests/x86_64/times-two.rtl | 57 + gcc/tree-dfa.c | 5 + 33 files changed, 3165 insertions(+), 41 deletions(-) create mode 100644 gcc/read-rtl-function.c create mode 100644 gcc/read-rtl-function.h create mode 100644 gcc/selftest-rtl.c create mode 100644 gcc/selftest-rtl.h create mode 100644 gcc/testsuite/selftests/aarch64/times-two.rtl create mode 100644 gcc/testsuite/selftests/asr_div1.rtl create mode 100644 gcc/testsuite/selftests/cfg-test.rtl create mode 100644 gcc/testsuite/selftests/const-int.rtl create mode 100644 gcc/testsuite/selftests/copy-hard-reg-into-frame.rtl create mode 100644 gcc/testsuite/selftests/example-labels.rtl create mode 100644 gcc/testsuite/selftests/insn-with-mode.rtl create mode 100644 gcc/testsuite/selftests/jump-to-label-ref.rtl create mode 100644 gcc/testsuite/selftests/jump-to-return.rtl create mode 100644 gcc/testsuite/selftests/jump-to-simple-return.rtl create mode 100644 gcc/testsuite/selftests/note-insn-deleted.rtl create mode 100644 gcc/testsuite/selftests/note_insn_basic_block.rtl create mode 100644 gcc/testsuite/selftests/symbol-ref.rtl create mode 100644 gcc/testsuite/selftests/test-regno-renumbering.rtl create mode 100644 gcc/testsuite/selftests/x86_64/call-insn.rtl create mode 100644 gcc/testsuite/selftests/x86_64/times-two.rtl diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 4b50f0b..7c8df56 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1270,6 +1270,7 @@ OBJS = \ dwarf2cfi.o \ dwarf2out.o \ emit-rtl.o \ + errors.o \ et-forest.o \ except.o \ explow.o \ @@ -1408,6 +1409,9 @@ OBJS = \ print-rtl-function.o \ print-tree.o \ profile.o \ + read-md.o \ + read-rtl.o \ + read-rtl-function.o \ real.o \ realmpfr.o \ recog.o \ @@ -1435,6 +1439,7 @@ OBJS = \ sel-sched-ir.o \ sel-sched-dump.o \ sel-sched.o \ + selftest-rtl.o \ selftest-run-tests.o \ sese.o \ shrink-wrap.o \ diff --git a/gcc/errors.c b/gcc/errors.c index 468384c..8df94ab 100644 --- a/gcc/errors.c +++ b/gcc/errors.c @@ -21,18 +21,21 @@ along with GCC; see the file COPYING3. If not see in the generator programs; the compiler has a more elaborate suite of diagnostic printers, found in diagnostic.c. */ -#ifdef HOST_GENERATOR_FILE -#include "config.h" -#define GENERATOR_FILE 1 -#else +/* This file is compiled twice: once for the generator programs + once for the compiler. */ +#ifdef GENERATOR_FILE #include "bconfig.h" +#else +#include "config.h" #endif #include "system.h" #include "errors.h" /* Set this to argv[0] at the beginning of main. */ +#ifdef GENERATOR_FILE const char *progname; +#endif /* #ifdef GENERATOR_FILE */ /* Starts out 0, set to 1 if error is called. */ @@ -55,13 +58,15 @@ warning (const char *format, ...) /* Print an error message - we keep going but the output is unusable. */ +#ifdef GENERATOR_FILE + void error (const char *format, ...) { va_list ap; va_start (ap, format); - fprintf (stderr, "%s: ", progname); + fprintf (stderr, "%s: error: ", progname); vfprintf (stderr, format, ap); va_end (ap); fputc ('\n', stderr); @@ -69,6 +74,7 @@ error (const char *format, ...) have_error = 1; } +#endif /* #ifdef GENERATOR_FILE */ /* Fatal error - terminate execution immediately. Does not return. */ @@ -78,7 +84,7 @@ fatal (const char *format, ...) va_list ap; va_start (ap, format); - fprintf (stderr, "%s: ", progname); + fprintf (stderr, "%s: error: ", progname); vfprintf (stderr, format, ap); va_end (ap); fputc ('\n', stderr); @@ -87,6 +93,8 @@ fatal (const char *format, ...) /* Similar, but say we got an internal error. */ +#ifdef GENERATOR_FILE + void internal_error (const char *format, ...) { @@ -127,8 +135,11 @@ trim_filename (const char *name) /* "Fancy" abort. Reports where in the compiler someone gave up. This file is used only by build programs, so we're not as polite as the version in diagnostic.c. */ + void fancy_abort (const char *file, int line, const char *func) { internal_error ("abort in %s, at %s:%d", func, trim_filename (file), line); } + +#endif /* #ifdef GENERATOR_FILE */ diff --git a/gcc/errors.h b/gcc/errors.h index a6973f3..ebafa77 100644 --- a/gcc/errors.h +++ b/gcc/errors.h @@ -28,8 +28,22 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_ERRORS_H #define GCC_ERRORS_H +/* Records a position in the file. */ +struct file_location { + file_location () {} + file_location (const char *, int, int); + + const char *filename; + int lineno; + int colno; +}; + +inline file_location::file_location (const char *filename_in, int lineno_in, int colno_in) +: filename (filename_in), lineno (lineno_in), colno (colno_in) {} + extern void warning (const char *, ...) ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD; extern void error (const char *, ...) ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD; +extern void error_at (file_location, const char *, ...) ATTRIBUTE_PRINTF_2 ATTRIBUTE_COLD; extern void fatal (const char *, ...) ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD; extern void internal_error (const char *, ...) ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD; extern const char *trim_filename (const char *); diff --git a/gcc/function-tests.c b/gcc/function-tests.c index 4152cd3..2235b4c 100644 --- a/gcc/function-tests.c +++ b/gcc/function-tests.c @@ -420,7 +420,7 @@ verify_three_block_gimple_cfg (function *fun) /* As above, but additionally verify the RTL insns are sane. */ -static void +void verify_three_block_rtl_cfg (function *fun) { verify_three_block_cfg (fun); diff --git a/gcc/function.c b/gcc/function.c index 94ed786..1614bee 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -1901,7 +1901,8 @@ instantiate_decls (tree fndecl) instantiate_decl_rtl (DECL_RTL (DECL_VALUE_EXPR (decl))); /* Now process all variables defined in the function or its subblocks. */ - instantiate_decls_1 (DECL_INITIAL (fndecl)); + if (DECL_INITIAL (fndecl)) + instantiate_decls_1 (DECL_INITIAL (fndecl)); FOR_EACH_LOCAL_DECL (cfun, ix, decl) if (DECL_RTL_SET_P (decl)) diff --git a/gcc/print-rtl.c b/gcc/print-rtl.c index a905127..a2cb69a 100644 --- a/gcc/print-rtl.c +++ b/gcc/print-rtl.c @@ -220,7 +220,7 @@ print_rtx (const_rtx in_rtx) string: if (str == 0) - fputs (" \"\"", outfile); + fputs (" (nil)", outfile); else fprintf (outfile, " (\"%s\")", str); sawclose = 1; @@ -586,6 +586,8 @@ print_rtx (const_rtx in_rtx) #ifndef GENERATOR_FILE if (XBBDEF (in_rtx, i)) fprintf (outfile, " %i", XBBDEF (in_rtx, i)->index); + else + fprintf (outfile, " (nil)"); #endif break; diff --git a/gcc/read-md.c b/gcc/read-md.c index 9ffdb8e..b6bafe7 100644 --- a/gcc/read-md.c +++ b/gcc/read-md.c @@ -65,6 +65,8 @@ static htab_t md_constants; /* A table of enum_type structures, hashed by name. */ static htab_t enum_types; +static void read_skip_construct (int depth, file_location loc); + /* Given an object that starts with a char * name field, return a hash code for its name. */ @@ -347,7 +349,7 @@ read_skip_spaces (void) if (c != '*') { unread_char (c); - fatal_with_file_and_line ("stray '/' in file"); + return '/'; } prevc = 0; @@ -446,8 +448,8 @@ peek_char (void) /* Read an rtx code name into NAME. It is terminated by any of the punctuation chars of rtx printed syntax. */ -void -read_name (struct md_name *name) +static bool +read_name_1 (struct md_name *name, file_location *out_loc) { int c; size_t i; @@ -485,8 +487,12 @@ read_name (struct md_name *name) c = read_char (); } + unread_char (c); + *out_loc = base_rtx_reader_ptr->get_current_location (); + read_char (); + if (i == 0) - fatal_with_file_and_line ("missing name or number"); + return false; name->buffer[i] = 0; name->string = name->buffer; @@ -507,6 +513,36 @@ read_name (struct md_name *name) } while (def); } + + return true; +} + +/* Read an rtx code name into NAME. It is terminated by any of the + punctuation chars of rtx printed syntax. */ + +file_location +read_name (struct md_name *name) +{ + file_location loc; + if (!read_name_1 (name, &loc)) + fatal_with_file_and_line ("missing name or number"); + return loc; +} + +file_location +read_name_or_nil (struct md_name *name) +{ + file_location loc; + if (!read_name_1 (name, &loc)) + { + file_location loc = base_rtx_reader_ptr->get_current_location (); + read_skip_construct (0, loc); + /* Skip the ')'. */ + read_char (); + name->buffer[0] = 0; + name->string = name->buffer; + } + return loc; } /* Subroutine of the string readers. Handles backslash escapes. @@ -652,6 +688,14 @@ read_string (int star_if_braced) obstack_1grow (&string_obstack, '*'); stringbuf = read_braced_string (); } + else if (saw_paren && c == 'n') + { + /* Handle (nil) by returning NULL. */ + require_char ('i'); + require_char ('l'); + require_char_ws (')'); + return NULL; + } else fatal_with_file_and_line ("expected `\"' or `{', found `%c'", c); diff --git a/gcc/read-md.h b/gcc/read-md.h index 0701f35..4933912 100644 --- a/gcc/read-md.h +++ b/gcc/read-md.h @@ -21,19 +21,7 @@ along with GCC; see the file COPYING3. If not see #define GCC_READ_MD_H #include "obstack.h" - -/* Records a position in the file. */ -struct file_location { - file_location () {} - file_location (const char *, int, int); - - const char *filename; - int lineno; - int colno; -}; - -inline file_location::file_location (const char *filename_in, int lineno_in, int colno_in) -: filename (filename_in), lineno (lineno_in), colno (colno_in) {} +#include "errors.h" /* Holds one symbol or number in the .md file. */ struct md_name { @@ -200,9 +188,17 @@ class rtx_reader : public base_rtx_reader ~rtx_reader (); rtx read_rtx_code (const char *); - rtx read_rtx_operand (rtx return_rtx, int idx); + virtual rtx read_rtx_operand (rtx return_rtx, int idx); rtx read_nested_rtx (void); rtx read_rtx_variadic (rtx); + char *read_until (const char *terminator_chars, bool consume_terminator); + + virtual void handle_any_trailing_information (rtx) {} + virtual rtx postprocess (rtx x) { return x; } + + protected: + /* Analogous to print-rtl.c's in_call_function_usage. */ + bool m_in_call_function_usage; }; /* Global singleton; constrast with base_rtx_reader_ptr above. */ @@ -247,7 +243,8 @@ extern int read_skip_spaces (void); extern void require_char (char expected); extern void require_char_ws (char expected); extern void require_word_ws (const char *expected); -extern void read_name (struct md_name *); +extern file_location read_name (struct md_name *); +extern file_location read_name_or_nil (struct md_name *); extern char *read_quoted_string (void); extern char *read_string (int); extern int n_comma_elts (const char *); diff --git a/gcc/read-rtl-function.c b/gcc/read-rtl-function.c new file mode 100644 index 0000000..0cd34d4 --- /dev/null +++ b/gcc/read-rtl-function.c @@ -0,0 +1,2402 @@ +/* read-rtl-function.c - Reader for RTL function dumps + 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 +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "target.h" +#include "tree.h" +#include "gimple-expr.h" +#include "diagnostic.h" +#include "opts.h" +#include "fold-const.h" +#include "gimplify.h" +#include "stor-layout.h" +#include "debug.h" +#include "convert.h" +#include "langhooks.h" +#include "langhooks-def.h" +#include "common/common-target.h" +#include "read-md.h" +#include +#include "rtl.h" +#include "cfghooks.h" +#include "stringpool.h" +#include "function.h" +#include "tree-cfg.h" +#include "cfg.h" +#include "basic-block.h" +#include "cfgrtl.h" +#include "emit-rtl.h" +#include "cgraph.h" +#include "tree-pass.h" +#include "context.h" +#include "pass_manager.h" +#include "toplev.h" +#include "bitmap.h" +#include "df.h" +#include "regs.h" +#include "varasm.h" +#include "insn-addr.h" +#include "read-rtl-function.h" +#include "selftest.h" +#include "selftest-rtl.h" + +/* Forward decls. */ +class function_reader; +class fixup; + +/* Subclass of rtx_reader for reading function dumps. */ + +class function_reader : public rtx_reader +{ + public: + function_reader (function_reader_policy *policy); + ~function_reader (); + + /* Overridden vfuncs of class base_rtx_reader. */ + void handle_unknown_directive (file_location, const char *) FINAL OVERRIDE; + + /* Overridden vfuncs of class rtx_reader. */ + rtx read_rtx_operand (rtx return_rtx, int idx) FINAL OVERRIDE; + void handle_any_trailing_information (rtx return_rtx) FINAL OVERRIDE; + rtx postprocess (rtx) FINAL OVERRIDE; + + int get_pseudo_offset () const { return m_pseudo_offset; } + rtx_insn **get_insn_by_uid (int uid); + tree parse_mem_expr (const char *desc); + + private: + void parse_function (); + void create_function (); + void parse_insn_chain (); + void parse_insn (file_location loc, const char *name); + void create_cfg_and_basic_blocks (); + void parse_cfg (file_location loc); + void parse_bb (); + void parse_edge (); + void parse_crtl (file_location loc); + + void read_rtx_operand_u (rtx return_rtx, int idx); + void read_rtx_operand_i_or_n (rtx return_rtx, int idx, char format_char); + rtx extra_parsing_for_operand_code_0 (rtx return_rtx, int idx); + rtx extra_parsing_for_operand_code_r (rtx return_rtx); + + void add_fixup_insn_uid (file_location loc, rtx insn, int operand_idx, + int insn_uid); + + void add_fixup_bb (file_location loc, rtx insn, + int operand_idx, int bb_idx); + + void add_fixup_note_insn_basic_block (file_location loc, rtx insn, + int operand_idx, int bb_idx); + + void add_fixup_source_location (file_location loc, rtx insn, + int operand_idx, + const char *filename, int lineno); + + void add_fixup_jump_label (file_location loc, rtx insn, + int operand_idx, + const char *label); + + void add_fixup_expr (file_location loc, rtx x, + const char *desc); + + void add_fixup_pseudo (file_location loc, rtx x); + + rtx consolidate_singletons (rtx x); + void maybe_read_location (int operand_idx, rtx insn); + + void apply_fixups (); + void recreate_implicit_cfg_edges (); + + private: + function_reader_policy *m_policy; + struct uid_hash : int_hash {}; + hash_map m_insns_by_uid; + auto_vec m_fixups; + bitmap_obstack m_bitmap_obstack; + bitmap_head m_dumped_pseudos; + bitmap_head m_bb_indices_in_insns; + bitmap_head m_bb_indices_in_cfg; + rtx_insn *m_first_insn; + auto_vec m_fake_scope; + char *m_name; + bool m_have_cfg_directive; + bool m_have_crtl_directive; + int m_pseudo_offset; +}; + +/* Abstract base class for recording post-processing steps that must be + done after reading a .rtl file. */ + +class fixup +{ + public: + fixup (file_location loc, rtx x) + : m_loc (loc), m_rtx (x) + {} + virtual ~fixup () {} + + virtual void apply (function_reader *reader) const = 0; + + protected: + file_location m_loc; + rtx m_rtx; +}; + +/* An abstract subclass of fixup for post-processing steps that + act on a specific operand of a specific instruction. */ + +class operand_fixup : public fixup +{ + public: + operand_fixup (file_location loc, rtx insn, int operand_idx) + : fixup (loc, insn), m_operand_idx (operand_idx) + {} + + protected: + int m_operand_idx; +}; + +/* A concrete subclass of operand_fixup: fixup an rtx_insn * + field (NEXT_INSN/PREV_INSN) based on an integer UID. */ + +class fixup_insn_uid : public operand_fixup +{ + public: + fixup_insn_uid (file_location loc, rtx insn, int operand_idx, int insn_uid) + : operand_fixup (loc, insn, operand_idx), + m_insn_uid (insn_uid) + {} + + void apply (function_reader *reader) const; + + private: + int m_insn_uid; +}; + +/* A concrete subclass of operand_fixup: fix up a basic_block + pointer field based on an integer block ID. */ + +class fixup_bb : public operand_fixup +{ + public: + fixup_bb (file_location loc, rtx insn, int operand_idx, int bb_idx) + : operand_fixup (loc, insn, operand_idx), + m_bb_idx (bb_idx) + {} + + void apply (function_reader *reader) const; + + private: + int m_bb_idx; +}; + +/* A concrete subclass of operand_fixup: fix up a + NOTE_INSN_BASIC_BLOCK based on an integer block ID. */ + +class fixup_note_insn_basic_block : public operand_fixup +{ + public: + fixup_note_insn_basic_block (file_location loc, rtx insn, int operand_idx, + int bb_idx) + : operand_fixup (loc, insn, operand_idx), + m_bb_idx (bb_idx) + {} + + void apply (function_reader *reader) const; + + private: + int m_bb_idx; +}; + +/* A concrete subclass of operand_fixup: fix up the JUMP_LABEL + of an insn based on a label name. */ + +class fixup_jump_label : public operand_fixup +{ + public: + fixup_jump_label (file_location loc, rtx insn, + int operand_idx, + const char *label) + : operand_fixup (loc, insn, operand_idx), + m_label (xstrdup (label)) + {} + + ~fixup_jump_label () { free (m_label); } + + void apply (function_reader *reader) const; + + private: + char *m_label; +}; + +/* A concrete subclass of fixup (not operand_fixup): fix up + the expr of an rtx (REG or MEM) based on a textual dump. */ + +class fixup_expr : public fixup +{ + public: + fixup_expr (file_location loc, rtx x, const char *desc) + : fixup (loc, x), + m_desc (xstrdup (desc)) + {} + + ~fixup_expr () { free (m_desc); } + + void apply (function_reader *reader) const; + + private: + char *m_desc; +}; + +/* A concrete subclass of fixup (not operand_fixup): fix up + the regno of a pseudo REG to ensure that it is a pseudo + on the current target. */ + +class fixup_pseudo : public fixup +{ + public: + fixup_pseudo (file_location loc, rtx x) + : fixup (loc, x) + {} + + void apply (function_reader *reader) const; +}; + +/* Return a textual description of the given operand of the given rtx. */ + +static const char * +get_operand_name (rtx insn, int operand_idx) +{ + gcc_assert (is_a (insn)); + switch (operand_idx) + { + case 0: + return "PREV_INSN"; + case 1: + return "NEXT_INSN"; + default: + return NULL; + } +} + +/* Fixup an rtx_insn * field (NEXT_INSN/PREV_INSN) based on an integer + UID. */ + +void +fixup_insn_uid::apply (function_reader *reader) const +{ + rtx_insn **insn_from_uid = reader->get_insn_by_uid (m_insn_uid); + if (insn_from_uid) + XEXP (m_rtx, m_operand_idx) = *insn_from_uid; + else + { + const char *op_name = get_operand_name (m_rtx, m_operand_idx); + if (op_name) + error_at (m_loc, + "insn with UID %i not found for operand %i (`%s') of insn %i", + m_insn_uid, m_operand_idx, op_name, INSN_UID (m_rtx)); + else + error_at (m_loc, + "insn with UID %i not found for operand %i of insn %i", + m_insn_uid, m_operand_idx, INSN_UID (m_rtx)); + } +} + +/* Fix up a basic_block pointer field based on an integer block ID. + Update BB_HEAD and BB_END of the block as appropriate, as if the insn + was being added to the end of the block. */ + +void +fixup_bb::apply (function_reader */*reader*/) const +{ + basic_block bb = BASIC_BLOCK_FOR_FN (cfun, m_bb_idx); + gcc_assert (bb); + XBBDEF (m_rtx, m_operand_idx) = bb; + + rtx_insn *insn = as_a (m_rtx); + if (!BB_HEAD (bb)) + BB_HEAD (bb) = insn; + BB_END (bb) = insn; +} + +/* Fix up a NOTE_INSN_BASIC_BLOCK based on an integer block ID. */ + +void +fixup_note_insn_basic_block::apply (function_reader */*reader*/) const +{ + basic_block bb = BASIC_BLOCK_FOR_FN (cfun, m_bb_idx); + gcc_assert (bb); + NOTE_BASIC_BLOCK (m_rtx) = bb; +} + +/* Fix up the JUMP_LABEL of an insn based on a label name. */ + +void +fixup_jump_label::apply (function_reader *reader) const +{ + if (0 == strcmp (m_label, "return")) + JUMP_LABEL (m_rtx) = ret_rtx; + else if (0 == strcmp (m_label, "simple_return")) + JUMP_LABEL (m_rtx) = simple_return_rtx; + else + { + int label_uid = atoi (m_label); + rtx_insn **insn_from_uid = reader->get_insn_by_uid (label_uid); + if (insn_from_uid) + JUMP_LABEL (m_rtx) = *insn_from_uid; + else + error_at (m_loc, + "insn with UID %i not found for operand %i (`%s') of insn %i", + label_uid, m_operand_idx, "JUMP_LABEL", INSN_UID (m_rtx)); + } +} + +/* Fix up the expr of an rtx (REG or MEM) based on a textual dump. */ + +void +fixup_expr::apply (function_reader *reader) const +{ + tree expr = reader->parse_mem_expr (m_desc); + switch (GET_CODE (m_rtx)) + { + case REG: + set_reg_attrs_for_decl_rtl (expr, m_rtx); + break; + case MEM: + set_mem_expr (m_rtx, expr); + break; + default: + gcc_unreachable (); + } +#if 0 + if (!DECL_RTL_SET_P (expr)) + SET_DECL_RTL (expr, m_rtx); +#endif +} + +/* Fix up the regno of a REG that ought to be a pseudo, based + on an offset generated from all of the pseudo regnos seen. */ + +void +fixup_pseudo::apply (function_reader *reader) const +{ + gcc_assert (GET_CODE (m_rtx) == REG); + + int dumped_regno = REGNO (m_rtx); + int effective_regno = dumped_regno + reader->get_pseudo_offset (); + set_regno_raw (m_rtx, effective_regno, 1); + + ORIGINAL_REGNO (m_rtx) = effective_regno; + // FIXME: do we want this? it makes comparing dumps easier +} + +/* Strip trailing whitespace from DESC. */ + +static void +strip_trailing_whitespace (char *desc) +{ + char *terminator = desc + strlen (desc); + while (desc < terminator) + { + terminator--; + if (ISSPACE (*terminator)) + *terminator = '\0'; + else + break; + } +} + +/* Return the numeric value n for GET_NOTE_INSN_NAME (n) for STRING, + or fail if STRING isn't recognized. */ + +static int +parse_note_insn_name (const char *string) +{ + for (int i = 0; i < NOTE_INSN_MAX; i++) + if (0 == strcmp (string, GET_NOTE_INSN_NAME (i))) + return i; + fatal_with_file_and_line ("unrecognized NOTE_INSN name: `%s'", string); +} + +/* Return the register number for NAME, or return -1 if it isn't + recognized. */ + +static int +lookup_reg_by_dump_name (const char *name) +{ + for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (reg_names[i][0] + && ! strcmp (name, reg_names[i])) + return i; + + /* Also lookup virtuals. */ + if (!strcmp (name, "virtual-incoming-args")) + return VIRTUAL_INCOMING_ARGS_REGNUM; + if (!strcmp (name, "virtual-stack-vars")) + return VIRTUAL_STACK_VARS_REGNUM; + if (!strcmp (name, "virtual-stack-dynamic")) + return VIRTUAL_STACK_DYNAMIC_REGNUM; + if (!strcmp (name, "virtual-outgoing-args")) + return VIRTUAL_OUTGOING_ARGS_REGNUM; + if (!strcmp (name, "virtual-cfa")) + return VIRTUAL_CFA_REGNUM; + if (!strcmp (name, "virtual-preferred-stack-boundary")) + return VIRTUAL_PREFERRED_STACK_BOUNDARY_REGNUM; + /* TODO: handle "virtual-reg-%d". */ + + /* Not found. */ + return -1; +} + +/* class function_reader : public rtx_reader */ + +/* function_reader's constructor. */ + +function_reader::function_reader (function_reader_policy *policy) +: rtx_reader (), + m_policy (policy), + m_first_insn (NULL), + m_name (NULL), + m_have_cfg_directive (false), + m_have_crtl_directive (false), + m_pseudo_offset (0) +{ + bitmap_obstack_initialize (&m_bitmap_obstack); + bitmap_initialize (&m_dumped_pseudos, &m_bitmap_obstack); + bitmap_initialize (&m_bb_indices_in_insns, &m_bitmap_obstack); + bitmap_initialize (&m_bb_indices_in_cfg, &m_bitmap_obstack); + bitmap_set_bit (&m_bb_indices_in_insns, ENTRY_BLOCK); + bitmap_set_bit (&m_bb_indices_in_insns, EXIT_BLOCK); +} + +/* function_reader's destructor. */ + +function_reader::~function_reader () +{ + int i; + fixup *f; + FOR_EACH_VEC_ELT (m_fixups, i, f) + delete f; + + bitmap_obstack_release (&m_bitmap_obstack); + + free (m_name); +} + +/* Implementation of rtx_reader::handle_unknown_directive. + + Require a top-level "function" elements, as emitted by + print_rtx_function, and parse it. */ + +void +function_reader::handle_unknown_directive (file_location start_loc, + const char *name) +{ + if (strcmp (name, "function")) + fatal_at (start_loc, "expected 'function'"); + + parse_function (); +} + +/* Parse the output of print_rtx_function (or hand-written data in the + same format), having parsed the "(function" heading, and finishing + immediately before the final ")". + + The "cfg" and "crtl" clauses are optional. */ + +void +function_reader::parse_function () +{ + m_name = xstrdup (read_string (0)); + + create_function (); + + require_char_ws ('('); + require_word_ws ("insn-chain"); + parse_insn_chain (); + + create_cfg_and_basic_blocks (); + + while (1) + { + int c = read_skip_spaces (); + if (c == ')') + { + unread_char (c); + break; + } + unread_char (c); + require_char ('('); + file_location loc = get_current_location (); + struct md_name directive; + read_name (&directive); + if (strcmp (directive.string, "cfg") == 0) + parse_cfg (loc); + else if (strcmp (directive.string, "crtl") == 0) + parse_crtl (loc); + else + fatal_with_file_and_line ("missing 'cfg' or 'crtl'"); + } + + apply_fixups (); + if (!m_have_cfg_directive) + recreate_implicit_cfg_edges (); + + /* Ensure x_cur_insn_uid is 1 more than the biggest insn UID seen. + This is normally updated by the various make_*insn_raw functions. */ + { + rtx_insn *insn; + int max_uid = 0; + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + max_uid = MAX (max_uid, INSN_UID (insn)); + crtl->emit.x_cur_insn_uid = max_uid + 1; + } + + crtl->init_stack_alignment (); +} + +/* Set up state for the function *before* fixups are applied. + + Create "cfun" and a decl for the function. + By default, every function decl is hardcoded as + int test_1 (int i, int j, int k); + Set up various other state: + - the cfg and basic blocks (edges are created later, *after* fixups + are applied). + - add the function to the callgraph. */ + +void +function_reader::create_function () +{ + /* Currently we assume cfgrtl mode, rather than cfglayout mode. */ + if (0) + cfg_layout_rtl_register_cfg_hooks (); + else + rtl_register_cfg_hooks (); + + /* Create cfun. */ + tree fn_name = get_identifier (m_name ? m_name : "test_1"); + tree int_type = integer_type_node; + tree return_type = int_type; + tree arg_types[3] = {int_type, int_type, int_type}; + tree fn_type = build_function_type_array (return_type, 3, arg_types); + tree fndecl = build_decl_stat (UNKNOWN_LOCATION, FUNCTION_DECL, fn_name, + fn_type); + tree resdecl = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, + return_type); + DECL_ARTIFICIAL (resdecl) = 1; + DECL_IGNORED_P (resdecl) = 1; + DECL_RESULT (fndecl) = resdecl; + allocate_struct_function (fndecl, false); + /* This sets cfun. */ + + current_function_decl = fndecl; + + cfun->curr_properties = (PROP_cfg | PROP_rtl); + + /* Do we need this to force cgraphunit.c to output the function? */ + DECL_EXTERNAL (fndecl) = 0; + DECL_PRESERVE_P (fndecl) = 1; + + /* Add to cgraph. */ + { + /* cgraph_node::add_new_function does additional processing + based on symtab->state. We need to avoid it attempting to gimplify + things. Temporarily putting it in the PARSING state appears to + achieve this. */ + enum symtab_state old_state = symtab->state; + symtab->state = PARSING; + cgraph_node::add_new_function (fndecl, true /*lowered*/); + /* Reset the state. */ + symtab->state = old_state; + } +} + +/* Parse zero or more child insn elements within an + "insn-chain" element. */ + +void +function_reader::parse_insn_chain () +{ + while (1) + { + int c = read_skip_spaces (); + file_location loc = get_current_location (); + if (c == ')') + break; + else if (c == '(') + { + struct md_name directive; + read_name (&directive); + parse_insn (loc, directive.string); + require_char_ws (')'); + } + else + fatal_at (loc, "expected '(' or ')'"); + } +} + +/* Parse rtx instructions by calling read_rtx_code, calling + set_first_insn and set_last_insn as appropriate. */ + +void +function_reader::parse_insn (file_location start_loc, const char *name) +{ + rtx x = read_rtx_code (name); + if (!x) + fatal_at (start_loc, "expected insn type; got '%s'", name); + rtx_insn *insn = as_a (x); + set_last_insn (insn); + if (!m_first_insn) + { + m_first_insn = insn; + set_first_insn (insn); + } + m_insns_by_uid.put (INSN_UID (insn), insn); +} + +/* Create cfun's CFG and populate with blocks, a helper + function for function_reader::parse_function (). + + The edges are created later on, either by parsing "edge" directives + in the input file, or implicity from the insns, after fixups are applied. + + We can't call create_basic_block and use the regular RTL block-creation + hooks, since this creates NOTE_INSN_BASIC_BLOCK instances. We don't + want to do that; we want to use the notes we were provided with. + + The term "index" has two meanings for basic blocks in a CFG: + (a) the "index" field within struct basic_block_def. + (b) the index of a basic_block within the cfg's x_basic_block_info + vector, as accessed via BASIC_BLOCK_FOR_FN. + + These can get out-of-sync when basic blocks are optimized away. + They get back in sync by "compact_blocks". + We make the assumption that the CFG has been compacted, and + so we reconstruct cfun->cfg->x_basic_block_info->m_vecdata with NULL + values in it for any missing basic blocks, so that (a) == (b) for + all of the blocks we create. The doubly-linked list of basic + blocks (next_bb/prev_bb) skips over these "holes". */ + +void +function_reader::create_cfg_and_basic_blocks () +{ + /* Create bare-bones cfg. This creates the entry and exit blocks. */ + init_empty_tree_cfg_for_function (cfun); + ENTRY_BLOCK_PTR_FOR_FN (cfun)->flags |= BB_RTL; + EXIT_BLOCK_PTR_FOR_FN (cfun)->flags |= BB_RTL; + + /* Ensure that the vector of basic_block pointers is big enough. */ + unsigned highest_bb_idx = bitmap_last_set_bit (&m_bb_indices_in_insns); + size_t new_size = highest_bb_idx + 1; + if (basic_block_info_for_fn (cfun)->length () < new_size) + vec_safe_grow_cleared (basic_block_info_for_fn (cfun), new_size); + + /* Populate the vector. */ + unsigned bb_idx; + bitmap_iterator bi; + basic_block after = ENTRY_BLOCK_PTR_FOR_FN (cfun); + gcc_assert (after); + EXECUTE_IF_SET_IN_BITMAP (&m_bb_indices_in_insns, 0, bb_idx, bi) + { + /* The entry and exit blocks were already created above. */ + if (bb_idx == ENTRY_BLOCK || bb_idx == EXIT_BLOCK) + { + basic_block bb = BASIC_BLOCK_FOR_FN (cfun, bb_idx); + init_rtl_bb_info (bb); + continue; + } + basic_block bb = alloc_block (); + init_rtl_bb_info (bb); + bb->index = bb_idx; + bb->flags = BB_NEW | BB_RTL; + link_block (bb, after); + + n_basic_blocks_for_fn (cfun)++; + SET_BASIC_BLOCK_FOR_FN (cfun, bb_idx, bb); + BB_SET_PARTITION (bb, BB_UNPARTITIONED); + + /* Tag the block so that we know it has been used when considering + other basic block notes. */ + bb->aux = bb; + + after = bb; + } + + last_basic_block_for_fn (cfun) = highest_bb_idx + 1; +} + +/* Parse a "(cfg)" directive, having parsed the "(cfg" heading. + Consume the final ")". */ + +void +function_reader::parse_cfg (file_location loc) +{ + if (m_have_cfg_directive) + error_at (loc, "more than one 'cfg' directive"); + m_have_cfg_directive = true; + + while (1) + { + int c = read_skip_spaces (); + file_location loc = get_current_location (); + if (c == ')') + break; + else if (c == '(') + { + require_word_ws ("bb"); + parse_bb (); + } + else + fatal_at (loc, "expected '(' or ')'"); + } + + /* Creating the BBs is fiddly. For now, we rely on + create_cfg_and_basic_blocks, which works purely on the + set of BB indexes seen within the insns. + Hence we error-check to make sure the BB indexes seen + in the (cfg) is the same. + + parse_bb complains about BB indices seen in (bb) directives that + weren't seen within the (insn-chain). + + Complain here about the opposite: BB indices that were in the + (insn-chain) that were not seen in the (cfg). */ + bitmap_head just_in_insns; + bitmap_initialize (&just_in_insns, &m_bitmap_obstack); + bitmap_and_compl (&just_in_insns, &m_bb_indices_in_insns, + &m_bb_indices_in_cfg); + unsigned bb_idx; + bitmap_iterator bi; + int num_errors = 0; + EXECUTE_IF_SET_IN_BITMAP (&just_in_insns, 0, bb_idx, bi) + /* Don't worry about entry/exit. */ + if (bb_idx > 1) + { + error_at (loc, + "error: bb index %i used in (insn-chain)" + " but not listed in (cfg)", bb_idx); + num_errors++; + } + if (num_errors) + fatal_at (loc, "%i missing bb(s)", num_errors); +} + +/* Parse a "(bb)" directive, having parsed the "(bb" heading. + Consume the final ")". Call parse_edge on any "(edge)" + directives encountered. Don't actually created basic blocks; + instead, verify that the BB indices correspond to + those seen in the insn chain. */ + +void +function_reader::parse_bb () +{ + struct md_name name; + file_location loc = read_name (&name); + int bb_idx = atoi (name.string); + if (0) + fprintf (stderr, "parse_bb: %i\n", bb_idx); + bitmap_set_bit (&m_bb_indices_in_cfg, bb_idx); + + /* The bb should already have been created by + create_cfg_and_basic_blocks. */ + basic_block bb = BASIC_BLOCK_FOR_FN (cfun, bb_idx); + if (!bb) + fatal_at (loc, "error: bb index %i not referenced by insns", bb_idx); + + /* Parse 0 or more edges. */ + while (1) + { + int c = read_skip_spaces (); + file_location loc = get_current_location (); + if (c == ')') + break; + else if (c == '(') + { + require_word_ws ("edge"); + parse_edge (); + } + else + fatal_at (loc, "expected '(' or ')'"); + } +} + +/* Parse a "(edge)" directive, having parsed the "(edge" heading. + Consume the final ")". Create the edge within the CFG. */ + +void +function_reader::parse_edge () +{ + struct md_name name; + file_location loc = read_name (&name); + int src_bb_idx = atoi (name.string); + loc = read_name (&name); + int dst_bb_idx = atoi (name.string); + + /* Flags. */ + require_char_ws ('('); + require_word_ws ("flags"); + struct md_name hexval; + read_name (&hexval); + int flags = strtol (hexval.string, NULL, 16); + require_char_ws (')'); + + require_char_ws (')'); + + if (0) + fprintf (stderr, "parse_edge: %i-> %i flags 0x%x \n", + src_bb_idx, dst_bb_idx, flags); + + /* The BBs should already have been created by + create_cfg_and_basic_blocks. */ + basic_block src = BASIC_BLOCK_FOR_FN (cfun, src_bb_idx); + if (!src) + fatal_at (loc, "error: bb index %i not referenced by insns", src_bb_idx); + basic_block dst = BASIC_BLOCK_FOR_FN (cfun, dst_bb_idx); + if (!dst) + fatal_at (loc, "error: bb index %i not referenced by insns", dst_bb_idx); + unchecked_make_edge (src, dst, flags); +} + +/* Parse a "(crtl)" directive, having parsed the "(crtl" heading. + Consume the final ")". */ + +void +function_reader::parse_crtl (file_location loc) +{ + if (m_have_crtl_directive) + error_at (loc, "more than one 'crtl' directive"); + m_have_crtl_directive = true; + + /* return_rtx. */ + require_char_ws ('('); + require_word_ws ("return_rtx"); + + require_char_ws ('('); + struct md_name directive; + read_name (&directive); + crtl->return_rtx + = consolidate_singletons (read_rtx_code (directive.string)); + require_char_ws (')'); + + require_char_ws (')'); + + require_char_ws (')'); +} + +/* Overridden implementation of rtx_reader::read_rtx_operand for + function_reader, handling various extra data printed by print_rtx, + and sometimes calling the base class implementation. */ + +rtx +function_reader::read_rtx_operand (rtx return_rtx, int idx) +{ + RTX_CODE code = GET_CODE (return_rtx); + const char *format_ptr = GET_RTX_FORMAT (code); + const char format_char = format_ptr[idx]; + struct md_name name; + + /* Override the regular parser for some format codes. */ + switch (format_char) + { + case 'e': + { + if (idx == 7 && CALL_P (return_rtx)) + { + m_in_call_function_usage = true; + return rtx_reader::read_rtx_operand (return_rtx, idx); + m_in_call_function_usage = false; + } + else + return rtx_reader::read_rtx_operand (return_rtx, idx); + } + break; + + case 'u': + read_rtx_operand_u (return_rtx, idx); + /* Don't run regular parser for 'u'. */ + return return_rtx; + + case 'i': + case 'n': + read_rtx_operand_i_or_n (return_rtx, idx, format_char); + /* Don't run regular parser for these codes. */ + return return_rtx; + + case 'B': + { + file_location loc = read_name_or_nil (&name); + int bb_idx = atoi (name.string); + if (bb_idx) + add_fixup_bb (loc, return_rtx, idx, bb_idx); + /* Don't run regular parser. */ + return return_rtx; + } + break; + + default: + break; + } + + /* Call base class implementation. */ + return_rtx = rtx_reader::read_rtx_operand (return_rtx, idx); + + /* Handle any additional parsing needed to handle what the dump + could contain. */ + switch (format_char) + { + case '0': + return extra_parsing_for_operand_code_0 (return_rtx, idx); + + case 'w': + { + /* Strip away the redundant hex dump of the value. */ + require_char_ws ('['); + read_name (&name); + require_char_ws (']'); + } + break; + + case 'r': + return extra_parsing_for_operand_code_r (return_rtx); + + default: + break; + } + + return return_rtx; +} + +/* Special-cased handling of code 'u' for reading function dumps. + + The RTL file recorded the ID of an insn (or 0 for NULL); we + must store this as a pointer, but the insn might not have + been loaded yet. Store the ID away for now, via a fixup. */ + +void +function_reader::read_rtx_operand_u (rtx return_rtx, int idx) +{ + struct md_name name; + file_location loc = read_name (&name); + int insn_id = atoi (name.string); + if (insn_id) + add_fixup_insn_uid (loc, return_rtx, idx, insn_id); +} + +/* Special-cased handling of codes 'i' and 'n' for reading function + dumps. */ + +void +function_reader::read_rtx_operand_i_or_n (rtx return_rtx, int idx, + char format_char) +{ + /* Handle some of the extra information that print_rtx + can write out for these cases. */ + /* print_rtx only writes out operand 5 for notes + for NOTE_KIND values NOTE_INSN_DELETED_LABEL + and NOTE_INSN_DELETED_DEBUG_LABEL. */ + if (idx == 5 && NOTE_P (return_rtx)) + return; + + if (idx == 4 && INSN_P (return_rtx)) + { + maybe_read_location (idx, return_rtx); + return; + } + + struct md_name name; + read_name (&name); + int value; + if (format_char == 'n') + value = parse_note_insn_name (name.string); + else + value = atoi (name.string); + XINT (return_rtx, idx) = value; + + /* print-rtl.c prints the names of recognized insns, after + the insn code, wrapping them in braces. Skip them, + and reset the insn code to be unrecognized, since insn + codes are likely to change every time the .md files + change. */ + if (idx == 5 && INSN_P (return_rtx) && format_char == 'i') + if (value >= 0) + { + /* Get the content within the braces, skipping the braces. */ + require_char_ws ('{'); + char *dumped_insn_name = read_until ("}", true); + if (0) + fprintf (stderr, "got insn name: '%s'\n", dumped_insn_name); + /* For now, ignore the dumped insn name. */ + free (dumped_insn_name); + + /* Reset the insn to be unrecognized. */ + INSN_CODE (return_rtx) = -1; + } +} + +/* Additional parsing for format code '0' in dumps, handling a variety + of special-cases in print_rtx. */ + +rtx +function_reader::extra_parsing_for_operand_code_0 (rtx return_rtx, int idx) +{ + RTX_CODE code = GET_CODE (return_rtx); + int c; + struct md_name name; + + if (idx == 1 && code == SYMBOL_REF) + { + /* Possibly wrote " [flags %#x]", SYMBOL_REF_FLAGS (in_rtx). */ + c = read_skip_spaces (); + if (c == '[') + { + file_location loc = read_name (&name); + if (strcmp (name.string, "flags")) + error_at (loc, "was expecting `%s'", "flags"); + read_name (&name); + SYMBOL_REF_FLAGS (return_rtx) = strtol (name.string, NULL, 16); + + /* We can't reconstruct SYMBOL_REF_BLOCK; set it to NULL. */ + if (SYMBOL_REF_HAS_BLOCK_INFO_P (return_rtx)) + SYMBOL_REF_BLOCK (return_rtx) = NULL; + + require_char (']'); + } + else + unread_char (c); + + /* Possibly wrote: + print_node_brief (outfile, "", SYMBOL_REF_DECL (in_rtx), + dump_flags); */ + c = read_skip_spaces (); + if (c == '<') + { + /* Skip the content for now. */ + while (1) + { + char ch = read_char (); + if (ch == '>') + break; + } + } + else + unread_char (c); + } + else if (idx == 3 && code == NOTE) + { + /* Note-specific data appears for operand 3, which annoyingly + is before the enum specifying which kind of note we have + (operand 4). */ + c = read_skip_spaces (); + if (c == '[') + { + /* Possibly data for a NOTE_INSN_BASIC_BLOCK, of the form: + [bb %d]. */ + file_location bb_loc = read_name (&name); + if (strcmp (name.string, "bb")) + error_at (bb_loc, "was expecting `%s'", "bb"); + read_name (&name); + int bb_idx = atoi (name.string); + add_fixup_note_insn_basic_block (bb_loc, return_rtx, idx, + bb_idx); + require_char_ws (']'); + } + else + unread_char (c); + } + else if (idx == 7 && JUMP_P (return_rtx)) + { + c = read_skip_spaces (); + if (c != '-') + { + unread_char (c); + return return_rtx; + } + require_char ('>'); + file_location loc = read_name (&name); + add_fixup_jump_label (loc, return_rtx, idx, name.string); + } + + return return_rtx; +} + +/* Additional parsing for format code 'r' in dumps: register numbers. + The initial number has already been parsed. */ + +rtx +function_reader::extra_parsing_for_operand_code_r (rtx return_rtx) +{ + /* REGNO holds the number from the dump. */ + unsigned int dumped_regno = REGNO (return_rtx); + + /* print_rtx will print a name for hard and virtual registers + after the register number, and no name for pseudos. + + It will then print zero, one, or two sections marked by square + brackets. + + - see if there's a name after the number. If there is, assume a hard + or virtual reg, and try to parse the name: + - if the name is recognized, use the target's current number for that + name (future-proofing virtuals against .md changes) + - if the name is not recognized, issue a fatal error (it's probably a + typo, or maybe a backport from a future version of gcc, or a target + incompatibility) + - if there's no name, assume it's a pseudo. Remap all pseudos in a + postprocessing phase to ensure that they *are* pseudos (even if the .md + has gained some hard regs in the meantime), leaving the numbering + untouched if possible (the common case). */ + + int effective_regno; + + int ch = read_skip_spaces (); + unread_char (ch); + if (ch != '[' && ch != ')') + { + struct md_name name; + file_location loc = read_name (&name); + int regno_by_name = lookup_reg_by_dump_name (name.string); + if (regno_by_name == -1) + fatal_at (loc, "unrecognized register: '%s'", name.string); + effective_regno = regno_by_name; + } + else + { + /* No reg name in dump: this was a pseudo. */ + bitmap_set_bit (&m_dumped_pseudos, dumped_regno); + + /* For now, use the dumped regno, but potentially + fix it up later. */ + effective_regno = dumped_regno; + add_fixup_pseudo (get_current_location (), return_rtx); + } + + set_regno_raw (return_rtx, effective_regno, 1); + + /* Consolidate singletons. */ + return_rtx = consolidate_singletons (return_rtx); + + ORIGINAL_REGNO (return_rtx) = effective_regno; + + /* Parse extra stuff at end of 'r'. + We may have zero, one, or two sections marked by square + brackets. */ + ch = read_skip_spaces (); + bool expect_original_regno = false; + if (ch == '[') + { + file_location loc = get_current_location (); + char *desc = read_until ("]", true); + strip_trailing_whitespace (desc); + const char *desc_start = desc; + /* If ORIGINAL_REGNO (rtx) != regno, we will have: + "orig:%i", ORIGINAL_REGNO (rtx). + Consume it, we don't set ORIGINAL_REGNO, since we can + get that from the 2nd copy later. */ + if (0 == strncmp (desc, "orig:", 5)) + { + expect_original_regno = true; + desc_start += 5; + /* Skip to any whitespace following the integer. */ + const char *space = strchr (desc_start, ' '); + if (space) + desc_start = space + 1; + } + /* Any remaining text may be the REG_EXPR. Alternatively we have + no REG_ATTRS, and instead we have ORIGINAL_REGNO. */ + if (ISDIGIT (*desc_start)) + { + /* Assume we have ORIGINAL_REGNO. */ + ORIGINAL_REGNO (return_rtx) = atoi (desc_start); + } + else + { + /* Assume we have REG_EXPR. */ + add_fixup_expr (loc, return_rtx, desc_start); + } + free (desc); + } + else + unread_char (ch); + if (expect_original_regno) + { + require_char_ws ('['); + char *desc = read_until ("]", true); + ORIGINAL_REGNO (return_rtx) = atoi (desc); + free (desc); + } + + return return_rtx; +} + +/* Implementation of rtx_reader::handle_any_trailing_information. + Handle the various additional information that print-rtl.c can + write after the regular fields. */ + +void +function_reader::handle_any_trailing_information (rtx return_rtx) +{ + struct md_name name; + + switch (GET_CODE (return_rtx)) + { + case MEM: + { + int ch; + require_char_ws ('['); + read_name (&name); + MEM_ALIAS_SET (return_rtx) = atoi (name.string); + /* We have either a MEM_EXPR, or a space. */ + if (peek_char () != ' ') + { + file_location loc = get_current_location (); + char *desc = read_until (" +", false); + add_fixup_expr (loc, consolidate_singletons (return_rtx), desc); + free (desc); + } + else + read_char (); + + /* We may optionally have '+' for MEM_OFFSET_KNOWN_P. */ + ch = read_skip_spaces (); + if (ch == '+') + { + read_name (&name); + MEM_OFFSET_KNOWN_P (return_rtx) = 1; + MEM_OFFSET (return_rtx) = atoi (name.string); + } + else + unread_char (ch); + + /* Handle optional " S" for MEM_SIZE. */ + ch = read_skip_spaces (); + if (ch == 'S') + { + read_name (&name); + MEM_SIZE (return_rtx) = atoi (name.string); + } + else + unread_char (ch); + + /* Handle optional " A" for MEM_ALIGN. */ + ch = read_skip_spaces (); + if (ch == 'A' && peek_char () != 'S') + { + read_name (&name); + MEM_ALIGN (return_rtx) = atoi (name.string); + } + + /* Handle optional " AS" for MEM_ADDR_SPACE. */ + ch = read_skip_spaces (); + if (ch == 'A' && peek_char () == 'S') + { + read_char (); + read_name (&name); + MEM_ADDR_SPACE (return_rtx) = atoi (name.string); + } + else + unread_char (ch); + + require_char (']'); + } + break; + + case CODE_LABEL: + { + /* Parse LABEL_NUSES. */ + require_char_ws ('['); + read_name (&name); + LABEL_NUSES (return_rtx) = atoi (name.string); + require_word_ws ("uses"); + require_char_ws (']'); + /* TODO: parse LABEL_KIND. */ + /* For now, skip until closing ')'. */ + do + { + char ch = read_char (); + if (ch == ')') + { + unread_char (ch); + break; + } + } + while (1); + } + break; + + default: + break; + } +} + +/* Parse a tree dump for MEM_EXPR and turn it back into a tree. + We handle "", but for anything else we "cheat" by building a + global VAR_DECL of type "int" with that name (returning the same global + for a name if we see the same name more than once). */ + +tree +function_reader::parse_mem_expr (const char *desc) +{ + tree fndecl = cfun->decl; + + if (0 == strcmp (desc, "")) + { + return DECL_RESULT (fndecl); + } + + /* Search within function parms. */ + for (tree arg = DECL_ARGUMENTS (fndecl); arg; arg = TREE_CHAIN (arg)) + { + if (strcmp (desc, IDENTIFIER_POINTER (DECL_NAME (arg))) == 0) + return arg; + } + + /* Search within decls we already created. + FIXME: use a hash rather than linear search. */ + int i; + tree t; + FOR_EACH_VEC_ELT (m_fake_scope, i, t) + if (strcmp (desc, IDENTIFIER_POINTER (DECL_NAME (t))) == 0) + return t; + + /* Not found? Create it. + This allows mimicing of real data but avoids having to specify + e.g. names of locals, params etc. + Though this way we don't know if we have a PARM_DECL vs a VAR_DECL, + and we don't know the types. Fake it by making everything be + a VAR_DECL of "int" type. */ + t = build_decl (UNKNOWN_LOCATION, VAR_DECL, + get_identifier (desc), + integer_type_node); + m_fake_scope.safe_push (t); + return t; +} + +/* Record the information for later post-processing. */ + +void +function_reader::add_fixup_insn_uid (file_location loc, rtx insn, int operand_idx, + int insn_uid) +{ + m_fixups.safe_push (new fixup_insn_uid (loc, insn, operand_idx, insn_uid)); +} + +/* Record the information for later post-processing. */ + +void +function_reader::add_fixup_bb (file_location loc, rtx insn, int operand_idx, + int bb_idx) +{ + bitmap_set_bit (&m_bb_indices_in_insns, bb_idx); + m_fixups.safe_push (new fixup_bb (loc, insn, operand_idx, bb_idx)); +} + +/* Record the information for later post-processing. */ + +void +function_reader::add_fixup_note_insn_basic_block (file_location loc, rtx insn, + int operand_idx, int bb_idx) +{ + m_fixups.safe_push (new fixup_note_insn_basic_block (loc, insn, operand_idx, + bb_idx)); +} + +/* Record the information for later post-processing. */ +void +function_reader::add_fixup_source_location (file_location, rtx, + int, const char *, int) +{ + /* Empty for now. */ +} + +/* Record the information for later post-processing. */ + +void +function_reader::add_fixup_jump_label (file_location loc, rtx insn, + int operand_idx, + const char *label) +{ + m_fixups.safe_push (new fixup_jump_label (loc, insn, operand_idx, label)); +} + +/* Record the information for later post-processing. */ + +void +function_reader::add_fixup_expr (file_location loc, rtx insn, + const char *desc) +{ + gcc_assert (desc); + /* Fail early if the RTL reader erroneously hands us an int. */ + gcc_assert (!ISDIGIT (desc[0])); + + m_fixups.safe_push (new fixup_expr (loc, insn, desc)); +} + +/* Record the information for later post-processing. */ + +void +function_reader::add_fixup_pseudo (file_location loc, rtx x) +{ + m_fixups.safe_push (new fixup_pseudo (loc, x)); +} + +/* Helper function for consolidate_reg. */ + +static rtx +lookup_global_register (int regno) +{ + /* We can't use a switch here, as some of the REGNUMs might not be constants + for some targets. */ + if (regno == STACK_POINTER_REGNUM) + return stack_pointer_rtx; + else if (regno == FRAME_POINTER_REGNUM) + return frame_pointer_rtx; + else if (regno == HARD_FRAME_POINTER_REGNUM) + return hard_frame_pointer_rtx; + else if (regno == ARG_POINTER_REGNUM) + return arg_pointer_rtx; + else if (regno == VIRTUAL_INCOMING_ARGS_REGNUM) + return virtual_incoming_args_rtx; + else if (regno == VIRTUAL_STACK_VARS_REGNUM) + return virtual_stack_vars_rtx; + else if (regno == VIRTUAL_STACK_DYNAMIC_REGNUM) + return virtual_stack_dynamic_rtx; + else if (regno == VIRTUAL_OUTGOING_ARGS_REGNUM) + return virtual_outgoing_args_rtx; + else if (regno == VIRTUAL_CFA_REGNUM) + return virtual_cfa_rtx; + else if (regno == VIRTUAL_PREFERRED_STACK_BOUNDARY_REGNUM) + return virtual_preferred_stack_boundary_rtx; +#ifdef return_ADDRESS_POINTER_REGNUM + else if (regno == RETURN_ADDRESS_POINTER_REGNUM) + return return_address_pointer_rtx; +#endif + + return NULL; +} + +/* Normally REG instances are created by gen_reg_rtx which updates + regno_reg_rtx, growing it as necessary. + The REG instances created from the dumpfile weren't created this + way, so we need to manually update regno_reg_rtx. */ + +static void +ensure_regno (int regno) +{ + crtl->emit.ensure_regno_capacity (regno + 1); + gcc_assert (regno < crtl->emit.regno_pointer_align_length); + + if (reg_rtx_no < regno + 1) + reg_rtx_no = regno + 1; +} + +/* Helper function for consolidate_singletons, for handling REG instances. */ + +static rtx +consolidate_reg (rtx x) +{ + gcc_assert (GET_CODE (x) == REG); + + unsigned int regno = REGNO (x); + + ensure_regno (regno); + + /* Some register numbers have their rtx created in init_emit_regs + e.g. stack_pointer_rtx for STACK_POINTER_REGNUM. + Consolidate on this. */ + rtx global_reg = lookup_global_register (regno); + if (global_reg) + return global_reg; + + /* Populate regno_reg_rtx if necessary. */ + if (regno_reg_rtx[regno] == NULL) + regno_reg_rtx[regno] = x; + /* Use it. */ + gcc_assert (GET_CODE (regno_reg_rtx[regno]) == REG); + gcc_assert (REGNO (regno_reg_rtx[regno]) == regno); + if (GET_MODE (x) == GET_MODE (regno_reg_rtx[regno])) + return regno_reg_rtx[regno]; + + return x; +} + +/* When reading RTL function dumps, we must consolidate some + rtx so that we use singletons where singletons are expected + (e.g. we don't want multiple "(const_int 0 [0])" rtx, since + these are tested via pointer equality against const0_rtx. */ + +rtx +function_reader::consolidate_singletons (rtx x) +{ + if (!x) + return x; + + switch (GET_CODE (x)) + { + /* FIXME: do we need to check for VOIDmode for these? */ + case PC: return pc_rtx; + case RETURN: return ret_rtx; + case SIMPLE_RETURN: return simple_return_rtx; + case CC0: return cc0_rtx; + + case REG: + return consolidate_reg (x); + + case CONST_INT: + return gen_rtx_CONST_INT (GET_MODE (x), INTVAL (x)); + + default: + break; + } + + return x; +} + +/* Implementation of rtx_reader::postprocess for reading function dumps. */ + +rtx +function_reader::postprocess (rtx x) +{ + return consolidate_singletons (x); +} + +/* Handle the optional location information written by print_rtx for + instructions. Specifically, operand 4 of instructions (of type "i') + is printed thus: + + if (INSN_HAS_LOCATION (in_insn)) + { + expanded_location xloc = insn_location (in_insn); + fprintf (outfile, " %s:%i", xloc.file, xloc.line); + } + + Hence we need to speculatively read a location of the form + " %s:%i", and unread the content if there wasn't one. + + Assume that filenames can't contain whitespace, and can't + contain ':'. */ + +void +function_reader::maybe_read_location (int operand_idx, rtx insn) +{ + file_location loc = get_current_location (); + + /* Skip to first non-whitespace. */ + int ch = read_skip_spaces (); + auto_vec buf; + buf.safe_push (ch); + while (1) + { + int ch = read_char (); + /* If we see a ':', assume we have a filename. */ + if (ch == ':') + { + buf.safe_push ('\0'); + break; + } + buf.safe_push (ch); + + /* If we see a space before ':', assume we don't have a + filename. */ + if (ISSPACE (ch)) + { + while (!buf.is_empty ()) + unread_char (buf.pop ()); + return; + } + } + char *filename = buf.address (); + struct md_name name; + read_name (&name); + + add_fixup_source_location (loc, insn, operand_idx, + filename, atoi(name.string)); +} + +/* Apply all of the recorded fixups. */ + +void +function_reader::apply_fixups () +{ + /* Set up m_pseudo_offset so that the lowest REGNO seen for a pseudo in + the dump will be a pseudo after any fixup_pseudo instances are applied, + preferring to leave it as zero if possible (to avoid renumbering). */ + if (!bitmap_empty_p (&m_dumped_pseudos)) + { + int lowest_pseudo_in_dump = bitmap_first_set_bit (&m_dumped_pseudos); + if (lowest_pseudo_in_dump < LAST_VIRTUAL_REGISTER + 1) + { + m_pseudo_offset = (LAST_VIRTUAL_REGISTER + 1) - lowest_pseudo_in_dump; + + /* We may need to resize the regno-based arrays. */ + int highest_pseudo_in_dump = bitmap_last_set_bit (&m_dumped_pseudos); + int highest_effective_pseudo = highest_pseudo_in_dump + m_pseudo_offset; + ensure_regno (highest_effective_pseudo); + } + } + + int i; + fixup *f; + FOR_EACH_VEC_ELT (m_fixups, i, f) + f->apply (this); +} + +/* Given a UID value, try to locate a pointer to the corresponding + rtx_insn *, or NULL if if can't be found. */ + +rtx_insn ** +function_reader::get_insn_by_uid (int uid) +{ + return m_insns_by_uid.get (uid); +} + +/* Visit every LABEL_REF within JUMP_INSN, calling CB on it, providing it + with USER_DATA. + FIXME: is there a preexisting way to do this? */ + +static void +for_each_label_ref (rtx_jump_insn *jump_insn, + void (*cb) (rtx_jump_insn *jump_insn, + rtx label_ref, + void *user_data), + void *user_data) +{ + if (any_condjump_p (jump_insn)) + { + rtx label_ref = condjump_label (jump_insn); + cb (jump_insn, label_ref, user_data); + return; + } + + if (simplejump_p (jump_insn)) + { + rtx label_ref = SET_SRC (PATTERN (jump_insn)); + cb (jump_insn, label_ref, user_data); + return; + } + + rtx_jump_table_data *tablep; + if (tablejump_p (jump_insn, NULL, &tablep)) + { + gcc_assert (tablep); + rtvec labels = tablep->get_labels (); + int i, veclen = GET_NUM_ELEM (labels); + for (i = 0; i < veclen; ++i) + { + rtx label_ref = RTVEC_ELT (labels, i); + cb (jump_insn, label_ref, user_data); + } + return; + } + + if (ANY_RETURN_P (PATTERN (jump_insn))) + return; + + /* TODO: something else? */ + gcc_unreachable (); +} + +/* Create an edge from SRC to DST, with the given flags. */ + +static void +add_edge (basic_block src, basic_block dst, int flags) +{ + if (0) + fprintf (stderr, "making edge %i->%i\n", + src->index, dst->index); + unchecked_make_edge (src, dst, flags); +} + +/* Edge-creation callback for function_reader::create_cfg_edges. */ + +static void +add_edge_cb (rtx_jump_insn *jump_insn, rtx label_ref, void */*user_data*/) +{ + gcc_assert (jump_insn); + gcc_assert (label_ref); + rtx_insn *label_insn = as_a (LABEL_REF_LABEL (label_ref)); + add_edge (BLOCK_FOR_INSN (jump_insn), BLOCK_FOR_INSN (label_insn), 0); +} + +/* Create edges within cfun's CFG, by examining instructions in the + basic blocks and reconstructing the edges accordingly. + This is done after fixups are applied, since the latter is responsible + for setting up BB_HEAD and BB_END within each basic block. + + This assumes cfgrtl mode, in which the edges are implicit from + the jump instructions. It won't work for cfglayout mode, which + represents unconditional jumps purely as edges within the CFG, + without instructions, and this information isn't (currently) + written out to dumps. */ + +void +function_reader::recreate_implicit_cfg_edges () +{ + /* Create edge from ENTRY_BLOCK to block of first insn. */ + basic_block entry = ENTRY_BLOCK_PTR_FOR_FN (cfun); + add_edge (entry, entry->next_bb, EDGE_FALLTHRU); + + /* Create other edges. + + The edge from the block of last insn to EXIT_BLOCK is created + below, by fall-through from the end of its block. */ + basic_block bb; + FOR_ALL_BB_FN (bb, cfun) + { + if (!BB_HEAD (bb)) + continue; + rtx_insn *end_insn = BB_END (bb); + if (rtx_jump_insn *jump_insn = dyn_cast (end_insn)) + { + if (0) + fprintf (stderr, "bb %i ends in jump\n", bb->index); + if (!any_uncondjump_p (end_insn)) + { + /* Add fallthrough edge first. */ + gcc_assert (bb->next_bb); + add_edge (bb, bb->next_bb, EDGE_FALLTHRU); + } + for_each_label_ref (jump_insn, add_edge_cb, NULL); + } + else + { + if (0) + fprintf (stderr, "bb %i ends in non-jump\n", bb->index); + if (bb->next_bb != NULL) + { + /* Add fallthrough edge. */ + gcc_assert (bb->next_bb); + add_edge (bb, bb->next_bb, EDGE_FALLTHRU); + } + } + } +} + +/* Run the RTL dump parser. If OUT_PSEUDO_OFFSET is non-NULL, + write any offset applied to the regnos of pseudoes to + *OUT_PSEUDO_OFFSET. */ + +bool +read_rtl_function_body (int argc, const char **argv, + bool (*parse_opt) (const char *), + function_reader_policy *policy, + int *out_pseudo_offset) +{ + initialize_rtl (); + init_emit (); + init_varasm_status (); + + function_reader reader (policy); + if (!reader.read_md_files (argc, argv, parse_opt)) + return false; + + if (out_pseudo_offset) + *out_pseudo_offset = reader.get_pseudo_offset (); + + return true; +} + +#if CHECKING_P + +namespace selftest { + +/* Verify that the src and dest indices and flags of edge E are as + expected, using LOC as the effective location when reporting + failures. */ + +static void +assert_edge_at (const location &loc, edge e, int expected_src_idx, + int expected_dest_idx, int expected_flags) +{ + ASSERT_EQ_AT (loc, expected_src_idx, e->src->index); + ASSERT_EQ_AT (loc, expected_dest_idx, e->dest->index); + ASSERT_EQ_AT (loc, expected_flags, e->flags); +} + +/* Verify that the src and dest indices and flags of EDGE are as + expected. */ + +#define ASSERT_EDGE(EDGE, EXPECTED_SRC_IDX, EXPECTED_DEST_IDX, \ + EXPECTED_FLAGS) \ + assert_edge_at (SELFTEST_LOCATION, EDGE, EXPECTED_SRC_IDX, \ + EXPECTED_DEST_IDX, EXPECTED_FLAGS) + +/* Verify that we can load RTL dumps. */ + +static void +test_loading_dump_fragment_1 () +{ + // TODO: filter on target? + rtl_dump_test t (SELFTEST_LOCATION, locate_file ("asr_div1.rtl")); + + /* Verify that the insns were loaded correctly. */ + rtx_insn *insn_8 = get_insns (); + ASSERT_TRUE (insn_8); + ASSERT_EQ (8, INSN_UID (insn_8)); + ASSERT_EQ (INSN, GET_CODE (insn_8)); + ASSERT_EQ (SET, GET_CODE (PATTERN (insn_8))); + ASSERT_EQ (NULL, PREV_INSN (insn_8)); + + rtx_insn *insn_9 = NEXT_INSN (insn_8); + ASSERT_TRUE (insn_9); + ASSERT_EQ (9, INSN_UID (insn_9)); + ASSERT_EQ (INSN, GET_CODE (insn_9)); + ASSERT_EQ (insn_8, PREV_INSN (insn_9)); + ASSERT_EQ (NULL, NEXT_INSN (insn_9)); + + /* Verify that registers were remapped. */ + rtx insn_8_dest = SET_DEST (PATTERN (insn_8)); + ASSERT_EQ (REG, GET_CODE (insn_8_dest)); + ASSERT_EQ (t.effective_regno (78), REGNO (insn_8_dest)); + rtx insn_8_src = SET_SRC (PATTERN (insn_8)); + ASSERT_EQ (LSHIFTRT, GET_CODE (insn_8_src)); + rtx reg = XEXP (insn_8_src, 0); + ASSERT_EQ (REG, GET_CODE (reg)); + ASSERT_EQ (t.effective_regno (76), REGNO (reg)); + + /* Verify that get_insn_by_uid works. */ + ASSERT_EQ (insn_8, get_insn_by_uid (8)); + ASSERT_EQ (insn_9, get_insn_by_uid (9)); + + /* Verify that basic blocks were created. */ + ASSERT_EQ (2, BLOCK_FOR_INSN (insn_8)->index); + ASSERT_EQ (2, BLOCK_FOR_INSN (insn_9)->index); + + /* Verify that a sane CFG was created, with the implicit edges being + recreated. */ + ASSERT_TRUE (cfun); + verify_three_block_rtl_cfg (cfun); + basic_block bb2 = BASIC_BLOCK_FOR_FN (cfun, 2); + ASSERT_TRUE (bb2 != NULL); + ASSERT_EQ (BB_RTL, bb2->flags & BB_RTL); + ASSERT_EQ (2, bb2->index); + ASSERT_EQ (insn_8, BB_HEAD (bb2)); + ASSERT_EQ (insn_9, BB_END (bb2)); +} + +/* Verify loading another RTL dump; this time a dump of copying + a param on x86_64 from a hard reg into the frame. */ + +static void +test_loading_dump_fragment_2 () +{ + /* Only run this test for i386, since it hardcodes a hard reg name. */ +#ifndef I386_OPTS_H + return; +#endif + + rtl_dump_test t (SELFTEST_LOCATION, + locate_file ("copy-hard-reg-into-frame.rtl")); + + rtx_insn *insn = get_insn_by_uid (2); + + /* The block structure and indentation here is purely for + readability; it mirrors the structure of the rtx. */ + tree mem_expr; + { + rtx pat = PATTERN (insn); + ASSERT_EQ (SET, GET_CODE (pat)); + { + rtx dest = SET_DEST (pat); + ASSERT_EQ (MEM, GET_CODE (dest)); + /* Verify the "/c" was parsed. */ + ASSERT_TRUE (RTX_FLAG (dest, call)); + ASSERT_EQ (SImode, GET_MODE (dest)); + { + rtx addr = XEXP (dest, 0); + ASSERT_EQ (PLUS, GET_CODE (addr)); + ASSERT_EQ (DImode, GET_MODE (addr)); + { + rtx lhs = XEXP (addr, 0); + ASSERT_EQ (REG, GET_CODE (lhs)); + /* Verify the "/f" was parsed. */ + ASSERT_TRUE (RTX_FLAG (lhs, frame_related)); + ASSERT_EQ (DImode, GET_MODE (lhs)); + } + { + rtx rhs = XEXP (addr, 1); + ASSERT_EQ (CONST_INT, GET_CODE (rhs)); + ASSERT_EQ (-4, INTVAL (rhs)); + } + } + /* Verify the "[1 i+0 S4 A32]" was parsed. */ + ASSERT_EQ (1, MEM_ALIAS_SET (dest)); + /* "i" should have been handled by synthesizing a global int + variable named "i". */ + mem_expr = MEM_EXPR (dest); + ASSERT_NE (mem_expr, NULL); + ASSERT_EQ (VAR_DECL, TREE_CODE (mem_expr)); + ASSERT_EQ (integer_type_node, TREE_TYPE (mem_expr)); + ASSERT_EQ (IDENTIFIER_NODE, TREE_CODE (DECL_NAME (mem_expr))); + ASSERT_STREQ ("i", IDENTIFIER_POINTER (DECL_NAME (mem_expr))); + /* "+0". */ + ASSERT_TRUE (MEM_OFFSET_KNOWN_P (dest)); + ASSERT_EQ (0, MEM_OFFSET (dest)); + /* "S4". */ + ASSERT_EQ (4, MEM_SIZE (dest)); + /* "A32. */ + ASSERT_EQ (32, MEM_ALIGN (dest)); + } + { + rtx src = SET_SRC (pat); + ASSERT_EQ (REG, GET_CODE (src)); + ASSERT_EQ (SImode, GET_MODE (src)); + ASSERT_EQ (5, REGNO (src)); + tree reg_expr = REG_EXPR (src); + /* "i" here should point to the same var as for the MEM_EXPR. */ + ASSERT_EQ (reg_expr, mem_expr); + } + } +} + +/* Verify that CODE_LABEL insns are loaded correctly. */ + +static void +test_loading_labels () +{ + rtl_dump_test t (SELFTEST_LOCATION, locate_file ("example-labels.rtl")); + + rtx_insn *insn_1 = get_insn_by_uid (1); + ASSERT_EQ (CODE_LABEL, GET_CODE (insn_1)); + ASSERT_EQ (NULL, LABEL_NAME (insn_1)); + ASSERT_EQ (0, LABEL_NUSES (insn_1)); + + rtx_insn *insn_2 = get_insn_by_uid (2); + ASSERT_EQ (CODE_LABEL, GET_CODE (insn_2)); + ASSERT_STREQ ("some_label_name", LABEL_NAME (insn_2)); + ASSERT_EQ (57, LABEL_NUSES (insn_2)); + + /* Ensure that label names read from a dump are GC-managed + and are found through the insn. */ + forcibly_ggc_collect (); + ASSERT_TRUE (ggc_marked_p (insn_2)); + ASSERT_TRUE (ggc_marked_p (LABEL_NAME (insn_2))); +} + +/* Verify that the loader copes with an insn with a mode. */ + +static void +test_loading_insn_with_mode () +{ + rtl_dump_test t (SELFTEST_LOCATION, locate_file ("insn-with-mode.rtl")); + rtx_insn *insn = get_insn_by_uid (31); + ASSERT_EQ (INSN, GET_CODE (insn)); + + /* Verify that the "TI" mode was set from "insn:TI". */ + ASSERT_EQ (TImode, GET_MODE (insn)); +} + +/* Verify that the loader copes with a jump_insn to a label_ref. */ + +static void +test_loading_jump_to_label_ref () +{ + rtl_dump_test t (SELFTEST_LOCATION, locate_file ("jump-to-label-ref.rtl")); + + rtx_insn *jump_insn = get_insn_by_uid (1); + ASSERT_EQ (JUMP_INSN, GET_CODE (jump_insn)); + + rtx_insn *barrier = get_insn_by_uid (2); + ASSERT_EQ (BARRIER, GET_CODE (barrier)); + + rtx_insn *code_label = get_insn_by_uid (3); + ASSERT_EQ (CODE_LABEL, GET_CODE (code_label)); + + /* Verify the jump_insn. */ + ASSERT_EQ (4, BLOCK_FOR_INSN (jump_insn)->index); + ASSERT_EQ (SET, GET_CODE (PATTERN (jump_insn))); + /* Ensure that the "(pc)" is using the global singleton. */ + ASSERT_EQ (pc_rtx, SET_DEST (PATTERN (jump_insn))); + // FIXME: ^^^ use ASSERT_RTX_PTR_EQ here ^^^ + rtx label_ref = SET_SRC (PATTERN (jump_insn)); + ASSERT_EQ (LABEL_REF, GET_CODE (label_ref)); + ASSERT_EQ (code_label, LABEL_REF_LABEL (label_ref)); + ASSERT_EQ (code_label, JUMP_LABEL (jump_insn)); + + /* Verify the code_label. */ + ASSERT_EQ (5, BLOCK_FOR_INSN (code_label)->index); + ASSERT_EQ (NULL, LABEL_NAME (code_label)); + ASSERT_EQ (1, LABEL_NUSES (code_label)); + + /* Verify the generated CFG. */ + + /* Locate blocks. */ + basic_block entry = ENTRY_BLOCK_PTR_FOR_FN (cfun); + ASSERT_TRUE (entry != NULL); + ASSERT_EQ (ENTRY_BLOCK, entry->index); + + basic_block exit = EXIT_BLOCK_PTR_FOR_FN (cfun); + ASSERT_TRUE (exit != NULL); + ASSERT_EQ (EXIT_BLOCK, exit->index); + + basic_block bb4 = (*cfun->cfg->x_basic_block_info)[4]; + basic_block bb5 = (*cfun->cfg->x_basic_block_info)[5]; + ASSERT_EQ (4, bb4->index); + ASSERT_EQ (5, bb5->index); + + /* Entry block. */ + ASSERT_EQ (NULL, entry->preds); + ASSERT_EQ (1, entry->succs->length ()); + ASSERT_EDGE ((*entry->succs)[0], 0, 4, EDGE_FALLTHRU); + + /* bb4. */ + ASSERT_EQ (1, bb4->preds->length ()); + ASSERT_EDGE ((*bb4->preds)[0], 0, 4, EDGE_FALLTHRU); + ASSERT_EQ (1, bb4->succs->length ()); + ASSERT_EDGE ((*bb4->succs)[0], 4, 5, 0x0); + + /* bb5. */ + ASSERT_EQ (1, bb5->preds->length ()); + ASSERT_EDGE ((*bb5->preds)[0], 4, 5, 0x0); + ASSERT_EQ (1, bb5->succs->length ()); + ASSERT_EDGE ((*bb5->succs)[0], 5, 1, EDGE_FALLTHRU); + + /* Exit block. */ + ASSERT_EQ (1, exit->preds->length ()); + ASSERT_EDGE ((*exit->preds)[0], 5, 1, EDGE_FALLTHRU); + ASSERT_EQ (NULL, exit->succs); +} + +/* Verify that the loader copes with a jump_insn to a label_ref + marked "return". */ + +static void +test_loading_jump_to_return () +{ + rtl_dump_test t (SELFTEST_LOCATION, locate_file ("jump-to-return.rtl")); + + rtx_insn *jump_insn = get_insn_by_uid (1); + ASSERT_EQ (JUMP_INSN, GET_CODE (jump_insn)); + ASSERT_EQ (ret_rtx, JUMP_LABEL (jump_insn)); + // FIXME: ^^^ use ASSERT_RTX_PTR_EQ here ^^^ +} + +/* Verify that the loader copes with a jump_insn to a label_ref + marked "simple_return". */ + +static void +test_loading_jump_to_simple_return () +{ + rtl_dump_test t (SELFTEST_LOCATION, + locate_file ("jump-to-simple-return.rtl")); + + rtx_insn *jump_insn = get_insn_by_uid (1); + ASSERT_EQ (JUMP_INSN, GET_CODE (jump_insn)); + ASSERT_EQ (simple_return_rtx, JUMP_LABEL (jump_insn)); + // FIXME: ^^^ use ASSERT_RTX_PTR_EQ here ^^^ +} + +/* Verify that the loader copes with a NOTE_INSN_BASIC_BLOCK. */ + +static void +test_loading_note_insn_basic_block () +{ + rtl_dump_test t (SELFTEST_LOCATION, + locate_file ("note_insn_basic_block.rtl")); + + rtx_insn *note = get_insn_by_uid (1); + ASSERT_EQ (NOTE, GET_CODE (note)); + ASSERT_EQ (2, BLOCK_FOR_INSN (note)->index); + + ASSERT_EQ (NOTE_INSN_BASIC_BLOCK, NOTE_KIND (note)); + ASSERT_EQ (2, NOTE_BASIC_BLOCK (note)->index); + ASSERT_EQ (BASIC_BLOCK_FOR_FN (cfun, 2), NOTE_BASIC_BLOCK (note)); +} + +/* Verify that the loader copes with a NOTE_INSN_DELETED. */ + +static void +test_loading_note_insn_deleted () +{ + rtl_dump_test t (SELFTEST_LOCATION, locate_file ("note-insn-deleted.rtl")); + + rtx_insn *note = get_insn_by_uid (1); + ASSERT_EQ (NOTE, GET_CODE (note)); + ASSERT_EQ (NOTE_INSN_DELETED, NOTE_KIND (note)); +} + +/* Verify that the const_int values are consolidated, since + pointer equality corresponds to value equality. + TODO: do this for all in CASE_CONST_UNIQUE. */ + +static void +test_loading_const_int () +{ + rtl_dump_test t (SELFTEST_LOCATION, locate_file ("const-int.rtl")); + + /* Verify that const_int values below MAX_SAVED_CONST_INT use + the global values. */ + ASSERT_EQ (const0_rtx, SET_SRC (PATTERN (get_insn_by_uid (100)))); + ASSERT_EQ (const1_rtx, SET_SRC (PATTERN (get_insn_by_uid (101)))); + ASSERT_EQ (constm1_rtx, SET_SRC (PATTERN (get_insn_by_uid (102)))); + + /* Verify that other const_int values are consolidated. */ + rtx int256 = gen_rtx_CONST_INT (SImode, 256); + ASSERT_EQ (int256, SET_SRC (PATTERN (get_insn_by_uid (103)))); +} + +/* Verify that the loader copes with a SYMBOL_REF. */ + +static void +test_loading_symbol_ref () +{ + rtl_dump_test t (SELFTEST_LOCATION, locate_file ("symbol-ref.rtl")); + + rtx_insn *insn = get_insn_by_uid (1045); + + rtx high = SET_SRC (PATTERN (insn)); + ASSERT_EQ (HIGH, GET_CODE (high)); + + rtx symbol_ref = XEXP (high, 0); + ASSERT_EQ (SYMBOL_REF, GET_CODE (symbol_ref)); + + /* Verify that "[flags 0xc0]" was parsed. */ + ASSERT_EQ (0xc0, SYMBOL_REF_FLAGS (symbol_ref)); + /* TODO: we don't yet load SYMBOL_REF_DECL. */ +} + +/* Verify that the loader copes with a call_insn dump. */ + +static void +test_loading_call_insn_x86_64 () +{ + /* Only run this test for i386. */ +#ifndef I386_OPTS_H + return; +#endif + + rtl_dump_test t (SELFTEST_LOCATION, locate_file ("x86_64/call-insn.rtl")); + + rtx_insn *insn_17 = get_insn_by_uid (17); + ASSERT_EQ (CALL_INSN, GET_CODE (insn_17)); + + /* "/j". */ + ASSERT_TRUE (RTX_FLAG (insn_17, jump)); + + rtx pat = PATTERN (insn_17); + ASSERT_EQ (CALL, GET_CODE (SET_SRC (pat))); + + /* Verify REG_NOTES. */ + { + /* "(expr_list:REG_CALL_DECL". */ + ASSERT_EQ (EXPR_LIST, GET_CODE (REG_NOTES (insn_17))); + rtx_expr_list *note0 = as_a (REG_NOTES (insn_17)); + ASSERT_EQ (REG_CALL_DECL, REG_NOTE_KIND (note0)); + + /* "(expr_list:REG_EH_REGION (const_int 0 [0])". */ + rtx_expr_list *note1 = note0->next (); + ASSERT_EQ (REG_EH_REGION, REG_NOTE_KIND (note1)); + + ASSERT_EQ (NULL, note1->next ()); + } + + /* Verify CALL_INSN_FUNCTION_USAGE. */ + { + /* "(expr_list:DF (use (reg:DF 21 xmm0))". */ + rtx_expr_list *usage + = as_a (CALL_INSN_FUNCTION_USAGE (insn_17)); + ASSERT_EQ (EXPR_LIST, GET_CODE (usage)); + ASSERT_EQ (DFmode, GET_MODE (usage)); + ASSERT_EQ (USE, GET_CODE (usage->element ())); + ASSERT_EQ (NULL, usage->next ()); + } +} + +/* Verify that the loader copes a dump from print_rtx_function. */ + +static void +test_loading_full_dump_aarch64 () +{ + /* Only run this test for target==aarch64. */ +#ifndef GCC_AARCH64_H + return; +#endif + + rtl_dump_test t (SELFTEST_LOCATION, locate_file ("aarch64/times-two.rtl")); + + ASSERT_STREQ ("times_two", IDENTIFIER_POINTER (DECL_NAME (cfun->decl))); + + rtx_insn *insn_1 = get_insn_by_uid (1); + ASSERT_EQ (NOTE, GET_CODE (insn_1)); + + rtx_insn *insn_15 = get_insn_by_uid (15); + ASSERT_EQ (INSN, GET_CODE (insn_15)); + ASSERT_EQ (USE, GET_CODE (PATTERN (insn_15))); + + /* Verify crtl->return_rtx. */ + ASSERT_EQ (REG, GET_CODE (crtl->return_rtx)); + ASSERT_EQ (0, REGNO (crtl->return_rtx)); + ASSERT_EQ (SImode, GET_MODE (crtl->return_rtx)); +} + +/* Verify that the loader copes a dump from print_rtx_function. */ + +static void +test_loading_full_dump_x86_64 () +{ + /* Only run this test for i386. */ +#ifndef I386_OPTS_H + return; +#endif + + rtl_dump_test t (SELFTEST_LOCATION, locate_file ("x86_64/times-two.rtl")); + + ASSERT_STREQ ("times_two", IDENTIFIER_POINTER (DECL_NAME (cfun->decl))); + + rtx_insn *insn_1 = get_insn_by_uid (1); + ASSERT_EQ (NOTE, GET_CODE (insn_1)); + + rtx_insn *insn_7 = get_insn_by_uid (7); + ASSERT_EQ (INSN, GET_CODE (insn_7)); + ASSERT_EQ (PARALLEL, GET_CODE (PATTERN (insn_7))); + + rtx_insn *insn_15 = get_insn_by_uid (15); + ASSERT_EQ (INSN, GET_CODE (insn_15)); + ASSERT_EQ (USE, GET_CODE (PATTERN (insn_15))); + + /* Verify crtl->return_rtx. */ + ASSERT_EQ (REG, GET_CODE (crtl->return_rtx)); + ASSERT_EQ (0, REGNO (crtl->return_rtx)); + ASSERT_EQ (SImode, GET_MODE (crtl->return_rtx)); +} + +/* Verify that the loader can rebuild a CFG. */ + +static void +test_loading_cfg () +{ + rtl_dump_test t (SELFTEST_LOCATION, locate_file ("cfg-test.rtl")); + + ASSERT_STREQ ("cfg_test", IDENTIFIER_POINTER (DECL_NAME (cfun->decl))); + + ASSERT_TRUE (cfun); + + ASSERT_TRUE (cfun->cfg != NULL); + ASSERT_EQ (6, n_basic_blocks_for_fn (cfun)); + ASSERT_EQ (6, n_edges_for_fn (cfun)); + + /* The "fake" basic blocks. */ + basic_block entry = ENTRY_BLOCK_PTR_FOR_FN (cfun); + ASSERT_TRUE (entry != NULL); + ASSERT_EQ (ENTRY_BLOCK, entry->index); + + basic_block exit = EXIT_BLOCK_PTR_FOR_FN (cfun); + ASSERT_TRUE (exit != NULL); + ASSERT_EQ (EXIT_BLOCK, exit->index); + + /* The "real" basic blocks. */ + basic_block bb2 = (*cfun->cfg->x_basic_block_info)[2]; + basic_block bb3 = (*cfun->cfg->x_basic_block_info)[3]; + basic_block bb4 = (*cfun->cfg->x_basic_block_info)[4]; + basic_block bb5 = (*cfun->cfg->x_basic_block_info)[5]; + + ASSERT_EQ (2, bb2->index); + ASSERT_EQ (3, bb3->index); + ASSERT_EQ (4, bb4->index); + ASSERT_EQ (5, bb5->index); + + /* Verify connectivity. */ + + /* Entry block. */ + ASSERT_EQ (NULL, entry->preds); + ASSERT_EQ (1, entry->succs->length ()); + ASSERT_EDGE ((*entry->succs)[0], 0, 2, 0x1); + + /* bb2. */ + ASSERT_EQ (1, bb2->preds->length ()); + ASSERT_EDGE ((*bb2->preds)[0], 0, 2, 0x1); + ASSERT_EQ (2, bb2->succs->length ()); + ASSERT_EDGE ((*bb2->succs)[0], 2, 3, 0x1); + ASSERT_EDGE ((*bb2->succs)[1], 2, 4, 0x1); + + /* bb3. */ + ASSERT_EQ (1, bb3->preds->length ()); + ASSERT_EDGE ((*bb3->preds)[0], 2, 3, 0x1); + ASSERT_EQ (1, bb3->succs->length ()); + ASSERT_EDGE ((*bb3->succs)[0], 3, 5, 0x1); + + /* bb4. */ + ASSERT_EQ (1, bb4->preds->length ()); + ASSERT_EDGE ((*bb4->preds)[0], 2, 4, 0x1); + ASSERT_EQ (1, bb4->succs->length ()); + ASSERT_EDGE ((*bb4->succs)[0], 4, 5, 0x1); + + /* bb5. */ + ASSERT_EQ (2, bb5->preds->length ()); + ASSERT_EDGE ((*bb5->preds)[0], 3, 5, 0x1); + ASSERT_EDGE ((*bb5->preds)[1], 4, 5, 0x1); + ASSERT_EQ (1, bb5->succs->length ()); + ASSERT_EDGE ((*bb5->succs)[0], 5, 1, 0x1); + + /* Exit block. */ + ASSERT_EQ (1, exit->preds->length ()); + ASSERT_EDGE ((*exit->preds)[0], 5, 1, 0x1); + ASSERT_EQ (NULL, exit->succs); +} + +/* Verify that renumbering pseudos works. */ + +static void +test_regno_renumbering () +{ + rtl_dump_test t (SELFTEST_LOCATION, + locate_file ("test-regno-renumbering.rtl")); + + rtx_insn *insn_1 = get_insn_by_uid (1); + rtx set = single_set (insn_1); + ASSERT_NE (NULL, set); + rtx dst = SET_DEST (set); + rtx src = SET_SRC (set); + /* The registers did not have names, and so should have been treated + as pseudos. + Verify that they have been renumbered, with the lower one + having LAST_VIRTUAL_REGISTER + 1. */ + ASSERT_EQ (LAST_VIRTUAL_REGISTER + 1, REGNO (dst)); + ASSERT_EQ (LAST_VIRTUAL_REGISTER + 2, REGNO (src)); +} + +/* Run all of the selftests within this file. */ + +void +read_rtl_function_c_tests () +{ + test_loading_dump_fragment_1 (); + test_loading_dump_fragment_2 (); + test_loading_labels (); + test_loading_insn_with_mode (); + test_loading_jump_to_label_ref (); + test_loading_jump_to_return (); + test_loading_jump_to_simple_return (); + test_loading_note_insn_basic_block (); + test_loading_note_insn_deleted (); + test_loading_const_int (); + test_loading_symbol_ref (); + test_loading_call_insn_x86_64 (); + test_loading_full_dump_aarch64 (); + test_loading_full_dump_x86_64 (); + test_loading_cfg (); + test_regno_renumbering (); +} + +} // namespace selftest + +#endif /* #if CHECKING_P */ diff --git a/gcc/read-rtl-function.h b/gcc/read-rtl-function.h new file mode 100644 index 0000000..d26c797 --- /dev/null +++ b/gcc/read-rtl-function.h @@ -0,0 +1,37 @@ +/* read-rtl-function.h - Reader for RTL function dumps + 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 +. */ + +#ifndef GCC_READ_RTL_FUNCTION_H +#define GCC_READ_RTL_FUNCTION_H + +/* An optional policy class for class function_reader. */ + +struct function_reader_policy +{ + function_reader_policy () + { + } +}; + +extern bool read_rtl_function_body (int argc, const char **argv, + bool (*parse_opt) (const char *), + function_reader_policy *policy, + int *out_pseudo_offset); + +#endif /* GCC_READ_RTL_FUNCTION_H */ diff --git a/gcc/read-rtl.c b/gcc/read-rtl.c index 2622bfe..5e8f0fb 100644 --- a/gcc/read-rtl.c +++ b/gcc/read-rtl.c @@ -17,7 +17,13 @@ You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ +/* This file is compiled twice: once for the generator programs + once for the compiler. */ +#ifdef GENERATOR_FILE #include "bconfig.h" +#else +#include "config.h" +#endif /* Disable rtl checking; it conflicts with the iterator handling. */ #undef ENABLE_RTL_CHECKING @@ -30,6 +36,11 @@ along with GCC; see the file COPYING3. If not see #include "read-md.h" #include "gensupport.h" +#ifndef GENERATOR_FILE +#include "function.h" +#include "emit-rtl.h" +#endif + /* One element in a singly-linked list of (integer, string) pairs. */ struct map_value { struct map_value *next; @@ -106,6 +117,7 @@ htab_t subst_attr_to_iter_map = NULL; const char *current_iterator_name; static void validate_const_int (const char *); +static void one_time_initialization (void); /* Global singleton. */ rtx_reader *rtx_reader_ptr = NULL; @@ -181,6 +193,8 @@ apply_int_iterator (void *loc, int value) *(int *)loc = value; } +#ifdef GENERATOR_FILE + /* This routine adds attribute or does nothing depending on VALUE. When VALUE is 1, it does nothing - the first duplicate of original template is kept untouched when it's subjected to a define_subst. @@ -252,6 +266,8 @@ bind_subst_iter_and_attr (const char *iter, const char *attr) *slot = value; } +#endif /* #ifdef GENERATOR_FILE */ + /* Return name of a subst-iterator, corresponding to subst-attribute ATTR. */ static char* @@ -418,6 +434,8 @@ copy_rtx_for_iterators (rtx original) return x; } +#ifdef GENERATOR_FILE + /* Return a condition that must satisfy both ORIGINAL and EXTRA. If ORIGINAL has the form "&& ..." (as used in define_insn_and_splits), assume that EXTRA is already satisfied. Empty strings are treated like "true". */ @@ -581,6 +599,7 @@ apply_iterators (rtx original, vec *queue) } } } +#endif /* #ifdef GENERATOR_FILE */ /* Add a new "mapping" structure to hashtable TABLE. NAME is the name of the mapping and GROUP is the group to which it belongs. */ @@ -655,7 +674,9 @@ initialize_iterators (void) substs.iterators = htab_create (13, leading_string_hash, leading_string_eq_p, 0); substs.find_builtin = find_int; /* We don't use it, anyway. */ +#ifdef GENERATOR_FILE substs.apply_iterator = apply_subst_iterator; +#endif lower = add_mapping (&modes, modes.attrs, "mode"); upper = add_mapping (&modes, modes.attrs, "MODE"); @@ -724,6 +745,8 @@ atoll (const char *p) } #endif + +#ifdef GENERATOR_FILE /* Process a define_conditions directive, starting with the optional space after the "define_conditions". The directive looks like this: @@ -765,6 +788,7 @@ read_conditions (void) add_c_test (expr, value); } } +#endif /* #ifdef GENERATOR_FILE */ static void validate_const_int (const char *string) @@ -861,6 +885,8 @@ record_potential_iterator_use (struct iterator_group *group, void *ptr, } } +#ifdef GENERATOR_FILE + /* Finish reading a declaration of the form: (define... [ ... ]) @@ -1020,14 +1046,7 @@ check_code_iterator (struct mapping *iterator) bool read_rtx (const char *rtx_name, vec *rtxen) { - static bool initialized = false; - - /* Do one-time initialization. */ - if (!initialized) - { - initialize_iterators (); - initialized = true; - } + one_time_initialization (); /* Handle various rtx-related declarations that aren't themselves encoded as rtxes. */ @@ -1082,6 +1101,103 @@ read_rtx (const char *rtx_name, vec *rtxen) return true; } +#endif /* #ifdef GENERATOR_FILE */ + +/* Do one-time initialization. */ + +static void +one_time_initialization (void) +{ + static bool initialized = false; + + if (!initialized) + { + initialize_iterators (); + initialized = true; + } +} + +/* Consume characters until encountering a character in TERMINATOR_CHARS, + consuming the terminator character if CONSUME_TERMINATOR is true. + Return all characters before the terminator as an allocated buffer. */ + +char * +rtx_reader::read_until (const char *terminator_chars, bool consume_terminator) +{ + int ch = read_skip_spaces (); + unread_char (ch); + auto_vec buf; + while (1) + { + ch = read_char (); + if (strchr (terminator_chars, ch)) + { + if (!consume_terminator) + unread_char (ch); + break; + } + buf.safe_push (ch); + } + buf.safe_push ('\0'); + return xstrdup (buf.address ()); +} + +/* Subroutine of read_rtx_code, for parsing zero or more flags. */ + +static void +read_flags (rtx return_rtx) +{ + while (1) + { + int ch = read_char (); + if (ch != '/') + { + unread_char (ch); + break; + } + + int flag_char = read_char (); + switch (flag_char) + { + case 's': + RTX_FLAG (return_rtx, in_struct) = 1; + break; + case 'v': + RTX_FLAG (return_rtx, volatil) = 1; + break; + case 'u': + RTX_FLAG (return_rtx, unchanging) = 1; + break; + case 'f': + RTX_FLAG (return_rtx, frame_related) = 1; + break; + case 'j': + RTX_FLAG (return_rtx, jump) = 1; + break; + case 'c': + RTX_FLAG (return_rtx, call) = 1; + break; + case 'i': + RTX_FLAG (return_rtx, return_val) = 1; + break; + default: + fatal_with_file_and_line ("unrecognized flag: `%c'", flag_char); + } + } +} + +/* Return the numeric value n for GET_REG_NOTE_NAME (n) for STRING, + or fail if STRING isn't recognized. */ + +static int +parse_reg_note_name (const char *string) +{ + for (int i = 0; i < REG_NOTE_MAX; i++) + if (0 == strcmp (string, GET_REG_NOTE_NAME (i))) + return i; + fatal_with_file_and_line ("unrecognized REG_NOTE name: `%s'", string); +} + /* Subroutine of read_rtx and read_nested_rtx. CODE_NAME is the name of either an rtx code or a code iterator. Parse the rest of the rtx and return it. */ @@ -1090,7 +1206,7 @@ rtx rtx_reader::read_rtx_code (const char *code_name) { RTX_CODE code; - struct mapping *iterator; + struct mapping *iterator = NULL; const char *format_ptr; struct md_name name; rtx return_rtx; @@ -1103,13 +1219,19 @@ rtx_reader::read_rtx_code (const char *code_name) rtx value; /* Value of this node. */ }; + one_time_initialization (); + /* If this code is an iterator, build the rtx using the iterator's first value. */ +#ifdef GENERATOR_FILE iterator = (struct mapping *) htab_find (codes.iterators, &code_name); if (iterator != 0) code = (enum rtx_code) iterator->values->number; else code = (enum rtx_code) codes.find_builtin (code_name); +#else + code = (enum rtx_code) codes.find_builtin (code_name); +#endif /* If we end up with an insn expression then we free this space below. */ return_rtx = rtx_alloc (code); @@ -1120,6 +1242,26 @@ rtx_reader::read_rtx_code (const char *code_name) if (iterator) record_iterator_use (iterator, return_rtx); + /* Check for flags. */ + read_flags (return_rtx); + + /* Read REG_NOTE names for EXPR_LIST and INSN_LIST. */ + if ((GET_CODE (return_rtx) == EXPR_LIST + || GET_CODE (return_rtx) == INSN_LIST + || GET_CODE (return_rtx) == INT_LIST) + && !m_in_call_function_usage) + { + char ch = read_char (); + if (ch == ':') + { + read_name (&name); + PUT_MODE_RAW (return_rtx, + (machine_mode)parse_reg_note_name (name.string)); + } + else + unread_char (ch); + } + /* If what follows is `: mode ', read it and store the mode in the rtx. */ @@ -1132,8 +1274,24 @@ rtx_reader::read_rtx_code (const char *code_name) else unread_char (c); + if (INSN_CHAIN_CODE_P (code)) + { + read_name (&name); + INSN_UID (return_rtx) = atoi (name.string); + } + + /* Use the format_ptr to parse the various operands of this rtx. + read_rtx_operand is a vfunc, allowing the parser to vary between + parsing .md files and parsing .rtl dumps; the function_reader subclass + overrides the defaults when loading RTL dumps, to handle the + various extra attributes emitted by print_rtx. */ for (int idx = 0; format_ptr[idx] != 0; idx++) - read_rtx_operand (return_rtx, idx); + return_rtx = read_rtx_operand (return_rtx, idx); + + /* Call a vfunc to handle the various additional information that + print-rtl.c can write after the regular fields; does nothing when + parsing .md files. */ + handle_any_trailing_information (return_rtx); if (CONST_WIDE_INT_P (return_rtx)) { @@ -1217,6 +1375,9 @@ rtx_reader::read_rtx_operand (rtx return_rtx, int idx) break; case 'e': + XEXP (return_rtx, idx) = read_nested_rtx (); + break; + case 'u': XEXP (return_rtx, idx) = read_nested_rtx (); break; @@ -1292,7 +1453,10 @@ rtx_reader::read_rtx_operand (rtx return_rtx, int idx) star_if_braced = (format_ptr[idx] == 'T'); stringbuf = read_string (star_if_braced); + if (!stringbuf) + break; +#ifdef GENERATOR_FILE /* For insn patterns, we want to provide a default name based on the file and line, like "*foo.md:12", if the given name is blank. These are only for define_insn and @@ -1347,11 +1511,23 @@ rtx_reader::read_rtx_operand (rtx return_rtx, int idx) if (m != 0) record_iterator_use (m, return_rtx); } +#endif /* #ifdef GENERATOR_FILE */ + + /* "stringbuf" was allocated within string_obstack and thus has + the its lifetime restricted to that of the rtx_reader. This is + OK for the generator programs, but for non-generator programs, + XSTR and XTMPL fields are meant to be allocated in the GC-managed + heap. Hence we need to allocate a copy in the GC-managed heap + for the non-generator case. */ + const char *string_ptr = stringbuf; +#ifndef GENERATOR_FILE + string_ptr = ggc_strdup (stringbuf); +#endif /* #ifndef GENERATOR_FILE */ if (star_if_braced) - XTMPL (return_rtx, idx) = stringbuf; + XTMPL (return_rtx, idx) = string_ptr; else - XSTR (return_rtx, idx) = stringbuf; + XSTR (return_rtx, idx) = string_ptr; } break; @@ -1419,6 +1595,8 @@ rtx_reader::read_nested_rtx (void) require_char_ws (')'); + return_rtx = postprocess (return_rtx); + return return_rtx; } @@ -1456,7 +1634,8 @@ rtx_reader::read_rtx_variadic (rtx form) /* Constructor for class rtx_reader. */ rtx_reader::rtx_reader () -: base_rtx_reader () +: base_rtx_reader (), + m_in_call_function_usage (false) { /* Set the global singleton pointer. */ rtx_reader_ptr = this; diff --git a/gcc/rtl.h b/gcc/rtl.h index ce1131b..0741fc6 100644 --- a/gcc/rtl.h +++ b/gcc/rtl.h @@ -3641,7 +3641,9 @@ extern void init_varasm_once (void); extern rtx make_debug_expr_from_rtl (const_rtx); /* In read-rtl.c */ +#ifdef GENERATOR_FILE extern bool read_rtx (const char *, vec *); +#endif /* In alias.c */ extern rtx canon_rtx (rtx); diff --git a/gcc/selftest-rtl.c b/gcc/selftest-rtl.c new file mode 100644 index 0000000..0849cc6 --- /dev/null +++ b/gcc/selftest-rtl.c @@ -0,0 +1,90 @@ +/* Selftest support for 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 +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "selftest.h" +#include "backend.h" +#include "target.h" +#include "rtl.h" +#include "read-rtl-function.h" +#include "read-md.h" +#include "tree-core.h" +#include "emit-rtl.h" +#include "selftest-rtl.h" + +#if CHECKING_P + +namespace selftest { + +/* Constructor for selftest::rtl_dump_test. + Read a dumped RTL function from PATH. + Takes ownership of PATH, freeing in dtor. + Use LOC as the effective location when reporting failures. */ + +rtl_dump_test::rtl_dump_test (const location &loc, char *path) + : m_path (path), m_pseudo_offset (0) +{ + /* Parse the tempfile. */ + auto_vec argv (2); + argv.safe_push (progname); + argv.safe_push (path); + bool read_ok + = read_rtl_function_body (argv.length (), argv.address (), NULL, NULL, + &m_pseudo_offset); + ASSERT_TRUE_AT (loc, read_ok); +} + +/* Destructor for selftest::rtl_dump_test. + Cleanup global state relating to the function, and free the path. */ + +selftest::rtl_dump_test::~rtl_dump_test () +{ + /* Cleanups. */ + current_function_decl = NULL; + free_after_compilation (cfun); + set_cfun (NULL); + free (m_path); +} + +/* Given INPUT_REGNO, a register number in the input RTL dump, return + the effective register number that was used. */ +unsigned int +selftest::rtl_dump_test::effective_regno (int input_regno) const +{ + return input_regno + m_pseudo_offset; +} + +/* Get the insn with the given uid, or NULL if not found. */ + +rtx_insn * +get_insn_by_uid (int uid) +{ + for (rtx_insn *insn = get_insns (); insn; insn = NEXT_INSN (insn)) + if (INSN_UID (insn) == uid) + return insn; + + /* Not found. */ + return NULL; +} + +} // namespace selftest + +#endif /* #if CHECKING_P */ diff --git a/gcc/selftest-rtl.h b/gcc/selftest-rtl.h new file mode 100644 index 0000000..4858f3a --- /dev/null +++ b/gcc/selftest-rtl.h @@ -0,0 +1,67 @@ +/* A self-testing framework, for use by -fself-test. + 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 +. */ + +#ifndef GCC_SELFTEST_RTL_H +#define GCC_SELFTEST_RTL_H + +/* The selftest code should entirely disappear in a production + configuration, hence we guard all of it with #if CHECKING_P. */ + +#if CHECKING_P + +#include "read-rtl-function.h" + +namespace selftest { + +/* A class for testing RTL function dumps. */ + +class rtl_dump_test +{ + public: + /* Takes ownership of PATH. */ + rtl_dump_test (const location &loc, char *path); + ~rtl_dump_test (); + + unsigned int effective_regno (int input_regno) const; + + private: + char *m_path; + int m_pseudo_offset; +}; + +/* Wrapper class for initializing/cleaning up df. */ + +class dataflow_test +{ + public: + dataflow_test (); + ~dataflow_test (); +}; + +/* Get the insn with the given uid, or NULL if not found. */ + +extern rtx_insn *get_insn_by_uid (int uid); + +extern void verify_three_block_rtl_cfg (function *fun); + +} /* end of namespace selftest. */ + +#endif /* #if CHECKING_P */ + +#endif /* GCC_SELFTEST_RTL_H */ diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c index ecc3d71..1dce665 100644 --- a/gcc/selftest-run-tests.c +++ b/gcc/selftest-run-tests.c @@ -70,6 +70,7 @@ selftest::run_tests () tree_c_tests (); gimple_c_tests (); rtl_tests_c_tests (); + read_rtl_function_c_tests (); /* Higher-level tests, or for components that other selftests don't rely on. */ diff --git a/gcc/selftest.h b/gcc/selftest.h index 8b9c007..ae39101 100644 --- a/gcc/selftest.h +++ b/gcc/selftest.h @@ -213,6 +213,7 @@ extern void hash_map_tests_c_tests (); extern void hash_set_tests_c_tests (); extern void input_c_tests (); extern void pretty_print_c_tests (); +extern void read_rtl_function_c_tests (); extern void rtl_tests_c_tests (); extern void selftest_c_tests (); extern void spellcheck_c_tests (); diff --git a/gcc/testsuite/selftests/aarch64/times-two.rtl b/gcc/testsuite/selftests/aarch64/times-two.rtl new file mode 100644 index 0000000..8d198f2 --- /dev/null +++ b/gcc/testsuite/selftests/aarch64/times-two.rtl @@ -0,0 +1,42 @@ +(function "times_two" + (insn-chain + (note 1 0 4 (nil) NOTE_INSN_DELETED) + (note 4 1 2 2 [bb 2] NOTE_INSN_BASIC_BLOCK) + (insn 2 4 3 2 (set (mem/c:SI (plus:DI (reg/f:DI 68 virtual-stack-vars) + (const_int -4 [0xfffffffffffffffc])) [1 i+0 S4 A32]) + (reg:SI 0 x0 [ i ])) times-two.c:2 -1 + (nil)) + (note 3 2 6 2 NOTE_INSN_FUNCTION_BEG) + (insn 6 3 7 2 (set (reg:SI 75) + (mem/c:SI (plus:DI (reg/f:DI 68 virtual-stack-vars) + (const_int -4 [0xfffffffffffffffc])) [1 i+0 S4 A32])) times-two.c:3 -1 + (nil)) + (insn 7 6 10 2 (set (reg:SI 73 [ _2 ]) + (ashift:SI (reg:SI 75) + (const_int 1 [0x1]))) times-two.c:3 -1 + (nil)) + (insn 10 7 14 2 (set (reg:SI 74 [ ]) + (reg:SI 73 [ _2 ])) times-two.c:3 -1 + (nil)) + (insn 14 10 15 2 (set (reg/i:SI 0 x0) + (reg:SI 74 [ ])) times-two.c:4 -1 + (nil)) + (insn 15 14 0 2 (use (reg/i:SI 0 x0)) times-two.c:4 -1 + (nil)) + ) ;; insn-chain + (cfg + (bb 0 + (edge 0 2 (flags 0x1)) + ) ;; bb + (bb 2 + (edge 2 1 (flags 0x1)) + ) ;; bb + (bb 1 + ) ;; bb + ) ;; cfg + (crtl + (return_rtx + (reg/i:SI 0 x0) + ) ;; return_rtx + ) ;; crtl +) ;; function "times_two" diff --git a/gcc/testsuite/selftests/asr_div1.rtl b/gcc/testsuite/selftests/asr_div1.rtl new file mode 100644 index 0000000..2c2a825 --- /dev/null +++ b/gcc/testsuite/selftests/asr_div1.rtl @@ -0,0 +1,23 @@ +;; Taken from +;; gcc/testsuite/gcc.dg/asr_div1.c -O2 -fdump-rtl-all -mtune=cortex-a53 +;; for aarch64, hand editing the prev/next insns to 0 as needed, and +;; editing whitespace to avoid over-long lines. */ + +(function "f1" + (insn-chain +(insn 8 0 9 2 (set (reg:DI 78) + (lshiftrt:DI (reg:DI 76) + (const_int 32 [0x20]))) + ../../src/gcc/testsuite/gcc.dg/asr_div1.c:14 + 641 {*aarch64_lshr_sisd_or_int_di3} + (expr_list:REG_DEAD (reg:DI 76) + (nil))) +(insn 9 8 0 2 (set (reg:SI 79) + (ashiftrt:SI (subreg:SI (reg:DI 78) 0) + (const_int 3 [0x3]))) + ../../src/gcc/testsuite/gcc.dg/asr_div1.c:14 + 642 {*aarch64_ashr_sisd_or_int_si3} + (expr_list:REG_DEAD (reg:DI 78) + (nil))) + ) ;; insn-chain +) ;; function diff --git a/gcc/testsuite/selftests/cfg-test.rtl b/gcc/testsuite/selftests/cfg-test.rtl new file mode 100644 index 0000000..d6be7f9 --- /dev/null +++ b/gcc/testsuite/selftests/cfg-test.rtl @@ -0,0 +1,38 @@ +/* Example of a loading a CFG like this: + 0 (entry) + | + 2 + / \ + 3 4 + \ / + 5 + | + 1 (exit). */ + +(function "cfg_test" + (insn-chain + (note 2 0 3 2 [bb 2] NOTE_INSN_BASIC_BLOCK) + (note 3 2 4 3 [bb 3] NOTE_INSN_BASIC_BLOCK) + (note 4 3 5 4 [bb 4] NOTE_INSN_BASIC_BLOCK) + (note 5 4 0 5 [bb 5] NOTE_INSN_BASIC_BLOCK) + ) ;; empty + (cfg + (bb 0 ;; entry + (edge 0 2 (flags 0x1)) + ) ;; bb + (bb 2 + (edge 2 3 (flags 0x1)) + (edge 2 4 (flags 0x1)) + ) ;; bb + (bb 3 + (edge 3 5 (flags 0x1)) + ) ;; bb + (bb 4 + (edge 4 5 (flags 0x1)) + ) ;; bb + (bb 5 + (edge 5 1 (flags 0x1)) + ) ;; bb + (bb 1) ;; exit + ) ;; cfg +) diff --git a/gcc/testsuite/selftests/const-int.rtl b/gcc/testsuite/selftests/const-int.rtl new file mode 100644 index 0000000..933a0bf --- /dev/null +++ b/gcc/testsuite/selftests/const-int.rtl @@ -0,0 +1,16 @@ +(function "const_int_examples" + (insn-chain + (insn 100 0 101 2 + (set (reg:SI 100) (const_int 0 [0x0])) + test.c:2 -1 (nil)) + (insn 101 100 102 2 + (set (reg:SI 101) (const_int 1 [0x1])) + test.c:2 -1 (nil)) + (insn 102 101 103 2 + (set (reg:SI 102) (const_int -1 [0xffffffff])) + test.c:2 -1 (nil)) + (insn 103 102 0 2 + (set (reg:SI 103) (const_int 256 [0x100])) + test.c:2 -1 (nil)) + ) ;; insn-chain +) ;; function diff --git a/gcc/testsuite/selftests/copy-hard-reg-into-frame.rtl b/gcc/testsuite/selftests/copy-hard-reg-into-frame.rtl new file mode 100644 index 0000000..a8abee4 --- /dev/null +++ b/gcc/testsuite/selftests/copy-hard-reg-into-frame.rtl @@ -0,0 +1,12 @@ +(function "copy_hard_reg_into_frame" + (insn-chain + (insn 2 0 0 2 + (set (mem/c:SI + (plus:DI + (reg/f:DI 20 frame) + (const_int -4 [0xfffffffffffffffc])) + [1 i+0 S4 A32]) + (reg:SI 5 di [ i ])) test.c:2 -1 + (nil)) + ) ;; insn-chain +) ;; function diff --git a/gcc/testsuite/selftests/example-labels.rtl b/gcc/testsuite/selftests/example-labels.rtl new file mode 100644 index 0000000..edadcc9 --- /dev/null +++ b/gcc/testsuite/selftests/example-labels.rtl @@ -0,0 +1,6 @@ +(function "example_labels" + (insn-chain + (code_label 1 0 2 6 3 (nil) [0 uses]) + (code_label 2 1 0 6 3 ("some_label_name") [57 uses]) + ) ;; insn-chain +) ;; function diff --git a/gcc/testsuite/selftests/insn-with-mode.rtl b/gcc/testsuite/selftests/insn-with-mode.rtl new file mode 100644 index 0000000..890e5f9 --- /dev/null +++ b/gcc/testsuite/selftests/insn-with-mode.rtl @@ -0,0 +1,5 @@ +(function "insn_with_mode" + (insn-chain + (insn:TI 31 0 0 2 (set (reg:SI 100) (reg:SI 101)) -1 (nil)) + ) ;; insn-chain +) ;; function diff --git a/gcc/testsuite/selftests/jump-to-label-ref.rtl b/gcc/testsuite/selftests/jump-to-label-ref.rtl new file mode 100644 index 0000000..1089997 --- /dev/null +++ b/gcc/testsuite/selftests/jump-to-label-ref.rtl @@ -0,0 +1,11 @@ +(function "jump_to_label_ref" + (insn-chain + (jump_insn 1 0 2 4 (set (pc) + (label_ref 3)) ../../src/gcc/testsuite/rtl.dg/test.c:4 -1 + (nil) + -> 3) + (barrier 2 1 3) + (code_label 3 2 0 5 2 (nil) [1 uses]) + ) ;; insn-chain +) ;; function + diff --git a/gcc/testsuite/selftests/jump-to-return.rtl b/gcc/testsuite/selftests/jump-to-return.rtl new file mode 100644 index 0000000..1316d03 --- /dev/null +++ b/gcc/testsuite/selftests/jump-to-return.rtl @@ -0,0 +1,10 @@ +(function "jump_to_return" + (insn-chain + (jump_insn 1 0 2 4 (set (pc) + (label_ref 3)) ../../src/gcc/testsuite/rtl.dg/test.c:4 -1 + (nil) + -> return) + (barrier 2 1 3) + (code_label 3 2 0 5 2 (nil) [1 uses]) + ) ;; insn-chain +) ;; function diff --git a/gcc/testsuite/selftests/jump-to-simple-return.rtl b/gcc/testsuite/selftests/jump-to-simple-return.rtl new file mode 100644 index 0000000..ab27b85 --- /dev/null +++ b/gcc/testsuite/selftests/jump-to-simple-return.rtl @@ -0,0 +1,11 @@ +(function "jump_to_simple_return" + (insn-chain + (jump_insn 1 0 2 4 (set (pc) + (label_ref 3)) ../../src/gcc/testsuite/rtl.dg/test.c:4 -1 + (nil) + -> simple_return) + (barrier 2 1 3) + (code_label 3 2 0 5 2 (nil) [1 uses]) + ) ;; insn-chain +) ;; function + diff --git a/gcc/testsuite/selftests/note-insn-deleted.rtl b/gcc/testsuite/selftests/note-insn-deleted.rtl new file mode 100644 index 0000000..baf36cc --- /dev/null +++ b/gcc/testsuite/selftests/note-insn-deleted.rtl @@ -0,0 +1,5 @@ +(function "example_note" + (insn-chain + (note 1 0 0 (nil) NOTE_INSN_DELETED) + ) ;; insn-chain +) ;; function diff --git a/gcc/testsuite/selftests/note_insn_basic_block.rtl b/gcc/testsuite/selftests/note_insn_basic_block.rtl new file mode 100644 index 0000000..11eb14d --- /dev/null +++ b/gcc/testsuite/selftests/note_insn_basic_block.rtl @@ -0,0 +1,5 @@ +(function "example_of_note" + (insn-chain + (note 1 0 0 2 [bb 2] NOTE_INSN_BASIC_BLOCK) + ) ;; insn-chain +) ;; function diff --git a/gcc/testsuite/selftests/symbol-ref.rtl b/gcc/testsuite/selftests/symbol-ref.rtl new file mode 100644 index 0000000..226295d --- /dev/null +++ b/gcc/testsuite/selftests/symbol-ref.rtl @@ -0,0 +1,7 @@ +(function "example_of_symbol_ref" + (insn-chain + (insn 1045 0 0 2 (set (reg:SI 480) + (high:SI (symbol_ref:SI ("isl_obj_map_vtable") [flags 0xc0] ))) y.c:12702 -1 + (nil)) + ) ;; insn-chain +) ;; function diff --git a/gcc/testsuite/selftests/test-regno-renumbering.rtl b/gcc/testsuite/selftests/test-regno-renumbering.rtl new file mode 100644 index 0000000..007666f --- /dev/null +++ b/gcc/testsuite/selftests/test-regno-renumbering.rtl @@ -0,0 +1,6 @@ +(function "test" + (insn-chain + ;; The regs here have no names and are thus to be treated as pseudos. + (insn 1 0 0 2 (set (reg:SI 10) (reg:SI 11)) -1 (nil)) + ) ;; insn-chain +) ;; function diff --git a/gcc/testsuite/selftests/x86_64/call-insn.rtl b/gcc/testsuite/selftests/x86_64/call-insn.rtl new file mode 100644 index 0000000..b3be835 --- /dev/null +++ b/gcc/testsuite/selftests/x86_64/call-insn.rtl @@ -0,0 +1,12 @@ +(function "test" + (insn-chain + (call_insn/j 17 0 0 2 (set (reg:DF 21 xmm0) + (call (mem:QI (symbol_ref:DI ("sqrt") [flags 0x41] ) [0 __builtin_sqrt S1 A8]) + (const_int 0 [0]))) test.c:19 -1 + (expr_list:REG_CALL_DECL (symbol_ref:DI ("sqrt") [flags 0x41] ) + (expr_list:REG_EH_REGION (const_int 0 [0]) + (nil))) + (expr_list:DF (use (reg:DF 21 xmm0)) + (nil))) + ) ;; insn-chain +) ;; function "test" diff --git a/gcc/testsuite/selftests/x86_64/times-two.rtl b/gcc/testsuite/selftests/x86_64/times-two.rtl new file mode 100644 index 0000000..9eaee45 --- /dev/null +++ b/gcc/testsuite/selftests/x86_64/times-two.rtl @@ -0,0 +1,57 @@ +;; Dump of this C function: +;; +;; int times_two (int i) +;; { +;; return i * 2; +;; } +;; +;; after expand for target==x86_64 + +(function "times_two" + (insn-chain + (note 1 0 4 (nil) NOTE_INSN_DELETED) + (note 4 1 2 2 [bb 2] NOTE_INSN_BASIC_BLOCK) + (insn 2 4 3 2 (set (mem/c:SI (plus:DI (reg/f:DI 82 virtual-stack-vars) + (const_int -4 [0xfffffffffffffffc])) [1 i+0 S4 A32]) + (reg:SI 5 di [ i ])) t.c:2 -1 + (nil)) + (note 3 2 6 2 NOTE_INSN_FUNCTION_BEG) + (insn 6 3 7 2 (set (reg:SI 89) + (mem/c:SI (plus:DI (reg/f:DI 82 virtual-stack-vars) + (const_int -4 [0xfffffffffffffffc])) [1 i+0 S4 A32])) t.c:3 -1 + (nil)) + (insn 7 6 10 2 (parallel [ + (set (reg:SI 87 [ _2 ]) + (ashift:SI (reg:SI 89) + (const_int 1 [0x1]))) + (clobber (reg:CC 17 flags)) + ]) t.c:3 -1 + (expr_list:REG_EQUAL (ashift:SI (mem/c:SI (plus:DI (reg/f:DI 82 virtual-stack-vars) + (const_int -4 [0xfffffffffffffffc])) [1 i+0 S4 A32]) + (const_int 1 [0x1])) + (nil))) + (insn 10 7 14 2 (set (reg:SI 88 [ ]) + (reg:SI 87 [ _2 ])) t.c:3 -1 + (nil)) + (insn 14 10 15 2 (set (reg/i:SI 0 ax) + (reg:SI 88 [ ])) t.c:4 -1 + (nil)) + (insn 15 14 0 2 (use (reg/i:SI 0 ax)) t.c:4 -1 + (nil)) + ) ;; insn-chain + (cfg + (bb 0 + (edge 0 2 (flags 0x1)) + ) ;; bb + (bb 2 + (edge 2 1 (flags 0x1)) + ) ;; bb + (bb 1 + ) ;; bb + ) ;; cfg + (crtl + (return_rtx + (reg/i:SI 0 ax) + ) ;; return_rtx + ) ;; crtl +) ;; function "times_two" diff --git a/gcc/tree-dfa.c b/gcc/tree-dfa.c index 9a3b072..ec63b34 100644 --- a/gcc/tree-dfa.c +++ b/gcc/tree-dfa.c @@ -305,6 +305,11 @@ ssa_default_def (struct function *fn, tree var) gcc_assert (TREE_CODE (var) == VAR_DECL || TREE_CODE (var) == PARM_DECL || TREE_CODE (var) == RESULT_DECL); + + /* Always NULL_TREE for rtl function dumps. */ + if (!fn->gimple_df) + return NULL_TREE; + in.var = (tree)&ind; ind.uid = DECL_UID (var); return DEFAULT_DEFS (fn)->find_with_hash ((tree)&in, DECL_UID (var)); -- 1.8.5.3