1 /* Code to maintain a C++ template repository.
2    Copyright (C) 1995-2018 Free Software Foundation, Inc.
3    Contributed by Jason Merrill (jason@cygnus.com)
4 
5 This file is part of GCC.
6 
7 GCC is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
10 any later version.
11 
12 GCC is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public 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 /* My strategy here is as follows:
22 
23    Everything should be emitted in a translation unit where it is used.
24    The results of the automatic process should be easily reproducible with
25    explicit code.  */
26 
27 #include "config.h"
28 #include "system.h"
29 #include "coretypes.h"
30 #include "cp-tree.h"
31 #include "stringpool.h"
32 #include "toplev.h"
33 
34 static const char *extract_string (const char **);
35 static const char *get_base_filename (const char *);
36 static FILE *open_repo_file (const char *);
37 static char *afgets (FILE *);
38 static FILE *reopen_repo_file_for_write (void);
39 
40 static GTY(()) vec<tree, va_gc> *pending_repo;
41 static char *repo_name;
42 
43 static const char *old_args, *old_dir, *old_main;
44 
45 static struct obstack temporary_obstack;
46 static bool temporary_obstack_initialized_p;
47 
48 /* Parse a reasonable subset of shell quoting syntax.  */
49 
50 static const char *
extract_string(const char ** pp)51 extract_string (const char **pp)
52 {
53   const char *p = *pp;
54   int backquote = 0;
55   int inside = 0;
56 
57   for (;;)
58     {
59       char c = *p;
60       if (c == '\0')
61 	break;
62       ++p;
63       if (backquote)
64 	{
65 	  obstack_1grow (&temporary_obstack, c);
66 	  backquote = 0;
67 	}
68       else if (! inside && c == ' ')
69 	break;
70       else if (! inside && c == '\\')
71 	backquote = 1;
72       else if (c == '\'')
73 	inside = !inside;
74       else
75 	obstack_1grow (&temporary_obstack, c);
76     }
77 
78   obstack_1grow (&temporary_obstack, '\0');
79   *pp = p;
80   return (char *) obstack_finish (&temporary_obstack);
81 }
82 
83 static const char *
get_base_filename(const char * filename)84 get_base_filename (const char *filename)
85 {
86   const char *p = getenv ("COLLECT_GCC_OPTIONS");
87   const char *output = NULL;
88   int compiling = 0;
89 
90   while (p && *p)
91     {
92       const char *q = extract_string (&p);
93 
94       if (strcmp (q, "-o") == 0)
95 	{
96 	  if (flag_compare_debug)
97 	    /* Just in case aux_base_name was based on a name with two
98 	       or more '.'s, add an arbitrary extension that will be
99 	       stripped by the caller.  */
100 	    output = concat (aux_base_name, ".o", NULL);
101 	  else
102 	    output = extract_string (&p);
103 	}
104       else if (strcmp (q, "-c") == 0)
105 	compiling = 1;
106     }
107 
108   if (compiling && output)
109     return output;
110 
111   if (p && ! compiling)
112     {
113       warning (0, "-frepo must be used with -c");
114       flag_use_repository = 0;
115       return NULL;
116     }
117 
118   return lbasename (filename);
119 }
120 
121 static FILE *
open_repo_file(const char * filename)122 open_repo_file (const char *filename)
123 {
124   const char *p;
125   const char *s = get_base_filename (filename);
126 
127   if (s == NULL)
128     return NULL;
129 
130   p = lbasename (s);
131   p = strrchr (p, '.');
132   if (! p)
133     p = s + strlen (s);
134 
135   repo_name = XNEWVEC (char, p - s + 5);
136   memcpy (repo_name, s, p - s);
137   memcpy (repo_name + (p - s), ".rpo", 5);
138 
139   return fopen (repo_name, "r");
140 }
141 
142 static char *
afgets(FILE * stream)143 afgets (FILE *stream)
144 {
145   int c;
146   while ((c = getc (stream)) != EOF && c != '\n')
147     obstack_1grow (&temporary_obstack, c);
148   if (obstack_object_size (&temporary_obstack) == 0)
149     return NULL;
150   obstack_1grow (&temporary_obstack, '\0');
151   return (char *) obstack_finish (&temporary_obstack);
152 }
153 
154 void
init_repo(void)155 init_repo (void)
156 {
157   char *buf;
158   const char *p;
159   FILE *repo_file;
160 
161   if (! flag_use_repository)
162     return;
163 
164   /* When a PCH file is loaded, the entire identifier table is
165      replaced, with the result that IDENTIFIER_REPO_CHOSEN is cleared.
166      So, we have to reread the repository file.  */
167   lang_post_pch_load = init_repo;
168 
169   if (!temporary_obstack_initialized_p)
170     gcc_obstack_init (&temporary_obstack);
171 
172   repo_file = open_repo_file (main_input_filename);
173 
174   if (repo_file == 0)
175     return;
176 
177   while ((buf = afgets (repo_file)))
178     {
179       switch (buf[0])
180 	{
181 	case 'A':
182 	  old_args = ggc_strdup (buf + 2);
183 	  break;
184 	case 'D':
185 	  old_dir = ggc_strdup (buf + 2);
186 	  break;
187 	case 'M':
188 	  old_main = ggc_strdup (buf + 2);
189 	  break;
190 	case 'O':
191 	  /* A symbol that we were able to define the last time this
192 	     file was compiled.  */
193 	  break;
194 	case 'C':
195 	  /* A symbol that the prelinker has requested that we
196 	     define.  */
197 	  {
198 	    tree id = get_identifier (buf + 2);
199 	    IDENTIFIER_REPO_CHOSEN (id) = 1;
200 	  }
201 	  break;
202 	default:
203 	  error ("mysterious repository information in %s", repo_name);
204 	}
205       obstack_free (&temporary_obstack, buf);
206     }
207   fclose (repo_file);
208 
209   if (old_args && !get_random_seed (true)
210       && (p = strstr (old_args, "'-frandom-seed=")))
211     set_random_seed (extract_string (&p) + strlen ("-frandom-seed="));
212 }
213 
214 static FILE *
reopen_repo_file_for_write(void)215 reopen_repo_file_for_write (void)
216 {
217   FILE *repo_file = fopen (repo_name, "w");
218 
219   if (repo_file == 0)
220     {
221       error ("can%'t create repository information file %qs", repo_name);
222       flag_use_repository = 0;
223     }
224 
225   return repo_file;
226 }
227 
228 /* Emit any pending repos.  */
229 
230 void
finish_repo(void)231 finish_repo (void)
232 {
233   tree val;
234   char *dir, *args;
235   FILE *repo_file;
236   unsigned ix;
237 
238   if (!flag_use_repository || flag_compare_debug)
239     return;
240 
241   if (seen_error ())
242     return;
243 
244   repo_file = reopen_repo_file_for_write ();
245   if (repo_file == 0)
246     goto out;
247 
248   fprintf (repo_file, "M %s\n", main_input_filename);
249   dir = getpwd ();
250   fprintf (repo_file, "D %s\n", dir);
251   args = getenv ("COLLECT_GCC_OPTIONS");
252   if (args)
253     {
254       fprintf (repo_file, "A %s", args);
255       /* If -frandom-seed is not among the ARGS, then add the value
256 	 that we chose.  That will ensure that the names of types from
257 	 anonymous namespaces will get the same mangling when this
258 	 file is recompiled.  */
259       if (!strstr (args, "'-frandom-seed="))
260 	fprintf (repo_file, " '-frandom-seed=" HOST_WIDE_INT_PRINT_HEX_PURE "'",
261 		 get_random_seed (false));
262       fprintf (repo_file, "\n");
263     }
264 
265   FOR_EACH_VEC_SAFE_ELT_REVERSE (pending_repo, ix, val)
266     {
267       tree name = DECL_ASSEMBLER_NAME (val);
268       char type = IDENTIFIER_REPO_CHOSEN (name) ? 'C' : 'O';
269       fprintf (repo_file, "%c %s\n", type, IDENTIFIER_POINTER (name));
270     }
271 
272  out:
273   if (repo_file)
274     fclose (repo_file);
275 }
276 
277 /* DECL is a FUNCTION_DECL or VAR_DECL with vague linkage whose
278    definition is available in this translation unit.  Returns 0 if
279    this definition should not be emitted in this translation unit
280    because it will be emitted elsewhere.  Returns 1 if the repository
281    file indicates that that DECL should be emitted in this translation
282    unit, or 2 if the repository file is not in use.  */
283 
284 int
repo_emit_p(tree decl)285 repo_emit_p (tree decl)
286 {
287   int ret = 0;
288   gcc_assert (TREE_PUBLIC (decl));
289   gcc_assert (VAR_OR_FUNCTION_DECL_P (decl));
290   gcc_assert (!DECL_REALLY_EXTERN (decl)
291 	      /* A clone might not have its linkage flags updated yet
292 		 because we call import_export_decl before
293 		 maybe_clone_body.  */
294 	      || DECL_ABSTRACT_ORIGIN (decl));
295 
296   /* When not using the repository, emit everything.  */
297   if (!flag_use_repository)
298     return 2;
299 
300   /* Only template instantiations are managed by the repository.  This
301      is an artificial restriction; the code in the prelinker and here
302      will work fine if all entities with vague linkage are managed by
303      the repository.  */
304   if (VAR_P (decl))
305     {
306       tree type = NULL_TREE;
307       if (DECL_VTABLE_OR_VTT_P (decl))
308 	type = DECL_CONTEXT (decl);
309       else if (DECL_TINFO_P (decl))
310 	type = TREE_TYPE (DECL_NAME (decl));
311       if (!DECL_TEMPLATE_INSTANTIATION (decl)
312 	  && (!TYPE_LANG_SPECIFIC (type)
313 	      || !CLASSTYPE_TEMPLATE_INSTANTIATION (type)))
314 	return 2;
315       /* Const static data members initialized by constant expressions must
316 	 be processed where needed so that their definitions are
317 	 available.  Still record them into *.rpo files, so if they
318 	 weren't actually emitted and collect2 requests them, they can
319 	 be provided.  */
320       if (decl_maybe_constant_var_p (decl)
321 	  && DECL_CLASS_SCOPE_P (decl))
322 	ret = 2;
323     }
324   else if (!DECL_TEMPLATE_INSTANTIATION (decl))
325     return 2;
326 
327   if (DECL_EXPLICIT_INSTANTIATION (decl))
328     return 2;
329 
330   /* For constructors and destructors, the repository contains
331      information about the clones -- not the original function --
332      because only the clones are emitted in the object file.  */
333   if (DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (decl)
334       || DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (decl))
335     {
336       int emit_p = 0;
337       tree clone;
338       /* There is no early exit from this loop because we want to
339 	 ensure that all of the clones are marked as available in this
340 	 object file.  */
341       FOR_EACH_CLONE (clone, decl)
342 	/* The only possible results from the recursive call to
343 	   repo_emit_p are 0 or 1.  */
344 	if (repo_emit_p (clone))
345 	  emit_p = 1;
346       return emit_p;
347     }
348 
349   /* Keep track of all available entities.  */
350   if (!DECL_REPO_AVAILABLE_P (decl))
351     {
352       DECL_REPO_AVAILABLE_P (decl) = 1;
353       vec_safe_push (pending_repo, decl);
354     }
355 
356   return IDENTIFIER_REPO_CHOSEN (DECL_ASSEMBLER_NAME (decl)) ? 1 : ret;
357 }
358 
359 /* Returns true iff the prelinker has explicitly marked CLASS_TYPE for
360    export from this translation unit.  */
361 
362 bool
repo_export_class_p(const_tree class_type)363 repo_export_class_p (const_tree class_type)
364 {
365   if (!flag_use_repository)
366     return false;
367   if (!CLASSTYPE_VTABLES (class_type))
368     return false;
369   /* If the virtual table has been assigned to this translation unit,
370      export the class.  */
371   return (IDENTIFIER_REPO_CHOSEN
372 	  (DECL_ASSEMBLER_NAME (CLASSTYPE_VTABLES (class_type))));
373 }
374 
375 #include "gt-cp-repo.h"
376