1 /* Offload image generation tool for PTX.
3 Copyright (C) 2014-2018 Free Software Foundation, Inc.
5 Contributed by Nathan Sidwell <nathan@codesourcery.com> and
6 Bernd Schmidt <bernds@codesourcery.com>.
8 This file is part of GCC.
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.
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.
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/>. */
24 /* Munges PTX assembly into a C source file defining the PTX code as a
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. */
30 #define IN_TARGET_CODE 1
34 #include "coretypes.h"
36 #include "diagnostic.h"
39 #include "collect-utils.h"
40 #include "gomp-constants.h"
42 const char tool_name
[] = "nvptx mkoffload";
44 #define COMMENT_PREFIX "#"
52 static id_map
*func_ids
, **funcs_tail
= &func_ids
;
53 static id_map
*var_ids
, **vars_tail
= &var_ids
;
55 /* Files to unlink. */
56 static const char *ptx_name
;
57 static const char *ptx_cfile_name
;
59 enum offload_abi offload_abi
= OFFLOAD_ABI_UNSET
;
61 /* Delete tempfiles. */
64 tool_cleanup (bool from_signal ATTRIBUTE_UNUSED
)
67 maybe_unlink (ptx_cfile_name
);
69 maybe_unlink (ptx_name
);
73 mkoffload_cleanup (void)
78 /* Unlink FILE unless requested otherwise. */
81 maybe_unlink (const char *file
)
85 if (unlink_if_ordinary (file
)
87 fatal_error (input_location
, "deleting file %s: %m", file
);
90 fprintf (stderr
, "[Leaving %s]\n", file
);
93 /* Add or change the value of an environment variable, outputting the
94 change to standard error if in verbose mode. */
96 xputenv (const char *string
)
99 fprintf (stderr
, "%s\n", string
);
100 putenv (CONST_CAST (char *, string
));
105 record_id (const char *p1
, id_map
***where
)
107 const char *end
= strchr (p1
, '\n');
109 fatal_error (input_location
, "malformed ptx file");
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';
117 id_map
**tail
= *where
;
122 /* Read the whole input file. It will be NUL terminated (but
123 remember, there could be a NUL in the file itself. */
126 read_file (FILE *stream
, size_t *plen
)
128 size_t alloc
= 16384;
132 if (!fseek (stream
, 0, SEEK_END
))
134 /* Get the file size. */
135 long s
= ftell (stream
);
138 fseek (stream
, 0, SEEK_SET
);
140 buffer
= XNEWVEC (char, alloc
);
144 size_t n
= fread (buffer
+ base
, 1, alloc
- base
- 1, stream
);
149 if (base
+ 1 == alloc
)
152 buffer
= XRESIZEVEC (char, buffer
, alloc
);
160 /* Parse STR, saving found tokens into PVALUES and return their number.
161 Tokens are assumed to be delimited by ':'. */
163 parse_env_var (const char *str
, char ***pvalues
)
165 const char *curval
, *nextval
;
169 curval
= strchr (str
, ':');
173 curval
= strchr (curval
+ 1, ':');
176 values
= (char **) xmalloc (num
* sizeof (char *));
178 nextval
= strchr (curval
, ':');
180 nextval
= strchr (curval
, '\0');
182 for (i
= 0; i
< num
; i
++)
184 int l
= nextval
- curval
;
185 values
[i
] = (char *) xmalloc (l
+ 1);
186 memcpy (values
[i
], curval
, l
);
188 curval
= nextval
+ 1;
189 nextval
= strchr (curval
, ':');
191 nextval
= strchr (curval
, '\0');
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. */
201 free_array_of_ptrs (void **ptr
, unsigned n
)
206 for (i
= 0; i
< n
; i
++)
216 /* Check whether NAME can be accessed in MODE. This is like access,
217 except that it never considers directories to be executable. */
219 access_check (const char *name
, int mode
)
225 if (stat (name
, &st
) < 0 || S_ISDIR (st
.st_mode
))
229 return access (name
, mode
);
233 process (FILE *in
, FILE *out
)
236 const char *input
= read_file (in
, &len
);
239 unsigned obj_count
= 0;
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
;)
248 fprintf (out
, "static const char ptx_code_%u[] =\n\t\"", obj_count
++);
249 while ((c
= input
[i
++]))
256 fprintf (out
, "\\n\"\n\t\"");
257 /* Look for mappings on subsequent lines. */
258 while (strncmp (input
+ i
, "//:", 3) == 0)
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
);
268 /* Skip to next line. */
269 while (input
[i
++] != '\n')
282 fprintf (out
, "\";\n\n");
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"
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");
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");
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");
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"
320 " sizeof (var_mappings) / sizeof (var_mappings[0]),\n"
322 " sizeof (func_mappings) / sizeof (func_mappings[0])\n"
325 fprintf (out
, "#ifdef __cplusplus\n"
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");
334 fprintf (out
, "#ifdef __cplusplus\n"
338 fprintf (out
, "extern const void *const __OFFLOAD_TABLE__[];\n\n");
340 fprintf (out
, "static __attribute__((constructor)) void init (void)\n"
342 " GOMP_offload_register_ver (%#x, __OFFLOAD_TABLE__,"
343 " %d/*NVIDIA_PTX*/, &target_data);\n"
345 GOMP_VERSION_PACK (GOMP_VERSION
, GOMP_VERSION_NVIDIA_PTX
),
346 GOMP_DEVICE_NVIDIA_PTX
);
348 fprintf (out
, "static __attribute__((destructor)) void fini (void)\n"
350 " GOMP_offload_unregister_ver (%#x, __OFFLOAD_TABLE__,"
351 " %d/*NVIDIA_PTX*/, &target_data);\n"
353 GOMP_VERSION_PACK (GOMP_VERSION
, GOMP_VERSION_NVIDIA_PTX
),
354 GOMP_DEVICE_NVIDIA_PTX
);
358 compile_native (const char *infile
, const char *outfile
, const char *compiler
)
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");
365 struct obstack argv_obstack
;
366 obstack_init (&argv_obstack
);
367 obstack_ptr_grow (&argv_obstack
, compiler
);
369 obstack_ptr_grow (&argv_obstack
, "-save-temps");
371 obstack_ptr_grow (&argv_obstack
, "-v");
374 case OFFLOAD_ABI_LP64
:
375 obstack_ptr_grow (&argv_obstack
, "-m64");
377 case OFFLOAD_ABI_ILP32
:
378 obstack_ptr_grow (&argv_obstack
, "-m32");
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
);
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
);
395 main (int argc
, char **argv
)
399 const char *outname
= 0;
401 progname
= "mkoffload";
402 diagnostic_initialize (global_dc
, 0);
404 if (atexit (mkoffload_cleanup
) != 0)
405 fatal_error (input_location
, "atexit failed");
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
));
413 size_t len
= (strlen (gcc_path
) + 1
414 + strlen (GCC_INSTALL_NAME
)
416 char *driver
= XALLOCAVEC (char, len
);
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. */
424 if (gcc_path
!= NULL
)
425 driver_used
= sprintf (driver
, "%s/", gcc_path
);
426 sprintf (driver
+ driver_used
, "%s", GCC_INSTALL_NAME
);
429 if (gcc_path
== NULL
)
431 else if (access_check (driver
, X_OK
) == 0)
435 /* Don't use alloca pointer with XRESIZEVEC. */
437 /* Look in all COMPILER_PATHs for GCC_INSTALL_NAME. */
440 n_paths
= parse_env_var (getenv ("COMPILER_PATH"), &paths
);
441 for (unsigned i
= 0; i
< n_paths
; i
++)
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)
452 free_array_of_ptrs ((void **) paths
, n_paths
);
456 fatal_error (input_location
,
457 "offload compiler %s not found (consider using '-B')",
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
);
464 /* Scan the argument vector. */
465 bool fopenmp
= false;
466 bool fopenacc
= false;
467 for (int i
= 1; i
< argc
; i
++)
469 #define STR "-foffload-abi="
470 if (strncmp (argv
[i
], STR
, strlen (STR
)) == 0)
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
;
477 fatal_error (input_location
,
478 "unrecognizable argument of option " STR
);
481 else if (strcmp (argv
[i
], "-fopenmp") == 0)
483 else if (strcmp (argv
[i
], "-fopenacc") == 0)
485 else if (strcmp (argv
[i
], "-save-temps") == 0)
487 else if (strcmp (argv
[i
], "-v") == 0)
490 if (!(fopenacc
^ fopenmp
))
491 fatal_error (input_location
, "either -fopenacc or -fopenmp must be set");
493 struct obstack argv_obstack
;
494 obstack_init (&argv_obstack
);
495 obstack_ptr_grow (&argv_obstack
, driver
);
497 obstack_ptr_grow (&argv_obstack
, "-save-temps");
499 obstack_ptr_grow (&argv_obstack
, "-v");
500 obstack_ptr_grow (&argv_obstack
, "-xlto");
503 case OFFLOAD_ABI_LP64
:
504 obstack_ptr_grow (&argv_obstack
, "-m64");
506 case OFFLOAD_ABI_ILP32
:
507 obstack_ptr_grow (&argv_obstack
, "-m32");
513 obstack_ptr_grow (&argv_obstack
, "-mgomp");
515 for (int ix
= 1; ix
!= argc
; ix
++)
517 if (!strcmp (argv
[ix
], "-o") && ix
+ 1 != argc
)
518 outname
= argv
[++ix
];
520 obstack_ptr_grow (&argv_obstack
, argv
[ix
]);
523 ptx_cfile_name
= make_temp_file (".c");
525 out
= fopen (ptx_cfile_name
, "w");
527 fatal_error (input_location
, "cannot open '%s'", ptx_cfile_name
);
529 /* PR libgomp/65099: Currently, we only support offloading in 64-bit
531 if (offload_abi
== OFFLOAD_ABI_LP64
)
533 ptx_name
= make_temp_file (".mkoffload");
534 obstack_ptr_grow (&argv_obstack
, "-o");
535 obstack_ptr_grow (&argv_obstack
, ptx_name
);
536 obstack_ptr_grow (&argv_obstack
, NULL
);
537 const char **new_argv
= XOBFINISH (&argv_obstack
, const char **);
539 char *execpath
= getenv ("GCC_EXEC_PREFIX");
540 char *cpath
= getenv ("COMPILER_PATH");
541 char *lpath
= getenv ("LIBRARY_PATH");
542 unsetenv ("GCC_EXEC_PREFIX");
543 unsetenv ("COMPILER_PATH");
544 unsetenv ("LIBRARY_PATH");
546 fork_execute (new_argv
[0], CONST_CAST (char **, new_argv
), true);
547 obstack_free (&argv_obstack
, NULL
);
549 xputenv (concat ("GCC_EXEC_PREFIX=", execpath
, NULL
));
550 xputenv (concat ("COMPILER_PATH=", cpath
, NULL
));
551 xputenv (concat ("LIBRARY_PATH=", lpath
, NULL
));
553 in
= fopen (ptx_name
, "r");
555 fatal_error (input_location
, "cannot open intermediate ptx file");
562 compile_native (ptx_cfile_name
, outname
, collect_gcc
);