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