nvptx mkoffload: __OPENMP_TARGET__ -> __OFFLOAD_TABLE__.
[official-gcc.git] / gcc / config / nvptx / mkoffload.c
blob2287316c33d2c27b628db4bb2454f76a6e49cabe
1 /* Offload image generation tool for PTX.
3 Copyright (C) 2014-2015 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
25 string.
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 #include "config.h"
31 #include "system.h"
32 #include "coretypes.h"
33 #include "intl.h"
34 #include <libgen.h>
35 #include "obstack.h"
36 #include "diagnostic-core.h"
37 #include "collect-utils.h"
38 #include "gomp-constants.h"
40 const char tool_name[] = "nvptx mkoffload";
42 #define COMMENT_PREFIX "#"
44 typedef enum Kind
46 /* 0-ff used for single char tokens */
47 K_symbol = 0x100, /* a symbol */
48 K_label, /* a label defn (i.e. symbol:) */
49 K_ident, /* other ident */
50 K_dotted, /* dotted identifier */
51 K_number,
52 K_string,
53 K_comment
54 } Kind;
56 typedef struct Token
58 unsigned short kind : 12;
59 unsigned short space : 1; /* preceded by space */
60 unsigned short end : 1; /* succeeded by end of line */
61 /* Length of token */
62 unsigned short len;
64 /* Token itself */
65 char const *ptr;
66 } Token;
68 /* statement info */
69 typedef enum Vis
71 V_dot = 0, /* random pseudo */
72 V_var = 1, /* var decl/defn */
73 V_func = 2, /* func decl/defn */
74 V_insn = 3, /* random insn */
75 V_label = 4, /* label defn */
76 V_comment = 5,
77 V_pred = 6, /* predicate */
78 V_mask = 0x7,
79 V_global = 0x08, /* globalize */
80 V_weak = 0x10, /* weakly globalize */
81 V_no_eol = 0x20, /* no end of line */
82 V_prefix_comment = 0x40 /* prefixed comment */
83 } Vis;
85 typedef struct Stmt
87 struct Stmt *next;
88 Token *tokens;
89 unsigned char vis;
90 unsigned len : 12;
91 unsigned sym : 12;
92 } Stmt;
94 struct id_map
96 id_map *next;
97 char *ptx_name;
100 static const char *read_file (FILE *);
101 static Token *tokenize (const char *);
103 static void write_token (FILE *, const Token *);
104 static void write_tokens (FILE *, const Token *, unsigned, int);
106 static Stmt *alloc_stmt (unsigned, Token *, Token *, const Token *);
107 #define alloc_comment(S,E) alloc_stmt (V_comment, S, E, 0)
108 #define append_stmt(V, S) ((S)->next = *(V), *(V) = (S))
109 static Stmt *rev_stmts (Stmt *);
110 static void write_stmt (FILE *, const Stmt *);
111 static void write_stmts (FILE *, const Stmt *);
113 static Token *parse_insn (Token *);
114 static Token *parse_list_nosemi (Token *);
115 static Token *parse_init (Token *);
116 static Token *parse_file (Token *);
118 static Stmt *decls;
119 static Stmt *vars;
120 static Stmt *fns;
122 static id_map *func_ids, **funcs_tail = &func_ids;
123 static id_map *var_ids, **vars_tail = &var_ids;
125 /* Files to unlink. */
126 static const char *ptx_name;
127 static const char *ptx_cfile_name;
129 /* Delete tempfiles. */
131 /* Unlink a temporary file unless requested otherwise. */
133 void
134 maybe_unlink (const char *file)
136 if (! debug)
138 if (unlink_if_ordinary (file)
139 && errno != ENOENT)
140 fatal_error (input_location, "deleting file %s: %m", file);
142 else
143 fprintf (stderr, "[Leaving %s]\n", file);
146 void
147 tool_cleanup (bool)
151 /* Add or change the value of an environment variable, outputting the
152 change to standard error if in verbose mode. */
153 static void
154 xputenv (const char *string)
156 if (verbose)
157 fprintf (stderr, "%s\n", string);
158 putenv (CONST_CAST (char *, string));
162 static void
163 record_id (const char *p1, id_map ***where)
165 const char *end = strchr (p1, '\n');
166 if (!end)
167 fatal_error (input_location, "malformed ptx file");
169 id_map *v = XNEW (id_map);
170 size_t len = end - p1;
171 v->ptx_name = XNEWVEC (char, len + 1);
172 memcpy (v->ptx_name, p1, len);
173 v->ptx_name[len] = '\0';
174 v->next = NULL;
175 id_map **tail = *where;
176 *tail = v;
177 *where = &v->next;
180 /* Read the whole input file. It will be NUL terminated (but
181 remember, there could be a NUL in the file itself. */
183 static const char *
184 read_file (FILE *stream)
186 size_t alloc = 16384;
187 size_t base = 0;
188 char *buffer;
190 if (!fseek (stream, 0, SEEK_END))
192 /* Get the file size. */
193 long s = ftell (stream);
194 if (s >= 0)
195 alloc = s + 100;
196 fseek (stream, 0, SEEK_SET);
198 buffer = XNEWVEC (char, alloc);
200 for (;;)
202 size_t n = fread (buffer + base, 1, alloc - base - 1, stream);
204 if (!n)
205 break;
206 base += n;
207 if (base + 1 == alloc)
209 alloc *= 2;
210 buffer = XRESIZEVEC (char, buffer, alloc);
213 buffer[base] = 0;
214 return buffer;
217 /* Read a token, advancing ptr.
218 If we read a comment, append it to the comments block. */
220 static Token *
221 tokenize (const char *ptr)
223 unsigned alloc = 1000;
224 unsigned num = 0;
225 Token *toks = XNEWVEC (Token, alloc);
226 int in_comment = 0;
227 int not_comment = 0;
229 for (;; num++)
231 const char *base;
232 unsigned kind;
233 int ws = 0;
234 int eol = 0;
236 again:
237 base = ptr;
238 if (in_comment)
239 goto block_comment;
240 switch (kind = *ptr++)
242 default:
243 break;
245 case '\n':
246 eol = 1;
247 /* Fall through */
248 case ' ':
249 case '\t':
250 case '\r':
251 case '\v':
252 /* White space */
253 ws = not_comment;
254 goto again;
256 case '/':
258 if (*ptr == '/')
260 /* line comment. Do not include trailing \n */
261 base += 2;
262 for (; *ptr; ptr++)
263 if (*ptr == '\n')
264 break;
265 kind = K_comment;
267 else if (*ptr == '*')
269 /* block comment */
270 base += 2;
271 ptr++;
273 block_comment:
274 eol = in_comment;
275 in_comment = 1;
276 for (; *ptr; ptr++)
278 if (*ptr == '\n')
280 ptr++;
281 break;
283 if (ptr[0] == '*' && ptr[1] == '/')
285 in_comment = 2;
286 ptr += 2;
287 break;
290 kind = K_comment;
292 else
293 break;
295 break;
297 case '"':
298 /* quoted string */
299 kind = K_string;
300 while (*ptr)
301 if (*ptr == '"')
303 ptr++;
304 break;
306 else if (*ptr++ == '\\')
307 ptr++;
308 break;
310 case '.':
311 if (*ptr < '0' || *ptr > '9')
313 kind = K_dotted;
314 ws = not_comment;
315 goto ident;
317 /* FALLTHROUGH */
318 case '0'...'9':
319 kind = K_number;
320 goto ident;
321 break;
323 case '$': /* local labels. */
324 case '%': /* register names, pseudoes etc */
325 kind = K_ident;
326 goto ident;
328 case 'a'...'z':
329 case 'A'...'Z':
330 case '_':
331 kind = K_symbol; /* possible symbol name */
332 ident:
333 for (; *ptr; ptr++)
335 if (*ptr >= 'A' && *ptr <= 'Z')
336 continue;
337 if (*ptr >= 'a' && *ptr <= 'z')
338 continue;
339 if (*ptr >= '0' && *ptr <= '9')
340 continue;
341 if (*ptr == '_' || *ptr == '$')
342 continue;
343 if (*ptr == '.' && kind != K_dotted)
344 /* Idents starting with a dot, cannot have internal dots. */
345 continue;
346 if ((*ptr == '+' || *ptr == '-')
347 && kind == K_number
348 && (ptr[-1] == 'e' || ptr[-1] == 'E'
349 || ptr[-1] == 'p' || ptr[-1] == 'P'))
350 /* exponent */
351 continue;
352 break;
354 if (*ptr == ':')
356 ptr++;
357 kind = K_label;
359 break;
362 if (alloc == num)
364 alloc *= 2;
365 toks = XRESIZEVEC (Token, toks, alloc);
367 Token *tok = toks + num;
369 tok->kind = kind;
370 tok->space = ws;
371 tok->end = 0;
372 tok->ptr = base;
373 tok->len = ptr - base - in_comment;
374 in_comment &= 1;
375 not_comment = kind != K_comment;
376 if (eol && num)
377 tok[-1].end = 1;
378 if (!kind)
379 break;
382 return toks;
385 /* Write an encoded token. */
387 static void
388 write_token (FILE *out, Token const *tok)
390 if (tok->space)
391 fputc (' ', out);
393 switch (tok->kind)
395 case K_string:
397 const char *c = tok->ptr + 1;
398 size_t len = tok->len - 2;
400 fputs ("\\\"", out);
401 while (len)
403 const char *bs = (const char *)memchr (c, '\\', len);
404 size_t l = bs ? bs - c : len;
406 fprintf (out, "%.*s", (int)l, c);
407 len -= l;
408 c += l;
409 if (bs)
411 fputs ("\\\\", out);
412 len--, c++;
415 fputs ("\\\"", out);
417 break;
419 default:
420 /* All other tokens shouldn't have anything magic in them */
421 fprintf (out, "%.*s", tok->len, tok->ptr);
422 break;
424 if (tok->end)
425 fputs ("\\n", out);
428 static void
429 write_tokens (FILE *out, Token const *toks, unsigned len, int spc)
431 fputs ("\t\"", out);
432 for (; len--; toks++)
433 write_token (out, toks);
434 if (spc)
435 fputs (" ", out);
436 fputs ("\"", out);
439 static Stmt *
440 alloc_stmt (unsigned vis, Token *tokens, Token *end, Token const *sym)
442 static unsigned alloc = 0;
443 static Stmt *heap = 0;
445 if (!alloc)
447 alloc = 1000;
448 heap = XNEWVEC (Stmt, alloc);
451 Stmt *stmt = heap++;
452 alloc--;
454 tokens->space = 0;
455 stmt->next = 0;
456 stmt->vis = vis;
457 stmt->tokens = tokens;
458 stmt->len = end - tokens;
459 stmt->sym = sym ? sym - tokens : ~0;
461 return stmt;
464 static Stmt *
465 rev_stmts (Stmt *stmt)
467 Stmt *prev = 0;
468 Stmt *next;
470 while (stmt)
472 next = stmt->next;
473 stmt->next = prev;
474 prev = stmt;
475 stmt = next;
478 return prev;
481 static void
482 write_stmt (FILE *out, const Stmt *stmt)
484 if ((stmt->vis & V_mask) != V_comment)
486 write_tokens (out, stmt->tokens, stmt->len,
487 (stmt->vis & V_mask) == V_pred);
488 fputs (stmt->vis & V_no_eol ? "\t" : "\n", out);
492 static void
493 write_stmts (FILE *out, const Stmt *stmts)
495 for (; stmts; stmts = stmts->next)
496 write_stmt (out, stmts);
499 static Token *
500 parse_insn (Token *tok)
502 unsigned depth = 0;
506 Stmt *stmt;
507 Token *sym = 0;
508 unsigned s = V_insn;
509 Token *start = tok;
511 switch (tok++->kind)
513 case K_comment:
514 while (tok->kind == K_comment)
515 tok++;
516 stmt = alloc_comment (start, tok);
517 append_stmt (&fns, stmt);
518 continue;
520 case '{':
521 depth++;
522 break;
524 case '}':
525 depth--;
526 break;
528 case K_label:
529 if (tok[-1].ptr[0] != '$')
530 sym = tok - 1;
531 tok[-1].end = 1;
532 s = V_label;
533 break;
535 case '@':
536 tok->space = 0;
537 if (tok->kind == '!')
538 tok++;
539 if (tok->kind == K_symbol)
540 sym = tok;
541 tok++;
542 s = V_pred;
543 break;
545 default:
546 for (; tok->kind != ';'; tok++)
548 if (tok->kind == ',')
549 tok[1].space = 0;
550 else if (tok->kind == K_symbol)
551 sym = tok;
553 tok++->end = 1;
554 break;
557 stmt = alloc_stmt (s, start, tok, sym);
558 append_stmt (&fns, stmt);
560 if (!tok[-1].end && tok[0].kind == K_comment)
562 stmt->vis |= V_no_eol;
563 stmt = alloc_comment (tok, tok + 1);
564 append_stmt (&fns, stmt);
565 tok++;
568 while (depth);
570 return tok;
573 /* comma separated list of tokens */
575 static Token *
576 parse_list_nosemi (Token *tok)
578 Token *start = tok;
581 if (!(++tok)->kind)
582 break;
583 while ((++tok)->kind == ',');
585 tok[-1].end = 1;
586 Stmt *stmt = alloc_stmt (V_dot, start, tok, 0);
587 append_stmt (&decls, stmt);
589 return tok;
592 #define is_keyword(T,S) \
593 (sizeof (S) == (T)->len && !memcmp ((T)->ptr + 1, (S), (T)->len - 1))
595 static Token *
596 parse_init (Token *tok)
598 for (;;)
600 Token *start = tok;
601 Token const *sym = 0;
602 Stmt *stmt;
604 if (tok->kind == K_comment)
606 while (tok->kind == K_comment)
607 tok++;
608 stmt = alloc_comment (start, tok);
609 append_stmt (&vars, stmt);
610 start = tok;
613 if (tok->kind == '{')
614 tok[1].space = 0;
615 for (; tok->kind != ',' && tok->kind != ';'; tok++)
616 if (tok->kind == K_symbol)
617 sym = tok;
618 tok[1].space = 0;
619 int end = tok++->kind == ';';
620 stmt = alloc_stmt (V_insn, start, tok, sym);
621 append_stmt (&vars, stmt);
622 if (!tok[-1].end && tok->kind == K_comment)
624 stmt->vis |= V_no_eol;
625 stmt = alloc_comment (tok, tok + 1);
626 append_stmt (&vars, stmt);
627 tok++;
629 if (end)
630 break;
632 return tok;
635 static Token *
636 parse_file (Token *tok)
638 Stmt *comment = 0;
640 if (tok->kind == K_comment)
642 Token *start = tok;
644 while (tok->kind == K_comment)
646 if (strncmp (tok->ptr, ":VAR_MAP ", 9) == 0)
647 record_id (tok->ptr + 9, &vars_tail);
648 if (strncmp (tok->ptr, ":FUNC_MAP ", 10) == 0)
649 record_id (tok->ptr + 10, &funcs_tail);
650 tok++;
652 comment = alloc_comment (start, tok);
653 comment->vis |= V_prefix_comment;
656 if (tok->kind == K_dotted)
658 if (is_keyword (tok, "version")
659 || is_keyword (tok, "target")
660 || is_keyword (tok, "address_size"))
662 if (comment)
663 append_stmt (&decls, comment);
664 tok = parse_list_nosemi (tok);
666 else
668 unsigned vis = 0;
669 const Token *def = 0;
670 unsigned is_decl = 0;
671 Token *start;
673 for (start = tok;
674 tok->kind && tok->kind != '=' && tok->kind != K_comment
675 && tok->kind != '{' && tok->kind != ';'; tok++)
677 if (is_keyword (tok, "global")
678 || is_keyword (tok, "const"))
679 vis |= V_var;
680 else if (is_keyword (tok, "func")
681 || is_keyword (tok, "entry"))
682 vis |= V_func;
683 else if (is_keyword (tok, "visible"))
684 vis |= V_global;
685 else if (is_keyword (tok, "extern"))
686 is_decl = 1;
687 else if (is_keyword (tok, "weak"))
688 vis |= V_weak;
689 if (tok->kind == '(')
691 tok[1].space = 0;
692 tok[0].space = 1;
694 else if (tok->kind == ')' && tok[1].kind != ';')
695 tok[1].space = 1;
697 if (tok->kind == K_symbol)
698 def = tok;
701 if (!tok->kind)
703 /* end of file */
704 if (comment)
705 append_stmt (&fns, comment);
707 else if (tok->kind == '{'
708 || tok->kind == K_comment)
710 /* function defn */
711 Stmt *stmt = alloc_stmt (vis, start, tok, def);
712 if (comment)
714 append_stmt (&fns, comment);
715 stmt->vis |= V_prefix_comment;
717 append_stmt (&fns, stmt);
718 tok = parse_insn (tok);
720 else
722 int assign = tok->kind == '=';
724 tok++->end = 1;
725 if ((vis & V_mask) == V_var && !is_decl)
727 /* variable */
728 Stmt *stmt = alloc_stmt (vis, start, tok, def);
729 if (comment)
731 append_stmt (&vars, comment);
732 stmt->vis |= V_prefix_comment;
734 append_stmt (&vars, stmt);
735 if (assign)
736 tok = parse_init (tok);
738 else
740 /* declaration */
741 Stmt *stmt = alloc_stmt (vis, start, tok, 0);
742 if (comment)
744 append_stmt (&decls, comment);
745 stmt->vis |= V_prefix_comment;
747 append_stmt (&decls, stmt);
752 else
754 /* Something strange. Ignore it. */
755 if (comment)
756 append_stmt (&fns, comment);
758 while (tok->kind && !tok->end)
759 tok++;
761 return tok;
764 static void
765 process (FILE *in, FILE *out)
767 const char *input = read_file (in);
768 Token *tok = tokenize (input);
771 tok = parse_file (tok);
772 while (tok->kind);
774 fprintf (out, "static const char ptx_code[] = \n");
775 write_stmts (out, rev_stmts (decls));
776 write_stmts (out, rev_stmts (vars));
777 write_stmts (out, rev_stmts (fns));
778 fprintf (out, ";\n\n");
779 fprintf (out, "static const char *var_mappings[] = {\n");
780 for (id_map *id = var_ids; id; id = id->next)
781 fprintf (out, "\t\"%s\"%s\n", id->ptx_name, id->next ? "," : "");
782 fprintf (out, "};\n\n");
783 fprintf (out, "static const char *func_mappings[] = {\n");
784 for (id_map *id = func_ids; id; id = id->next)
785 fprintf (out, "\t\"%s\"%s\n", id->ptx_name, id->next ? "," : "");
786 fprintf (out, "};\n\n");
788 fprintf (out, "static const void *target_data[] = {\n");
789 fprintf (out, " ptx_code, var_mappings, func_mappings\n");
790 fprintf (out, "};\n\n");
792 fprintf (out, "extern void GOMP_offload_register (const void *, int, void *);\n");
794 fprintf (out, "extern void *__OFFLOAD_TABLE__[];\n\n");
795 fprintf (out, "static __attribute__((constructor)) void init (void)\n{\n");
796 fprintf (out, " GOMP_offload_register (__OFFLOAD_TABLE__, %d,\n",
797 GOMP_DEVICE_NVIDIA_PTX);
798 fprintf (out, " &target_data);\n");
799 fprintf (out, "};\n");
802 static void
803 compile_native (const char *infile, const char *outfile, const char *compiler)
805 const char *collect_gcc_options = getenv ("COLLECT_GCC_OPTIONS");
806 if (!collect_gcc_options)
807 fatal_error (input_location,
808 "environment variable COLLECT_GCC_OPTIONS must be set");
810 struct obstack argv_obstack;
811 obstack_init (&argv_obstack);
812 obstack_ptr_grow (&argv_obstack, compiler);
813 obstack_ptr_grow (&argv_obstack, infile);
814 obstack_ptr_grow (&argv_obstack, "-c");
815 obstack_ptr_grow (&argv_obstack, "-o");
816 obstack_ptr_grow (&argv_obstack, outfile);
817 obstack_ptr_grow (&argv_obstack, NULL);
819 const char **new_argv = XOBFINISH (&argv_obstack, const char **);
820 fork_execute (new_argv[0], CONST_CAST (char **, new_argv), true);
821 obstack_free (&argv_obstack, NULL);
825 main (int argc, char **argv)
827 FILE *in = stdin;
828 FILE *out = stdout;
829 const char *outname = 0;
831 char *collect_gcc = getenv ("COLLECT_GCC");
832 if (collect_gcc == NULL)
833 fatal_error (input_location, "COLLECT_GCC must be set.");
834 const char *gcc_path = dirname (ASTRDUP (collect_gcc));
835 const char *gcc_exec = basename (ASTRDUP (collect_gcc));
837 size_t len = (strlen (gcc_path) + 1
838 + strlen (GCC_INSTALL_NAME)
839 + 1);
840 char *driver = XALLOCAVEC (char, len);
842 if (strcmp (gcc_exec, collect_gcc) == 0)
843 /* collect_gcc has no path, so it was found in PATH. Make sure we also
844 find accel-gcc in PATH. */
845 gcc_path = NULL;
847 int driver_used = 0;
848 if (gcc_path != NULL)
849 driver_used = sprintf (driver, "%s/", gcc_path);
850 sprintf (driver + driver_used, "%s", GCC_INSTALL_NAME);
852 /* We may be called with all the arguments stored in some file and
853 passed with @file. Expand them into argv before processing. */
854 expandargv (&argc, &argv);
856 struct obstack argv_obstack;
857 obstack_init (&argv_obstack);
858 obstack_ptr_grow (&argv_obstack, driver);
859 obstack_ptr_grow (&argv_obstack, "-xlto");
860 obstack_ptr_grow (&argv_obstack, "-m64");
861 obstack_ptr_grow (&argv_obstack, "-S");
863 for (int ix = 1; ix != argc; ix++)
865 if (!strcmp (argv[ix], "-o") && ix + 1 != argc)
866 outname = argv[++ix];
867 else
868 obstack_ptr_grow (&argv_obstack, argv[ix]);
871 ptx_name = make_temp_file (".mkoffload");
872 obstack_ptr_grow (&argv_obstack, "-o");
873 obstack_ptr_grow (&argv_obstack, ptx_name);
874 obstack_ptr_grow (&argv_obstack, NULL);
875 const char **new_argv = XOBFINISH (&argv_obstack, const char **);
877 char *execpath = getenv ("GCC_EXEC_PREFIX");
878 char *cpath = getenv ("COMPILER_PATH");
879 char *lpath = getenv ("LIBRARY_PATH");
880 unsetenv ("GCC_EXEC_PREFIX");
881 unsetenv ("COMPILER_PATH");
882 unsetenv ("LIBRARY_PATH");
884 fork_execute (new_argv[0], CONST_CAST (char **, new_argv), true);
885 obstack_free (&argv_obstack, NULL);
887 xputenv (concat ("GCC_EXEC_PREFIX=", execpath, NULL));
888 xputenv (concat ("COMPILER_PATH=", cpath, NULL));
889 xputenv (concat ("LIBRARY_PATH=", lpath, NULL));
891 in = fopen (ptx_name, "r");
892 if (!in)
893 fatal_error (input_location, "cannot open intermediate ptx file");
895 ptx_cfile_name = make_temp_file (".c");
897 out = fopen (ptx_cfile_name, "w");
898 if (!out)
899 fatal_error (input_location, "cannot open '%s'", ptx_cfile_name);
901 process (in, out);
902 fclose (out);
904 compile_native (ptx_cfile_name, outname, collect_gcc);
906 utils_cleanup (false);
908 return 0;