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 (®counts_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 ®count.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 ®count.sgpr_count) == 1)
265 break;
266 else if (sscanf (buf, " workitem_vgpr_count = %d\n",
267 ®count.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 (®counts_os, ®count, 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 (®counts_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 (®counts_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