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