]>
git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/config/nvptx/mkoffload.c
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.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
);
760 while (tok
->kind
&& !tok
->end
);
765 /* Parse STR, saving found tokens into PVALUES and return their number.
766 Tokens are assumed to be delimited by ':'. */
768 parse_env_var (const char *str
, char ***pvalues
)
770 const char *curval
, *nextval
;
774 curval
= strchr (str
, ':');
778 curval
= strchr (curval
+ 1, ':');
781 values
= (char **) xmalloc (num
* sizeof (char *));
783 nextval
= strchr (curval
, ':');
785 nextval
= strchr (curval
, '\0');
787 for (i
= 0; i
< num
; i
++)
789 int l
= nextval
- curval
;
790 values
[i
] = (char *) xmalloc (l
+ 1);
791 memcpy (values
[i
], curval
, l
);
793 curval
= nextval
+ 1;
794 nextval
= strchr (curval
, ':');
796 nextval
= strchr (curval
, '\0');
802 /* Auxiliary function that frees elements of PTR and PTR itself.
803 N is number of elements to be freed. If PTR is NULL, nothing is freed.
804 If an element is NULL, subsequent elements are not freed. */
806 free_array_of_ptrs (void **ptr
, unsigned n
)
811 for (i
= 0; i
< n
; i
++)
821 /* Check whether NAME can be accessed in MODE. This is like access,
822 except that it never considers directories to be executable. */
824 access_check (const char *name
, int mode
)
830 if (stat (name
, &st
) < 0 || S_ISDIR (st
.st_mode
))
834 return access (name
, mode
);
838 process (FILE *in
, FILE *out
)
840 const char *input
= read_file (in
);
841 Token
*tok
= tokenize (input
);
842 unsigned int nvars
= 0, nfuncs
= 0;
845 tok
= parse_file (tok
);
848 fprintf (out
, "static const char ptx_code[] = \n");
849 write_stmts (out
, rev_stmts (decls
));
850 write_stmts (out
, rev_stmts (vars
));
851 write_stmts (out
, rev_stmts (fns
));
852 fprintf (out
, ";\n\n");
853 fprintf (out
, "static const char *var_mappings[] = {\n");
854 for (id_map
*id
= var_ids
; id
; id
= id
->next
, nvars
++)
855 fprintf (out
, "\t\"%s\"%s\n", id
->ptx_name
, id
->next
? "," : "");
856 fprintf (out
, "};\n\n");
857 fprintf (out
, "static const char *func_mappings[] = {\n");
858 for (id_map
*id
= func_ids
; id
; id
= id
->next
, nfuncs
++)
859 fprintf (out
, "\t\"%s\"%s\n", id
->ptx_name
, id
->next
? "," : "");
860 fprintf (out
, "};\n\n");
862 fprintf (out
, "static const void *target_data[] = {\n");
863 fprintf (out
, " ptx_code, (void*) %u, var_mappings, (void*) %u, "
864 "func_mappings\n", nvars
, nfuncs
);
865 fprintf (out
, "};\n\n");
867 fprintf (out
, "extern void GOMP_offload_register (const void *, int, void *);\n");
869 fprintf (out
, "extern void *__OFFLOAD_TABLE__[];\n\n");
870 fprintf (out
, "static __attribute__((constructor)) void init (void)\n{\n");
871 fprintf (out
, " GOMP_offload_register (__OFFLOAD_TABLE__, %d,\n",
872 GOMP_DEVICE_NVIDIA_PTX
);
873 fprintf (out
, " &target_data);\n");
874 fprintf (out
, "};\n");
878 compile_native (const char *infile
, const char *outfile
, const char *compiler
)
880 const char *collect_gcc_options
= getenv ("COLLECT_GCC_OPTIONS");
881 if (!collect_gcc_options
)
882 fatal_error (input_location
,
883 "environment variable COLLECT_GCC_OPTIONS must be set");
885 struct obstack argv_obstack
;
886 obstack_init (&argv_obstack
);
887 obstack_ptr_grow (&argv_obstack
, compiler
);
888 obstack_ptr_grow (&argv_obstack
, infile
);
889 obstack_ptr_grow (&argv_obstack
, "-c");
890 obstack_ptr_grow (&argv_obstack
, "-o");
891 obstack_ptr_grow (&argv_obstack
, outfile
);
892 obstack_ptr_grow (&argv_obstack
, NULL
);
894 const char **new_argv
= XOBFINISH (&argv_obstack
, const char **);
895 fork_execute (new_argv
[0], CONST_CAST (char **, new_argv
), true);
896 obstack_free (&argv_obstack
, NULL
);
900 main (int argc
, char **argv
)
904 const char *outname
= 0;
906 progname
= "mkoffload";
907 diagnostic_initialize (global_dc
, 0);
909 char *collect_gcc
= getenv ("COLLECT_GCC");
910 if (collect_gcc
== NULL
)
911 fatal_error (input_location
, "COLLECT_GCC must be set.");
912 const char *gcc_path
= dirname (ASTRDUP (collect_gcc
));
913 const char *gcc_exec
= basename (ASTRDUP (collect_gcc
));
915 size_t len
= (strlen (gcc_path
) + 1
916 + strlen (GCC_INSTALL_NAME
)
918 char *driver
= XALLOCAVEC (char, len
);
920 if (strcmp (gcc_exec
, collect_gcc
) == 0)
921 /* collect_gcc has no path, so it was found in PATH. Make sure we also
922 find accel-gcc in PATH. */
926 if (gcc_path
!= NULL
)
927 driver_used
= sprintf (driver
, "%s/", gcc_path
);
928 sprintf (driver
+ driver_used
, "%s", GCC_INSTALL_NAME
);
931 if (gcc_path
== NULL
)
933 else if (access_check (driver
, X_OK
) == 0)
937 /* Don't use alloca pointer with XRESIZEVEC. */
939 /* Look in all COMPILER_PATHs for GCC_INSTALL_NAME. */
942 n_paths
= parse_env_var (getenv ("COMPILER_PATH"), &paths
);
943 for (unsigned i
= 0; i
< n_paths
; i
++)
945 len
= strlen (paths
[i
]) + 1 + strlen (GCC_INSTALL_NAME
) + 1;
946 driver
= XRESIZEVEC (char, driver
, len
);
947 sprintf (driver
, "%s/%s", paths
[i
], GCC_INSTALL_NAME
);
948 if (access_check (driver
, X_OK
) == 0)
954 free_array_of_ptrs ((void **) paths
, n_paths
);
958 fatal_error (input_location
,
959 "offload compiler %s not found", GCC_INSTALL_NAME
);
961 /* We may be called with all the arguments stored in some file and
962 passed with @file. Expand them into argv before processing. */
963 expandargv (&argc
, &argv
);
965 struct obstack argv_obstack
;
966 obstack_init (&argv_obstack
);
967 obstack_ptr_grow (&argv_obstack
, driver
);
968 obstack_ptr_grow (&argv_obstack
, "-xlto");
969 obstack_ptr_grow (&argv_obstack
, "-m64");
970 obstack_ptr_grow (&argv_obstack
, "-S");
972 for (int ix
= 1; ix
!= argc
; ix
++)
974 if (!strcmp (argv
[ix
], "-o") && ix
+ 1 != argc
)
975 outname
= argv
[++ix
];
977 obstack_ptr_grow (&argv_obstack
, argv
[ix
]);
980 ptx_name
= make_temp_file (".mkoffload");
981 obstack_ptr_grow (&argv_obstack
, "-o");
982 obstack_ptr_grow (&argv_obstack
, ptx_name
);
983 obstack_ptr_grow (&argv_obstack
, NULL
);
984 const char **new_argv
= XOBFINISH (&argv_obstack
, const char **);
986 char *execpath
= getenv ("GCC_EXEC_PREFIX");
987 char *cpath
= getenv ("COMPILER_PATH");
988 char *lpath
= getenv ("LIBRARY_PATH");
989 unsetenv ("GCC_EXEC_PREFIX");
990 unsetenv ("COMPILER_PATH");
991 unsetenv ("LIBRARY_PATH");
993 fork_execute (new_argv
[0], CONST_CAST (char **, new_argv
), true);
994 obstack_free (&argv_obstack
, NULL
);
996 xputenv (concat ("GCC_EXEC_PREFIX=", execpath
, NULL
));
997 xputenv (concat ("COMPILER_PATH=", cpath
, NULL
));
998 xputenv (concat ("LIBRARY_PATH=", lpath
, NULL
));
1000 in
= fopen (ptx_name
, "r");
1002 fatal_error (input_location
, "cannot open intermediate ptx file");
1004 ptx_cfile_name
= make_temp_file (".c");
1006 out
= fopen (ptx_cfile_name
, "w");
1008 fatal_error (input_location
, "cannot open '%s'", ptx_cfile_name
);
1013 compile_native (ptx_cfile_name
, outname
, collect_gcc
);
1015 utils_cleanup (false);