145
|
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
|
|
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
|
|
63 mkoffload_cleanup (void)
|
|
64 {
|
|
65 tool_cleanup (false);
|
|
66 }
|
|
67
|
|
68 /* Unlink FILE unless requested otherwise. */
|
|
69
|
|
70 void
|
|
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
|
|
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 *
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 gcn_s1_name = make_temp_file (".mkoffload.1.s");
|
|
632 gcn_s2_name = make_temp_file (".mkoffload.2.s");
|
|
633 gcn_o_name = make_temp_file (".mkoffload.hsaco");
|
|
634 gcn_cfile_name = make_temp_file (".c");
|
|
635
|
|
636 /* Build arguments for compiler pass. */
|
|
637 struct obstack cc_argv_obstack;
|
|
638 obstack_init (&cc_argv_obstack);
|
|
639 obstack_ptr_grow (&cc_argv_obstack, driver);
|
|
640 obstack_ptr_grow (&cc_argv_obstack, "-S");
|
|
641
|
|
642 if (save_temps)
|
|
643 obstack_ptr_grow (&cc_argv_obstack, "-save-temps");
|
|
644 if (verbose)
|
|
645 obstack_ptr_grow (&cc_argv_obstack, "-v");
|
|
646 obstack_ptr_grow (&cc_argv_obstack, abi);
|
|
647 obstack_ptr_grow (&cc_argv_obstack, "-xlto");
|
|
648 if (fopenmp)
|
|
649 obstack_ptr_grow (&cc_argv_obstack, "-mgomp");
|
|
650
|
|
651 for (int ix = 1; ix != argc; ix++)
|
|
652 {
|
|
653 if (!strcmp (argv[ix], "-o") && ix + 1 != argc)
|
|
654 outname = argv[++ix];
|
|
655 else
|
|
656 {
|
|
657 obstack_ptr_grow (&cc_argv_obstack, argv[ix]);
|
|
658
|
|
659 if (argv[ix][0] != '-')
|
|
660 offloadsrc = argv[ix];
|
|
661 }
|
|
662 }
|
|
663
|
|
664 obstack_ptr_grow (&cc_argv_obstack, "-o");
|
|
665 obstack_ptr_grow (&cc_argv_obstack, gcn_s1_name);
|
|
666 obstack_ptr_grow (&cc_argv_obstack,
|
|
667 concat ("-mlocal-symbol-id=", offloadsrc, NULL));
|
|
668 obstack_ptr_grow (&cc_argv_obstack, NULL);
|
|
669 const char **cc_argv = XOBFINISH (&cc_argv_obstack, const char **);
|
|
670
|
|
671 /* Build arguments for assemble/link pass. */
|
|
672 struct obstack ld_argv_obstack;
|
|
673 obstack_init (&ld_argv_obstack);
|
|
674 obstack_ptr_grow (&ld_argv_obstack, driver);
|
|
675 obstack_ptr_grow (&ld_argv_obstack, gcn_s2_name);
|
|
676 obstack_ptr_grow (&ld_argv_obstack, "-lgomp");
|
|
677
|
|
678 for (int i = 1; i < argc; i++)
|
|
679 if (strncmp (argv[i], "-l", 2) == 0
|
|
680 || strncmp (argv[i], "-Wl", 3) == 0
|
|
681 || strncmp (argv[i], "-march", 6) == 0)
|
|
682 obstack_ptr_grow (&ld_argv_obstack, argv[i]);
|
|
683
|
|
684 obstack_ptr_grow (&ld_argv_obstack, "-o");
|
|
685 obstack_ptr_grow (&ld_argv_obstack, gcn_o_name);
|
|
686 obstack_ptr_grow (&ld_argv_obstack, NULL);
|
|
687 const char **ld_argv = XOBFINISH (&ld_argv_obstack, const char **);
|
|
688
|
|
689 /* Clean up unhelpful environment variables. */
|
|
690 char *execpath = getenv ("GCC_EXEC_PREFIX");
|
|
691 char *cpath = getenv ("COMPILER_PATH");
|
|
692 char *lpath = getenv ("LIBRARY_PATH");
|
|
693 unsetenv ("GCC_EXEC_PREFIX");
|
|
694 unsetenv ("COMPILER_PATH");
|
|
695 unsetenv ("LIBRARY_PATH");
|
|
696
|
|
697 /* Run the compiler pass. */
|
|
698 fork_execute (cc_argv[0], CONST_CAST (char **, cc_argv), true);
|
|
699 obstack_free (&cc_argv_obstack, NULL);
|
|
700
|
|
701 in = fopen (gcn_s1_name, "r");
|
|
702 if (!in)
|
|
703 fatal_error (input_location, "cannot open intermediate gcn asm file");
|
|
704
|
|
705 out = fopen (gcn_s2_name, "w");
|
|
706 if (!out)
|
|
707 fatal_error (input_location, "cannot open '%s'", gcn_s2_name);
|
|
708
|
|
709 cfile = fopen (gcn_cfile_name, "w");
|
|
710 if (!cfile)
|
|
711 fatal_error (input_location, "cannot open '%s'", gcn_cfile_name);
|
|
712
|
|
713 process_asm (in, out, cfile);
|
|
714
|
|
715 fclose (in);
|
|
716 fclose (out);
|
|
717
|
|
718 /* Run the assemble/link pass. */
|
|
719 fork_execute (ld_argv[0], CONST_CAST (char **, ld_argv), true);
|
|
720 obstack_free (&ld_argv_obstack, NULL);
|
|
721
|
|
722 in = fopen (gcn_o_name, "r");
|
|
723 if (!in)
|
|
724 fatal_error (input_location, "cannot open intermediate gcn obj file");
|
|
725
|
|
726 process_obj (in, cfile);
|
|
727
|
|
728 fclose (in);
|
|
729 fclose (cfile);
|
|
730
|
|
731 xputenv (concat ("GCC_EXEC_PREFIX=", execpath, NULL));
|
|
732 xputenv (concat ("COMPILER_PATH=", cpath, NULL));
|
|
733 xputenv (concat ("LIBRARY_PATH=", lpath, NULL));
|
|
734
|
|
735 compile_native (gcn_cfile_name, outname, collect_gcc);
|
|
736
|
|
737 return 0;
|
|
738 }
|