Update gcc-50 to SVN version 231263 (gcc-5-branch)
[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 "system.h"
26 #include "coretypes.h"
27 #include "obstack.h"
28 #include "intl.h"
29 #include "diagnostic.h"
30 #include "collect-utils.h"
31 #include "intelmic-offload.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   char *target_compiler;
162   const char *collect_gcc = getenv ("COLLECT_GCC");
163   const char *gcc_path = dirname (ASTRDUP (collect_gcc));
164   const char *gcc_exec = basename (ASTRDUP (collect_gcc));
165
166   if (strcmp (gcc_exec, collect_gcc) == 0)
167     {
168       /* collect_gcc has no path, so it was found in PATH.  Make sure we also
169          find accel-gcc in PATH.  */
170       target_compiler = XDUPVEC (char, name, strlen (name) + 1);
171       found = true;
172       goto out;
173     }
174
175   target_compiler = concat (gcc_path, "/", name, NULL);
176   if (access_check (target_compiler, X_OK) == 0)
177     {
178       found = true;
179       goto out;
180     }
181
182   n_paths = parse_env_var (getenv ("COMPILER_PATH"), &paths);
183   for (i = 0; i < n_paths; i++)
184     {
185       size_t len = strlen (paths[i]) + 1 + strlen (name) + 1;
186       target_compiler = XRESIZEVEC (char, target_compiler, len);
187       sprintf (target_compiler, "%s/%s", paths[i], name);
188       if (access_check (target_compiler, X_OK) == 0)
189         {
190           found = true;
191           break;
192         }
193     }
194
195 out:
196   free_array_of_ptrs ((void **) paths, n_paths);
197   return found ? target_compiler : NULL;
198 }
199
200 static void
201 compile_for_target (struct obstack *argv_obstack)
202 {
203   if (target_ilp32)
204     obstack_ptr_grow (argv_obstack, "-m32");
205   else
206     obstack_ptr_grow (argv_obstack, "-m64");
207   obstack_ptr_grow (argv_obstack, NULL);
208   char **argv = XOBFINISH (argv_obstack, char **);
209
210   /* Save environment variables.  */
211   const char *epath = getenv ("GCC_EXEC_PREFIX");
212   const char *cpath = getenv ("COMPILER_PATH");
213   const char *lpath = getenv ("LIBRARY_PATH");
214   const char *rpath = getenv ("LD_RUN_PATH");
215   unsetenv ("GCC_EXEC_PREFIX");
216   unsetenv ("COMPILER_PATH");
217   unsetenv ("LIBRARY_PATH");
218   unsetenv ("LD_RUN_PATH");
219
220   fork_execute (argv[0], argv, false);
221   obstack_free (argv_obstack, NULL);
222
223   /* Restore environment variables.  */
224   xputenv (concat ("GCC_EXEC_PREFIX=", epath, NULL));
225   xputenv (concat ("COMPILER_PATH=", cpath, NULL));
226   xputenv (concat ("LIBRARY_PATH=", lpath, NULL));
227   xputenv (concat ("LD_RUN_PATH=", rpath, NULL));
228 }
229
230 /* Generates object file with the descriptor for the target library.  */
231 static const char *
232 generate_target_descr_file (const char *target_compiler)
233 {
234   const char *src_filename = make_temp_file ("_target_descr.c");
235   const char *obj_filename = make_temp_file ("_target_descr.o");
236   temp_files[num_temps++] = src_filename;
237   temp_files[num_temps++] = obj_filename;
238   FILE *src_file = fopen (src_filename, "w");
239
240   if (!src_file)
241     fatal_error (input_location, "cannot open '%s'", src_filename);
242
243   fprintf (src_file,
244            "extern void *__offload_funcs_end[];\n"
245            "extern void *__offload_vars_end[];\n\n"
246
247            "void *__offload_func_table[0]\n"
248            "__attribute__ ((__used__, visibility (\"hidden\"),\n"
249            "section (\".gnu.offload_funcs\"))) = { };\n\n"
250
251            "void *__offload_var_table[0]\n"
252            "__attribute__ ((__used__, visibility (\"hidden\"),\n"
253            "section (\".gnu.offload_vars\"))) = { };\n\n"
254
255            "void *__OFFLOAD_TARGET_TABLE__[]\n"
256            "__attribute__ ((__used__, visibility (\"hidden\"))) = {\n"
257            "  &__offload_func_table, &__offload_funcs_end,\n"
258            "  &__offload_var_table, &__offload_vars_end\n"
259            "};\n\n");
260
261   fprintf (src_file,
262            "#ifdef __cplusplus\n"
263            "extern \"C\"\n"
264            "#endif\n"
265            "void target_register_lib (const void *);\n\n"
266
267            "__attribute__((constructor))\n"
268            "static void\n"
269            "init (void)\n"
270            "{\n"
271            "  target_register_lib (__OFFLOAD_TARGET_TABLE__);\n"
272            "}\n");
273   fclose (src_file);
274
275   struct obstack argv_obstack;
276   obstack_init (&argv_obstack);
277   obstack_ptr_grow (&argv_obstack, target_compiler);
278   obstack_ptr_grow (&argv_obstack, "-c");
279   obstack_ptr_grow (&argv_obstack, "-shared");
280   obstack_ptr_grow (&argv_obstack, "-fPIC");
281   obstack_ptr_grow (&argv_obstack, src_filename);
282   obstack_ptr_grow (&argv_obstack, "-o");
283   obstack_ptr_grow (&argv_obstack, obj_filename);
284   compile_for_target (&argv_obstack);
285
286   return obj_filename;
287 }
288
289 /* Generates object file with __offload_*_end symbols for the target
290    library.  */
291 static const char *
292 generate_target_offloadend_file (const char *target_compiler)
293 {
294   const char *src_filename = make_temp_file ("_target_offloadend.c");
295   const char *obj_filename = make_temp_file ("_target_offloadend.o");
296   temp_files[num_temps++] = src_filename;
297   temp_files[num_temps++] = obj_filename;
298   FILE *src_file = fopen (src_filename, "w");
299
300   if (!src_file)
301     fatal_error (input_location, "cannot open '%s'", src_filename);
302
303   fprintf (src_file,
304            "void *__offload_funcs_end[0]\n"
305            "__attribute__ ((__used__, visibility (\"hidden\"),\n"
306            "section (\".gnu.offload_funcs\"))) = { };\n\n"
307
308            "void *__offload_vars_end[0]\n"
309            "__attribute__ ((__used__, visibility (\"hidden\"),\n"
310            "section (\".gnu.offload_vars\"))) = { };\n");
311   fclose (src_file);
312
313   struct obstack argv_obstack;
314   obstack_init (&argv_obstack);
315   obstack_ptr_grow (&argv_obstack, target_compiler);
316   obstack_ptr_grow (&argv_obstack, "-c");
317   obstack_ptr_grow (&argv_obstack, "-shared");
318   obstack_ptr_grow (&argv_obstack, "-fPIC");
319   obstack_ptr_grow (&argv_obstack, src_filename);
320   obstack_ptr_grow (&argv_obstack, "-o");
321   obstack_ptr_grow (&argv_obstack, obj_filename);
322   compile_for_target (&argv_obstack);
323
324   return obj_filename;
325 }
326
327 /* Generates object file with the host side descriptor.  */
328 static const char *
329 generate_host_descr_file (const char *host_compiler)
330 {
331   const char *src_filename = make_temp_file ("_host_descr.c");
332   const char *obj_filename = make_temp_file ("_host_descr.o");
333   temp_files[num_temps++] = src_filename;
334   temp_files[num_temps++] = obj_filename;
335   FILE *src_file = fopen (src_filename, "w");
336
337   if (!src_file)
338     fatal_error (input_location, "cannot open '%s'", src_filename);
339
340   fprintf (src_file,
341            "extern void *__OFFLOAD_TABLE__;\n"
342            "extern void *__offload_image_intelmic_start;\n"
343            "extern void *__offload_image_intelmic_end;\n\n"
344
345            "static const void *__offload_target_data[] = {\n"
346            "  &__offload_image_intelmic_start, &__offload_image_intelmic_end\n"
347            "};\n\n");
348
349   fprintf (src_file,
350            "#ifdef __cplusplus\n"
351            "extern \"C\"\n"
352            "#endif\n"
353            "void GOMP_offload_register (void *, int, void *);\n"
354            "#ifdef __cplusplus\n"
355            "extern \"C\"\n"
356            "#endif\n"
357            "void GOMP_offload_unregister (void *, int, void *);\n\n"
358
359            "__attribute__((constructor))\n"
360            "static void\n"
361            "init (void)\n"
362            "{\n"
363            "  GOMP_offload_register (&__OFFLOAD_TABLE__, %d, __offload_target_data);\n"
364            "}\n\n", GOMP_DEVICE_INTEL_MIC);
365
366   fprintf (src_file,
367            "__attribute__((destructor))\n"
368            "static void\n"
369            "fini (void)\n"
370            "{\n"
371            "  GOMP_offload_unregister (&__OFFLOAD_TABLE__, %d, __offload_target_data);\n"
372            "}\n", GOMP_DEVICE_INTEL_MIC);
373
374   fclose (src_file);
375
376   unsigned new_argc = 0;
377   const char *new_argv[9];
378   new_argv[new_argc++] = host_compiler;
379   new_argv[new_argc++] = "-c";
380   new_argv[new_argc++] = "-fPIC";
381   new_argv[new_argc++] = "-shared";
382   if (target_ilp32)
383     new_argv[new_argc++] = "-m32";
384   else
385     new_argv[new_argc++] = "-m64";
386   new_argv[new_argc++] = src_filename;
387   new_argv[new_argc++] = "-o";
388   new_argv[new_argc++] = obj_filename;
389   new_argv[new_argc++] = NULL;
390
391   fork_execute (new_argv[0], CONST_CAST (char **, new_argv), false);
392
393   return obj_filename;
394 }
395
396 static const char *
397 prepare_target_image (const char *target_compiler, int argc, char **argv)
398 {
399   const char *target_descr_filename
400     = generate_target_descr_file (target_compiler);
401   const char *target_offloadend_filename
402     = generate_target_offloadend_file (target_compiler);
403
404   char *opt1
405     = XALLOCAVEC (char, sizeof ("-Wl,") + strlen (target_descr_filename));
406   char *opt2
407     = XALLOCAVEC (char, sizeof ("-Wl,") + strlen (target_offloadend_filename));
408   sprintf (opt1, "-Wl,%s", target_descr_filename);
409   sprintf (opt2, "-Wl,%s", target_offloadend_filename);
410
411   const char *target_so_filename = make_temp_file ("_offload_intelmic.so");
412   temp_files[num_temps++] = target_so_filename;
413   struct obstack argv_obstack;
414   obstack_init (&argv_obstack);
415   obstack_ptr_grow (&argv_obstack, target_compiler);
416   obstack_ptr_grow (&argv_obstack, "-xlto");
417   obstack_ptr_grow (&argv_obstack, "-shared");
418   obstack_ptr_grow (&argv_obstack, "-fPIC");
419   obstack_ptr_grow (&argv_obstack, opt1);
420   for (int i = 1; i < argc; i++)
421     {
422       if (!strcmp (argv[i], "-o") && i + 1 != argc)
423         out_obj_filename = argv[++i];
424       else
425         obstack_ptr_grow (&argv_obstack, argv[i]);
426     }
427   if (!out_obj_filename)
428     fatal_error (input_location, "output file not specified");
429   obstack_ptr_grow (&argv_obstack, opt2);
430   obstack_ptr_grow (&argv_obstack, "-o");
431   obstack_ptr_grow (&argv_obstack, target_so_filename);
432   compile_for_target (&argv_obstack);
433
434   /* Run objcopy.  */
435   char *rename_section_opt
436     = XALLOCAVEC (char, sizeof (".data=") + strlen (image_section_name));
437   sprintf (rename_section_opt, ".data=%s", image_section_name);
438   const char *objcopy_argv[11];
439   objcopy_argv[0] = "objcopy";
440   objcopy_argv[1] = "-B";
441   objcopy_argv[2] = "i386";
442   objcopy_argv[3] = "-I";
443   objcopy_argv[4] = "binary";
444   objcopy_argv[5] = "-O";
445   if (target_ilp32)
446     objcopy_argv[6] = "elf32-i386";
447   else
448     objcopy_argv[6] = "elf64-x86-64";
449   objcopy_argv[7] = target_so_filename;
450   objcopy_argv[8] = "--rename-section";
451   objcopy_argv[9] = rename_section_opt;
452   objcopy_argv[10] = NULL;
453   fork_execute (objcopy_argv[0], CONST_CAST (char **, objcopy_argv), false);
454
455   /* Objcopy has created symbols, containing the input file name with
456      non-alphanumeric characters replaced by underscores.
457      We are going to rename these new symbols.  */
458   size_t symbol_name_len = strlen (target_so_filename);
459   char *symbol_name = XALLOCAVEC (char, symbol_name_len + 1);
460   for (size_t i = 0; i < symbol_name_len; i++)
461     {
462       char c = target_so_filename[i];
463       if (!ISALNUM (c))
464         c = '_';
465       symbol_name[i] = c;
466     }
467   symbol_name[symbol_name_len] = '\0';
468
469   char *opt_for_objcopy[3];
470   opt_for_objcopy[0] = XALLOCAVEC (char, sizeof ("_binary__start=")
471                                          + symbol_name_len
472                                          + strlen (symbols[0]));
473   opt_for_objcopy[1] = XALLOCAVEC (char, sizeof ("_binary__end=")
474                                          + symbol_name_len
475                                          + strlen (symbols[1]));
476   opt_for_objcopy[2] = XALLOCAVEC (char, sizeof ("_binary__size=")
477                                          + symbol_name_len
478                                          + strlen (symbols[2]));
479   sprintf (opt_for_objcopy[0], "_binary_%s_start=%s", symbol_name, symbols[0]);
480   sprintf (opt_for_objcopy[1], "_binary_%s_end=%s", symbol_name, symbols[1]);
481   sprintf (opt_for_objcopy[2], "_binary_%s_size=%s", symbol_name, symbols[2]);
482
483   objcopy_argv[0] = "objcopy";
484   objcopy_argv[1] = target_so_filename;
485   objcopy_argv[2] = "--redefine-sym";
486   objcopy_argv[3] = opt_for_objcopy[0];
487   objcopy_argv[4] = "--redefine-sym";
488   objcopy_argv[5] = opt_for_objcopy[1];
489   objcopy_argv[6] = "--redefine-sym";
490   objcopy_argv[7] = opt_for_objcopy[2];
491   objcopy_argv[8] = NULL;
492   fork_execute (objcopy_argv[0], CONST_CAST (char **, objcopy_argv), false);
493
494   return target_so_filename;
495 }
496
497 int
498 main (int argc, char **argv)
499 {
500   progname = "mkoffload-intelmic";
501   gcc_init_libintl ();
502   diagnostic_initialize (global_dc, 0);
503
504   if (atexit (mkoffload_atexit) != 0)
505     fatal_error (input_location, "atexit failed");
506
507   const char *host_compiler = getenv ("COLLECT_GCC");
508   if (!host_compiler)
509     fatal_error (input_location, "COLLECT_GCC must be set");
510
511   const char *target_driver_name = GCC_INSTALL_NAME;
512   char *target_compiler = find_target_compiler (target_driver_name);
513   if (target_compiler == NULL)
514     fatal_error (input_location, "offload compiler %s not found",
515                  target_driver_name);
516
517   /* We may be called with all the arguments stored in some file and
518      passed with @file.  Expand them into argv before processing.  */
519   expandargv (&argc, &argv);
520
521   /* Find out whether we should compile binaries for i386 or x86-64.  */
522   for (int i = argc - 1; i > 0; i--)
523     if (strncmp (argv[i], "-foffload-abi=", sizeof ("-foffload-abi=") - 1) == 0)
524       {
525         if (strstr (argv[i], "ilp32"))
526           target_ilp32 = true;
527         else if (!strstr (argv[i], "lp64"))
528           fatal_error (input_location,
529                        "unrecognizable argument of option -foffload-abi");
530         break;
531       }
532
533   const char *target_so_filename
534     = prepare_target_image (target_compiler, argc, argv);
535
536   const char *host_descr_filename = generate_host_descr_file (host_compiler);
537
538   /* Perform partial linking for the target image and host side descriptor.
539      As a result we'll get a finalized object file with all offload data.  */
540   unsigned new_argc = 0;
541   const char *new_argv[9];
542   new_argv[new_argc++] = "ld";
543   new_argv[new_argc++] = "-m";
544   if (target_ilp32)
545     new_argv[new_argc++] = "elf_i386";
546   else
547     new_argv[new_argc++] = "elf_x86_64";
548   new_argv[new_argc++] = "--relocatable";
549   new_argv[new_argc++] = host_descr_filename;
550   new_argv[new_argc++] = target_so_filename;
551   new_argv[new_argc++] = "-o";
552   new_argv[new_argc++] = out_obj_filename;
553   new_argv[new_argc++] = NULL;
554   fork_execute (new_argv[0], CONST_CAST (char **, new_argv), false);
555
556   /* Run objcopy on the resultant object file to localize generated symbols
557      to avoid conflicting between different DSO and an executable.  */
558   new_argv[0] = "objcopy";
559   new_argv[1] = "-L";
560   new_argv[2] = symbols[0];
561   new_argv[3] = "-L";
562   new_argv[4] = symbols[1];
563   new_argv[5] = "-L";
564   new_argv[6] = symbols[2];
565   new_argv[7] = out_obj_filename;
566   new_argv[8] = NULL;
567   fork_execute (new_argv[0], CONST_CAST (char **, new_argv), false);
568
569   return 0;
570 }