aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorGiuliano Belinassi <giuliano.belinassi@usp.br>2020-07-27 23:19:21 -0300
committerGiuliano Belinassi <giuliano.belinassi@usp.br>2020-07-27 23:19:21 -0300
commit47981062961083e03a08123169ed9d55ef59f633 (patch)
treeb5235e255b50c47d244a5a8e021202a659bfeeae /gcc
parentef9d764a47f797420db9413de670d2e1e140afbc (diff)
downloadgcc-47981062961083e03a08123169ed9d55ef59f633.zip
gcc-47981062961083e03a08123169ed9d55ef59f633.tar.gz
gcc-47981062961083e03a08123169ed9d55ef59f633.tar.bz2
Intergrate with GNU Jobserver
This commit makes GCC communicate with GNU Make's Jobserver using the -fparallel-jobs=jobserver flag. It also disables automatic parallelization when -fparallel-jobs are not providen. gcc/ChangeLog 2020-07-27 Giuliano Belinassi <giuliano.belinassi@usp.br> * Makefile.in: Mark jobserver.cc to be compiled. * jobserver.cc: New file. * cgraphunit.c (is_number): New function. * (compile): Move parallelization logic to... * (maybe_compile_in_parallel): Here. * common.opt: New flag. * gcc.c (execute): Check for fparallel-jobs before calling append_split_outputs. * toplev.c (main): Finalize jobserver before exit.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog12
-rw-r--r--gcc/Makefile.in1
-rw-r--r--gcc/cgraphunit.c255
-rw-r--r--gcc/common.opt8
-rw-r--r--gcc/gcc.c2
-rw-r--r--gcc/jobserver.cc185
-rw-r--r--gcc/jobserver.h33
-rw-r--r--gcc/toplev.c7
8 files changed, 406 insertions, 97 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 80e57b0..ed48394 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,15 @@
+2020-07-27 Giuliano Belinassi <giuliano.belinassi@usp.br>
+
+ * Makefile.in: Mark jobserver.cc to be compiled.
+ * jobserver.cc: New file.
+ * cgraphunit.c (is_number): New function.
+ * (compile): Move parallelization logic to...
+ * (maybe_compile_in_parallel): Here.
+ * common.opt: New flag.
+ * gcc.c (execute): Check for fparallel-jobs before calling
+ append_split_outputs.
+ * toplev.c (main): Finalize jobserver before exit.
+
2020-07-16 Giuliano Belinassi <giuliano.belinassi@usp.br>
* ipa-sra.c (gate): Enable when split_outputs.
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index a9b7926..ae2827e 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1429,6 +1429,7 @@ OBJS = \
ira-color.o \
ira-emit.o \
ira-lives.o \
+ jobserver.o \
jump.o \
langhooks.o \
lcm.o \
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 6ea665a..a029455 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -207,6 +207,7 @@ along with GCC; see the file COPYING3. If not see
#include "attribs.h"
#include "ipa-inline.h"
#include "lto-partition.h"
+#include "jobserver.h"
/* Queue of cgraph nodes scheduled to be added into cgraph. This is a
secondary queue used during optimization to accommodate passes that
@@ -2741,6 +2742,163 @@ symbol_table::output_weakrefs (void)
}
}
+static bool is_number (const char *str)
+{
+ while (*str != '\0')
+ switch (*str++)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ continue;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+static bool maybe_compile_in_parallel (void)
+{
+ struct symtab_node *node;
+ int partitions, i, j;
+ int *pids;
+
+ bool promote_statics = param_promote_statics;
+ bool balance = param_balance_partitions;
+ bool jobserver = false;
+ int num_jobs = -1;
+
+ if (!flag_parallel_jobs || !split_outputs)
+ return false;
+
+ if (!strcmp (flag_parallel_jobs, "jobserver"))
+ jobserver = jobserver_initialize ();
+ else if (is_number (flag_parallel_jobs))
+ num_jobs = atoi (flag_parallel_jobs);
+ else
+ gcc_unreachable ();
+
+ if (num_jobs < 0 && !jobserver)
+ {
+ error (UNKNOWN_LOCATION,
+ "'-fparallel-jobs=jobserver', but no GNU Jobserver found");
+ return false;
+ }
+
+ if (num_jobs == 0)
+ {
+ inform (UNKNOWN_LOCATION, "-fparallel-jobs=0 makes no sense");
+ return false;
+ }
+
+ /* Trick the compiler to think that we are in WPA. */
+ flag_wpa = "";
+ symtab_node::checking_verify_symtab_nodes ();
+
+ /* Partition the program so that COMDATs get mapped to the same
+ partition. If promote_statics is true, it also maps statics
+ to the same partition. If balance is true, try to balance the
+ partitions for compilation performance. */
+ lto_merge_comdat_map (balance, promote_statics);
+
+ /* AUX pointers are used by partitioning code to bookkeep number of
+ partitions symbol is in. This is no longer needed. */
+ FOR_EACH_SYMBOL (node)
+ node->aux = NULL;
+
+ /* We decided that partitioning is a bad idea. In this case, just
+ proceed with the default compilation method. */
+ if (ltrans_partitions.length () <= 1)
+ {
+ flag_wpa = NULL;
+ jobserver_finalize ();
+ return false;
+ }
+
+ /* Find out statics that need to be promoted
+ to globals with hidden visibility because they are accessed from
+ multiple partitions. */
+ lto_promote_cross_file_statics (promote_statics);
+
+ /* Check if we have variables being referenced across partitions. */
+ lto_check_usage_from_other_partitions ();
+
+ /* Trick the compiler to think we are not in WPA anymore. */
+ flag_wpa = NULL;
+
+ partitions = ltrans_partitions.length ();
+ pids = XALLOCAVEC (pid_t, partitions);
+
+ /* There is no point in launching more jobs than we have partitions. */
+ if (num_jobs > partitions)
+ num_jobs = partitions;
+
+ /* Trick the compiler to think we are in LTRANS mode. */
+ flag_ltrans = true;
+
+ init_additional_asm_names_file ();
+
+ /* Flush asm file, so we don't get repeated output as we fork. */
+ fflush (asm_out_file);
+
+ /* Insert a token for child to consume. */
+ if (jobserver)
+ {
+ num_jobs = partitions;
+ jobserver_return_token ('p');
+ }
+
+ /* Spawn processes. Spawn as soon as there is a free slot. */
+ for (j = 0, i = -num_jobs; i < partitions; i++, j++)
+ {
+ if (i >= 0)
+ {
+ int wstatus, ret;
+ ret = waitpid (pids[i], &wstatus, 0);
+
+ if (ret < 0)
+ internal_error ("Unable to wait child %d to finish", i);
+ else if (WIFEXITED (wstatus))
+ {
+ if (WEXITSTATUS (wstatus) != 0)
+ error ("Child %d exited with error", i);
+ }
+ else if (WIFSIGNALED (wstatus))
+ error ("Child %d aborted with error", i);
+ }
+
+ if (j < partitions)
+ {
+ gcc_assert (ltrans_partitions[j]->symbols > 0);
+
+ if (jobserver)
+ jobserver_get_token ();
+
+ pids[j] = fork ();
+ if (pids[j] == 0)
+ {
+ lto_apply_partition_mask (ltrans_partitions[j]);
+ return true;
+ }
+ }
+ }
+
+ /* Get the token which parent inserted for the childs, which they returned by
+ now. */
+ if (jobserver)
+ jobserver_get_token ();
+ exit (0);
+}
+
+
/* Perform simple optimizations based on callgraph. */
void
@@ -2767,102 +2925,7 @@ symbol_table::compile (const char *name)
{
timevar_start (TV_CGRAPH_IPA_PASSES);
ipa_passes ();
-
- if (split_outputs)
- {
- struct symtab_node *node;
- int partitions, i;
- int *pids;
-
- bool promote_statics = param_promote_statics;
- bool balance = param_balance_partitions;
-
- /* Trick the compiler to think that we are in WPA. */
- flag_wpa = "";
- symtab_node::checking_verify_symtab_nodes ();
-
- /* Partition the program so that COMDATs get mapped to the same
- partition. If promote_statics is true, it also maps statics
- to the same partition. If balance is true, try to balance the
- partitions for compilation performance. */
- lto_merge_comdat_map (balance, promote_statics);
-
- /* AUX pointers are used by partitioning code to bookkeep number of
- partitions symbol is in. This is no longer needed. */
- FOR_EACH_SYMBOL (node)
- node->aux = NULL;
-
- /* We decided that partitioning is a bad idea. In this case, just
- proceed with the default compilation method. */
- if (ltrans_partitions.length () <= 1)
- {
- flag_wpa = NULL;
- goto continue_compilation;
- }
-
- /* Find out statics that need to be promoted
- to globals with hidden visibility because they are accessed from
- multiple partitions. */
- lto_promote_cross_file_statics (promote_statics);
-
- /* Check if we have variables being referenced across partitions. */
- lto_check_usage_from_other_partitions ();
-
- /* Trick the compiler to think we are not in WPA anymore. */
- flag_wpa = NULL;
-
- partitions = ltrans_partitions.length ();
- pids = (int *) alloca (partitions * sizeof (*pids));
-
- /* Trick the compiler to think we are in LTRANS mode. */
- flag_ltrans = true;
-
- init_additional_asm_names_file ();
-
- /* Flush asm file, so we don't get repeated output as we fork. */
- fflush (asm_out_file);
-
- symtab_node::checking_verify_symtab_nodes ();
-
- /* Run serially for now. */
- for (i = 0; i < partitions; ++i)
- {
- gcc_assert (ltrans_partitions[i]->symbols > 0);
-
- pids[i] = fork ();
- if (pids[i] == 0)
- {
- lto_apply_partition_mask (ltrans_partitions[i]);
-
- goto continue_compilation;
- }
- else
- {
- int wstatus;
- waitpid (pids[i], &wstatus, 0);
-
- if (WIFEXITED (wstatus))
- {
- if (WEXITSTATUS (wstatus) == 0)
- continue;
- else
- {
- fprintf (stderr, "Child %d exited with error\n", i);
- internal_error ("Child exited with error");
- }
-
- }
- else if (WIFSIGNALED (wstatus))
- {
- fprintf (stderr, "Child %d aborted due to signal\n", i);
- internal_error ("Child aborted with error");
- }
- }
- }
- exit (0);
- }
-
-continue_compilation:
+ maybe_compile_in_parallel ();
timevar_stop (TV_CGRAPH_IPA_PASSES);
}
/* Do nothing else if any IPA pass found errors or if we are just streaming LTO. */
diff --git a/gcc/common.opt b/gcc/common.opt
index 51ae35e..8b50b72 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -230,6 +230,14 @@ bool flag_disable_hsa = false
Variable
const char *split_outputs = NULL
+; Set to compile in parallel with N jobs, or Jobserver.
+Variable
+const char *flag_parallel_jobs = NULL
+
+fparallel-jobs=
+Common Driver RejectNegative Joined Var(flag_parallel_jobs)
+Compiles in parallel with a number of parallel jobs, or jobserver.
+
###
Driver
diff --git a/gcc/gcc.c b/gcc/gcc.c
index d549626..88ac31e 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -3877,7 +3877,7 @@ execute (void)
/* Parse the argbuf into several commands. */
commands = parse_argbuf (&argbuf, &n_commands);
- if (!have_S && !have_E)
+ if (!have_S && !have_E && flag_parallel_jobs)
append_split_outputs (&storer, &additional_ld, &commands, &n_commands);
if (!wrapper_string)
diff --git a/gcc/jobserver.cc b/gcc/jobserver.cc
new file mode 100644
index 0000000..4c879cc
--- /dev/null
+++ b/gcc/jobserver.cc
@@ -0,0 +1,185 @@
+/* GNU Jobserver Integration Interface.
+ Copyright (C) 2005-2020 Free Software Foundation, Inc.
+
+ Contributed by Giuliano Belinassi <giuliano.belinassi@usp.br>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#include "jobserver.h"
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "diagnostic.h"
+#include "errno.h"
+
+#define JOBSERVER_MAKE_TOKEN '+' /* Token which make sent when invoking GCC. */
+
+bool jobserver_initialized = false;
+bool nonblock_mode = false;
+static int wfd = -1;
+static int rfd = -1;
+
+jobserver_token_t jobserver_curr_token = JOBSERVER_NULL_TOKEN;
+
+/* When using GNU Jobserver but the user did not prepend the recursive make
+ token `+' to the GCC invocation, Make can close the file descriptors used
+ to comunicate with us, and there is no reliable way to detect this.
+ Therefore the best we can do is crash and alert the user to do hte right
+ thing. */
+static void jobserver_crash ()
+{
+ error ("-fparallel-jobs=jobserver, but Make jobserver pipe is closed");
+ inform (UNKNOWN_LOCATION,
+ "GCC must be invoked with a prepended `+' in the Makefile rule's command "
+ "to use the jobserver parallel mode");
+ exit (1);
+}
+
+/* Initialize this interface. We try to find whether the Jobserver is active
+ and working. */
+bool jobserver_initialize ()
+{
+ bool success;
+ const char *makeflags = getenv ("MAKEFLAGS");
+ if (makeflags == NULL)
+ return false;
+
+ const char *needle = "--jobserver-auth=";
+ const char *n = strstr (makeflags, needle);
+ if (n == NULL)
+ return false;
+
+ success = (sscanf (n + strlen (needle), "%d,%d", &rfd, &wfd) == 2
+ && rfd > 0
+ && wfd > 0
+ && is_valid_fd (rfd)
+ && is_valid_fd (wfd));
+
+ if (!success)
+ return false;
+
+ int flags = fcntl (rfd, F_GETFL);
+ if (flags < 0)
+ return false;
+
+ /* Mark that rfd is O_NONBLOCK, as we will have to use select. */
+ if (flags & O_NONBLOCK)
+ nonblock_mode = true;
+
+ return (jobserver_initialized = true);
+
+#if 0
+ /* Test if we can read from the rfd. */
+ jobserver_token_t token;
+ ssize_t r = read (rfd, &token, sizeof (jobserver_token_t));
+
+ gcc_assert (r == sizeof (jobserver_token_t) || r == 0 || r == -1);
+
+ if (r < 0)
+ {
+ /* Check errno. We may have no problem at all. */
+ if (errno != EAGAIN)
+ return false;
+ }
+
+ /* Reading 0 bytes indicate that fd was closed by parent process. */
+ if (r == 0)
+ return false;
+
+ /* Check if we got a important token that we have to return. */
+ if (token != JOBSERVER_NULL_TOKEN)
+ jobserver_return_token (token);
+#endif
+}
+
+/* Finalize the jobserver, so this interface could be used again later. */
+bool jobserver_finalize ()
+{
+ if (!jobserver_initialized)
+ return false;
+
+ close (rfd);
+ close (wfd);
+
+ rfd = wfd = -1;
+
+ jobserver_initialized = false;
+ return true;
+}
+
+/* Return token to the jobserver. If c is the NULL token, then return
+ the last token we got. */
+void jobserver_return_token (jobserver_token_t c)
+{
+ ssize_t w;
+
+ if (c == JOBSERVER_NULL_TOKEN)
+ c = jobserver_curr_token;
+
+ w = write (wfd, &c, sizeof (jobserver_token_t));
+
+ if (w <= 0)
+ jobserver_crash ();
+}
+
+/* TODO: Check if select if available in our system. */
+#define HAVE_SELECT
+
+/* Retrieve a token from the Jobserver. We have two cases, in which we must be
+ careful. First is when the function pselect is available in our system, as
+ Make will set the read fd as nonblocking and will expect that we use select.
+ (see posixos.c in GNU Make sourcecode).
+ The other is when select is not available in our system, and Make will set
+ it as blocking. */
+char jobserver_get_token ()
+{
+ jobserver_token_t ret = JOBSERVER_NULL_TOKEN;
+ ssize_t r = -1;
+
+ while (r < 0)
+ {
+ if (nonblock_mode)
+ {
+#ifdef HAVE_SELECT
+ fd_set readfd_set;
+
+ FD_ZERO (&readfd_set);
+ FD_SET (rfd, &readfd_set);
+
+ r = select (rfd+1, &readfd_set, NULL, NULL, NULL);
+
+ if (r < 0 && errno == EAGAIN)
+ continue;
+
+ gcc_assert (r > 0);
+#else
+ internal_error ("Make set Jobserver pipe to nonblock mode, but select "
+ "is not supported in your system");
+#endif
+ }
+
+ r = read (rfd, &ret, sizeof (jobserver_token_t));
+
+ if (!(r > 0 || (r < 0 && errno == EAGAIN)))
+ {
+ jobserver_crash ();
+ break;
+ }
+ }
+
+ return (jobserver_curr_token = ret);
+}
diff --git a/gcc/jobserver.h b/gcc/jobserver.h
new file mode 100644
index 0000000..2047cf4
--- /dev/null
+++ b/gcc/jobserver.h
@@ -0,0 +1,33 @@
+/* GNU Jobserver Integration Interface.
+ Copyright (C) 2005-2020 Free Software Foundation, Inc.
+
+ Contributed by Giuliano Belinassi <giuliano.belinassi@usp.br>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#define JOBSERVER_NULL_TOKEN ('\0')
+
+typedef char jobserver_token_t;
+
+extern bool jobserver_initialized;
+extern jobserver_token_t jobserver_curr_token;
+
+bool jobserver_initialize ();
+bool jobserver_finalize ();
+jobserver_token_t jobserver_get_token ();
+void jobserver_return_token (jobserver_token_t);
+
diff --git a/gcc/toplev.c b/gcc/toplev.c
index b62ba25..057812d 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -85,6 +85,7 @@ along with GCC; see the file COPYING3. If not see
#include "dump-context.h"
#include "print-tree.h"
#include "optinfo-emit-json.h"
+#include "jobserver.h"
#if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO)
#include "dbxout.h"
@@ -2481,6 +2482,12 @@ toplev::main (int argc, char **argv)
finalize_plugins ();
+ if (jobserver_initialized)
+ {
+ jobserver_return_token (JOBSERVER_NULL_TOKEN);
+ jobserver_finalize ();
+ }
+
after_memory_report = true;
if (seen_error () || werrorcount)