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 | |
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 |
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 |
71 | mkoffload_cleanup (void) |
72 | { |
73 | tool_cleanup (false); |
74 | } |
75 | |
76 | /* Unlink FILE unless requested otherwise. */ |
77 | |
78 | void |
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 |
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 |
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 * |
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 |
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 |
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 |
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 |
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 |
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 |
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 | 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.