gcc.c: Split up the driver's "main" into smaller functions
authorDavid Malcolm <dmalcolm@redhat.com>
Fri, 3 Oct 2014 16:28:03 +0000 (16:28 +0000)
committerDavid Malcolm <dmalcolm@gcc.gnu.org>
Fri, 3 Oct 2014 16:28:03 +0000 (16:28 +0000)
gcc/ChangeLog:
* gcc.c (class driver): New class.
(main): Reimplement in terms of driver::main, moving most of the
locals to be locals within individual methods of class driver.
The remaining locals "explicit_link_files", "decoded_options" and
"decoded_options_count" are used by multiple driver:: methods, and
so become member data.  Doing so isolates the argc/argv reads and
writes.  Replace "goto out" with a special exit code from
new method driver::prepare_infiles.  Split out the old
implementation of main into the following...
(driver::main): New function, corresponding to the old "main"
implementation.
(driver::set_progname): New function, taken from the old
"main" implementation.
(driver::expand_at_files): Likewise.
(driver::decode_argv): Likewise.
(driver::global_initializations): Likewise.
(driver::build_multilib_strings): Likewise.
(driver::set_up_specs): Likewise.
(driver::putenv_COLLECT_GCC): Likewise.
(driver::maybe_putenv_COLLECT_LTO_WRAPPER): Likewise.
(driver::handle_unrecognized_options): Likewise.
(driver::maybe_print_and_exit): Likewise.
(driver::prepare_infiles): Likewise.
(driver::do_spec_on_infiles): Likewise.
(driver::maybe_run_linker): Likewise.
(driver::final_actions): Likewise.
(driver::get_exit_code): Likewise.

From-SVN: r215861

gcc/ChangeLog
gcc/gcc.c

index 3ed527741e52152bef9dbd3d002623e1fc3d5aab..8d2521eecbb7f72f3d4f9b1a3e9fe2e84998fa8d 100644 (file)
@@ -1,3 +1,33 @@
+2014-10-03  David Malcolm  <dmalcolm@redhat.com>
+
+       * gcc.c (class driver): New class.
+       (main): Reimplement in terms of driver::main, moving most of the
+       locals to be locals within individual methods of class driver.
+       The remaining locals "explicit_link_files", "decoded_options" and
+       "decoded_options_count" are used by multiple driver:: methods, and
+       so become member data.  Doing so isolates the argc/argv reads and
+       writes.  Replace "goto out" with a special exit code from
+       new method driver::prepare_infiles.  Split out the old
+       implementation of main into the following...
+       (driver::main): New function, corresponding to the old "main"
+       implementation.
+       (driver::set_progname): New function, taken from the old
+       "main" implementation.
+       (driver::expand_at_files): Likewise.
+       (driver::decode_argv): Likewise.
+       (driver::global_initializations): Likewise.
+       (driver::build_multilib_strings): Likewise.
+       (driver::set_up_specs): Likewise.
+       (driver::putenv_COLLECT_GCC): Likewise.
+       (driver::maybe_putenv_COLLECT_LTO_WRAPPER): Likewise.
+       (driver::handle_unrecognized_options): Likewise.
+       (driver::maybe_print_and_exit): Likewise.
+       (driver::prepare_infiles): Likewise.
+       (driver::do_spec_on_infiles): Likewise.
+       (driver::maybe_run_linker): Likewise.
+       (driver::final_actions): Likewise.
+       (driver::get_exit_code): Likewise.
+
 2014-10-03  Yury Gribov  <y.gribov@samsung.com>
 
        * asan.c (asan_finish_file): Disable __asan_init calls for KASan;
index 47c4e283bcd07366c75debbc7276bb58198e9f98..f53b92ffd1276c4bfd8b42bff33de90b80dbfbe8 100644 (file)
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -6724,49 +6724,132 @@ compare_files (char *cmpfile[])
   return ret;
 }
 
-extern int main (int, char **);
+/* The top-level "main" within the driver would be ~1000 lines long.
+   This class breaks it up into smaller functions and contains some
+   state shared by them.  */
 
-int
-main (int argc, char **argv)
+class driver
 {
-  size_t i;
-  int value;
-  int linker_was_run = 0;
-  int lang_n_infiles = 0;
-  int num_linker_inputs = 0;
+ public:
+  int main (int argc, char **argv);
+
+ private:
+  void set_progname (const char *argv0) const;
+  void expand_at_files (int *argc, char ***argv) const;
+  void decode_argv (int argc, const char **argv);
+  void global_initializations () const;
+  void build_multilib_strings () const;
+  void set_up_specs () const;
+  void putenv_COLLECT_GCC (const char *argv0) const;
+  void maybe_putenv_COLLECT_LTO_WRAPPER () const;
+  void handle_unrecognized_options () const;
+  int maybe_print_and_exit () const;
+  bool prepare_infiles ();
+  void do_spec_on_infiles () const;
+  void maybe_run_linker (const char *argv0) const;
+  void final_actions () const;
+  int get_exit_code () const;
+
+ private:
   char *explicit_link_files;
-  char *specs_file;
-  char *lto_wrapper_file;
-  const char *p;
-  struct user_specs *uptr;
-  char **old_argv = argv;
   struct cl_decoded_option *decoded_options;
   unsigned int decoded_options_count;
+};
+
+/* Implement the top-level "main" within the driver in terms of
+   driver::main.  */
+
+extern int main (int, char **);
 
