Always pass explicit location to fatal_error.
[official-gcc.git] / gcc / config / nvptx / mkoffload.c
blob38ccdba5de612ffd51de4cf187b5f7c0e5ba12ac
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"
39 const char tool_name[] = "nvptx mkoffload";
41 #define COMMENT_PREFIX "#"
43 typedef enum Kind
45 /* 0-ff used for single char tokens */
46 K_symbol = 0x100, /* a symbol */
47 K_label, /* a label defn (i.e. symbol:) */
48 K_ident, /* other ident */
49 K_dotted, /* dotted identifier */
50 K_number,
51 K_string,
52 K_comment
53 } Kind;
55 typedef struct Token
57 unsigned short kind : 12;
58 unsigned short space : 1; /* preceded by space */
59 unsigned short end : 1; /* succeeded by end of line */
60 /* Length of token */
61 unsigned short len;
63 /* Token itself */
64 char const *ptr;
65 } Token;
67 /* statement info */
68 typedef enum Vis
70 V_dot = 0, /* random pseudo */
71 V_var = 1, /* var decl/defn */
72 V_func = 2, /* func decl/defn */
73 V_insn = 3, /* random insn */
74 V_label = 4, /* label defn */
75 V_comment = 5,
76 V_pred = 6, /* predicate */
77 V_mask = 0x7,
78 V_global = 0x08, /* globalize */
79 V_weak = 0x10, /* weakly globalize */
80 V_no_eol = 0x20, /* no end of line */
81 V_prefix_comment = 0x40 /* prefixed comment */
82 } Vis;
84 typedef struct Stmt
86 struct Stmt *next;
87 Token *tokens;
88 unsigned char vis;
89 unsigned len : 12;
90 unsigned sym : 12;
91 } Stmt;
93 struct id_map
95 id_map *next;
96 char *ptx_name;
99 static const char *read_file (FILE *);
100 static Token *tokenize (const char *);
102 static void write_token (FILE *, const Token *);
103 static void write_tokens (FILE *, const Token *, unsigned, int);
105 static Stmt *alloc_stmt (unsigned, Token *, Token *, const Token *);
106 #define alloc_comment(S,E) alloc_stmt (V_comment, S, E, 0)
107 #define append_stmt(V, S) ((S)->next = *(V), *(V) = (S))
108 static Stmt *rev_stmts (Stmt *);
109 static void write_stmt (FILE *, const Stmt *);
110 static void write_stmts (FILE *, const Stmt *);
112 static Token *parse_insn (Token *);
113 static Token *parse_list_nosemi (Token *);
114 static Token *parse_init (Token *);
115 static Token *parse_file (Token *);
117 static Stmt *decls;
118 static Stmt *vars;
119 static Stmt *fns;
121 static id_map *func_ids, **funcs_tail = &func_ids;
122 static id_map *var_ids, **vars_tail = &var_ids;
124 /* Files to unlink. */
125 static const char *ptx_name;
126 static const char *ptx_cfile_name;
128 /* Delete tempfiles. */
130 /* Unlink a temporary file unless requested otherwise. */
132 void
133 maybe_unlink (const char *file)
135 if (! debug)
137 if (unlink_if_ordinary (file)
138 && errno != ENOENT)
139 fatal_error (input_location, "deleting file %s: %m", file);
141 else
142 fprintf (stderr, "[Leaving %s]\n", file);
145 void
146 tool_cleanup (bool)
150 /* Add or change the value of an environment variable, outputting the
151 change to standard error if in verbose mode. */
152 static void
153 xputenv (const char *string)
155 if (verbose)
156 fprintf (stderr, "%s\n", string);
157 putenv (CONST_CAST (char *, string));
161 static void
162 record_id (const char *p1, id_map ***where)
164 const char *end = strchr (p1, '\n');
165 if (!end)
166 fatal_error (input_location, "malformed ptx file");
168 id_map *v = XNEW (id_map);
169 size_t len = end - p1;
170 v->ptx_name = XNEWVEC (char, len + 1);
171 memcpy (v->ptx_name, p1, len);
172 v->ptx_name[len] = '\0';
173 v->next = NULL;
174 id_map **tail = *where;
175 *tail = v;
176 *where = &v->next;
179 /* Read the whole input file. It will be NUL terminated (but
180 remember, there could be a NUL in the file itself. */
182 static const char *
183 read_file (FILE *stream)
185 size_t alloc = 16384;
186 size_t base = 0;
187 char *buffer;
189 if (!fseek (stream, 0, SEEK_END))
191 /* Get the file size. */
192 long s = ftell (stream);
193 if (s >= 0)
194 alloc = s + 100;
195 fseek (stream, 0, SEEK_SET);
197 buffer = XNEWVEC (char, alloc);
199 for (;;)
201 size_t n = fread (buffer + base, 1, alloc - base - 1, stream);
203 if (!n)
204 break;
205 base += n;
206 if (base + 1 == alloc)
208 alloc *= 2;
209 buffer = XRESIZEVEC (char, buffer, alloc);
212 buffer[base] = 0;
213 return buffer;
216 /* Read a token, advancing ptr.
217 If we read a comment, append it to the comments block. */
219 static Token *
220 tokenize (const char *ptr)
222 unsigned alloc = 1000;
223 unsigned num = 0;
224 Token *toks = XNEWVEC (Token, alloc);
225 int in_comment = 0;
226 int not_comment = 0;
228 for (;; num++)
230 const char *base;
231 unsigned kind;
232 int ws = 0;
233 int eol = 0;
235 again:
236 base = ptr;
237 if (in_comment)
238 goto block_comment;
239 switch (kind = *ptr++)
241 default:
242 break;
244 case '\n':
245 eol = 1;
246 /* Fall through */
247 case ' ':
248 case '\t':
249 case '\r':
250 case '\v':
251 /* White space */
252 ws = not_comment;
253 goto again;
255 case '/':
257 if (*ptr == '/')
259 /* line comment. Do not include trailing \n */
260 base += 2;
261 for (; *ptr; ptr++)
262 if (*ptr == '\n')
263 break;
264 kind = K_comment;
266 else if (*ptr == '*')
268 /* block comment */
269 base += 2;
270 ptr++;
272 block_comment:
273 eol = in_comment;
274 in_comment = 1;
275 for (; *ptr; ptr++)
277 if (*ptr == '\n')
279 ptr++;
280 break;
282 if (ptr[0] == '*' && ptr[1] == '/')
284 in_comment = 2;
285 ptr += 2;
286 break;
289 kind = K_comment;
291 else
292 break;
294 break;
296 case '"':
297 /* quoted string */
298 kind = K_string;
299 while (*ptr)
300 if (*ptr == '"')
302 ptr++;
303 break;
305 else if (*ptr++ == '\\')
306 ptr++;
307 break;
309 case '.':
310 if (*ptr < '0' || *ptr > '9')
312 kind = K_dotted;
313 ws = not_comment;
314 goto ident;
316 /* FALLTHROUGH */
317 case '0'...'9':
318 kind = K_number;
319 goto ident;
320 break;
322 case '$': /* local labels. */
323 case '%': /* register names, pseudoes etc */
324 kind = K_ident;
325 goto ident;
327 case 'a'...'z':
328 case 'A'...'Z':
329 case '_':
330 kind = K_symbol; /* possible symbol name */
331 ident:
332 for (; *ptr; ptr++)
334 if (*ptr >= 'A' && *ptr <= 'Z')
335 continue;
336 if (*ptr >= 'a' && *ptr <= 'z')
337 continue;
338 if (*ptr >= '0' && *ptr <= '9')
339 continue;
340 if (*ptr == '_' || *ptr == '$')
341 continue;
342 if (*ptr == '.' && kind != K_dotted)
343 /* Idents starting with a dot, cannot have internal dots. */
344 continue;
345 if ((*ptr == '+' || *ptr == '-')
346 && kind == K_number
347 && (ptr[-1] == 'e' || ptr[-1] == 'E'
348 || ptr[-1] == 'p' || ptr[-1] == 'P'))
349 /* exponent */
350 continue;
351 break;
353 if (*ptr == ':')
355 ptr++;
356 kind = K_label;
358 break;
361 if (alloc == num)
363 alloc *= 2;
364 toks = XRESIZEVEC (Token, toks, alloc);
366 Token *tok = toks + num;
368 tok->kind = kind;
369 tok->space = ws;
370 tok->end = 0;
371 tok->ptr = base;
372 tok->len = ptr - base - in_comment;
373 in_comment &= 1;
374 not_comment = kind != K_comment;
375 if (eol && num)
376 tok[-1].end = 1;
377 if (!kind)
378 break;
381 return toks;
384 /* Write an encoded token. */
386 static void
387 write_token (FILE *out, Token const *tok)
389 if (tok->space)
390 fputc (' ', out);
392 switch (tok->kind)
394 case K_string:
396 const char *c = tok->ptr + 1;
397 size_t len = tok->len - 2;
399 fputs ("\\\"", out);
400 while (len)
402 const char *bs = (const char *)memchr (c, '\\', len);
403 size_t l = bs ? bs - c : len;
405 fprintf (out, "%.*s", (int)l, c);
406 len -= l;
407 c += l;
408 if (bs)
410 fputs ("\\\\", out);
411 len--, c++;
414 fputs ("\\\"", out);
416 break;
418 default:
419 /* All other tokens shouldn't have anything magic in them */
420 fprintf (out, "%.*s", tok->len, tok->ptr);
421 break;
423 if (tok->end)
424 fputs ("\\n", out);
427 static void
428 write_tokens (FILE *out, Token const *toks, unsigned len, int spc)
430 fputs ("\t\"", out);
431 for (; len--; toks++)
432 write_token (out, toks);
433 if (spc)
434 fputs (" ", out);
435 fputs ("\"", out);
438 static Stmt *
439 alloc_stmt (unsigned vis, Token *tokens, Token *end, Token const *sym)
441 static unsigned alloc = 0;
442 static Stmt *heap = 0;
444 if (!alloc)
446 alloc = 1000;
447 heap = XNEWVEC (Stmt, alloc);
450 Stmt *stmt = heap++;
451 alloc--;
453 tokens->space = 0;
454 stmt->next = 0;
455 stmt->vis = vis;
456 stmt->tokens = tokens;
457 stmt->len = end - tokens;
458 stmt->sym = sym ? sym - tokens : ~0;
460 return stmt;
463 static Stmt *
464 rev_stmts (Stmt *stmt)
466 Stmt *prev = 0;
467 Stmt *next;
469 while (stmt)
471 next = stmt->next;
472 stmt->next = prev;
473 prev = stmt;
474 stmt = next;
477 return prev;
480 static void
481 write_stmt (FILE *out, const Stmt *stmt)
483 if ((stmt->vis & V_mask) != V_comment)
485 write_tokens (out, stmt->tokens, stmt->len,
486 (stmt->vis & V_mask) == V_pred);
487 fputs (stmt->vis & V_no_eol ? "\t" : "\n", out);
491 static void
492 write_stmts (FILE *out, const Stmt *stmts)
494 for (; stmts; stmts = stmts->next)
495 write_stmt (out, stmts);
498 static Token *
499 parse_insn (Token *tok)
501 unsigned depth = 0;
505 Stmt *stmt;
506 Token *sym = 0;
507 unsigned s = V_insn;
508 Token *start = tok;
510 switch (tok++->kind)
512 case K_comment:
513 while (tok->kind == K_comment)
514 tok++;
515 stmt = alloc_comment (start, tok);
516 append_stmt (&fns, stmt);
517 continue;
519 case '{':
520 depth++;
521 break;
523 case '}':
524 depth--;
525 break;
527 case K_label:
528 if (tok[-1].ptr[0] != '$')
529 sym = tok - 1;
530 tok[-1].end = 1;
531 s = V_label;
532 break;
534 case '@':
535 tok->space = 0;
536 if (tok->kind == '!')
537 tok++;
538 if (tok->kind == K_symbol)
539 sym = tok;
540 tok++;
541 s = V_pred;
542 break;
544 default:
545 for (; tok->kind != ';'; tok++)
547 if (tok->kind == ',')
548 tok[1].space = 0;
549 else if (tok->kind == K_symbol)
550 sym = tok;
552 tok++->end = 1;
553 break;
556 stmt = alloc_stmt (s, start, tok, sym);
557 append_stmt (&fns, stmt);
559 if (!tok[-1].end && tok[0].kind == K_comment)
561 stmt->vis |= V_no_eol;
562 stmt = alloc_comment (tok, tok + 1);
563 append_stmt (&fns, stmt);
564 tok++;
567 while (depth);
569 return tok;
572 /* comma separated list of tokens */
574 static Token *
575 parse_list_nosemi (Token *tok)
577 Token *start = tok;
580 if (!(++tok)->kind)
581 break;
582 while ((++tok)->kind == ',');
584 tok[-1].end = 1;
585 Stmt *stmt = alloc_stmt (V_dot, start, tok, 0);
586 append_stmt (&decls, stmt);
588 return tok;
591 #define is_keyword(T,S) \
592 (sizeof (S) == (T)->len && !memcmp ((T)->ptr + 1, (S), (T)->len - 1))
594 static Token *
595 parse_init (Token *tok)
597 for (;;)
599 Token *start = tok;
600 Token const *sym = 0;
601 Stmt *stmt;
603 if (tok->kind == K_comment)
605 while (tok->kind == K_comment)
606 tok++;
607 stmt = alloc_comment (start, tok);
608 append_stmt (&vars, stmt);
609 start = tok;
612 if (tok->kind == '{')
613 tok[1].space = 0;
614 for (; tok->kind != ',' && tok->kind != ';'; tok++)
615 if (tok->kind == K_symbol)
616 sym = tok;
617 tok[1].space = 0;
618 int end = tok++->kind == ';';
619 stmt = alloc_stmt (V_insn, start, tok, sym);
620 append_stmt (&vars, stmt);
621 if (!tok[-1].end && tok->kind == K_comment)
623 stmt->vis |= V_no_eol;
624 stmt = alloc_comment (tok, tok + 1);
625 append_stmt (&vars, stmt);
626 tok++;
628 if (end)
629 break;
631 return tok;
634 static Token *
635 parse_file (Token *tok)
637 Stmt *comment = 0;
639 if (tok->kind == K_comment)
641 Token *start = tok;
643 while (tok->kind == K_comment)
645 if (strncmp (tok->ptr, ":VAR_MAP ", 9) == 0)
646 record_id (tok->ptr + 9, &vars_tail);
647 if (strncmp (tok->ptr, ":FUNC_MAP ", 10) == 0)
648 record_id (tok->ptr + 10, &funcs_tail);
649 tok++;
651 comment = alloc_comment (start, tok);
652 comment->vis |= V_prefix_comment;
655 if (tok->kind == K_dotted)
657 if (is_keyword (tok, "version")
658 || is_keyword (tok, "target")
659 || is_keyword (tok, "address_size"))
661 if (comment)
662 append_stmt (&decls, comment);
663 tok = parse_list_nosemi (tok);
665 else
667 unsigned vis = 0;
668 const Token *def = 0;
669 unsigned is_decl = 0;
670 Token *start;
672 for (start = tok;
673 tok->kind && tok->kind != '=' && tok->kind != K_comment
674 && tok->kind != '{' && tok->kind != ';'; tok++)
676 if (is_keyword (tok, "global")
677 || is_keyword (tok, "const"))
678 vis |= V_var;
679 else if (is_keyword (tok, "func")
680 || is_keyword (tok, "entry"))
681 vis |= V_func;
682 else if (is_keyword (tok, "visible"))
683 vis |= V_global;
684 else if (is_keyword (tok, "extern"))
685 is_decl = 1;
686 else if (is_keyword (tok, "weak"))
687 vis |= V_weak;
688 if (tok->kind == '(')
690 tok[1].space = 0;
691 tok[0].space = 1;
693 else if (tok->kind == ')' && tok[1].kind != ';')
694 tok[1].space = 1;
696 if (tok->kind == K_symbol)
697 def = tok;
700 if (!tok->kind)
702 /* end of file */
703 if (comment)
704 append_stmt (&fns, comment);
706 else if (tok->kind == '{'
707 || tok->kind == K_comment)
709 /* function defn */
710 Stmt *stmt = alloc_stmt (vis, start, tok, def);
711 if (comment)
713 append_stmt (&fns, comment);
714 stmt->vis |= V_prefix_comment;
716 append_stmt (&fns, stmt);
717 tok = parse_insn (tok);
719 else
721 int assign = tok->kind == '=';
723 tok++->end = 1;
724 if ((vis & V_mask) == V_var && !is_decl)
726 /* variable */
727 Stmt *stmt = alloc_stmt (vis, start, tok, def);
728 if (comment)
730 append_stmt (&vars, comment);
731 stmt->vis |= V_prefix_comment;
733 append_stmt (&vars, stmt);
734 if (assign)
735 tok = parse_init (tok);
737 else
739 /* declaration */
740 Stmt *stmt = alloc_stmt (vis, start, tok, 0);
741 if (comment)
743 append_stmt (&decls, comment);
744 stmt->vis |= V_prefix_comment;
746 append_stmt (&decls, stmt);
751 else
753 /* Something strange. Ignore it. */
754 if (comment)
755 append_stmt (&fns, comment);
757 while (tok->kind && !tok->end)
758 tok++;
760 return tok;
763 static void
764 process (FILE *in, FILE *out)
766 const char *input = read_file (in);
767 Token *tok = tokenize (input);
770 tok = parse_file (tok);
771 while (tok->kind);
773 fprintf (out, "static const char ptx_code[] = \n");
774 write_stmts (out, rev_stmts (decls));
775 write_stmts (out, rev_stmts (vars));
776 write_stmts (out, rev_stmts (fns));
777 fprintf (out, ";\n\n");
778 fprintf (out, "static const char *var_mappings[] = {\n");
779 for (id_map *id = var_ids; id; id = id->next)
780 fprintf (out, "\t\"%s\"%s\n", id->ptx_name, id->next ? "," : "");
781 fprintf (out, "};\n\n");
782 fprintf (out, "static const char *func_mappings[] = {\n");
783 for (id_map *id = func_ids; id; id = id->next)
784 fprintf (out, "\t\"%s\"%s\n", id->ptx_name, id->next ? "," : "");
785 fprintf (out, "};\n\n");
787 fprintf (out, "static const void *target_data[] = {\n");
788 fprintf (out, " ptx_code, var_mappings, func_mappings\n");
789 fprintf (out, "};\n\n");
791 fprintf (out, "extern void GOMP_offload_register (const void *, int, void *);\n");
793 fprintf (out, "extern void *__OPENMP_TARGET__[];\n\n");
794 fprintf (out, "#define PTX_ID 1\n");
795 fprintf (out, "static __attribute__((constructor)) void init (void)\n{\n");
796 fprintf (out, " GOMP_offload_register (__OPENMP_TARGET__, PTX_ID,\n");
797 fprintf (out, " &target_data);\n");
798 fprintf (out, "};\n");
801 static void
802 compile_native (const char *infile, const char *outfile, const char *compiler)
804 const char *collect_gcc_options = getenv ("COLLECT_GCC_OPTIONS");
805 if (!collect_gcc_options)
806 fatal_error (input_location,
807 "environment variable COLLECT_GCC_OPTIONS must be set");
809 struct obstack argv_obstack;
810 obstack_init (&argv_obstack);
811 obstack_ptr_grow (&argv_obstack, compiler);
812 obstack_ptr_grow (&argv_obstack, infile);
813 obstack_ptr_grow (&argv_obstack, "-c");
814 obstack_ptr_grow (&argv_obstack, "-o");
815 obstack_ptr_grow (&argv_obstack, outfile);
816 obstack_ptr_grow (&argv_obstack, NULL);
818 const char **new_argv = XOBFINISH (&argv_obstack, const char **);
819 fork_execute (new_argv[0], CONST_CAST (char **, new_argv), true);
820 obstack_free (&argv_obstack, NULL);
824 main (int argc, char **argv)
826 FILE *in = stdin;
827 FILE *out = stdout;
828 const char *outname = 0;
830 char *collect_gcc = getenv ("COLLECT_GCC");
831 if (collect_gcc == NULL)
832 fatal_error (input_location, "COLLECT_GCC must be set.");
833 const char *gcc_path = dirname (ASTRDUP (collect_gcc));
834 const char *gcc_exec = basename (ASTRDUP (collect_gcc));
836 size_t len = (strlen (gcc_path) + 1
837 + strlen (GCC_INSTALL_NAME)
838 + 1);
839 char *driver = XALLOCAVEC (char, len);
841 if (strcmp (gcc_exec, collect_gcc) == 0)
842 /* collect_gcc has no path, so it was found in PATH. Make sure we also
843 find accel-gcc in PATH. */
844 gcc_path = NULL;
846 int driver_used = 0;
847 if (gcc_path != NULL)
848 driver_used = sprintf (driver, "%s/", gcc_path);
849 sprintf (driver + driver_used, "%s", GCC_INSTALL_NAME);
851 /* We may be called with all the arguments stored in some file and
852 passed with @file. Expand them into argv before processing. */
853 expandargv (&argc, &argv);
855 struct obstack argv_obstack;
856 obstack_init (&argv_obstack);
857 obstack_ptr_grow (&argv_obstack, driver);
858 obstack_ptr_grow (&argv_obstack, "-xlto");
859 obstack_ptr_grow (&argv_obstack, "-m64");
860 obstack_ptr_grow (&argv_obstack, "-S");
862 for (int ix = 1; ix != argc; ix++)
864 if (!strcmp (argv[ix], "-o") && ix + 1 != argc)
865 outname = argv[++ix];
866 else
867 obstack_ptr_grow (&argv_obstack, argv[ix]);
870 ptx_name = make_temp_file (".mkoffload");
871 obstack_ptr_grow (&argv_obstack, "-o");
872 obstack_ptr_grow (&argv_obstack, ptx_name);
873 obstack_ptr_grow (&argv_obstack, NULL);
874 const char **new_argv = XOBFINISH (&argv_obstack, const char **);
876 char *execpath = getenv ("GCC_EXEC_PREFIX");
877 char *cpath = getenv ("COMPILER_PATH");
878 char *lpath = getenv ("LIBRARY_PATH");
879 unsetenv ("GCC_EXEC_PREFIX");
880 unsetenv ("COMPILER_PATH");
881 unsetenv ("LIBRARY_PATH");
883 fork_execute (new_argv[0], CONST_CAST (char **, new_argv), true);
884 obstack_free (&argv_obstack, NULL);
886 xputenv (concat ("GCC_EXEC_PREFIX=", execpath, NULL));
887 xputenv (concat ("COMPILER_PATH=", cpath, NULL));
888 xputenv (concat ("LIBRARY_PATH=", lpath, NULL));
890 in = fopen (ptx_name, "r");
891 if (!in)
892 fatal_error (input_location, "cannot open intermediate ptx file");
894 ptx_cfile_name = make_temp_file (".c");
896 out = fopen (ptx_cfile_name, "w");
897 if (!out)
898 fatal_error (input_location, "cannot open '%s'", ptx_cfile_name);
900 process (in, out);
901 fclose (out);
903 compile_native (ptx_cfile_name, outname, collect_gcc);
905 utils_cleanup (false);
907 return 0;