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"
38 #include "gomp-constants.h"
40 const char tool_name
[] = "nvptx mkoffload";
42 #define COMMENT_PREFIX "#"
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 */
58 unsigned short kind
: 12;
59 unsigned short space
: 1; /* preceded by space */
60 unsigned short end
: 1; /* succeeded by end of line */
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 */
77 V_pred
= 6, /* predicate */
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 */
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
*);
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. */
134 maybe_unlink (const char *file
)
138 if (unlink_if_ordinary (file
)
140 fatal_error (input_location
, "deleting file %s: %m", file
);
143 fprintf (stderr
, "[Leaving %s]\n", file
);
151 /* Add or change the value of an environment variable, outputting the
152 change to standard error if in verbose mode. */
154 xputenv (const char *string
)
157 fprintf (stderr
, "%s\n", string
);
158 putenv (CONST_CAST (char *, string
));
163 record_id (const char *p1
, id_map
***where
)
165 const char *end
= strchr (p1
, '\n');
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';
175 id_map
**tail
= *where
;
180 /* Read the whole input file. It will be NUL terminated (but
181 remember, there could be a NUL in the file itself. */
184 read_file (FILE *stream
)
186 size_t alloc
= 16384;
190 if (!fseek (stream
, 0, SEEK_END
))
192 /* Get the file size. */
193 long s
= ftell (stream
);
196 fseek (stream
, 0, SEEK_SET
);
198 buffer
= XNEWVEC (char, alloc
);
202 size_t n
= fread (buffer
+ base
, 1, alloc
- base
- 1, stream
);
207 if (base
+ 1 == alloc
)
210 buffer
= XRESIZEVEC (char, buffer
, alloc
);
217 /* Read a token, advancing ptr.
218 If we read a comment, append it to the comments block. */
221 tokenize (const char *ptr
)
223 unsigned alloc
= 1000;
225 Token
*toks
= XNEWVEC (Token
, alloc
);
240 switch (kind
= *ptr
++)
260 /* line comment. Do not include trailing \n */
267 else if (*ptr
== '*')
283 if (ptr
[0] == '*' && ptr
[1] == '/')
306 else if (*ptr
++ == '\\')
311 if (*ptr
< '0' || *ptr
> '9')
323 case '$': /* local labels. */
324 case '%': /* register names, pseudoes etc */
331 kind
= K_symbol
; /* possible symbol name */
335 if (*ptr
>= 'A' && *ptr
<= 'Z')
337 if (*ptr
>= 'a' && *ptr
<= 'z')
339 if (*ptr
>= '0' && *ptr
<= '9')
341 if (*ptr
== '_' || *ptr
== '$')
343 if (*ptr
== '.' && kind
!= K_dotted
)
344 /* Idents starting with a dot, cannot have internal dots. */
346 if ((*ptr
== '+' || *ptr
== '-')
348 && (ptr
[-1] == 'e' || ptr
[-1] == 'E'
349 || ptr
[-1] == 'p' || ptr
[-1] == 'P'))
365 toks
= XRESIZEVEC (Token
, toks
, alloc
);
367 Token
*tok
= toks
+ num
;
373 tok
->len
= ptr
- base
- in_comment
;
375 not_comment
= kind
!= K_comment
;
385 /* Write an encoded token. */
388 write_token (FILE *out
, Token
const *tok
)
397 const char *c
= tok
->ptr
+ 1;
398 size_t len
= tok
->len
- 2;
403 const char *bs
= (const char *)memchr (c
, '\\', len
);
404 size_t l
= bs
? bs
- c
: len
;
406 fprintf (out
, "%.*s", (int)l
, c
);
420 /* All other tokens shouldn't have anything magic in them */
421 fprintf (out
, "%.*s", tok
->len
, tok
->ptr
);
429 write_tokens (FILE *out
, Token
const *toks
, unsigned len
, int spc
)
432 for (; len
--; toks
++)
433 write_token (out
, toks
);
440 alloc_stmt (unsigned vis
, Token
*tokens
, Token
*end
, Token
const *sym
)
442 static unsigned alloc
= 0;
443 static Stmt
*heap
= 0;
448 heap
= XNEWVEC (Stmt
, alloc
);
457 stmt
->tokens
= tokens
;
458 stmt
->len
= end
- tokens
;
459 stmt
->sym
= sym
? sym
- tokens
: ~0;
465 rev_stmts (Stmt
*stmt
)
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
);
493 write_stmts (FILE *out
, const Stmt
*stmts
)
495 for (; stmts
; stmts
= stmts
->next
)
496 write_stmt (out
, stmts
);
500 parse_insn (Token
*tok
)
514 while (tok
->kind
== K_comment
)
516 stmt
= alloc_comment (start
, tok
);
517 append_stmt (&fns
, stmt
);
529 if (tok
[-1].ptr
[0] != '$')
537 if (tok
->kind
== '!')
539 if (tok
->kind
== K_symbol
)
546 for (; tok
->kind
!= ';'; tok
++)
548 if (tok
->kind
== ',')
550 else if (tok
->kind
== K_symbol
)
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
);
573 /* comma separated list of tokens */
576 parse_list_nosemi (Token
*tok
)
583 while ((++tok
)->kind
== ',');
586 Stmt
*stmt
= alloc_stmt (V_dot
, start
, tok
, 0);
587 append_stmt (&decls
, stmt
);
592 #define is_keyword(T,S) \
593 (sizeof (S) == (T)->len && !memcmp ((T)->ptr + 1, (S), (T)->len - 1))
596 parse_init (Token
*tok
)
601 Token
const *sym
= 0;
604 if (tok
->kind
== K_comment
)
606 while (tok
->kind
== K_comment
)
608 stmt
= alloc_comment (start
, tok
);
609 append_stmt (&vars
, stmt
);
613 if (tok
->kind
== '{')
615 for (; tok
->kind
!= ',' && tok
->kind
!= ';'; tok
++)
616 if (tok
->kind
== K_symbol
)
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
);
636 parse_file (Token
*tok
)
640 if (tok
->kind
== K_comment
)
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
);
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"))
663 append_stmt (&decls
, comment
);
664 tok
= parse_list_nosemi (tok
);
669 const Token
*def
= 0;
670 unsigned is_decl
= 0;
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"))
680 else if (is_keyword (tok
, "func")
681 || is_keyword (tok
, "entry"))
683 else if (is_keyword (tok
, "visible"))
685 else if (is_keyword (tok
, "extern"))
687 else if (is_keyword (tok
, "weak"))
689 if (tok
->kind
== '(')
694 else if (tok
->kind
== ')' && tok
[1].kind
!= ';')
697 if (tok
->kind
== K_symbol
)
705 append_stmt (&fns
, comment
);
707 else if (tok
->kind
== '{'
708 || tok
->kind
== K_comment
)
711 Stmt
*stmt
= alloc_stmt (vis
, start
, tok
, def
);
714 append_stmt (&fns
, comment
);
715 stmt
->vis
|= V_prefix_comment
;
717 append_stmt (&fns
, stmt
);
718 tok
= parse_insn (tok
);
722 int assign
= tok
->kind
== '=';
725 if ((vis
& V_mask
) == V_var
&& !is_decl
)
728 Stmt
*stmt
= alloc_stmt (vis
, start
, tok
, def
);
731 append_stmt (&vars
, comment
);
732 stmt
->vis
|= V_prefix_comment
;
734 append_stmt (&vars
, stmt
);
736 tok
= parse_init (tok
);
741 Stmt
*stmt
= alloc_stmt (vis
, start
, tok
, 0);
744 append_stmt (&decls
, comment
);
745 stmt
->vis
|= V_prefix_comment
;
747 append_stmt (&decls
, stmt
);
754 /* Something strange. Ignore it. */
756 append_stmt (&fns
, comment
);
758 while (tok
->kind
&& !tok
->end
)
765 process (FILE *in
, FILE *out
)
767 const char *input
= read_file (in
);
768 Token
*tok
= tokenize (input
);
771 tok
= parse_file (tok
);
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 *__OPENMP_TARGET__[];\n\n");
795 fprintf (out
, "static __attribute__((constructor)) void init (void)\n{\n");
796 fprintf (out
, " GOMP_offload_register (__OPENMP_TARGET__, %d,\n",
797 GOMP_DEVICE_NVIDIA_PTX
);
798 fprintf (out
, " &target_data);\n");
799 fprintf (out
, "};\n");
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
)
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
)
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. */
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
];
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");
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");
899 fatal_error (input_location
, "cannot open '%s'", ptx_cfile_name
);
904 compile_native (ptx_cfile_name
, outname
, collect_gcc
);
906 utils_cleanup (false);