@@ -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_<target>_{start,size,end}).
+ DESC_FILENAME is a name of a descriptor file. The descriptor declares symbol
+ __OPENMP_TARGET__ and refers to symbols _omp_image_<target>_{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_<target>_start, _omp_image_<target>_end, and
+ _omp_image_<target>_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);