1 /* LTO IL compression streams.
2 
3    Copyright (C) 2009-2021 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 #ifdef HAVE_ZSTD_H
39 #include <zstd.h>
40 #endif
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 /* Free the buffer and memory associated with STREAM.  */
100 
101 static void
lto_destroy_compression_stream(struct lto_compression_stream * stream)102 lto_destroy_compression_stream (struct lto_compression_stream *stream)
103 {
104   free (stream->buffer);
105   free (stream);
106 }
107 
108 #ifdef HAVE_ZSTD_H
109 /* Return a zstd compression level that zstd will not reject.  Normalizes
110    the compression level from the command line flag, clamping non-default
111    values to the appropriate end of their valid range.  */
112 
113 static int
lto_normalized_zstd_level(void)114 lto_normalized_zstd_level (void)
115 {
116   int level = flag_lto_compression_level;
117 
118   if (level < 0)
119     level = 0;
120   else if (level > ZSTD_maxCLevel ())
121     level = ZSTD_maxCLevel ();
122 
123   return level;
124 }
125 
126 /* Compress STREAM using ZSTD algorithm.  */
127 
128 static void
lto_compression_zstd(struct lto_compression_stream * stream)129 lto_compression_zstd (struct lto_compression_stream *stream)
130 {
131   unsigned char *cursor = (unsigned char *) stream->buffer;
132   size_t size = stream->bytes;
133 
134   timevar_push (TV_IPA_LTO_COMPRESS);
135   size_t const outbuf_length = ZSTD_compressBound (size);
136   char *outbuf = (char *) xmalloc (outbuf_length);
137 
138   size_t const csize = ZSTD_compress (outbuf, outbuf_length, cursor, size,
139 				      lto_normalized_zstd_level ());
140 
141   if (ZSTD_isError (csize))
142     internal_error ("compressed stream: %s", ZSTD_getErrorName (csize));
143 
144   lto_stats.num_compressed_il_bytes += csize;
145   stream->callback (outbuf, csize, NULL);
146 
147   lto_destroy_compression_stream (stream);
148   free (outbuf);
149   timevar_pop (TV_IPA_LTO_COMPRESS);
150 }
151 
152 /* Uncompress STREAM using ZSTD algorithm.  */
153 
154 static void
lto_uncompression_zstd(struct lto_compression_stream * stream)155 lto_uncompression_zstd (struct lto_compression_stream *stream)
156 {
157   unsigned char *cursor = (unsigned char *) stream->buffer;
158   size_t size = stream->bytes;
159 
160   timevar_push (TV_IPA_LTO_DECOMPRESS);
161   unsigned long long const rsize = ZSTD_getFrameContentSize (cursor, size);
162   if (rsize == ZSTD_CONTENTSIZE_ERROR)
163     internal_error ("original not compressed with zstd");
164   else if (rsize == ZSTD_CONTENTSIZE_UNKNOWN)
165     internal_error ("original size unknown");
166 
167   char *outbuf = (char *) xmalloc (rsize);
168   size_t const dsize = ZSTD_decompress (outbuf, rsize, cursor, size);
169 
170   if (ZSTD_isError (dsize))
171     internal_error ("decompressed stream: %s", ZSTD_getErrorName (dsize));
172 
173   lto_stats.num_uncompressed_il_bytes += dsize;
174   stream->callback (outbuf, dsize, stream->opaque);
175 
176   lto_destroy_compression_stream (stream);
177   free (outbuf);
178   timevar_pop (TV_IPA_LTO_DECOMPRESS);
179 }
180 
181 #endif
182 
183 /* Create a new compression stream, with CALLBACK flush function passed
184    OPAQUE token, IS_COMPRESSION indicates if compressing or uncompressing.  */
185 
186 static struct lto_compression_stream *
lto_new_compression_stream(void (* callback)(const char *,unsigned,void *),void * opaque,bool is_compression)187 lto_new_compression_stream (void (*callback) (const char *, unsigned, void *),
188 			    void *opaque, bool is_compression)
189 {
190   struct lto_compression_stream *stream
191     = (struct lto_compression_stream *) xmalloc (sizeof (*stream));
192 
193   memset (stream, 0, sizeof (*stream));
194   stream->callback = callback;
195   stream->opaque = opaque;
196   stream->is_compression = is_compression;
197 
198   return stream;
199 }
200 
201 /* Append NUM_CHARS from address BASE to STREAM.  */
202 
203 static void
lto_append_to_compression_stream(struct lto_compression_stream * stream,const char * base,size_t num_chars)204 lto_append_to_compression_stream (struct lto_compression_stream *stream,
205 				  const char *base, size_t num_chars)
206 {
207   size_t required = stream->bytes + num_chars;
208 
209   if (stream->allocation < required)
210     {
211       if (stream->allocation == 0)
212 	stream->allocation = MIN_STREAM_ALLOCATION;
213       while (stream->allocation < required)
214 	stream->allocation *= 2;
215 
216       stream->buffer = (char *) xrealloc (stream->buffer, stream->allocation);
217     }
218 
219   memcpy (stream->buffer + stream->bytes, base, num_chars);
220   stream->bytes += num_chars;
221 }
222 
223 /* Return a new compression stream, with CALLBACK flush function passed
224    OPAQUE token.  */
225 
226 struct lto_compression_stream *
lto_start_compression(void (* callback)(const char *,unsigned,void *),void * opaque)227 lto_start_compression (void (*callback) (const char *, unsigned, void *),
228 		       void *opaque)
229 {
230   return lto_new_compression_stream (callback, opaque, true);
231 }
232 
233 /* Append NUM_CHARS from address BASE to STREAM.  */
234 
235 void
lto_compress_block(struct lto_compression_stream * stream,const char * base,size_t num_chars)236 lto_compress_block (struct lto_compression_stream *stream,
237 		    const char *base, size_t num_chars)
238 {
239   gcc_assert (stream->is_compression);
240 
241   lto_append_to_compression_stream (stream, base, num_chars);
242   lto_stats.num_output_il_bytes += num_chars;
243 }
244 
245 static void ATTRIBUTE_UNUSED
lto_compression_zlib(struct lto_compression_stream * stream)246 lto_compression_zlib (struct lto_compression_stream *stream)
247 {
248   unsigned char *cursor = (unsigned char *) stream->buffer;
249   size_t remaining = stream->bytes;
250   const size_t outbuf_length = Z_BUFFER_LENGTH;
251   unsigned char *outbuf = (unsigned char *) xmalloc (outbuf_length);
252   z_stream out_stream;
253   size_t compressed_bytes = 0;
254   int status;
255 
256   gcc_assert (stream->is_compression);
257 
258   timevar_push (TV_IPA_LTO_COMPRESS);
259 
260   out_stream.next_out = outbuf;
261   out_stream.avail_out = outbuf_length;
262   out_stream.next_in = cursor;
263   out_stream.avail_in = remaining;
264   out_stream.zalloc = lto_zalloc;
265   out_stream.zfree = lto_zfree;
266   out_stream.opaque = Z_NULL;
267 
268   status = deflateInit (&out_stream, lto_normalized_zlib_level ());
269   if (status != Z_OK)
270     internal_error ("compressed stream: %s", zError (status));
271 
272   do
273     {
274       size_t in_bytes, out_bytes;
275 
276       status = deflate (&out_stream, Z_FINISH);
277       if (status != Z_OK && status != Z_STREAM_END)
278 	internal_error ("compressed stream: %s", zError (status));
279 
280       in_bytes = remaining - out_stream.avail_in;
281       out_bytes = outbuf_length - out_stream.avail_out;
282 
283       stream->callback ((const char *) outbuf, out_bytes, stream->opaque);
284       lto_stats.num_compressed_il_bytes += out_bytes;
285       compressed_bytes += out_bytes;
286 
287       cursor += in_bytes;
288       remaining -= in_bytes;
289 
290       out_stream.next_out = outbuf;
291       out_stream.avail_out = outbuf_length;
292       out_stream.next_in = cursor;
293       out_stream.avail_in = remaining;
294     }
295   while (status != Z_STREAM_END);
296 
297   status = deflateEnd (&out_stream);
298   if (status != Z_OK)
299     internal_error ("compressed stream: %s", zError (status));
300 
301   lto_destroy_compression_stream (stream);
302   free (outbuf);
303   timevar_pop (TV_IPA_LTO_COMPRESS);
304 }
305 
306 void
lto_end_compression(struct lto_compression_stream * stream)307 lto_end_compression (struct lto_compression_stream *stream)
308 {
309 #ifdef HAVE_ZSTD_H
310   lto_compression_zstd (stream);
311 #else
312   lto_compression_zlib (stream);
313 #endif
314 }
315 
316 /* Return a new uncompression stream, with CALLBACK flush function passed
317    OPAQUE token.  */
318 
319 struct lto_compression_stream *
lto_start_uncompression(void (* callback)(const char *,unsigned,void *),void * opaque)320 lto_start_uncompression (void (*callback) (const char *, unsigned, void *),
321 			 void *opaque)
322 {
323   return lto_new_compression_stream (callback, opaque, false);
324 }
325 
326 /* Append NUM_CHARS from address BASE to STREAM.  */
327 
328 void
lto_uncompress_block(struct lto_compression_stream * stream,const char * base,size_t num_chars)329 lto_uncompress_block (struct lto_compression_stream *stream,
330 		      const char *base, size_t num_chars)
331 {
332   gcc_assert (!stream->is_compression);
333 
334   lto_append_to_compression_stream (stream, base, num_chars);
335   lto_stats.num_input_il_bytes += num_chars;
336 }
337 
338 static void
lto_uncompression_zlib(struct lto_compression_stream * stream)339 lto_uncompression_zlib (struct lto_compression_stream *stream)
340 {
341   unsigned char *cursor = (unsigned char *) stream->buffer;
342   size_t remaining = stream->bytes;
343   const size_t outbuf_length = Z_BUFFER_LENGTH;
344   unsigned char *outbuf = (unsigned char *) xmalloc (outbuf_length);
345   size_t uncompressed_bytes = 0;
346 
347   gcc_assert (!stream->is_compression);
348   timevar_push (TV_IPA_LTO_DECOMPRESS);
349 
350   while (remaining > 0)
351     {
352       z_stream in_stream;
353       size_t out_bytes;
354       int status;
355 
356       in_stream.next_out = outbuf;
357       in_stream.avail_out = outbuf_length;
358       in_stream.next_in = cursor;
359       in_stream.avail_in = remaining;
360       in_stream.zalloc = lto_zalloc;
361       in_stream.zfree = lto_zfree;
362       in_stream.opaque = Z_NULL;
363 
364       status = inflateInit (&in_stream);
365       if (status != Z_OK)
366 	internal_error ("compressed stream: %s", zError (status));
367 
368       do
369 	{
370 	  size_t in_bytes;
371 
372 	  status = inflate (&in_stream, Z_SYNC_FLUSH);
373 	  if (status != Z_OK && status != Z_STREAM_END)
374 	    internal_error ("compressed stream: %s", zError (status));
375 
376 	  in_bytes = remaining - in_stream.avail_in;
377 	  out_bytes = outbuf_length - in_stream.avail_out;
378 
379 	  stream->callback ((const char *) outbuf, out_bytes, stream->opaque);
380 	  lto_stats.num_uncompressed_il_bytes += out_bytes;
381 	  uncompressed_bytes += out_bytes;
382 
383 	  cursor += in_bytes;
384 	  remaining -= in_bytes;
385 
386 	  in_stream.next_out = outbuf;
387 	  in_stream.avail_out = outbuf_length;
388 	  in_stream.next_in = cursor;
389 	  in_stream.avail_in = remaining;
390 	}
391       while (!(status == Z_STREAM_END && out_bytes == 0));
392 
393       status = inflateEnd (&in_stream);
394       if (status != Z_OK)
395 	internal_error ("compressed stream: %s", zError (status));
396     }
397 
398   lto_destroy_compression_stream (stream);
399   free (outbuf);
400   timevar_pop (TV_IPA_LTO_DECOMPRESS);
401 }
402 
403 void
lto_end_uncompression(struct lto_compression_stream * stream,lto_compression compression)404 lto_end_uncompression (struct lto_compression_stream *stream,
405 		       lto_compression compression)
406 {
407 #ifdef HAVE_ZSTD_H
408   if (compression == ZSTD)
409     {
410       lto_uncompression_zstd (stream);
411       return;
412     }
413 #endif
414   if (compression == ZSTD)
415     internal_error ("compiler does not support ZSTD LTO compression");
416 
417   lto_uncompression_zlib (stream);
418 }
419