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" 36#include "diagnostic.h" 37#include "collect-utils.h" 38#include "gomp-constants.h" 39 40const char tool_name[] = "nvptx mkoffload"; 41 42#define COMMENT_PREFIX "#" 43 44typedef 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 56typedef 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 */ 69typedef 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 85typedef 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 94struct id_map 95{ 96 id_map *next; 97 char *ptx_name; 98}; 99 100static const char *read_file (FILE *); 101static Token *tokenize (const char *); 102 103static void write_token (FILE *, const Token *); 104static void write_tokens (FILE *, const Token *, unsigned, int); 105 106static 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)) 109static Stmt *rev_stmts (Stmt *); 110static void write_stmt (FILE *, const Stmt *); 111static void write_stmts (FILE *, const Stmt *); 112 113static Token *parse_insn (Token *); 114static Token *parse_list_nosemi (Token *); 115static Token *parse_init (Token *); 116static Token *parse_file (Token *); 117 118static Stmt *decls; 119static Stmt *vars; 120static Stmt *fns; 121 122static id_map *func_ids, **funcs_tail = &func_ids; 123static id_map *var_ids, **vars_tail = &var_ids; 124 125/* Files to unlink. */ 126static const char *ptx_name; 127static const char *ptx_cfile_name; 128 129/* Shows if we should compile binaries for i386 instead of x86-64. */ 130bool target_ilp32 = false; 131 132/* Delete tempfiles. */ 133 134/* Unlink a temporary file unless requested otherwise. */ 135 136void 137maybe_unlink (const char *file) 138{ 139 if (! debug) 140 { 141 if (unlink_if_ordinary (file) 142 && errno != ENOENT) 143 fatal_error (input_location, "deleting file %s: %m", file); 144 } 145 else 146 fprintf (stderr, "[Leaving %s]\n", file); 147} 148 149void 150tool_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. */ 156static void 157xputenv (const char *string) 158{ 159 if (verbose) 160 fprintf (stderr, "%s\n", string); 161 putenv (CONST_CAST (char *, string)); 162} 163 164 165static void 166record_id (const char *p1, id_map ***where) 167{ 168 const char *end = strchr (p1, '\n'); 169 if (!end) 170 fatal_error (input_location, "malformed ptx file"); 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 186static const char * 187read_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 223static Token * 224tokenize (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 390static void 391write_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 431static void 432write_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 442static Stmt * 443alloc_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 467static Stmt * 468rev_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 484static void 485write_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 495static void 496write_stmts (FILE *out, const Stmt *stmts) 497{ 498 for (; stmts; stmts = stmts->next) 499 write_stmt (out, stmts); 500} 501 502static Token * 503parse_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 578static Token * 579parse_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 598static Token * 599parse_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 638static Token * 639parse_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 761 do 762 tok++; 763 while (tok->kind && !tok->end); 764 } 765 return tok; 766} 767 768/* Parse STR, saving found tokens into PVALUES and return their number. 769 Tokens are assumed to be delimited by ':'. */ 770static unsigned 771parse_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. */ 808static void 809free_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. */ 826static int 827access_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 840static void 841process (FILE *in, FILE *out) 842{ 843 const char *input = read_file (in); 844 Token *tok = tokenize (input); 845 unsigned int nvars = 0, nfuncs = 0; 846 847 do 848 tok = parse_file (tok); 849 while (tok->kind); 850 851 fprintf (out, "static const char ptx_code[] = \n"); 852 write_stmts (out, rev_stmts (decls)); 853 write_stmts (out, rev_stmts (vars)); 854 write_stmts (out, rev_stmts (fns)); 855 fprintf (out, ";\n\n"); 856 fprintf (out, "static const char *var_mappings[] = {\n"); 857 for (id_map *id = var_ids; id; id = id->next, nvars++) 858 fprintf (out, "\t\"%s\"%s\n", id->ptx_name, id->next ? "," : ""); 859 fprintf (out, "};\n\n"); 860 fprintf (out, "static const char *func_mappings[] = {\n"); 861 for (id_map *id = func_ids; id; id = id->next, nfuncs++) 862 fprintf (out, "\t\"%s\"%s\n", id->ptx_name, id->next ? "," : ""); 863 fprintf (out, "};\n\n"); 864 865 fprintf (out, "static const void *target_data[] = {\n"); 866 fprintf (out, " ptx_code, (void*) %u, var_mappings, (void*) %u, " 867 "func_mappings\n", nvars, nfuncs); 868 fprintf (out, "};\n\n"); 869 870 fprintf (out, "extern void GOMP_offload_register (const void *, int, void *);\n"); 871 872 fprintf (out, "extern void *__OFFLOAD_TABLE__[];\n\n"); 873 fprintf (out, "static __attribute__((constructor)) void init (void)\n{\n"); 874 fprintf (out, " GOMP_offload_register (__OFFLOAD_TABLE__, %d,\n", 875 GOMP_DEVICE_NVIDIA_PTX); 876 fprintf (out, " &target_data);\n"); 877 fprintf (out, "};\n"); 878} 879 880static void 881compile_native (const char *infile, const char *outfile, const char *compiler) 882{ 883 const char *collect_gcc_options = getenv ("COLLECT_GCC_OPTIONS"); 884 if (!collect_gcc_options) 885 fatal_error (input_location, 886 "environment variable COLLECT_GCC_OPTIONS must be set"); 887 888 struct obstack argv_obstack; 889 obstack_init (&argv_obstack); 890 obstack_ptr_grow (&argv_obstack, compiler); 891 obstack_ptr_grow (&argv_obstack, target_ilp32 ? "-m32" : "-m64"); 892 obstack_ptr_grow (&argv_obstack, infile); 893 obstack_ptr_grow (&argv_obstack, "-c"); 894 obstack_ptr_grow (&argv_obstack, "-o"); 895 obstack_ptr_grow (&argv_obstack, outfile); 896 obstack_ptr_grow (&argv_obstack, NULL); 897 898 const char **new_argv = XOBFINISH (&argv_obstack, const char **); 899 fork_execute (new_argv[0], CONST_CAST (char **, new_argv), true); 900 obstack_free (&argv_obstack, NULL); 901} 902 903int 904main (int argc, char **argv) 905{ 906 FILE *in = stdin; 907 FILE *out = stdout; 908 const char *outname = 0; 909 910 progname = "mkoffload"; 911 diagnostic_initialize (global_dc, 0); 912 913 char *collect_gcc = getenv ("COLLECT_GCC"); 914 if (collect_gcc == NULL) 915 fatal_error (input_location, "COLLECT_GCC must be set."); 916 const char *gcc_path = dirname (ASTRDUP (collect_gcc)); 917 const char *gcc_exec = basename (ASTRDUP (collect_gcc)); 918 919 size_t len = (strlen (gcc_path) + 1 920 + strlen (GCC_INSTALL_NAME) 921 + 1); 922 char *driver = XALLOCAVEC (char, len); 923 924 if (strcmp (gcc_exec, collect_gcc) == 0) 925 /* collect_gcc has no path, so it was found in PATH. Make sure we also 926 find accel-gcc in PATH. */ 927 gcc_path = NULL; 928 929 int driver_used = 0; 930 if (gcc_path != NULL) 931 driver_used = sprintf (driver, "%s/", gcc_path); 932 sprintf (driver + driver_used, "%s", GCC_INSTALL_NAME); 933 934 bool found = false; 935 if (gcc_path == NULL) 936 found = true; 937 else if (access_check (driver, X_OK) == 0) 938 found = true; 939 else 940 { 941 /* Don't use alloca pointer with XRESIZEVEC. */ 942 driver = NULL; 943 /* Look in all COMPILER_PATHs for GCC_INSTALL_NAME. */ 944 char **paths = NULL; 945 unsigned n_paths; 946 n_paths = parse_env_var (getenv ("COMPILER_PATH"), &paths); 947 for (unsigned i = 0; i < n_paths; i++) 948 { 949 len = strlen (paths[i]) + 1 + strlen (GCC_INSTALL_NAME) + 1; 950 driver = XRESIZEVEC (char, driver, len); 951 sprintf (driver, "%s/%s", paths[i], GCC_INSTALL_NAME); 952 if (access_check (driver, X_OK) == 0) 953 { 954 found = true; 955 break; 956 } 957 } 958 free_array_of_ptrs ((void **) paths, n_paths); 959 } 960 961 if (!found) 962 fatal_error (input_location, 963 "offload compiler %s not found", GCC_INSTALL_NAME); 964 965 /* We may be called with all the arguments stored in some file and 966 passed with @file. Expand them into argv before processing. */ 967 expandargv (&argc, &argv); 968 969 /* Find out whether we should compile binaries for i386 or x86-64. */ 970 for (int i = argc - 1; i > 0; i--) 971 if (strncmp (argv[i], "-foffload-abi=", sizeof ("-foffload-abi=") - 1) == 0) 972 { 973 if (strstr (argv[i], "ilp32")) 974 target_ilp32 = true; 975 else if (!strstr (argv[i], "lp64")) 976 fatal_error (input_location, 977 "unrecognizable argument of option -foffload-abi"); 978 break; 979 } 980 981 struct obstack argv_obstack; 982 obstack_init (&argv_obstack); 983 obstack_ptr_grow (&argv_obstack, driver); 984 obstack_ptr_grow (&argv_obstack, "-xlto"); 985 obstack_ptr_grow (&argv_obstack, target_ilp32 ? "-m32" : "-m64"); 986 obstack_ptr_grow (&argv_obstack, "-S"); 987 988 for (int ix = 1; ix != argc; ix++) 989 { 990 if (!strcmp (argv[ix], "-o") && ix + 1 != argc) 991 outname = argv[++ix]; 992 else 993 obstack_ptr_grow (&argv_obstack, argv[ix]); 994 } 995 996 ptx_cfile_name = make_temp_file (".c"); 997 998 out = fopen (ptx_cfile_name, "w"); 999 if (!out) 1000 fatal_error (input_location, "cannot open '%s'", ptx_cfile_name); 1001 1002 /* PR libgomp/65099: Currently, we only support offloading in 64-bit 1003 configurations. */ 1004 if (!target_ilp32) 1005 { 1006 ptx_name = make_temp_file (".mkoffload"); 1007 obstack_ptr_grow (&argv_obstack, "-o"); 1008 obstack_ptr_grow (&argv_obstack, ptx_name); 1009 obstack_ptr_grow (&argv_obstack, NULL); 1010 const char **new_argv = XOBFINISH (&argv_obstack, const char **); 1011 1012 char *execpath = getenv ("GCC_EXEC_PREFIX"); 1013 char *cpath = getenv ("COMPILER_PATH"); 1014 char *lpath = getenv ("LIBRARY_PATH"); 1015 unsetenv ("GCC_EXEC_PREFIX"); 1016 unsetenv ("COMPILER_PATH"); 1017 unsetenv ("LIBRARY_PATH"); 1018 1019 fork_execute (new_argv[0], CONST_CAST (char **, new_argv), true); 1020 obstack_free (&argv_obstack, NULL); 1021 1022 xputenv (concat ("GCC_EXEC_PREFIX=", execpath, NULL)); 1023 xputenv (concat ("COMPILER_PATH=", cpath, NULL)); 1024 xputenv (concat ("LIBRARY_PATH=", lpath, NULL)); 1025 1026 in = fopen (ptx_name, "r"); 1027 if (!in) 1028 fatal_error (input_location, "cannot open intermediate ptx file"); 1029 1030 process (in, out); 1031 } 1032 1033 fclose (out); 1034 1035 compile_native (ptx_cfile_name, outname, collect_gcc); 1036 1037 utils_cleanup (false); 1038 1039 return 0; 1040} 1041