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