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