| /* String pool for GCC. |
| Copyright (C) 2000-2022 Free Software Foundation, Inc. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it under |
| the terms of the GNU General Public License as published by the Free |
| Software Foundation; either version 3, or (at your option) any later |
| version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| /* String text, identifier text and identifier node allocator. |
| Identifiers are uniquely stored in a hash table. |
| |
| We use cpplib's hash table implementation. libiberty's |
| hashtab.c is not used because it requires 100% average space |
| overhead per string, which is unacceptable. Also, this algorithm |
| is faster. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tree.h" |
| |
| struct ht *ident_hash; |
| |
| static hashnode alloc_node (cpp_hash_table *); |
| static int mark_ident (struct cpp_reader *, hashnode, const void *); |
| |
| static void * |
| stringpool_ggc_alloc (size_t x) |
| { |
| return ggc_alloc_atomic (x); |
| } |
| |
| /* Initialize the string pool. */ |
| void |
| init_stringpool (void) |
| { |
| /* Clean up if we're called more than once. |
| (We can't make this idempotent since identifiers contain state) */ |
| if (ident_hash) |
| ht_destroy (ident_hash); |
| |
| /* Create with 16K (2^14) entries. */ |
| ident_hash = ht_create (14); |
| ident_hash->alloc_node = alloc_node; |
| ident_hash->alloc_subobject = stringpool_ggc_alloc; |
| } |
| |
| /* Allocate a hash node. */ |
| static hashnode |
| alloc_node (cpp_hash_table *table ATTRIBUTE_UNUSED) |
| { |
| return GCC_IDENT_TO_HT_IDENT (make_node (IDENTIFIER_NODE)); |
| } |
| |
| /* Allocate and return a string constant of length LENGTH, containing |
| CONTENTS. If LENGTH is -1, CONTENTS is assumed to be a |
| nul-terminated string, and the length is calculated using strlen. */ |
| |
| const char * |
| ggc_alloc_string (const char *contents, int length MEM_STAT_DECL) |
| { |
| if (length == -1) |
| length = strlen (contents); |
| |
| if (!length) |
| return ""; |
| |
| char *result = (char *) ggc_alloc_atomic (length + 1); |
| memcpy (result, contents, length); |
| result[length] = '\0'; |
| |
| return (const char *) result; |
| } |
| |
| /* Return an IDENTIFIER_NODE whose name is TEXT (a null-terminated string). |
| If an identifier with that name has previously been referred to, |
| the same node is returned this time. */ |
| |
| #undef get_identifier |
| |
| tree |
| get_identifier (const char *text) |
| { |
| hashnode ht_node = ht_lookup (ident_hash, |
| (const unsigned char *) text, |
| strlen (text), HT_ALLOC); |
| |
| /* ht_node can't be NULL here. */ |
| return HT_IDENT_TO_GCC_IDENT (ht_node); |
| } |
| |
| /* Identical to get_identifier, except that the length is assumed |
| known. */ |
| |
| tree |
| get_identifier_with_length (const char *text, size_t length) |
| { |
| hashnode ht_node = ht_lookup (ident_hash, |
| (const unsigned char *) text, |
| length, HT_ALLOC); |
| |
| /* ht_node can't be NULL here. */ |
| return HT_IDENT_TO_GCC_IDENT (ht_node); |
| } |
| |
| /* If an identifier with the name TEXT (a null-terminated string) has |
| previously been referred to, return that node; otherwise return |
| NULL_TREE. */ |
| |
| tree |
| maybe_get_identifier (const char *text) |
| { |
| hashnode ht_node; |
| |
| ht_node = ht_lookup (ident_hash, (const unsigned char *) text, |
| strlen (text), HT_NO_INSERT); |
| if (ht_node) |
| return HT_IDENT_TO_GCC_IDENT (ht_node); |
| |
| return NULL_TREE; |
| } |
| |
| /* Report some basic statistics about the string pool. */ |
| |
| void |
| stringpool_statistics (void) |
| { |
| ht_dump_statistics (ident_hash); |
| } |
| |
| /* Mark an identifier for GC. */ |
| |
| static int |
| mark_ident (struct cpp_reader *pfile ATTRIBUTE_UNUSED, hashnode h, |
| const void *v ATTRIBUTE_UNUSED) |
| { |
| gt_ggc_m_9tree_node (HT_IDENT_TO_GCC_IDENT (h)); |
| return 1; |
| } |
| |
| /* Return true if an identifier should be removed from the table. */ |
| |
| static int |
| maybe_delete_ident (struct cpp_reader *pfile ATTRIBUTE_UNUSED, hashnode h, |
| const void *v ATTRIBUTE_UNUSED) |
| { |
| return !ggc_marked_p (HT_IDENT_TO_GCC_IDENT (h)); |
| } |
| |
| /* Mark the trees hanging off the identifier node for GGC. These are |
| handled specially (not using gengtype) because identifiers are only |
| roots during one part of compilation. */ |
| |
| void |
| ggc_mark_stringpool (void) |
| { |
| ht_forall (ident_hash, mark_ident, NULL); |
| } |
| |
| /* Purge the identifier hash of identifiers which are no longer |
| referenced. */ |
| |
| void |
| ggc_purge_stringpool (void) |
| { |
| ht_purge (ident_hash, maybe_delete_ident, NULL); |
| } |
| |
| /* Pointer-walking routine for strings (not very interesting, since |
| strings don't contain pointers). */ |
| |
| void |
| gt_pch_p_S (void *obj ATTRIBUTE_UNUSED, void *x ATTRIBUTE_UNUSED, |
| gt_pointer_operator op ATTRIBUTE_UNUSED, |
| void *cookie ATTRIBUTE_UNUSED) |
| { |
| } |
| |
| /* PCH pointer-walking routine for strings. */ |
| |
| void |
| gt_pch_n_S (const void *x) |
| { |
| gt_pch_note_object (CONST_CAST (void *, x), CONST_CAST (void *, x), |
| >_pch_p_S); |
| } |
| |
| void |
| gt_pch_n_S2 (const void *x, size_t string_len) |
| { |
| gt_pch_note_object (CONST_CAST (void *, x), CONST_CAST (void *, x), |
| >_pch_p_S, string_len); |
| } |
| |
| |
| /* User-callable entry point for marking string X. */ |
| |
| void |
| gt_pch_nx (const char *& x) |
| { |
| gt_pch_n_S (x); |
| } |
| |
| void |
| gt_pch_nx (char *& x) |
| { |
| gt_pch_n_S (x); |
| } |
| |
| void |
| gt_pch_nx (unsigned char *& x) |
| { |
| gt_pch_n_S (x); |
| } |
| |
| void |
| gt_pch_nx (unsigned char& x ATTRIBUTE_UNUSED) |
| { |
| } |
| |
| void |
| gt_pch_nx (unsigned char *x, gt_pointer_operator op, void *cookie) |
| { |
| op (x, NULL, cookie); |
| } |
| |
| /* Handle saving and restoring the string pool for PCH. */ |
| |
| /* SPD is saved in the PCH file and holds the information needed |
| to restore the string pool. */ |
| |
| struct GTY(()) string_pool_data { |
| ht_identifier_ptr * |
| GTY((length ("%h.nslots"), |
| nested_ptr (union tree_node, "%h ? GCC_IDENT_TO_HT_IDENT (%h) : NULL", |
| "%h ? HT_IDENT_TO_GCC_IDENT (%h) : NULL"))) |
| entries; |
| unsigned int nslots; |
| unsigned int nelements; |
| }; |
| |
| static GTY(()) struct string_pool_data * spd; |
| |
| /* Save the stringpool data in SPD. */ |
| |
| void |
| gt_pch_save_stringpool (void) |
| { |
| spd = ggc_alloc<string_pool_data> (); |
| spd->nslots = ident_hash->nslots; |
| spd->nelements = ident_hash->nelements; |
| spd->entries = ggc_vec_alloc<ht_identifier_ptr> (spd->nslots); |
| memcpy (spd->entries, ident_hash->entries, |
| spd->nslots * sizeof (spd->entries[0])); |
| } |
| |
| /* Return the stringpool to its state before gt_pch_save_stringpool |
| was called. */ |
| |
| void |
| gt_pch_fixup_stringpool (void) |
| { |
| } |
| |
| /* A PCH file has been restored, which loaded SPD; fill the real hash table |
| from SPD. */ |
| |
| void |
| gt_pch_restore_stringpool (void) |
| { |
| ht_load (ident_hash, spd->entries, spd->nslots, spd->nelements, false); |
| spd = NULL; |
| } |
| |
| #include "gt-stringpool.h" |