1 /* LTO IL compression streams.
2 
3    Copyright (C) 2009-2014 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 "basic-block.h"
32 #include "tree-ssa-alias.h"
33 #include "internal-fn.h"
34 #include "gimple-expr.h"
35 #include "is-a.h"
36 #include "gimple.h"
37 #include "diagnostic-core.h"
38 #include "langhooks.h"
39 #include "lto-streamer.h"
40 #include "lto-compress.h"
41 
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 *
lto_zalloc(void * opaque,unsigned items,unsigned size)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
lto_zfree(void * opaque,void * address)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
lto_normalized_zlib_level(void)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 
99 /* Create a new compression stream, with CALLBACK flush function passed
100    OPAQUE token, IS_COMPRESSION indicates if compressing or uncompressing.  */
101 
102 static struct lto_compression_stream *
lto_new_compression_stream(void (* callback)(const char *,unsigned,void *),void * opaque,bool is_compression)103 lto_new_compression_stream (void (*callback) (const char *, unsigned, void *),
104 			    void *opaque, bool is_compression)
105 {
106   struct lto_compression_stream *stream
107     = (struct lto_compression_stream *) xmalloc (sizeof (*stream));
108 
109   memset (stream, 0, sizeof (*stream));
110   stream->callback = callback;
111   stream->opaque = opaque;
112   stream->is_compression = is_compression;
113 
114   return stream;
115 }
116 
117 /* Append NUM_CHARS from address BASE to STREAM.  */
118 
119 static void
lto_append_to_compression_stream(struct lto_compression_stream * stream,const char * base,size_t num_chars)120 lto_append_to_compression_stream (struct lto_compression_stream *stream,
121 				  const char *base, size_t num_chars)
122 {
123   size_t required = stream->bytes + num_chars;
124 
125   if (stream->allocation < required)
126     {
127       if (stream->allocation == 0)
128 	stream->allocation = MIN_STREAM_ALLOCATION;
129       while (stream->allocation < required)
130 	stream->allocation *= 2;
131 
132       stream->buffer = (char *) xrealloc (stream->buffer, stream->allocation);
133     }
134 
135   memcpy (stream->buffer + stream->bytes, base, num_chars);
136   stream->bytes += num_chars;
137 }
138 
139 /* Free the buffer and memory associated with STREAM.  */
140 
141 static void
lto_destroy_compression_stream(struct lto_compression_stream * stream)142 lto_destroy_compression_stream (struct lto_compression_stream *stream)
143 {
144   free (stream->buffer);
145   free (stream);
146 }
147 
148 /* Return a new compression stream, with CALLBACK flush function passed
149    OPAQUE token.  */
150 
151 struct lto_compression_stream *
lto_start_compression(void (* callback)(const char *,unsigned,void *),void * opaque)152 lto_start_compression (void (*callback) (const char *, unsigned, void *),
153 		       void *opaque)
154 {
155   return lto_new_compression_stream (callback, opaque, true);
156 }
157 
158 /* Append NUM_CHARS from address BASE to STREAM.  */
159 
160 void
lto_compress_block(struct lto_compression_stream * stream,const char * base,size_t num_chars)161 lto_compress_block (struct lto_compression_stream *stream,
162 		    const char *base, size_t num_chars)
163 {
164   gcc_assert (stream->is_compression);
165 
166   lto_append_to_compression_stream (stream, base, num_chars);
167   lto_stats.num_output_il_bytes += num_chars;
168 }
169 
170 /* Finalize STREAM compression, and free stream allocations.  */
171 
172 void
lto_end_compression(struct lto_compression_stream * stream)173 lto_end_compression (struct lto_compression_stream *stream)
174 {
175   unsigned char *cursor = (unsigned char *) stream->buffer;
176   size_t remaining = stream->bytes;
177   const size_t outbuf_length = Z_BUFFER_LENGTH;
178   unsigned char *outbuf = (unsigned char *) xmalloc (outbuf_length);
179   z_stream out_stream;
180   size_t compressed_bytes = 0;
181   int status;
182 
183   gcc_assert (stream->is_compression);
184 
185   out_stream.next_out = outbuf;
186   out_stream.avail_out = outbuf_length;
187   out_stream.next_in = cursor;
188   out_stream.avail_in = remaining;
189   out_stream.zalloc = lto_zalloc;
190   out_stream.zfree = lto_zfree;
191   out_stream.opaque = Z_NULL;
192 
193   status = deflateInit (&out_stream, lto_normalized_zlib_level ());
194   if (status != Z_OK)
195     internal_error ("compressed stream: %s", zError (status));
196 
197   do
198     {
199       size_t in_bytes, out_bytes;
200 
201       status = deflate (&out_stream, Z_FINISH);
202       if (status != Z_OK && status != Z_STREAM_END)
203 	internal_error ("compressed stream: %s", zError (status));
204 
205       in_bytes = remaining - out_stream.avail_in;
206       out_bytes = outbuf_length - out_stream.avail_out;
207 
208       stream->callback ((const char *) outbuf, out_bytes, stream->opaque);
209       lto_stats.num_compressed_il_bytes += out_bytes;
210       compressed_bytes += out_bytes;
211 
212       cursor += in_bytes;
213       remaining -= in_bytes;
214 
215       out_stream.next_out = outbuf;
216       out_stream.avail_out = outbuf_length;
217       out_stream.next_in = cursor;
218       out_stream.avail_in = remaining;
219     }
220   while (status != Z_STREAM_END);
221 
222   status = deflateEnd (&out_stream);
223   if (status != Z_OK)
224     internal_error ("compressed stream: %s", zError (status));
225 
226   lto_destroy_compression_stream (stream);
227   free (outbuf);
228 }
229 
230 /* Return a new uncompression stream, with CALLBACK flush function passed
231    OPAQUE token.  */
232 
233 struct lto_compression_stream *
lto_start_uncompression(void (* callback)(const char *,unsigned,void *),void * opaque)234 lto_start_uncompression (void (*callback) (const char *, unsigned, void *),
235 			 void *opaque)
236 {
237   return lto_new_compression_stream (callback, opaque, false);
238 }
239 
240 /* Append NUM_CHARS from address BASE to STREAM.  */
241 
242 void
lto_uncompress_block(struct lto_compression_stream * stream,const char * base,size_t num_chars)243 lto_uncompress_block (struct lto_compression_stream *stream,
244 		      const char *base, size_t num_chars)
245 {
246   gcc_assert (!stream->is_compression);
247 
248   lto_append_to_compression_stream (stream, base, num_chars);
249   lto_stats.num_input_il_bytes += num_chars;
250 }
251 
252 /* Finalize STREAM uncompression, and free stream allocations.
253 
254    Because of the way LTO IL streams are compressed, there may be several
255    concatenated compressed segments in the accumulated data, so for this
256    function we iterate decompressions until no data remains.  */
257 
258 void
lto_end_uncompression(struct lto_compression_stream * stream)259 lto_end_uncompression (struct lto_compression_stream *stream)
260 {
261   unsigned char *cursor = (unsigned char *) stream->buffer;
262   size_t remaining = stream->bytes;
263   const size_t outbuf_length = Z_BUFFER_LENGTH;
264   unsigned char *outbuf = (unsigned char *) xmalloc (outbuf_length);
265   size_t uncompressed_bytes = 0;
266 
267   gcc_assert (!stream->is_compression);
268 
269   while (remaining > 0)
270     {
271       z_stream in_stream;
272       size_t out_bytes;
273       int status;
274 
275       in_stream.next_out = outbuf;
276       in_stream.avail_out = outbuf_length;
277       in_stream.next_in = cursor;
278       in_stream.avail_in = remaining;
279       in_stream.zalloc = lto_zalloc;
280       in_stream.zfree = lto_zfree;
281       in_stream.opaque = Z_NULL;
282 
283       status = inflateInit (&in_stream);
284       if (status != Z_OK)
285 	internal_error ("compressed stream: %s", zError (status));
286 
287       do
288 	{
289 	  size_t in_bytes;
290 
291 	  status = inflate (&in_stream, Z_SYNC_FLUSH);
292 	  if (status != Z_OK && status != Z_STREAM_END)
293 	    internal_error ("compressed stream: %s", zError (status));
294 
295 	  in_bytes = remaining - in_stream.avail_in;
296 	  out_bytes = outbuf_length - in_stream.avail_out;
297 
298 	  stream->callback ((const char *) outbuf, out_bytes, stream->opaque);
299 	  lto_stats.num_uncompressed_il_bytes += out_bytes;
300 	  uncompressed_bytes += out_bytes;
301 
302 	  cursor += in_bytes;
303 	  remaining -= in_bytes;
304 
305 	  in_stream.next_out = outbuf;
306 	  in_stream.avail_out = outbuf_length;
307 	  in_stream.next_in = cursor;
308 	  in_stream.avail_in = remaining;
309 	}
310       while (!(status == Z_STREAM_END && out_bytes == 0));
311 
312       status = inflateEnd (&in_stream);
313       if (status != Z_OK)
314 	internal_error ("compressed stream: %s", zError (status));
315     }
316 
317   lto_destroy_compression_stream (stream);
318   free (outbuf);
319 }
320