From 87b6c18c01a9e328ae9e4ef0929c40b9213be9f8 Mon Sep 17 00:00:00 2001 From: Ilya Verbin Date: Thu, 13 Nov 2014 13:57:58 +0000 Subject: [PATCH] [PATCH 1/4] OpenMP 4.0 offloading to Intel MIC: mkoffload. gcc/ * config.gcc (*-intelmic-* | *-intelmicemul-*): Add i386/t-intelmic to tmake_file. (i[34567]86-*-* | x86_64-*-*): Build mkoffload$(exeext) with the accelerator compiler. * config/i386/intelmic-mkoffload.c: New file. * config/i386/t-intelmic: Ditto. Co-Authored-By: Andrey Turetskiy From-SVN: r217495 --- gcc/ChangeLog | 10 + gcc/config.gcc | 15 + gcc/config/i386/intelmic-mkoffload.c | 541 +++++++++++++++++++++++++++ gcc/config/i386/t-intelmic | 9 + 4 files changed, 575 insertions(+) create mode 100644 gcc/config/i386/intelmic-mkoffload.c create mode 100644 gcc/config/i386/t-intelmic diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 22c2e0443f9..9267d828c9c 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,13 @@ +2014-11-13 Ilya Verbin + Andrey Turetskiy + + * config.gcc (*-intelmic-* | *-intelmicemul-*): Add i386/t-intelmic to + tmake_file. + (i[34567]86-*-* | x86_64-*-*): Build mkoffload$(exeext) with the + accelerator compiler. + * config/i386/intelmic-mkoffload.c: New file. + * config/i386/t-intelmic: Ditto. + 2014-11-13 Bernd Schmidt Andrey Turetskiy Ilya Verbin diff --git a/gcc/config.gcc b/gcc/config.gcc index 0af4a1a851c..a6b37d8b8f5 100644 --- a/gcc/config.gcc +++ b/gcc/config.gcc @@ -2884,6 +2884,13 @@ powerpc*-*-* | rs6000-*-*) tm_file="${tm_file} rs6000/option-defaults.h" esac +# Build mkoffload tool +case ${target} in +*-intelmic-* | *-intelmicemul-*) + tmake_file="${tmake_file} i386/t-intelmic" + ;; +esac + if [ "$target_has_targetcm" = "no" ]; then c_target_objs="$c_target_objs default-c.o" cxx_target_objs="$cxx_target_objs default-c.o" @@ -4279,3 +4286,11 @@ then target_cpu_default=$target_cpu_default2 fi fi + +case ${target} in +i[34567]86-*-* | x86_64-*-*) + if test x$enable_as_accelerator = xyes; then + extra_programs="mkoffload\$(exeext)" + fi + ;; +esac diff --git a/gcc/config/i386/intelmic-mkoffload.c b/gcc/config/i386/intelmic-mkoffload.c new file mode 100644 index 00000000000..c972f56a50e --- /dev/null +++ b/gcc/config/i386/intelmic-mkoffload.c @@ -0,0 +1,541 @@ +/* Offload image generation tool for Intel MIC devices. + + Copyright (C) 2014 Free Software Foundation, Inc. + + Contributed by Ilya Verbin . + + 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 + . */ + +#include "config.h" +#include +#include "system.h" +#include "coretypes.h" +#include "obstack.h" +#include "intl.h" +#include "diagnostic.h" +#include "collect-utils.h" +#include + +const char tool_name[] = "intelmic mkoffload"; + +const char image_section_name[] = ".gnu.offload_images"; +const char *symbols[3] = { "__offload_image_intelmic_start", + "__offload_image_intelmic_end", + "__offload_image_intelmic_size" }; +const char *out_obj_filename = NULL; + +int num_temps = 0; +const int MAX_NUM_TEMPS = 10; +const char *temp_files[MAX_NUM_TEMPS]; + +/* Shows if we should compile binaries for i386 instead of x86-64. */ +bool target_ilp32 = false; + +/* Delete tempfiles and exit function. */ +void +tool_cleanup (bool from_signal ATTRIBUTE_UNUSED) +{ + for (int i = 0; i < num_temps; i++) + maybe_unlink (temp_files[i]); +} + +static void +mkoffload_atexit (void) +{ + tool_cleanup (false); +} + +/* Unlink FILE unless we are debugging. */ +void +maybe_unlink (const char *file) +{ + if (debug) + notice ("[Leaving %s]\n", file); + else + unlink_if_ordinary (file); +} + +/* Add or change the value of an environment variable, outputting the + change to standard error if in verbose mode. */ +static void +xputenv (const char *string) +{ + if (verbose) + fprintf (stderr, "%s\n", string); + putenv (CONST_CAST (char *, string)); +} + +/* 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 = strchr (curval, ':'); + if (nextval == NULL) + nextval = strchr (curval, '\0'); + + 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 = strchr (curval, ':'); + if (nextval == NULL) + nextval = strchr (curval, '\0'); + } + *pvalues = values; + return num; +} + +/* 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; + for (i = 0; i < n; i++) + { + if (!ptr[i]) + break; + free (ptr[i]); + } + free (ptr); + return; +} + +/* Check whether NAME can be accessed in MODE. This is like access, + except that it never considers directories to be executable. */ +static int +access_check (const char *name, int mode) +{ + if (mode == X_OK) + { + struct stat st; + + if (stat (name, &st) < 0 || S_ISDIR (st.st_mode)) + return -1; + } + + return access (name, mode); +} + +/* Find target compiler using a path from COLLECT_GCC or COMPILER_PATH. */ +static char * +find_target_compiler (const char *name) +{ + bool found = false; + char **paths = NULL; + unsigned n_paths, i; + const char *collect_path = dirname (ASTRDUP (getenv ("COLLECT_GCC"))); + size_t len = strlen (collect_path) + 1 + strlen (name) + 1; + char *target_compiler = XNEWVEC (char, len); + sprintf (target_compiler, "%s/%s", collect_path, name); + if (access_check (target_compiler, X_OK) == 0) + { + found = true; + goto out; + } + + n_paths = parse_env_var (getenv ("COMPILER_PATH"), &paths); + for (i = 0; i < n_paths; i++) + { + len = strlen (paths[i]) + 1 + strlen (name) + 1; + target_compiler = XRESIZEVEC (char, target_compiler, len); + sprintf (target_compiler, "%s/%s", paths[i], name); + if (access_check (target_compiler, X_OK) == 0) + { + found = true; + break; + } + } + +out: + free_array_of_ptrs ((void **) paths, n_paths); + return found ? target_compiler : NULL; +} + +static void +compile_for_target (struct obstack *argv_obstack) +{ + if (target_ilp32) + obstack_ptr_grow (argv_obstack, "-m32"); + obstack_ptr_grow (argv_obstack, NULL); + char **argv = XOBFINISH (argv_obstack, char **); + + /* Save environment variables. */ + const char *epath = getenv ("GCC_EXEC_PREFIX"); + const char *cpath = getenv ("COMPILER_PATH"); + const char *lpath = getenv ("LIBRARY_PATH"); + const char *rpath = getenv ("LD_RUN_PATH"); + unsetenv ("GCC_EXEC_PREFIX"); + unsetenv ("COMPILER_PATH"); + unsetenv ("LIBRARY_PATH"); + unsetenv ("LD_RUN_PATH"); + + fork_execute (argv[0], argv, false); + obstack_free (argv_obstack, NULL); + + /* Restore environment variables. */ + xputenv (concat ("GCC_EXEC_PREFIX=", epath, NULL)); + xputenv (concat ("COMPILER_PATH=", cpath, NULL)); + xputenv (concat ("LIBRARY_PATH=", lpath, NULL)); + xputenv (concat ("LD_RUN_PATH=", rpath, NULL)); +} + +/* Generates object file with the descriptor for the target library. */ +static const char * +generate_target_descr_file (const char *target_compiler) +{ + const char *src_filename = make_temp_file ("_target_descr.c"); + const char *obj_filename = make_temp_file ("_target_descr.o"); + temp_files[num_temps++] = src_filename; + temp_files[num_temps++] = obj_filename; + FILE *src_file = fopen (src_filename, "w"); + + if (!src_file) + fatal_error ("cannot open '%s'", src_filename); + + fprintf (src_file, + "extern void *__offload_funcs_end[];\n" + "extern void *__offload_vars_end[];\n\n" + + "void *__offload_func_table[0]\n" + "__attribute__ ((__used__, visibility (\"hidden\"),\n" + "section (\".gnu.offload_funcs\"))) = { };\n\n" + + "void *__offload_var_table[0]\n" + "__attribute__ ((__used__, visibility (\"hidden\"),\n" + "section (\".gnu.offload_vars\"))) = { };\n\n" + + "void *__OFFLOAD_TARGET_TABLE__[]\n" + "__attribute__ ((__used__, visibility (\"hidden\"))) = {\n" + " &__offload_func_table, &__offload_funcs_end,\n" + " &__offload_var_table, &__offload_vars_end\n" + "};\n\n"); + + fprintf (src_file, + "#ifdef __cplusplus\n" + "extern \"C\"\n" + "#endif\n" + "void target_register_lib (const void *);\n\n" + + "__attribute__((constructor))\n" + "static void\n" + "init (void)\n" + "{\n" + " target_register_lib (__OFFLOAD_TARGET_TABLE__);\n" + "}\n"); + fclose (src_file); + + struct obstack argv_obstack; + obstack_init (&argv_obstack); + obstack_ptr_grow (&argv_obstack, target_compiler); + obstack_ptr_grow (&argv_obstack, "-c"); + obstack_ptr_grow (&argv_obstack, "-shared"); + obstack_ptr_grow (&argv_obstack, "-fPIC"); + obstack_ptr_grow (&argv_obstack, src_filename); + obstack_ptr_grow (&argv_obstack, "-o"); + obstack_ptr_grow (&argv_obstack, obj_filename); + compile_for_target (&argv_obstack); + + return obj_filename; +} + +/* Generates object file with __offload_*_end symbols for the target + library. */ +static const char * +generate_target_offloadend_file (const char *target_compiler) +{ + const char *src_filename = make_temp_file ("_target_offloadend.c"); + const char *obj_filename = make_temp_file ("_target_offloadend.o"); + temp_files[num_temps++] = src_filename; + temp_files[num_temps++] = obj_filename; + FILE *src_file = fopen (src_filename, "w"); + + if (!src_file) + fatal_error ("cannot open '%s'", src_filename); + + fprintf (src_file, + "void *__offload_funcs_end[0]\n" + "__attribute__ ((__used__, visibility (\"hidden\"),\n" + "section (\".gnu.offload_funcs\"))) = { };\n\n" + + "void *__offload_vars_end[0]\n" + "__attribute__ ((__used__, visibility (\"hidden\"),\n" + "section (\".gnu.offload_vars\"))) = { };\n"); + fclose (src_file); + + struct obstack argv_obstack; + obstack_init (&argv_obstack); + obstack_ptr_grow (&argv_obstack, target_compiler); + obstack_ptr_grow (&argv_obstack, "-c"); + obstack_ptr_grow (&argv_obstack, "-shared"); + obstack_ptr_grow (&argv_obstack, "-fPIC"); + obstack_ptr_grow (&argv_obstack, src_filename); + obstack_ptr_grow (&argv_obstack, "-o"); + obstack_ptr_grow (&argv_obstack, obj_filename); + compile_for_target (&argv_obstack); + + return obj_filename; +} + +/* Generates object file with the host side descriptor. */ +static const char * +generate_host_descr_file (const char *host_compiler) +{ + const char *src_filename = make_temp_file ("_host_descr.c"); + const char *obj_filename = make_temp_file ("_host_descr.o"); + temp_files[num_temps++] = src_filename; + temp_files[num_temps++] = obj_filename; + FILE *src_file = fopen (src_filename, "w"); + + if (!src_file) + fatal_error ("cannot open '%s'", src_filename); + + fprintf (src_file, + "extern void *__OFFLOAD_TABLE__;\n" + "extern void *__offload_image_intelmic_start;\n" + "extern void *__offload_image_intelmic_end;\n\n" + + "static const void *__offload_target_data[] = {\n" + " &__offload_image_intelmic_start, &__offload_image_intelmic_end\n" + "};\n\n"); + + fprintf (src_file, + "#ifdef __cplusplus\n" + "extern \"C\"\n" + "#endif\n" + "void GOMP_offload_register (void *, int, void *);\n\n" + + "__attribute__((constructor))\n" + "static void\n" + "init (void)\n" + "{\n" + " GOMP_offload_register (&__OFFLOAD_TABLE__, %d, __offload_target_data);\n" + "}\n", OFFLOAD_TARGET_TYPE_INTEL_MIC); + fclose (src_file); + + unsigned new_argc = 0; + const char *new_argv[9]; + new_argv[new_argc++] = host_compiler; + new_argv[new_argc++] = "-c"; + new_argv[new_argc++] = "-fPIC"; + new_argv[new_argc++] = "-shared"; + if (target_ilp32) + new_argv[new_argc++] = "-m32"; + new_argv[new_argc++] = src_filename; + new_argv[new_argc++] = "-o"; + new_argv[new_argc++] = obj_filename; + new_argv[new_argc++] = NULL; + + fork_execute (new_argv[0], CONST_CAST (char **, new_argv), false); + + return obj_filename; +} + +static const char * +prepare_target_image (const char *target_compiler, int argc, char **argv) +{ + const char *target_descr_filename + = generate_target_descr_file (target_compiler); + const char *target_offloadend_filename + = generate_target_offloadend_file (target_compiler); + + char *opt1 + = XALLOCAVEC (char, sizeof ("-Wl,") + strlen (target_descr_filename)); + char *opt2 + = XALLOCAVEC (char, sizeof ("-Wl,") + strlen (target_offloadend_filename)); + sprintf (opt1, "-Wl,%s", target_descr_filename); + sprintf (opt2, "-Wl,%s", target_offloadend_filename); + + const char *target_so_filename = make_temp_file ("_offload_intelmic.so"); + temp_files[num_temps++] = target_so_filename; + struct obstack argv_obstack; + obstack_init (&argv_obstack); + obstack_ptr_grow (&argv_obstack, target_compiler); + obstack_ptr_grow (&argv_obstack, "-xlto"); + obstack_ptr_grow (&argv_obstack, "-fopenmp"); + obstack_ptr_grow (&argv_obstack, "-shared"); + obstack_ptr_grow (&argv_obstack, "-fPIC"); + obstack_ptr_grow (&argv_obstack, opt1); + for (int i = 1; i < argc; i++) + { + if (!strcmp (argv[i], "-o") && i + 1 != argc) + out_obj_filename = argv[++i]; + else + obstack_ptr_grow (&argv_obstack, argv[i]); + } + if (!out_obj_filename) + fatal_error ("output file not specified"); + obstack_ptr_grow (&argv_obstack, opt2); + obstack_ptr_grow (&argv_obstack, "-o"); + obstack_ptr_grow (&argv_obstack, target_so_filename); + compile_for_target (&argv_obstack); + + /* Run objcopy. */ + char *rename_section_opt + = XALLOCAVEC (char, sizeof (".data=") + strlen (image_section_name)); + sprintf (rename_section_opt, ".data=%s", image_section_name); + const char *objcopy_argv[11]; + objcopy_argv[0] = "objcopy"; + objcopy_argv[1] = "-B"; + objcopy_argv[2] = "i386"; + objcopy_argv[3] = "-I"; + objcopy_argv[4] = "binary"; + objcopy_argv[5] = "-O"; + if (target_ilp32) + objcopy_argv[6] = "elf32-i386"; + else + objcopy_argv[6] = "elf64-x86-64"; + objcopy_argv[7] = target_so_filename; + objcopy_argv[8] = "--rename-section"; + objcopy_argv[9] = rename_section_opt; + objcopy_argv[10] = NULL; + fork_execute (objcopy_argv[0], CONST_CAST (char **, objcopy_argv), false); + + /* Objcopy has created symbols, containing the input file name with + special characters replaced with '_'. We are going to rename these + new symbols. */ + size_t symbol_name_len = strlen (target_so_filename); + char *symbol_name = XALLOCAVEC (char, symbol_name_len + 1); + for (size_t i = 0; i <= symbol_name_len; i++) + { + char c = target_so_filename[i]; + if ((c == '/') || (c == '.')) + c = '_'; + symbol_name[i] = c; + } + + char *opt_for_objcopy[3]; + opt_for_objcopy[0] = XALLOCAVEC (char, sizeof ("_binary__start=") + + symbol_name_len + + strlen (symbols[0])); + opt_for_objcopy[1] = XALLOCAVEC (char, sizeof ("_binary__end=") + + symbol_name_len + + strlen (symbols[1])); + opt_for_objcopy[2] = XALLOCAVEC (char, sizeof ("_binary__size=") + + symbol_name_len + + strlen (symbols[2])); + sprintf (opt_for_objcopy[0], "_binary_%s_start=%s", symbol_name, symbols[0]); + sprintf (opt_for_objcopy[1], "_binary_%s_end=%s", symbol_name, symbols[1]); + sprintf (opt_for_objcopy[2], "_binary_%s_size=%s", symbol_name, symbols[2]); + + objcopy_argv[0] = "objcopy"; + objcopy_argv[1] = target_so_filename; + objcopy_argv[2] = "--redefine-sym"; + objcopy_argv[3] = opt_for_objcopy[0]; + objcopy_argv[4] = "--redefine-sym"; + objcopy_argv[5] = opt_for_objcopy[1]; + objcopy_argv[6] = "--redefine-sym"; + objcopy_argv[7] = opt_for_objcopy[2]; + objcopy_argv[8] = NULL; + fork_execute (objcopy_argv[0], CONST_CAST (char **, objcopy_argv), false); + + return target_so_filename; +} + +int +main (int argc, char **argv) +{ + progname = "mkoffload-intelmic"; + gcc_init_libintl (); + diagnostic_initialize (global_dc, 0); + + if (atexit (mkoffload_atexit) != 0) + fatal_error ("atexit failed"); + + const char *host_compiler = getenv ("COLLECT_GCC"); + if (!host_compiler) + fatal_error ("COLLECT_GCC must be set"); + + const char *target_driver_name + = DEFAULT_REAL_TARGET_MACHINE "-accel-" DEFAULT_TARGET_MACHINE "-gcc"; + char *target_compiler = find_target_compiler (target_driver_name); + if (target_compiler == NULL) + fatal_error ("offload compiler %s not found", target_driver_name); + + /* 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); + + /* Find out whether we should compile binaries for i386 or x86-64. */ + for (int i = argc - 1; i > 0; i--) + if (strncmp (argv[i], "-foffload-abi=", sizeof ("-foffload-abi=") - 1) == 0) + { + if (strstr (argv[i], "ilp32")) + target_ilp32 = true; + else if (!strstr (argv[i], "lp64")) + fatal_error ("unrecognizable argument of option -foffload-abi"); + break; + } + + const char *target_so_filename + = prepare_target_image (target_compiler, argc, argv); + + const char *host_descr_filename = generate_host_descr_file (host_compiler); + + /* Perform partial linking for the target image and host side descriptor. + As a result we'll get a finalized object file with all offload data. */ + unsigned new_argc = 0; + const char *new_argv[9]; + new_argv[new_argc++] = "ld"; + if (target_ilp32) + { + new_argv[new_argc++] = "-m"; + new_argv[new_argc++] = "elf_i386"; + } + new_argv[new_argc++] = "--relocatable"; + new_argv[new_argc++] = host_descr_filename; + new_argv[new_argc++] = target_so_filename; + new_argv[new_argc++] = "-o"; + new_argv[new_argc++] = out_obj_filename; + new_argv[new_argc++] = NULL; + fork_execute (new_argv[0], CONST_CAST (char **, new_argv), false); + + /* Run objcopy on the resultant object file to localize generated symbols + to avoid conflicting between different DSO and an executable. */ + new_argv[0] = "objcopy"; + new_argv[1] = "-L"; + new_argv[2] = symbols[0]; + new_argv[3] = "-L"; + new_argv[4] = symbols[1]; + new_argv[5] = "-L"; + new_argv[6] = symbols[2]; + new_argv[7] = out_obj_filename; + new_argv[8] = NULL; + fork_execute (new_argv[0], CONST_CAST (char **, new_argv), false); + + return 0; +} diff --git a/gcc/config/i386/t-intelmic b/gcc/config/i386/t-intelmic new file mode 100644 index 00000000000..8b36e0d00a8 --- /dev/null +++ b/gcc/config/i386/t-intelmic @@ -0,0 +1,9 @@ +mkoffload.o: $(srcdir)/config/i386/intelmic-mkoffload.c | insn-modes.h + $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \ + -I$(srcdir)/../libgomp \ + -DDEFAULT_REAL_TARGET_MACHINE=\"$(real_target_noncanonical)\" \ + -DDEFAULT_TARGET_MACHINE=\"$(target_noncanonical)\" \ + $< $(OUTPUT_OPTION) + +mkoffload$(exeext): mkoffload.o collect-utils.o libcommon-target.a $(LIBIBERTY) $(LIBDEPS) + $(COMPILER) -o $@ mkoffload.o collect-utils.o libcommon-target.a $(LIBIBERTY) $(LIBS) -- 2.30.2