Two-level map

Change-Id: I008ddd9a1ef773a8d17e439d24d75d37bbf1a076
diff --git a/gdb/regcache.c b/gdb/regcache.c
index 8ca59fd..394efdf 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -313,41 +313,17 @@
     gdb_assert (regnum < gdbarch_num_regs (arch ()));
 }
 
-/* Key type for target_ptid_regcache_map.  */
+/* Type to map a ptid to a list of regcaches (one thread may have multiple
+   regcaches, associated to different gdbarches).  */
 
-struct target_ptid
-{
-  target_ptid (process_stratum_target *target, ptid_t ptid)
-    : target (target), ptid (ptid)
-  {}
+using ptid_regcache_map
+  = std::unordered_multimap<ptid_t, regcache_up, hash_ptid>;
 
-  process_stratum_target *target;
-  ptid_t ptid;
-
-  bool operator== (const target_ptid &other) const
-  {
-    return (this->target == other.target
-	    && this->ptid == other.ptid);
-  }
-};
-
-/* Hash function for target_ptid.  */
-
-struct hash_target_ptid
-{
-  size_t operator() (const target_ptid &val) const
-  {
-    std::hash<process_stratum_target *> h_ptr;
-    hash_ptid h_ptid;
-
-    return h_ptr (val.target) + h_ptid (val.ptid);
-  }
-};
-
-/* Type to map a (target, ptid) tuple to a regcache.  */
+/* Type to map a target to a ptid_regcache_map, holding the regcaches for the
+   threads defined by that target.  */
 
 using target_ptid_regcache_map
-  = std::unordered_multimap<target_ptid, regcache_up, hash_target_ptid>;
+  = std::unordered_map<process_stratum_target *, ptid_regcache_map>;
 
 /* Global structure containing the existing regcaches.  */
 
@@ -364,9 +340,11 @@
 {
   gdb_assert (target != nullptr);
 
-  /* Look up the regcache for this (target, ptid, arch).  */
-  target_ptid key (target, ptid);
-  auto range = regcaches.equal_range (key);
+  /* Find the ptid -> regcache map for this target.  */
+  auto &ptid_regc_map = regcaches[target];
+
+  /* Check first if a regcache for this arch already exists.  */
+  auto range = ptid_regc_map.equal_range (ptid);
   for (auto it = range.first; it != range.second; it++)
     {
       if (it->second->arch () == arch)
@@ -374,11 +352,9 @@
     }
 
   /* It does not exist, create it.  */
-  regcache *new_regcache = new regcache (target, arch, aspace);
+  regcache *new_regcache (new regcache (target, arch, aspace));
   new_regcache->set_ptid (ptid);
-
-  /* Insert it in the map.  */
-  regcaches.insert (std::make_pair (key, new_regcache));
+  ptid_regc_map.insert (std::make_pair (ptid, new_regcache));
 
   return new_regcache;
 }
@@ -458,19 +434,22 @@
 regcache_thread_ptid_changed (process_stratum_target *target,
 			      ptid_t old_ptid, ptid_t new_ptid)
 {
-  target_ptid old_key (target, old_ptid);
-  target_ptid new_key (target, new_ptid);
+  auto ptid_regc_map_it = regcaches.find (target);
 
-  auto range = regcaches.equal_range (old_key);
+  if (ptid_regc_map_it == regcaches.end ())
+    return;
+
+  auto &ptid_regc_map = ptid_regc_map_it->second;
+  auto range = ptid_regc_map.equal_range (old_ptid);
   for (auto it = range.first; it != range.second;)
     {
       regcache_up rc = std::move (it->second);
       rc->set_ptid (new_ptid);
 
-      /* Remove old before inserting new, to avoid rehashing, which would
-	 invalidate iterators.  */
-      it = regcaches.erase (it);
-      regcaches.insert (std::make_pair (new_key, std::move (rc)));
+      /* Remove old before inserting new, to avoid rehashing,
+	 which would invalidate iterators.  */
+      it = ptid_regc_map.erase (it);
+      ptid_regc_map.insert (std::make_pair (new_ptid, std::move (rc)));
     }
 }
 
@@ -495,29 +474,31 @@
       gdb_assert (ptid == minus_one_ptid);
 
       /* Delete all the regcaches.  */
-      regcaches.clear ();
+      for (auto &pair : regcaches)
+	pair.second.clear ();
     }
   else if (ptid != minus_one_ptid)
     {
       /* Non-NULL target and non-minus_one_ptid, delete all regcaches belonging
-	 to this (TARGET, PTID).  */
-      regcaches.erase (target_ptid (target, ptid));
+	to this (TARGET, PTID).  */
+      auto ptid_regc_map_it = regcaches.find (target);
+      if (ptid_regc_map_it != regcaches.end ())
+	{
+	  auto &ptid_regc_map = ptid_regc_map_it->second;
+	  ptid_regc_map.erase (ptid);
+	}
     }
   else
     {
-       /* Non-NULL target and minus_one_ptid, delete all regcaches associated
-	  to this target.
-
-	  We unfortunately don't have an efficient way to do this.  Fall back
-	  to iterating all items to find all those belonging to TARGET.  */
-       for (auto it = regcaches.begin (); it != regcaches.end ();)
-	 {
-	   if (it->second->target () == target)
-	     it = regcaches.erase (it);
-	   else
-	     it++;
-	 }
-     }
+       /* Non-NULL target and minus_one_ptid, delete all regcaches
+	  associated to this target.  */
+      auto ptid_regc_map_it = regcaches.find (target);
+      if (ptid_regc_map_it != regcaches.end ())
+	{
+	  auto &ptid_regc_map = ptid_regc_map_it->second;
+	  ptid_regc_map.clear ();
+	}
+    }
 
   if ((target == nullptr || current_thread_target == target)
       && current_thread_ptid.matches (ptid))
