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
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. */
32 #include "coretypes.h"
36 #include "diagnostic-core.h"
37 #include "collect-utils.h"
39 const char tool_name
[] = "nvptx mkoffload";
41 #define COMMENT_PREFIX "#"
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 */
57 unsigned short kind
: 12;
58 unsigned short space
: 1; /* preceded by space */
59 unsigned short end
: 1; /* succeeded by end of line */
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 */
76 V_pred
= 6, /* predicate */
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 */
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
*);
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. */
133 maybe_unlink (const char *file
)
137 if (unlink_if_ordinary (file
)
139 fatal_error (input_location
, "deleting file %s: %m", file
);
142 fprintf (stderr
, "[Leaving %s]\n", file
);
150 /* Add or change the value of an environment variable, outputting the
151 change to standard error if in verbose mode. */
153 xputenv (const char *string
)
156 fprintf (stderr
, "%s\n", string
);
157 putenv (CONST_CAST (char *, string
));
162 record_id (const char *p1
, id_map
***where
)
164 const char *end
= strchr (p1
, '\n');
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';
174 id_map
**tail
= *where
;
179 /* Read the whole input file. It will be NUL terminated (but
180 remember, there could be a NUL in the file itself. */
183 read_file (FILE *stream
)
185 size_t alloc
= 16384;
189 if (!fseek (stream
, 0, SEEK_END
))
191 /* Get the file size. */
192 long s
= ftell (stream
);
195 fseek (stream
, 0, SEEK_SET
);
197 buffer
= XNEWVEC (char, alloc
);
201 size_t n
= fread (buffer
+ base
, 1, alloc
- base
- 1, stream
);
206 if (base
+ 1 == alloc
)
209 buffer
= XRESIZEVEC (char, buffer
, alloc
);
216 /* Read a token, advancing ptr.
217 If we read a comment, append it to the comments block. */
220 tokenize (const char *ptr
)
222 unsigned alloc
= 1000;
224 Token
*toks
= XNEWVEC (Token
, alloc
);
239 switch (kind
= *ptr
++)
259 /* line comment. Do not include trailing \n */
266 else if (*ptr
== '*')
282 if (ptr
[0] == '*' && ptr
[1] == '/')
305 else if (*ptr
++ == '\\')
310 if (*ptr
< '0' || *ptr
> '9')
322 case '$': /* local labels. */
323 case '%': /* register names, pseudoes etc */
330 kind
= K_symbol
; /* possible symbol name */
334 if (*ptr
>= 'A' && *ptr
<= 'Z')
336 if (*ptr
>= 'a' && *ptr
<= 'z')
338 if (*ptr
>= '0' && *ptr
<= '9')
340 if (*ptr
== '_' || *ptr
== '$')
342 if (*ptr
== '.' && kind
!= K_dotted
)
343 /* Idents starting with a dot, cannot have internal dots. */
345 if ((*ptr
== '+' || *ptr
== '-')
347 && (ptr
[-1] == 'e' || ptr
[-1] == 'E'
348 || ptr
[-1] == 'p' || ptr
[-1] == 'P'))
364 toks
= XRESIZEVEC (Token
, toks
, alloc
);
366 Token
*tok
= toks
+ num
;
372 tok
->len
= ptr
- base
- in_comment
;
374 not_comment
= kind
!= K_comment
;
384 /* Write an encoded token. */
387 write_token (FILE *out
, Token
const *tok
)
396 const char *c
= tok
->ptr
+ 1;
397 size_t len
= tok
->len
- 2;
402 const char *bs
= (const char *)memchr (c
, '\\', len
);
403 size_t l
= bs
? bs
- c
: len
;
405 fprintf (out
, "%.*s", (int)l
, c
);
419 /* All other tokens shouldn't have anything magic in them */
420 fprintf (out
, "%.*s", tok
->len
, tok
->ptr
);
428 write_tokens (FILE *out
, Token
const *toks
, unsigned len
, int spc
)
431 for (; len
--; toks
++)
432 write_token (out
, toks
);
439 alloc_stmt (unsigned vis
, Token
*tokens
, Token
*end
, Token
const *sym
)
441 static unsigned alloc
= 0;
442 static Stmt
*heap
= 0;
447 heap
= XNEWVEC (Stmt
, alloc
);
456 stmt
->tokens
= tokens
;
457 stmt
->len
= end
- tokens
;
458 stmt
->sym
= sym
? sym
- tokens
: ~0;
464 rev_stmts (Stmt
*stmt
)
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
);
492 write_stmts (FILE *out
, const Stmt
*stmts
)
494 for (; stmts
; stmts
= stmts
->next
)
495 write_stmt (out
, stmts
);
499 parse_insn (Token
*tok
)
513 while (tok
->kind
== K_comment
)
515 stmt
= alloc_comment (start
, tok
);
516 append_stmt (&fns
, stmt
);
528 if (tok
[-1].ptr
[0] != '$')
536 if (tok
->kind
== '!')
538 if (tok
->kind
== K_symbol
)
545 for (; tok
->kind
!= ';'; tok
++)
547 if (tok
->kind
== ',')
549 else if (tok
->kind
== K_symbol
)
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
);
572 /* comma separated list of tokens */
575 parse_list_nosemi (Token
*tok
)
582 while ((++tok
)->kind
== ',');
585 Stmt
*stmt
= alloc_stmt (V_dot
, start
, tok
, 0);
586 append_stmt (&decls
, stmt
);
591 #define is_keyword(T,S) \
592 (sizeof (S) == (T)->len && !memcmp ((T)->ptr + 1, (S), (T)->len - 1))
595 parse_init (Token
*tok
)
600 Token
const *sym
= 0;
603 if (tok
->kind
== K_comment
)
605 while (tok
->kind
== K_comment
)
607 stmt
= alloc_comment (start
, tok
);
608 append_stmt (&vars
, stmt
);
612 if (tok
->kind
== '{')
614 for (; tok
->kind
!= ',' && tok
->kind
!= ';'; tok
++)
615 if (tok
->kind
== K_symbol
)
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
);
635 parse_file (Token
*tok
)
639 if (tok
->kind
== K_comment
)
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
);
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"))
662 append_stmt (&decls
, comment
);
663 tok
= parse_list_nosemi (tok
);
668 const Token
*def
= 0;
669 unsigned is_decl
= 0;
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"))
679 else if (is_keyword (tok
, "func")
680 || is_keyword (tok
, "entry"))
682 else if (is_keyword (tok
, "visible"))
684 else if (is_keyword (tok
, "extern"))
686 else if (is_keyword (tok
, "weak"))
688 if (tok
->kind
== '(')
693 else if (tok
->kind
== ')' && tok
[1].kind
!= ';')
696 if (tok
->kind
== K_symbol
)
704 append_stmt (&fns
, comment
);
706 else if (tok
->kind
== '{'
707 || tok
->kind
== K_comment
)
710 Stmt
*stmt
= alloc_stmt (vis
, start
, tok
, def
);
713 append_stmt (&fns
, comment
);
714 stmt
->vis
|= V_prefix_comment
;
716 append_stmt (&fns
, stmt
);
717 tok
= parse_insn (tok
);
721 int assign
= tok
->kind
== '=';
724 if ((vis
& V_mask
) == V_var
&& !is_decl
)
727 Stmt
*stmt
= alloc_stmt (vis
, start
, tok
, def
);
730 append_stmt (&vars
, comment
);
731 stmt
->vis
|= V_prefix_comment
;
733 append_stmt (&vars
, stmt
);
735 tok
= parse_init (tok
);
740 Stmt
*stmt
= alloc_stmt (vis
, start
, tok
, 0);
743 append_stmt (&decls
, comment
);
744 stmt
->vis
|= V_prefix_comment
;
746 append_stmt (&decls
, stmt
);
753 /* Something strange. Ignore it. */
755 append_stmt (&fns
, comment
);
757 while (tok
->kind
&& !tok
->end
)
764 process (FILE *in
, FILE *out
)
766 const char *input
= read_file (in
);
767 Token
*tok
= tokenize (input
);
770 tok
= parse_file (tok
);
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");
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
)
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
)
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. */
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
];
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");
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");
898 fatal_error (input_location
, "cannot open '%s'", ptx_cfile_name
);
903 compile_native (ptx_cfile_name
, outname
, collect_gcc
);
905 utils_cleanup (false);