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