From df092d93b511a28180105a2db40777fb977e23da Mon Sep 17 00:00:00 2001
From: Giuliano Belinassi <giuliano.belinassi@usp.br>
Date: Mon, 18 May 2020 21:08:43 -0300
Subject: [PATCH] Refactor execute in gcc.c
Refactor `execute' function to avoid exposing unnecessary details of
how it works, encapsulating each important step into a function.
gcc/ChangeLog
2020-05-18 Giuliano Belinassi <giuliano.belinassi@usp.brr>
* gcc.c (struct command): Move from execute.
(await_commands_to_finish): New function.
(split_commands): Same as above.
(parse_argbuf): Same as above.
(execute): Refactor based on new functions.
---
gcc/ChangeLog | 8 +
gcc/gcc.c | 595 +++++++++++++++++++++++++++++---------------------
2 files changed, 353 insertions(+), 250 deletions(-)
@@ -1,3 +1,11 @@
+2020-05-18 Giuliano Belinassi <giuliano.belinassi@usp.brr>
+
+ * gcc.c (struct command): Move from execute.
+ (await_commands_to_finish): New function.
+ (split_commands): Same as above.
+ (parse_argbuf): Same as above.
+ (execute): Refactor based on new functions.
+
2020-05-18 Jason Merrill <jason@redhat.com>
* aclocal.m4: Add ax_cxx_compile_stdcxx.m4.
@@ -3019,157 +3019,105 @@ add_sysrooted_hdrs_prefix (struct path_prefix *pprefix, const char *prefix,
}
-/* Execute the command specified by the arguments on the current line of spec.
- When using pipes, this includes several piped-together commands
- with `|' between them.
- Return 0 if successful, -1 if failed. */
+struct command
+{
+ const char *prog; /* program name. */
+ const char **argv; /* vector of args. */
+};
+
+/* Print what commands will run. Return 0 if success, anything else on
+ error. */
static int
-execute (void)
+print_verbose (int n_commands, struct command commands[])
{
int i;
- int n_commands; /* # of command. */
- char *string;
- struct pex_obj *pex;
- struct command
- {
- const char *prog; /* program name. */
- const char **argv; /* vector of args. */
- };
- const char *arg;
-
- struct command *commands; /* each command buffer with above info. */
-
- gcc_assert (!processing_spec_function);
-
- if (wrapper_string)
- {
- string = find_a_file (&exec_prefixes,
- argbuf[0], X_OK, false);
- if (string)
- argbuf[0] = string;
- insert_wrapper (wrapper_string);
- }
- /* Count # of piped commands. */
- for (n_commands = 1, i = 0; argbuf.iterate (i, &arg); i++)
- if (strcmp (arg, "|") == 0)
- n_commands++;
-
- /* Get storage for each command. */
- commands = (struct command *) alloca (n_commands * sizeof (struct command));
-
- /* Split argbuf into its separate piped processes,
- and record info about each one.
- Also search for the programs that are to be run. */
-
- argbuf.safe_push (0);
-
- commands[0].prog = argbuf[0]; /* first command. */
- commands[0].argv = argbuf.address ();
-
- if (!wrapper_string)
- {
- string = find_a_file (&exec_prefixes, commands[0].prog, X_OK, false);
- if (string)
- commands[0].argv[0] = string;
- }
-
- for (n_commands = 1, i = 0; argbuf.iterate (i, &arg); i++)
- if (arg && strcmp (arg, "|") == 0)
- { /* each command. */
-#if defined (__MSDOS__) || defined (OS2) || defined (VMS)
- fatal_error (input_location, "%<-pipe%> not supported");
-#endif
- argbuf[i] = 0; /* Termination of command args. */
- commands[n_commands].prog = argbuf[i + 1];
- commands[n_commands].argv
- = &(argbuf.address ())[i + 1];
- string = find_a_file (&exec_prefixes, commands[n_commands].prog,
- X_OK, false);
- if (string)
- commands[n_commands].argv[0] = string;
- n_commands++;
- }
-
- /* If -v, print what we are about to do, and maybe query. */
+ /* For help listings, put a blank line between sub-processes. */
+ if (print_help_list)
+ fputc ('\n', stderr);
- if (verbose_flag)
+ /* Print each piped command as a separate line. */
+ for (i = 0; i < n_commands; i++)
{
- /* For help listings, put a blank line between sub-processes. */
- if (print_help_list)
- fputc ('\n', stderr);
+ const char *const *j;
- /* Print each piped command as a separate line. */
- for (i = 0; i < n_commands; i++)
+ if (verbose_only_flag)
{
- const char *const *j;
-
- if (verbose_only_flag)
+ for (j = commands[i].argv; *j; j++)
{
- for (j = commands[i].argv; *j; j++)
+ const char *p;
+ for (p = *j; *p; ++p)
+ if (!ISALNUM ((unsigned char) *p)
+ && *p != '_' && *p != '/' && *p != '-' && *p != '.')
+ break;
+ if (*p || !*j)
{
- const char *p;
+ fprintf (stderr, " \"");
for (p = *j; *p; ++p)
- if (!ISALNUM ((unsigned char) *p)
- && *p != '_' && *p != '/' && *p != '-' && *p != '.')
- break;
- if (*p || !*j)
{
- fprintf (stderr, " \"");
- for (p = *j; *p; ++p)
- {
- if (*p == '"' || *p == '\\' || *p == '$')
- fputc ('\\', stderr);
- fputc (*p, stderr);
- }
- fputc ('"', stderr);
+ if (*p == '"' || *p == '\\' || *p == '$')
+ fputc ('\\', stderr);
+ fputc (*p, stderr);
}
- /* If it's empty, print "". */
- else if (!**j)
- fprintf (stderr, " \"\"");
- else
- fprintf (stderr, " %s", *j);
+ fputc ('"', stderr);
}
- }
- else
- for (j = commands[i].argv; *j; j++)
/* If it's empty, print "". */
- if (!**j)
+ else if (!**j)
fprintf (stderr, " \"\"");
else
fprintf (stderr, " %s", *j);
-
- /* Print a pipe symbol after all but the last command. */
- if (i + 1 != n_commands)
- fprintf (stderr, " |");
- fprintf (stderr, "\n");
+ }
}
- fflush (stderr);
- if (verbose_only_flag != 0)
- {
- /* verbose_only_flag should act as if the spec was
- executed, so increment execution_count before
- returning. This prevents spurious warnings about
- unused linker input files, etc. */
- execution_count++;
- return 0;
- }
+ else
+ for (j = commands[i].argv; *j; j++)
+ /* If it's empty, print "". */
+ if (!**j)
+ fprintf (stderr, " \"\"");
+ else
+ fprintf (stderr, " %s", *j);
+
+ /* Print a pipe symbol after all but the last command. */
+ if (i + 1 != n_commands)
+ fprintf (stderr, " |");
+ fprintf (stderr, "\n");
+ }
+ fflush (stderr);
+ if (verbose_only_flag != 0)
+ {
+ /* verbose_only_flag should act as if the spec was
+ executed, so increment execution_count before
+ returning. This prevents spurious warnings about
+ unused linker input files, etc. */
+ execution_count++;
+ return 1;
+ }
#ifdef DEBUG
- fnotice (stderr, "\nGo ahead? (y or n) ");
- fflush (stderr);
- i = getchar ();
- if (i != '\n')
- while (getchar () != '\n')
- ;
-
- if (i != 'y' && i != 'Y')
- return 0;
+ fnotice (stderr, "\nGo ahead? (y or n) ");
+ fflush (stderr);
+ i = getchar ();
+ if (i != '\n')
+ while (getchar () != '\n')
+ ;
+
+ if (i != 'y' && i != 'Y')
+ return 1;
#endif /* DEBUG */
- }
+
+ return 0;
+}
#ifdef ENABLE_VALGRIND_CHECKING
+
+/* Append valgrind to each program. */
+
+static void
+append_valgrind (struct obstack *to_be_released,
+ int n_commands, struct command commands[])
+{
+ int i;
+
/* Run the each command through valgrind. To simplify prepending the
path to valgrind and the option "-q" (for quiet operation unless
something triggers), we allocate a separate argv array. */
@@ -3183,7 +3131,7 @@ execute (void)
for (argc = 0; commands[i].argv[argc] != NULL; argc++)
;
- argv = XALLOCAVEC (const char *, argc + 3);
+ argv = obstack_alloc (to_be_released, (argc + 3) * sizeof (const char *));
argv[0] = VALGRIND_PATH;
argv[1] = "-q";
@@ -3194,15 +3142,16 @@ execute (void)
commands[i].argv = argv;
commands[i].prog = argv[0];
}
+}
#endif
- /* Run each piped subprocess. */
+/* Launch a list of commands asynchronously. */
- pex = pex_init (PEX_USE_PIPES | ((report_times || report_times_to_file)
- ? PEX_RECORD_TIMES : 0),
- progname, temp_filename);
- if (pex == NULL)
- fatal_error (input_location, "%<pex_init%> failed: %m");
+static void
+async_launch_commands (struct pex_obj *pex,
+ int n_commands, struct command commands[])
+{
+ int i;
for (i = 0; i < n_commands; i++)
{
@@ -3229,150 +3178,296 @@ execute (void)
}
execution_count++;
+}
- /* Wait for all the subprocesses to finish. */
- {
- int *statuses;
- struct pex_time *times = NULL;
- int ret_code = 0;
+/* Wait for all the subprocesses to finish. Return 0 on success, -1 on
+ failure. */
- statuses = (int *) alloca (n_commands * sizeof (int));
- if (!pex_get_status (pex, n_commands, statuses))
- fatal_error (input_location, "failed to get exit status: %m");
+static int
+await_commands_to_finish (struct pex_obj *pex,
+ int n_commands, struct command commands[])
+{
- if (report_times || report_times_to_file)
- {
- times = (struct pex_time *) alloca (n_commands * sizeof (struct pex_time));
- if (!pex_get_times (pex, n_commands, times))
- fatal_error (input_location, "failed to get process times: %m");
- }
+ int *statuses;
+ struct pex_time *times = NULL;
+ int ret_code = 0, i;
- pex_free (pex);
+ statuses = (int *) alloca (n_commands * sizeof (int));
+ if (!pex_get_status (pex, n_commands, statuses))
+ fatal_error (input_location, "failed to get exit status: %m");
- for (i = 0; i < n_commands; ++i)
- {
- int status = statuses[i];
+ if (report_times || report_times_to_file)
+ {
+ times = (struct pex_time *) alloca (n_commands * sizeof (*times));
+ if (!pex_get_times (pex, n_commands, times))
+ fatal_error (input_location, "failed to get process times: %m");
+ }
- if (WIFSIGNALED (status))
- switch (WTERMSIG (status))
- {
- case SIGINT:
- case SIGTERM:
- /* SIGQUIT and SIGKILL are not available on MinGW. */
+ for (i = 0; i < n_commands; ++i)
+ {
+ int status = statuses[i];
+
+ if (WIFSIGNALED (status))
+ switch (WTERMSIG (status))
+ {
+ case SIGINT:
+ case SIGTERM:
+ /* SIGQUIT and SIGKILL are not available on MinGW. */
#ifdef SIGQUIT
- case SIGQUIT:
+ case SIGQUIT:
#endif
#ifdef SIGKILL
- case SIGKILL:
+ case SIGKILL:
#endif
- /* The user (or environment) did something to the
- inferior. Making this an ICE confuses the user into
- thinking there's a compiler bug. Much more likely is
- the user or OOM killer nuked it. */
- fatal_error (input_location,
- "%s signal terminated program %s",
- strsignal (WTERMSIG (status)),
- commands[i].prog);
- break;
+ /* The user (or environment) did something to the
+ inferior. Making this an ICE confuses the user into
+ thinking there's a compiler bug. Much more likely is
+ the user or OOM killer nuked it. */
+ fatal_error (input_location,
+ "%s signal terminated program %s",
+ strsignal (WTERMSIG (status)),
+ commands[i].prog);
+ break;
#ifdef SIGPIPE
- case SIGPIPE:
- /* SIGPIPE is a special case. It happens in -pipe mode
- when the compiler dies before the preprocessor is
- done, or the assembler dies before the compiler is
- done. There's generally been an error already, and
- this is just fallout. So don't generate another
- error unless we would otherwise have succeeded. */
- if (signal_count || greatest_status >= MIN_FATAL_STATUS)
- {
- signal_count++;
- ret_code = -1;
- break;
- }
+ case SIGPIPE:
+ /* SIGPIPE is a special case. It happens in -pipe mode
+ when the compiler dies before the preprocessor is
+ done, or the assembler dies before the compiler is
+ done. There's generally been an error already, and
+ this is just fallout. So don't generate another
+ error unless we would otherwise have succeeded. */
+ if (signal_count || greatest_status >= MIN_FATAL_STATUS)
+ {
+ signal_count++;
+ ret_code = -1;
+ break;
+ }
#endif
- /* FALLTHROUGH */
+ /* FALLTHROUGH. */
- default:
- /* The inferior failed to catch the signal. */
- internal_error_no_backtrace ("%s signal terminated program %s",
- strsignal (WTERMSIG (status)),
- commands[i].prog);
- }
- else if (WIFEXITED (status)
- && WEXITSTATUS (status) >= MIN_FATAL_STATUS)
- {
- /* For ICEs in cc1, cc1obj, cc1plus see if it is
- reproducible or not. */
- const char *p;
- if (flag_report_bug
- && WEXITSTATUS (status) == ICE_EXIT_CODE
- && i == 0
- && (p = strrchr (commands[0].argv[0], DIR_SEPARATOR))
- && ! strncmp (p + 1, "cc1", 3))
- try_generate_repro (commands[0].argv);
- if (WEXITSTATUS (status) > greatest_status)
- greatest_status = WEXITSTATUS (status);
- ret_code = -1;
+ default:
+ /* The inferior failed to catch the signal. */
+ internal_error_no_backtrace ("%s signal terminated program %s",
+ strsignal (WTERMSIG (status)),
+ commands[i].prog);
}
+ else if (WIFEXITED (status)
+ && WEXITSTATUS (status) >= MIN_FATAL_STATUS)
+ {
+ /* For ICEs in cc1, cc1obj, cc1plus see if it is
+ reproducible or not. */
+ const char *p;
+ if (flag_report_bug
+ && WEXITSTATUS (status) == ICE_EXIT_CODE
+ && i == 0
+ && (p = strrchr (commands[0].argv[0], DIR_SEPARATOR))
+ && ! strncmp (p + 1, "cc1", 3))
+ try_generate_repro (commands[0].argv);
+ if (WEXITSTATUS (status) > greatest_status)
+ greatest_status = WEXITSTATUS (status);
+ ret_code = -1;
+ }
- if (report_times || report_times_to_file)
- {
- struct pex_time *pt = ×[i];
- double ut, st;
+ if (report_times || report_times_to_file)
+ {
+ struct pex_time *pt = ×[i];
+ double ut, st;
- ut = ((double) pt->user_seconds
- + (double) pt->user_microseconds / 1.0e6);
- st = ((double) pt->system_seconds
- + (double) pt->system_microseconds / 1.0e6);
+ ut = ((double) pt->user_seconds
+ + (double) pt->user_microseconds / 1.0e6);
+ st = ((double) pt->system_seconds
+ + (double) pt->system_microseconds / 1.0e6);
- if (ut + st != 0)
- {
- if (report_times)
- fnotice (stderr, "# %s %.2f %.2f\n",
- commands[i].prog, ut, st);
+ if (ut + st != 0)
+ {
+ if (report_times)
+ fnotice (stderr, "# %s %.2f %.2f\n",
+ commands[i].prog, ut, st);
- if (report_times_to_file)
- {
- int c = 0;
- const char *const *j;
+ if (report_times_to_file)
+ {
+ int c = 0;
+ const char *const *j;
- fprintf (report_times_to_file, "%g %g", ut, st);
+ fprintf (report_times_to_file, "%g %g", ut, st);
- for (j = &commands[i].prog; *j; j = &commands[i].argv[++c])
- {
- const char *p;
- for (p = *j; *p; ++p)
- if (*p == '"' || *p == '\\' || *p == '$'
- || ISSPACE (*p))
- break;
+ for (j = &commands[i].prog; *j; j = &commands[i].argv[++c])
+ {
+ const char *p;
+ for (p = *j; *p; ++p)
+ if (*p == '"' || *p == '\\' || *p == '$'
+ || ISSPACE (*p))
+ break;
- if (*p)
- {
- fprintf (report_times_to_file, " \"");
- for (p = *j; *p; ++p)
- {
- if (*p == '"' || *p == '\\' || *p == '$')
- fputc ('\\', report_times_to_file);
- fputc (*p, report_times_to_file);
- }
- fputc ('"', report_times_to_file);
- }
- else
- fprintf (report_times_to_file, " %s", *j);
- }
+ if (*p)
+ {
+ fprintf (report_times_to_file, " \"");
+ for (p = *j; *p; ++p)
+ {
+ if (*p == '"' || *p == '\\' || *p == '$')
+ fputc ('\\', report_times_to_file);
+ fputc (*p, report_times_to_file);
+ }
+ fputc ('"', report_times_to_file);
+ }
+ else
+ fprintf (report_times_to_file, " %s", *j);
+ }
- fputc ('\n', report_times_to_file);
- }
- }
- }
+ fputc ('\n', report_times_to_file);
+ }
+ }
+ }
+ }
+
+ return ret_code;
+}
+
+/* Split a single command with pipes into several commands. */
+
+static void
+split_commands (vec<const_char_p> *argbuf_p,
+ int n_commands, struct command commands[])
+{
+ int i;
+ const char *arg;
+ vec<const_char_p> &argbuf = *argbuf_p;
+
+ for (n_commands = 1, i = 0; argbuf.iterate (i, &arg); i++)
+ if (arg && strcmp (arg, "|") == 0)
+ { /* each command. */
+ const char *string;
+#if defined (__MSDOS__) || defined (OS2) || defined (VMS)
+ fatal_error (input_location, "%<-pipe%> not supported");
+#endif
+ argbuf[i] = 0; /* Termination of command args. */
+ commands[n_commands].prog = argbuf[i + 1];
+ commands[n_commands].argv
+ = &(argbuf.address ())[i + 1];
+ string = find_a_file (&exec_prefixes, commands[n_commands].prog,
+ X_OK, false);
+ if (string)
+ commands[n_commands].argv[0] = string;
+ n_commands++;
}
+}
- if (commands[0].argv[0] != commands[0].prog)
- free (CONST_CAST (char *, commands[0].argv[0]));
+struct command
+*parse_argbuf (vec <const_char_p> *argbuf_p, int *n)
+{
+ int i, n_commands;
+ vec<const_char_p> &argbuf = *argbuf_p;
+ const char *arg;
+ struct command *commands;
- return ret_code;
- }
+ /* Count # of piped commands. */
+ for (n_commands = 1, i = 0; argbuf.iterate (i, &arg); i++)
+ if (strcmp (arg, "|") == 0)
+ n_commands++;
+
+ /* Get storage for each command. */
+ commands = XNEWVEC (struct command, n_commands);
+
+ /* Split argbuf into its separate piped processes,
+ and record info about each one.
+ Also search for the programs that are to be run. */
+
+ argbuf.safe_push (0);
+
+ commands[0].prog = argbuf[0]; /* first command. */
+ commands[0].argv = argbuf.address ();
+
+ split_commands (argbuf_p, n_commands, commands);
+
+ *n = n_commands;
+ return commands;
+}
+
+/* Execute the command specified by the arguments on the current line of spec.
+ When using pipes, this includes several piped-together commands
+ with `|' between them.
+
+ Return 0 if successful, -1 if failed. */
+
+static int
+execute (void)
+{
+ struct pex_obj *pex;
+ struct command *commands; /* each command buffer with program to call
+ and arguments. */
+ int n_commands; /* # of command. */
+ int ret = 0;
+
+ gcc_assert (!processing_spec_function);
+
+ if (wrapper_string)
+ {
+ char *string = find_a_file (&exec_prefixes, argbuf[0], X_OK, false);
+ if (string)
+ argbuf[0] = string;
+ insert_wrapper (wrapper_string);
+ }
+
+ /* Parse the argbuf into several commands. */
+ commands = parse_argbuf (&argbuf, &n_commands);
+
+ if (!wrapper_string)
+ {
+ char *string = find_a_file (&exec_prefixes, commands[0].prog,
+ X_OK, false);
+ if (string)
+ commands[0].argv[0] = string;
+ }
+
+ /* If -v, print what we are about to do, and maybe query. */
+
+ if (verbose_flag)
+ {
+ int ret_verbose = print_verbose (n_commands, commands);
+ if (ret_verbose > 0)
+ {
+ ret = 0;
+ goto cleanup;
+ }
+ }
+
+#ifdef ENABLE_VALGRIND_CHECKING
+ /* Stack of strings to be released on function return. */
+ struct obstack to_be_released;
+ obstack_init (&to_be_released);
+ append_valgrind (&to_be_released, n_commands, commands);
+#endif
+
+ /* Run each piped subprocess. */
+
+ pex = pex_init (PEX_USE_PIPES | ((report_times || report_times_to_file)
+ ? PEX_RECORD_TIMES : 0),
+ progname, temp_filename);
+ if (pex == NULL)
+ fatal_error (input_location, "%<pex_init%> failed: %m");
+
+ /* Lauch the commands. */
+ async_launch_commands (pex, n_commands, commands);
+
+ /* Await them to be done. */
+ ret = await_commands_to_finish (pex, n_commands, commands);
+
+ /* Cleanup. */
+ pex_free (pex);
+
+#ifdef ENABLE_VALGRIND_CHECKING
+ obstack_free (&to_be_released, NULL);
+#endif
+
+cleanup:
+ if (commands[0].argv[0] != commands[0].prog)
+ free (CONST_CAST (char *, commands[0].argv[0]));
+
+ free (commands);
+
+ return ret;
}
/* Find all the switches given to us
--
2.26.2