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