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(-)

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 7f484da04f0..3ad1b9f7fb9 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -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.
diff --git a/gcc/gcc.c b/gcc/gcc.c
index b0d0308f127..082a5d1d4a8 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -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 = &times[i];
-	    double ut, st;
+      if (report_times || report_times_to_file)
+	{
+	  struct pex_time *pt = &times[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

