LCOV - code coverage report
Current view: top level - gcc - lto-compress.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 42.2 % 185 78
Test Date: 2026-02-28 14:20:25 Functions: 70.6 % 17 12
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.