Rollup changes for Helgrind:

* Add new client request VALGRIND_HG_CLEAN_MEMORY_HEAPBLOCK.  This is
  like VALGRIND_HG_CLEAN_MEMORY but doesn't take an address range.
  Instead it takes a single argument which is supposed to be a pointer
  to the start of, or anywhere within, a heap allocated block.
  Helgrind then finds the block and paints it as belonging to the
  calling thread.  This is needed for correctly describing the
  behaviour of threadsafe reference counting when applied to classes
  involving inheritance of release methods or involving multiple
  inheritance.

* Add statistics counters for all basic VTS operations (tick, join,
  cmpLEQ, cmp_structural).

* Rewrite VTS__cmp_structural to be much faster.



git-svn-id: svn://svn.valgrind.org/valgrind/trunk@11123 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/helgrind/hg_main.c b/helgrind/hg_main.c
index 7df5e47..ab9da5d 100644
--- a/helgrind/hg_main.c
+++ b/helgrind/hg_main.c
@@ -3879,32 +3879,62 @@
 
 
 /* For error creation: map 'data_addr' to a malloc'd chunk, if any.
-   Slow linear search. */
+   Slow linear search.  With a bit of hash table help if 'data_addr'
+   is either the start of a block or up to 15 word-sized steps along
+   from the start of a block. */
 
 static inline Bool addr_is_in_MM_Chunk( MallocMeta* mm, Addr a )
 {
-   if (a < mm->payload) return False;
-   if (a >= mm->payload + mm->szB) return False;
-   return True;
+   /* Accept 'a' as within 'mm' if 'mm's size is zero and 'a' points
+      right at it. */
+  if (UNLIKELY(mm->szB == 0 && a == mm->payload))
+     return True;
+  /* else normal interval rules apply */
+  if (LIKELY(a < mm->payload)) return False;
+  if (LIKELY(a >= mm->payload + mm->szB)) return False;
+  return True;
 }
 
-void HG_(mm_find_containing_block)( /*OUT*/ExeContext** where,
+Bool HG_(mm_find_containing_block)( /*OUT*/ExeContext** where,
                                     /*OUT*/Addr*        payload,
                                     /*OUT*/SizeT*       szB,
                                     Addr                data_addr )
 {
    MallocMeta* mm;
+   Int i;
+   const Int n_fast_check_words = 16;
+
+   /* First, do a few fast searches on the basis that data_addr might
+      be exactly the start of a block or up to 15 words inside.  This
+      can happen commonly via the creq
+      _VG_USERREQ__HG_CLEAN_MEMORY_HEAPBLOCK. */
+   for (i = 0; i < n_fast_check_words; i++) {
+      mm = VG_(HT_lookup)( hg_mallocmeta_table,
+                           data_addr - (UWord)(UInt)i * sizeof(UWord) );
+      if (UNLIKELY(mm && addr_is_in_MM_Chunk(mm, data_addr)))
+         goto found;
+   }
+
    /* Well, this totally sucks.  But without using an interval tree or
-      some such, it's hard to see how to do better. */
+      some such, it's hard to see how to do better.  We have to check
+      every block in the entire table. */
    VG_(HT_ResetIter)(hg_mallocmeta_table);
    while ( (mm = VG_(HT_Next)(hg_mallocmeta_table)) ) {
-      if (UNLIKELY(addr_is_in_MM_Chunk(mm, data_addr))) {
-         *where   = mm->where;
-         *payload = mm->payload;
-         *szB     = mm->szB;
-         return;
-      }
+      if (UNLIKELY(addr_is_in_MM_Chunk(mm, data_addr)))
+         goto found;
    }
+
+   /* Not found.  Bah. */
+   return False;
+   /*NOTREACHED*/
+
+  found:
+   tl_assert(mm);
+   tl_assert(addr_is_in_MM_Chunk(mm, data_addr));
+   if (where)   *where   = mm->where;
+   if (payload) *payload = mm->payload;
+   if (szB)     *szB     = mm->szB;
+   return True;
 }
 
 
@@ -4295,6 +4325,23 @@
          }
          break;
 
+      case _VG_USERREQ__HG_CLEAN_MEMORY_HEAPBLOCK: {
+         Addr  payload = 0;
+         SizeT pszB = 0;
+         if (0) VG_(printf)("VG_USERREQ__HG_CLEAN_MEMORY_HEAPBLOCK(%#lx)\n",
+                            args[1]);
+         if (HG_(mm_find_containing_block)(NULL, &payload, &pszB, args[1])) {
+            if (pszB > 0) {
+               evh__die_mem(payload, pszB);
+               evh__new_mem(payload, pszB);
+            }
+            *ret = pszB;
+         } else {
+            *ret = (UWord)-1;
+         }
+         break;
+      }
+
       case _VG_USERREQ__HG_ARANGE_MAKE_UNTRACKED:
          if (0) VG_(printf)("HG_ARANGE_MAKE_UNTRACKED(%#lx,%ld)\n",
                             args[1], args[2]);