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