-  p = argv[0] + strlen (argv[0]);
-  while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
+int
+main (int argc, char **argv)
+{
+  driver d;
+
+  return d.main (argc, argv);
+}
+
+/* driver::main is implemented as a series of driver:: method calls.  */
+
+int
+driver::main (int argc, char **argv)
+{
+  bool early_exit;
+
+  set_progname (argv[0]);
+  expand_at_files (&argc, &argv);
+  decode_argv (argc, const_cast <const char **> (argv));
+  global_initializations ();
+  build_multilib_strings ();
+  set_up_specs ();
+  putenv_COLLECT_GCC (argv[0]);
+  maybe_putenv_COLLECT_LTO_WRAPPER ();
+  handle_unrecognized_options ();
+
+  if (!maybe_print_and_exit ())
+    return 0;
+
+  early_exit = prepare_infiles ();
+  if (early_exit)
+    return get_exit_code ();
+
+  do_spec_on_infiles ();
+  maybe_run_linker (argv[0]);
+  final_actions ();
+  return get_exit_code ();
+}
+
+/* Locate the final component of argv[0] after any leading path, and set
+   the program name accordingly.  */
+
+void
+driver::set_progname (const char *argv0) const
+{
+  const char *p = argv0 + strlen (argv0);
+  while (p != argv0 && !IS_DIR_SEPARATOR (p[-1]))
     --p;
   progname = p;
 
   xmalloc_set_program_name (progname);
+}
 
-  expandargv (&argc, &argv);
+/* Expand any @ files within the command-line args,
+   setting at_file_supplied if any were expanded.  */
+
+void
+driver::expand_at_files (int *argc, char ***argv) const
+{
+  char **old_argv = *argv;
+
+  expandargv (argc, argv);
 
   /* Determine if any expansions were made.  */
-  if (argv != old_argv)
+  if (*argv != old_argv)
     at_file_supplied = true;
+}
 
