On 11/3/20 4:16 PM, Nathan Sidwell wrote:
This is probably the messiest diff.

Let's break this diff apart a bit more, for digestibility.

Here's the MODULE_VECTOR piece. This is a sparse array used for name lookup. A namespace symbol table entry may contain one of these, which holds the bindings for each loaded module. If a module doesn't bind that name, there'll be no slot for it. The current TU always uses slot 0. The Global Module uses slot 1, and in a named-module partitions share slot 2. Slots 1 & 2 are used for duplicate declaration matching.
[These slots are managed entirely in name-lookup].

For data layout purposes, the vector is partitioned into clusters. Each cluster holds two slots. A slot is the pointer to the bound value (often an OVERLOAD) or a lazy cookie, a slot index, and a slot span. The span is usually '1', except for namespaces which are module-spanning entities. It is '0' in the fixed slots, for simplicity. Both the index and spans are 16-bit 'unsigned short', and the slot is a 32 or 64 bit pointer. Thus on a 64-bit host we can pack 2 indices, 2 spans and 2 pointers without padding. (the slots are not adjacent to their index/span).

The slot itself can contain either a pointer or a load cookie. These are distinguished using bit 0 -- 1 for cookie, zero for pointer. Thus pointers have their natural representation (they are 4 or 8 byte aligned). The lazy cookie happens to be partitioned into two pieces. A pair of bits concerning pending template instantiations and member definitions, with the remaining bits (29 or 61) being a section number in that module's CMI. When we do name lookup we see if there's a lazy cookie in a slot of interest, and if so load that section before proceeding. (the same cookie may be present in several slots [in different bindings], loading will populate all those slots.) There's a preceding check to determine whether the contents of that slot are visible to us (the containing module has been exported to us). the result is that importing a module is a cheap operation with an amortized load cost depending what you look at. (there's a flag to turn lazy loading off)

As you can guess, there's a limit of 16383 imported modules (there is an independent limit of 2^31 imported entities)

Indices are always in increasing order, and by construction we only need to append to the array.

nathan


--
Nathan Sidwell
diff --git c/gcc/cp/cp-tree.h w/gcc/cp/cp-tree.h
index 63724c0e84f..4752ddef898 100644
--- c/gcc/cp/cp-tree.h
+++ w/gcc/cp/cp-tree.h
@@ -929,6 +959,84 @@ struct named_decl_hash : ggc_remove <tree> {
   static void mark_deleted (value_type) { gcc_unreachable (); }
 };
 
+/* Bindings for modules are held in a sparse array.  There is always a
+   current TU slot, others are allocated as needed.  By construction
+   of the importing mechanism we only ever need to append to the
+   array.  Rather than have straight index/slot tuples, we bunch them
+   up for greater packing.
+
+   The cluster representation packs well on a 64-bit system.  */
+
+#define MODULE_VECTOR_SLOTS_PER_CLUSTER 2
+struct mc_index {
+  unsigned short base;
+  unsigned short span;
+};
+
+struct GTY(()) module_cluster
+{
+  mc_index GTY((skip)) indices[MODULE_VECTOR_SLOTS_PER_CLUSTER];
+  mc_slot slots[MODULE_VECTOR_SLOTS_PER_CLUSTER];
+};
+
+/* These two fields overlay lang flags.  So don't use those.  */
+#define MODULE_VECTOR_ALLOC_CLUSTERS(NODE) \
+  (MODULE_VECTOR_CHECK (NODE)->base.u.dependence_info.clique)
+#define MODULE_VECTOR_NUM_CLUSTERS(NODE) \
+  (MODULE_VECTOR_CHECK (NODE)->base.u.dependence_info.base)
+#define MODULE_VECTOR_CLUSTER_BASE(NODE) \
+  (((tree_module_vec *)MODULE_VECTOR_CHECK (NODE))->vec)
+#define MODULE_VECTOR_CLUSTER_LAST(NODE) \
+  (&MODULE_VECTOR_CLUSTER (NODE, MODULE_VECTOR_NUM_CLUSTERS (NODE) - 1))
+#define MODULE_VECTOR_CLUSTER(NODE,IX) \
+  (((tree_module_vec *)MODULE_VECTOR_CHECK (NODE))->vec[IX])
+
+struct GTY(()) tree_module_vec {
+  struct tree_base base;
+  tree name;
+  module_cluster GTY((length ("%h.base.u.dependence_info.base"))) vec[1];
+};
+
+/* The name of a module vector.  */
+#define MODULE_VECTOR_NAME(NODE) \
+  (((tree_module_vec *)MODULE_VECTOR_CHECK (NODE))->name)
+
+/* tree_module_vec does uses  base.u.dependence_info.base field for
+   length.  It does not have lang_flag etc available!  */
+
+/* These two flags note if a module-vector contains deduplicated
+   bindings (i.e. multiple declarations in different imports).  */
+/* This binding contains duplicate references to a global module
+   entity.  */
+#define MODULE_VECTOR_GLOBAL_DUPS_P(NODE) \
+  (MODULE_VECTOR_CHECK (NODE)->base.static_flag)
+/* This binding contains duplicate references to a partioned module
+   entity.  */
+#define MODULE_VECTOR_PARTITION_DUPS_P(NODE) \
+  (MODULE_VECTOR_CHECK (NODE)->base.volatile_flag)
+
+/* These two flags indicate the provenence of the bindings on this
+   particular vector slot.  We can of course determine this from slot
+   number, but that's a relatively expensive lookup.  This avoids
+   that when iterating.  */
+/* This slot is part of the global module (a header unit).  */
+#define MODULE_BINDING_GLOBAL_P(NODE) \
+  (OVERLOAD_CHECK (NODE)->base.static_flag)
+/* This slot is part of the current module (a partition or primary).  */
+#define MODULE_BINDING_PARTITION_P(NODE)		\
+  (OVERLOAD_CHECK (NODE)->base.volatile_flag)
+
+/* There are specializations of a template keyed to this binding.  */
+#define MODULE_VECTOR_PENDING_SPECIALIZATIONS_P(NODE) \
+  (MODULE_VECTOR_CHECK (NODE)->base.public_flag)
+/* The key is in a header unit (not a named module partition or
+   primary).  */
+#define MODULE_VECTOR_PENDING_IS_HEADER_P(NODE) \
+  (MODULE_VECTOR_CHECK (NODE)->base.protected_flag)
+/* The key is in a named module (primary or partition).  */
+#define MODULE_VECTOR_PENDING_IS_PARTITION_P(NODE) \
+  (MODULE_VECTOR_CHECK (NODE)->base.private_flag)
+
 /* Simplified unique_ptr clone to release a tree vec on exit.  */
 
 class releasing_vec
