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