1 /* Offload image generation tool for PTX.
2 
3    Copyright (C) 2014-2016 Free Software Foundation, Inc.
4 
5    Contributed by Nathan Sidwell <nathan@codesourcery.com> and
6    Bernd Schmidt <bernds@codesourcery.com>.
7 
8    This file is part of GCC.
9 
10    GCC is free software; you can redistribute it and/or modify it
11    under the terms of the GNU General Public License as published
12    by the Free Software Foundation; either version 3, or (at your
13    option) any later version.
14 
15    GCC is distributed in the hope that it will be useful, but WITHOUT
16    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
18    License for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with GCC; see the file COPYING3.  If not see
22    <http://www.gnu.org/licenses/>.  */
23 
24 /* Munges PTX assembly into a C source file defining the PTX code as a
25    string.
26 
27    This is not a complete assembler.  We presume the source is well
28    formed from the compiler and can die horribly if it is not.  */
29 
30 #include "config.h"
31 #include "system.h"
32 #include "coretypes.h"
33 #include "obstack.h"
34 #include "diagnostic.h"
35 #include "intl.h"
36 #include <libgen.h>
37 #include "collect-utils.h"
38 #include "gomp-constants.h"
39 
40 const char tool_name[] = "nvptx mkoffload";
41 
42 #define COMMENT_PREFIX "#"
43 
44 struct id_map
45 {
46   id_map *next;
47   char *ptx_name;
48 };
49 
50 static id_map *func_ids, **funcs_tail = &func_ids;
51 static id_map *var_ids, **vars_tail = &var_ids;
52 
53 /* Files to unlink.  */
54 static const char *ptx_name;
55 static const char *ptx_cfile_name;
56 
57 enum offload_abi offload_abi = OFFLOAD_ABI_UNSET;
58 
59 /* Delete tempfiles.  */
60 
61 void
tool_cleanup(bool from_signal ATTRIBUTE_UNUSED)62 tool_cleanup (bool from_signal ATTRIBUTE_UNUSED)
63 {
64   if (ptx_cfile_name)
65     maybe_unlink (ptx_cfile_name);
66   if (ptx_name)
67     maybe_unlink (ptx_name);
68 }
69 
70 static void
mkoffload_cleanup(void)71 mkoffload_cleanup (void)
72 {
73   tool_cleanup (false);
74 }
75 
76 /* Unlink FILE unless requested otherwise.  */
77 
78 void
maybe_unlink(const char * file)79 maybe_unlink (const char *file)
80 {
81   if (!save_temps)
82     {
83       if (unlink_if_ordinary (file)
84 	  && errno != ENOENT)
85 	fatal_error (input_location, "deleting file %s: %m", file);
86     }
87   else if (verbose)
88     fprintf (stderr, "[Leaving %s]\n", file);
89 }
90 
91 /* Add or change the value of an environment variable, outputting the
92    change to standard error if in verbose mode.  */
93 static void
xputenv(const char * string)94 xputenv (const char *string)
95 {
96   if (verbose)
97     fprintf (stderr, "%s\n", string);
98   putenv (CONST_CAST (char *, string));
99 }
100 
101 
102 static void
record_id(const char * p1,id_map *** where)103 record_id (const char *p1, id_map ***where)
104 {
105   const char *end = strchr (p1, '\n');
106   if (!end)
107     fatal_error (input_location, "malformed ptx file");
108 
109   id_map *v = XNEW (id_map);
110   size_t len = end - p1;
111   v->ptx_name = XNEWVEC (char, len + 1);
112   memcpy (v->ptx_name, p1, len);
113   v->ptx_name[len] = '\0';
114   v->next = NULL;
115   id_map **tail = *where;
116   *tail = v;
117   *where = &v->next;
118 }
119 
120 /* Read the whole input file.  It will be NUL terminated (but
121    remember, there could be a NUL in the file itself.  */
122 
123 static const char *
read_file(FILE * stream,size_t * plen)124 read_file (FILE *stream, size_t *plen)
125 {
126   size_t alloc = 16384;
127   size_t base = 0;
128   char *buffer;
129 
130   if (!fseek (stream, 0, SEEK_END))
131     {
132       /* Get the file size.  */
133       long s = ftell (stream);
134       if (s >= 0)
135 	alloc = s + 100;
136       fseek (stream, 0, SEEK_SET);
137     }
138   buffer = XNEWVEC (char, alloc);
139 
140   for (;;)
141     {
142       size_t n = fread (buffer + base, 1, alloc - base - 1, stream);
143 
144       if (!n)
145 	break;
146       base += n;
147       if (base + 1 == alloc)
148 	{
149 	  alloc *= 2;
150 	  buffer = XRESIZEVEC (char, buffer, alloc);
151 	}
152     }
153   buffer[base] = 0;
154   *plen = base;
155   return buffer;
156 }
157 
158 /* Parse STR, saving found tokens into PVALUES and return their number.
159    Tokens are assumed to be delimited by ':'.  */
160 static unsigned
parse_env_var(const char * str,char *** pvalues)161 parse_env_var (const char *str, char ***pvalues)
162 {
163   const char *curval, *nextval;
164   char **values;
165   unsigned num = 1, i;
166 
167   curval = strchr (str, ':');
168   while (curval)
169     {
170       num++;
171       curval = strchr (curval + 1, ':');
172     }
173 
174   values = (char **) xmalloc (num * sizeof (char *));
175   curval = str;
176   nextval = strchr (curval, ':');
177   if (nextval == NULL)
178     nextval = strchr (curval, '\0');
179 
180   for (i = 0; i < num; i++)
181     {
182       int l = nextval - curval;
183       values[i] = (char *) xmalloc (l + 1);
184       memcpy (values[i], curval, l);
185       values[i][l] = 0;
186       curval = nextval + 1;
187       nextval = strchr (curval, ':');
188       if (nextval == NULL)
189 	nextval = strchr (curval, '\0');
190     }
191   *pvalues = values;
192   return num;
193 }
194 
195 /* Auxiliary function that frees elements of PTR and PTR itself.
196    N is number of elements to be freed.  If PTR is NULL, nothing is freed.
197    If an element is NULL, subsequent elements are not freed.  */
198 static void
free_array_of_ptrs(void ** ptr,unsigned n)199 free_array_of_ptrs (void **ptr, unsigned n)
200 {
201   unsigned i;
202   if (!ptr)
203     return;
204   for (i = 0; i < n; i++)
205     {
206       if (!ptr[i])
207 	break;
208       free (ptr[i]);
209     }
210   free (ptr);
211   return;
212 }
213 
214 /* Check whether NAME can be accessed in MODE.  This is like access,
215    except that it never considers directories to be executable.  */
216 static int
access_check(const char * name,int mode)217 access_check (const char *name, int mode)
218 {
219   if (mode == X_OK)
220     {
221       struct stat st;
222 
223       if (stat (name, &st) < 0 || S_ISDIR (st.st_mode))
224 	return -1;
225     }
226 
227   return access (name, mode);
228 }
229 
230 static void
process(FILE * in,FILE * out)231 process (FILE *in, FILE *out)
232 {
233   size_t len = 0;
234   const char *input = read_file (in, &len);
235   const char *comma;
236   id_map const *id;
237   unsigned obj_count = 0;
238   unsigned ix;
239 
240   /* Dump out char arrays for each PTX object file.  These are
241      terminated by a NUL.  */
242   for (size_t i = 0; i != len;)
243     {
244       char c;
245 
246       fprintf (out, "static const char ptx_code_%u[] =\n\t\"", obj_count++);
247       while ((c = input[i++]))
248 	{
249 	  switch (c)
250 	    {
251 	    case '\r':
252 	      continue;
253 	    case '\n':
254 	      fprintf (out, "\\n\"\n\t\"");
255 	      /* Look for mappings on subsequent lines.  */
256 	      while (strncmp (input + i, "//:", 3) == 0)
257 		{
258 		  i += 3;
259 
260 		  if (strncmp (input + i, "VAR_MAP ", 8) == 0)
261 		    record_id (input + i + 8, &vars_tail);
262 		  else if (strncmp (input + i, "FUNC_MAP ", 9) == 0)
263 		    record_id (input + i + 9, &funcs_tail);
264 		  else
265 		    abort ();
266 		  /* Skip to next line. */
267 		  while (input[i++] != '\n')
268 		    continue;
269 		}
270 	      continue;
271 	    case '"':
272 	    case '\\':
273 	      putc ('\\', out);
274 	      break;
275 	    default:
276 	      break;
277 	    }
278 	  putc (c, out);
279 	}
280       fprintf (out, "\";\n\n");
281     }
282 
283   /* Dump out array of pointers to ptx object strings.  */
284   fprintf (out, "static const struct ptx_obj {\n"
285 	   "  const char *code;\n"
286 	   "  __SIZE_TYPE__ size;\n"
287 	   "} ptx_objs[] = {");
288   for (comma = "", ix = 0; ix != obj_count; comma = ",", ix++)
289     fprintf (out, "%s\n\t{ptx_code_%u, sizeof (ptx_code_%u)}", comma, ix, ix);
290   fprintf (out, "\n};\n\n");
291 
292   /* Dump out variable idents.  */
293   fprintf (out, "static const char *const var_mappings[] = {");
294   for (comma = "", id = var_ids; id; comma = ",", id = id->next)
295     fprintf (out, "%s\n\t%s", comma, id->ptx_name);
296   fprintf (out, "\n};\n\n");
297 
298   /* Dump out function idents.  */
299   fprintf (out, "static const struct nvptx_fn {\n"
300 	   "  const char *name;\n"
301 	   "  unsigned short dim[%d];\n"
302 	   "} func_mappings[] = {\n", GOMP_DIM_MAX);
303   for (comma = "", id = func_ids; id; comma = ",", id = id->next)
304     fprintf (out, "%s\n\t{%s}", comma, id->ptx_name);
305   fprintf (out, "\n};\n\n");
306 
307   fprintf (out,
308 	   "static const struct nvptx_tdata {\n"
309 	   "  const struct ptx_obj *ptx_objs;\n"
310 	   "  unsigned ptx_num;\n"
311 	   "  const char *const *var_names;\n"
312 	   "  unsigned var_num;\n"
313 	   "  const struct nvptx_fn *fn_names;\n"
314 	   "  unsigned fn_num;\n"
315 	   "} target_data = {\n"
316 	   "  ptx_objs, sizeof (ptx_objs) / sizeof (ptx_objs[0]),\n"
317 	   "  var_mappings,"
318 	   "  sizeof (var_mappings) / sizeof (var_mappings[0]),\n"
319 	   "  func_mappings,"
320 	   "  sizeof (func_mappings) / sizeof (func_mappings[0])\n"
321 	   "};\n\n");
322 
323   fprintf (out, "#ifdef __cplusplus\n"
324 	   "extern \"C\" {\n"
325 	   "#endif\n");
326 
327   fprintf (out, "extern void GOMP_offload_register_ver"
328 	   " (unsigned, const void *, int, const void *);\n");
329   fprintf (out, "extern void GOMP_offload_unregister_ver"
330 	   " (unsigned, const void *, int, const void *);\n");
331 
332   fprintf (out, "#ifdef __cplusplus\n"
333 	   "}\n"
334 	   "#endif\n");
335 
336   fprintf (out, "extern const void *const __OFFLOAD_TABLE__[];\n\n");
337 
338   fprintf (out, "static __attribute__((constructor)) void init (void)\n"
339 	   "{\n"
340 	   "  GOMP_offload_register_ver (%#x, __OFFLOAD_TABLE__,"
341 	   "%d/*NVIDIA_PTX*/, &target_data);\n"
342 	   "};\n",
343 	   GOMP_VERSION_PACK (GOMP_VERSION, GOMP_VERSION_NVIDIA_PTX),
344 	   GOMP_DEVICE_NVIDIA_PTX);
345 
346   fprintf (out, "static __attribute__((destructor)) void fini (void)\n"
347 	   "{\n"
348 	   "  GOMP_offload_unregister_ver (%#x, __OFFLOAD_TABLE__,"
349 	   "%d/*NVIDIA_PTX*/, &target_data);\n"
350 	   "};\n",
351 	   GOMP_VERSION_PACK (GOMP_VERSION, GOMP_VERSION_NVIDIA_PTX),
352 	   GOMP_DEVICE_NVIDIA_PTX);
353 }
354 
355 static void
compile_native(const char * infile,const char * outfile,const char * compiler)356 compile_native (const char *infile, const char *outfile, const char *compiler)
357 {
358   const char *collect_gcc_options = getenv ("COLLECT_GCC_OPTIONS");
359   if (!collect_gcc_options)
360     fatal_error (input_location,
361 		 "environment variable COLLECT_GCC_OPTIONS must be set");
362 
363   struct obstack argv_obstack;
364   obstack_init (&argv_obstack);
365   obstack_ptr_grow (&argv_obstack, compiler);
366   if (save_temps)
367     obstack_ptr_grow (&argv_obstack, "-save-temps");
368   if (verbose)
369     obstack_ptr_grow (&argv_obstack, "-v");
370   switch (offload_abi)
371     {
372     case OFFLOAD_ABI_LP64:
373       obstack_ptr_grow (&argv_obstack, "-m64");
374       break;
375     case OFFLOAD_ABI_ILP32:
376       obstack_ptr_grow (&argv_obstack, "-m32");
377       break;
378     default:
379       gcc_unreachable ();
380     }
381   obstack_ptr_grow (&argv_obstack, infile);
382   obstack_ptr_grow (&argv_obstack, "-c");
383   obstack_ptr_grow (&argv_obstack, "-o");
384   obstack_ptr_grow (&argv_obstack, outfile);
385   obstack_ptr_grow (&argv_obstack, NULL);
386 
387   const char **new_argv = XOBFINISH (&argv_obstack, const char **);
388   fork_execute (new_argv[0], CONST_CAST (char **, new_argv), true);
389   obstack_free (&argv_obstack, NULL);
390 }
391 
392 int
main(int argc,char ** argv)393 main (int argc, char **argv)
394 {
395   FILE *in = stdin;
396   FILE *out = stdout;
397   const char *outname = 0;
398 
399   progname = "mkoffload";
400   diagnostic_initialize (global_dc, 0);
401 
402   if (atexit (mkoffload_cleanup) != 0)
403     fatal_error (input_location, "atexit failed");
404 
405   char *collect_gcc = getenv ("COLLECT_GCC");
406   if (collect_gcc == NULL)
407     fatal_error (input_location, "COLLECT_GCC must be set.");
408   const char *gcc_path = dirname (ASTRDUP (collect_gcc));
409   const char *gcc_exec = basename (ASTRDUP (collect_gcc));
410 
411   size_t len = (strlen (gcc_path) + 1
412 		+ strlen (GCC_INSTALL_NAME)
413 		+ 1);
414   char *driver = XALLOCAVEC (char, len);
415 
416   if (strcmp (gcc_exec, collect_gcc) == 0)
417     /* collect_gcc has no path, so it was found in PATH.  Make sure we also
418        find accel-gcc in PATH.  */
419     gcc_path = NULL;
420 
421   int driver_used = 0;
422   if (gcc_path != NULL)
423     driver_used = sprintf (driver, "%s/", gcc_path);
424   sprintf (driver + driver_used, "%s", GCC_INSTALL_NAME);
425 
426   bool found = false;
427   if (gcc_path == NULL)
428     found = true;
429   else if (access_check (driver, X_OK) == 0)
430     found = true;
431   else
432     {
433       /* Don't use alloca pointer with XRESIZEVEC.  */
434       driver = NULL;
435       /* Look in all COMPILER_PATHs for GCC_INSTALL_NAME.  */
436       char **paths = NULL;
437       unsigned n_paths;
438       n_paths = parse_env_var (getenv ("COMPILER_PATH"), &paths);
439       for (unsigned i = 0; i < n_paths; i++)
440 	{
441 	  len = strlen (paths[i]) + 1 + strlen (GCC_INSTALL_NAME) + 1;
442 	  driver = XRESIZEVEC (char, driver, len);
443 	  sprintf (driver, "%s/%s", paths[i], GCC_INSTALL_NAME);
444 	  if (access_check (driver, X_OK) == 0)
445 	    {
446 	      found = true;
447 	      break;
448 	    }
449 	}
450       free_array_of_ptrs ((void **) paths, n_paths);
451     }
452 
453   if (!found)
454     fatal_error (input_location,
455 		 "offload compiler %s not found", GCC_INSTALL_NAME);
456 
457   /* We may be called with all the arguments stored in some file and
458      passed with @file.  Expand them into argv before processing.  */
459   expandargv (&argc, &argv);
460 
461   /* Scan the argument vector.  */
462   bool fopenmp = false;
463   for (int i = 1; i < argc; i++)
464     {
465 #define STR "-foffload-abi="
466       if (strncmp (argv[i], STR, strlen (STR)) == 0)
467 	{
468 	  if (strcmp (argv[i] + strlen (STR), "lp64") == 0)
469 	    offload_abi = OFFLOAD_ABI_LP64;
470 	  else if (strcmp (argv[i] + strlen (STR), "ilp32") == 0)
471 	    offload_abi = OFFLOAD_ABI_ILP32;
472 	  else
473 	    fatal_error (input_location,
474 			 "unrecognizable argument of option " STR);
475 	}
476 #undef STR
477       else if (strcmp (argv[i], "-fopenmp") == 0)
478 	fopenmp = true;
479       else if (strcmp (argv[i], "-save-temps") == 0)
480 	save_temps = true;
481       else if (strcmp (argv[i], "-v") == 0)
482 	verbose = true;
483     }
484 
485   struct obstack argv_obstack;
486   obstack_init (&argv_obstack);
487   obstack_ptr_grow (&argv_obstack, driver);
488   if (save_temps)
489     obstack_ptr_grow (&argv_obstack, "-save-temps");
490   if (verbose)
491     obstack_ptr_grow (&argv_obstack, "-v");
492   obstack_ptr_grow (&argv_obstack, "-xlto");
493   switch (offload_abi)
494     {
495     case OFFLOAD_ABI_LP64:
496       obstack_ptr_grow (&argv_obstack, "-m64");
497       break;
498     case OFFLOAD_ABI_ILP32:
499       obstack_ptr_grow (&argv_obstack, "-m32");
500       break;
501     default:
502       gcc_unreachable ();
503     }
504 
505   for (int ix = 1; ix != argc; ix++)
506     {
507       if (!strcmp (argv[ix], "-o") && ix + 1 != argc)
508 	outname = argv[++ix];
509       else
510 	obstack_ptr_grow (&argv_obstack, argv[ix]);
511     }
512 
513   ptx_cfile_name = make_temp_file (".c");
514 
515   out = fopen (ptx_cfile_name, "w");
516   if (!out)
517     fatal_error (input_location, "cannot open '%s'", ptx_cfile_name);
518 
519   /* PR libgomp/65099: Currently, we only support offloading in 64-bit
520      configurations.  PR target/67822: OpenMP offloading to nvptx fails.  */
521   if (offload_abi == OFFLOAD_ABI_LP64 && !fopenmp)
522     {
523       ptx_name = make_temp_file (".mkoffload");
524       obstack_ptr_grow (&argv_obstack, "-o");
525       obstack_ptr_grow (&argv_obstack, ptx_name);
526       obstack_ptr_grow (&argv_obstack, NULL);
527       const char **new_argv = XOBFINISH (&argv_obstack, const char **);
528 
529       char *execpath = getenv ("GCC_EXEC_PREFIX");
530       char *cpath = getenv ("COMPILER_PATH");
531       char *lpath = getenv ("LIBRARY_PATH");
532       unsetenv ("GCC_EXEC_PREFIX");
533       unsetenv ("COMPILER_PATH");
534       unsetenv ("LIBRARY_PATH");
535 
536       fork_execute (new_argv[0], CONST_CAST (char **, new_argv), true);
537       obstack_free (&argv_obstack, NULL);
538 
539       xputenv (concat ("GCC_EXEC_PREFIX=", execpath, NULL));
540       xputenv (concat ("COMPILER_PATH=", cpath, NULL));
541       xputenv (concat ("LIBRARY_PATH=", lpath, NULL));
542 
543       in = fopen (ptx_name, "r");
544       if (!in)
545 	fatal_error (input_location, "cannot open intermediate ptx file");
546 
547       process (in, out);
548     }
549 
550   fclose (out);
551 
552   compile_native (ptx_cfile_name, outname, collect_gcc);
553 
554   return 0;
555 }
556