]>
Commit | Line | Data |
---|---|---|
1f83528e TS |
1 | /* Offload image generation tool for PTX. |
2 | ||
3 | Copyright (C) 2014-2015 Free Software Foundation, Inc. | |
4 | ||
5 | Contributed by Nathan Sidwell <nathan@codesourcery.com> and | |
6 | Bernd Schmidt <bernds@codesourcery.com>. | |
7 | ||
8 | This file is part of GCC. | |
9 | ||
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. | |
14 | ||
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. | |
19 | ||
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/>. */ | |
23 | ||
24 | /* Munges PTX assembly into a C source file defining the PTX code as a | |
25 | string. | |
26 | ||
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. */ | |
29 | ||
30 | #include "config.h" | |
31 | #include "system.h" | |
32 | #include "coretypes.h" | |
33 | #include "intl.h" | |
34 | #include <libgen.h> | |
35 | #include "obstack.h" | |
ce888a99 | 36 | #include "diagnostic.h" |
1f83528e | 37 | #include "collect-utils.h" |
e6f229ca | 38 | #include "gomp-constants.h" |
1f83528e TS |
39 | |
40 | const char tool_name[] = "nvptx mkoffload"; | |
41 | ||
42 | #define COMMENT_PREFIX "#" | |
43 | ||
44 | typedef enum Kind | |
45 | { | |
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 */ | |
51 | K_number, | |
52 | K_string, | |
53 | K_comment | |
54 | } Kind; | |
55 | ||
56 | typedef struct Token | |
57 | { | |
58 | unsigned short kind : 12; | |
59 | unsigned short space : 1; /* preceded by space */ | |
60 | unsigned short end : 1; /* succeeded by end of line */ | |
61 | /* Length of token */ | |
62 | unsigned short len; | |
63 | ||
64 | /* Token itself */ | |
65 | char const *ptr; | |
66 | } Token; | |
67 | ||
68 | /* statement info */ | |
69 | typedef enum Vis | |
70 | { | |
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 */ | |
76 | V_comment = 5, | |
77 | V_pred = 6, /* predicate */ | |
78 | V_mask = 0x7, | |
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 */ | |
83 | } Vis; | |
84 | ||
85 | typedef struct Stmt | |
86 | { | |
87 | struct Stmt *next; | |
88 | Token *tokens; | |
89 | unsigned char vis; | |
90 | unsigned len : 12; | |
91 | unsigned sym : 12; | |
92 | } Stmt; | |
93 | ||
94 | struct id_map | |
95 | { | |
96 | id_map *next; | |
97 | char *ptx_name; | |
98 | }; | |
99 | ||
100 | static const char *read_file (FILE *); | |
101 | static Token *tokenize (const char *); | |
102 | ||
103 | static void write_token (FILE *, const Token *); | |
104 | static void write_tokens (FILE *, const Token *, unsigned, int); | |
105 | ||
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 *); | |
112 | ||
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 *); | |
117 | ||
118 | static Stmt *decls; | |
119 | static Stmt *vars; | |
120 | static Stmt *fns; | |
121 | ||
122 | static id_map *func_ids, **funcs_tail = &func_ids; | |
123 | static id_map *var_ids, **vars_tail = &var_ids; | |
124 | ||
125 | /* Files to unlink. */ | |
126 | static const char *ptx_name; | |
127 | static const char *ptx_cfile_name; | |
128 | ||
f82a9d90 TS |
129 | /* Shows if we should compile binaries for i386 instead of x86-64. */ |
130 | bool target_ilp32 = false; | |
131 | ||
1f83528e TS |
132 | /* Delete tempfiles. */ |
133 | ||
134 | /* Unlink a temporary file unless requested otherwise. */ | |
135 | ||
136 | void | |
137 | maybe_unlink (const char *file) | |
138 | { | |
139 | if (! debug) | |
140 | { | |
141 | if (unlink_if_ordinary (file) | |
142 | && errno != ENOENT) | |
40fecdd6 | 143 | fatal_error (input_location, "deleting file %s: %m", file); |
1f83528e TS |
144 | } |
145 | else | |
146 | fprintf (stderr, "[Leaving %s]\n", file); | |
147 | } | |
148 | ||
149 | void | |
150 | tool_cleanup (bool) | |
151 | { | |
152 | } | |
153 | ||
154 | /* Add or change the value of an environment variable, outputting the | |
155 | change to standard error if in verbose mode. */ | |
156 | static void | |
157 | xputenv (const char *string) | |
158 | { | |
159 | if (verbose) | |
160 | fprintf (stderr, "%s\n", string); | |
161 | putenv (CONST_CAST (char *, string)); | |
162 | } | |
163 | ||
164 | ||
165 | static void | |
166 | record_id (const char *p1, id_map ***where) | |
167 | { | |
168 | const char *end = strchr (p1, '\n'); | |
169 | if (!end) | |
40fecdd6 | 170 | fatal_error (input_location, "malformed ptx file"); |
1f83528e TS |
171 | |
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'; | |
177 | v->next = NULL; | |
178 | id_map **tail = *where; | |
179 | *tail = v; | |
180 | *where = &v->next; | |
181 | } | |
182 | ||
183 | /* Read the whole input file. It will be NUL terminated (but | |
184 | remember, there could be a NUL in the file itself. */ | |
185 | ||
186 | static const char * | |
187 | read_file (FILE *stream) | |
188 | { | |
189 | size_t alloc = 16384; | |
190 | size_t base = 0; | |
191 | char *buffer; | |
192 | ||
193 | if (!fseek (stream, 0, SEEK_END)) | |
194 | { | |
195 | /* Get the file size. */ | |
196 | long s = ftell (stream); | |
197 | if (s >= 0) | |
198 | alloc = s + 100; | |
199 | fseek (stream, 0, SEEK_SET); | |
200 | } | |
201 | buffer = XNEWVEC (char, alloc); | |
202 | ||
203 | for (;;) | |
204 | { | |
205 | size_t n = fread (buffer + base, 1, alloc - base - 1, stream); | |
206 | ||
207 | if (!n) | |
208 | break; | |
209 | base += n; | |
210 | if (base + 1 == alloc) | |
211 | { | |
212 | alloc *= 2; | |
213 | buffer = XRESIZEVEC (char, buffer, alloc); | |
214 | } | |
215 | } | |
216 | buffer[base] = 0; | |
217 | return buffer; | |
218 | } | |
219 | ||
220 | /* Read a token, advancing ptr. | |
221 | If we read a comment, append it to the comments block. */ | |
222 | ||
223 | static Token * | |
224 | tokenize (const char *ptr) | |
225 | { | |
226 | unsigned alloc = 1000; | |
227 | unsigned num = 0; | |
228 | Token *toks = XNEWVEC (Token, alloc); | |
229 | int in_comment = 0; | |
230 | int not_comment = 0; | |
231 | ||
232 | for (;; num++) | |
233 | { | |
234 | const char *base; | |
235 | unsigned kind; | |
236 | int ws = 0; | |
237 | int eol = 0; | |
238 | ||
239 | again: | |
240 | base = ptr; | |
241 | if (in_comment) | |
242 | goto block_comment; | |
243 | switch (kind = *ptr++) | |
244 | { | |
245 | default: | |
246 | break; | |
247 | ||
248 | case '\n': | |
249 | eol = 1; | |
250 | /* Fall through */ | |
251 | case ' ': | |
252 | case '\t': | |
253 | case '\r': | |
254 | case '\v': | |
255 | /* White space */ | |
256 | ws = not_comment; | |
257 | goto again; | |
258 | ||
259 | case '/': | |
260 | { | |
261 | if (*ptr == '/') | |
262 | { | |
263 | /* line comment. Do not include trailing \n */ | |
264 | base += 2; | |
265 | for (; *ptr; ptr++) | |
266 | if (*ptr == '\n') | |
267 | break; | |
268 | kind = K_comment; | |
269 | } | |
270 | else if (*ptr == '*') | |
271 | { | |
272 | /* block comment */ | |
273 | base += 2; | |
274 | ptr++; | |
275 | ||
276 | block_comment: | |
277 | eol = in_comment; | |
278 | in_comment = 1; | |
279 | for (; *ptr; ptr++) | |
280 | { | |
281 | if (*ptr == '\n') | |
282 | { | |
283 | ptr++; | |
284 | break; | |
285 | } | |
286 | if (ptr[0] == '*' && ptr[1] == '/') | |
287 | { | |
288 | in_comment = 2; | |
289 | ptr += 2; | |
290 | break; | |
291 | } | |
292 | } | |
293 | kind = K_comment; | |
294 | } | |
295 | else | |
296 | break; | |
297 | } | |
298 | break; | |
299 | ||
300 | case '"': | |
301 | /* quoted string */ | |
302 | kind = K_string; | |
303 | while (*ptr) | |
304 | if (*ptr == '"') | |
305 | { | |
306 | ptr++; | |
307 | break; | |
308 | } | |
309 | else if (*ptr++ == '\\') | |
310 | ptr++; | |
311 | break; | |
312 | ||
313 | case '.': | |
314 | if (*ptr < '0' || *ptr > '9') | |
315 | { | |
316 | kind = K_dotted; | |
317 | ws = not_comment; | |
318 | goto ident; | |
319 | } | |
320 | /* FALLTHROUGH */ | |
321 | case '0'...'9': | |
322 | kind = K_number; | |
323 | goto ident; | |
324 | break; | |
325 | ||
326 | case '$': /* local labels. */ | |
327 | case '%': /* register names, pseudoes etc */ | |
328 | kind = K_ident; | |
329 | goto ident; | |
330 | ||
331 | case 'a'...'z': | |
332 | case 'A'...'Z': | |
333 | case '_': | |
334 | kind = K_symbol; /* possible symbol name */ | |
335 | ident: | |
336 | for (; *ptr; ptr++) | |
337 | { | |
338 | if (*ptr >= 'A' && *ptr <= 'Z') | |
339 | continue; | |
340 | if (*ptr >= 'a' && *ptr <= 'z') | |
341 | continue; | |
342 | if (*ptr >= '0' && *ptr <= '9') | |
343 | continue; | |
344 | if (*ptr == '_' || *ptr == '$') | |
345 | continue; | |
346 | if (*ptr == '.' && kind != K_dotted) | |
347 | /* Idents starting with a dot, cannot have internal dots. */ | |
348 | continue; | |
349 | if ((*ptr == '+' || *ptr == '-') | |
350 | && kind == K_number | |
351 | && (ptr[-1] == 'e' || ptr[-1] == 'E' | |
352 | || ptr[-1] == 'p' || ptr[-1] == 'P')) | |
353 | /* exponent */ | |
354 | continue; | |
355 | break; | |
356 | } | |
357 | if (*ptr == ':') | |
358 | { | |
359 | ptr++; | |
360 | kind = K_label; | |
361 | } | |
362 | break; | |
363 | } | |
364 | ||
365 | if (alloc == num) | |
366 | { | |
367 | alloc *= 2; | |
368 | toks = XRESIZEVEC (Token, toks, alloc); | |
369 | } | |
370 | Token *tok = toks + num; | |
371 | ||
372 | tok->kind = kind; | |
373 | tok->space = ws; | |
374 | tok->end = 0; | |
375 | tok->ptr = base; | |
376 | tok->len = ptr - base - in_comment; | |
377 | in_comment &= 1; | |
378 | not_comment = kind != K_comment; | |
379 | if (eol && num) | |
380 | tok[-1].end = 1; | |
381 | if (!kind) | |
382 | break; | |
383 | } | |
384 | ||
385 | return toks; | |
386 | } | |
387 | ||
388 | /* Write an encoded token. */ | |
389 | ||
390 | static void | |
391 | write_token (FILE *out, Token const *tok) | |
392 | { | |
393 | if (tok->space) | |
394 | fputc (' ', out); | |
395 | ||
396 | switch (tok->kind) | |
397 | { | |
398 | case K_string: | |
399 | { | |
400 | const char *c = tok->ptr + 1; | |
401 | size_t len = tok->len - 2; | |
402 | ||
403 | fputs ("\\\"", out); | |
404 | while (len) | |
405 | { | |
406 | const char *bs = (const char *)memchr (c, '\\', len); | |
407 | size_t l = bs ? bs - c : len; | |
408 | ||
409 | fprintf (out, "%.*s", (int)l, c); | |
410 | len -= l; | |
411 | c += l; | |
412 | if (bs) | |
413 | { | |
414 | fputs ("\\\\", out); | |
415 | len--, c++; | |
416 | } | |
417 | } | |
418 | fputs ("\\\"", out); | |
419 | } | |
420 | break; | |
421 | ||
422 | default: | |
423 | /* All other tokens shouldn't have anything magic in them */ | |
424 | fprintf (out, "%.*s", tok->len, tok->ptr); | |
425 | break; | |
426 | } | |
427 | if (tok->end) | |
428 | fputs ("\\n", out); | |
429 | } | |
430 | ||
431 | static void | |
432 | write_tokens (FILE *out, Token const *toks, unsigned len, int spc) | |
433 | { | |
434 | fputs ("\t\"", out); | |
435 | for (; len--; toks++) | |
436 | write_token (out, toks); | |
437 | if (spc) | |
438 | fputs (" ", out); | |
439 | fputs ("\"", out); | |
440 | } | |
441 | ||
442 | static Stmt * | |
443 | alloc_stmt (unsigned vis, Token *tokens, Token *end, Token const *sym) | |
444 | { | |
445 | static unsigned alloc = 0; | |
446 | static Stmt *heap = 0; | |
447 | ||
448 | if (!alloc) | |
449 | { | |
450 | alloc = 1000; | |
451 | heap = XNEWVEC (Stmt, alloc); | |
452 | } | |
453 | ||
454 | Stmt *stmt = heap++; | |
455 | alloc--; | |
456 | ||
457 | tokens->space = 0; | |
458 | stmt->next = 0; | |
459 | stmt->vis = vis; | |
460 | stmt->tokens = tokens; | |
461 | stmt->len = end - tokens; | |
462 | stmt->sym = sym ? sym - tokens : ~0; | |
463 | ||
464 | return stmt; | |
465 | } | |
466 | ||
467 | static Stmt * | |
468 | rev_stmts (Stmt *stmt) | |
469 | { | |
470 | Stmt *prev = 0; | |
471 | Stmt *next; | |
472 | ||
473 | while (stmt) | |
474 | { | |
475 | next = stmt->next; | |
476 | stmt->next = prev; | |
477 | prev = stmt; | |
478 | stmt = next; | |
479 | } | |
480 | ||
481 | return prev; | |
482 | } | |
483 | ||
484 | static void | |
485 | write_stmt (FILE *out, const Stmt *stmt) | |
486 | { | |
487 | if ((stmt->vis & V_mask) != V_comment) | |
488 | { | |
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); | |
492 | } | |
493 | } | |
494 | ||
495 | static void | |
496 | write_stmts (FILE *out, const Stmt *stmts) | |
497 | { | |
498 | for (; stmts; stmts = stmts->next) | |
499 | write_stmt (out, stmts); | |
500 | } | |
501 | ||
502 | static Token * | |
503 | parse_insn (Token *tok) | |
504 | { | |
505 | unsigned depth = 0; | |
506 | ||
507 | do | |
508 | { | |
509 | Stmt *stmt; | |
510 | Token *sym = 0; | |
511 | unsigned s = V_insn; | |
512 | Token *start = tok; | |
513 | ||
514 | switch (tok++->kind) | |
515 | { | |
516 | case K_comment: | |
517 | while (tok->kind == K_comment) | |
518 | tok++; | |
519 | stmt = alloc_comment (start, tok); | |
520 | append_stmt (&fns, stmt); | |
521 | continue; | |
522 | ||
523 | case '{': | |
524 | depth++; | |
525 | break; | |
526 | ||
527 | case '}': | |
528 | depth--; | |
529 | break; | |
530 | ||
531 | case K_label: | |
532 | if (tok[-1].ptr[0] != '$') | |
533 | sym = tok - 1; | |
534 | tok[-1].end = 1; | |
535 | s = V_label; | |
536 | break; | |
537 | ||
538 | case '@': | |
539 | tok->space = 0; | |
540 | if (tok->kind == '!') | |
541 | tok++; | |
542 | if (tok->kind == K_symbol) | |
543 | sym = tok; | |
544 | tok++; | |
545 | s = V_pred; | |
546 | break; | |
547 | ||
548 | default: | |
549 | for (; tok->kind != ';'; tok++) | |
550 | { | |
551 | if (tok->kind == ',') | |
552 | tok[1].space = 0; | |
553 | else if (tok->kind == K_symbol) | |
554 | sym = tok; | |
555 | } | |
556 | tok++->end = 1; | |
557 | break; | |
558 | } | |
559 | ||
560 | stmt = alloc_stmt (s, start, tok, sym); | |
561 | append_stmt (&fns, stmt); | |
562 | ||
563 | if (!tok[-1].end && tok[0].kind == K_comment) | |
564 | { | |
565 | stmt->vis |= V_no_eol; | |
566 | stmt = alloc_comment (tok, tok + 1); | |
567 | append_stmt (&fns, stmt); | |
568 | tok++; | |
569 | } | |
570 | } | |
571 | while (depth); | |
572 | ||
573 | return tok; | |
574 | } | |
575 | ||
576 | /* comma separated list of tokens */ | |
577 | ||
578 | static Token * | |
579 | parse_list_nosemi (Token *tok) | |
580 | { | |
581 | Token *start = tok; | |
582 | ||
583 | do | |
584 | if (!(++tok)->kind) | |
585 | break; | |
586 | while ((++tok)->kind == ','); | |
587 | ||
588 | tok[-1].end = 1; | |
589 | Stmt *stmt = alloc_stmt (V_dot, start, tok, 0); | |
590 | append_stmt (&decls, stmt); | |
591 | ||
592 | return tok; | |
593 | } | |
594 | ||
595 | #define is_keyword(T,S) \ | |
596 | (sizeof (S) == (T)->len && !memcmp ((T)->ptr + 1, (S), (T)->len - 1)) | |
597 | ||
598 | static Token * | |
599 | parse_init (Token *tok) | |
600 | { | |
601 | for (;;) | |
602 | { | |
603 | Token *start = tok; | |
604 | Token const *sym = 0; | |
605 | Stmt *stmt; | |
606 | ||
607 | if (tok->kind == K_comment) | |
608 | { | |
609 | while (tok->kind == K_comment) | |
610 | tok++; | |
611 | stmt = alloc_comment (start, tok); | |
612 | append_stmt (&vars, stmt); | |
613 | start = tok; | |
614 | } | |
615 | ||
616 | if (tok->kind == '{') | |
617 | tok[1].space = 0; | |
618 | for (; tok->kind != ',' && tok->kind != ';'; tok++) | |
619 | if (tok->kind == K_symbol) | |
620 | sym = tok; | |
621 | tok[1].space = 0; | |
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) | |
626 | { | |
627 | stmt->vis |= V_no_eol; | |
628 | stmt = alloc_comment (tok, tok + 1); | |
629 | append_stmt (&vars, stmt); | |
630 | tok++; | |
631 | } | |
632 | if (end) | |
633 | break; | |
634 | } | |
635 | return tok; | |
636 | } | |
637 | ||
638 | static Token * | |
639 | parse_file (Token *tok) | |
640 | { | |
641 | Stmt *comment = 0; | |
642 | ||
643 | if (tok->kind == K_comment) | |
644 | { | |
645 | Token *start = tok; | |
646 | ||
647 | while (tok->kind == K_comment) | |
648 | { | |
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); | |
653 | tok++; | |
654 | } | |
655 | comment = alloc_comment (start, tok); | |
656 | comment->vis |= V_prefix_comment; | |
657 | } | |
658 | ||
659 | if (tok->kind == K_dotted) | |
660 | { | |
661 | if (is_keyword (tok, "version") | |
662 | || is_keyword (tok, "target") | |
663 | || is_keyword (tok, "address_size")) | |
664 | { | |
665 | if (comment) | |
666 | append_stmt (&decls, comment); | |
667 | tok = parse_list_nosemi (tok); | |
668 | } | |
669 | else | |
670 | { | |
671 | unsigned vis = 0; | |
672 | const Token *def = 0; | |
673 | unsigned is_decl = 0; | |
674 | Token *start; | |
675 | ||
676 | for (start = tok; | |
677 | tok->kind && tok->kind != '=' && tok->kind != K_comment | |
678 | && tok->kind != '{' && tok->kind != ';'; tok++) | |
679 | { | |
680 | if (is_keyword (tok, "global") | |
681 | || is_keyword (tok, "const")) | |
682 | vis |= V_var; | |
683 | else if (is_keyword (tok, "func") | |
684 | || is_keyword (tok, "entry")) | |
685 | vis |= V_func; | |
686 | else if (is_keyword (tok, "visible")) | |
687 | vis |= V_global; | |
688 | else if (is_keyword (tok, "extern")) | |
689 | is_decl = 1; | |
690 | else if (is_keyword (tok, "weak")) | |
691 | vis |= V_weak; | |
692 | if (tok->kind == '(') | |
693 | { | |
694 | tok[1].space = 0; | |
695 | tok[0].space = 1; | |
696 | } | |
697 | else if (tok->kind == ')' && tok[1].kind != ';') | |
698 | tok[1].space = 1; | |
699 | ||
700 | if (tok->kind == K_symbol) | |
701 | def = tok; | |
702 | } | |
703 | ||
704 | if (!tok->kind) | |
705 | { | |
706 | /* end of file */ | |
707 | if (comment) | |
708 | append_stmt (&fns, comment); | |
709 | } | |
710 | else if (tok->kind == '{' | |
711 | || tok->kind == K_comment) | |
712 | { | |
713 | /* function defn */ | |
714 | Stmt *stmt = alloc_stmt (vis, start, tok, def); | |
715 | if (comment) | |
716 | { | |
717 | append_stmt (&fns, comment); | |
718 | stmt->vis |= V_prefix_comment; | |
719 | } | |
720 | append_stmt (&fns, stmt); | |
721 | tok = parse_insn (tok); | |
722 | } | |
723 | else | |
724 | { | |
725 | int assign = tok->kind == '='; | |
726 | ||
727 | tok++->end = 1; | |
728 | if ((vis & V_mask) == V_var && !is_decl) | |
729 | { | |
730 | /* variable */ | |
731 | Stmt *stmt = alloc_stmt (vis, start, tok, def); | |
732 | if (comment) | |
733 | { | |
734 | append_stmt (&vars, comment); | |
735 | stmt->vis |= V_prefix_comment; | |
736 | } | |
737 | append_stmt (&vars, stmt); | |
738 | if (assign) | |
739 | tok = parse_init (tok); | |
740 | } | |
741 | else | |
742 | { | |
743 | /* declaration */ | |
744 | Stmt *stmt = alloc_stmt (vis, start, tok, 0); | |
745 | if (comment) | |
746 | { | |
747 | append_stmt (&decls, comment); | |
748 | stmt->vis |= V_prefix_comment; | |
749 | } | |
750 | append_stmt (&decls, stmt); | |
751 | } | |
752 | } | |
753 | } | |
754 | } | |
755 | else | |
756 | { | |
757 | /* Something strange. Ignore it. */ | |
758 | if (comment) | |
759 | append_stmt (&fns, comment); | |
760 | ||
6f3c1d38 | 761 | do |
1f83528e | 762 | tok++; |
6f3c1d38 | 763 | while (tok->kind && !tok->end); |
1f83528e TS |
764 | } |
765 | return tok; | |
766 | } | |
767 | ||
865fc32a TS |
768 | /* Parse STR, saving found tokens into PVALUES and return their number. |
769 | Tokens are assumed to be delimited by ':'. */ | |
770 | static unsigned | |
771 | parse_env_var (const char *str, char ***pvalues) | |
772 | { | |
773 | const char *curval, *nextval; | |
774 | char **values; | |
775 | unsigned num = 1, i; | |
776 | ||
777 | curval = strchr (str, ':'); | |
778 | while (curval) | |
779 | { | |
780 | num++; | |
781 | curval = strchr (curval + 1, ':'); | |
782 | } | |
783 | ||
784 | values = (char **) xmalloc (num * sizeof (char *)); | |
785 | curval = str; | |
786 | nextval = strchr (curval, ':'); | |
787 | if (nextval == NULL) | |
788 | nextval = strchr (curval, '\0'); | |
789 | ||
790 | for (i = 0; i < num; i++) | |
791 | { | |
792 | int l = nextval - curval; | |
793 | values[i] = (char *) xmalloc (l + 1); | |
794 | memcpy (values[i], curval, l); | |
795 | values[i][l] = 0; | |
796 | curval = nextval + 1; | |
797 | nextval = strchr (curval, ':'); | |
798 | if (nextval == NULL) | |
799 | nextval = strchr (curval, '\0'); | |
800 | } | |
801 | *pvalues = values; | |
802 | return num; | |
803 | } | |
804 | ||
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. */ | |
808 | static void | |
809 | free_array_of_ptrs (void **ptr, unsigned n) | |
810 | { | |
811 | unsigned i; | |
812 | if (!ptr) | |
813 | return; | |
814 | for (i = 0; i < n; i++) | |
815 | { | |
816 | if (!ptr[i]) | |
817 | break; | |
818 | free (ptr[i]); | |
819 | } | |
820 | free (ptr); | |
821 | return; | |
822 | } | |
823 | ||
824 | /* Check whether NAME can be accessed in MODE. This is like access, | |
825 | except that it never considers directories to be executable. */ | |
826 | static int | |
827 | access_check (const char *name, int mode) | |
828 | { | |
829 | if (mode == X_OK) | |
830 | { | |
831 | struct stat st; | |
832 | ||
833 | if (stat (name, &st) < 0 || S_ISDIR (st.st_mode)) | |
834 | return -1; | |
835 | } | |
836 | ||
837 | return access (name, mode); | |
838 | } | |
839 | ||
1f83528e TS |
840 | static void |
841 | process (FILE *in, FILE *out) | |
842 | { | |
843 | const char *input = read_file (in); | |
844 | Token *tok = tokenize (input); | |
845 | ||
846 | do | |
847 | tok = parse_file (tok); | |
848 | while (tok->kind); | |
849 | ||
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"); | |
a4cb876d NS |
855 | |
856 | fprintf (out, "static const char *const var_mappings[] = {\n"); | |
857 | for (id_map *id = var_ids; id; id = id->next) | |
1f83528e TS |
858 | fprintf (out, "\t\"%s\"%s\n", id->ptx_name, id->next ? "," : ""); |
859 | fprintf (out, "};\n\n"); | |
a4cb876d NS |
860 | fprintf (out, "static const char *const func_mappings[] = {\n"); |
861 | for (id_map *id = func_ids; id; id = id->next) | |
1f83528e TS |
862 | fprintf (out, "\t\"%s\"%s\n", id->ptx_name, id->next ? "," : ""); |
863 | fprintf (out, "};\n\n"); | |
864 | ||
a4cb876d | 865 | fprintf (out, |
afb2d80b | 866 | "static const struct nvptx_tdata {\n" |
a4cb876d NS |
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" | |
873 | " ptx_code,\n" | |
874 | " var_mappings," | |
875 | " sizeof (var_mappings) / sizeof (var_mappings[0]),\n" | |
876 | " func_mappings," | |
877 | " sizeof (func_mappings) / sizeof (func_mappings[0])\n" | |
878 | "};\n\n"); | |
1f83528e | 879 | |
852b55f9 NS |
880 | fprintf (out, "#ifdef __cplusplus\n" |
881 | "extern \"C\" {\n" | |
882 | "#endif\n"); | |
afb2d80b | 883 | fprintf (out, "extern void GOMP_offload_register" |
ebe4a560 | 884 | " (const void *, int, const void *);\n"); |
852b55f9 NS |
885 | fprintf (out, "#ifdef __cplusplus\n" |
886 | "}\n" | |
887 | "#endif\n"); | |
1f83528e | 888 | |
ebe4a560 | 889 | fprintf (out, "extern const void *const __OFFLOAD_TABLE__[];\n\n"); |
1f83528e | 890 | fprintf (out, "static __attribute__((constructor)) void init (void)\n{\n"); |
9584e638 | 891 | fprintf (out, " GOMP_offload_register (__OFFLOAD_TABLE__, %d,\n", |
e6f229ca | 892 | GOMP_DEVICE_NVIDIA_PTX); |
1f83528e TS |
893 | fprintf (out, " &target_data);\n"); |
894 | fprintf (out, "};\n"); | |
895 | } | |
896 | ||
897 | static void | |
898 | compile_native (const char *infile, const char *outfile, const char *compiler) | |
899 | { | |
900 | const char *collect_gcc_options = getenv ("COLLECT_GCC_OPTIONS"); | |
901 | if (!collect_gcc_options) | |
40fecdd6 JM |
902 | fatal_error (input_location, |
903 | "environment variable COLLECT_GCC_OPTIONS must be set"); | |
1f83528e TS |
904 | |
905 | struct obstack argv_obstack; | |
906 | obstack_init (&argv_obstack); | |
907 | obstack_ptr_grow (&argv_obstack, compiler); | |
f82a9d90 | 908 | obstack_ptr_grow (&argv_obstack, target_ilp32 ? "-m32" : "-m64"); |
1f83528e TS |
909 | obstack_ptr_grow (&argv_obstack, infile); |
910 | obstack_ptr_grow (&argv_obstack, "-c"); | |
911 | obstack_ptr_grow (&argv_obstack, "-o"); | |
912 | obstack_ptr_grow (&argv_obstack, outfile); | |
913 | obstack_ptr_grow (&argv_obstack, NULL); | |
914 | ||
915 | const char **new_argv = XOBFINISH (&argv_obstack, const char **); | |
916 | fork_execute (new_argv[0], CONST_CAST (char **, new_argv), true); | |
917 | obstack_free (&argv_obstack, NULL); | |
918 | } | |
919 | ||
920 | int | |
921 | main (int argc, char **argv) | |
922 | { | |
923 | FILE *in = stdin; | |
924 | FILE *out = stdout; | |
925 | const char *outname = 0; | |
926 | ||
ce888a99 TS |
927 | progname = "mkoffload"; |
928 | diagnostic_initialize (global_dc, 0); | |
929 | ||
1f83528e TS |
930 | char *collect_gcc = getenv ("COLLECT_GCC"); |
931 | if (collect_gcc == NULL) | |
40fecdd6 | 932 | fatal_error (input_location, "COLLECT_GCC must be set."); |
1f83528e TS |
933 | const char *gcc_path = dirname (ASTRDUP (collect_gcc)); |
934 | const char *gcc_exec = basename (ASTRDUP (collect_gcc)); | |
935 | ||
936 | size_t len = (strlen (gcc_path) + 1 | |
937 | + strlen (GCC_INSTALL_NAME) | |
938 | + 1); | |
939 | char *driver = XALLOCAVEC (char, len); | |
940 | ||
941 | if (strcmp (gcc_exec, collect_gcc) == 0) | |
942 | /* collect_gcc has no path, so it was found in PATH. Make sure we also | |
943 | find accel-gcc in PATH. */ | |
944 | gcc_path = NULL; | |
945 | ||
946 | int driver_used = 0; | |
947 | if (gcc_path != NULL) | |
948 | driver_used = sprintf (driver, "%s/", gcc_path); | |
949 | sprintf (driver + driver_used, "%s", GCC_INSTALL_NAME); | |
950 | ||
865fc32a TS |
951 | bool found = false; |
952 | if (gcc_path == NULL) | |
953 | found = true; | |
954 | else if (access_check (driver, X_OK) == 0) | |
955 | found = true; | |
956 | else | |
957 | { | |
958 | /* Don't use alloca pointer with XRESIZEVEC. */ | |
959 | driver = NULL; | |
960 | /* Look in all COMPILER_PATHs for GCC_INSTALL_NAME. */ | |
961 | char **paths = NULL; | |
962 | unsigned n_paths; | |
963 | n_paths = parse_env_var (getenv ("COMPILER_PATH"), &paths); | |
964 | for (unsigned i = 0; i < n_paths; i++) | |
965 | { | |
966 | len = strlen (paths[i]) + 1 + strlen (GCC_INSTALL_NAME) + 1; | |
967 | driver = XRESIZEVEC (char, driver, len); | |
968 | sprintf (driver, "%s/%s", paths[i], GCC_INSTALL_NAME); | |
969 | if (access_check (driver, X_OK) == 0) | |
970 | { | |
971 | found = true; | |
972 | break; | |
973 | } | |
974 | } | |
975 | free_array_of_ptrs ((void **) paths, n_paths); | |
976 | } | |
977 | ||
978 | if (!found) | |
979 | fatal_error (input_location, | |
980 | "offload compiler %s not found", GCC_INSTALL_NAME); | |
981 | ||
1f83528e TS |
982 | /* We may be called with all the arguments stored in some file and |
983 | passed with @file. Expand them into argv before processing. */ | |
984 | expandargv (&argc, &argv); | |
985 | ||
f82a9d90 TS |
986 | /* Find out whether we should compile binaries for i386 or x86-64. */ |
987 | for (int i = argc - 1; i > 0; i--) | |
988 | if (strncmp (argv[i], "-foffload-abi=", sizeof ("-foffload-abi=") - 1) == 0) | |
989 | { | |
990 | if (strstr (argv[i], "ilp32")) | |
991 | target_ilp32 = true; | |
992 | else if (!strstr (argv[i], "lp64")) | |
993 | fatal_error (input_location, | |
994 | "unrecognizable argument of option -foffload-abi"); | |
995 | break; | |
996 | } | |
997 | ||
1f83528e TS |
998 | struct obstack argv_obstack; |
999 | obstack_init (&argv_obstack); | |
1000 | obstack_ptr_grow (&argv_obstack, driver); | |
1001 | obstack_ptr_grow (&argv_obstack, "-xlto"); | |
f82a9d90 | 1002 | obstack_ptr_grow (&argv_obstack, target_ilp32 ? "-m32" : "-m64"); |
1f83528e TS |
1003 | obstack_ptr_grow (&argv_obstack, "-S"); |
1004 | ||
1005 | for (int ix = 1; ix != argc; ix++) | |
1006 | { | |
1007 | if (!strcmp (argv[ix], "-o") && ix + 1 != argc) | |
1008 | outname = argv[++ix]; | |
1009 | else | |
1010 | obstack_ptr_grow (&argv_obstack, argv[ix]); | |
1011 | } | |
1012 | ||
1f83528e TS |
1013 | ptx_cfile_name = make_temp_file (".c"); |
1014 | ||
1015 | out = fopen (ptx_cfile_name, "w"); | |
1016 | if (!out) | |
40fecdd6 | 1017 | fatal_error (input_location, "cannot open '%s'", ptx_cfile_name); |
1f83528e | 1018 | |
a92defda TS |
1019 | /* PR libgomp/65099: Currently, we only support offloading in 64-bit |
1020 | configurations. */ | |
1021 | if (!target_ilp32) | |
1022 | { | |
1023 | ptx_name = make_temp_file (".mkoffload"); | |
1024 | obstack_ptr_grow (&argv_obstack, "-o"); | |
1025 | obstack_ptr_grow (&argv_obstack, ptx_name); | |
1026 | obstack_ptr_grow (&argv_obstack, NULL); | |
1027 | const char **new_argv = XOBFINISH (&argv_obstack, const char **); | |
1028 | ||
1029 | char *execpath = getenv ("GCC_EXEC_PREFIX"); | |
1030 | char *cpath = getenv ("COMPILER_PATH"); | |
1031 | char *lpath = getenv ("LIBRARY_PATH"); | |
1032 | unsetenv ("GCC_EXEC_PREFIX"); | |
1033 | unsetenv ("COMPILER_PATH"); | |
1034 | unsetenv ("LIBRARY_PATH"); | |
1035 | ||
1036 | fork_execute (new_argv[0], CONST_CAST (char **, new_argv), true); | |
1037 | obstack_free (&argv_obstack, NULL); | |
1038 | ||
1039 | xputenv (concat ("GCC_EXEC_PREFIX=", execpath, NULL)); | |
1040 | xputenv (concat ("COMPILER_PATH=", cpath, NULL)); | |
1041 | xputenv (concat ("LIBRARY_PATH=", lpath, NULL)); | |
1042 | ||
1043 | in = fopen (ptx_name, "r"); | |
1044 | if (!in) | |
1045 | fatal_error (input_location, "cannot open intermediate ptx file"); | |
1046 | ||
1047 | process (in, out); | |
1048 | } | |
1049 | ||
1f83528e TS |
1050 | fclose (out); |
1051 | ||
1052 | compile_native (ptx_cfile_name, outname, collect_gcc); | |
1053 | ||
1054 | utils_cleanup (false); | |
1055 | ||
1056 | return 0; | |
1057 | } |