@@ -1498,6 +1479,18 @@
 
 namespace selftests {
 
+static size_t
+regcaches_size ()
+{
+  size_t size = 0;
+  for (auto it = regcaches.begin (); it != regcaches.end (); ++it)
+    {
+      auto &ptid_regc_map = it->second;
+      size += ptid_regc_map.size ();
+    }
+  return size;
+}
+
 /* Wrapper around get_thread_arch_aspace_regcache that does some self checks.  */
 
 static void
@@ -1517,7 +1510,7 @@
 regcaches_test (void)
 {
   /* It is empty at the start.  */
-  SELF_CHECK (regcaches.size () == 0);
+  SELF_CHECK (regcaches_size () == 0);
 
   ptid_t ptid1 (1), ptid2 (2), ptid3 (3);
 
@@ -1529,52 +1522,52 @@
   test_get_thread_arch_aspace_regcache (&test_target1, ptid1,
 					target_gdbarch (),
 					NULL);
-  SELF_CHECK (regcaches.size () == 1);
+  SELF_CHECK (regcaches_size () == 1);
 
   /* Get regcache from (target1,ptid2), a new regcache is added to
      REGCACHES.  */
   test_get_thread_arch_aspace_regcache (&test_target1, ptid2,
 					target_gdbarch (),
 					NULL);
-  SELF_CHECK (regcaches.size () == 2);
+  SELF_CHECK (regcaches_size () == 2);
 
   /* Get regcache from (target1,ptid3), a new regcache is added to
      REGCACHES.  */
   test_get_thread_arch_aspace_regcache (&test_target1, ptid3,
 					target_gdbarch (),
 					NULL);
-  SELF_CHECK (regcaches.size () == 3);
+  SELF_CHECK (regcaches_size () == 3);
 
   /* Get regcache from (target1,ptid2) again, nothing is added to
      REGCACHES.  */
   test_get_thread_arch_aspace_regcache (&test_target1, ptid2,
 					target_gdbarch (),
 					NULL);
-  SELF_CHECK (regcaches.size () == 3);
+  SELF_CHECK (regcaches_size () == 3);
 
   /* Get regcache from (target2,ptid2), a new regcache is added to
      REGCACHES, since this time we're using a different target.  */
   test_get_thread_arch_aspace_regcache (&test_target2, ptid2,
 					target_gdbarch (),
 					NULL);
-  SELF_CHECK (regcaches.size () == 4);
+  SELF_CHECK (regcaches_size () == 4);
 
   /* Mark that (target1,ptid2) changed.  The regcache of (target1,
      ptid2) should be removed from REGCACHES.  */
   registers_changed_ptid (&test_target1, ptid2);
-  SELF_CHECK (regcaches.size () == 3);
+  SELF_CHECK (regcaches_size () == 3);
 
   /* Get the regcache from (target2,ptid2) again, confirming the
      registers_changed_ptid call above did not delete it.  */
   test_get_thread_arch_aspace_regcache (&test_target2, ptid2,
 					target_gdbarch (),
 					NULL);
-  SELF_CHECK (regcaches.size () == 3);
+  SELF_CHECK (regcaches_size () == 3);
 
   /* Confirm that marking all regcaches of all targets as changed
      clears REGCACHES.  */
   registers_changed_ptid (nullptr, minus_one_ptid);
-  SELF_CHECK (regcaches.size () == 0);
+  SELF_CHECK (regcaches_size () == 0);
 }
 
 class target_ops_no_register : public test_target_ops
@@ -1911,11 +1904,16 @@
 
   /* Return the count of regcaches for (TARGET, PTID) in REGCACHES.  */
   auto regcache_count = [] (process_stratum_target *target, ptid_t ptid)
+    -> int
     {
-      target_ptid key (target, ptid);
-
-      auto range = regcaches.equal_range (key);
-      return std::distance (range.first, range.second);
+      auto ptid_regc_map_it = regcaches.find (target);
+      if (ptid_regc_map_it != regcaches.end ())
+	{
+	  auto &ptid_regc_map = ptid_regc_map_it->second;
+	  auto range = ptid_regc_map.equal_range (ptid);
+	  return std::distance (range.first, range.second);
+	}
+      return 0;
     };
 
   gdb_assert (regcaches.size () == 2);