]>
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 /* Shows if we should compile binaries for i386 instead of x86-64. */
130 bool target_ilp32
= false;
132 /* Delete tempfiles. */
134 /* Unlink a temporary file unless requested otherwise. */
137 maybe_unlink (const char *file
)
141 if (unlink_if_ordinary (file
)
143 fatal_error (input_location
, "deleting file %s: %m", file
);
146 fprintf (stderr
, "[Leaving %s]\n", file
);
154 /* Add or change the value of an environment variable, outputting the
155 change to standard error if in verbose mode. */
157 xputenv (const char *string
)
160 fprintf (stderr
, "%s\n", string
);
161 putenv (CONST_CAST (char *, string
));
166 record_id (const char *p1
, id_map
***where
)
168 const char *end
= strchr (p1
, '\n');
170 fatal_error (input_location
, "malformed ptx file");
172 id_map
*v
= XNEW (id_map
);
173 size_t len
= end
- p1
;
174 v
->ptx_name
= XNEWVEC (char, len
+ 1);
175 memcpy (v
->ptx_name
, p1
, len
);
176 v
->ptx_name
[len
] = '\0';
178 id_map
**tail
= *where
;
183 /* Read the whole input file. It will be NUL terminated (but
184 remember, there could be a NUL in the file itself. */
187 read_file (FILE *stream
)
189 size_t alloc
= 16384;
193 if (!fseek (stream
, 0, SEEK_END
))
195 /* Get the file size. */
196 long s
= ftell (stream
);
199 fseek (stream
, 0, SEEK_SET
);
201 buffer
= XNEWVEC (char, alloc
);
205 size_t n
= fread (buffer
+ base
, 1, alloc
- base
- 1, stream
);
210 if (base
+ 1 == alloc
)
213 buffer
= XRESIZEVEC (char, buffer
, alloc
);
220 /* Read a token, advancing ptr.
221 If we read a comment, append it to the comments block. */
224 tokenize (const char *ptr
)
226 unsigned alloc
= 1000;
228 Token
*toks
= XNEWVEC (Token
, alloc
);
243 switch (kind
= *ptr
++)
263 /* line comment. Do not include trailing \n */
270 else if (*ptr
== '*')
286 if (ptr
[0] == '*' && ptr
[1] == '/')
309 else if (*ptr
++ == '\\')
314 if (*ptr
< '0' || *ptr
> '9')
326 case '$': /* local labels. */
327 case '%': /* register names, pseudoes etc */
334 kind
= K_symbol
; /* possible symbol name */
338 if (*ptr
>= 'A' && *ptr
<= 'Z')
340 if (*ptr
>= 'a' && *ptr
<= 'z')
342 if (*ptr
>= '0' && *ptr
<= '9')
344 if (*ptr
== '_' || *ptr
== '$')
346 if (*ptr
== '.' && kind
!= K_dotted
)
347 /* Idents starting with a dot, cannot have internal dots. */
349 if ((*ptr
== '+' || *ptr
== '-')
351 && (ptr
[-1] == 'e' || ptr
[-1] == 'E'
352 || ptr
[-1] == 'p' || ptr
[-1] == 'P'))
368 toks
= XRESIZEVEC (Token
, toks
, alloc
);
370 Token
*tok
= toks
+ num
;
376 tok
->len
= ptr
- base
- in_comment
;
378 not_comment
= kind
!= K_comment
;
388 /* Write an encoded token. */
391 write_token (FILE *out
, Token
const *tok
)
400 const char *c
= tok
->ptr
+ 1;
401 size_t len
= tok
->len
- 2;
406 const char *bs
= (const char *)memchr (c
, '\\', len
);
407 size_t l
= bs
? bs
- c
: len
;
409 fprintf (out
, "%.*s", (int)l
, c
);
423 /* All other tokens shouldn't have anything magic in them */
424 fprintf (out
, "%.*s", tok
->len
, tok
->ptr
);
432 write_tokens (FILE *out
, Token
const *toks
, unsigned len
, int spc
)
435 for (; len
--; toks
++)
436 write_token (out
, toks
);
443 alloc_stmt (unsigned vis
, Token
*tokens
, Token
*end
, Token
const *sym
)
445 static unsigned alloc
= 0;
446 static Stmt
*heap
= 0;
451 heap
= XNEWVEC (Stmt
, alloc
);
460 stmt
->tokens
= tokens
;
461 stmt
->len
= end
- tokens
;
462 stmt
->sym
= sym
? sym
- tokens
: ~0;
468 rev_stmts (Stmt
*stmt
)
485 write_stmt (FILE *out
, const Stmt
*stmt
)
487 if ((stmt
->vis
& V_mask
) != V_comment
)
489 write_tokens (out
, stmt
->tokens
, stmt
->len
,
490 (stmt
->vis
& V_mask
) == V_pred
);
491 fputs (stmt
->vis
& V_no_eol
? "\t" : "\n", out
);
496 write_stmts (FILE *out
, const Stmt
*stmts
)
498 for (; stmts
; stmts
= stmts
->next
)
499 write_stmt (out
, stmts
);
503 parse_insn (Token
*tok
)
517 while (tok
->kind
== K_comment
)
519 stmt
= alloc_comment (start
, tok
);
520 append_stmt (&fns
, stmt
);
532 if (tok
[-1].ptr
[0] != '$')
540 if (tok
->kind
== '!')
542 if (tok
->kind
== K_symbol
)
549 for (; tok
->kind
!= ';'; tok
++)
551 if (tok
->kind
== ',')
553 else if (tok
->kind
== K_symbol
)
560 stmt
= alloc_stmt (s
, start
, tok
, sym
);
561 append_stmt (&fns
, stmt
);
563 if (!tok
[-1].end
&& tok
[0].kind
== K_comment
)
565 stmt
->vis
|= V_no_eol
;
566 stmt
= alloc_comment (tok
, tok
+ 1);
567 append_stmt (&fns
, stmt
);
576 /* comma separated list of tokens */
579 parse_list_nosemi (Token
*tok
)
586 while ((++tok
)->kind
== ',');
589 Stmt
*stmt
= alloc_stmt (V_dot
, start
, tok
, 0);
590 append_stmt (&decls
, stmt
);
595 #define is_keyword(T,S) \
596 (sizeof (S) == (T)->len && !memcmp ((T)->ptr + 1, (S), (T)->len - 1))
599 parse_init (Token
*tok
)
604 Token
const *sym
= 0;
607 if (tok
->kind
== K_comment
)
609 while (tok
->kind
== K_comment
)
611 stmt
= alloc_comment (start
, tok
);
612 append_stmt (&vars
, stmt
);
616 if (tok
->kind
== '{')
618 for (; tok
->kind
!= ',' && tok
->kind
!= ';'; tok
++)
619 if (tok
->kind
== K_symbol
)
622 int end
= tok
++->kind
== ';';
623 stmt
= alloc_stmt (V_insn
, start
, tok
, sym
);
624 append_stmt (&vars
, stmt
);
625 if (!tok
[-1].end
&& tok
->kind
== K_comment
)
627 stmt
->vis
|= V_no_eol
;
628 stmt
= alloc_comment (tok
, tok
+ 1);
629 append_stmt (&vars
, stmt
);
639 parse_file (Token
*tok
)
643 if (tok
->kind
== K_comment
)
647 while (tok
->kind
== K_comment
)
649 if (strncmp (tok
->ptr
, ":VAR_MAP ", 9) == 0)
650 record_id (tok
->ptr
+ 9, &vars_tail
);
651 if (strncmp (tok
->ptr
, ":FUNC_MAP ", 10) == 0)
652 record_id (tok
->ptr
+ 10, &funcs_tail
);
655 comment
= alloc_comment (start
, tok
);
656 comment
->vis
|= V_prefix_comment
;
659 if (tok
->kind
== K_dotted
)
661 if (is_keyword (tok
, "version")
662 || is_keyword (tok
, "target")
663 || is_keyword (tok
, "address_size"))
666 append_stmt (&decls
, comment
);
667 tok
= parse_list_nosemi (tok
);
672 const Token
*def
= 0;
673 unsigned is_decl
= 0;
677 tok
->kind
&& tok
->kind
!= '=' && tok
->kind
!= K_comment
678 && tok
->kind
!= '{' && tok
->kind
!= ';'; tok
++)
680 if (is_keyword (tok
, "global")
681 || is_keyword (tok
, "const"))
683 else if (is_keyword (tok
, "func")
684 || is_keyword (tok
, "entry"))
686 else if (is_keyword (tok
, "visible"))
688 else if (is_keyword (tok
, "extern"))
690 else if (is_keyword (tok
, "weak"))
692 if (tok
->kind
== '(')
697 else if (tok
->kind
== ')' && tok
[1].kind
!= ';')
700 if (tok
->kind
== K_symbol
)
708 append_stmt (&fns
, comment
);
710 else if (tok
->kind
== '{'
711 || tok
->kind
== K_comment
)
714 Stmt
*stmt
= alloc_stmt (vis
, start
, tok
, def
);
717 append_stmt (&fns
, comment
);
718 stmt
->vis
|= V_prefix_comment
;
720 append_stmt (&fns
, stmt
);
721 tok
= parse_insn (tok
);
725 int assign
= tok
->kind
== '=';
728 if ((vis
& V_mask
) == V_var
&& !is_decl
)
731 Stmt
*stmt
= alloc_stmt (vis
, start
, tok
, def
);
734 append_stmt (&vars
, comment
);
735 stmt
->vis
|= V_prefix_comment
;
737 append_stmt (&vars
, stmt
);
739 tok
= parse_init (tok
);
744 Stmt
*stmt
= alloc_stmt (vis
, start
, tok
, 0);
747 append_stmt (&decls
, comment
);
748 stmt
->vis
|= V_prefix_comment
;
750 append_stmt (&decls
, stmt
);
757 /* Something strange. Ignore it. */
759 append_stmt (&fns
, comment
);
763 while (tok
->kind
&& !tok
->end
);
768 /* Parse STR, saving found tokens into PVALUES and return their number.
769 Tokens are assumed to be delimited by ':'. */
771 parse_env_var (const char *str
, char ***pvalues
)
773 const char *curval
, *nextval
;
777 curval
= strchr (str
, ':');
781 curval
= strchr (curval
+ 1, ':');
784 values
= (char **) xmalloc (num
* sizeof (char *));
786 nextval
= strchr (curval
, ':');
788 nextval
= strchr (curval
, '\0');
790 for (i
= 0; i
< num
; i
++)
792 int l
= nextval
- curval
;
793 values
[i
] = (char *) xmalloc (l
+ 1);
794 memcpy (values
[i
], curval
, l
);
796 curval
= nextval
+ 1;
797 nextval
= strchr (curval
, ':');
799 nextval
= strchr (curval
, '\0');
805 /* Auxiliary function that frees elements of PTR and PTR itself.
806 N is number of elements to be freed. If PTR is NULL, nothing is freed.
807 If an element is NULL, subsequent elements are not freed. */
809 free_array_of_ptrs (void **ptr
, unsigned n
)
814 for (i
= 0; i
< n
; i
++)
824 /* Check whether NAME can be accessed in MODE. This is like access,
825 except that it never considers directories to be executable. */
827 access_check (const char *name
, int mode
)
833 if (stat (name
, &st
) < 0 || S_ISDIR (st
.st_mode
))
837 return access (name
, mode
);
841 process (FILE *in
, FILE *out
)
843 const char *input
= read_file (in
);
844 Token
*tok
= tokenize (input
);
847 tok
= parse_file (tok
);
850 fprintf (out
, "static const char ptx_code[] = \n");
851 write_stmts (out
, rev_stmts (decls
));
852 write_stmts (out
, rev_stmts (vars
));
853 write_stmts (out
, rev_stmts (fns
));
854 fprintf (out
, ";\n\n");
856 fprintf (out
, "static const char *const var_mappings[] = {\n");
857 for (id_map
*id
= var_ids
; id
; id
= id
->next
)
858 fprintf (out
, "\t\"%s\"%s\n", id
->ptx_name
, id
->next
? "," : "");
859 fprintf (out
, "};\n\n");
860 fprintf (out
, "static const char *const func_mappings[] = {\n");
861 for (id_map
*id
= func_ids
; id
; id
= id
->next
)
862 fprintf (out
, "\t\"%s\"%s\n", id
->ptx_name
, id
->next
? "," : "");
863 fprintf (out
, "};\n\n");
866 "static const struct nvptx_tdata {\n"
867 " const char *ptx_src;\n"
868 " const char *const *var_names;\n"
869 " __SIZE_TYPE__ var_num;\n"
870 " const char *const *fn_names;\n"
871 " __SIZE_TYPE__ fn_num;\n"
872 "} target_data = {\n"
875 " sizeof (var_mappings) / sizeof (var_mappings[0]),\n"
877 " sizeof (func_mappings) / sizeof (func_mappings[0])\n"
880 fprintf (out
, "#ifdef __cplusplus\n"
884 fprintf (out
, "extern void GOMP_offload_register"
885 " (const void *, int, const void *);\n");
886 fprintf (out
, "extern void GOMP_offload_unregister"
887 " (const void *, int, const void *);\n");
889 fprintf (out
, "#ifdef __cplusplus\n"
893 fprintf (out
, "extern const void *const __OFFLOAD_TABLE__[];\n\n");
895 fprintf (out
, "static __attribute__((constructor)) void init (void)\n"
897 " GOMP_offload_register (__OFFLOAD_TABLE__, %d/*NVIDIA_PTX*/,\n"
899 "};\n", GOMP_DEVICE_NVIDIA_PTX
);
901 fprintf (out
, "static __attribute__((destructor)) void fini (void)\n"
903 " GOMP_offload_unregister (__OFFLOAD_TABLE__, %d/*NVIDIA_PTX*/,\n"
905 "};\n", GOMP_DEVICE_NVIDIA_PTX
);
909 compile_native (const char *infile
, const char *outfile
, const char *compiler
)
911 const char *collect_gcc_options
= getenv ("COLLECT_GCC_OPTIONS");
912 if (!collect_gcc_options
)
913 fatal_error (input_location
,
914 "environment variable COLLECT_GCC_OPTIONS must be set");
916 struct obstack argv_obstack
;
917 obstack_init (&argv_obstack
);
918 obstack_ptr_grow (&argv_obstack
, compiler
);
919 obstack_ptr_grow (&argv_obstack
, target_ilp32
? "-m32" : "-m64");
920 obstack_ptr_grow (&argv_obstack
, infile
);
921 obstack_ptr_grow (&argv_obstack
, "-c");
922 obstack_ptr_grow (&argv_obstack
, "-o");
923 obstack_ptr_grow (&argv_obstack
, outfile
);
924 obstack_ptr_grow (&argv_obstack
, NULL
);
926 const char **new_argv
= XOBFINISH (&argv_obstack
, const char **);
927 fork_execute (new_argv
[0], CONST_CAST (char **, new_argv
), true);
928 obstack_free (&argv_obstack
, NULL
);
932 main (int argc
, char **argv
)
936 const char *outname
= 0;
938 progname
= "mkoffload";
939 diagnostic_initialize (global_dc
, 0);
941 char *collect_gcc
= getenv ("COLLECT_GCC");
942 if (collect_gcc
== NULL
)
943 fatal_error (input_location
, "COLLECT_GCC must be set.");
944 const char *gcc_path
= dirname (ASTRDUP (collect_gcc
));
945 const char *gcc_exec
= basename (ASTRDUP (collect_gcc
));
947 size_t len
= (strlen (gcc_path
) + 1
948 + strlen (GCC_INSTALL_NAME
)
950 char *driver
= XALLOCAVEC (char, len
);
952 if (strcmp (gcc_exec
, collect_gcc
) == 0)
953 /* collect_gcc has no path, so it was found in PATH. Make sure we also
954 find accel-gcc in PATH. */
958 if (gcc_path
!= NULL
)
959 driver_used
= sprintf (driver
, "%s/", gcc_path
);
960 sprintf (driver
+ driver_used
, "%s", GCC_INSTALL_NAME
);
963 if (gcc_path
== NULL
)
965 else if (access_check (driver
, X_OK
) == 0)
969 /* Don't use alloca pointer with XRESIZEVEC. */
971 /* Look in all COMPILER_PATHs for GCC_INSTALL_NAME. */
974 n_paths
= parse_env_var (getenv ("COMPILER_PATH"), &paths
);
975 for (unsigned i
= 0; i
< n_paths
; i
++)
977 len
= strlen (paths
[i
]) + 1 + strlen (GCC_INSTALL_NAME
) + 1;
978 driver
= XRESIZEVEC (char, driver
, len
);
979 sprintf (driver
, "%s/%s", paths
[i
], GCC_INSTALL_NAME
);
980 if (access_check (driver
, X_OK
) == 0)
986 free_array_of_ptrs ((void **) paths
, n_paths
);
990 fatal_error (input_location
,
991 "offload compiler %s not found", GCC_INSTALL_NAME
);
993 /* We may be called with all the arguments stored in some file and
994 passed with @file. Expand them into argv before processing. */
995 expandargv (&argc
, &argv
);
997 /* Find out whether we should compile binaries for i386 or x86-64. */
998 for (int i
= argc
- 1; i
> 0; i
--)
999 if (strncmp (argv
[i
], "-foffload-abi=", sizeof ("-foffload-abi=") - 1) == 0)
1001 if (strstr (argv
[i
], "ilp32"))
1002 target_ilp32
= true;
1003 else if (!strstr (argv
[i
], "lp64"))
1004 fatal_error (input_location
,
1005 "unrecognizable argument of option -foffload-abi");
1009 struct obstack argv_obstack
;
1010 obstack_init (&argv_obstack
);
1011 obstack_ptr_grow (&argv_obstack
, driver
);
1012 obstack_ptr_grow (&argv_obstack
, "-xlto");
1013 obstack_ptr_grow (&argv_obstack
, target_ilp32
? "-m32" : "-m64");
1014 obstack_ptr_grow (&argv_obstack
, "-S");
1016 for (int ix
= 1; ix
!= argc
; ix
++)
1018 if (!strcmp (argv
[ix
], "-o") && ix
+ 1 != argc
)
1019 outname
= argv
[++ix
];
1021 obstack_ptr_grow (&argv_obstack
, argv
[ix
]);
1024 ptx_cfile_name
= make_temp_file (".c");
1026 out
= fopen (ptx_cfile_name
, "w");
1028 fatal_error (input_location
, "cannot open '%s'", ptx_cfile_name
);
1030 /* PR libgomp/65099: Currently, we only support offloading in 64-bit
1034 ptx_name
= make_temp_file (".mkoffload");
1035 obstack_ptr_grow (&argv_obstack
, "-o");
1036 obstack_ptr_grow (&argv_obstack
, ptx_name
);
1037 obstack_ptr_grow (&argv_obstack
, NULL
);
1038 const char **new_argv
= XOBFINISH (&argv_obstack
, const char **);
1040 char *execpath
= getenv ("GCC_EXEC_PREFIX");
1041 char *cpath
= getenv ("COMPILER_PATH");
1042 char *lpath
= getenv ("LIBRARY_PATH");
1043 unsetenv ("GCC_EXEC_PREFIX");
1044 unsetenv ("COMPILER_PATH");
1045 unsetenv ("LIBRARY_PATH");
1047 fork_execute (new_argv
[0], CONST_CAST (char **, new_argv
), true);
1048 obstack_free (&argv_obstack
, NULL
);
1050 xputenv (concat ("GCC_EXEC_PREFIX=", execpath
, NULL
));
1051 xputenv (concat ("COMPILER_PATH=", cpath
, NULL
));
1052 xputenv (concat ("LIBRARY_PATH=", lpath
, NULL
));
1054 in
= fopen (ptx_name
, "r");
1056 fatal_error (input_location
, "cannot open intermediate ptx file");
1063 compile_native (ptx_cfile_name
, outname
, collect_gcc
);
1065 utils_cleanup (false);