From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 4493 invoked by alias); 17 Dec 2013 11:43:04 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Received: (qmail 4484 invoked by uid 89); 17 Dec 2013 11:43:04 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.5 required=5.0 tests=AWL,BAYES_00,FREEMAIL_FROM,RCVD_IN_DNSWL_LOW,SPF_PASS autolearn=ham version=3.3.2 X-HELO: mail-pa0-f43.google.com Received: from mail-pa0-f43.google.com (HELO mail-pa0-f43.google.com) (209.85.220.43) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-SHA encrypted) ESMTPS; Tue, 17 Dec 2013 11:43:02 +0000 Received: by mail-pa0-f43.google.com with SMTP id bj1so4339126pad.16 for ; Tue, 17 Dec 2013 03:43:00 -0800 (PST) X-Received: by 10.67.1.70 with SMTP id be6mr26805057pad.9.1387280580065; Tue, 17 Dec 2013 03:43:00 -0800 (PST) Received: from msticlxl57.ims.intel.com ([192.55.55.41]) by mx.google.com with ESMTPSA id bh6sm45305028pad.20.2013.12.17.03.42.55 for (version=TLSv1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 17 Dec 2013 03:42:59 -0800 (PST) Date: Tue, 17 Dec 2013 11:43:00 -0000 From: "Michael V. Zolotukhin" To: Jakub Jelinek , Thomas Schwinge , Bernd Schmidt , Richard Biener , Kirill Yukhin , Ilya Verbin , Andrey Turetskiy , Ilya Tocar , gcc Subject: [RFC][gomp4] Offloading patches (3/3): Add invocation of target compiler Message-ID: <20131217114251.GB39975@msticlxl57.ims.intel.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.5.21 (2010-09-15) X-IsSubscribed: yes X-SW-Source: 2013-12/txt/msg01486.txt.bz2 Hi everybody, Here is a patch 3/3: Add invocation of target compiler. With this patch lto-wrapper performs invocation of target compilers and embeds the resultant target images into the host binary. The targets and the corresponding compilers are supposed to be specified in a special environment variables (that works well with the recent Andrey's patch). For now, we need '-flto' options for the infrastructure to run - but I think we could remove this requirement in future. We generate C-files which are used for creating symbols for descriptor header and descriptor end. With this and after some manipulations with symbols (we need to call objcopy a couple of times for this) all files: i.e. target images, descriptor-header and descriptor-end - are linked together into a new object file which is passed to the linker as an output of lto-wrapper. Thanks, Michael --- gcc/lto-wrapper.c | 560 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 560 insertions(+), 0 deletions(-) diff --git a/gcc/lto-wrapper.c b/gcc/lto-wrapper.c index 335ec8f..a9085b5 100644 --- a/gcc/lto-wrapper.c +++ b/gcc/lto-wrapper.c @@ -52,6 +52,11 @@ along with GCC; see the file COPYING3. If not see #define LTO_SECTION_NAME_PREFIX ".gnu.lto_" +#define OFFLOAD_FUNC_TABLE_SECTION_NAME ".offload_func_table_section" +#define OFFLOAD_IMAGE_SECTION_NAME ".offload_image_section" +#define OFFLOAD_TARGET_NAMES_ENV "OFFLOAD_TARGET_NAMES" +#define OFFLOAD_TARGET_COMPILERS_ENV "OFFLOAD_TARGET_COMPILERS" + /* End of lto-streamer.h copy. */ int debug; /* true if -save-temps. */ @@ -447,6 +452,540 @@ merge_and_complain (struct cl_decoded_option **decoded_options, } } + +/* Parse STR, saving found tokens into PVALUES and return their number. + Tokens are assumed to be delimited by ':'. */ + +static unsigned +parse_env_var (const char *str, char ***pvalues) +{ + const char *curval, *nextval; + char **values; + unsigned num = 1, i; + + curval = strchr (str, ':'); + while (curval) + { + num++; + curval = strchr (curval + 1, ':'); + } + + values = (char**) xmalloc (num * sizeof (char*)); + curval = str; + nextval = strchrnul (curval, ':'); + for (i = 0; i < num; i++) + { + int l = nextval - curval; + values[i] = (char*) xmalloc (l + 1); + memcpy (values[i], curval, l); + values[i][l] = 0; + + curval = nextval + 1; + nextval = strchrnul (curval, ':'); + } + *pvalues = values; + return num; +} + +/* Generate openmp-descriptor file. The function generates source-file and then + compiles it with COLLECT_GCC. + NAMES are the names of the targets, they are used in names of generated + symbols. NUM is the number of targets. + FOR_TARGET specifies whether we generate descriptor for host or for + target-side. We add pointers to images into the table only for host side. + Return value is the name of the generated object file. */ + +static char* +generate_descriptor_file (int num, char **names, const char *collect_gcc, + bool for_target = false) +{ + const char **target_argv; + struct obstack target_argv_obstack; + FILE *desc_src_file = NULL; + char *desc_src_filename = NULL; + char *desc_obj_filename = NULL; + int i; + + desc_src_filename = make_temp_file ("_omp_descr.c"); + desc_src_file = fopen (desc_src_filename, "wb"); + if (!desc_src_file) + { + free (desc_src_filename); + return NULL; + } + + for (i = 0; i < num; i++) + { + fprintf (desc_src_file, "extern void *_omp_image_%s_start;\n", names[i]); + fprintf (desc_src_file, "extern void *_omp_image_%s_end;\n", names[i]); + } + fprintf (desc_src_file, "extern void *_omp_func_table[];\n"); + fprintf (desc_src_file, "extern void *_omp_table_end[];\n"); + fprintf (desc_src_file, + "void *__OPENMP_TARGET__[]\n" + " __attribute__ ((__used__, visibility (\"protected\"),\n" + " section (\"%s\"))) = {\n", + OFFLOAD_IMAGE_SECTION_NAME); + /* First two elements describes Openmp Functions/Globals table. + Target side descriptor contains nothing else. Host side descriptor + contains pointers to images after that. */ + fprintf (desc_src_file, " &_omp_func_table, &_omp_table_end,\n"); + if (!for_target) + for (i = 0; i < num; i++) + fprintf (desc_src_file, " &_omp_image_%s_start, &_omp_image_%s_end,\n", + names[i], names[i]); + fprintf (desc_src_file, "};\n"); + + if (!for_target) + fprintf (desc_src_file, + "void GOMP_register_lib (const void *);\n" + "__attribute__((constructor))\n" + "static void\n" + "init (void)\n" + "{\n" + " GOMP_register_lib (__OPENMP_TARGET__);\n" + "}\n"); + else + fprintf (desc_src_file, + "void target_register_lib (const void *);\n" + "__attribute__((constructor))\n" + "static void\n" + "init (void)\n" + "{\n" + " target_register_lib (__OPENMP_TARGET__);\n" + "}\n"); + + /* Declare symbol for Openmp Functions/Globals table. + It is placed in a dedicated section - all objects files have their entries + for this table in the same section and during linking its merged into a + single table, which could be referred to by this symbol. + End of this table is also marked with a symbol, which is linked after all + other files. */ + fprintf (desc_src_file, + "void *_omp_func_table[0]\n" + " __attribute__ ((__used__, visibility (\"protected\"),\n" + " section (\"%s\"))) = { };\n", + OFFLOAD_FUNC_TABLE_SECTION_NAME); + fclose (desc_src_file); + + /* Compile descriptor-file. */ + desc_obj_filename = make_temp_file ("_omp_descr.o"); + obstack_init (&target_argv_obstack); + obstack_ptr_grow (&target_argv_obstack, collect_gcc); + obstack_ptr_grow (&target_argv_obstack, "-c"); + obstack_ptr_grow (&target_argv_obstack, desc_src_filename); + obstack_ptr_grow (&target_argv_obstack, "-o"); + obstack_ptr_grow (&target_argv_obstack, desc_obj_filename); + obstack_ptr_grow (&target_argv_obstack, "-shared"); + obstack_ptr_grow (&target_argv_obstack, "-fPIC"); + obstack_ptr_grow (&target_argv_obstack, NULL); + + target_argv = XOBFINISH (&target_argv_obstack, const char **); + fork_execute (CONST_CAST (char **, target_argv)); + obstack_free (&target_argv_obstack, NULL); + + if (!debug) + maybe_unlink_file (desc_src_filename); + free (desc_src_filename); + return desc_obj_filename; +} + + +/* Generate object file containing a 0-sized _omp_table_end symbol. + The function generates source-file and then compiles it with COLLECT_GCC. + Return value is the name of the generated object file. */ + +static char* +generate_omp_table_end_file (const char *collect_gcc) +{ + const char **target_argv; + struct obstack target_argv_obstack; + FILE *desc_src_file = NULL; + char *desc_src_filename = NULL; + char *desc_obj_filename = NULL; + + desc_src_filename = make_temp_file ("_omp_table_end.c"); + desc_src_file = fopen (desc_src_filename, "wb"); + if (!desc_src_file) + { + free (desc_src_filename); + return NULL; + } + + fprintf (desc_src_file, + "void *_omp_table_end[0]\n" + " __attribute__ ((__used__, visibility (\"protected\"),\n" + " section (\"%s\"))) = { };\n", + OFFLOAD_FUNC_TABLE_SECTION_NAME); + fclose (desc_src_file); + + /* Compile descriptor-file and pass resultant object file to linker. */ + desc_obj_filename = make_temp_file ("_omp_table_end.o"); + obstack_init (&target_argv_obstack); + obstack_ptr_grow (&target_argv_obstack, collect_gcc); + obstack_ptr_grow (&target_argv_obstack, "-c"); + obstack_ptr_grow (&target_argv_obstack, desc_src_filename); + obstack_ptr_grow (&target_argv_obstack, "-o"); + obstack_ptr_grow (&target_argv_obstack, desc_obj_filename); + obstack_ptr_grow (&target_argv_obstack, "-shared"); + obstack_ptr_grow (&target_argv_obstack, "-fPIC"); + obstack_ptr_grow (&target_argv_obstack, NULL); + + target_argv = XOBFINISH (&target_argv_obstack, const char **); + fork_execute (CONST_CAST (char **, target_argv)); + obstack_free (&target_argv_obstack, NULL); + + if (!debug) + maybe_unlink_file (desc_src_filename); + free (desc_src_filename); + return desc_obj_filename; +} + + +/* Prepare target image for target NAME. + Firstly, we execute COMPILER, passing all input files to it to produce DSO. + We also use descriptor-file and omp_table_end file at this moment to properly + fill all OpenMP tables in the target image. + When target DSO is ready, we pass it to objcopy to place its image into a + special data section. After that we rename target image's symbols to values, + expected by the host side, and return the name of the resultant file. */ + +static char* +prepare_target_image (char *name, char *compiler, char *desc_filename, + char *omp_table_end_filename, + unsigned in_argc, char *in_argv[], char **symbols) +{ + char *option_for_objdump[3]; + const char **argv; + struct obstack argv_obstack; + char *target_image_file_name = NULL; + char *symbol_name; + unsigned i; + char *filename; + char *buf1 = NULL, *buf2 = NULL; + + /* Generate temp file name. */ + target_image_file_name = make_temp_file (".target.so"); + filename = (char*) xmalloc (strlen (target_image_file_name) + 1); + if (!filename) + return NULL; + strcpy (filename, target_image_file_name); + + /* -------------------------------------- */ + /* Run gcc for target. */ + obstack_init (&argv_obstack); + obstack_ptr_grow (&argv_obstack, compiler); + obstack_ptr_grow (&argv_obstack, "-shared"); + obstack_ptr_grow (&argv_obstack, "-fPIC"); + obstack_ptr_grow (&argv_obstack, "-xlto"); + obstack_ptr_grow (&argv_obstack, "-fopenmp_target"); + obstack_ptr_grow (&argv_obstack, "-o"); + obstack_ptr_grow (&argv_obstack, target_image_file_name); + + /* Append the input objects. */ + buf1 = (char*) xmalloc (strlen (desc_filename) + strlen ("-Wl,") + 1); + buf2 = (char*) xmalloc (strlen (omp_table_end_filename) + + strlen ("-Wl,") + 1); + if (!buf1 || !buf2) + return NULL; + sprintf (buf1, "-Wl,%s", desc_filename); + obstack_ptr_grow (&argv_obstack, buf1); + for (i = 1; i < in_argc; ++i) + if (strncmp (in_argv[i], "-fresolution=", sizeof ("-fresolution=") - 1)) + obstack_ptr_grow (&argv_obstack, in_argv[i]); + sprintf (buf2, "-Wl,%s", omp_table_end_filename); + obstack_ptr_grow (&argv_obstack, buf2); + obstack_ptr_grow (&argv_obstack, NULL); + + argv = XOBFINISH (&argv_obstack, const char **); + fork_execute (CONST_CAST (char **, argv)); + obstack_free (&argv_obstack, NULL); + free (buf1); + free (buf2); + + /* -------------------------------------- */ + /* Run objcopy on TARGET_IMAGE_FILE_NAME. */ + buf1 = (char*) xmalloc (strlen (".data=.") + + strlen (OFFLOAD_IMAGE_SECTION_NAME) + 1); + if (!buf1) + return NULL; + sprintf (buf1, ".data=%s", OFFLOAD_IMAGE_SECTION_NAME); + obstack_init (&argv_obstack); + obstack_ptr_grow (&argv_obstack, "objcopy"); + obstack_ptr_grow (&argv_obstack, "-B"); + obstack_ptr_grow (&argv_obstack, "i386"); + obstack_ptr_grow (&argv_obstack, "-I"); + obstack_ptr_grow (&argv_obstack, "binary"); + obstack_ptr_grow (&argv_obstack, "-O"); + /* TODO: Properly handle 32-bit mode. */ + obstack_ptr_grow (&argv_obstack, "elf64-x86-64"); + obstack_ptr_grow (&argv_obstack, target_image_file_name); + obstack_ptr_grow (&argv_obstack, "--rename-section"); + obstack_ptr_grow (&argv_obstack, buf1); + obstack_ptr_grow (&argv_obstack, NULL); + + argv = XOBFINISH (&argv_obstack, const char **); + fork_execute (CONST_CAST (char **, argv)); + obstack_free (&argv_obstack, NULL); + free (buf1); + + /* -------------------------------------- */ + /* Run objcopy again to rename new symbols. */ + obstack_init (&argv_obstack); + obstack_ptr_grow (&argv_obstack, "objcopy"); + obstack_ptr_grow (&argv_obstack, target_image_file_name); + + /* Objcopy has created symbols, containing the input file name with + special characters replaced with '_'. We are going to rename these + new symbols, and to do that we need to know their names. */ + symbol_name = (char*) xmalloc (strlen (target_image_file_name)); + if (!symbol_name) + return NULL; + i = 0; + while (target_image_file_name[i]) + { + char c = target_image_file_name[i]; + if (c == '/' || c == '.') + c = '_'; + symbol_name[i] = c; + i++; + } + symbol_name[i] = 0; + + for (i = 0; i < 3; i++) + { + option_for_objdump[i] + = (char*) xmalloc (strlen (target_image_file_name) * 2 + 1 + + strlen ("_binary__start=_omp_image__start")); + symbols[i] = (char*) xmalloc (strlen (name) + 1 + + strlen ("_omp_image__start")); + if (!option_for_objdump[i] || !symbols[i]) + return NULL; + } + sprintf (symbols[0], "_omp_image_%s_start", name); + sprintf (symbols[1], "_omp_image_%s_size", name); + sprintf (symbols[2], "_omp_image_%s_end", name); + + sprintf (option_for_objdump[0], "_binary_%s_start=_omp_image_%s_start", + symbol_name, name); + sprintf (option_for_objdump[1], "_binary_%s_end=_omp_image_%s_end", + symbol_name, name); + sprintf (option_for_objdump[2], "_binary_%s_size=_omp_image_%s_size", + symbol_name, name); + obstack_ptr_grow (&argv_obstack, "--redefine-sym"); + obstack_ptr_grow (&argv_obstack, option_for_objdump[0]); + obstack_ptr_grow (&argv_obstack, "--redefine-sym"); + obstack_ptr_grow (&argv_obstack, option_for_objdump[1]); + obstack_ptr_grow (&argv_obstack, "--redefine-sym"); + obstack_ptr_grow (&argv_obstack, option_for_objdump[2]); + obstack_ptr_grow (&argv_obstack, NULL); + + argv = XOBFINISH (&argv_obstack, const char **); + fork_execute (CONST_CAST (char **, argv)); + obstack_free (&argv_obstack, NULL); + free (symbol_name); + + for (i = 0; i < 3; i++) + free (option_for_objdump[i]); + return filename; +} + + +/* The routine performs partial linking of all target images (which are already + placed in a data section), and localize all symbols except __OPENMP_TARGET__. + That is done to prevent overriding of symbols from DSO by symbols from + another DSO or executable (here we are talking about symbols + _omp_image__{start,size,end}). + DESC_FILENAME is a name of a descriptor file. The descriptor declares symbol + __OPENMP_TARGET__ and refers to symbols _omp_image__{start,size,end} + from object files with target images. + NUM_TARGETS is the number of targets (and correspondyngly number of object + files). + FILENAMES is an array of object filenames, SYMBOLS is an array of the symbols + (three symbols per target). + Return value is name of object file, containing OpenMP descriptor, + function/globals tables and images. */ + +static char* +postprocess_target_images (char *desc_filename, unsigned num_targets, + char **filenames, char **symbols) +{ + const char **argv; + struct obstack argv_obstack; + unsigned i; + char *offload_out = make_temp_file (".offload.o"); + if (!offload_out) + return NULL; + + /* Perform partial linking for the target images and descriptor. + As a result we'll get a finalized object file with all offload data. */ + obstack_init (&argv_obstack); + obstack_ptr_grow (&argv_obstack, "ld"); + obstack_ptr_grow (&argv_obstack, "-r"); + obstack_ptr_grow (&argv_obstack, desc_filename); + for (i = 0; i < num_targets; i++) + obstack_ptr_grow (&argv_obstack, filenames[i]); + obstack_ptr_grow (&argv_obstack, "-o"); + obstack_ptr_grow (&argv_obstack, offload_out); + obstack_ptr_grow (&argv_obstack, NULL); + argv = XOBFINISH (&argv_obstack, const char **); + fork_execute (CONST_CAST (char **, argv)); + obstack_free (&argv_obstack, NULL); + + if (!debug) + for (i = 0; i < num_targets; i++) + maybe_unlink_file (filenames[i]); + + /* Run objcopy on the resultant object file to localize generated symbols to + avoid conflicting between different DSO and executable. */ + obstack_init (&argv_obstack); + obstack_ptr_grow (&argv_obstack, "objcopy"); + for (i = 0; i < num_targets*3; i++) + { + obstack_ptr_grow (&argv_obstack, "-L"); + obstack_ptr_grow (&argv_obstack, symbols[i]); + } + obstack_ptr_grow (&argv_obstack, offload_out); + obstack_ptr_grow (&argv_obstack, NULL); + argv = XOBFINISH (&argv_obstack, const char **); + fork_execute (CONST_CAST (char **, argv)); + obstack_free (&argv_obstack, NULL); + return offload_out; +} + +/* Auxiliary function that frees elements of PTR and PTR itself. + N is number of elements to be freed. + If PTR is NULL, nothing is freed. If an element is NULL, subsequent elements + are not freed. */ +static void** +free_array_of_ptrs (void **ptr, unsigned n) +{ + unsigned i; + if (!ptr) + return NULL; + for (i = 0; i < n; i++) + { + if (!ptr[i]) + break; + free (ptr[i]); + } + free (ptr); + return NULL; +} + +/* Replace all special characters in array of strings with '_'. + This is needed, e.g., when we want to use a string for a symbol name. */ +static void +replace_special_characters (char **ptr, unsigned n) +{ + unsigned i, j; + const char *special_chars = "-+=/\\~`!@#$%^&*()[]{},;.:\"'"; + for (i = 0; i < n; i++) + { + char *str = ptr[i]; + for (j = 0; j < strlen (str); j++) + { + if (strchr (special_chars, str[j])) + str[j] = '_'; + } + } +} + +/* The main routine dealing with openmp offloading. + The routine builds a target image for each offloading target, and places all + images into a dedicated data-section, adding a descriptor for accessing it. + IN_ARGC and IN_ARGV specify input files. As all of them could contain + omp-sections, we pass them all to target compilers. + COLLECT_GCC is a host compiler. + Env-variable OFFLOAD_TARGET_NAMES_ENV describes for which targets we should + build images. + Env-variable OFFLOAD_TARGET_COMPILERS_ENV specifies compilers to be used for + target image building. + Return value is name of object file, containing OpenMP descriptor, + function/globals tables and images. */ + +static char* +compile_images_for_openmp_targets (unsigned in_argc, char *in_argv[], + const char *collect_gcc) +{ + char *desc_filename = NULL, *offload_out = NULL; + char *omp_table_end_filename = NULL, *target_desc_filename = NULL; + char *target_names, *target_compilers; + char **names, **compilers, **filenames = NULL, **symbols = NULL; + unsigned num_targets, num_compilers, i; + + /* Step 1: Obtain names of offload targets and corresponding compilers. */ + target_names = getenv (OFFLOAD_TARGET_NAMES_ENV); + target_compilers = getenv (OFFLOAD_TARGET_COMPILERS_ENV); + if (!target_names || !target_compilers) + return NULL; + + num_targets = parse_env_var (target_names, &names); + replace_special_characters (names, num_targets); + + num_compilers = parse_env_var (target_compilers, &compilers); + if (num_compilers != num_targets) + goto cleanup; + + /* Step 2: Generate a file with omp descriptor table and a file with + omp_table_end symbol for target images. We'll link these files into the + images. */ + target_desc_filename = generate_descriptor_file (num_targets, names, + collect_gcc, true); + omp_table_end_filename = generate_omp_table_end_file (collect_gcc); + if (!target_desc_filename || !omp_table_end_filename) + fatal_perror ("Problem with building offloading targets.\n"); + + /* Step 3: Prepare an image for each target. A prepared image contains three + symbols: _omp_image__start, _omp_image__end, and + _omp_image__end. We save these names in SYMBOLS array. + Images filenames are stored in FILENAMES array. */ + filenames = (char**) xmalloc (sizeof (char*) * num_targets); + symbols = (char**) xmalloc (sizeof (char*) * num_targets * 3); + for (i = 0; i < num_targets; i++) + { + filenames[i] = prepare_target_image (names[i], compilers[i], + target_desc_filename, + omp_table_end_filename, + in_argc, in_argv, &symbols[i*3]); + if (!filenames[i]) + fatal_perror ("Problem with building target image for %s.\n", names[i]); + } + if (!debug) + { + maybe_unlink_file (target_desc_filename); + maybe_unlink_file (omp_table_end_filename); + } + + /* Step 4: Generate a file with omp descriptor table for host binary. */ + desc_filename = generate_descriptor_file (num_targets, names, collect_gcc); + if (!desc_filename) + goto cleanup; + + /* Step 5: Perform partial linking on all generated files, and then localize + all generated symbols, except __OPENMP_TARGET__. */ + offload_out = postprocess_target_images (desc_filename, num_targets, + filenames, symbols); + if (!offload_out) + fatal_perror ("Problem with embedding target images for offloading.\n"); + if (!debug) + maybe_unlink_file (desc_filename); + +cleanup: + names = (char**) free_array_of_ptrs ((void**) names, num_targets); + compilers = (char**) free_array_of_ptrs ((void**) compilers, num_compilers); + filenames = (char**) free_array_of_ptrs ((void**) filenames, num_targets); + symbols = (char**) free_array_of_ptrs ((void**) symbols, num_targets); + if (omp_table_end_filename) + free (omp_table_end_filename); + if (target_desc_filename) + free (target_desc_filename); + if (desc_filename) + free (desc_filename); + return offload_out; +} + + /* Execute gcc. ARGC is the number of arguments. ARGV contains the arguments. */ static void @@ -768,6 +1307,7 @@ run_gcc (unsigned argc, char *argv[]) FILE *stream = fopen (ltrans_output_file, "r"); FILE *mstream = NULL; struct obstack env_obstack; + char *offload_output_name, *offload_table_end_name; if (!stream) fatal_perror ("fopen: %s", ltrans_output_file); @@ -906,12 +1446,32 @@ cont: for (i = 0; i < nr; ++i) maybe_unlink_file (input_names[i]); } + + offload_output_name = compile_images_for_openmp_targets (argc, argv, + collect_gcc); + if (offload_output_name) + { + fputs (offload_output_name, stdout); + putc ('\n', stdout); + free (offload_output_name); + } for (i = 0; i < nr; ++i) { fputs (output_names[i], stdout); putc ('\n', stdout); free (input_names[i]); } + + /* Generate a file with omp descriptor table and pass its name to + linker. */ + offload_table_end_name = generate_omp_table_end_file (collect_gcc); + if (offload_table_end_name) + { + fputs (offload_table_end_name, stdout); + putc ('\n', stdout); + free (offload_table_end_name); + } + nr = 0; free (output_names); free (input_names); -- 1.7.1