@@ -7349,6 +7657,7 @@ extern tree hash_tree_cons			(tree, tree, tree);
 extern tree hash_tree_chain			(tree, tree);
 extern tree build_qualified_name		(tree, tree, tree, bool);
 extern tree build_ref_qualified_type		(tree, cp_ref_qualifier);
+extern tree make_module_vec			(tree, unsigned clusters);
 inline tree ovl_first				(tree) ATTRIBUTE_PURE;
 extern tree ovl_make				(tree fn,
 						 tree next = NULL_TREE);
@@ -7972,14 +8295,16 @@ type_unknown_p (const_tree expr)
 inline hashval_t
 named_decl_hash::hash (const value_type decl)
 {
-  tree name = OVL_NAME (decl);
+  tree name = (TREE_CODE (decl) == MODULE_VECTOR
+	       ? MODULE_VECTOR_NAME (decl) : OVL_NAME (decl));
   return name ? IDENTIFIER_HASH_VALUE (name) : 0;
 }
 
 inline bool
 named_decl_hash::equal (const value_type existing, compare_type candidate)
 {
-  tree name = OVL_NAME (existing);
+  tree name = (TREE_CODE (existing) == MODULE_VECTOR
+	       ? MODULE_VECTOR_NAME (existing) : OVL_NAME (existing));
   return candidate == name;
 }
 
diff --git c/gcc/cp/tree.c w/gcc/cp/tree.c
index 28e591086b3..9979fd4f828 100644
--- c/gcc/cp/tree.c
+++ w/gcc/cp/tree.c
@@ -2218,6 +2226,23 @@ build_ref_qualified_type (tree type, cp_ref_qualifier rqual)
   return build_cp_fntype_variant (type, rqual, raises, late);
 }
 
+tree
+make_module_vec (tree name, unsigned clusters MEM_STAT_DECL)
+{
+  /* Stored in an unsigned short, but we're limited to the number of
+     modules anyway.  */
+  gcc_checking_assert (clusters <= (unsigned short)(~0));
+  size_t length = (clusters * sizeof (module_cluster)
+		   + sizeof (tree_module_vec) - sizeof (module_cluster));
+  tree vec = ggc_alloc_cleared_tree_node_stat (length PASS_MEM_STAT);
+  TREE_SET_CODE (vec, MODULE_VECTOR);
+  MODULE_VECTOR_NAME (vec) = name;
+  MODULE_VECTOR_ALLOC_CLUSTERS (vec) = clusters;
+  MODULE_VECTOR_NUM_CLUSTERS (vec) = 0;
+
+  return vec;
+}
+
 /* Make a raw overload node containing FN.  */
 
 tree

Reply via email to