Merge branch 'vendor/GCC50' - gcc 5.0 snapshot 1 FEB 2015
[dragonfly.git] / contrib / gcc-5.0 / gcc / config / i386 / intelmic-mkoffload.c
1 /* Offload image generation tool for Intel MIC devices.
2
3    Copyright (C) 2014-2015 Free Software Foundation, Inc.
4
5    Contributed by Ilya Verbin <ilya.verbin@intel.com>.
6
7    This file is part of GCC.
8
9    GCC is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3, or (at your option)
12    any later version.
13
14    GCC is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with GCC; see the file COPYING3.  If not see
21    <http://www.gnu.org/licenses/>.  */
22
23 #include "config.h"
24 #include <libgen.h>
25 #include "libgomp-plugin.h"
26 #include "system.h"
27 #include "coretypes.h"
28 #include "obstack.h"
29 #include "intl.h"
30 #include "diagnostic.h"
31 #include "collect-utils.h"
32
33 const char tool_name[] = "intelmic mkoffload";
34
35 const char image_section_name[] = ".gnu.offload_images";
36 const char *symbols[3] = { "__offload_image_intelmic_start",
37                            "__offload_image_intelmic_end",
38                            "__offload_image_intelmic_size" };
39 const char *out_obj_filename = NULL;
40
41 int num_temps = 0;
42 const int MAX_NUM_TEMPS = 10;
43 const char *temp_files[MAX_NUM_TEMPS];
44
45 /* Shows if we should compile binaries for i386 instead of x86-64.  */
46 bool target_ilp32 = false;
47
48 /* Delete tempfiles and exit function.  */
49 void
50 tool_cleanup (bool from_signal ATTRIBUTE_UNUSED)
51 {
52   for (int i = 0; i < num_temps; i++)
53     maybe_unlink (temp_files[i]);
54 }
55
56 static void
57 mkoffload_atexit (void)
58 {
59   tool_cleanup (false);
60 }
61
62 /* Unlink FILE unless we are debugging.  */
63 void
64 maybe_unlink (const char *file)
65 {
66   if (debug)
67     notice ("[Leaving %s]\n", file);
68   else
69     unlink_if_ordinary (file);
70 }
71
72 /* Add or change the value of an environment variable, outputting the
73    change to standard error if in verbose mode.  */
74 static void
75 xputenv (const char *string)
76 {
77   if (verbose)
78     fprintf (stderr, "%s\n", string);
79   putenv (CONST_CAST (char *, string));
80 }
81
82 /* Parse STR, saving found tokens into PVALUES and return their number.
83    Tokens are assumed to be delimited by ':'.  */
84 static unsigned
85 parse_env_var (const char *str, char ***pvalues)
86 {
87   const char *curval, *nextval;
88   char **values;
89   unsigned num = 1, i;
90
91   curval = strchr (str, ':');
92   while (curval)
93     {
94       num++;
95       curval = strchr (curval + 1, ':');
96     }
97
98   values = (char **) xmalloc (num * sizeof (char *));
99   curval = str;
100   nextval = strchr (curval, ':');
101   if (nextval == NULL)
102     nextval = strchr (curval, '\0');
103
104   for (i = 0; i < num; i++)
105     {
106       int l = nextval - curval;
107       values[i] = (char *) xmalloc (l + 1);
108       memcpy (values[i], curval, l);
109       values[i][l] = 0;
110       curval = nextval + 1;
111       nextval = strchr (curval, ':');
112       if (nextval == NULL)
113         nextval = strchr (curval, '\0');
114     }
115   *pvalues = values;
116   return num;
117 }
118
119 /* Auxiliary function that frees elements of PTR and PTR itself.
120    N is number of elements to be freed.  If PTR is NULL, nothing is freed.
121    If an element is NULL, subsequent elements are not freed.  */
122 static void
123 free_array_of_ptrs (void **ptr, unsigned n)
124 {
125   unsigned i;
126   if (!ptr)
127     return;
128   for (i = 0; i < n; i++)
129     {
130       if (!ptr[i])
131         break;
132       free (ptr[i]);
133     }
134   free (ptr);
135   return;
136 }
137
138 /* Check whether NAME can be accessed in MODE.  This is like access,
139    except that it never considers directories to be executable.  */
140 static int
141 access_check (const char *name, int mode)
142 {
143   if (mode == X_OK)
144     {
145       struct stat st;
146
147       if (stat (name, &st) < 0 || S_ISDIR (st.st_mode))
148         return -1;
149     }
150
151   return access (name, mode);
152 }
153
154 /* Find target compiler using a path from COLLECT_GCC or COMPILER_PATH.  */
155 static char *
156 find_target_compiler (const char *name)
157 {
158   bool found = false;
159   char **paths = NULL;
160   unsigned n_paths, i;
161   const char *collect_path = dirname (ASTRDUP (getenv ("COLLECT_GCC")));
162   size_t len = strlen (collect_path) + 1 + strlen (name) + 1;
163   char *target_compiler = XNEWVEC (char, len);
164   sprintf (target_compiler, "%s/%s", collect_path, name);
165   if (access_check (target_compiler, X_OK) == 0)
166     {
167       found = true;
168       goto out;
169     }
170
171   n_paths = parse_env_var (getenv ("COMPILER_PATH"), &paths);
172   for (i = 0; i < n_paths; i++)
173     {
174       len = strlen (paths[i]) + 1 + strlen (name) + 1;
175       target_compiler = XRESIZEVEC (char, target_compiler, len);
176       sprintf (target_compiler, "%s/%s", paths[i], name);
177       if (access_check (target_compiler, X_OK) == 0)
178         {
179           found = true;
180           break;
181         }
182     }
183
184 out:
185   free_array_of_ptrs ((void **) paths, n_paths);
186   return found ? target_compiler : NULL;
187 }
188
189 static void
190 compile_for_target (struct obstack *argv_obstack)
191 {
192   if (target_ilp32)
193     obstack_ptr_grow (argv_obstack, "-m32");
194   else
195     obstack_ptr_grow (argv_obstack, "-m64");
196   obstack_ptr_grow (argv_obstack, NULL);
197   char **argv = XOBFINISH (argv_obstack, char **);
198
199   /* Save environment variables.  */
200   const char *epath = getenv ("GCC_EXEC_PREFIX");
201   const char *cpath = getenv ("COMPILER_PATH");
202   const char *lpath = getenv ("LIBRARY_PATH");
203   const char *rpath = getenv ("LD_RUN_PATH");
204   unsetenv ("GCC_EXEC_PREFIX");
205   unsetenv ("COMPILER_PATH");
206   unsetenv ("LIBRARY_PATH");
207   unsetenv ("LD_RUN_PATH");
208
209   fork_execute (argv[0], argv, false);
210   obstack_free (argv_obstack, NULL);
211
212   /* Restore environment variables.  */
213   xputenv (concat ("GCC_EXEC_PREFIX=", epath, NULL));
214   xputenv (concat ("COMPILER_PATH=", cpath, NULL));
215   xputenv (concat ("LIBRARY_PATH=", lpath, NULL));
216   xputenv (concat ("LD_RUN_PATH=", rpath, NULL));
217 }
218
219 /* Generates object file with the descriptor for the target library.  */
220 static const char *
221 generate_target_descr_file (const char *target_compiler)
222 {
223   const char *src_filename = make_temp_file ("_target_descr.c");
224   const char *obj_filename = make_temp_file ("_target_descr.o");
225   temp_files[num_temps++] = src_filename;
226   temp_files[num_temps++] = obj_filename;
227   FILE *src_file = fopen (src_filename, "w");
228
229   if (!src_file)
230     fatal_error (input_location, "cannot open '%s'", src_filename);
231
232   fprintf (src_file,
233            "extern void *__offload_funcs_end[];\n"
234            "extern void *__offload_vars_end[];\n\n"
235
236            "void *__offload_func_table[0]\n"
237            "__attribute__ ((__used__, visibility (\"hidden\"),\n"
238            "section (\".gnu.offload_funcs\"))) = { };\n\n"
239
240            "void *__offload_var_table[0]\n"
241            "__attribute__ ((__used__, visibility (\"hidden\"),\n"
242            "section (\".gnu.offload_vars\"))) = { };\n\n"
243
244            "void *__OFFLOAD_TARGET_TABLE__[]\n"
245            "__attribute__ ((__used__, visibility (\"hidden\"))) = {\n"
246            "  &__offload_func_table, &__offload_funcs_end,\n"
247            "  &__offload_var_table, &__offload_vars_end\n"
248            "};\n\n");
249
250   fprintf (src_file,
251            "#ifdef __cplusplus\n"
252            "extern \"C\"\n"
253            "#endif\n"
254            "void target_register_lib (const void *);\n\n"
255
256            "__attribute__((constructor))\n"
257            "static void\n"
258            "init (void)\n"
259            "{\n"
260            "  target_register_lib (__OFFLOAD_TARGET_TABLE__);\n"
261            "}\n");
262   fclose (src_file);
263
264   struct obstack argv_obstack;
265   obstack_init (&argv_obstack);
266   obstack_ptr_grow (&argv_obstack, target_compiler);
267   obstack_ptr_grow (&argv_obstack, "-c");
268   obstack_ptr_grow (&argv_obstack, "-shared");
269   obstack_ptr_grow (&argv_obstack, "-fPIC");
270   obstack_ptr_grow (&argv_obstack, src_filename);
271   obstack_ptr_grow (&argv_obstack, "-o");
272   obstack_ptr_grow (&argv_obstack, obj_filename);
273   compile_for_target (&argv_obstack);
274
275   return obj_filename;
276 }
277
278 /* Generates object file with __offload_*_end symbols for the target
279    library.  */
280 static const char *
281 generate_target_offloadend_file (const char *target_compiler)
282 {
283   const char *src_filename = make_temp_file ("_target_offloadend.c");
284   const char *obj_filename = make_temp_file ("_target_offloadend.o");
285   temp_files[num_temps++] = src_filename;
286   temp_files[num_temps++] = obj_filename;
287   FILE *src_file = fopen (src_filename, "w");
288
289   if (!src_file)
290     fatal_error (input_location, "cannot open '%s'", src_filename);
291
292   fprintf (src_file,
293            "void *__offload_funcs_end[0]\n"
294            "__attribute__ ((__used__, visibility (\"hidden\"),\n"
295            "section (\".gnu.offload_funcs\"))) = { };\n\n"
296
297            "void *__offload_vars_end[0]\n"
298            "__attribute__ ((__used__, visibility (\"hidden\"),\n"
299            "section (\".gnu.offload_vars\"))) = { };\n");
300   fclose (src_file);
301
302   struct obstack argv_obstack;
303   obstack_init (&argv_obstack);
304   obstack_ptr_grow (&argv_obstack, target_compiler);
305   obstack_ptr_grow (&argv_obstack, "-c");
306   obstack_ptr_grow (&argv_obstack, "-shared");
307   obstack_ptr_grow (&argv_obstack, "-fPIC");
308   obstack_ptr_grow (&argv_obstack, src_filename);
309   obstack_ptr_grow (&argv_obstack, "-o");
310   obstack_ptr_grow (&argv_obstack, obj_filename);
311   compile_for_target (&argv_obstack);
312
313   return obj_filename;
314 }
315
316 /* Generates object file with the host side descriptor.  */
317 static const char *
318 generate_host_descr_file (const char *host_compiler)
319 {
320   const char *src_filename = make_temp_file ("_host_descr.c");
321   const char *obj_filename = make_temp_file ("_host_descr.o");
322   temp_files[num_temps++] = src_filename;
323   temp_files[num_temps++] = obj_filename;
324   FILE *src_file = fopen (src_filename, "w");
325
326   if (!src_file)
327     fatal_error (input_location, "cannot open '%s'", src_filename);
328
329   fprintf (src_file,
330            "extern void *__OFFLOAD_TABLE__;\n"
331            "extern void *__offload_image_intelmic_start;\n"
332            "extern void *__offload_image_intelmic_end;\n\n"
333
334            "static const void *__offload_target_data[] = {\n"
335            "  &__offload_image_intelmic_start, &__offload_image_intelmic_end\n"
336            "};\n\n");
337
338   fprintf (src_file,
339            "#ifdef __cplusplus\n"
340            "extern \"C\"\n"
341            "#endif\n"
342            "void GOMP_offload_register (void *, int, void *);\n\n"
343
344            "__attribute__((constructor))\n"
345            "static void\n"
346            "init (void)\n"
347            "{\n"
348            "  GOMP_offload_register (&__OFFLOAD_TABLE__, %d, __offload_target_data);\n"
349            "}\n", OFFLOAD_TARGET_TYPE_INTEL_MIC);
350   fclose (src_file);
351
352   unsigned new_argc = 0;
353   const char *new_argv[9];
354   new_argv[new_argc++] = host_compiler;
355   new_argv[new_argc++] = "-c";
356   new_argv[new_argc++] = "-fPIC";
357   new_argv[new_argc++] = "-shared";
358   if (target_ilp32)
359     new_argv[new_argc++] = "-m32";
360   else
361     new_argv[new_argc++] = "-m64";
362   new_argv[new_argc++] = src_filename;
363   new_argv[new_argc++] = "-o";
364   new_argv[new_argc++] = obj_filename;
365   new_argv[new_argc++] = NULL;
366
367   fork_execute (new_argv[0], CONST_CAST (char **, new_argv), false);
368
369   return obj_filename;
370 }
371
372 static const char *
373 prepare_target_image (const char *target_compiler, int argc, char **argv)
374 {
375   const char *target_descr_filename
376     = generate_target_descr_file (target_compiler);
377   const char *target_offloadend_filename
378     = generate_target_offloadend_file (target_compiler);
379
380   char *opt1
381     = XALLOCAVEC (char, sizeof ("-Wl,") + strlen (target_descr_filename));
382   char *opt2
383     = XALLOCAVEC (char, sizeof ("-Wl,") + strlen (target_offloadend_filename));
384   sprintf (opt1, "-Wl,%s", target_descr_filename);
385   sprintf (opt2, "-Wl,%s", target_offloadend_filename);
386
387   const char *target_so_filename = make_temp_file ("_offload_intelmic.so");
388   temp_files[num_temps++] = target_so_filename;
389   struct obstack argv_obstack;
390   obstack_init (&argv_obstack);
391   obstack_ptr_grow (&argv_obstack, target_compiler);
392   obstack_ptr_grow (&argv_obstack, "-xlto");
393   obstack_ptr_grow (&argv_obstack, "-shared");
394   obstack_ptr_grow (&argv_obstack, "-fPIC");
395   obstack_ptr_grow (&argv_obstack, opt1);
396   for (int i = 1; i < argc; i++)
397     {
398       if (!strcmp (argv[i], "-o") && i + 1 != argc)
399         out_obj_filename = argv[++i];
400       else
401         obstack_ptr_grow (&argv_obstack, argv[i]);
402     }
403   if (!out_obj_filename)
404     fatal_error (input_location, "output file not specified");
405   obstack_ptr_grow (&argv_obstack, opt2);
406   obstack_ptr_grow (&argv_obstack, "-o");
407   obstack_ptr_grow (&argv_obstack, target_so_filename);
408   compile_for_target (&argv_obstack);
409
410   /* Run objcopy.  */
411   char *rename_section_opt
412     = XALLOCAVEC (char, sizeof (".data=") + strlen (image_section_name));
413   sprintf (rename_section_opt, ".data=%s", image_section_name);
414   const char *objcopy_argv[11];
415   objcopy_argv[0] = "objcopy";
416   objcopy_argv[1] = "-B";
417   objcopy_argv[2] = "i386";
418   objcopy_argv[3] = "-I";
419   objcopy_argv[4] = "binary";
420   objcopy_argv[5] = "-O";
421   if (target_ilp32)
422     objcopy_argv[6] = "elf32-i386";
423   else
424     objcopy_argv[6] = "elf64-x86-64";
425   objcopy_argv[7] = target_so_filename;
426   objcopy_argv[8] = "--rename-section";
427   objcopy_argv[9] = rename_section_opt;
428   objcopy_argv[10] = NULL;
429   fork_execute (objcopy_argv[0], CONST_CAST (char **, objcopy_argv), false);
430
431   /* Objcopy has created symbols, containing the input file name with
432      special characters replaced with '_'.  We are going to rename these
433      new symbols.  */
434   size_t symbol_name_len = strlen (target_so_filename);
435   char *symbol_name = XALLOCAVEC (char, symbol_name_len + 1);
436   for (size_t i = 0; i <= symbol_name_len; i++)
437     {
438       char c = target_so_filename[i];
439       if ((c == '/') || (c == '.'))
440         c = '_';
441       symbol_name[i] = c;
442     }
443
444   char *opt_for_objcopy[3];
445   opt_for_objcopy[0] = XALLOCAVEC (char, sizeof ("_binary__start=")
446                                          + symbol_name_len
447                                          + strlen (symbols[0]));
448   opt_for_objcopy[1] = XALLOCAVEC (char, sizeof ("_binary__end=")
449                                          + symbol_name_len
450                                          + strlen (symbols[1]));
451   opt_for_objcopy[2] = XALLOCAVEC (char, sizeof ("_binary__size=")
452                                          + symbol_name_len
453                                          + strlen (symbols[2]));
454   sprintf (opt_for_objcopy[0], "_binary_%s_start=%s", symbol_name, symbols[0]);
455   sprintf (opt_for_objcopy[1], "_binary_%s_end=%s", symbol_name, symbols[1]);
456   sprintf (opt_for_objcopy[2], "_binary_%s_size=%s", symbol_name, symbols[2]);
457
458   objcopy_argv[0] = "objcopy";
459   objcopy_argv[1] = target_so_filename;
460   objcopy_argv[2] = "--redefine-sym";
461   objcopy_argv[3] = opt_for_objcopy[0];
462   objcopy_argv[4] = "--redefine-sym";
463   objcopy_argv[5] = opt_for_objcopy[1];
464   objcopy_argv[6] = "--redefine-sym";
465   objcopy_argv[7] = opt_for_objcopy[2];
466   objcopy_argv[8] = NULL;
467   fork_execute (objcopy_argv[0], CONST_CAST (char **, objcopy_argv), false);
468
469   return target_so_filename;
470 }
471
472 int
473 main (int argc, char **argv)
474 {
475   progname = "mkoffload-intelmic";
476   gcc_init_libintl ();
477   diagnostic_initialize (global_dc, 0);
478
479   if (atexit (mkoffload_atexit) != 0)
480     fatal_error (input_location, "atexit failed");
481
482   const char *host_compiler = getenv ("COLLECT_GCC");
483   if (!host_compiler)
484     fatal_error (input_location, "COLLECT_GCC must be set");
485
486   const char *target_driver_name
487     = DEFAULT_REAL_TARGET_MACHINE "-accel-" DEFAULT_TARGET_MACHINE "-gcc";
488   char *target_compiler = find_target_compiler (target_driver_name);
489   if (target_compiler == NULL)
490     fatal_error (input_location, "offload compiler %s not found",
491                  target_driver_name);
492
493   /* We may be called with all the arguments stored in some file and
494      passed with @file.  Expand them into argv before processing.  */
495   expandargv (&argc, &argv);
496
497   /* Find out whether we should compile binaries for i386 or x86-64.  */
498   for (int i = argc - 1; i > 0; i--)
499     if (strncmp (argv[i], "-foffload-abi=", sizeof ("-foffload-abi=") - 1) == 0)
500       {
501         if (strstr (argv[i], "ilp32"))
502           target_ilp32 = true;
503         else if (!strstr (argv[i], "lp64"))
504           fatal_error (input_location,
505                        "unrecognizable argument of option -foffload-abi");
506         break;
507       }
508
509   const char *target_so_filename
510     = prepare_target_image (target_compiler, argc, argv);
511
512   const char *host_descr_filename = generate_host_descr_file (host_compiler);
513
514   /* Perform partial linking for the target image and host side descriptor.
515      As a result we'll get a finalized object file with all offload data.  */
516   unsigned new_argc = 0;
517   const char *new_argv[9];
518   new_argv[new_argc++] = "ld";
519   new_argv[new_argc++] = "-m";
520   if (target_ilp32)
521     new_argv[new_argc++] = "elf_i386";
522   else
523     new_argv[new_argc++] = "elf_x86_64";
524   new_argv[new_argc++] = "--relocatable";
525   new_argv[new_argc++] = host_descr_filename;
526   new_argv[new_argc++] = target_so_filename;
527   new_argv[new_argc++] = "-o";
528   new_argv[new_argc++] = out_obj_filename;
529   new_argv[new_argc++] = NULL;
530   fork_execute (new_argv[0], CONST_CAST (char **, new_argv), false);
531
532   /* Run objcopy on the resultant object file to localize generated symbols
533      to avoid conflicting between different DSO and an executable.  */
534   new_argv[0] = "objcopy";
535   new_argv[1] = "-L";
536   new_argv[2] = symbols[0];
537   new_argv[3] = "-L";
538   new_argv[4] = symbols[1];
539   new_argv[5] = "-L";
540   new_argv[6] = symbols[2];
541   new_argv[7] = out_obj_filename;
542   new_argv[8] = NULL;
543   fork_execute (new_argv[0], CONST_CAST (char **, new_argv), false);
544
545   return 0;
546 }