1 /* Offload image generation tool for AMD GCN.
2 
3    Copyright (C) 2014-2020 Free Software Foundation, Inc.
4 
5    This file is part of GCC.
6 
7    GCC is free software; you can redistribute it and/or modify it
8    under the terms of the GNU General Public License as published
9    by the Free Software Foundation; either version 3, or (at your
10    option) any later version.
11 
12    GCC is distributed in the hope that it will be useful, but WITHOUT
13    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
15    License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with GCC; see the file COPYING3.  If not see
19    <http://www.gnu.org/licenses/>.  */
20 
21 /* Munges GCN assembly into a C source file defining the GCN code as a
22    string.
23 
24    This is not a complete assembler.  We presume the source is well
25    formed from the compiler and can die horribly if it is not.  */
26 
27 #include "config.h"
28 #include "system.h"
29 #include "coretypes.h"
30 #include "obstack.h"
31 #include "diagnostic.h"
32 #include "intl.h"
33 #include <libgen.h>
34 #include "collect-utils.h"
35 #include "gomp-constants.h"
36 
37 const char tool_name[] = "gcn mkoffload";
38 
39 /* Files to unlink.  */
40 static const char *gcn_s1_name;
41 static const char *gcn_s2_name;
42 static const char *gcn_o_name;
43 static const char *gcn_cfile_name;
44 
45 enum offload_abi offload_abi = OFFLOAD_ABI_UNSET;
46 
47 /* Delete tempfiles.  */
48 
49 void
tool_cleanup(bool from_signal ATTRIBUTE_UNUSED)50 tool_cleanup (bool from_signal ATTRIBUTE_UNUSED)
51 {
52   if (gcn_cfile_name)
53     maybe_unlink (gcn_cfile_name);
54   if (gcn_s1_name)
55     maybe_unlink (gcn_s1_name);
56   if (gcn_s2_name)
57     maybe_unlink (gcn_s2_name);
58   if (gcn_o_name)
59     maybe_unlink (gcn_o_name);
60 }
61 
62 static void
mkoffload_cleanup(void)63 mkoffload_cleanup (void)
64 {
65   tool_cleanup (false);
66 }
67 
68 /* Unlink FILE unless requested otherwise.  */
69 
70 void
maybe_unlink(const char * file)71 maybe_unlink (const char *file)
72 {
73   if (!save_temps)
74     {
75       if (unlink_if_ordinary (file) && errno != ENOENT)
76 	fatal_error (input_location, "deleting file %s: %m", file);
77     }
78   else if (verbose)
79     fprintf (stderr, "[Leaving %s]\n", file);
80 }
81 
82 /* Add or change the value of an environment variable, outputting the
83    change to standard error if in verbose mode.  */
84 
85 static void
xputenv(const char * string)86 xputenv (const char *string)
87 {
88   if (verbose)
89     fprintf (stderr, "%s\n", string);
90   putenv (CONST_CAST (char *, string));
91 }
92 
93 /* Read the whole input file.  It will be NUL terminated (but
94    remember, there could be a NUL in the file itself.  */
95 
96 static const char *
read_file(FILE * stream,size_t * plen)97 read_file (FILE *stream, size_t *plen)
98 {
99   size_t alloc = 16384;
100   size_t base = 0;
101   char *buffer;
102 
103   if (!fseek (stream, 0, SEEK_END))
104     {
105       /* Get the file size.  */
106       long s = ftell (stream);
107       if (s >= 0)
108 	alloc = s + 100;
109       fseek (stream, 0, SEEK_SET);
110     }
111   buffer = XNEWVEC (char, alloc);
112 
113   for (;;)
114     {
115       size_t n = fread (buffer + base, 1, alloc - base - 1, stream);
116 
117       if (!n)
118 	break;
119       base += n;
120       if (base + 1 == alloc)
121 	{
122 	  alloc *= 2;
123 	  buffer = XRESIZEVEC (char, buffer, alloc);
124 	}
125     }
126   buffer[base] = 0;
127   *plen = base;
128   return buffer;
129 }
130 
131 /* Parse STR, saving found tokens into PVALUES and return their number.
132    Tokens are assumed to be delimited by ':'.  */
133 
134 static unsigned
parse_env_var(const char * str,char *** pvalues)135 parse_env_var (const char *str, char ***pvalues)
136 {
137   const char *curval, *nextval;
138   char **values;
139   unsigned num = 1, i;
140 
141   curval = strchr (str, ':');
142   while (curval)
143     {
144       num++;
145       curval = strchr (curval + 1, ':');
146     }
147 
148   values = (char **) xmalloc (num * sizeof (char *));
149   curval = str;
150   nextval = strchr (curval, ':');
151   if (nextval == NULL)
152     nextval = strchr (curval, '\0');
153 
154   for (i = 0; i < num; i++)
155     {
156       int l = nextval - curval;
157       values[i] = (char *) xmalloc (l + 1);
158       memcpy (values[i], curval, l);
159       values[i][l] = 0;
160       curval = nextval + 1;
161       nextval = strchr (curval, ':');
162       if (nextval == NULL)
163 	nextval = strchr (curval, '\0');
164     }
165   *pvalues = values;
166   return num;
167 }
168 
169 /* Auxiliary function that frees elements of PTR and PTR itself.
170    N is number of elements to be freed.  If PTR is NULL, nothing is freed.
171    If an element is NULL, subsequent elements are not freed.  */
172 
173 static void
free_array_of_ptrs(void ** ptr,unsigned n)174 free_array_of_ptrs (void **ptr, unsigned n)
175 {
176   unsigned i;
177   if (!ptr)
178     return;
179   for (i = 0; i < n; i++)
180     {
181       if (!ptr[i])
182 	break;
183       free (ptr[i]);
184     }
185   free (ptr);
186   return;
187 }
188 
189 /* Check whether NAME can be accessed in MODE.  This is like access,
190    except that it never considers directories to be executable.  */
191 
192 static int
access_check(const char * name,int mode)193 access_check (const char *name, int mode)
194 {
195   if (mode == X_OK)
196     {
197       struct stat st;
198 
199       if (stat (name, &st) < 0 || S_ISDIR (st.st_mode))
200 	return -1;
201     }
202 
203   return access (name, mode);
204 }
205 
206 /* Parse an input assembler file, extract the offload tables etc.,
207    and output (1) the assembler code, minus the tables (which can contain
208    problematic relocations), and (2) a C file with the offload tables
209    encoded as structured data.  */
210 
211 static void
process_asm(FILE * in,FILE * out,FILE * cfile)212 process_asm (FILE *in, FILE *out, FILE *cfile)
213 {
214   int fn_count = 0, var_count = 0, dims_count = 0, regcount_count = 0;
215   struct obstack fns_os, vars_os, varsizes_os, dims_os, regcounts_os;
216   obstack_init (&fns_os);
217   obstack_init (&vars_os);
218   obstack_init (&varsizes_os);
219   obstack_init (&dims_os);
220   obstack_init (&regcounts_os);
221 
222   struct oaccdims
223   {
224     int d[3];
225     char *name;
226   } dim;
227 
228   struct regcount
229   {
230     int sgpr_count;
231     int vgpr_count;
232     char *kernel_name;
233   } regcount;
234 
235   /* Always add _init_array and _fini_array as kernels.  */
236   obstack_ptr_grow (&fns_os, xstrdup ("_init_array"));
237   obstack_ptr_grow (&fns_os, xstrdup ("_fini_array"));
238   fn_count += 2;
239 
240   char buf[1000];
241   enum { IN_CODE, IN_AMD_KERNEL_CODE_T, IN_VARS, IN_FUNCS } state = IN_CODE;
242   while (fgets (buf, sizeof (buf), in))
243     {
244       switch (state)
245 	{
246 	case IN_CODE:
247 	  {
248 	    if (sscanf (buf, " ;; OPENACC-DIMS: %d, %d, %d : %ms\n",
249 			&dim.d[0], &dim.d[1], &dim.d[2], &dim.name) == 4)
250 	      {
251 		obstack_grow (&dims_os, &dim, sizeof (dim));
252 		dims_count++;
253 	      }
254 	    else if (sscanf (buf, " .amdgpu_hsa_kernel %ms\n",
255 			     &regcount.kernel_name) == 1)
256 	      break;
257 
258 	    break;
259 	  }
260 	case IN_AMD_KERNEL_CODE_T:
261 	  {
262 	    gcc_assert (regcount.kernel_name);
263 	    if (sscanf (buf, " wavefront_sgpr_count = %d\n",
264 			&regcount.sgpr_count) == 1)
265 	      break;
266 	    else if (sscanf (buf, " workitem_vgpr_count = %d\n",
267 			     &regcount.vgpr_count) == 1)
268 	      break;
269 
270 	    break;
271 	  }
272 	case IN_VARS:
273 	  {
274 	    char *varname;
275 	    unsigned varsize;
276 	    if (sscanf (buf, " .8byte %ms\n", &varname))
277 	      {
278 		obstack_ptr_grow (&vars_os, varname);
279 		fgets (buf, sizeof (buf), in);
280 		if (!sscanf (buf, " .8byte %u\n", &varsize))
281 		  abort ();
282 		obstack_int_grow (&varsizes_os, varsize);
283 		var_count++;
284 
285 		/* The HSA Runtime cannot locate the symbol if it is not
286 		   exported from the kernel.  */
287 		fprintf (out, "\t.global %s\n", varname);
288 	      }
289 	    break;
290 	  }
291 	case IN_FUNCS:
292 	  {
293 	    char *funcname;
294 	    if (sscanf (buf, "\t.8byte\t%ms\n", &funcname))
295 	      {
296 		obstack_ptr_grow (&fns_os, funcname);
297 		fn_count++;
298 		continue;
299 	      }
300 	    break;
301 	  }
302 	}
303 
304       char dummy;
305       if (sscanf (buf, " .section .gnu.offload_vars%c", &dummy) > 0)
306 	state = IN_VARS;
307       else if (sscanf (buf, " .section .gnu.offload_funcs%c", &dummy) > 0)
308 	state = IN_FUNCS;
309       else if (sscanf (buf, " .amd_kernel_code_%c", &dummy) > 0)
310 	{
311 	  state = IN_AMD_KERNEL_CODE_T;
312 	  regcount.sgpr_count = regcount.vgpr_count = -1;
313 	}
314       else if (sscanf (buf, " .section %c", &dummy) > 0
315 	       || sscanf (buf, " .text%c", &dummy) > 0
316 	       || sscanf (buf, " .bss%c", &dummy) > 0
317 	       || sscanf (buf, " .data%c", &dummy) > 0
318 	       || sscanf (buf, " .ident %c", &dummy) > 0)
319 	state = IN_CODE;
320       else if (sscanf (buf, " .end_amd_kernel_code_%c", &dummy) > 0)
321 	{
322 	  state = IN_CODE;
323 	  gcc_assert (regcount.kernel_name != NULL
324 		      && regcount.sgpr_count >= 0
325 		      && regcount.vgpr_count >= 0);
326 	  obstack_grow (&regcounts_os, &regcount, sizeof (regcount));
327 	  regcount_count++;
328 	  regcount.kernel_name = NULL;
329 	  regcount.sgpr_count = regcount.vgpr_count = -1;
330 	}
331 
332       if (state == IN_CODE || state == IN_AMD_KERNEL_CODE_T)
333 	fputs (buf, out);
334     }
335 
336   char **fns = XOBFINISH (&fns_os, char **);
337   struct oaccdims *dims = XOBFINISH (&dims_os, struct oaccdims *);
338   struct regcount *regcounts = XOBFINISH (&regcounts_os, struct regcount *);
339 
340   fprintf (cfile, "#include <stdlib.h>\n");
341   fprintf (cfile, "#include <stdbool.h>\n\n");
342 
343   char **vars = XOBFINISH (&vars_os, char **);
344   unsigned *varsizes = XOBFINISH (&varsizes_os, unsigned *);
345   fprintf (cfile,
346 	   "static const struct global_var_info {\n"
347 	   "  const char *name;\n"
348 	   "  void *address;\n"
349 	   "} vars[] = {\n");
350   int i;
351   for (i = 0; i < var_count; ++i)
352     {
353       const char *sep = i < var_count - 1 ? "," : " ";
354       fprintf (cfile, "  { \"%s\", NULL }%s /* size: %u */\n", vars[i], sep,
355 	       varsizes[i]);
356     }
357   fprintf (cfile, "};\n\n");
358 
359   obstack_free (&vars_os, NULL);
360   obstack_free (&varsizes_os, NULL);
361 
362   /* Dump out function idents.  */
363   fprintf (cfile, "static const struct hsa_kernel_description {\n"
364 	   "  const char *name;\n"
365 	   "  int oacc_dims[3];\n"
366 	   "  int sgpr_count;\n"
367 	   "  int vgpr_count;\n"
368 	   "} gcn_kernels[] = {\n  ");
369   dim.d[0] = dim.d[1] = dim.d[2] = 0;
370   const char *comma;
371   for (comma = "", i = 0; i < fn_count; comma = ",\n  ", i++)
372     {
373       /* Find if we recorded dimensions for this function.  */
374       int *d = dim.d;		/* Previously zeroed.  */
375       int sgpr_count = 0;
376       int vgpr_count = 0;
377       for (int j = 0; j < dims_count; j++)
378 	if (strcmp (fns[i], dims[j].name) == 0)
379 	  {
380 	    d = dims[j].d;
381 	    break;
382 	  }
383       for (int j = 0; j < regcount_count; j++)
384 	if (strcmp (fns[i], regcounts[j].kernel_name) == 0)
385 	  {
386 	    sgpr_count = regcounts[j].sgpr_count;
387 	    vgpr_count = regcounts[j].vgpr_count;
388 	    break;
389 	  }
390 
391       fprintf (cfile, "%s{\"%s\", {%d, %d, %d}, %d, %d}", comma,
392 	       fns[i], d[0], d[1], d[2], sgpr_count, vgpr_count);
393 
394       free (fns[i]);
395     }
396   fprintf (cfile, "\n};\n\n");
397 
398   obstack_free (&fns_os, NULL);
399   for (i = 0; i < dims_count; i++)
400     free (dims[i].name);
401   for (i = 0; i < regcount_count; i++)
402     free (regcounts[i].kernel_name);
403   obstack_free (&dims_os, NULL);
404   obstack_free (&regcounts_os, NULL);
405 }
406 
407 /* Embed an object file into a C source file.  */
408 
409 static void
process_obj(FILE * in,FILE * cfile)410 process_obj (FILE *in, FILE *cfile)
411 {
412   size_t len = 0;
413   const char *input = read_file (in, &len);
414 
415   /* Dump out an array containing the binary.
416      FIXME: do this with objcopy.  */
417   fprintf (cfile, "static unsigned char gcn_code[] = {");
418   for (size_t i = 0; i < len; i += 17)
419     {
420       fprintf (cfile, "\n\t");
421       for (size_t j = i; j < i + 17 && j < len; j++)
422 	fprintf (cfile, "%3u,", (unsigned char) input[j]);
423     }
424   fprintf (cfile, "\n};\n\n");
425 
426   fprintf (cfile,
427 	   "static const struct gcn_image {\n"
428 	   "  size_t size;\n"
429 	   "  void *image;\n"
430 	   "} gcn_image = {\n"
431 	   "  %zu,\n"
432 	   "  gcn_code\n"
433 	   "};\n\n",
434 	   len);
435 
436   fprintf (cfile,
437 	   "static const struct gcn_image_desc {\n"
438 	   "  const struct gcn_image *gcn_image;\n"
439 	   "  unsigned kernel_count;\n"
440 	   "  const struct hsa_kernel_description *kernel_infos;\n"
441 	   "  unsigned global_variable_count;\n"
442 	   "  const struct global_var_info *global_variables;\n"
443 	   "} target_data = {\n"
444 	   "  &gcn_image,\n"
445 	   "  sizeof (gcn_kernels) / sizeof (gcn_kernels[0]),\n"
446 	   "  gcn_kernels,\n"
447 	   "  sizeof (vars) / sizeof (vars[0]),\n"
448 	   "  vars\n"
449 	   "};\n\n");
450 
451   fprintf (cfile,
452 	   "#ifdef __cplusplus\n"
453 	   "extern \"C\" {\n"
454 	   "#endif\n"
455 	   "extern void GOMP_offload_register_ver"
456 	   " (unsigned, const void *, int, const void *);\n"
457 	   "extern void GOMP_offload_unregister_ver"
458 	   " (unsigned, const void *, int, const void *);\n"
459 	   "#ifdef __cplusplus\n"
460 	   "}\n"
461 	   "#endif\n\n");
462 
463   fprintf (cfile, "extern const void *const __OFFLOAD_TABLE__[];\n\n");
464 
465   fprintf (cfile, "static __attribute__((constructor)) void init (void)\n"
466 	   "{\n"
467 	   "  GOMP_offload_register_ver (%#x, __OFFLOAD_TABLE__,"
468 	   " %d/*GCN*/, &target_data);\n"
469 	   "};\n",
470 	   GOMP_VERSION_PACK (GOMP_VERSION, GOMP_VERSION_GCN),
471 	   GOMP_DEVICE_GCN);
472 
473   fprintf (cfile, "static __attribute__((destructor)) void fini (void)\n"
474 	   "{\n"
475 	   "  GOMP_offload_unregister_ver (%#x, __OFFLOAD_TABLE__,"
476 	   " %d/*GCN*/, &target_data);\n"
477 	   "};\n",
478 	   GOMP_VERSION_PACK (GOMP_VERSION, GOMP_VERSION_GCN),
479 	   GOMP_DEVICE_GCN);
480 }
481 
482 /* Compile a C file using the host compiler.  */
483 
484 static void
compile_native(const char * infile,const char * outfile,const char * compiler)485 compile_native (const char *infile, const char *outfile, const char *compiler)
486 {
487   const char *collect_gcc_options = getenv ("COLLECT_GCC_OPTIONS");
488   if (!collect_gcc_options)
489     fatal_error (input_location,
490 		 "environment variable COLLECT_GCC_OPTIONS must be set");
491 
492   struct obstack argv_obstack;
493   obstack_init (&argv_obstack);
494   obstack_ptr_grow (&argv_obstack, compiler);
495   if (save_temps)
496     obstack_ptr_grow (&argv_obstack, "-save-temps");
497   if (verbose)
498     obstack_ptr_grow (&argv_obstack, "-v");
499   switch (offload_abi)
500     {
501     case OFFLOAD_ABI_LP64:
502       obstack_ptr_grow (&argv_obstack, "-m64");
503       break;
504     case OFFLOAD_ABI_ILP32:
505       obstack_ptr_grow (&argv_obstack, "-m32");
506       break;
507     default:
508       gcc_unreachable ();
509     }
510   obstack_ptr_grow (&argv_obstack, infile);
511   obstack_ptr_grow (&argv_obstack, "-c");
512   obstack_ptr_grow (&argv_obstack, "-o");
513   obstack_ptr_grow (&argv_obstack, outfile);
514   obstack_ptr_grow (&argv_obstack, NULL);
515 
516   const char **new_argv = XOBFINISH (&argv_obstack, const char **);
517   fork_execute (new_argv[0], CONST_CAST (char **, new_argv), true);
518   obstack_free (&argv_obstack, NULL);
519 }
520 
521 int
main(int argc,char ** argv)522 main (int argc, char **argv)
523 {
524   FILE *in = stdin;
525   FILE *out = stdout;
526   FILE *cfile = stdout;
527   const char *outname = 0, *offloadsrc = 0;
528 
529   progname = "mkoffload";
530   diagnostic_initialize (global_dc, 0);
531 
532   if (atexit (mkoffload_cleanup) != 0)
533     fatal_error (input_location, "atexit failed");
534 
535   char *collect_gcc = getenv ("COLLECT_GCC");
536   if (collect_gcc == NULL)
537     fatal_error (input_location, "COLLECT_GCC must be set.");
538   const char *gcc_path = dirname (ASTRDUP (collect_gcc));
539   const char *gcc_exec = basename (ASTRDUP (collect_gcc));
540 
541   size_t len = (strlen (gcc_path) + 1 + strlen (GCC_INSTALL_NAME) + 1);
542   char *driver = XALLOCAVEC (char, len);
543 
544   if (strcmp (gcc_exec, collect_gcc) == 0)
545     /* collect_gcc has no path, so it was found in PATH.  Make sure we also
546        find accel-gcc in PATH.  */
547     gcc_path = NULL;
548 
549   int driver_used = 0;
550   if (gcc_path != NULL)
551     driver_used = sprintf (driver, "%s/", gcc_path);
552   sprintf (driver + driver_used, "%s", GCC_INSTALL_NAME);
553 
554   bool found = false;
555   if (gcc_path == NULL)
556     found = true;
557   else if (access_check (driver, X_OK) == 0)
558     found = true;
559   else
560     {
561       /* Don't use alloca pointer with XRESIZEVEC.  */
562       driver = NULL;
563       /* Look in all COMPILER_PATHs for GCC_INSTALL_NAME.  */
564       char **paths = NULL;
565       unsigned n_paths;
566       n_paths = parse_env_var (getenv ("COMPILER_PATH"), &paths);
567       for (unsigned i = 0; i < n_paths; i++)
568 	{
569 	  len = strlen (paths[i]) + 1 + strlen (GCC_INSTALL_NAME) + 1;
570 	  driver = XRESIZEVEC (char, driver, len);
571 	  sprintf (driver, "%s/%s", paths[i], GCC_INSTALL_NAME);
572 	  if (access_check (driver, X_OK) == 0)
573 	    {
574 	      found = true;
575 	      break;
576 	    }
577 	}
578       free_array_of_ptrs ((void **) paths, n_paths);
579     }
580 
581   if (!found)
582     fatal_error (input_location,
583 		 "offload compiler %s not found", GCC_INSTALL_NAME);
584 
585   /* We may be called with all the arguments stored in some file and
586      passed with @file.  Expand them into argv before processing.  */
587   expandargv (&argc, &argv);
588 
589   /* Scan the argument vector.  */
590   bool fopenmp = false;
591   bool fopenacc = false;
592   for (int i = 1; i < argc; i++)
593     {
594 #define STR "-foffload-abi="
595       if (strncmp (argv[i], STR, strlen (STR)) == 0)
596 	{
597 	  if (strcmp (argv[i] + strlen (STR), "lp64") == 0)
598 	    offload_abi = OFFLOAD_ABI_LP64;
599 	  else if (strcmp (argv[i] + strlen (STR), "ilp32") == 0)
600 	    offload_abi = OFFLOAD_ABI_ILP32;
601 	  else
602 	    fatal_error (input_location,
603 			 "unrecognizable argument of option " STR);
604 	}
605 #undef STR
606       else if (strcmp (argv[i], "-fopenmp") == 0)
607 	fopenmp = true;
608       else if (strcmp (argv[i], "-fopenacc") == 0)
609 	fopenacc = true;
610       else if (strcmp (argv[i], "-save-temps") == 0)
611 	save_temps = true;
612       else if (strcmp (argv[i], "-v") == 0)
613 	verbose = true;
614     }
615   if (!(fopenacc ^ fopenmp))
616     fatal_error (input_location, "either -fopenacc or -fopenmp must be set");
617 
618   const char *abi;
619   switch (offload_abi)
620     {
621     case OFFLOAD_ABI_LP64:
622       abi = "-m64";
623       break;
624     case OFFLOAD_ABI_ILP32:
625       abi = "-m32";
626       break;
627     default:
628       gcc_unreachable ();
629     }
630 
631   /* Build arguments for compiler pass.  */
632   struct obstack cc_argv_obstack;
633   obstack_init (&cc_argv_obstack);
634   obstack_ptr_grow (&cc_argv_obstack, driver);
635   obstack_ptr_grow (&cc_argv_obstack, "-S");
636 
637   if (save_temps)
638     obstack_ptr_grow (&cc_argv_obstack, "-save-temps");
639   if (verbose)
640     obstack_ptr_grow (&cc_argv_obstack, "-v");
641   obstack_ptr_grow (&cc_argv_obstack, abi);
642   obstack_ptr_grow (&cc_argv_obstack, "-xlto");
643   if (fopenmp)
644     obstack_ptr_grow (&cc_argv_obstack, "-mgomp");
645 
646   for (int ix = 1; ix != argc; ix++)
647     {
648       if (!strcmp (argv[ix], "-o") && ix + 1 != argc)
649 	outname = argv[++ix];
650       else
651 	{
652 	  obstack_ptr_grow (&cc_argv_obstack, argv[ix]);
653 
654 	  if (argv[ix][0] != '-')
655 	    offloadsrc = argv[ix];
656 	}
657     }
658 
659   gcn_cfile_name = make_temp_file (".c");
660 
661   cfile = fopen (gcn_cfile_name, "w");
662   if (!cfile)
663     fatal_error (input_location, "cannot open '%s'", gcn_cfile_name);
664 
665   /* Currently, we only support offloading in 64-bit configurations.  */
666   if (offload_abi == OFFLOAD_ABI_LP64)
667     {
668       gcn_s1_name = make_temp_file (".mkoffload.1.s");
669       gcn_s2_name = make_temp_file (".mkoffload.2.s");
670       gcn_o_name = make_temp_file (".mkoffload.hsaco");
671 
672       obstack_ptr_grow (&cc_argv_obstack, "-o");
673       obstack_ptr_grow (&cc_argv_obstack, gcn_s1_name);
674       obstack_ptr_grow (&cc_argv_obstack,
675 			concat ("-mlocal-symbol-id=", offloadsrc, NULL));
676       obstack_ptr_grow (&cc_argv_obstack, NULL);
677       const char **cc_argv = XOBFINISH (&cc_argv_obstack, const char **);
678 
679       /* Build arguments for assemble/link pass.  */
680       struct obstack ld_argv_obstack;
681       obstack_init (&ld_argv_obstack);
682       obstack_ptr_grow (&ld_argv_obstack, driver);
683       obstack_ptr_grow (&ld_argv_obstack, gcn_s2_name);
684       obstack_ptr_grow (&ld_argv_obstack, "-lgomp");
685 
686       for (int i = 1; i < argc; i++)
687 	if (strncmp (argv[i], "-l", 2) == 0
688 	    || strncmp (argv[i], "-Wl", 3) == 0
689 	    || strncmp (argv[i], "-march", 6) == 0)
690 	  obstack_ptr_grow (&ld_argv_obstack, argv[i]);
691 
692       obstack_ptr_grow (&ld_argv_obstack, "-o");
693       obstack_ptr_grow (&ld_argv_obstack, gcn_o_name);
694       obstack_ptr_grow (&ld_argv_obstack, NULL);
695       const char **ld_argv = XOBFINISH (&ld_argv_obstack, const char **);
696 
697       /* Clean up unhelpful environment variables.  */
698       char *execpath = getenv ("GCC_EXEC_PREFIX");
699       char *cpath = getenv ("COMPILER_PATH");
700       char *lpath = getenv ("LIBRARY_PATH");
701       unsetenv ("GCC_EXEC_PREFIX");
702       unsetenv ("COMPILER_PATH");
703       unsetenv ("LIBRARY_PATH");
704 
705       /* Run the compiler pass.  */
706       fork_execute (cc_argv[0], CONST_CAST (char **, cc_argv), true);
707       obstack_free (&cc_argv_obstack, NULL);
708 
709       in = fopen (gcn_s1_name, "r");
710       if (!in)
711 	fatal_error (input_location, "cannot open intermediate gcn asm file");
712 
713       out = fopen (gcn_s2_name, "w");
714       if (!out)
715 	fatal_error (input_location, "cannot open '%s'", gcn_s2_name);
716 
717       process_asm (in, out, cfile);
718 
719       fclose (in);
720       fclose (out);
721 
722       /* Run the assemble/link pass.  */
723       fork_execute (ld_argv[0], CONST_CAST (char **, ld_argv), true);
724       obstack_free (&ld_argv_obstack, NULL);
725 
726       in = fopen (gcn_o_name, "r");
727       if (!in)
728 	fatal_error (input_location, "cannot open intermediate gcn obj file");
729 
730       process_obj (in, cfile);
731 
732       fclose (in);
733 
734       xputenv (concat ("GCC_EXEC_PREFIX=", execpath, NULL));
735       xputenv (concat ("COMPILER_PATH=", cpath, NULL));
736       xputenv (concat ("LIBRARY_PATH=", lpath, NULL));
737     }
738 
739   fclose (cfile);
740 
741   compile_native (gcn_cfile_name, outname, collect_gcc);
742 
743   return 0;
744 }
745