1 | /* Functions for writing LTO sections. |
2 | |
3 | Copyright (C) 2009-2024 Free Software Foundation, Inc. |
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 "backend.h" |
26 | #include "rtl.h" |
27 | #include "tree.h" |
28 | #include "gimple.h" |
29 | #include "cgraph.h" |
30 | #include "data-streamer.h" |
31 | #include "langhooks.h" |
32 | #include "lto-compress.h" |
33 | #include "print-tree.h" |
34 | |
35 | static vec<lto_out_decl_state_ptr> decl_state_stack; |
36 | |
37 | /* List of out decl states used by functions. We use this to |
38 | generate the decl directory later. */ |
39 | |
40 | vec<lto_out_decl_state_ptr> lto_function_decl_states; |
41 | |
42 | |
43 | /***************************************************************************** |
44 | Output routines shared by all of the serialization passes. |
45 | *****************************************************************************/ |
46 | |
47 | |
48 | /* Flush compressed stream data function, sends NUM_CHARS from CHARS |
49 | to the append lang hook, OPAQUE is currently always NULL. */ |
50 | |
51 | static void |
52 | lto_append_data (const char *chars, unsigned int num_chars, void *opaque) |
53 | { |
54 | gcc_assert (opaque == NULL); |
55 | lang_hooks.lto.append_data (chars, num_chars, opaque); |
56 | } |
57 | |
58 | /* Pointer to the current compression stream. */ |
59 | |
60 | static struct lto_compression_stream *compression_stream = NULL; |
61 | |
62 | /* Begin a new output section named NAME. If COMPRESS is true, zlib compress |
63 | the section. */ |
64 | |
65 | void |
66 | lto_begin_section (const char *name, bool compress) |
67 | { |
68 | lang_hooks.lto.begin_section (name); |
69 | |
70 | if (streamer_dump_file) |
71 | { |
72 | if (flag_dump_unnumbered || flag_dump_noaddr) |
73 | fprintf (stream: streamer_dump_file, format: "Creating %ssection\n" , |
74 | compress ? "compressed " : "" ); |
75 | else |
76 | fprintf (stream: streamer_dump_file, format: "Creating %ssection %s\n" , |
77 | compress ? "compressed " : "" , name); |
78 | } |
79 | gcc_assert (compression_stream == NULL); |
80 | if (compress) |
81 | compression_stream = lto_start_compression (callback: lto_append_data, NULL); |
82 | } |
83 | |
84 | |
85 | /* End the current output section. */ |
86 | |
87 | void |
88 | lto_end_section (void) |
89 | { |
90 | if (compression_stream) |
91 | { |
92 | lto_end_compression (stream: compression_stream); |
93 | compression_stream = NULL; |
94 | } |
95 | lang_hooks.lto.end_section (); |
96 | } |
97 | |
98 | /* Write SIZE bytes starting at DATA to the assembler. */ |
99 | |
100 | void |
101 | lto_write_data (const void *data, unsigned int size) |
102 | { |
103 | if (compression_stream) |
104 | lto_compress_block (stream: compression_stream, base: (const char *)data, num_chars: size); |
105 | else |
106 | lang_hooks.lto.append_data ((const char *)data, size, NULL); |
107 | } |
108 | |
109 | /* Write SIZE bytes starting at DATA to the assembler. */ |
110 | |
111 | void |
112 | lto_write_raw_data (const void *data, unsigned int size) |
113 | { |
114 | lang_hooks.lto.append_data ((const char *)data, size, NULL); |
115 | } |
116 | |
117 | /* Write all of the chars in OBS to the assembler. Recycle the blocks |
118 | in obs as this is being done. */ |
119 | |
120 | void |
121 | lto_write_stream (struct lto_output_stream *obs) |
122 | { |
123 | unsigned int block_size = 1024; |
124 | struct lto_char_ptr_base *block; |
125 | struct lto_char_ptr_base *next_block; |
126 | if (!obs->first_block) |
127 | return; |
128 | |
129 | for (block = obs->first_block; block; block = next_block) |
130 | { |
131 | const char *base = ((char *)block) + sizeof (struct lto_char_ptr_base); |
132 | unsigned int num_chars = block_size - sizeof (struct lto_char_ptr_base); |
133 | |
134 | /* If this is not the last block, it is full. If it is the last |
135 | block, left_in_block indicates how many chars are unoccupied in |
136 | this block; subtract from num_chars to obtain occupancy. */ |
137 | next_block = (struct lto_char_ptr_base *) block->ptr; |
138 | if (!next_block) |
139 | num_chars -= obs->left_in_block; |
140 | |
141 | if (compression_stream) |
142 | lto_compress_block (stream: compression_stream, base, num_chars); |
143 | else |
144 | lang_hooks.lto.append_data (base, num_chars, block); |
145 | free (ptr: block); |
146 | block_size *= 2; |
147 | } |
148 | } |
149 | |
150 | /* Create the output block and return it. */ |
151 | |
152 | struct lto_simple_output_block * |
153 | lto_create_simple_output_block (enum lto_section_type section_type) |
154 | { |
155 | struct lto_simple_output_block *ob |
156 | = ((struct lto_simple_output_block *) |
157 | xcalloc (1, sizeof (struct lto_simple_output_block))); |
158 | |
159 | ob->section_type = section_type; |
160 | ob->decl_state = lto_get_out_decl_state (); |
161 | ob->main_stream = ((struct lto_output_stream *) |
162 | xcalloc (1, sizeof (struct lto_output_stream))); |
163 | |
164 | return ob; |
165 | } |
166 | |
167 | |
168 | /* Produce a simple section for one of the ipa passes. */ |
169 | |
170 | void |
171 | lto_destroy_simple_output_block (struct lto_simple_output_block *ob) |
172 | { |
173 | char *section_name; |
174 | struct lto_simple_header ; |
175 | |
176 | section_name = lto_get_section_name (ob->section_type, NULL, 0, NULL); |
177 | lto_begin_section (name: section_name, compress: !flag_wpa); |
178 | free (ptr: section_name); |
179 | |
180 | /* Write the header which says how to decode the pieces of the |
181 | t. */ |
182 | memset (s: &header, c: 0, n: sizeof (struct lto_simple_header)); |
183 | header.main_size = ob->main_stream->total_size; |
184 | lto_write_data (data: &header, size: sizeof header); |
185 | |
186 | lto_write_stream (obs: ob->main_stream); |
187 | |
188 | /* Put back the assembly section that was there before we started |
189 | writing lto info. */ |
190 | lto_end_section (); |
191 | |
192 | free (ptr: ob->main_stream); |
193 | free (ptr: ob); |
194 | } |
195 | |
196 | |
197 | /* Return a new lto_out_decl_state. */ |
198 | |
199 | struct lto_out_decl_state * |
200 | lto_new_out_decl_state (void) |
201 | { |
202 | struct lto_out_decl_state *state = XCNEW (struct lto_out_decl_state); |
203 | int i; |
204 | |
205 | for (i = 0; i < LTO_N_DECL_STREAMS; i++) |
206 | lto_init_tree_ref_encoder (encoder: &state->streams[i]); |
207 | |
208 | /* At WPA time we do not compress sections by default. */ |
209 | state->compressed = !flag_wpa; |
210 | |
211 | return state; |
212 | } |
213 | |
214 | |
215 | /* Delete STATE and components. */ |
216 | |
217 | void |
218 | lto_delete_out_decl_state (struct lto_out_decl_state *state) |
219 | { |
220 | int i; |
221 | |
222 | for (i = 0; i < LTO_N_DECL_STREAMS; i++) |
223 | lto_destroy_tree_ref_encoder (encoder: &state->streams[i]); |
224 | |
225 | free (ptr: state); |
226 | } |
227 | |
228 | |
229 | /* Get the currently used lto_out_decl_state structure. */ |
230 | |
231 | struct lto_out_decl_state * |
232 | lto_get_out_decl_state (void) |
233 | { |
234 | return decl_state_stack.last (); |
235 | } |
236 | |
237 | /* Push STATE to top of out decl stack. */ |
238 | |
239 | void |
240 | lto_push_out_decl_state (struct lto_out_decl_state *state) |
241 | { |
242 | decl_state_stack.safe_push (obj: state); |
243 | } |
244 | |
245 | /* Pop the currently used out-decl state from top of stack. */ |
246 | |
247 | struct lto_out_decl_state * |
248 | lto_pop_out_decl_state (void) |
249 | { |
250 | return decl_state_stack.pop (); |
251 | } |
252 | |
253 | /* Record STATE after it has been used in serializing the body of |
254 | FN_DECL. STATE should no longer be used by the caller. The ownership |
255 | of it is taken over from this point. */ |
256 | |
257 | void |
258 | lto_record_function_out_decl_state (tree fn_decl, |
259 | struct lto_out_decl_state *state) |
260 | { |
261 | int i; |
262 | |
263 | /* Strip all hash tables to save some memory. */ |
264 | for (i = 0; i < LTO_N_DECL_STREAMS; i++) |
265 | if (state->streams[i].tree_hash_table) |
266 | { |
267 | delete state->streams[i].tree_hash_table; |
268 | state->streams[i].tree_hash_table = NULL; |
269 | } |
270 | state->fn_decl = fn_decl; |
271 | lto_function_decl_states.safe_push (obj: state); |
272 | } |
273 | |