+/* Decode the command-line arguments from argc/argv into the
+   decoded_options array.  */
+
+void
+driver::decode_argv (int argc, const char **argv)
+{
   /* Register the language-independent parameters.  */
   global_init_params ();
   finish_params ();
 
   init_options_struct (&global_options, &global_options_set);
 
-  decode_cmdline_options_to_array (argc, CONST_CAST2 (const char **, char **,
-                                                     argv),
+  decode_cmdline_options_to_array (argc, argv,
                                   CL_DRIVER,
                                   &decoded_options, &decoded_options_count);
+}
+
+/* Perform various initializations and setup.  */
 
+void
+driver::global_initializations () const
+{
   /* Unlock the stdio streams.  */
   unlock_std_streams ();
 
@@ -6808,10 +6891,16 @@ main (int argc, char **argv)
   alloc_args ();
 
   obstack_init (&obstack);
+}
+
+/* Build multilib_select, et. al from the separate lines that make up each
+   multilib selection.  */
 
-  /* Build multilib_select, et. al from the separate lines that make up each
-     multilib selection.  */
+void
+driver::build_multilib_strings () const
+{
   {
+    const char *p;
     const char *const *q = multilib_raw;
     int need_space;
 
@@ -6844,7 +6933,7 @@ main (int argc, char **argv)
     multilib_reuse = XOBFINISH (&multilib_obstack, const char *);
 
     need_space = FALSE;
-    for (i = 0; i < ARRAY_SIZE (multilib_defaults_raw); i++)
+    for (size_t i = 0; i < ARRAY_SIZE (multilib_defaults_raw); i++)
       {
        if (need_space)
          obstack_1grow (&multilib_obstack, ' ');
@@ -6857,6 +6946,15 @@ main (int argc, char **argv)
     obstack_1grow (&multilib_obstack, 0);
     multilib_defaults = XOBFINISH (&multilib_obstack, const char *);
   }
+}
+
+/* Set up the spec-handling machinery.  */
+
+void
+driver::set_up_specs () const
+{
+  char *specs_file;
+  size_t i;
 
 #ifdef INIT_ENVIRONMENT
   /* Set up any other necessary machine specific environment variables.  */
@@ -7016,7 +7114,7 @@ main (int argc, char **argv)
 
   /* Process any user specified specs in the order given on the command
      line.  */
-  for (uptr = user_specs_head; uptr; uptr = uptr->next)
+  for (struct user_specs *uptr = user_specs_head; uptr; uptr = uptr->next)
     {
       char *filename = find_a_file (&startfile_prefixes, uptr->filename,
                                    R_OK, true);
@@ -7088,16 +7186,27 @@ main (int argc, char **argv)
   /* Now that we have the switches and the specs, set
      the subdirectory based on the options.  */
   set_multilib_dir ();
+}
+
+/* Set up to remember the pathname of gcc and any options
+   needed for collect.  We use argv[0] instead of progname because
+   we need the complete pathname.  */
 
-  /* Set up to remember the pathname of gcc and any options
-     needed for collect.  We use argv[0] instead of progname because
-     we need the complete pathname.  */
+void
+driver::putenv_COLLECT_GCC (const char *argv0) const
+{
   obstack_init (&collect_obstack);
   obstack_grow (&collect_obstack, "COLLECT_GCC=", sizeof ("COLLECT_GCC=") - 1);
-  obstack_grow (&collect_obstack, argv[0], strlen (argv[0]) + 1);
+  obstack_grow (&collect_obstack, argv0, strlen (argv0) + 1);
   xputenv (XOBFINISH (&collect_obstack, char *));
+}
 
-  /* Set up to remember the pathname of the lto wrapper. */
+/* Set up to remember the pathname of the lto wrapper. */
+
+void
+driver::maybe_putenv_COLLECT_LTO_WRAPPER () const
+{
+  char *lto_wrapper_file;
 
   if (have_c)
     lto_wrapper_file = NULL;
@@ -7116,14 +7225,24 @@ main (int argc, char **argv)
       xputenv (XOBFINISH (&collect_obstack, char *));
     }
 
-  /* Reject switches that no pass was interested in.  */
+}
 
-  for (i = 0; (int) i < n_switches; i++)
+/* Reject switches that no pass was interested in.  */
+
+void
+driver::handle_unrecognized_options () const
+{
+  for (size_t i = 0; (int) i < n_switches; i++)
     if (! switches[i].validated)
       error ("unrecognized command line option %<-%s%>", switches[i].part1);
+}
 
-  /* Obey some of the options.  */
+/* Handle the various -print-* options, returning 0 if the driver
+   should exit, or nonzero if the driver should continue.  */
 
+int
+driver::maybe_print_and_exit () const
+{
   if (print_search_dirs)
     {
       printf (_("install: %s%s\n"),
@@ -7286,11 +7405,24 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"
        return (0);
     }
 
+  return 1;
+}
+
+/* Figure out what to do with each input file.
+   Return true if we need to exit early from "main", false otherwise.  */
+
+bool
+driver::prepare_infiles ()
+{
+  size_t i;
+  int lang_n_infiles = 0;
+
   if (n_infiles == added_libraries)
     fatal_error ("no input files");
 
   if (seen_error ())
-    goto out;
+    /* Early exit needed from main.  */
+    return true;
 
   /* Make a place to record the compiler output file names
      that correspond to the input files.  */
@@ -7338,6 +7470,17 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"
   if (!combine_inputs && have_c && have_o && lang_n_infiles > 1)
     fatal_error ("cannot specify -o with -c, -S or -E with multiple files");
 
+  /* No early exit needed from main; we can continue.  */
+  return false;
+}
+
+/* Run the spec machinery on each input file.  */
+
+void
+driver::do_spec_on_infiles () const
+{
+  size_t i;
+
   for (i = 0; (int) i < n_infiles; i++)
     {
       int this_file_error = 0;
@@ -7372,6 +7515,8 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"
            }
          else
            {
+             int value;
+
              if (compare_debug)
                {
                  free (debug_check_temp_file[0]);
@@ -7473,6 +7618,16 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"
       if (lang_specific_pre_link ())
        errorcount++;
     }
+}
+
+/* If we have to run the linker, do it now.  */
+
+void
+driver::maybe_run_linker (const char *argv0) const
+{
+  size_t i;
+  int linker_was_run = 0;
+  int num_linker_inputs;
 
   /* Determine if there are any linker input files.  */
   num_linker_inputs = 0;
@@ -7524,7 +7679,7 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"
              linker_plugin_file_spec = convert_white_space (temp_spec);
            }
 #endif
-         lto_gcc_spec = argv[0];
+         lto_gcc_spec = argv0;
        }
 
       /* Rebuild the COMPILER_PATH and LIBRARY_PATH environment variables
@@ -7539,7 +7694,7 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"
                    " to the linker.\n\n"));
          fflush (stdout);
        }
-      value = do_spec (link_command_spec);
+      int value = do_spec (link_command_spec);
       if (value < 0)
        errorcount = 1;
       linker_was_run = (tmp != execution_count);
@@ -7554,7 +7709,13 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"
          && !(infiles[i].language && infiles[i].language[0] == '*'))
        warning (0, "%s: linker input file unused because linking not done",
                 outfiles[i]);
+}
+
+/* The end of "main".  */
 
+void
+driver::final_actions () const
+{
   /* Delete some or all of the temporary files we made.  */
 
   if (seen_error ())
@@ -7566,8 +7727,13 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"
       printf (("\nFor bug reporting instructions, please see:\n"));
       printf ("%s\n", bug_report_url);
     }
+}
 
- out:
+/* Determine what the exit code of the driver should be.  */
+
+int
+driver::get_exit_code () const
+{
   return (signal_count != 0 ? 2
          : seen_error () ? (pass_exit_codes ? greatest_status : 1)
          : 0);