]>
Commit | Line | Data |
---|---|---|
7bfefa9d | 1 | /* LTO IL compression streams. |
2 | ||
d353bf18 | 3 | Copyright (C) 2009-2015 Free Software Foundation, Inc. |
7bfefa9d | 4 | Contributed by Simon Baldwin <simonb@google.com> |
5 | ||
6 | This file is part of GCC. | |
7 | ||
8 | GCC is free software; you can redistribute it and/or modify it | |
9 | under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation; either version 3, or (at your option) | |
11 | any later version. | |
12 | ||
13 | GCC is distributed in the hope that it will be useful, but WITHOUT | |
14 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
15 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public | |
16 | License 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 | /* zlib.h includes other system headers. Those headers may test feature | |
25 | test macros. config.h may define feature test macros. For this reason, | |
26 | zlib.h needs to be included after, rather than before, config.h and | |
27 | system.h. */ | |
28 | #include <zlib.h> | |
29 | #include "coretypes.h" | |
94ea8568 | 30 | #include "hash-set.h" |
b20a8bb4 | 31 | #include "vec.h" |
b20a8bb4 | 32 | #include "input.h" |
33 | #include "alias.h" | |
34 | #include "symtab.h" | |
35 | #include "options.h" | |
b20a8bb4 | 36 | #include "inchash.h" |
37 | #include "tree.h" | |
38 | #include "fold-const.h" | |
39 | #include "predict.h" | |
94ea8568 | 40 | #include "tm.h" |
41 | #include "hard-reg-set.h" | |
42 | #include "input.h" | |
43 | #include "function.h" | |
bc61cadb | 44 | #include "basic-block.h" |
45 | #include "tree-ssa-alias.h" | |
46 | #include "internal-fn.h" | |
47 | #include "gimple-expr.h" | |
48 | #include "is-a.h" | |
b23fb4cb | 49 | #include "gimple.h" |
852f689e | 50 | #include "diagnostic-core.h" |
7bfefa9d | 51 | #include "langhooks.h" |
1140c305 | 52 | #include "hash-map.h" |
53 | #include "plugin-api.h" | |
54 | #include "ipa-ref.h" | |
55 | #include "cgraph.h" | |
7bfefa9d | 56 | #include "lto-streamer.h" |
57 | #include "lto-compress.h" | |
58 | ||
59 | /* Compression stream structure, holds the flush callback and opaque token, | |
60 | the buffered data, and a note of whether compressing or uncompressing. */ | |
61 | ||
62 | struct lto_compression_stream | |
63 | { | |
64 | void (*callback) (const char *, unsigned, void *); | |
65 | void *opaque; | |
66 | char *buffer; | |
67 | size_t bytes; | |
68 | size_t allocation; | |
69 | bool is_compression; | |
70 | }; | |
71 | ||
72 | /* Overall compression constants for zlib. */ | |
73 | ||
74 | static const size_t Z_BUFFER_LENGTH = 4096; | |
75 | static const size_t MIN_STREAM_ALLOCATION = 1024; | |
76 | ||
77 | /* For zlib, allocate SIZE count of ITEMS and return the address, OPAQUE | |
78 | is unused. */ | |
79 | ||
80 | static void * | |
81 | lto_zalloc (void *opaque, unsigned items, unsigned size) | |
82 | { | |
83 | gcc_assert (opaque == Z_NULL); | |
84 | return xmalloc (items * size); | |
85 | } | |
86 | ||
87 | /* For zlib, free memory at ADDRESS, OPAQUE is unused. */ | |
88 | ||
89 | static void | |
90 | lto_zfree (void *opaque, void *address) | |
91 | { | |
92 | gcc_assert (opaque == Z_NULL); | |
93 | free (address); | |
94 | } | |
95 | ||
96 | /* Return a zlib compression level that zlib will not reject. Normalizes | |
97 | the compression level from the command line flag, clamping non-default | |
98 | values to the appropriate end of their valid range. */ | |
99 | ||
100 | static int | |
101 | lto_normalized_zlib_level (void) | |
102 | { | |
103 | int level = flag_lto_compression_level; | |
104 | ||
105 | if (level != Z_DEFAULT_COMPRESSION) | |
106 | { | |
107 | if (level < Z_NO_COMPRESSION) | |
108 | level = Z_NO_COMPRESSION; | |
109 | else if (level > Z_BEST_COMPRESSION) | |
110 | level = Z_BEST_COMPRESSION; | |
111 | } | |
112 | ||
113 | return level; | |
114 | } | |
115 | ||
116 | /* Create a new compression stream, with CALLBACK flush function passed | |
117 | OPAQUE token, IS_COMPRESSION indicates if compressing or uncompressing. */ | |
118 | ||
119 | static struct lto_compression_stream * | |
120 | lto_new_compression_stream (void (*callback) (const char *, unsigned, void *), | |
121 | void *opaque, bool is_compression) | |
122 | { | |
123 | struct lto_compression_stream *stream | |
124 | = (struct lto_compression_stream *) xmalloc (sizeof (*stream)); | |
125 | ||
126 | memset (stream, 0, sizeof (*stream)); | |
127 | stream->callback = callback; | |
128 | stream->opaque = opaque; | |
129 | stream->is_compression = is_compression; | |
130 | ||
131 | return stream; | |
132 | } | |
133 | ||
134 | /* Append NUM_CHARS from address BASE to STREAM. */ | |
135 | ||
136 | static void | |
137 | lto_append_to_compression_stream (struct lto_compression_stream *stream, | |
138 | const char *base, size_t num_chars) | |
139 | { | |
140 | size_t required = stream->bytes + num_chars; | |
141 | ||
142 | if (stream->allocation < required) | |
143 | { | |
144 | if (stream->allocation == 0) | |
145 | stream->allocation = MIN_STREAM_ALLOCATION; | |
146 | while (stream->allocation < required) | |
147 | stream->allocation *= 2; | |
148 | ||
149 | stream->buffer = (char *) xrealloc (stream->buffer, stream->allocation); | |
150 | } | |
151 | ||
152 | memcpy (stream->buffer + stream->bytes, base, num_chars); | |
153 | stream->bytes += num_chars; | |
154 | } | |
155 | ||
156 | /* Free the buffer and memory associated with STREAM. */ | |
157 | ||
158 | static void | |
159 | lto_destroy_compression_stream (struct lto_compression_stream *stream) | |
160 | { | |
161 | free (stream->buffer); | |
162 | free (stream); | |
163 | } | |
164 | ||
165 | /* Return a new compression stream, with CALLBACK flush function passed | |
166 | OPAQUE token. */ | |
167 | ||
168 | struct lto_compression_stream * | |
169 | lto_start_compression (void (*callback) (const char *, unsigned, void *), | |
170 | void *opaque) | |
171 | { | |
172 | return lto_new_compression_stream (callback, opaque, true); | |
173 | } | |
174 | ||
175 | /* Append NUM_CHARS from address BASE to STREAM. */ | |
176 | ||
177 | void | |
178 | lto_compress_block (struct lto_compression_stream *stream, | |
179 | const char *base, size_t num_chars) | |
180 | { | |
181 | gcc_assert (stream->is_compression); | |
182 | ||
183 | lto_append_to_compression_stream (stream, base, num_chars); | |
184 | lto_stats.num_output_il_bytes += num_chars; | |
185 | } | |
186 | ||
187 | /* Finalize STREAM compression, and free stream allocations. */ | |
188 | ||
189 | void | |
190 | lto_end_compression (struct lto_compression_stream *stream) | |
191 | { | |
192 | unsigned char *cursor = (unsigned char *) stream->buffer; | |
193 | size_t remaining = stream->bytes; | |
194 | const size_t outbuf_length = Z_BUFFER_LENGTH; | |
195 | unsigned char *outbuf = (unsigned char *) xmalloc (outbuf_length); | |
196 | z_stream out_stream; | |
197 | size_t compressed_bytes = 0; | |
198 | int status; | |
199 | ||
200 | gcc_assert (stream->is_compression); | |
201 | ||
202 | out_stream.next_out = outbuf; | |
203 | out_stream.avail_out = outbuf_length; | |
204 | out_stream.next_in = cursor; | |
205 | out_stream.avail_in = remaining; | |
206 | out_stream.zalloc = lto_zalloc; | |
207 | out_stream.zfree = lto_zfree; | |
208 | out_stream.opaque = Z_NULL; | |
209 | ||
210 | status = deflateInit (&out_stream, lto_normalized_zlib_level ()); | |
211 | if (status != Z_OK) | |
212 | internal_error ("compressed stream: %s", zError (status)); | |
213 | ||
214 | do | |
215 | { | |
216 | size_t in_bytes, out_bytes; | |
217 | ||
218 | status = deflate (&out_stream, Z_FINISH); | |
219 | if (status != Z_OK && status != Z_STREAM_END) | |
220 | internal_error ("compressed stream: %s", zError (status)); | |
221 | ||
222 | in_bytes = remaining - out_stream.avail_in; | |
223 | out_bytes = outbuf_length - out_stream.avail_out; | |
224 | ||
225 | stream->callback ((const char *) outbuf, out_bytes, stream->opaque); | |
226 | lto_stats.num_compressed_il_bytes += out_bytes; | |
227 | compressed_bytes += out_bytes; | |
228 | ||
229 | cursor += in_bytes; | |
230 | remaining -= in_bytes; | |
231 | ||
232 | out_stream.next_out = outbuf; | |
233 | out_stream.avail_out = outbuf_length; | |
234 | out_stream.next_in = cursor; | |
235 | out_stream.avail_in = remaining; | |
236 | } | |
237 | while (status != Z_STREAM_END); | |
238 | ||
239 | status = deflateEnd (&out_stream); | |
240 | if (status != Z_OK) | |
241 | internal_error ("compressed stream: %s", zError (status)); | |
242 | ||
243 | lto_destroy_compression_stream (stream); | |
244 | free (outbuf); | |
245 | } | |
246 | ||
247 | /* Return a new uncompression stream, with CALLBACK flush function passed | |
248 | OPAQUE token. */ | |
249 | ||
250 | struct lto_compression_stream * | |
251 | lto_start_uncompression (void (*callback) (const char *, unsigned, void *), | |
252 | void *opaque) | |
253 | { | |
254 | return lto_new_compression_stream (callback, opaque, false); | |
255 | } | |
256 | ||
257 | /* Append NUM_CHARS from address BASE to STREAM. */ | |
258 | ||
259 | void | |
260 | lto_uncompress_block (struct lto_compression_stream *stream, | |
261 | const char *base, size_t num_chars) | |
262 | { | |
263 | gcc_assert (!stream->is_compression); | |
264 | ||
265 | lto_append_to_compression_stream (stream, base, num_chars); | |
266 | lto_stats.num_input_il_bytes += num_chars; | |
267 | } | |
268 | ||
269 | /* Finalize STREAM uncompression, and free stream allocations. | |
48e1416a | 270 | |
7bfefa9d | 271 | Because of the way LTO IL streams are compressed, there may be several |
272 | concatenated compressed segments in the accumulated data, so for this | |
273 | function we iterate decompressions until no data remains. */ | |
274 | ||
275 | void | |
276 | lto_end_uncompression (struct lto_compression_stream *stream) | |
277 | { | |
278 | unsigned char *cursor = (unsigned char *) stream->buffer; | |
279 | size_t remaining = stream->bytes; | |
280 | const size_t outbuf_length = Z_BUFFER_LENGTH; | |
281 | unsigned char *outbuf = (unsigned char *) xmalloc (outbuf_length); | |
282 | size_t uncompressed_bytes = 0; | |
283 | ||
284 | gcc_assert (!stream->is_compression); | |
285 | ||
286 | while (remaining > 0) | |
287 | { | |
288 | z_stream in_stream; | |
289 | size_t out_bytes; | |
290 | int status; | |
291 | ||
292 | in_stream.next_out = outbuf; | |
293 | in_stream.avail_out = outbuf_length; | |
294 | in_stream.next_in = cursor; | |
295 | in_stream.avail_in = remaining; | |
296 | in_stream.zalloc = lto_zalloc; | |
297 | in_stream.zfree = lto_zfree; | |
298 | in_stream.opaque = Z_NULL; | |
299 | ||
300 | status = inflateInit (&in_stream); | |
301 | if (status != Z_OK) | |
302 | internal_error ("compressed stream: %s", zError (status)); | |
303 | ||
304 | do | |
305 | { | |
306 | size_t in_bytes; | |
307 | ||
308 | status = inflate (&in_stream, Z_SYNC_FLUSH); | |
309 | if (status != Z_OK && status != Z_STREAM_END) | |
310 | internal_error ("compressed stream: %s", zError (status)); | |
311 | ||
312 | in_bytes = remaining - in_stream.avail_in; | |
313 | out_bytes = outbuf_length - in_stream.avail_out; | |
314 | ||
315 | stream->callback ((const char *) outbuf, out_bytes, stream->opaque); | |
316 | lto_stats.num_uncompressed_il_bytes += out_bytes; | |
317 | uncompressed_bytes += out_bytes; | |
318 | ||
319 | cursor += in_bytes; | |
320 | remaining -= in_bytes; | |
321 | ||
322 | in_stream.next_out = outbuf; | |
323 | in_stream.avail_out = outbuf_length; | |
324 | in_stream.next_in = cursor; | |
325 | in_stream.avail_in = remaining; | |
326 | } | |
327 | while (!(status == Z_STREAM_END && out_bytes == 0)); | |
328 | ||
329 | status = inflateEnd (&in_stream); | |
330 | if (status != Z_OK) | |
331 | internal_error ("compressed stream: %s", zError (status)); | |
332 | } | |
333 | ||
334 | lto_destroy_compression_stream (stream); | |
335 | free (outbuf); | |
336 | } |