diff options
-rw-r--r-- | gcc/common.opt | 4 | ||||
-rw-r--r-- | gcc/gcc.c | 1217 | ||||
-rw-r--r-- | gcc/testsuite/driver/a.c | 6 | ||||
-rw-r--r-- | gcc/testsuite/driver/b.c | 6 | ||||
-rw-r--r-- | gcc/testsuite/driver/driver.exp | 80 | ||||
-rw-r--r-- | gcc/testsuite/driver/empty.c | 0 | ||||
-rw-r--r-- | gcc/testsuite/driver/foo.c | 7 |
7 files changed, 1048 insertions, 272 deletions
diff --git a/gcc/common.opt b/gcc/common.opt index 4b08e91859f..4aa3ad8c95b 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -3465,4 +3465,8 @@ fipa-ra Common Report Var(flag_ipa_ra) Optimization Use caller save register across calls if possible. +fsplit-outputs= +Common Joined Var(split_outputs) +-fsplit-outputs=<tempfile> Filename in which current Compilation Unit will be split to. + ; This comment is to ensure we retain the blank line above. diff --git a/gcc/gcc.c b/gcc/gcc.c index 10bc9881aed..c276a11ca7a 100644 --- a/gcc/gcc.c +++ b/gcc/gcc.c @@ -343,6 +343,74 @@ static struct obstack obstack; static struct obstack collect_obstack; +/* This is used to store new argv arrays created dinamically to avoid memory + leaks. */ + +class extra_arg_storer +{ + public: + + /* Initialize the vec with a default size. */ + + extra_arg_storer () + { + string_vec.create (8); + extra_args.create (64); + } + + /* Create new array of strings of size N. */ + const char **create_new (size_t n) + { + const char **ret = XNEWVEC (const char *, n); + extra_args.safe_push (ret); + return ret; + } + + char *create_string (size_t n) + { + char *ret = XNEWVEC (char, n); + string_vec.safe_push (ret); + return ret; + } + + void store (char *str) + { + string_vec.safe_push (str); + } + + ~extra_arg_storer () + { + release_extra_args (); + release_string_vec (); + } + + + private: + + /* Release all allocated strings. */ + void release_extra_args () + { + size_t i; + + for (i = 0; i < extra_args.length (); i++) + free (extra_args[i]); + extra_args.release (); + } + + void release_string_vec () + { + size_t i; + + for (i = 0; i < string_vec.length (); i++) + free (string_vec[i]); + string_vec.release (); + } + + /* Data structure to hold all arrays. */ + vec<const char **> extra_args; + vec<char *> string_vec; +}; + /* Forward declaration for prototypes. */ struct path_prefix; struct prefix_list; @@ -1993,6 +2061,9 @@ static int have_o = 0; /* Was the option -E passed. */ static int have_E = 0; +/* Was the option -S passed. */ +static int have_S = 0; + /* Pointer to output file name passed in with -o. */ static const char *output_file = 0; @@ -3056,158 +3127,522 @@ add_sysrooted_hdrs_prefix (struct path_prefix *pprefix, const char *prefix, require_machine_suffix, os_multilib); } - -/* 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. +struct command +{ + const char *prog; /* program name. */ + const char **argv; /* vector of args. */ +}; - Return 0 if successful, -1 if failed. */ +#define EMPTY_CMD(x) (!((x).prog)) /* Is the provided CMD empty? */ + +/* Check if arg is a call to a compiler. Return false if not, true if yes. */ + +static bool +is_compiler (const char *arg) +{ + static const char *const compilers[] = {"cc1", "cc1plus", "f771"}; + const char* ptr = arg; + + size_t i; + + /* Jump to last '/' of string. */ + while (*arg) + if (*arg++ == '/') + ptr = arg; + + /* Look if current character seems valid. */ + gcc_assert (!(*ptr == '\0' || *ptr == '/')); + + for (i = 0; i < ARRAY_SIZE (compilers); i++) + { + if (!strcmp (ptr, compilers[i])) + return true; + } + + return false; +} + +/* Check if arg is a call to as. Return false if not, true if yes. */ + +static bool +is_assembler (const char *arg) +{ + static const char *const assemblers[] = {"as", "gas"}; + const char* ptr = arg; + + size_t i; + + /* Jump to last '/' of string. */ + while (*arg) + if (*arg++ == '/') + ptr = arg; + + /* Look if current character seems valid. */ + gcc_assert (!(*ptr == '\0' || *ptr == '/')); + + for (i = 0; i < ARRAY_SIZE (assemblers); i++) + { + if (!strcmp (ptr, assemblers[i])) + return true; + } + + return false; +} + +/* Get argv[] array length. */ static int -execute (void) +get_number_of_args (const char *argv[]) +{ + int argc; + + for (argc = 0; argv[argc] != NULL; argc++) + ; + + return argc; +} + +static const char *fsplit_arg (extra_arg_storer *); + +/* Accumulate each line in lines vec. Return true if file exists, false if + not. */ + +static bool +get_file_by_lines (extra_arg_storer *storer, vec<char *> *lines, const char *name) +{ + int buf_size = 64, len = 0; + char *buf = XNEWVEC (char, buf_size); + + + FILE *file = fopen (name, "r"); + + if (!file) + return false; + + while (1) + { + if (!fgets (buf + len, buf_size, file)) + { + free (buf); /* Release buffer we created unecessarily. */ + break; + } + + len = strlen (buf); + if (buf[len - 1] == '\n') /* Check if we indeed read the entire line. */ + { + buf[len - 1] = '\0'; + /* Yes. Insert into the lines vector. */ + lines->safe_push (buf); + len = 0; + + /* Store the created string for future release. */ + storer->store (buf); + buf = XNEWVEC (char, buf_size); + } + else + { + /* No. Increase the buffer size and read again. */ + buf = XRESIZEVEC (char, buf, buf_size * 2); + } + } + + if (lines->length () == 0) + internal_error ("Empty file: %s", name); + + fclose (file); + return true; +} + +static void +identify_asm_file (int argc, const char *argv[], + int *infile_pos, int *outfile_pos) { 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. */ + static const char *asm_extension[] = {"s", "S"}; - gcc_assert (!processing_spec_function); + bool infile_found = false; + bool outfile_found = false; - if (wrapper_string) + for (i = 0; i < argc; i++) { - string = find_a_file (&exec_prefixes, - argbuf[0], X_OK, false); - if (string) - argbuf[0] = string; - insert_wrapper (wrapper_string); + const char *arg = argv[i]; + const char *ext = argv[i]; + unsigned j; + + /* Jump to last '.' of string. */ + while (*arg) + if (*arg++ == '.') + ext = arg; + + if (!infile_found) + for (j = 0; j < ARRAY_SIZE (asm_extension); ++j) + if (!strcmp (ext, asm_extension[j])) + { + infile_found = true; + *infile_pos = i; + break; + } + + if (!outfile_found) + if (!strcmp (ext, "-o")) + { + outfile_found = true; + *outfile_pos = i+1; + } + + if (infile_found && outfile_found) + return; } - /* Count # of piped commands. */ - for (n_commands = 1, i = 0; argbuf.iterate (i, &arg); i++) - if (strcmp (arg, "|") == 0) - n_commands++; + gcc_assert (infile_found && outfile_found); - /* 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. */ +/* Language is one of three things: - argbuf.safe_push (0); + 1) The name of a real programming language. + 2) NULL, indicating that no one has figured out + what it is yet. + 3) '*', indicating that the file should be passed + to the linker. */ +struct infile +{ + const char *name; + const char *language; + const char *temp_additional_asm; + struct compiler *incompiler; + bool compiled; + bool preprocessed; +}; - commands[0].prog = argbuf[0]; /* first command. */ - commands[0].argv = argbuf.address (); +/* Also a vector of input files specified. */ - if (!wrapper_string) +static struct infile *infiles; +static struct infile *current_infile = NULL; + +int n_infiles; + +static int n_infiles_alloc; + +static vec<const char *> temp_object_files; + +/* Get path to the configured ld. */ + +static const char * +get_path_to_ld (void) +{ + const char *ret = find_a_file (&exec_prefixes, LINKER_NAME, X_OK, false); + if (!ret) + ret = "ld"; + + return ret; +} + +/* Check if a hidden -E was passed as argument to something. */ + +static bool +has_hidden_E (int argc, const char *argv[]) +{ + int i; + for (i = 0; i < argc; ++i) + if (!strcmp (argv[i], "-E")) + return true; + + return false; +} + +/* Assembler in the container file are inserted as soon as they are ready. + Sort them so that builds are reproducible. */ + +static void +sort_asm_files (vec <char *> *_lines) +{ + vec <char *> &lines = *_lines; + int i, n = lines.length (); + char **temp_buf = XALLOCAVEC (char *, n); + + for (i = 0; i < n; i++) + temp_buf[i] = lines[i]; + + for (i = 0; i < n; i++) { - string = find_a_file (&exec_prefixes, commands[0].prog, X_OK, false); - if (string) - commands[0].argv[0] = string; + char *no_str = strtok (temp_buf[i], " "); + char *name = strtok (NULL, ""); + + int pos = atoi (no_str); + lines[pos] = name; } +} - 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++; - } +/* Append -fsplit-output=<tempfile> to all calls to compilers. Return true + if a additional call to LD is required to merge the resulting files. */ - /* If -v, print what we are about to do, and maybe query. */ +static void +append_split_outputs (extra_arg_storer *storer, + struct command *additional_ld, + struct command **_commands, + int *_n_commands) +{ + int i; - if (verbose_flag) + struct command *commands = *_commands; + int n_commands = *_n_commands; + + const char **argv; + int argc; + + if (is_compiler (commands[0].prog)) + { + argc = get_number_of_args (commands[0].argv); + argv = storer->create_new (argc + 4); + + memcpy (argv, commands[0].argv, argc * sizeof (const char *)); + + if (!has_hidden_E (argc, commands[0].argv)) + { + const char *extra_argument = fsplit_arg (storer); + argv[argc++] = extra_argument; + } + + if (have_c) + { + argv[argc++] = "-fPIE"; + argv[argc++] = "-fPIC"; + } + + argv[argc] = NULL; + + commands[0].argv = argv; + } + + else if (is_assembler (commands[0].prog)) { - /* For help listings, put a blank line between sub-processes. */ - if (print_help_list) - fputc ('\n', stderr); + vec<char *> additional_asm_files; + + struct command orig; + const char **orig_argv; + int orig_argc; + const char *orig_obj_file; + + int infile_pos = -1; + int outfile_pos = -1; + + static const char *path_to_ld = NULL; + + if (!current_infile->temp_additional_asm) + { + /* Return because we did not create a additional-asm file for this + input. */ + + return; + } + + additional_asm_files.create (2); + + if (!get_file_by_lines (storer, &additional_asm_files, + current_infile->temp_additional_asm)) + { + additional_asm_files.release (); + return; /* File not found. This means that cc1* decided not to + parallelize. */ + } + + sort_asm_files (&additional_asm_files); + + if (n_commands != 1) + fatal_error (input_location, + "Auto parallelism is unsupported when piping commands"); + + if (!path_to_ld) + path_to_ld = get_path_to_ld (); + + /* Get original command. */ + orig = commands[0]; + orig_argv = commands[0].argv; + orig_argc = get_number_of_args (orig.argv); + + + /* Update commands array to include the extra `as' calls. */ + *_n_commands = additional_asm_files.length (); + n_commands = *_n_commands; + + gcc_assert (n_commands > 0); + + identify_asm_file (orig_argc, orig_argv, &infile_pos, &outfile_pos); + + *_commands = XRESIZEVEC (struct command, *_commands, n_commands); + commands = *_commands; - /* Print each piped command as a separate line. */ for (i = 0; i < n_commands; i++) { - const char *const *j; + const char **argv = storer->create_new (orig_argc + 1); + const char *temp_obj = make_temp_file ("additional-obj.o"); + record_temp_file (temp_obj, true, true); + record_temp_file (additional_asm_files[i], true, true); - if (verbose_only_flag) + memcpy (argv, orig_argv, (orig_argc + 1) * sizeof (const char *)); + + orig_obj_file = argv[outfile_pos]; + + argv[infile_pos] = additional_asm_files[i]; + argv[outfile_pos] = temp_obj; + + commands[i].prog = orig.prog; + commands[i].argv = argv; + + temp_object_files.safe_push (temp_obj); + } + + if (have_c) + { + unsigned int num_temp_objs = temp_object_files.length (); + const char **argv = storer->create_new (num_temp_objs + 5); + unsigned int j; + + argv[0] = path_to_ld; + argv[1] = "-o"; + argv[2] = orig_obj_file; + argv[3] = "-r"; + + for (j = 0; j < num_temp_objs; j++) + argv[j + 4] = temp_object_files[j]; + argv[j + 4] = NULL; + + additional_ld->prog = path_to_ld; + additional_ld->argv = argv; + + if (!have_o) + temp_object_files.truncate (0); + } + + additional_asm_files.release (); + } +} + +DEBUG_FUNCTION void +print_command (struct command *command) +{ + const char **argv; + + for (argv = command->argv; *argv != NULL; argv++) + fprintf (stdout, " %s", *argv); + fputc ('\n', stdout); +} + +DEBUG_FUNCTION void +print_commands (int n, struct command *commands) +{ + int i; + + for (i = 0; i < n; i++) + print_command (&commands[i]); +} + +DEBUG_FUNCTION void +print_argbuf () +{ + int i; + const char *arg; + + for (i = 0; argbuf.iterate (i, &arg); i++) + fprintf (stdout, "%s ", arg); + fputc ('\n', stdout); +} + + +/* Print what commands will run. Return 0 if success, anything else on + error. */ + +static int +handle_verbose (int n_commands, struct command commands[]) +{ + int i; + + /* For help listings, put a blank line between sub-processes. */ + if (print_help_list) + fputc ('\n', stderr); + + /* Print each piped command as a separate line. */ + for (i = 0; i < n_commands; i++) + { + 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. */ @@ -3221,7 +3656,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"; @@ -3232,15 +3667,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++) { @@ -3267,151 +3703,341 @@ 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. */ + +static int +await_commands_to_finish (struct pex_obj *pex, + int n_commands, struct command commands[]) +{ - 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"); + int *statuses; + struct pex_time *times = NULL; + int ret_code = 0, i; - 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"); - } + 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"); - pex_free (pex); + 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"); + } - for (i = 0; i < n_commands; ++i) - { - int status = statuses[i]; + 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. */ + 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; + + struct command additional_ld = {NULL, NULL}; + extra_arg_storer storer; + + struct command *commands_batch; + int n; + + 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 (!have_S && !have_E && flag_parallel_jobs) + append_split_outputs (&storer, &additional_ld, &commands, &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 = handle_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 + + /* FIXME: Interact with GNU Jobserver if necessary. */ + + commands_batch = commands; + n = flag_parallel_jobs? 1: n_commands; + + for (int i = 0; i < n_commands; i += n) + { + /* 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_batch); + + /* Await them to be done. */ + ret |= await_commands_to_finish (pex, n, commands_batch); + + commands_batch = commands_batch + n; + + /* Cleanup. */ + pex_free (pex); + } + + + if (ret != 0) + goto cleanup; + + /* Run extra ld call. */ + if (!EMPTY_CMD (additional_ld)) + { + /* If we are here, we must be sure that we had at least two object + files to link. */ + //gcc_assert (n_commands != 1); + + pex = pex_init (PEX_USE_PIPES | ((report_times || report_times_to_file) + ? PEX_RECORD_TIMES : 0), + progname, temp_filename); + + if (verbose_flag) + print_command (&additional_ld); + + async_launch_commands (pex, 1, &additional_ld); + ret = await_commands_to_finish (pex, 1, &additional_ld); + 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 and make a vector describing them. @@ -3480,29 +4106,33 @@ static int n_switches_alloc_debug_check[2]; static char *debug_check_temp_file[2]; -/* Language is one of three things: - - 1) The name of a real programming language. - 2) NULL, indicating that no one has figured out - what it is yet. - 3) '*', indicating that the file should be passed - to the linker. */ -struct infile +static const char * +fsplit_arg (extra_arg_storer *storer) { - const char *name; - const char *language; - struct compiler *incompiler; - bool compiled; - bool preprocessed; -}; + const char *tempname = make_temp_file ("additional-asm"); + const char arg[] = "-fsplit-outputs="; + char *final; -/* Also a vector of input files specified. */ + size_t n = ARRAY_SIZE (arg) + strlen (tempname); -static struct infile *infiles; + gcc_assert (current_infile); -int n_infiles; + current_infile->temp_additional_asm = tempname; + + /* Remove file, once we may not even need it and create it later. */ + /* FIXME: This is a little hackish. */ + remove (tempname); + + final = storer->create_string (n); + + strcpy (final, arg); + strcat (final, tempname); + + record_temp_file (tempname, true, true); + + return final; +} -static int n_infiles_alloc; /* True if undefined environment variables encountered during spec processing are ok to ignore, typically when we're running for --help or --version. */ @@ -3683,6 +4313,8 @@ alloc_infile (void) { n_infiles_alloc = 16; infiles = XNEWVEC (struct infile, n_infiles_alloc); + memset (infiles, 0x00, sizeof (*infiles) * n_infiles_alloc); + } else if (n_infiles_alloc == n_infiles) { @@ -4648,6 +5280,9 @@ process_command (unsigned int decoded_options_count, switch (decoded_options[j].opt_index) { case OPT_S: + have_S = 1; + have_c = 1; + break; case OPT_c: case OPT_E: have_c = 1; @@ -6155,11 +6790,14 @@ do_spec_1 (const char *spec, int inswitch, const char *soft_matched_part) open_at_file (); for (i = 0; (int) i < n_infiles; i++) - if (compile_input_file_p (&infiles[i])) - { - store_arg (infiles[i].name, 0, 0); - infiles[i].compiled = true; - } + { + current_infile = &infiles[i]; + if (compile_input_file_p (current_infile)) + { + store_arg (current_infile->name, 0, 0); + current_infile->compiled = true; + } + } if (at_file_supplied) close_at_file (); @@ -6515,7 +7153,7 @@ do_spec_1 (const char *spec, int inswitch, const char *soft_matched_part) "%{foo=*:bar%*}%{foo=*:one%*two}" matches -foo=hello then it will produce: - + barhello onehellotwo */ if (*p == 0 || *p == '}') @@ -8642,6 +9280,7 @@ driver::do_spec_on_infiles () const for (i = 0; (int) i < n_infiles; i++) { int this_file_error = 0; + current_infile = &infiles[i]; /* Tell do_spec what to substitute for %i. */ @@ -8761,12 +9400,15 @@ driver::do_spec_on_infiles () const int i; for (i = 0; i < n_infiles ; i++) - if (infiles[i].incompiler - || (infiles[i].language && infiles[i].language[0] != '*')) - { - set_input (infiles[i].name); - break; - } + { + current_infile = &infiles[i]; + if (infiles[i].incompiler + || (infiles[i].language && infiles[i].language[0] != '*')) + { + set_input (infiles[i].name); + break; + } + } } if (!seen_error ()) @@ -8788,11 +9430,31 @@ driver::maybe_run_linker (const char *argv0) const int linker_was_run = 0; int num_linker_inputs; - /* Determine if there are any linker input files. */ - num_linker_inputs = 0; - for (i = 0; (int) i < n_infiles; i++) - if (explicit_link_files[i] || outfiles[i] != NULL) - num_linker_inputs++; + /* Set outfiles to be the temporary object vector. */ + const char **outfiles_holder = outfiles; + int n_infiles_holder = n_infiles; + bool outfiles_switched = false; + if (temp_object_files.length () > 0) + { + /* Insert explicit link files into the temp object vector. */ + + for (i = 0; (int) i < n_infiles; i++) + if (explicit_link_files[i] && outfiles[i] != NULL) + temp_object_files.safe_push (outfiles[i]); + + num_linker_inputs = n_infiles = temp_object_files.length (); + temp_object_files.safe_push (NULL); /* the NULL sentinel. */ + outfiles = temp_object_files.address (); + } + else /* Fall back to the old method. */ + { + + /* Determine if there are any linker input files. */ + num_linker_inputs = 0; + for (i = 0; (int) i < n_infiles; i++) + if (explicit_link_files[i] || outfiles[i] != NULL) + num_linker_inputs++; + } /* Arrange for temporary file names created during linking to take on names related with the linker output rather than with the @@ -8897,14 +9559,24 @@ driver::maybe_run_linker (const char *argv0) const } /* If options said don't run linker, - complain about input files to be given to the linker. */ + complain about input files to be given to the linker. + When fsplit-arg is active, the linker will run and this if + will not be triggered. */ - if (! linker_was_run && !seen_error ()) + if (!outfiles_switched && !linker_was_run && !seen_error () + && temp_object_files.length () == 0) for (i = 0; (int) i < n_infiles; i++) if (explicit_link_files[i] && !(infiles[i].language && infiles[i].language[0] == '*')) warning (0, "%s: linker input file unused because linking not done", outfiles[i]); + + if (outfiles_switched) + { + /* Undo our changes. */ + outfiles = outfiles_holder; + n_infiles = n_infiles_holder; + } } /* The end of "main". */ @@ -10808,6 +11480,7 @@ driver::finalize () linker_options.truncate (0); assembler_options.truncate (0); preprocessor_options.truncate (0); + temp_object_files.truncate (0); path_prefix_reset (&exec_prefixes); path_prefix_reset (&startfile_prefixes); diff --git a/gcc/testsuite/driver/a.c b/gcc/testsuite/driver/a.c new file mode 100644 index 00000000000..c6b8c2eb61e --- /dev/null +++ b/gcc/testsuite/driver/a.c @@ -0,0 +1,6 @@ +int puts (const char *); + +void a_func (void) +{ + puts ("A test"); +} diff --git a/gcc/testsuite/driver/b.c b/gcc/testsuite/driver/b.c new file mode 100644 index 00000000000..76a2cba0bd9 --- /dev/null +++ b/gcc/testsuite/driver/b.c @@ -0,0 +1,6 @@ +int puts (const char *); + +void a_func (void) +{ + puts ("Another test"); +} diff --git a/gcc/testsuite/driver/driver.exp b/gcc/testsuite/driver/driver.exp new file mode 100644 index 00000000000..2bbaf07778a --- /dev/null +++ b/gcc/testsuite/driver/driver.exp @@ -0,0 +1,80 @@ +# Copyright (C) 2008-2020 Free Software Foundation, Inc. + +# This program 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 of the License, or +# (at your option) any later version. +# +# This program 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/>. + +# GCC testsuite that uses the `dg.exp' driver. + +# Load support procs. +load_lib gcc-dg.exp + +proc check-for-errors { test input } { + if { [string equal "$input" ""] } then { + pass "$test: std out" + } else { + fail "$test: std out\n$input" + } +} + +if ![check_effective_target_pthread] { + return +} + +# If a testcase doesn't have special options, use these. +global DEFAULT_CFLAGS +if ![info exists DEFAULT_CFLAGS] then { + set DEFAULT_CFLAGS " -ansi -pedantic-errors" +} + +# Initialize `dg'. +dg-init + + +# Test multi-input compilation +check-for-errors "Multi-input Compilation" \ + [gcc_target_compile "$srcdir/$subdir/a.c $srcdir/$subdir/b.c -c" "" none ""] + +# Compile file and generate an assembler and object file +check-for-errors "Object Generation" \ + [gcc_target_compile "$srcdir/$subdir/a.c -c" "a.o" none ""] +check-for-errors "Object Generation" \ + [gcc_target_compile "$srcdir/$subdir/b.c -c" "a.o" none ""] +check-for-errors "Assembler Generation" \ + [gcc_target_compile "$srcdir/$subdir/a.c -S" "a.S" none ""] +check-for-errors "Assembler Generation" \ + [gcc_target_compile "$srcdir/$subdir/b.c -S" "b.S" none ""] + +# Empty file is a valid program +check-for-errors "Empty Program" \ + [gcc_target_compile "$srcdir/$subdir/empty.c -c" "empty.o" none ""] + +# Test object file passthrough +check-for-errors "Object file passthrough" \ + [gcc_target_compile "$srcdir/$subdir/foo.c a.o" "a.exe" none ""] + +# Test compilation when assembler is provided +check-for-errors "Assembler with Macros" \ + [gcc_target_compile "a.S -c" "a.o" none ""] + +# Clean temporary generated files. +set temp_files {"a.o" "a.S" "b.o" "b.S" "empty.o"} + +foreach f $temp_files { + if { [file exists $f] } { + file delete $f + } +} + +# All done. +dg-finish diff --git a/gcc/testsuite/driver/empty.c b/gcc/testsuite/driver/empty.c new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/gcc/testsuite/driver/empty.c diff --git a/gcc/testsuite/driver/foo.c b/gcc/testsuite/driver/foo.c new file mode 100644 index 00000000000..a18fd2a3b14 --- /dev/null +++ b/gcc/testsuite/driver/foo.c @@ -0,0 +1,7 @@ +void a_func (void); + +int main() +{ + a_func (); + return 0; +} |