Warning: That file was not part of the compilation database. It may have many parsing errors.

1/* Offload image generation tool for PTX.
2
3 Copyright (C) 2014-2017 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
40const char tool_name[] = "nvptx mkoffload";
41
42#define COMMENT_PREFIX "#"
43
44struct id_map
45{
46 id_map *next;
47 char *ptx_name;
48};
49
50static id_map *func_ids, **funcs_tail = &func_ids;
51static id_map *var_ids, **vars_tail = &var_ids;
52
53/* Files to unlink. */
54static const char *ptx_name;
55static const char *ptx_cfile_name;
56
57enum offload_abi offload_abi = OFFLOAD_ABI_UNSET;
58
59/* Delete tempfiles. */
60
61void
62tool_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
70static void
71mkoffload_cleanup (void)
72{
73 tool_cleanup (false);
74}
75
76/* Unlink FILE unless requested otherwise. */
77
78void
79maybe_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. */
93static void
94xputenv (const char *string)
95{
96 if (verbose)
97 fprintf (stderr, "%s\n", string);
98 putenv (CONST_CAST (char *, string));
99}
100
101
102static void
103record_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
123static const char *
124read_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 ':'. */
160static unsigned
161parse_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. */
198static void
199free_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. */
216static int
217access_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
230static void
231process (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
355static void
356compile_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
392int
393main (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 bool fopenacc = false;
464 for (int i = 1; i < argc; i++)
465 {
466#define STR "-foffload-abi="
467 if (strncmp (argv[i], STR, strlen (STR)) == 0)
468 {
469 if (strcmp (argv[i] + strlen (STR), "lp64") == 0)
470 offload_abi = OFFLOAD_ABI_LP64;
471 else if (strcmp (argv[i] + strlen (STR), "ilp32") == 0)
472 offload_abi = OFFLOAD_ABI_ILP32;
473 else
474 fatal_error (input_location,
475 "unrecognizable argument of option " STR);
476 }
477#undef STR
478 else if (strcmp (argv[i], "-fopenmp") == 0)
479 fopenmp = true;
480 else if (strcmp (argv[i], "-fopenacc") == 0)
481 fopenacc = true;
482 else if (strcmp (argv[i], "-save-temps") == 0)
483 save_temps = true;
484 else if (strcmp (argv[i], "-v") == 0)
485 verbose = true;
486 }
487 if (!(fopenacc ^ fopenmp))
488 fatal_error (input_location, "either -fopenacc or -fopenmp must be set");
489
490 struct obstack argv_obstack;
491 obstack_init (&argv_obstack);
492 obstack_ptr_grow (&argv_obstack, driver);
493 if (save_temps)
494 obstack_ptr_grow (&argv_obstack, "-save-temps");
495 if (verbose)
496 obstack_ptr_grow (&argv_obstack, "-v");
497 obstack_ptr_grow (&argv_obstack, "-xlto");
498 switch (offload_abi)
499 {
500 case OFFLOAD_ABI_LP64:
501 obstack_ptr_grow (&argv_obstack, "-m64");
502 break;
503 case OFFLOAD_ABI_ILP32:
504 obstack_ptr_grow (&argv_obstack, "-m32");
505 break;
506 default:
507 gcc_unreachable ();
508 }
509 if (fopenmp)
510 obstack_ptr_grow (&argv_obstack, "-mgomp");
511
512 for (int ix = 1; ix != argc; ix++)
513 {
514 if (!strcmp (argv[ix], "-o") && ix + 1 != argc)
515 outname = argv[++ix];
516 else
517 obstack_ptr_grow (&argv_obstack, argv[ix]);
518 }
519
520 ptx_cfile_name = make_temp_file (".c");
521
522 out = fopen (ptx_cfile_name, "w");
523 if (!out)
524 fatal_error (input_location, "cannot open '%s'", ptx_cfile_name);
525
526 /* PR libgomp/65099: Currently, we only support offloading in 64-bit
527 configurations. */
528 if (offload_abi == OFFLOAD_ABI_LP64)
529 {
530 ptx_name = make_temp_file (".mkoffload");
531 obstack_ptr_grow (&argv_obstack, "-o");
532 obstack_ptr_grow (&argv_obstack, ptx_name);
533 obstack_ptr_grow (&argv_obstack, NULL);
534 const char **new_argv = XOBFINISH (&argv_obstack, const char **);
535
536 char *execpath = getenv ("GCC_EXEC_PREFIX");
537 char *cpath = getenv ("COMPILER_PATH");
538 char *lpath = getenv ("LIBRARY_PATH");
539 unsetenv ("GCC_EXEC_PREFIX");
540 unsetenv ("COMPILER_PATH");
541 unsetenv ("LIBRARY_PATH");
542
543 fork_execute (new_argv[0], CONST_CAST (char **, new_argv), true);
544 obstack_free (&argv_obstack, NULL);
545
546 xputenv (concat ("GCC_EXEC_PREFIX=", execpath, NULL));
547 xputenv (concat ("COMPILER_PATH=", cpath, NULL));
548 xputenv (concat ("LIBRARY_PATH=", lpath, NULL));
549
550 in = fopen (ptx_name, "r");
551 if (!in)
552 fatal_error (input_location, "cannot open intermediate ptx file");
553
554 process (in, out);
555 }
556
557 fclose (out);
558
559 compile_native (ptx_cfile_name, outname, collect_gcc);
560
561 return 0;
562}
563

Warning: That file was not part of the compilation database. It may have many parsing errors.