Mercurial > hg > CbC > CbC_gcc
view gcc/lto-wrapper.c @ 72:20c18990c3e4
merge gcc/c-typeck.c
author | Nobuyasu Oshiro <dimolto@cr.ie.u-ryukyu.ac.jp> |
---|---|
date | Sun, 21 Aug 2011 22:22:32 +0900 |
parents | f6334be47118 |
children | 04ced10e8804 |
line wrap: on
line source
/* Wrapper to call lto. Used by collect2 and the linker plugin. Copyright (C) 2009, 2010 Free Software Foundation, Inc. Factored out of collect2 by Rafael Espindola <espindola@google.com> 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/>. */ /* This program is passed a gcc, a list of gcc arguments and a list of object files containing IL. It scans the argument list to check if we are in whopr mode or not modifies the arguments and needed and prints a list of output files on stdout. Example: $ lto-wrapper gcc/xgcc -B gcc a.o b.o -o test -flto The above will print something like /tmp/ccwbQ8B2.lto.o If WHOPR is used instead, more than one file might be produced ./ccXj2DTk.lto.ltrans.o ./ccCJuXGv.lto.ltrans.o */ #include "config.h" #include "system.h" #include "coretypes.h" #include "intl.h" #include "obstack.h" int debug; /* true if -save-temps. */ int verbose; /* true if -v. */ enum lto_mode_d { LTO_MODE_NONE, /* Not doing LTO. */ LTO_MODE_LTO, /* Normal LTO. */ LTO_MODE_WHOPR /* WHOPR. */ }; /* Current LTO mode. */ static enum lto_mode_d lto_mode = LTO_MODE_NONE; static char *ltrans_output_file; static char *flto_out; static char *args_name; static unsigned int nr; static char **input_names; static char **output_names; static char *makefile; static void maybe_unlink_file (const char *); /* Delete tempfiles. */ static void lto_wrapper_cleanup (void) { static bool cleanup_done = false; unsigned int i; if (cleanup_done) return; /* Setting cleanup_done prevents an infinite loop if one of the calls to maybe_unlink_file fails. */ cleanup_done = true; if (ltrans_output_file) maybe_unlink_file (ltrans_output_file); if (flto_out) maybe_unlink_file (flto_out); if (args_name) maybe_unlink_file (args_name); if (makefile) maybe_unlink_file (makefile); for (i = 0; i < nr; ++i) { maybe_unlink_file (input_names[i]); if (output_names[i]) maybe_unlink_file (output_names[i]); } } static void fatal_signal (int signum) { signal (signum, SIG_DFL); lto_wrapper_cleanup (); /* Get the same signal again, this time not handled, so its normal effect occurs. */ kill (getpid (), signum); } /* Just die. CMSGID is the error message. */ static void __attribute__ ((format (printf, 1, 2))) fatal (const char * cmsgid, ...) { va_list ap; va_start (ap, cmsgid); fprintf (stderr, "lto-wrapper: "); vfprintf (stderr, _(cmsgid), ap); fprintf (stderr, "\n"); va_end (ap); lto_wrapper_cleanup (); exit (FATAL_EXIT_CODE); } /* Die when sys call fails. CMSGID is the error message. */ static void __attribute__ ((format (printf, 1, 2))) fatal_perror (const char *cmsgid, ...) { int e = errno; va_list ap; va_start (ap, cmsgid); fprintf (stderr, "lto-wrapper: "); vfprintf (stderr, _(cmsgid), ap); fprintf (stderr, ": %s\n", xstrerror (e)); va_end (ap); lto_wrapper_cleanup (); exit (FATAL_EXIT_CODE); } /* Execute a program, and wait for the reply. ARGV are the arguments. The last one must be NULL. */ static struct pex_obj * collect_execute (char **argv) { struct pex_obj *pex; const char *errmsg; int err; if (verbose) { char **p_argv; const char *str; for (p_argv = argv; (str = *p_argv) != (char *) 0; p_argv++) fprintf (stderr, " %s", str); fprintf (stderr, "\n"); } fflush (stdout); fflush (stderr); pex = pex_init (0, "lto-wrapper", NULL); if (pex == NULL) fatal_perror ("pex_init failed"); /* Do not use PEX_LAST here, we use our stdout for communicating with collect2 or the linker-plugin. Any output from the sub-process will confuse that. */ errmsg = pex_run (pex, PEX_SEARCH, argv[0], argv, NULL, NULL, &err); if (errmsg != NULL) { if (err != 0) { errno = err; fatal_perror (errmsg); } else fatal (errmsg); } return pex; } /* Wait for a process to finish, and exit if a nonzero status is found. PROG is the program name. PEX is the process we should wait for. */ static int collect_wait (const char *prog, struct pex_obj *pex) { int status; if (!pex_get_status (pex, 1, &status)) fatal_perror ("can't get program status"); pex_free (pex); if (status) { if (WIFSIGNALED (status)) { int sig = WTERMSIG (status); if (WCOREDUMP (status)) fatal ("%s terminated with signal %d [%s], core dumped", prog, sig, strsignal (sig)); else fatal ("%s terminated with signal %d [%s]", prog, sig, strsignal (sig)); } if (WIFEXITED (status)) fatal ("%s returned %d exit status", prog, WEXITSTATUS (status)); } return 0; } /* Unlink a temporary LTRANS file unless requested otherwise. */ static void maybe_unlink_file (const char *file) { if (! debug) { if (unlink_if_ordinary (file) && errno != ENOENT) fatal_perror ("deleting LTRANS file %s", file); } else fprintf (stderr, "[Leaving LTRANS %s]\n", file); } /* Execute program ARGV[0] with arguments ARGV. Wait for it to finish. */ static void fork_execute (char **argv) { struct pex_obj *pex; char *new_argv[3]; char *at_args; FILE *args; int status; args_name = make_temp_file (".args"); at_args = concat ("@", args_name, NULL); args = fopen (args_name, "w"); if (args == NULL) fatal ("failed to open %s", args_name); status = writeargv (&argv[1], args); if (status) fatal ("could not write to temporary file %s", args_name); fclose (args); new_argv[0] = argv[0]; new_argv[1] = at_args; new_argv[2] = NULL; pex = collect_execute (new_argv); collect_wait (new_argv[0], pex); maybe_unlink_file (args_name); args_name = NULL; free (at_args); } /* Template of LTRANS dumpbase suffix. */ #define DUMPBASE_SUFFIX ".ltrans18446744073709551615" /* Execute gcc. ARGC is the number of arguments. ARGV contains the arguments. */ static void run_gcc (unsigned argc, char *argv[]) { unsigned i, j; const char **new_argv; const char **argv_ptr; char *list_option_full = NULL; const char *linker_output = NULL; const char *collect_gcc_options, *collect_gcc; struct obstack env_obstack; bool seen_o = false; int parallel = 0; int jobserver = 0; bool no_partition = false; /* Get the driver and options. */ collect_gcc = getenv ("COLLECT_GCC"); if (!collect_gcc) fatal ("environment variable COLLECT_GCC must be set"); /* Set the CFLAGS environment variable. */ collect_gcc_options = getenv ("COLLECT_GCC_OPTIONS"); if (!collect_gcc_options) fatal ("environment variable COLLECT_GCC_OPTIONS must be set"); /* Count arguments. */ i = 0; for (j = 0; collect_gcc_options[j] != '\0'; ++j) if (collect_gcc_options[j] == '\'') ++i; if (i % 2 != 0) fatal ("malformed COLLECT_GCC_OPTIONS"); /* Initalize the common arguments for the driver. */ new_argv = (const char **) xmalloc ((15 + i / 2 + argc) * sizeof (char *)); argv_ptr = new_argv; *argv_ptr++ = collect_gcc; *argv_ptr++ = "-xlto"; *argv_ptr++ = "-c"; for (j = 0; collect_gcc_options[j] != '\0'; ++j) if (collect_gcc_options[j] == '\'') { char *option; ++j; i = j; while (collect_gcc_options[j] != '\'') ++j; obstack_init (&env_obstack); obstack_grow (&env_obstack, &collect_gcc_options[i], j - i); obstack_1grow (&env_obstack, 0); option = XOBFINISH (&env_obstack, char *); if (seen_o) { linker_output = option; seen_o = false; continue; } /* If we see -o, skip it and skip and record its argument. */ if (option[0] == '-' && option[1] == 'o') { if (option[2] == '\0') seen_o = true; else linker_output = &option[2]; continue; } if (strcmp (option, "-save-temps") == 0) debug = 1; if (strcmp (option, "-v") == 0) verbose = 1; if (strcmp (option, "-flto-partition=none") == 0) no_partition = true; /* We've handled these LTO options, do not pass them on. */ if (strncmp (option, "-flto=", 6) == 0 || !strcmp (option, "-flto")) { lto_mode = LTO_MODE_WHOPR; if (option[5] == '=') { if (!strcmp (option + 6, "jobserver")) { jobserver = 1; parallel = 1; } else { parallel = atoi (option + 6); if (parallel <= 1) parallel = 0; } } } else *argv_ptr++ = option; } if (no_partition) { lto_mode = LTO_MODE_LTO; jobserver = 0; parallel = 0; } if (linker_output) { char *output_dir, *base, *name; bool bit_bucket = strcmp (linker_output, HOST_BIT_BUCKET) == 0; output_dir = xstrdup (linker_output); base = output_dir; for (name = base; *name; name++) if (IS_DIR_SEPARATOR (*name)) base = name + 1; *base = '\0'; linker_output = &linker_output[base - output_dir]; if (*output_dir == '\0') { static char current_dir[] = { '.', DIR_SEPARATOR, '\0' }; output_dir = current_dir; } if (!bit_bucket) { *argv_ptr++ = "-dumpdir"; *argv_ptr++ = output_dir; } *argv_ptr++ = "-dumpbase"; } else argv_ptr--; if (lto_mode == LTO_MODE_LTO) { flto_out = make_temp_file (".lto.o"); if (linker_output) argv_ptr[0] = linker_output; argv_ptr[1] = "-o"; argv_ptr[2] = flto_out; } else { const char *list_option = "-fltrans-output-list="; size_t list_option_len = strlen (list_option); char *tmp; if (linker_output) { char *dumpbase = (char *) xmalloc (strlen (linker_output) + sizeof (".wpa") + 1); strcpy (dumpbase, linker_output); strcat (dumpbase, ".wpa"); argv_ptr[0] = dumpbase; } if (linker_output && debug) { ltrans_output_file = (char *) xmalloc (strlen (linker_output) + sizeof (".ltrans.out") + 1); strcpy (ltrans_output_file, linker_output); strcat (ltrans_output_file, ".ltrans.out"); } else ltrans_output_file = make_temp_file (".ltrans.out"); list_option_full = (char *) xmalloc (sizeof (char) * (strlen (ltrans_output_file) + list_option_len + 1)); tmp = list_option_full; argv_ptr[1] = tmp; strcpy (tmp, list_option); tmp += list_option_len; strcpy (tmp, ltrans_output_file); argv_ptr[2] = "-fwpa"; } /* Append the input objects and possible preceeding arguments. */ for (i = 1; i < argc; ++i) argv_ptr[2 + i] = argv[i]; argv_ptr[2 + i] = NULL; fork_execute (CONST_CAST (char **, new_argv)); if (lto_mode == LTO_MODE_LTO) { printf("%s\n", flto_out); free (flto_out); flto_out = NULL; } else { FILE *stream = fopen (ltrans_output_file, "r"); FILE *mstream = NULL; if (!stream) fatal_perror ("fopen: %s", ltrans_output_file); /* Parse the list of LTRANS inputs from the WPA stage. */ nr = 0; for (;;) { const unsigned piece = 32; char *output_name = NULL; char *buf, *input_name = (char *)xmalloc (piece); size_t len; buf = input_name; cont: if (!fgets (buf, piece, stream)) break; len = strlen (input_name); if (input_name[len - 1] != '\n') { input_name = (char *)xrealloc (input_name, len + piece); buf = input_name + len; goto cont; } input_name[len - 1] = '\0'; if (input_name[0] == '*') output_name = &input_name[1]; nr++; input_names = (char **)xrealloc (input_names, nr * sizeof (char *)); output_names = (char **)xrealloc (output_names, nr * sizeof (char *)); input_names[nr-1] = input_name; output_names[nr-1] = output_name; } fclose (stream); maybe_unlink_file (ltrans_output_file); ltrans_output_file = NULL; if (parallel) { makefile = make_temp_file (".mk"); mstream = fopen (makefile, "w"); } /* Execute the LTRANS stage for each input file (or prepare a makefile to invoke this in parallel). */ for (i = 0; i < nr; ++i) { char *output_name; char *input_name = input_names[i]; /* If it's a pass-through file do nothing. */ if (output_names[i]) continue; /* Replace the .o suffix with a .ltrans.o suffix and write the resulting name to the LTRANS output list. */ obstack_init (&env_obstack); obstack_grow (&env_obstack, input_name, strlen (input_name) - 2); obstack_grow (&env_obstack, ".ltrans.o", sizeof (".ltrans.o")); output_name = XOBFINISH (&env_obstack, char *); /* Adjust the dumpbase if the linker output file was seen. */ if (linker_output) { char *dumpbase = (char *) xmalloc (strlen (linker_output) + sizeof(DUMPBASE_SUFFIX) + 1); snprintf (dumpbase, strlen (linker_output) + sizeof(DUMPBASE_SUFFIX), "%s.ltrans%u", linker_output, i); argv_ptr[0] = dumpbase; } argv_ptr[1] = "-fltrans"; argv_ptr[2] = "-o"; argv_ptr[3] = output_name; argv_ptr[4] = input_name; argv_ptr[5] = NULL; if (parallel) { fprintf (mstream, "%s:\n\t@%s ", output_name, new_argv[0]); for (j = 1; new_argv[j] != NULL; ++j) fprintf (mstream, " '%s'", new_argv[j]); fprintf (mstream, "\n"); } else fork_execute (CONST_CAST (char **, new_argv)); output_names[i] = output_name; } if (parallel) { struct pex_obj *pex; char jobs[32]; fprintf (mstream, "all:"); for (i = 0; i < nr; ++i) fprintf (mstream, " \\\n\t%s", output_names[i]); fprintf (mstream, "\n"); fclose (mstream); if (!jobserver) { /* Avoid passing --jobserver-fd= and similar flags unless jobserver mode is explicitly enabled. */ putenv (xstrdup ("MAKEFLAGS=")); putenv (xstrdup ("MFLAGS=")); } new_argv[0] = getenv ("MAKE"); if (!new_argv[0]) new_argv[0] = "make"; new_argv[1] = "-f"; new_argv[2] = makefile; i = 3; if (!jobserver) { snprintf (jobs, 31, "-j%d", parallel); new_argv[i++] = jobs; } new_argv[i++] = "all"; new_argv[i++] = NULL; pex = collect_execute (CONST_CAST (char **, new_argv)); collect_wait (new_argv[0], pex); maybe_unlink_file (makefile); makefile = NULL; } for (i = 0; i < nr; ++i) { fputs (output_names[i], stdout); putc ('\n', stdout); maybe_unlink_file (input_names[i]); free (input_names[i]); } nr = 0; free (output_names); free (input_names); free (list_option_full); } obstack_free (&env_obstack, NULL); } /* Entry point. */ int main (int argc, char *argv[]) { gcc_init_libintl (); if (signal (SIGINT, SIG_IGN) != SIG_IGN) signal (SIGINT, fatal_signal); #ifdef SIGHUP if (signal (SIGHUP, SIG_IGN) != SIG_IGN) signal (SIGHUP, fatal_signal); #endif if (signal (SIGTERM, SIG_IGN) != SIG_IGN) signal (SIGTERM, fatal_signal); #ifdef SIGPIPE if (signal (SIGPIPE, SIG_IGN) != SIG_IGN) signal (SIGPIPE, fatal_signal); #endif #ifdef SIGCHLD /* We *MUST* set SIGCHLD to SIG_DFL so that the wait4() call will receive the signal. A different setting is inheritable */ signal (SIGCHLD, SIG_DFL); #endif /* We may be called with all the arguments stored in some file and passed with @file. Expand them into argv before processing. */ expandargv (&argc, &argv); run_gcc (argc, argv); return 0; }