]>
Commit | Line | Data |
---|---|---|
d7f09764 DN |
1 | /* LTO IL compression streams. |
2 | ||
8d9254fc | 3 | Copyright (C) 2009-2020 Free Software Foundation, Inc. |
d7f09764 DN |
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" | |
957060b5 AM |
24 | #include "coretypes.h" |
25 | #include "backend.h" | |
957060b5 AM |
26 | #include "tree.h" |
27 | #include "gimple.h" | |
28 | #include "cgraph.h" | |
29 | #include "lto-streamer.h" | |
d7f09764 DN |
30 | /* zlib.h includes other system headers. Those headers may test feature |
31 | test macros. config.h may define feature test macros. For this reason, | |
32 | zlib.h needs to be included after, rather than before, config.h and | |
33 | system.h. */ | |
34 | #include <zlib.h> | |
d7f09764 | 35 | #include "lto-compress.h" |
ed7dc894 | 36 | #include "timevar.h" |
d7f09764 | 37 | |
87741e51 ML |
38 | #ifdef HAVE_ZSTD_H |
39 | #include <zstd.h> | |
40 | #endif | |
41 | ||
d7f09764 DN |
42 | /* Compression stream structure, holds the flush callback and opaque token, |
43 | the buffered data, and a note of whether compressing or uncompressing. */ | |
44 | ||
45 | struct lto_compression_stream | |
46 | { | |
47 | void (*callback) (const char *, unsigned, void *); | |
48 | void *opaque; | |
49 | char *buffer; | |
50 | size_t bytes; | |
51 | size_t allocation; | |
52 | bool is_compression; | |
53 | }; | |
54 | ||
55 | /* Overall compression constants for zlib. */ | |
56 | ||
57 | static const size_t Z_BUFFER_LENGTH = 4096; | |
58 | static const size_t MIN_STREAM_ALLOCATION = 1024; | |
59 | ||
60 | /* For zlib, allocate SIZE count of ITEMS and return the address, OPAQUE | |
61 | is unused. */ | |
62 | ||
63 | static void * | |
64 | lto_zalloc (void *opaque, unsigned items, unsigned size) | |
65 | { | |
66 | gcc_assert (opaque == Z_NULL); | |
67 | return xmalloc (items * size); | |
68 | } | |
69 | ||
70 | /* For zlib, free memory at ADDRESS, OPAQUE is unused. */ | |
71 | ||
72 | static void | |
73 | lto_zfree (void *opaque, void *address) | |
74 | { | |
75 | gcc_assert (opaque == Z_NULL); | |
76 | free (address); | |
77 | } | |
78 | ||
79 | /* Return a zlib compression level that zlib will not reject. Normalizes | |
80 | the compression level from the command line flag, clamping non-default | |
81 | values to the appropriate end of their valid range. */ | |
82 | ||
83 | static int | |
84 | lto_normalized_zlib_level (void) | |
85 | { | |
86 | int level = flag_lto_compression_level; | |
87 | ||
88 | if (level != Z_DEFAULT_COMPRESSION) | |
89 | { | |
90 | if (level < Z_NO_COMPRESSION) | |
91 | level = Z_NO_COMPRESSION; | |
92 | else if (level > Z_BEST_COMPRESSION) | |
93 | level = Z_BEST_COMPRESSION; | |
94 | } | |
95 | ||
96 | return level; | |
97 | } | |
98 | ||
87741e51 ML |
99 | /* Free the buffer and memory associated with STREAM. */ |
100 | ||
101 | static void | |
102 | lto_destroy_compression_stream (struct lto_compression_stream *stream) | |
103 | { | |
104 | free (stream->buffer); | |
105 | free (stream); | |
106 | } | |
107 | ||
108 | #ifdef HAVE_ZSTD_H | |
109 | /* Return a zstd compression level that zstd will not reject. Normalizes | |
110 | the compression level from the command line flag, clamping non-default | |
111 | values to the appropriate end of their valid range. */ | |
112 | ||
113 | static int | |
114 | lto_normalized_zstd_level (void) | |
115 | { | |
116 | int level = flag_lto_compression_level; | |
117 | ||
7a26ff04 ML |
118 | if (level < 0) |
119 | level = 0; | |
120 | else if (level > ZSTD_maxCLevel ()) | |
121 | level = ZSTD_maxCLevel (); | |
87741e51 ML |
122 | |
123 | return level; | |
124 | } | |
125 | ||
126 | /* Compress STREAM using ZSTD algorithm. */ | |
127 | ||
128 | static void | |
129 | lto_compression_zstd (struct lto_compression_stream *stream) | |
130 | { | |
131 | unsigned char *cursor = (unsigned char *) stream->buffer; | |
132 | size_t size = stream->bytes; | |
133 | ||
134 | timevar_push (TV_IPA_LTO_COMPRESS); | |
135 | size_t const outbuf_length = ZSTD_compressBound (size); | |
136 | char *outbuf = (char *) xmalloc (outbuf_length); | |
137 | ||
138 | size_t const csize = ZSTD_compress (outbuf, outbuf_length, cursor, size, | |
139 | lto_normalized_zstd_level ()); | |
140 | ||
141 | if (ZSTD_isError (csize)) | |
142 | internal_error ("compressed stream: %s", ZSTD_getErrorName (csize)); | |
143 | ||
053dc901 | 144 | lto_stats.num_compressed_il_bytes += csize; |
87741e51 ML |
145 | stream->callback (outbuf, csize, NULL); |
146 | ||
147 | lto_destroy_compression_stream (stream); | |
148 | free (outbuf); | |
149 | timevar_pop (TV_IPA_LTO_COMPRESS); | |
150 | } | |
151 | ||
152 | /* Uncompress STREAM using ZSTD algorithm. */ | |
153 | ||
154 | static void | |
155 | lto_uncompression_zstd (struct lto_compression_stream *stream) | |
156 | { | |
157 | unsigned char *cursor = (unsigned char *) stream->buffer; | |
158 | size_t size = stream->bytes; | |
159 | ||
160 | timevar_push (TV_IPA_LTO_DECOMPRESS); | |
161 | unsigned long long const rsize = ZSTD_getFrameContentSize (cursor, size); | |
162 | if (rsize == ZSTD_CONTENTSIZE_ERROR) | |
163 | internal_error ("original not compressed with zstd"); | |
164 | else if (rsize == ZSTD_CONTENTSIZE_UNKNOWN) | |
165 | internal_error ("original size unknown"); | |
166 | ||
167 | char *outbuf = (char *) xmalloc (rsize); | |
168 | size_t const dsize = ZSTD_decompress (outbuf, rsize, cursor, size); | |
169 | ||
170 | if (ZSTD_isError (dsize)) | |
171 | internal_error ("decompressed stream: %s", ZSTD_getErrorName (dsize)); | |
172 | ||
053dc901 | 173 | lto_stats.num_uncompressed_il_bytes += dsize; |
87741e51 ML |
174 | stream->callback (outbuf, dsize, stream->opaque); |
175 | ||
176 | lto_destroy_compression_stream (stream); | |
177 | free (outbuf); | |
178 | timevar_pop (TV_IPA_LTO_DECOMPRESS); | |
179 | } | |
180 | ||
181 | #endif | |
182 | ||
d7f09764 DN |
183 | /* Create a new compression stream, with CALLBACK flush function passed |
184 | OPAQUE token, IS_COMPRESSION indicates if compressing or uncompressing. */ | |
185 | ||
186 | static struct lto_compression_stream * | |
187 | lto_new_compression_stream (void (*callback) (const char *, unsigned, void *), | |
188 | void *opaque, bool is_compression) | |
189 | { | |
190 | struct lto_compression_stream *stream | |
191 | = (struct lto_compression_stream *) xmalloc (sizeof (*stream)); | |
192 | ||
193 | memset (stream, 0, sizeof (*stream)); | |
194 | stream->callback = callback; | |
195 | stream->opaque = opaque; | |
196 | stream->is_compression = is_compression; | |
197 | ||
198 | return stream; | |
199 | } | |
200 | ||
201 | /* Append NUM_CHARS from address BASE to STREAM. */ | |
202 | ||
203 | static void | |
204 | lto_append_to_compression_stream (struct lto_compression_stream *stream, | |
205 | const char *base, size_t num_chars) | |
206 | { | |
207 | size_t required = stream->bytes + num_chars; | |
208 | ||
209 | if (stream->allocation < required) | |
210 | { | |
211 | if (stream->allocation == 0) | |
212 | stream->allocation = MIN_STREAM_ALLOCATION; | |
213 | while (stream->allocation < required) | |
214 | stream->allocation *= 2; | |
215 | ||
216 | stream->buffer = (char *) xrealloc (stream->buffer, stream->allocation); | |
217 | } | |
218 | ||
219 | memcpy (stream->buffer + stream->bytes, base, num_chars); | |
220 | stream->bytes += num_chars; | |
221 | } | |
222 | ||
d7f09764 DN |
223 | /* Return a new compression stream, with CALLBACK flush function passed |
224 | OPAQUE token. */ | |
225 | ||
226 | struct lto_compression_stream * | |
227 | lto_start_compression (void (*callback) (const char *, unsigned, void *), | |
228 | void *opaque) | |
229 | { | |
230 | return lto_new_compression_stream (callback, opaque, true); | |
231 | } | |
232 | ||
233 | /* Append NUM_CHARS from address BASE to STREAM. */ | |
234 | ||
235 | void | |
236 | lto_compress_block (struct lto_compression_stream *stream, | |
237 | const char *base, size_t num_chars) | |
238 | { | |
239 | gcc_assert (stream->is_compression); | |
240 | ||
241 | lto_append_to_compression_stream (stream, base, num_chars); | |
242 | lto_stats.num_output_il_bytes += num_chars; | |
243 | } | |
244 | ||
87741e51 ML |
245 | static void ATTRIBUTE_UNUSED |
246 | lto_compression_zlib (struct lto_compression_stream *stream) | |
d7f09764 DN |
247 | { |
248 | unsigned char *cursor = (unsigned char *) stream->buffer; | |
249 | size_t remaining = stream->bytes; | |
250 | const size_t outbuf_length = Z_BUFFER_LENGTH; | |
251 | unsigned char *outbuf = (unsigned char *) xmalloc (outbuf_length); | |
252 | z_stream out_stream; | |
253 | size_t compressed_bytes = 0; | |
254 | int status; | |
255 | ||
256 | gcc_assert (stream->is_compression); | |
257 | ||
ed7dc894 JH |
258 | timevar_push (TV_IPA_LTO_COMPRESS); |
259 | ||
d7f09764 DN |
260 | out_stream.next_out = outbuf; |
261 | out_stream.avail_out = outbuf_length; | |
262 | out_stream.next_in = cursor; | |
263 | out_stream.avail_in = remaining; | |
264 | out_stream.zalloc = lto_zalloc; | |
265 | out_stream.zfree = lto_zfree; | |
266 | out_stream.opaque = Z_NULL; | |
267 | ||
268 | status = deflateInit (&out_stream, lto_normalized_zlib_level ()); | |
269 | if (status != Z_OK) | |
270 | internal_error ("compressed stream: %s", zError (status)); | |
271 | ||
272 | do | |
273 | { | |
274 | size_t in_bytes, out_bytes; | |
275 | ||
276 | status = deflate (&out_stream, Z_FINISH); | |
277 | if (status != Z_OK && status != Z_STREAM_END) | |
278 | internal_error ("compressed stream: %s", zError (status)); | |
279 | ||
280 | in_bytes = remaining - out_stream.avail_in; | |
281 | out_bytes = outbuf_length - out_stream.avail_out; | |
282 | ||
283 | stream->callback ((const char *) outbuf, out_bytes, stream->opaque); | |
284 | lto_stats.num_compressed_il_bytes += out_bytes; | |
285 | compressed_bytes += out_bytes; | |
286 | ||
287 | cursor += in_bytes; | |
288 | remaining -= in_bytes; | |
289 | ||
290 | out_stream.next_out = outbuf; | |
291 | out_stream.avail_out = outbuf_length; | |
292 | out_stream.next_in = cursor; | |
293 | out_stream.avail_in = remaining; | |
294 | } | |
295 | while (status != Z_STREAM_END); | |
296 | ||
297 | status = deflateEnd (&out_stream); | |
298 | if (status != Z_OK) | |
299 | internal_error ("compressed stream: %s", zError (status)); | |
300 | ||
301 | lto_destroy_compression_stream (stream); | |
302 | free (outbuf); | |
ed7dc894 | 303 | timevar_pop (TV_IPA_LTO_COMPRESS); |
d7f09764 DN |
304 | } |
305 | ||
87741e51 ML |
306 | void |
307 | lto_end_compression (struct lto_compression_stream *stream) | |
308 | { | |
309 | #ifdef HAVE_ZSTD_H | |
310 | lto_compression_zstd (stream); | |
311 | #else | |
312 | lto_compression_zlib (stream); | |
313 | #endif | |
314 | } | |
315 | ||
d7f09764 DN |
316 | /* Return a new uncompression stream, with CALLBACK flush function passed |
317 | OPAQUE token. */ | |
318 | ||
319 | struct lto_compression_stream * | |
320 | lto_start_uncompression (void (*callback) (const char *, unsigned, void *), | |
321 | void *opaque) | |
322 | { | |
323 | return lto_new_compression_stream (callback, opaque, false); | |
324 | } | |
325 | ||
326 | /* Append NUM_CHARS from address BASE to STREAM. */ | |
327 | ||
328 | void | |
329 | lto_uncompress_block (struct lto_compression_stream *stream, | |
330 | const char *base, size_t num_chars) | |
331 | { | |
332 | gcc_assert (!stream->is_compression); | |
333 | ||
334 | lto_append_to_compression_stream (stream, base, num_chars); | |
335 | lto_stats.num_input_il_bytes += num_chars; | |
336 | } | |
337 | ||
87741e51 ML |
338 | static void |
339 | lto_uncompression_zlib (struct lto_compression_stream *stream) | |
d7f09764 DN |
340 | { |
341 | unsigned char *cursor = (unsigned char *) stream->buffer; | |
342 | size_t remaining = stream->bytes; | |
343 | const size_t outbuf_length = Z_BUFFER_LENGTH; | |
344 | unsigned char *outbuf = (unsigned char *) xmalloc (outbuf_length); | |
345 | size_t uncompressed_bytes = 0; | |
346 | ||
347 | gcc_assert (!stream->is_compression); | |
ed7dc894 | 348 | timevar_push (TV_IPA_LTO_DECOMPRESS); |
d7f09764 DN |
349 | |
350 | while (remaining > 0) | |
351 | { | |
352 | z_stream in_stream; | |
353 | size_t out_bytes; | |
354 | int status; | |
355 | ||
356 | in_stream.next_out = outbuf; | |
357 | in_stream.avail_out = outbuf_length; | |
358 | in_stream.next_in = cursor; | |
359 | in_stream.avail_in = remaining; | |
360 | in_stream.zalloc = lto_zalloc; | |
361 | in_stream.zfree = lto_zfree; | |
362 | in_stream.opaque = Z_NULL; | |
363 | ||
364 | status = inflateInit (&in_stream); | |
365 | if (status != Z_OK) | |
366 | internal_error ("compressed stream: %s", zError (status)); | |
367 | ||
368 | do | |
369 | { | |
370 | size_t in_bytes; | |
371 | ||
372 | status = inflate (&in_stream, Z_SYNC_FLUSH); | |
373 | if (status != Z_OK && status != Z_STREAM_END) | |
374 | internal_error ("compressed stream: %s", zError (status)); | |
375 | ||
376 | in_bytes = remaining - in_stream.avail_in; | |
377 | out_bytes = outbuf_length - in_stream.avail_out; | |
378 | ||
379 | stream->callback ((const char *) outbuf, out_bytes, stream->opaque); | |
380 | lto_stats.num_uncompressed_il_bytes += out_bytes; | |
381 | uncompressed_bytes += out_bytes; | |
382 | ||
383 | cursor += in_bytes; | |
384 | remaining -= in_bytes; | |
385 | ||
386 | in_stream.next_out = outbuf; | |
387 | in_stream.avail_out = outbuf_length; | |
388 | in_stream.next_in = cursor; | |
389 | in_stream.avail_in = remaining; | |
390 | } | |
391 | while (!(status == Z_STREAM_END && out_bytes == 0)); | |
392 | ||
393 | status = inflateEnd (&in_stream); | |
394 | if (status != Z_OK) | |
395 | internal_error ("compressed stream: %s", zError (status)); | |
396 | } | |
397 | ||
398 | lto_destroy_compression_stream (stream); | |
399 | free (outbuf); | |
ed7dc894 | 400 | timevar_pop (TV_IPA_LTO_DECOMPRESS); |
d7f09764 | 401 | } |
87741e51 ML |
402 | |
403 | void | |
404 | lto_end_uncompression (struct lto_compression_stream *stream, | |
405 | lto_compression compression) | |
406 | { | |
407 | #ifdef HAVE_ZSTD_H | |
408 | if (compression == ZSTD) | |
409 | { | |
410 | lto_uncompression_zstd (stream); | |
411 | return; | |
412 | } | |
413 | #endif | |
414 | if (compression == ZSTD) | |
415 | internal_error ("compiler does not support ZSTD LTO compression"); | |
416 | ||
417 | lto_uncompression_zlib (stream); | |
418 | } |