LCOV - code coverage report
Current view: top level - gcc - tree-streamer.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 95.9 % 169 162
Test Date: 2026-02-28 14:20:25 Functions: 100.0 % 12 12
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Miscellaneous utilities for tree streaming.  Things that are used
       2              :    in both input and output are here.
       3              : 
       4              :    Copyright (C) 2011-2026 Free Software Foundation, Inc.
       5              :    Contributed by Diego Novillo <dnovillo@google.com>
       6              : 
       7              : This file is part of GCC.
       8              : 
       9              : GCC is free software; you can redistribute it and/or modify it under
      10              : the terms of the GNU General Public License as published by the Free
      11              : Software Foundation; either version 3, or (at your option) any later
      12              : version.
      13              : 
      14              : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      15              : WARRANTY; without even the implied warranty of MERCHANTABILITY or
      16              : FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      17              : for more details.
      18              : 
      19              : You should have received a copy of the GNU General Public License
      20              : along with GCC; see the file COPYING3.  If not see
      21              : <http://www.gnu.org/licenses/>.  */
      22              : 
      23              : #include "config.h"
      24              : #include "system.h"
      25              : #include "coretypes.h"
      26              : #include "backend.h"
      27              : #include "tree.h"
      28              : #include "gimple.h"
      29              : #include "tree-streamer.h"
      30              : #include "cgraph.h"
      31              : 
      32              : /* Table indexed by machine_mode, used for 2 different purposes.
      33              :    During streaming out we record there non-zero value for all modes
      34              :    that were streamed out.
      35              :    During streaming in, we translate the on the disk mode using this
      36              :    table.  For normal LTO it is set to identity, for ACCEL_COMPILER
      37              :    depending on the mode_table content.  */
      38              : unsigned char streamer_mode_table[MAX_MACHINE_MODE];
      39              : 
      40              : /* Check that all the TS_* structures handled by the streamer_write_* and
      41              :    streamer_read_* routines are exactly ALL the structures defined in
      42              :    treestruct.def.  */
      43              : 
      44              : void
      45        51612 : streamer_check_handled_ts_structures (void)
      46              : {
      47        51612 :   bool handled_p[LAST_TS_ENUM];
      48        51612 :   unsigned i;
      49              : 
      50        51612 :   memset (&handled_p, 0, sizeof (handled_p));
      51              : 
      52              :   /* These are the TS_* structures that are either handled or
      53              :      explicitly ignored by the streamer routines.  */
      54        51612 :   handled_p[TS_BASE] = true;
      55        51612 :   handled_p[TS_TYPED] = true;
      56        51612 :   handled_p[TS_COMMON] = true;
      57        51612 :   handled_p[TS_INT_CST] = true;
      58        51612 :   handled_p[TS_POLY_INT_CST] = true;
      59        51612 :   handled_p[TS_REAL_CST] = true;
      60        51612 :   handled_p[TS_FIXED_CST] = true;
      61        51612 :   handled_p[TS_VECTOR] = true;
      62        51612 :   handled_p[TS_STRING] = true;
      63        51612 :   handled_p[TS_RAW_DATA_CST] = true;
      64        51612 :   handled_p[TS_COMPLEX] = true;
      65        51612 :   handled_p[TS_IDENTIFIER] = true;
      66        51612 :   handled_p[TS_DECL_MINIMAL] = true;
      67        51612 :   handled_p[TS_DECL_COMMON] = true;
      68        51612 :   handled_p[TS_DECL_WRTL] = true;
      69        51612 :   handled_p[TS_DECL_NON_COMMON] = true;
      70        51612 :   handled_p[TS_DECL_WITH_VIS] = true;
      71        51612 :   handled_p[TS_FIELD_DECL] = true;
      72        51612 :   handled_p[TS_VAR_DECL] = true;
      73        51612 :   handled_p[TS_PARM_DECL] = true;
      74        51612 :   handled_p[TS_LABEL_DECL] = true;
      75        51612 :   handled_p[TS_RESULT_DECL] = true;
      76        51612 :   handled_p[TS_CONST_DECL] = true;
      77        51612 :   handled_p[TS_TYPE_DECL] = true;
      78        51612 :   handled_p[TS_FUNCTION_DECL] = true;
      79        51612 :   handled_p[TS_TYPE_COMMON] = true;
      80        51612 :   handled_p[TS_TYPE_WITH_LANG_SPECIFIC] = true;
      81        51612 :   handled_p[TS_TYPE_NON_COMMON] = true;
      82        51612 :   handled_p[TS_LIST] = true;
      83        51612 :   handled_p[TS_VEC] = true;
      84        51612 :   handled_p[TS_EXP] = true;
      85        51612 :   handled_p[TS_SSA_NAME] = true;
      86        51612 :   handled_p[TS_BLOCK] = true;
      87        51612 :   handled_p[TS_BINFO] = true;
      88        51612 :   handled_p[TS_STATEMENT_LIST] = true;
      89        51612 :   handled_p[TS_CONSTRUCTOR] = true;
      90        51612 :   handled_p[TS_OMP_CLAUSE] = true;
      91        51612 :   handled_p[TS_OPTIMIZATION] = true;
      92        51612 :   handled_p[TS_TARGET_OPTION] = true;
      93        51612 :   handled_p[TS_TRANSLATION_UNIT_DECL] = true;
      94              : 
      95              :   /* Anything not marked above will trigger the following assertion.
      96              :      If this assertion triggers, it means that there is a new TS_*
      97              :      structure that should be handled by the streamer.  */
      98      2116092 :   for (i = 0; i < LAST_TS_ENUM; i++)
      99      2064480 :     gcc_assert (handled_p[i]);
     100        51612 : }
     101              : 
     102              : 
     103              : /* Helper for streamer_tree_cache_insert_1.  Add T to CACHE->NODES at
     104              :    slot IX.  */
     105              : 
     106              : static void
     107    109748509 : streamer_tree_cache_add_to_node_array (struct streamer_tree_cache_d *cache,
     108              :                                        unsigned ix, tree t, hashval_t hash)
     109              : {
     110              :   /* We're either replacing an old element or appending consecutively.  */
     111    109748509 :   if (cache->nodes.exists ())
     112              :     {
     113     42536491 :       if (cache->nodes.length () == ix)
     114     42383233 :         cache->nodes.safe_push (t);
     115              :       else
     116       153258 :         cache->nodes[ix] = t;
     117              :     }
     118    109748509 :   if (cache->hashes.exists ())
     119              :     {
     120     57622982 :       if (cache->hashes.length () == ix)
     121     57622982 :         cache->hashes.safe_push (hash);
     122              :       else
     123            0 :         cache->hashes[ix] = hash;
     124              :     }
     125    109748509 : }
     126              : 
     127              : 
     128              : /* Helper for streamer_tree_cache_insert and streamer_tree_cache_insert_at.
     129              :    CACHE, T, and IX_P are as in streamer_tree_cache_insert.
     130              : 
     131              :    If INSERT_AT_NEXT_SLOT_P is true, T is inserted at the next available
     132              :    slot in the cache.  Otherwise, T is inserted at the position indicated
     133              :    in *IX_P.
     134              : 
     135              :    If T already existed in CACHE, return true.  Otherwise,
     136              :    return false.  */
     137              : 
     138              : static bool
     139     67212018 : streamer_tree_cache_insert_1 (struct streamer_tree_cache_d *cache,
     140              :                               tree t, hashval_t hash, unsigned *ix_p,
     141              :                               bool insert_at_next_slot_p)
     142              : {
     143     67212018 :   bool existed_p;
     144              : 
     145     67212018 :   gcc_assert (t);
     146              : 
     147     67212018 :   unsigned int &ix = cache->node_map->get_or_insert (t, &existed_p);
     148     67212018 :   if (!existed_p)
     149              :     {
     150              :       /* Determine the next slot to use in the cache.  */
     151     43063820 :       if (insert_at_next_slot_p)
     152      8113882 :         ix = cache->next_idx++;
     153              :       else
     154     34949938 :         ix = *ix_p;
     155              : 
     156     43063820 :       streamer_tree_cache_add_to_node_array (cache, ix, t, hash);
     157              :     }
     158              :   else
     159              :     {
     160     24148198 :       if (!insert_at_next_slot_p && ix != *ix_p)
     161              :         {
     162              :           /* If the caller wants to insert T at a specific slot
     163              :              location, and ENTRY->TO does not match *IX_P, add T to
     164              :              the requested location slot.  */
     165     24148198 :           ix = *ix_p;
     166     24148198 :           streamer_tree_cache_add_to_node_array (cache, ix, t, hash);
     167              :         }
     168              :     }
     169              : 
     170     67212018 :   if (ix_p)
     171     66938860 :     *ix_p = ix;
     172              : 
     173     67212018 :   return existed_p;
     174              : }
     175              : 
     176              : 
     177              : /* Insert tree node T in CACHE.  If T already existed in the cache
     178              :    return true.  Otherwise, return false.
     179              : 
     180              :    If IX_P is non-null, update it with the index into the cache where
     181              :    T has been stored.  */
     182              : 
     183              : bool
     184      8113882 : streamer_tree_cache_insert (struct streamer_tree_cache_d *cache, tree t,
     185              :                             hashval_t hash, unsigned *ix_p)
     186              : {
     187      8113882 :   return streamer_tree_cache_insert_1 (cache, t, hash, ix_p, true);
     188              : }
     189              : 
     190              : 
     191              : /* Replace the tree node with T in CACHE at slot IX.  */
     192              : 
     193              : void
     194       153258 : streamer_tree_cache_replace_tree (struct streamer_tree_cache_d *cache,
     195              :                                   tree t, unsigned ix)
     196              : {
     197       153258 :   hashval_t hash = 0;
     198       153258 :   if (cache->hashes.exists ())
     199            0 :     hash = streamer_tree_cache_get_hash (cache, ix);
     200       153258 :   if (!cache->node_map)
     201       153258 :     streamer_tree_cache_add_to_node_array (cache, ix, t, hash);
     202              :   else
     203            0 :     streamer_tree_cache_insert_1 (cache, t, hash, &ix, false);
     204       153258 : }
     205              : 
     206              : 
     207              : /* Appends tree node T to CACHE, even if T already existed in it.  */
     208              : 
     209              : void
     210    101481369 : streamer_tree_cache_append (struct streamer_tree_cache_d *cache,
     211              :                             tree t, hashval_t hash)
     212              : {
     213    101481369 :   unsigned ix = cache->next_idx++;
     214    101481369 :   if (!cache->node_map)
     215     42383233 :     streamer_tree_cache_add_to_node_array (cache, ix, t, hash);
     216              :   else
     217     59098136 :     streamer_tree_cache_insert_1 (cache, t, hash, &ix, false);
     218    101481369 : }
     219              : 
     220              : /* Return true if tree node T exists in CACHE, otherwise false.  If IX_P is
     221              :    not NULL, write to *IX_P the index into the cache where T is stored
     222              :    ((unsigned)-1 if T is not found).  */
     223              : 
     224              : bool
     225     60150430 : streamer_tree_cache_lookup (struct streamer_tree_cache_d *cache, tree t,
     226              :                             unsigned *ix_p)
     227              : {
     228     60150430 :   unsigned *slot;
     229     60150430 :   bool retval;
     230     60150430 :   unsigned ix;
     231              : 
     232     60150430 :   gcc_assert (t);
     233              : 
     234     60150430 :   slot = cache->node_map->get (t);
     235     60150430 :   if (slot == NULL)
     236              :     {
     237              :       retval = false;
     238              :       ix = -1;
     239              :     }
     240              :   else
     241              :     {
     242     39439285 :       retval = true;
     243     39439285 :       ix = *slot;
     244              :     }
     245              : 
     246     60150430 :   if (ix_p)
     247     37474586 :     *ix_p = ix;
     248              : 
     249     60150430 :   return retval;
     250              : }
     251              : 
     252              : 
     253              : /* Verify that NODE is in CACHE.  */
     254              : 
     255              : static void
     256      5130990 : verify_common_node_recorded (struct streamer_tree_cache_d *cache, tree node)
     257              : {
     258              :   /* Restrict this to flag_checking only because in general violating it is
     259              :      harmless plus we never know what happens on all targets/frontend/flag(!)
     260              :      combinations.  */
     261      5130990 :   if (!flag_checking)
     262              :     return;
     263              : 
     264      5130610 :   if (cache->node_map)
     265      3143140 :     gcc_assert (streamer_tree_cache_lookup (cache, node, NULL));
     266              :   else
     267              :     {
     268      1987470 :       bool found = false;
     269      1987470 :       gcc_assert (cache->nodes.exists ());
     270              :       /* Linear search...  */
     271    111497067 :       for (unsigned i = 0; !found && i < cache->nodes.length (); ++i)
     272    109509597 :         if (cache->nodes[i] == node)
     273      1987470 :           found = true;
     274      1987470 :       gcc_assert (found);
     275              :     }
     276              : }
     277              : 
     278              : 
     279              : /* Record NODE in CACHE.  */
     280              : 
     281              : static void
     282     89792293 : record_common_node (struct streamer_tree_cache_d *cache, tree node)
     283              : {
     284              :   /* If we recursively end up at nodes we do not want to preload simply don't.
     285              :      ???  We'd want to verify that this doesn't happen, or alternatively
     286              :      do not recurse at all.  */
     287     96462580 :   if (node == char_type_node)
     288              :     return;
     289              : 
     290     96462572 :   gcc_checking_assert (node != boolean_type_node
     291              :                        && node != boolean_true_node
     292              :                        && node != boolean_false_node);
     293              : 
     294              :   /* We have to make sure to fill exactly the same number of
     295              :      elements for all frontends.  That can include NULL trees.
     296              :      As our hash table can't deal with zero entries we'll simply stream
     297              :      a random other tree.  A NULL tree never will be looked up so it
     298              :      doesn't matter which tree we replace it with, just to be sure
     299              :      use error_mark_node.  */
     300     96462572 :   if (!node)
     301      4617931 :     node = error_mark_node;
     302              : 
     303              :   /* This hash needs to be equal for all frontend and lto1 invocations.  So
     304              :      just use the position in the cache as hash value.
     305              :      Small integers are used by hash_tree to record positions within scc
     306              :      hash. Values are not in same range.  */
     307     96462572 :   streamer_tree_cache_append (cache, node, cache->next_idx + 0xc001);
     308              : 
     309     96462572 :   switch (TREE_CODE (node))
     310              :     {
     311              :     case ERROR_MARK:
     312              :     case FIELD_DECL:
     313              :     case FIXED_POINT_TYPE:
     314              :     case IDENTIFIER_NODE:
     315              :     case INTEGER_CST:
     316              :     case INTEGER_TYPE:
     317              :     case REAL_TYPE:
     318              :     case TREE_LIST:
     319              :     case VOID_CST:
     320              :     case VOID_TYPE:
     321              :     case OPAQUE_TYPE:
     322              :       /* No recursive trees.  */
     323              :       break;
     324      6670287 :     case ARRAY_TYPE:
     325      6670287 :     case POINTER_TYPE:
     326      6670287 :     case REFERENCE_TYPE:
     327      6670287 :       record_common_node (cache, TREE_TYPE (node));
     328      6670287 :       break;
     329      5130990 :     case COMPLEX_TYPE:
     330              :       /* Verify that a complex type's component type (node_type) has been
     331              :          handled already (and we thus don't need to recurse here).  */
     332      5130990 :       verify_common_node_recorded (cache, TREE_TYPE (node));
     333      5130990 :       break;
     334       513091 :     case RECORD_TYPE:
     335              :       /* The FIELD_DECLs of structures should be shared, so that every
     336              :          COMPONENT_REF uses the same tree node when referencing a field.
     337              :          Pointer equality between FIELD_DECLs is used by the alias
     338              :          machinery to compute overlapping component references (see
     339              :          nonoverlapping_component_refs_p and
     340              :          nonoverlapping_component_refs_of_decl_p).  */
     341      2565455 :       for (tree f = TYPE_FIELDS (node); f; f = TREE_CHAIN (f))
     342      2052364 :         record_common_node (cache, f);
     343              :       break;
     344            0 :     default:
     345              :       /* Unexpected tree code.  */
     346            0 :       gcc_unreachable ();
     347              :     }
     348              : }
     349              : 
     350              : 
     351              : /* Preload common nodes into CACHE and make sure they are merged
     352              :    properly according to the gimple type table.  */
     353              : 
     354              : static void
     355       513099 : preload_common_nodes (struct streamer_tree_cache_d *cache)
     356              : {
     357       513099 :   unsigned i;
     358              : 
     359     10261980 :   for (i = 0; i < itk_none; i++)
     360              :     /* Skip itk_char.  char_type_node is dependent on -f[un]signed-char.  */
     361      9748881 :     if (i != itk_char)
     362      9235782 :       record_common_node (cache, integer_types[i]);
     363              : 
     364      2565495 :   for (i = 0; i < stk_type_kind_last; i++)
     365      2052396 :     record_common_node (cache, sizetype_tab[i]);
     366              : 
     367     83635137 :   for (i = 0; i < TI_MAX; i++)
     368              :     /* Skip boolean type and constants, they are frontend dependent.  */
     369     83122038 :     if (i != TI_BOOLEAN_TYPE
     370     83122038 :         && i != TI_BOOLEAN_FALSE
     371     82095840 :         && i != TI_BOOLEAN_TRUE
     372              :         /* MAIN_IDENTIFIER is not always initialized by Fortran FE.  */
     373     82095840 :         && i != TI_MAIN_IDENTIFIER
     374              :         /* PID_TYPE is initialized only by C family front-ends.  */
     375     81069642 :         && i != TI_PID_TYPE
     376              :         /* Skip optimization and target option nodes; they depend on flags.  */
     377     81069642 :         && i != TI_OPTIMIZATION_DEFAULT
     378              :         && i != TI_OPTIMIZATION_CURRENT
     379     80043444 :         && i != TI_TARGET_OPTION_DEFAULT
     380              :         && i != TI_TARGET_OPTION_CURRENT
     381              :         && i != TI_CURRENT_TARGET_PRAGMA
     382              :         && i != TI_CURRENT_OPTIMIZE_PRAGMA
     383              :         /* SCEV state shouldn't reach the IL.  */
     384              :         && i != TI_CHREC_DONT_KNOW
     385              :         && i != TI_CHREC_KNOWN
     386              :         /* Skip va_list* related nodes if offloading.  For native LTO
     387              :            we want them to be merged for the stdarg pass, for offloading
     388              :            they might not be identical between host and offloading target.  */
     389     76451751 :         && (!lto_stream_offload_p
     390            0 :             || (i != TI_VA_LIST_TYPE
     391              :                 && i != TI_VA_LIST_GPR_COUNTER_FIELD
     392            0 :                 && i != TI_VA_LIST_FPR_COUNTER_FIELD)))
     393     76451751 :       record_common_node (cache, global_trees[i]);
     394       513099 : }
     395              : 
     396              : 
     397              : /* Create a cache of pickled nodes.  */
     398              : 
     399              : struct streamer_tree_cache_d *
     400       513099 : streamer_tree_cache_create (bool with_hashes, bool with_map, bool with_vec)
     401              : {
     402       513099 :   struct streamer_tree_cache_d *cache;
     403              : 
     404       513099 :   cache = XCNEW (struct streamer_tree_cache_d);
     405              : 
     406       513099 :   if (with_map)
     407       314352 :     cache->node_map = new hash_map<tree, unsigned> (251);
     408       513099 :   cache->next_idx = 0;
     409       513099 :   if (with_vec)
     410       198747 :     cache->nodes.create (165);
     411       513099 :   if (with_hashes)
     412       267768 :     cache->hashes.create (165);
     413              : 
     414              :   /* Load all the well-known tree nodes that are always created by
     415              :      the compiler on startup.  This prevents writing them out
     416              :      unnecessarily.  */
     417       513099 :   preload_common_nodes (cache);
     418              : 
     419       513099 :   return cache;
     420              : }
     421              : 
     422              : 
     423              : /* Delete the streamer cache C.  */
     424              : 
     425              : void
     426       513099 : streamer_tree_cache_delete (struct streamer_tree_cache_d *c)
     427              : {
     428       513099 :   if (c == NULL)
     429              :     return;
     430              : 
     431       827451 :   delete c->node_map;
     432       513099 :   c->node_map = NULL;
     433       513099 :   c->nodes.release ();
     434       513099 :   c->hashes.release ();
     435       513099 :   free (c);
     436              : }
        

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.