1 /* Offload image generation tool for PTX. 2 3 Copyright (C) 2014-2019 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 #define IN_TARGET_CODE 1 31 32 #include "config.h" 33 #include "system.h" 34 #include "coretypes.h" 35 #include "obstack.h" 36 #include "diagnostic.h" 37 #include "intl.h" 38 #include <libgen.h> 39 #include "collect-utils.h" 40 #include "gomp-constants.h" 41 42 const char tool_name[] = "nvptx mkoffload"; 43 44 #define COMMENT_PREFIX "#" 45 46 struct id_map 47 { 48 id_map *next; 49 char *ptx_name; 50 }; 51 52 static id_map *func_ids, **funcs_tail = &func_ids; 53 static id_map *var_ids, **vars_tail = &var_ids; 54 55 /* Files to unlink. */ 56 static const char *ptx_name; 57 static const char *ptx_cfile_name; 58 59 enum offload_abi offload_abi = OFFLOAD_ABI_UNSET; 60 61 /* Delete tempfiles. */ 62 63 void 64 tool_cleanup (bool from_signal ATTRIBUTE_UNUSED) 65 { 66 if (ptx_cfile_name) 67 maybe_unlink (ptx_cfile_name); 68 if (ptx_name) 69 maybe_unlink (ptx_name); 70 } 71 72 static void 73 mkoffload_cleanup (void) 74 { 75 tool_cleanup (false); 76 } 77 78 /* Unlink FILE unless requested otherwise. */ 79 80 void 81 maybe_unlink (const char *file) 82 { 83 if (!save_temps) 84 { 85 if (unlink_if_ordinary (file) 86 && errno != ENOENT) 87 fatal_error (input_location, "deleting file %s: %m", file); 88 } 89 else if (verbose) 90 fprintf (stderr, "[Leaving %s]\n", file); 91 } 92 93 /* Add or change the value of an environment variable, outputting the 94 change to standard error if in verbose mode. */ 95 static void 96 xputenv (const char *string) 97 { 98 if (verbose) 99 fprintf (stderr, "%s\n", string); 100 putenv (CONST_CAST (char *, string)); 101 } 102 103 104 static void 105 record_id (const char *p1, id_map ***where) 106 { 107 const char *end = strchr (p1, '\n'); 108 if (!end) 109 fatal_error (input_location, "malformed ptx file"); 110 111 id_map *v = XNEW (id_map); 112 size_t len = end - p1; 113 v->ptx_name = XNEWVEC (char, len + 1); 114 memcpy (v->ptx_name, p1, len); 115 v->ptx_name[len] = '\0'; 116 v->next = NULL; 117 id_map **tail = *where; 118 *tail = v; 119 *where = &v->next; 120 } 121 122 /* Read the whole input file. It will be NUL terminated (but 123 remember, there could be a NUL in the file itself. */ 124 125 static const char * 126 read_file (FILE *stream, size_t *plen) 127 { 128 size_t alloc = 16384; 129 size_t base = 0; 130 char *buffer; 131 132 if (!fseek (stream, 0, SEEK_END)) 133 { 134 /* Get the file size. */ 135 long s = ftell (stream); 136 if (s >= 0) 137 alloc = s + 100; 138 fseek (stream, 0, SEEK_SET); 139 } 140 buffer = XNEWVEC (char, alloc); 141 142 for (;;) 143 { 144 size_t n = fread (buffer + base, 1, alloc - base - 1, stream); 145 146 if (!n) 147 break; 148 base += n; 149 if (base + 1 == alloc) 150 { 151 alloc *= 2; 152 buffer = XRESIZEVEC (char, buffer, alloc); 153 } 154 } 155 buffer[base] = 0; 156 *plen = base; 157 return buffer; 158 } 159 160 /* Parse STR, saving found tokens into PVALUES and return their number. 161 Tokens are assumed to be delimited by ':'. */ 162 static unsigned 163 parse_env_var (const char *str, char ***pvalues) 164 { 165 const char *curval, *nextval; 166 char **values; 167 unsigned num = 1, i; 168 169 curval = strchr (str, ':'); 170 while (curval) 171 { 172 num++; 173 curval = strchr (curval + 1, ':'); 174 } 175 176 values = (char **) xmalloc (num * sizeof (char *)); 177 curval = str; 178 nextval = strchr (curval, ':'); 179 if (nextval == NULL) 180 nextval = strchr (curval, '\0'); 181 182 for (i = 0; i < num; i++) 183 { 184 int l = nextval - curval; 185 values[i] = (char *) xmalloc (l + 1); 186 memcpy (values[i], curval, l); 187 values[i][l] = 0; 188 curval = nextval + 1; 189 nextval = strchr (curval, ':'); 190 if (nextval == NULL) 191 nextval = strchr (curval, '\0'); 192 } 193 *pvalues = values; 194 return num; 195 } 196 197 /* Auxiliary function that frees elements of PTR and PTR itself. 198 N is number of elements to be freed. If PTR is NULL, nothing is freed. 199 If an element is NULL, subsequent elements are not freed. */ 200 static void 201 free_array_of_ptrs (void **ptr, unsigned n) 202 { 203 unsigned i; 204 if (!ptr) 205 return; 206 for (i = 0; i < n; i++) 207 { 208 if (!ptr[i]) 209 break; 210 free (ptr[i]); 211 } 212 free (ptr); 213 return; 214 } 215 216 /* Check whether NAME can be accessed in MODE. This is like access, 217 except that it never considers directories to be executable. */ 218 static int 219 access_check (const char *name, int mode) 220 { 221 if (mode == X_OK) 222 { 223 struct stat st; 224 225 if (stat (name, &st) < 0 || S_ISDIR (st.st_mode)) 226 return -1; 227 } 228 229 return access (name, mode); 230 } 231 232 static void 233 process (FILE *in, FILE *out) 234 { 235 size_t len = 0; 236 const char *input = read_file (in, &len); 237 const char *comma; 238 id_map const *id; 239 unsigned obj_count = 0; 240 unsigned ix; 241 242 /* Dump out char arrays for each PTX object file. These are 243 terminated by a NUL. */ 244 for (size_t i = 0; i != len;) 245 { 246 char c; 247 248 fprintf (out, "static const char ptx_code_%u[] =\n\t\"", obj_count++); 249 while ((c = input[i++])) 250 { 251 switch (c) 252 { 253 case '\r': 254 continue; 255 case '\n': 256 fprintf (out, "\\n\"\n\t\""); 257 /* Look for mappings on subsequent lines. */ 258 while (strncmp (input + i, "//:", 3) == 0) 259 { 260 i += 3; 261 262 if (strncmp (input + i, "VAR_MAP ", 8) == 0) 263 record_id (input + i + 8, &vars_tail); 264 else if (strncmp (input + i, "FUNC_MAP ", 9) == 0) 265 record_id (input + i + 9, &funcs_tail); 266 else 267 abort (); 268 /* Skip to next line. */ 269 while (input[i++] != '\n') 270 continue; 271 } 272 continue; 273 case '"': 274 case '\\': 275 putc ('\\', out); 276 break; 277 default: 278 break; 279 } 280 putc (c, out); 281 } 282 fprintf (out, "\";\n\n"); 283 } 284 285 /* Dump out array of pointers to ptx object strings. */ 286 fprintf (out, "static const struct ptx_obj {\n" 287 " const char *code;\n" 288 " __SIZE_TYPE__ size;\n" 289 "} ptx_objs[] = {"); 290 for (comma = "", ix = 0; ix != obj_count; comma = ",", ix++) 291 fprintf (out, "%s\n\t{ptx_code_%u, sizeof (ptx_code_%u)}", comma, ix, ix); 292 fprintf (out, "\n};\n\n"); 293 294 /* Dump out variable idents. */ 295 fprintf (out, "static const char *const var_mappings[] = {"); 296 for (comma = "", id = var_ids; id; comma = ",", id = id->next) 297 fprintf (out, "%s\n\t%s", comma, id->ptx_name); 298 fprintf (out, "\n};\n\n"); 299 300 /* Dump out function idents. */ 301 fprintf (out, "static const struct nvptx_fn {\n" 302 " const char *name;\n" 303 " unsigned short dim[%d];\n" 304 "} func_mappings[] = {\n", GOMP_DIM_MAX); 305 for (comma = "", id = func_ids; id; comma = ",", id = id->next) 306 fprintf (out, "%s\n\t{%s}", comma, id->ptx_name); 307 fprintf (out, "\n};\n\n"); 308 309 fprintf (out, 310 "static const struct nvptx_tdata {\n" 311 " const struct ptx_obj *ptx_objs;\n" 312 " unsigned ptx_num;\n" 313 " const char *const *var_names;\n" 314 " unsigned var_num;\n" 315 " const struct nvptx_fn *fn_names;\n" 316 " unsigned fn_num;\n" 317 "} target_data = {\n" 318 " ptx_objs, sizeof (ptx_objs) / sizeof (ptx_objs[0]),\n" 319 " var_mappings," 320 " sizeof (var_mappings) / sizeof (var_mappings[0]),\n" 321 " func_mappings," 322 " sizeof (func_mappings) / sizeof (func_mappings[0])\n" 323 "};\n\n"); 324 325 fprintf (out, "#ifdef __cplusplus\n" 326 "extern \"C\" {\n" 327 "#endif\n"); 328 329 fprintf (out, "extern void GOMP_offload_register_ver" 330 " (unsigned, const void *, int, const void *);\n"); 331 fprintf (out, "extern void GOMP_offload_unregister_ver" 332 " (unsigned, const void *, int, const void *);\n"); 333 334 fprintf (out, "#ifdef __cplusplus\n" 335 "}\n" 336 "#endif\n"); 337 338 fprintf (out, "extern const void *const __OFFLOAD_TABLE__[];\n\n"); 339 340 fprintf (out, "static __attribute__((constructor)) void init (void)\n" 341 "{\n" 342 " GOMP_offload_register_ver (%#x, __OFFLOAD_TABLE__," 343 " %d/*NVIDIA_PTX*/, &target_data);\n" 344 "};\n", 345 GOMP_VERSION_PACK (GOMP_VERSION, GOMP_VERSION_NVIDIA_PTX), 346 GOMP_DEVICE_NVIDIA_PTX); 347 348 fprintf (out, "static __attribute__((destructor)) void fini (void)\n" 349 "{\n" 350 " GOMP_offload_unregister_ver (%#x, __OFFLOAD_TABLE__," 351 " %d/*NVIDIA_PTX*/, &target_data);\n" 352 "};\n", 353 GOMP_VERSION_PACK (GOMP_VERSION, GOMP_VERSION_NVIDIA_PTX), 354 GOMP_DEVICE_NVIDIA_PTX); 355 } 356 357 static void 358 compile_native (const char *infile, const char *outfile, const char *compiler) 359 { 360 const char *collect_gcc_options = getenv ("COLLECT_GCC_OPTIONS"); 361 if (!collect_gcc_options) 362 fatal_error (input_location, 363 "environment variable COLLECT_GCC_OPTIONS must be set"); 364 365 struct obstack argv_obstack; 366 obstack_init (&argv_obstack); 367 obstack_ptr_grow (&argv_obstack, compiler); 368 if (save_temps) 369 obstack_ptr_grow (&argv_obstack, "-save-temps"); 370 if (verbose) 371 obstack_ptr_grow (&argv_obstack, "-v"); 372 switch (offload_abi) 373 { 374 case OFFLOAD_ABI_LP64: 375 obstack_ptr_grow (&argv_obstack, "-m64"); 376 break; 377 case OFFLOAD_ABI_ILP32: 378 obstack_ptr_grow (&argv_obstack, "-m32"); 379 break; 380 default: 381 gcc_unreachable (); 382 } 383 obstack_ptr_grow (&argv_obstack, infile); 384 obstack_ptr_grow (&argv_obstack, "-c"); 385 obstack_ptr_grow (&argv_obstack, "-o"); 386 obstack_ptr_grow (&argv_obstack, outfile); 387 obstack_ptr_grow (&argv_obstack, NULL); 388 389 const char **new_argv = XOBFINISH (&argv_obstack, const char **); 390 fork_execute (new_argv[0], CONST_CAST (char **, new_argv), true); 391 obstack_free (&argv_obstack, NULL); 392 } 393 394 int 395 main (int argc, char **argv) 396 { 397 FILE *in = stdin; 398 FILE *out = stdout; 399 const char *outname = 0; 400 401 progname = "mkoffload"; 402 diagnostic_initialize (global_dc, 0); 403 404 if (atexit (mkoffload_cleanup) != 0) 405 fatal_error (input_location, "atexit failed"); 406 407 char *collect_gcc = getenv ("COLLECT_GCC"); 408 if (collect_gcc == NULL) 409 fatal_error (input_location, "COLLECT_GCC must be set."); 410 const char *gcc_path = dirname (ASTRDUP (collect_gcc)); 411 const char *gcc_exec = basename (ASTRDUP (collect_gcc)); 412 413 size_t len = (strlen (gcc_path) + 1 414 + strlen (GCC_INSTALL_NAME) 415 + 1); 416 char *driver = XALLOCAVEC (char, len); 417 418 if (strcmp (gcc_exec, collect_gcc) == 0) 419 /* collect_gcc has no path, so it was found in PATH. Make sure we also 420 find accel-gcc in PATH. */ 421 gcc_path = NULL; 422 423 int driver_used = 0; 424 if (gcc_path != NULL) 425 driver_used = sprintf (driver, "%s/", gcc_path); 426 sprintf (driver + driver_used, "%s", GCC_INSTALL_NAME); 427 428 bool found = false; 429 if (gcc_path == NULL) 430 found = true; 431 else if (access_check (driver, X_OK) == 0) 432 found = true; 433 else 434 { 435 /* Don't use alloca pointer with XRESIZEVEC. */ 436 driver = NULL; 437 /* Look in all COMPILER_PATHs for GCC_INSTALL_NAME. */ 438 char **paths = NULL; 439 unsigned n_paths; 440 n_paths = parse_env_var (getenv ("COMPILER_PATH"), &paths); 441 for (unsigned i = 0; i < n_paths; i++) 442 { 443 len = strlen (paths[i]) + 1 + strlen (GCC_INSTALL_NAME) + 1; 444 driver = XRESIZEVEC (char, driver, len); 445 sprintf (driver, "%s/%s", paths[i], GCC_INSTALL_NAME); 446 if (access_check (driver, X_OK) == 0) 447 { 448 found = true; 449 break; 450 } 451 } 452 free_array_of_ptrs ((void **) paths, n_paths); 453 } 454 455 if (!found) 456 fatal_error (input_location, 457 "offload compiler %s not found (consider using %<-B%>)", 458 GCC_INSTALL_NAME); 459 460 /* We may be called with all the arguments stored in some file and 461 passed with @file. Expand them into argv before processing. */ 462 expandargv (&argc, &argv); 463 464 /* Scan the argument vector. */ 465 bool fopenmp = false; 466 bool fopenacc = false; 467 for (int i = 1; i < argc; i++) 468 { 469 #define STR "-foffload-abi=" 470 if (strncmp (argv[i], STR, strlen (STR)) == 0) 471 { 472 if (strcmp (argv[i] + strlen (STR), "lp64") == 0) 473 offload_abi = OFFLOAD_ABI_LP64; 474 else if (strcmp (argv[i] + strlen (STR), "ilp32") == 0) 475 offload_abi = OFFLOAD_ABI_ILP32; 476 else 477 fatal_error (input_location, 478 "unrecognizable argument of option " STR); 479 } 480 #undef STR 481 else if (strcmp (argv[i], "-fopenmp") == 0) 482 fopenmp = true; 483 else if (strcmp (argv[i], "-fopenacc") == 0) 484 fopenacc = true; 485 else if (strcmp (argv[i], "-save-temps") == 0) 486 save_temps = true; 487 else if (strcmp (argv[i], "-v") == 0) 488 verbose = true; 489 } 490 if (!(fopenacc ^ fopenmp)) 491 fatal_error (input_location, "either %<-fopenacc%> or %<-fopenmp%> " 492 "must be set"); 493 494 struct obstack argv_obstack; 495 obstack_init (&argv_obstack); 496 obstack_ptr_grow (&argv_obstack, driver); 497 if (save_temps) 498 obstack_ptr_grow (&argv_obstack, "-save-temps"); 499 if (verbose) 500 obstack_ptr_grow (&argv_obstack, "-v"); 501 obstack_ptr_grow (&argv_obstack, "-xlto"); 502 switch (offload_abi) 503 { 504 case OFFLOAD_ABI_LP64: 505 obstack_ptr_grow (&argv_obstack, "-m64"); 506 break; 507 case OFFLOAD_ABI_ILP32: 508 obstack_ptr_grow (&argv_obstack, "-m32"); 509 break; 510 default: 511 gcc_unreachable (); 512 } 513 if (fopenmp) 514 obstack_ptr_grow (&argv_obstack, "-mgomp"); 515 516 for (int ix = 1; ix != argc; ix++) 517 { 518 if (!strcmp (argv[ix], "-o") && ix + 1 != argc) 519 outname = argv[++ix]; 520 else 521 obstack_ptr_grow (&argv_obstack, argv[ix]); 522 } 523 524 ptx_cfile_name = make_temp_file (".c"); 525 526 out = fopen (ptx_cfile_name, "w"); 527 if (!out) 528 fatal_error (input_location, "cannot open '%s'", ptx_cfile_name); 529 530 /* PR libgomp/65099: Currently, we only support offloading in 64-bit 531 configurations. */ 532 if (offload_abi == OFFLOAD_ABI_LP64) 533 { 534 ptx_name = make_temp_file (".mkoffload"); 535 obstack_ptr_grow (&argv_obstack, "-o"); 536 obstack_ptr_grow (&argv_obstack, ptx_name); 537 obstack_ptr_grow (&argv_obstack, NULL); 538 const char **new_argv = XOBFINISH (&argv_obstack, const char **); 539 540 char *execpath = getenv ("GCC_EXEC_PREFIX"); 541 char *cpath = getenv ("COMPILER_PATH"); 542 char *lpath = getenv ("LIBRARY_PATH"); 543 unsetenv ("GCC_EXEC_PREFIX"); 544 unsetenv ("COMPILER_PATH"); 545 unsetenv ("LIBRARY_PATH"); 546 547 fork_execute (new_argv[0], CONST_CAST (char **, new_argv), true); 548 obstack_free (&argv_obstack, NULL); 549 550 xputenv (concat ("GCC_EXEC_PREFIX=", execpath, NULL)); 551 xputenv (concat ("COMPILER_PATH=", cpath, NULL)); 552 xputenv (concat ("LIBRARY_PATH=", lpath, NULL)); 553 554 in = fopen (ptx_name, "r"); 555 if (!in) 556 fatal_error (input_location, "cannot open intermediate ptx file"); 557 558 process (in, out); 559 } 560 561 fclose (out); 562 563 compile_native (ptx_cfile_name, outname, collect_gcc); 564 565 return 0; 566 } 567