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