Mercurial > hg > CbC > CbC_gcc
view gcc/lto-wrapper.c @ 63:b7f97abdc517 gcc-4.6-20100522
update gcc from gcc-4.5.0 to gcc-4.6
author | ryoma <e075725@ie.u-ryukyu.ac.jp> |
---|---|
date | Mon, 24 May 2010 12:47:05 +0900 |
parents | 77e2b8dfacca |
children | f6334be47118 |
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 -fwhopr 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 "tm.h" #include "intl.h" #include "libiberty.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 void maybe_unlink_file (const char *); /* Delete tempfiles and exit function. */ static void lto_wrapper_exit (int status) { static bool cleanup_done = false; if (!cleanup_done) { /* 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); } exit (status); } /* 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_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_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)) 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); 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; /* 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; /* We've handled these LTO options, do not pass them on. */ if (strcmp (option, "-flto") == 0) lto_mode = LTO_MODE_LTO; else if (strncmp (option, "-fwhopr", 7) == 0) { lto_mode = LTO_MODE_WHOPR; if (option[7] == '=') { parallel = atoi (option+8); if (parallel <= 1) parallel = 0; } } else *argv_ptr++ = option; } if (linker_output) { char *output_dir, *base, *name; 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; } *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; argv_ptr[3] = "-combine"; } else if (lto_mode == LTO_MODE_WHOPR) { 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; } 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"; argv_ptr[3] = "-combine"; } else fatal ("invalid LTO mode"); /* Append the input objects and possible preceeding arguments. */ for (i = 1; i < argc; ++i) argv_ptr[3 + i] = argv[i]; argv_ptr[3 + 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 if (lto_mode == LTO_MODE_WHOPR) { FILE *stream = fopen (ltrans_output_file, "r"); unsigned int nr = 0; char **input_names = NULL; char **output_names = NULL; char *makefile = NULL; FILE *mstream = NULL; if (!stream) fatal_perror ("fopen: %s", ltrans_output_file); argv_ptr[1] = "-fltrans"; if (parallel) { makefile = make_temp_file (".mk"); mstream = fopen (makefile, "w"); } for (;;) { const unsigned piece = 32; char *output_name; 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]; else { /* Otherwise, add FILES[I] to lto_execute_ltrans command line and add the resulting file to LTRANS output list. */ /* 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 *); 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, nr); argv_ptr[0] = dumpbase; } 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 (i = 1; new_argv[i] != NULL; ++i) fprintf (mstream, " '%s'", new_argv[i]); fprintf (mstream, "\n"); } else fork_execute (CONST_CAST (char **, new_argv)); } 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; } 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); new_argv[0] = "make"; new_argv[1] = "-f"; new_argv[2] = makefile; snprintf (jobs, 31, "-j%d", parallel); new_argv[3] = jobs; new_argv[4] = "all"; new_argv[5] = NULL; pex = collect_execute (CONST_CAST (char **, new_argv)); collect_wait (new_argv[0], pex); maybe_unlink_file (makefile); } for (i = 0; i < nr; ++i) { fputs (output_names[i], stdout); putc ('\n', stdout); maybe_unlink_file (input_names[i]); free (input_names[i]); } free (output_names); free (input_names); fclose (stream); maybe_unlink_file (ltrans_output_file); free (list_option_full); } else fatal ("invalid LTO mode"); obstack_free (&env_obstack, NULL); } /* Entry point. */ int main (int argc, char *argv[]) { gcc_init_libintl (); /* 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; }