]>
Commit | Line | Data |
---|---|---|
7bfefa9d | 1 | /* Functions for writing LTO sections. |
2 | ||
d353bf18 | 3 | Copyright (C) 2009-2015 Free Software Foundation, Inc. |
7bfefa9d | 4 | Contributed by Kenneth Zadeck <zadeck@naturalbridge.com> |
5 | ||
6 | This file is part of GCC. | |
7 | ||
8 | GCC is free software; you can redistribute it and/or modify it under | |
9 | the terms of the GNU General Public License as published by the Free | |
10 | Software Foundation; either version 3, or (at your option) any later | |
11 | version. | |
12 | ||
13 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY | |
14 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
16 | for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with GCC; see the file COPYING3. If not see | |
20 | <http://www.gnu.org/licenses/>. */ | |
21 | ||
22 | #include "config.h" | |
23 | #include "system.h" | |
24 | #include "coretypes.h" | |
25 | #include "tm.h" | |
b20a8bb4 | 26 | #include "alias.h" |
27 | #include "symtab.h" | |
b20a8bb4 | 28 | #include "tree.h" |
29 | #include "fold-const.h" | |
30 | #include "predict.h" | |
94ea8568 | 31 | #include "hard-reg-set.h" |
94ea8568 | 32 | #include "function.h" |
bc61cadb | 33 | #include "basic-block.h" |
34 | #include "tree-ssa-alias.h" | |
35 | #include "internal-fn.h" | |
36 | #include "gimple-expr.h" | |
b23fb4cb | 37 | #include "gimple.h" |
d53441c8 | 38 | #include "rtl.h" |
39 | #include "flags.h" | |
d53441c8 | 40 | #include "insn-config.h" |
41 | #include "expmed.h" | |
42 | #include "dojump.h" | |
43 | #include "explow.h" | |
44 | #include "calls.h" | |
45 | #include "emit-rtl.h" | |
46 | #include "varasm.h" | |
47 | #include "stmt.h" | |
7bfefa9d | 48 | #include "expr.h" |
49 | #include "params.h" | |
7bfefa9d | 50 | #include "except.h" |
7bfefa9d | 51 | #include "langhooks.h" |
1140c305 | 52 | #include "cgraph.h" |
7f385784 | 53 | #include "data-streamer.h" |
7bfefa9d | 54 | #include "lto-streamer.h" |
55 | #include "lto-compress.h" | |
56 | ||
f1f41a6c | 57 | static vec<lto_out_decl_state_ptr> decl_state_stack; |
7bfefa9d | 58 | |
59 | /* List of out decl states used by functions. We use this to | |
60 | generate the decl directory later. */ | |
61 | ||
f1f41a6c | 62 | vec<lto_out_decl_state_ptr> lto_function_decl_states; |
7bfefa9d | 63 | |
7bfefa9d | 64 | |
65 | /***************************************************************************** | |
66 | Output routines shared by all of the serialization passes. | |
67 | *****************************************************************************/ | |
68 | ||
69 | ||
70 | /* Flush compressed stream data function, sends NUM_CHARS from CHARS | |
71 | to the append lang hook, OPAQUE is currently always NULL. */ | |
72 | ||
73 | static void | |
74 | lto_append_data (const char *chars, unsigned int num_chars, void *opaque) | |
75 | { | |
76 | gcc_assert (opaque == NULL); | |
77 | lang_hooks.lto.append_data (chars, num_chars, opaque); | |
78 | } | |
79 | ||
80 | /* Pointer to the current compression stream. */ | |
81 | ||
82 | static struct lto_compression_stream *compression_stream = NULL; | |
83 | ||
84 | /* Begin a new output section named NAME. If COMPRESS is true, zlib compress | |
85 | the section. */ | |
86 | ||
87 | void | |
88 | lto_begin_section (const char *name, bool compress) | |
89 | { | |
90 | lang_hooks.lto.begin_section (name); | |
91 | ||
92 | /* FIXME lto: for now, suppress compression if the lang_hook that appends | |
93 | data is anything other than assembler output. The effect here is that | |
94 | we get compression of IL only in non-ltrans object files. */ | |
95 | gcc_assert (compression_stream == NULL); | |
96 | if (compress) | |
97 | compression_stream = lto_start_compression (lto_append_data, NULL); | |
98 | } | |
99 | ||
100 | ||
101 | /* End the current output section. */ | |
102 | ||
103 | void | |
104 | lto_end_section (void) | |
105 | { | |
106 | if (compression_stream) | |
107 | { | |
108 | lto_end_compression (compression_stream); | |
109 | compression_stream = NULL; | |
110 | } | |
111 | lang_hooks.lto.end_section (); | |
112 | } | |
113 | ||
2d97af95 | 114 | /* Write SIZE bytes starting at DATA to the assembler. */ |
115 | ||
116 | void | |
117 | lto_write_data (const void *data, unsigned int size) | |
118 | { | |
119 | if (compression_stream) | |
120 | lto_compress_block (compression_stream, (const char *)data, size); | |
121 | else | |
122 | lang_hooks.lto.append_data ((const char *)data, size, NULL); | |
123 | } | |
7bfefa9d | 124 | |
125 | /* Write all of the chars in OBS to the assembler. Recycle the blocks | |
126 | in obs as this is being done. */ | |
127 | ||
128 | void | |
129 | lto_write_stream (struct lto_output_stream *obs) | |
130 | { | |
131 | unsigned int block_size = 1024; | |
132 | struct lto_char_ptr_base *block; | |
133 | struct lto_char_ptr_base *next_block; | |
134 | if (!obs->first_block) | |
135 | return; | |
136 | ||
137 | for (block = obs->first_block; block; block = next_block) | |
138 | { | |
139 | const char *base = ((char *)block) + sizeof (struct lto_char_ptr_base); | |
140 | unsigned int num_chars = block_size - sizeof (struct lto_char_ptr_base); | |
141 | ||
142 | /* If this is not the last block, it is full. If it is the last | |
143 | block, left_in_block indicates how many chars are unoccupied in | |
144 | this block; subtract from num_chars to obtain occupancy. */ | |
145 | next_block = (struct lto_char_ptr_base *) block->ptr; | |
146 | if (!next_block) | |
147 | num_chars -= obs->left_in_block; | |
148 | ||
149 | /* FIXME lto: WPA mode uses an ELF function as a lang_hook to append | |
150 | output data. This hook is not happy with the way that compression | |
151 | blocks up output differently to the way it's blocked here. So for | |
152 | now, we don't compress WPA output. */ | |
153 | if (compression_stream) | |
2d97af95 | 154 | lto_compress_block (compression_stream, base, num_chars); |
7bfefa9d | 155 | else |
156 | lang_hooks.lto.append_data (base, num_chars, block); | |
2d97af95 | 157 | free (block); |
7bfefa9d | 158 | block_size *= 2; |
159 | } | |
160 | } | |
161 | ||
162 | ||
7bfefa9d | 163 | /* Lookup NAME in ENCODER. If NAME is not found, create a new entry in |
164 | ENCODER for NAME with the next available index of ENCODER, then | |
165 | print the index to OBS. True is returned if NAME was added to | |
166 | ENCODER. The resulting index is stored in THIS_INDEX. | |
167 | ||
168 | If OBS is NULL, the only action is to add NAME to the encoder. */ | |
169 | ||
170 | bool | |
171 | lto_output_decl_index (struct lto_output_stream *obs, | |
172 | struct lto_tree_ref_encoder *encoder, | |
173 | tree name, unsigned int *this_index) | |
174 | { | |
7bfefa9d | 175 | bool new_entry_p = FALSE; |
ea1a929f | 176 | bool existed_p; |
7bfefa9d | 177 | |
d62dd039 | 178 | unsigned int &index |
179 | = encoder->tree_hash_table->get_or_insert (name, &existed_p); | |
ea1a929f | 180 | if (!existed_p) |
7bfefa9d | 181 | { |
32d76803 | 182 | index = encoder->trees.length (); |
f1f41a6c | 183 | encoder->trees.safe_push (name); |
7bfefa9d | 184 | new_entry_p = TRUE; |
185 | } | |
7bfefa9d | 186 | |
187 | if (obs) | |
7f385784 | 188 | streamer_write_uhwi_stream (obs, index); |
7bfefa9d | 189 | *this_index = index; |
190 | return new_entry_p; | |
191 | } | |
192 | ||
193 | /* Output a field DECL to OBS. */ | |
194 | ||
195 | void | |
196 | lto_output_field_decl_index (struct lto_out_decl_state *decl_state, | |
197 | struct lto_output_stream * obs, tree decl) | |
198 | { | |
199 | unsigned int index; | |
200 | lto_output_decl_index (obs, &decl_state->streams[LTO_DECL_STREAM_FIELD_DECL], | |
201 | decl, &index); | |
202 | } | |
203 | ||
204 | /* Output a function DECL to OBS. */ | |
205 | ||
206 | void | |
48e1416a | 207 | lto_output_fn_decl_index (struct lto_out_decl_state *decl_state, |
7bfefa9d | 208 | struct lto_output_stream * obs, tree decl) |
209 | { | |
210 | unsigned int index; | |
211 | lto_output_decl_index (obs, &decl_state->streams[LTO_DECL_STREAM_FN_DECL], | |
212 | decl, &index); | |
213 | } | |
214 | ||
215 | /* Output a namespace DECL to OBS. */ | |
216 | ||
217 | void | |
218 | lto_output_namespace_decl_index (struct lto_out_decl_state *decl_state, | |
219 | struct lto_output_stream * obs, tree decl) | |
220 | { | |
221 | unsigned int index; | |
222 | lto_output_decl_index (obs, | |
223 | &decl_state->streams[LTO_DECL_STREAM_NAMESPACE_DECL], | |
224 | decl, &index); | |
225 | } | |
226 | ||
227 | /* Output a static or extern var DECL to OBS. */ | |
228 | ||
229 | void | |
230 | lto_output_var_decl_index (struct lto_out_decl_state *decl_state, | |
231 | struct lto_output_stream * obs, tree decl) | |
232 | { | |
233 | unsigned int index; | |
234 | lto_output_decl_index (obs, &decl_state->streams[LTO_DECL_STREAM_VAR_DECL], | |
235 | decl, &index); | |
236 | } | |
237 | ||
238 | /* Output a type DECL to OBS. */ | |
239 | ||
240 | void | |
241 | lto_output_type_decl_index (struct lto_out_decl_state *decl_state, | |
242 | struct lto_output_stream * obs, tree decl) | |
243 | { | |
244 | unsigned int index; | |
245 | lto_output_decl_index (obs, &decl_state->streams[LTO_DECL_STREAM_TYPE_DECL], | |
246 | decl, &index); | |
247 | } | |
248 | ||
249 | /* Output a type REF to OBS. */ | |
250 | ||
251 | void | |
252 | lto_output_type_ref_index (struct lto_out_decl_state *decl_state, | |
253 | struct lto_output_stream *obs, tree ref) | |
254 | { | |
255 | unsigned int index; | |
256 | lto_output_decl_index (obs, &decl_state->streams[LTO_DECL_STREAM_TYPE], | |
257 | ref, &index); | |
258 | } | |
259 | ||
260 | ||
261 | /* Create the output block and return it. */ | |
262 | ||
263 | struct lto_simple_output_block * | |
264 | lto_create_simple_output_block (enum lto_section_type section_type) | |
265 | { | |
266 | struct lto_simple_output_block *ob | |
267 | = ((struct lto_simple_output_block *) | |
268 | xcalloc (1, sizeof (struct lto_simple_output_block))); | |
269 | ||
270 | ob->section_type = section_type; | |
271 | ob->decl_state = lto_get_out_decl_state (); | |
272 | ob->main_stream = ((struct lto_output_stream *) | |
273 | xcalloc (1, sizeof (struct lto_output_stream))); | |
274 | ||
275 | return ob; | |
276 | } | |
277 | ||
278 | ||
279 | /* Produce a simple section for one of the ipa passes. */ | |
280 | ||
281 | void | |
282 | lto_destroy_simple_output_block (struct lto_simple_output_block *ob) | |
283 | { | |
284 | char *section_name; | |
285 | struct lto_simple_header header; | |
7bfefa9d | 286 | |
f18bad33 | 287 | section_name = lto_get_section_name (ob->section_type, NULL, NULL); |
7bfefa9d | 288 | lto_begin_section (section_name, !flag_wpa); |
289 | free (section_name); | |
290 | ||
291 | /* Write the header which says how to decode the pieces of the | |
292 | t. */ | |
293 | memset (&header, 0, sizeof (struct lto_simple_header)); | |
472ca566 | 294 | header.major_version = LTO_major_version; |
295 | header.minor_version = LTO_minor_version; | |
7bfefa9d | 296 | header.main_size = ob->main_stream->total_size; |
2d97af95 | 297 | lto_write_data (&header, sizeof header); |
7bfefa9d | 298 | |
299 | lto_write_stream (ob->main_stream); | |
300 | ||
301 | /* Put back the assembly section that was there before we started | |
302 | writing lto info. */ | |
303 | lto_end_section (); | |
304 | ||
305 | free (ob->main_stream); | |
306 | free (ob); | |
307 | } | |
308 | ||
309 | ||
310 | /* Return a new lto_out_decl_state. */ | |
311 | ||
312 | struct lto_out_decl_state * | |
313 | lto_new_out_decl_state (void) | |
314 | { | |
315 | struct lto_out_decl_state *state = XCNEW (struct lto_out_decl_state); | |
316 | int i; | |
7bfefa9d | 317 | |
318 | for (i = 0; i < LTO_N_DECL_STREAMS; i++) | |
32d76803 | 319 | lto_init_tree_ref_encoder (&state->streams[i]); |
7bfefa9d | 320 | |
7bfefa9d | 321 | return state; |
322 | } | |
323 | ||
324 | ||
325 | /* Delete STATE and components. */ | |
326 | ||
327 | void | |
328 | lto_delete_out_decl_state (struct lto_out_decl_state *state) | |
329 | { | |
330 | int i; | |
331 | ||
332 | for (i = 0; i < LTO_N_DECL_STREAMS; i++) | |
333 | lto_destroy_tree_ref_encoder (&state->streams[i]); | |
334 | ||
335 | free (state); | |
336 | } | |
337 | ||
338 | ||
339 | /* Get the currently used lto_out_decl_state structure. */ | |
340 | ||
341 | struct lto_out_decl_state * | |
342 | lto_get_out_decl_state (void) | |
343 | { | |
f1f41a6c | 344 | return decl_state_stack.last (); |
7bfefa9d | 345 | } |
346 | ||
347 | /* Push STATE to top of out decl stack. */ | |
348 | ||
349 | void | |
350 | lto_push_out_decl_state (struct lto_out_decl_state *state) | |
351 | { | |
f1f41a6c | 352 | decl_state_stack.safe_push (state); |
7bfefa9d | 353 | } |
354 | ||
355 | /* Pop the currently used out-decl state from top of stack. */ | |
356 | ||
357 | struct lto_out_decl_state * | |
358 | lto_pop_out_decl_state (void) | |
359 | { | |
f1f41a6c | 360 | return decl_state_stack.pop (); |
7bfefa9d | 361 | } |
362 | ||
363 | /* Record STATE after it has been used in serializing the body of | |
364 | FN_DECL. STATE should no longer be used by the caller. The ownership | |
365 | of it is taken over from this point. */ | |
366 | ||
367 | void | |
368 | lto_record_function_out_decl_state (tree fn_decl, | |
369 | struct lto_out_decl_state *state) | |
370 | { | |
371 | int i; | |
372 | ||
373 | /* Strip all hash tables to save some memory. */ | |
374 | for (i = 0; i < LTO_N_DECL_STREAMS; i++) | |
375 | if (state->streams[i].tree_hash_table) | |
376 | { | |
ea1a929f | 377 | delete state->streams[i].tree_hash_table; |
7bfefa9d | 378 | state->streams[i].tree_hash_table = NULL; |
379 | } | |
380 | state->fn_decl = fn_decl; | |
f1f41a6c | 381 | lto_function_decl_states.safe_push (state); |
7bfefa9d | 382 | } |