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