Files updated, added and removed in order to turn the ERASER branch into HEAD


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@1086 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/memcheck/Makefile.am b/memcheck/Makefile.am
index 60553dd..96911ed 100644
--- a/memcheck/Makefile.am
+++ b/memcheck/Makefile.am
@@ -1,15 +1,17 @@
+
+
 SUBDIRS = demangle . docs tests
 
 CFLAGS = $(WERROR) -DVG_LIBDIR="\"$(libdir)"\" \
-		-Winline -Wall -Wshadow -O -fomit-frame-pointer -g
+		-Winline -Wall -Wshadow -O -fomit-frame-pointer @PREFERRED_STACK_BOUNDARY@ -g
 
 valdir = $(libdir)/valgrind
 
-LDFLAGS = -Wl,-z -Wl,initfirst
+#LDFLAGS = -Wl,-z -Wl,initfirst
 
 INCLUDES = -I$(srcdir)/demangle
 
-bin_SCRIPTS = valgrind cachegrind vg_annotate
+bin_SCRIPTS = valgrind vg_annotate
 
 SUPP_FILES = glibc-2.1.supp glibc-2.2.supp xfree-3.supp xfree-4.supp
 
@@ -26,60 +28,103 @@
 	PATCHES_APPLIED ACKNOWLEDGEMENTS \
 	README_KDE3_FOLKS README_PACKAGERS \
 	README_MISSING_SYSCALL_OR_IOCTL TODO dosyms vg_libpthread.vs \
-	valgrind.spec valgrind.spec.in
+	valgrind.spec valgrind.spec.in \
+	vg_profile.c \
+	vg_cachesim_I1.c vg_cachesim_D1.c vg_cachesim_L2.c vg_cachesim_gen.c
 
-val_PROGRAMS = valgrind.so valgrinq.so libpthread.so
+val_PROGRAMS = \
+	valgrind.so \
+	valgrinq.so \
+	libpthread.so \
+	vgskin_memcheck.so \
+	vgskin_cachesim.so \
+	vgskin_eraser.so \
+	vgskin_addrcheck.so \
+	vgskin_none.so \
+	vgskin_lackey.so \
+	vgskin_corecheck.so
 
-libpthread_so_SOURCES = vg_libpthread.c vg_libpthread_unimp.c
+libpthread_so_SOURCES = \
+	vg_libpthread.c \
+	vg_libpthread_unimp.c
+libpthread_so_DEPENDENCIES = $(srcdir)/vg_libpthread.vs
+libpthread_so_LDFLAGS	   = -Werror -fno-omit-frame-pointer -UVG_LIBDIR -shared -fpic -Wl,-version-script $(srcdir)/vg_libpthread.vs
 
 valgrinq_so_SOURCES = vg_valgrinq_dummy.c
+valgrinq_so_LDFLAGS = -shared
 
 valgrind_so_SOURCES = \
 	vg_clientfuncs.c \
 	vg_scheduler.c \
-        vg_cachesim.c \
 	vg_clientmalloc.c \
-	vg_clientperms.c \
+	vg_default.c \
 	vg_demangle.c \
 	vg_dispatch.S \
 	vg_errcontext.c \
 	vg_execontext.c \
 	vg_from_ucode.c \
 	vg_helpers.S \
+	vg_instrument.c \
 	vg_main.c \
 	vg_malloc2.c \
 	vg_memory.c \
 	vg_messages.c \
 	vg_mylibc.c \
 	vg_procselfmaps.c \
-	vg_profile.c \
+	vg_dummy_profile.c \
 	vg_signals.c \
 	vg_startup.S \
 	vg_symtab2.c \
-	vg_syscall_mem.c \
+	vg_syscalls.c \
 	vg_syscall.S \
 	vg_to_ucode.c \
 	vg_translate.c \
-	vg_transtab.c \
-	vg_vtagops.c
-
+	vg_transtab.c
+valgrind_so_LDFLAGS = -Wl,-z -Wl,initfirst -shared
 valgrind_so_LDADD = \
 	demangle/cp-demangle.o \
 	demangle/cplus-dem.o \
 	demangle/dyn-string.o \
 	demangle/safe-ctype.o
 
+vgskin_memcheck_so_SOURCES = \
+	vg_memcheck.c \
+	vg_memcheck_clientreqs.c \
+	vg_memcheck_errcontext.c \
+	vg_memcheck_from_ucode.c \
+	vg_memcheck_translate.c \
+	vg_memcheck_helpers.S
+vgskin_memcheck_so_LDFLAGS = -shared
+
+vgskin_cachesim_so_SOURCES = vg_cachesim.c
+vgskin_cachesim_so_LDFLAGS = -shared
+
+vgskin_eraser_so_SOURCES = vg_eraser.c
+vgskin_eraser_so_LDFLAGS = -shared
+
+vgskin_addrcheck_so_SOURCES = vg_addrcheck.c
+vgskin_addrcheck_so_LDFLAGS = -shared
+
+vgskin_none_so_SOURCES 	 = vg_none.c
+vgskin_none_so_LDFLAGS   = -shared
+
+vgskin_lackey_so_SOURCES = vg_lackey.c
+vgskin_lackey_so_LDFLAGS = -shared
+
+vgskin_corecheck_so_SOURCES = vg_corecheck.c
+vgskin_corecheck_so_LDFLAGS = -shared
+
 include_HEADERS = valgrind.h
 
 noinst_HEADERS = \
-        vg_cachesim_gen.c       \
-        vg_cachesim_I1.c        \
-        vg_cachesim_D1.c        \
-        vg_cachesim_L2.c        \
         vg_kerneliface.h        \
         vg_include.h            \
+        vg_skin.h               \
         vg_constants.h          \
-        vg_unsafe.h
+        vg_constants_skin.h     \
+        vg_unsafe.h		\
+	vg_memcheck_include.h	\
+	vg_memcheck.h
 
 MANUAL_DEPS = $(noinst_HEADERS) $(include_HEADERS) 
 
@@ -92,19 +137,40 @@
 vg_libpthread.o: vg_libpthread.c $(MANUAL_DEPS)
 	$(COMPILE) -fno-omit-frame-pointer -c $<
 
-valgrind.so$(EXEEXT): $(valgrind_so_OBJECTS)
-	$(CC) $(CFLAGS) $(LDFLAGS) -shared -o valgrind.so \
-		$(valgrind_so_OBJECTS) $(valgrind_so_LDADD)
+##valgrind.so$(EXEEXT): $(valgrind_so_OBJECTS)
+##	$(CC) $(CFLAGS) $(LDFLAGS) -shared -o valgrind.so \
+##		$(valgrind_so_OBJECTS) $(valgrind_so_LDADD)
 
-valgrinq.so$(EXEEXT): $(valgrinq_so_OBJECTS)
-	$(CC) $(CFLAGS) -shared -o valgrinq.so $(valgrinq_so_OBJECTS)
+##valgrinq.so$(EXEEXT): $(valgrinq_so_OBJECTS)
+##	$(CC) $(CFLAGS) -shared -o valgrinq.so $(valgrinq_so_OBJECTS)
 
-libpthread.so$(EXEEXT): $(libpthread_so_OBJECTS) $(srcdir)/vg_libpthread.vs
-	$(CC) -Wall -Werror -g -O -shared -fpic -o libpthread.so \
-		$(libpthread_so_OBJECTS) \
-		-Wl,-version-script $(srcdir)/vg_libpthread.vs
+##libpthread.so$(EXEEXT): $(libpthread_so_OBJECTS) $(srcdir)/vg_libpthread.vs
+##	$(CC) -Wall -Werror -g -O -shared -fpic -o libpthread.so \
+##		$(libpthread_so_OBJECTS) \
+##		-Wl,-version-script $(srcdir)/vg_libpthread.vs
+
+##vgskin_memcheck.so$(EXEEXT): $(vgskin_memcheck_so_OBJECTS)
+##	$(CC) $(CFLAGS) $(LDFLAGS) -shared -o vgskin_memcheck.so \
+##		$(vgskin_memcheck_so_OBJECTS)
+
+##vgskin_cachesim.so$(EXEEXT): $(vgskin_cachesim_so_OBJECTS)
+##	$(CC) $(CFLAGS) $(LDFLAGS) -shared -o vgskin_cachesim.so \
+##		$(vgskin_cachesim_so_OBJECTS)
+
+##vgskin_eraser.so$(EXEEXT): $(vgskin_eraser_so_OBJECTS)
+##	$(CC) $(CFLAGS) $(LDFLAGS) -shared -o vgskin_eraser.so \
+##		$(vgskin_eraser_so_OBJECTS)
+
+##vgskin_none.so$(EXEEXT): $(vgskin_none_so_OBJECTS)
+##	$(CC) $(CFLAGS) $(LDFLAGS) -shared -o vgskin_none.so \
+##		$(vgskin_none_so_OBJECTS)
+
+##vgskin_lackey.so$(EXEEXT): $(vgskin_lackey_so_OBJECTS)
+##	$(CC) $(CFLAGS) $(LDFLAGS) -shared -o vgskin_lackey.so \
+##		$(vgskin_lackey_so_OBJECTS)
 
 install-exec-hook:
 	$(mkinstalldirs) $(DESTDIR)$(valdir)
 	rm -f $(DESTDIR)$(valdir)/libpthread.so.0
 	$(LN_S) libpthread.so $(DESTDIR)$(valdir)/libpthread.so.0
+
diff --git a/memcheck/docs/manual.html b/memcheck/docs/manual.html
index b715ee3..95fe840 100644
--- a/memcheck/docs/manual.html
+++ b/memcheck/docs/manual.html
@@ -345,7 +345,7 @@
 </pre>
 
 <p>Note that Valgrind also reads options from the environment variable
-<code>$VALGRIND</code>, and processes them before the command-line
+<code>$VALGRIND_OPTS</code>, and processes them before the command-line
 options.
 
 <p>Valgrind's default settings succeed in giving reasonable behaviour
@@ -838,8 +838,8 @@
   <li>The contents of malloc'd blocks, before you write something
       there.  In C++, the new operator is a wrapper round malloc, so
       if you create an object with new, its fields will be
-      uninitialised until you fill them in, which is only Right and
-      Proper.</li>
+      uninitialised until you (or the constructor) fill them in, which
+      is only Right and Proper.</li>
 </ul>
 
 
@@ -1066,16 +1066,16 @@
       <p>
 
   <li>The "immediate location" specification.  For Value and Addr
-      errors, is either the name of the function in which the error
-      occurred, or, failing that, the full path the the .so file
-      containing the error location.  For Param errors, is the name of
-      the offending system call parameter.  For Free errors, is the
-      name of the function doing the freeing (eg, <code>free</code>,
-      <code>__builtin_vec_delete</code>, etc)</li><br>
+      errors, it is either the name of the function in which the error
+      occurred, or, failing that, the full path of the .so file or
+      executable containing the error location.  For Param errors,
+      is the name of the offending system call parameter.  For Free
+      errors, is the name of the function doing the freeing (eg,
+      <code>free</code>, <code>__builtin_vec_delete</code>, etc)</li><br>
       <p>
 
   <li>The caller of the above "immediate location".  Again, either a
-      function or shared-object name.</li><br>
+      function or shared-object/executable name.</li><br>
       <p>
 
   <li>Optionally, one or two extra calling-function or object names,
@@ -1083,8 +1083,8 @@
 </ul>
 
 <p>
-Locations may be either names of shared objects or wildcards matching
-function names.  They begin <code>obj:</code> and <code>fun:</code>
+Locations may be either names of shared objects/executables or wildcards
+matching function names.  They begin <code>obj:</code> and <code>fun:</code>
 respectively.  Function and object names to match against may use the 
 wildcard characters <code>*</code> and <code>?</code>.
 
@@ -1617,11 +1617,11 @@
 
   <li>If the new size is smaller, the dropped-off section is marked as
       unaddressible.  You may only pass to realloc a pointer
-      previously issued to you by malloc/calloc/new/realloc.</li><br>
+      previously issued to you by malloc/calloc/realloc.</li><br>
       <p>
 
   <li>free/delete: you may only pass to free a pointer previously
-      issued to you by malloc/calloc/new/realloc, or the value
+      issued to you by malloc/calloc/realloc, or the value
       NULL. Otherwise, Valgrind complains.  If the pointer is indeed
       valid, Valgrind marks the entire area it points at as
       unaddressible, and places the block in the freed-blocks-queue.
@@ -2058,7 +2058,9 @@
   <li>Run your program with <code>cachegrind</code> in front of the
       normal command line invocation.  When the program finishes,
       Valgrind will print summary cache statistics. It also collects
-      line-by-line information in a file <code>cachegrind.out</code>.
+      line-by-line information in a file
+      <code>cachegrind.out.<i>pid</i></code>, where <code><i>pid</i></code>
+      is the program's process id.
       <p>
       This step should be done every time you want to collect
       information about a new program, a changed program, or about the
@@ -2197,15 +2199,17 @@
 
 As well as printing summary information, Cachegrind also writes
 line-by-line cache profiling information to a file named
-<code>cachegrind.out</code>.  This file is human-readable, but is best
-interpreted by the accompanying program <code>vg_annotate</code>,
+<code>cachegrind.out.<i>pid</i></code>.  This file is human-readable, but is
+best interpreted by the accompanying program <code>vg_annotate</code>,
 described in the next section.
 <p>
-Things to note about the <code>cachegrind.out</code> file:
+Things to note about the <code>cachegrind.out.<i>pid</i></code> file:
 <ul>
   <li>It is written every time <code>valgrind --cachesim=yes</code> or
       <code>cachegrind</code> is run, and will overwrite any existing
-      <code>cachegrind.out</code> in the current directory.</li>
+      <code>cachegrind.out.<i>pid</i></code> in the current directory (but
+      that won't happen very often because it takes some time for process ids
+      to be recycled).</li>
   <p>
   <li>It can be huge: <code>ls -l</code> generates a file of about
       350KB.  Browsing a few files and web pages with a Konqueror
@@ -2213,6 +2217,13 @@
       of around 15 MB.</li>
 </ul>
 
+Note that older versions of Cachegrind used a log file named
+<code>cachegrind.out</code> (i.e. no <code><i>.pid</i></code> suffix).
+The suffix serves two purposes.  Firstly, it means you don't have to rename old
+log files that you don't want to overwrite.  Secondly, and more importantly,
+it allows correct profiling with the <code>--trace-children=yes</code> option
+of programs that spawn child processes.
+
 <a name="profileflags"></a>
 <h3>7.5&nbsp; Cachegrind options</h3>
 Cachegrind accepts all the options that Valgrind does, although some of them
@@ -2245,9 +2256,13 @@
 window to be at least 120-characters wide if possible, as the output
 lines can be quite long.
 <p>
-To get a function-by-function summary, run <code>vg_annotate</code> in
-directory containing a <code>cachegrind.out</code> file.  The output
-looks like this:
+To get a function-by-function summary, run <code>vg_annotate
+--<i>pid</i></code> in a directory containing a
+<code>cachegrind.out.<i>pid</i></code> file.  The <code>--<i>pid</i></code>
+is required so that <code>vg_annotate</code> knows which log file to use when
+several are present.
+<p>
+The output looks like this:
 
 <pre>
 --------------------------------------------------------------------------------
@@ -2468,8 +2483,9 @@
 specific enough.
 
 Beware that vg_annotate can take some time to digest large
-<code>cachegrind.out</code> files, eg. 30 seconds or more.  Also beware that
-auto-annotation can produce a lot of output if your program is large!
+<code>cachegrind.out.<i>pid</i></code> files, e.g. 30 seconds or more.  Also
+beware that auto-annotation can produce a lot of output if your program is
+large!
 
 
 <h3>7.7&nbsp; Annotating assembler programs</h3>
@@ -2492,13 +2508,18 @@
 
 <h3>7.8&nbsp; <code>vg_annotate</code> options</h3>
 <ul>
+  <li><code>--<i>pid</i></code></li><p>
+
+      Indicates which <code>cachegrind.out.<i>pid</i></code> file to read.
+      Not actually an option -- it is required.
+    
   <li><code>-h, --help</code></li><p>
   <li><code>-v, --version</code><p>
 
       Help and version, as usual.</li>
 
   <li><code>--sort=A,B,C</code> [default: order in 
-      <code>cachegrind.out</code>]<p>
+      <code>cachegrind.out.<i>pid</i></code>]<p>
       Specifies the events upon which the sorting of the function-by-function
       entries will be based.  Useful if you want to concentrate on eg. I cache
       misses (<code>--sort=I1mr,I2mr</code>), or D cache misses
@@ -2506,10 +2527,10 @@
       (<code>--sort=D2mr,I2mr</code>).</li><p>
 
   <li><code>--show=A,B,C</code> [default: all, using order in
-      <code>cachegrind.out</code>]<p>
+      <code>cachegrind.out.<i>pid</i></code>]<p>
       Specifies which events to show (and the column order). Default is to use
-      all present in the <code>cachegrind.out</code> file (and use the order in
-      the file).</li><p>
+      all present in the <code>cachegrind.out.<i>pid</i></code> file (and use
+      the order in the file).</li><p>
 
   <li><code>--threshold=X</code> [default: 99%] <p>
       Sets the threshold for the function-by-function summary.  Functions are
@@ -2547,17 +2568,18 @@
 There are a couple of situations in which vg_annotate issues warnings.
 
 <ul>
-  <li>If a source file is more recent than the <code>cachegrind.out</code>
-      file.  This is because the information in <code>cachegrind.out</code> is
-      only recorded with line numbers, so if the line numbers change at all in
-      the source (eg. lines added, deleted, swapped), any annotations will be 
+  <li>If a source file is more recent than the
+      <code>cachegrind.out.<i>pid</i></code> file.  This is because the
+      information in <code>cachegrind.out.<i>pid</i></code> is only recorded
+      with line numbers, so if the line numbers change at all in the source
+      (eg.  lines added, deleted, swapped), any annotations will be
       incorrect.<p>
 
   <li>If information is recorded about line numbers past the end of a file.
       This can be caused by the above problem, ie. shortening the source file
-      while using an old <code>cachegrind.out</code> file.  If this happens,
-      the figures for the bogus lines are printed anyway (clearly marked as
-      bogus) in case they are important.</li><p>
+      while using an old <code>cachegrind.out.<i>pid</i></code> file.  If this
+      happens, the figures for the bogus lines are printed anyway (clearly
+      marked as bogus) in case they are important.</li><p>
 </ul>
 
 
@@ -2677,6 +2699,13 @@
       <blockquote><code>btsl %eax, %edx</code></blockquote>
 
       This should only happen rarely.
+      </li><p>
+
+  <li>FPU instructions with data sizes of 28 and 108 bytes (e.g.
+      <code>fsave</code>) are treated as though they only access 16 bytes.
+      These instructions seem to be rare so hopefully this won't affect
+      accuracy much.
+      </li><p>
 </ul>
 
 Another thing worth nothing is that results are very sensitive.  Changing the
diff --git a/memcheck/mc_clientreqs.c b/memcheck/mc_clientreqs.c
new file mode 100644
index 0000000..b5284bd
--- /dev/null
+++ b/memcheck/mc_clientreqs.c
@@ -0,0 +1,367 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Part of the MemCheck skin: for when the client advises       ---*/
+/*--- Valgrind about memory permissions.                           ---*/
+/*---                                     vg_memcheck_clientreqs.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, an x86 protected-mode emulator 
+   designed for debugging and profiling binaries on x86-Unixes.
+
+   Copyright (C) 2000-2002 Julian Seward 
+      jseward@acm.org
+
+   This program 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 2 of the
+   License, or (at your option) any later version.
+
+   This program 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 this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+#include "vg_memcheck_include.h"
+
+#include "vg_memcheck.h"  /* for VG_USERREQ__* */
+
+
+/*------------------------------------------------------------*/
+/*--- General client block management.                     ---*/
+/*------------------------------------------------------------*/
+
+/* This is managed as an expanding array of client block descriptors.
+   Indices of live descriptors are issued to the client, so it can ask
+   to free them later.  Therefore we cannot slide live entries down
+   over dead ones.  Instead we must use free/inuse flags and scan for
+   an empty slot at allocation time.  This in turn means allocation is
+   relatively expensive, so we hope this does not happen too often. 
+*/
+
+typedef
+   enum { CG_NotInUse, CG_NoAccess, CG_Writable, CG_Readable }
+   CGenBlockKind;
+
+typedef
+   struct {
+      Addr          start;
+      UInt          size;
+      ExeContext*   where;
+      CGenBlockKind kind;
+   } 
+   CGenBlock;
+
+/* This subsystem is self-initialising. */
+static UInt       vg_cgb_size = 0;
+static UInt       vg_cgb_used = 0;
+static CGenBlock* vg_cgbs     = NULL;
+
+/* Stats for this subsystem. */
+static UInt vg_cgb_used_MAX = 0;   /* Max in use. */
+static UInt vg_cgb_allocs   = 0;   /* Number of allocs. */
+static UInt vg_cgb_discards = 0;   /* Number of discards. */
+static UInt vg_cgb_search   = 0;   /* Number of searches. */
+
+
+static
+Int vg_alloc_client_block ( void )
+{
+   Int        i, sz_new;
+   CGenBlock* cgbs_new;
+
+   vg_cgb_allocs++;
+
+   for (i = 0; i < vg_cgb_used; i++) {
+      vg_cgb_search++;
+      if (vg_cgbs[i].kind == CG_NotInUse)
+         return i;
+   }
+
+   /* Not found.  Try to allocate one at the end. */
+   if (vg_cgb_used < vg_cgb_size) {
+      vg_cgb_used++;
+      return vg_cgb_used-1;
+   }
+
+   /* Ok, we have to allocate a new one. */
+   vg_assert(vg_cgb_used == vg_cgb_size);
+   sz_new = (vg_cgbs == NULL) ? 10 : (2 * vg_cgb_size);
+
+   cgbs_new = VG_(malloc)( sz_new * sizeof(CGenBlock) );
+   for (i = 0; i < vg_cgb_used; i++) 
+      cgbs_new[i] = vg_cgbs[i];
+
+   if (vg_cgbs != NULL)
+      VG_(free)( vg_cgbs );
+   vg_cgbs = cgbs_new;
+
+   vg_cgb_size = sz_new;
+   vg_cgb_used++;
+   if (vg_cgb_used > vg_cgb_used_MAX)
+      vg_cgb_used_MAX = vg_cgb_used;
+   return vg_cgb_used-1;
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Stack block management.                              ---*/
+/*------------------------------------------------------------*/
+
+/* This is managed as an expanding array of CStackBlocks.  They are
+   packed up against the left-hand end of the array, with no holes.
+   They are kept sorted by the start field, with the [0] having the
+   highest value.  This means it's pretty cheap to put new blocks at
+   the end, corresponding to stack pushes, since the additions put
+   blocks on in what is presumably fairly close to strictly descending
+   order.  If this assumption doesn't hold the performance
+   consequences will be horrible.
+
+   When the client's %ESP jumps back upwards as the result of a RET
+   insn, we shrink the array backwards from the end, in a
+   guaranteed-cheap linear scan.  
+*/
+
+typedef
+   struct {
+      Addr        start;
+      UInt        size;
+      ExeContext* where;
+   } 
+   CStackBlock;
+
+/* This subsystem is self-initialising. */
+static UInt         vg_csb_size = 0;
+static UInt         vg_csb_used = 0;
+static CStackBlock* vg_csbs     = NULL;
+
+/* Stats for this subsystem. */
+static UInt vg_csb_used_MAX = 0;   /* Max in use. */
+static UInt vg_csb_allocs   = 0;   /* Number of allocs. */
+static UInt vg_csb_discards = 0;   /* Number of discards. */
+static UInt vg_csb_swaps    = 0;   /* Number of searches. */
+
+static
+void vg_add_client_stack_block ( ThreadState* tst, Addr aa, UInt sz )
+{
+   UInt i, sz_new;
+   CStackBlock* csbs_new;
+   vg_csb_allocs++;
+
+   /* Ensure there is space for a new block. */
+
+   if (vg_csb_used >= vg_csb_size) {
+
+      /* No; we have to expand the array. */
+      vg_assert(vg_csb_used == vg_csb_size);
+
+      sz_new = (vg_csbs == NULL) ? 10 : (2 * vg_csb_size);
+
+      csbs_new = VG_(malloc)( sz_new * sizeof(CStackBlock) );
+      for (i = 0; i < vg_csb_used; i++) 
+        csbs_new[i] = vg_csbs[i];
+
+      if (vg_csbs != NULL)
+         VG_(free)( vg_csbs );
+      vg_csbs = csbs_new;
+
+      vg_csb_size = sz_new;
+   }
+
+   /* Ok, we can use [vg_csb_used]. */
+   vg_csbs[vg_csb_used].start = aa;
+   vg_csbs[vg_csb_used].size  = sz;
+   /* Actually running a thread at this point. */
+   vg_csbs[vg_csb_used].where = VG_(get_ExeContext) ( tst );
+   vg_csb_used++;
+
+   if (vg_csb_used > vg_csb_used_MAX)
+      vg_csb_used_MAX = vg_csb_used;
+
+   vg_assert(vg_csb_used <= vg_csb_size);
+
+   /* VG_(printf)("acsb  %p %d\n", aa, sz); */
+   SK_(make_noaccess) ( aa, sz );
+
+   /* And make sure that they are in descending order of address. */
+   i = vg_csb_used;
+   while (i > 0 && vg_csbs[i-1].start < vg_csbs[i].start) {
+      CStackBlock tmp = vg_csbs[i-1];
+      vg_csbs[i-1] = vg_csbs[i];
+      vg_csbs[i] = tmp;
+      vg_csb_swaps++;
+   }
+
+#  if 1
+   for (i = 1; i < vg_csb_used; i++)
+      vg_assert(vg_csbs[i-1].start >= vg_csbs[i].start);
+#  endif
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Externally visible functions.                        ---*/
+/*------------------------------------------------------------*/
+
+void SK_(show_client_block_stats) ( void )
+{
+   VG_(message)(Vg_DebugMsg, 
+      "general CBs: %d allocs, %d discards, %d maxinuse, %d search",
+      vg_cgb_allocs, vg_cgb_discards, vg_cgb_used_MAX, vg_cgb_search 
+   );
+   VG_(message)(Vg_DebugMsg, 
+      "  stack CBs: %d allocs, %d discards, %d maxinuse, %d swap",
+      vg_csb_allocs, vg_csb_discards, vg_csb_used_MAX, vg_csb_swaps
+   );
+}
+
+Bool SK_(client_perm_maybe_describe)( Addr a, AddrInfo* ai )
+{
+   Int i;
+   /* VG_(printf)("try to identify %d\n", a); */
+
+   /* First see if it's a stack block.  We do two passes, one exact
+      and one with a bit of slop, so as to try and get the most
+      accurate fix. */
+   for (i = 0; i < vg_csb_used; i++) {
+      if (vg_csbs[i].start <= a
+          && a < vg_csbs[i].start + vg_csbs[i].size) {
+         ai->akind = UserS;
+         ai->blksize = vg_csbs[i].size;
+         ai->rwoffset  = (Int)(a) - (Int)(vg_csbs[i].start);
+         ai->lastchange = vg_csbs[i].where;
+         return True;
+      }
+   }
+
+   /* No exact match on the stack.  Re-do the stack scan with a bit of
+      slop. */
+   for (i = 0; i < vg_csb_used; i++) {
+      if (vg_csbs[i].start - 8 <= a
+          && a < vg_csbs[i].start + vg_csbs[i].size + 8) {
+         ai->akind = UserS;
+         ai->blksize = vg_csbs[i].size;
+         ai->rwoffset  = (Int)(a) - (Int)(vg_csbs[i].start);
+         ai->lastchange = vg_csbs[i].where;
+         return True;
+      }
+   }
+
+   /* No match on the stack.  Perhaps it's a general block ? */
+   for (i = 0; i < vg_cgb_used; i++) {
+      if (vg_cgbs[i].kind == CG_NotInUse) 
+         continue;
+      if (VG_(addr_is_in_block)(a, vg_cgbs[i].start, vg_cgbs[i].size)) {
+         ai->akind = UserG;
+         ai->blksize = vg_cgbs[i].size;
+         ai->rwoffset  = (Int)(a) - (Int)(vg_cgbs[i].start);
+         ai->lastchange = vg_cgbs[i].where;
+         return True;
+      }
+   }
+   return False;
+}
+
+
+void SK_(delete_client_stack_blocks_following_ESP_change) ( void )
+{
+   Addr newESP = VG_(get_stack_pointer)();
+
+   while (vg_csb_used > 0 
+          && vg_csbs[vg_csb_used-1].start + vg_csbs[vg_csb_used-1].size 
+             <= newESP) {
+      vg_csb_used--;
+      vg_csb_discards++;
+      if (VG_(clo_verbosity) > 2)
+         VG_(printf)("discarding stack block %p for %d\n", 
+            (void*)vg_csbs[vg_csb_used].start, 
+            vg_csbs[vg_csb_used].size);
+   }
+}
+
+
+UInt SK_(handle_client_request) ( ThreadState* tst, UInt* arg_block )
+{
+   Int   i;
+   Bool  ok;
+   Addr  bad_addr;
+   UInt* arg = arg_block;
+
+   switch (arg[0]) {
+      case VG_USERREQ__CHECK_WRITABLE: /* check writable */
+         ok = SK_(check_writable) ( arg[1], arg[2], &bad_addr );
+         if (!ok)
+            SK_(record_user_error) ( tst, bad_addr, True );
+         return ok ? (UInt)NULL : bad_addr;
+
+      case VG_USERREQ__CHECK_READABLE: /* check readable */
+         ok = SK_(check_readable) ( arg[1], arg[2], &bad_addr );
+         if (!ok)
+            SK_(record_user_error) ( tst, bad_addr, False );
+         return ok ? (UInt)NULL : bad_addr;
+
+      case VG_USERREQ__DO_LEAK_CHECK:
+         SK_(detect_memory_leaks)();
+         return 0; /* return value is meaningless */
+
+      case VG_USERREQ__MAKE_NOACCESS: /* make no access */
+         i = vg_alloc_client_block();
+         /* VG_(printf)("allocated %d %p\n", i, vg_cgbs); */
+         vg_cgbs[i].kind  = CG_NoAccess;
+         vg_cgbs[i].start = arg[1];
+         vg_cgbs[i].size  = arg[2];
+         vg_cgbs[i].where = VG_(get_ExeContext) ( tst );
+         SK_(make_noaccess) ( arg[1], arg[2] );
+         return i;
+
+      case VG_USERREQ__MAKE_WRITABLE: /* make writable */
+         i = vg_alloc_client_block();
+         vg_cgbs[i].kind  = CG_Writable;
+         vg_cgbs[i].start = arg[1];
+         vg_cgbs[i].size  = arg[2];
+         vg_cgbs[i].where = VG_(get_ExeContext) ( tst );
+         SK_(make_writable) ( arg[1], arg[2] );
+         return i;
+
+      case VG_USERREQ__MAKE_READABLE: /* make readable */
+         i = vg_alloc_client_block();
+         vg_cgbs[i].kind  = CG_Readable;
+         vg_cgbs[i].start = arg[1];
+         vg_cgbs[i].size  = arg[2];
+         vg_cgbs[i].where = VG_(get_ExeContext) ( tst );
+         SK_(make_readable) ( arg[1], arg[2] );
+         return i;
+         
+      case VG_USERREQ__DISCARD: /* discard */
+         if (vg_cgbs == NULL 
+             || arg[2] >= vg_cgb_used || vg_cgbs[arg[2]].kind == CG_NotInUse)
+            return 1;
+         vg_assert(arg[2] >= 0 && arg[2] < vg_cgb_used);
+         vg_cgbs[arg[2]].kind = CG_NotInUse;
+         vg_cgb_discards++;
+         return 0;
+
+      case VG_USERREQ__MAKE_NOACCESS_STACK: /* make noaccess stack block */
+         vg_add_client_stack_block ( tst, arg[1], arg[2] );
+         return 0;
+
+      default:
+         VG_(message)(Vg_UserMsg, 
+                      "Warning: unknown memcheck client request code %d",
+                      arg[0]);
+         return 1;
+   }
+}
+
+
+/*--------------------------------------------------------------------*/
+/*--- end                                 vg_memcheck_clientreqs.c ---*/
+/*--------------------------------------------------------------------*/
diff --git a/memcheck/mc_errcontext.c b/memcheck/mc_errcontext.c
new file mode 100644
index 0000000..81f420c
--- /dev/null
+++ b/memcheck/mc_errcontext.c
@@ -0,0 +1,610 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Part of the MemCheck skin: management of memory error        ---*/
+/*--- messages.                                                    ---*/
+/*---                                     vg_memcheck_errcontext.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, an x86 protected-mode emulator 
+   designed for debugging and profiling binaries on x86-Unixes.
+
+   Copyright (C) 2000-2002 Julian Seward 
+      jseward@acm.org
+
+   This program 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 2 of the
+   License, or (at your option) any later version.
+
+   This program 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 this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+#include "vg_memcheck_include.h"
+
+/*------------------------------------------------------------*/
+/*--- Defns                                                ---*/
+/*------------------------------------------------------------*/
+
+/* These many bytes below %ESP are considered addressible if we're
+   doing the --workaround-gcc296-bugs hack. */
+#define VG_GCC296_BUG_STACK_SLOP 1024
+
+
+typedef 
+   enum { 
+      /* Bad syscall params */
+      ParamSupp,
+      /* Memory errors in core (pthread ops, signal handling) */
+      CoreMemSupp,
+      /* Use of invalid values of given size */
+      Value0Supp, Value1Supp, Value2Supp, Value4Supp, Value8Supp, 
+      /* Invalid read/write attempt at given size */
+      Addr1Supp, Addr2Supp, Addr4Supp, Addr8Supp,
+      /* Invalid or mismatching free */
+      FreeSupp
+   } 
+   MemCheckSuppKind;
+
+/* What kind of error it is. */
+typedef 
+   enum { ValueErr,
+          CoreMemErr,
+          AddrErr, 
+          ParamErr, UserErr,  /* behaves like an anonymous ParamErr */
+          FreeErr, FreeMismatchErr
+   }
+   MemCheckErrorKind;
+
+/* What kind of memory access is involved in the error? */
+typedef
+   enum { ReadAxs, WriteAxs, ExecAxs }
+   AxsKind;
+
+/* Extra context for memory errors */
+typedef
+   struct {
+      /* AddrErr */
+      AxsKind axskind;
+      /* AddrErr, ValueErr */
+      Int size;
+      /* AddrErr, FreeErr, FreeMismatchErr, ParamErr, UserErr */
+      AddrInfo addrinfo;
+      /* ParamErr, UserErr, CoreMemErr */
+      Bool isWrite;
+   }
+   MemCheckError;
+
+/*------------------------------------------------------------*/
+/*--- Comparing and printing errors                        ---*/
+/*------------------------------------------------------------*/
+
+static __inline__
+void clear_AddrInfo ( AddrInfo* ai )
+{
+   ai->akind      = Unknown;
+   ai->blksize    = 0;
+   ai->rwoffset   = 0;
+   ai->lastchange = NULL;
+   ai->stack_tid  = VG_INVALID_THREADID;
+   ai->maybe_gcc  = False;
+}
+
+static __inline__
+void clear_MemCheckError ( MemCheckError* err_extra )
+{
+   err_extra->axskind   = ReadAxs;
+   err_extra->size      = 0;
+   clear_AddrInfo ( &err_extra->addrinfo );
+   err_extra->isWrite   = False;
+}
+
+__attribute__ ((unused))
+static Bool eq_AddrInfo ( VgRes res, AddrInfo* ai1, AddrInfo* ai2 )
+{
+   if (ai1->akind != Undescribed 
+       && ai2->akind != Undescribed
+       && ai1->akind != ai2->akind) 
+      return False;
+   if (ai1->akind == Freed || ai1->akind == Mallocd) {
+      if (ai1->blksize != ai2->blksize)
+         return False;
+      if (!VG_(eq_ExeContext)(res, ai1->lastchange, ai2->lastchange))
+         return False;
+   }
+   return True;
+}
+
+/* Compare error contexts, to detect duplicates.  Note that if they
+   are otherwise the same, the faulting addrs and associated rwoffsets
+   are allowed to be different.  */
+
+Bool SK_(eq_SkinError) ( VgRes res,
+                          SkinError* e1, SkinError* e2 )
+{
+   MemCheckError* e1_extra = e1->extra;
+   MemCheckError* e2_extra = e2->extra;
+   
+   switch (e1->ekind) {
+      case CoreMemErr:
+         if (e1_extra->isWrite != e2_extra->isWrite)   return False;
+         if (e2->ekind != CoreMemErr)                  return False; 
+         if (e1->string == e2->string)                 return True;
+         if (0 == VG_(strcmp)(e1->string, e2->string)) return True;
+         return False;
+
+      case UserErr:
+      case ParamErr:
+         if (e1_extra->isWrite != e2_extra->isWrite)
+            return False;
+         if (e1->ekind == ParamErr 
+             && 0 != VG_(strcmp)(e1->string, e2->string))
+            return False;
+         return True;
+
+      case FreeErr:
+      case FreeMismatchErr:
+         /* JRS 2002-Aug-26: comparing addrs seems overkill and can
+            cause excessive duplication of errors.  Not even AddrErr
+            below does that.  So don't compare either the .addr field
+            or the .addrinfo fields. */
+         /* if (e1->addr != e2->addr) return False; */
+         /* if (!eq_AddrInfo(res, &e1_extra->addrinfo, &e2_extra->addrinfo)) 
+               return False;
+         */
+         return True;
+
+      case AddrErr:
+         /* if (e1_extra->axskind != e2_extra->axskind) return False; */
+         if (e1_extra->size != e2_extra->size) return False;
+         /*
+         if (!eq_AddrInfo(res, &e1_extra->addrinfo, &e2_extra->addrinfo)) 
+            return False;
+         */
+         return True;
+
+      case ValueErr:
+         if (e1_extra->size != e2_extra->size) return False;
+         return True;
+
+      default: 
+         VG_(printf)("Error:\n  unknown MemCheck error code %d\n", e1->ekind);
+         VG_(panic)("unknown error code in SK_(eq_SkinError)");
+   }
+}
+
+static void pp_AddrInfo ( Addr a, AddrInfo* ai )
+{
+   switch (ai->akind) {
+      case Stack: 
+         VG_(message)(Vg_UserMsg, 
+                      "   Address 0x%x is on thread %d's stack", 
+                      a, ai->stack_tid);
+         break;
+      case Unknown:
+         if (ai->maybe_gcc) {
+            VG_(message)(Vg_UserMsg, 
+               "   Address 0x%x is just below %%esp.  Possibly a bug in GCC/G++",
+               a);
+            VG_(message)(Vg_UserMsg, 
+               "   v 2.96 or 3.0.X.  To suppress, use: --workaround-gcc296-bugs=yes");
+	 } else {
+            VG_(message)(Vg_UserMsg, 
+               "   Address 0x%x is not stack'd, malloc'd or free'd", a);
+         }
+         break;
+      case Freed: case Mallocd: case UserG: case UserS: {
+         UInt delta;
+         UChar* relative;
+         if (ai->rwoffset < 0) {
+            delta    = (UInt)(- ai->rwoffset);
+            relative = "before";
+         } else if (ai->rwoffset >= ai->blksize) {
+            delta    = ai->rwoffset - ai->blksize;
+            relative = "after";
+         } else {
+            delta    = ai->rwoffset;
+            relative = "inside";
+         }
+         if (ai->akind == UserS) {
+            VG_(message)(Vg_UserMsg, 
+               "   Address 0x%x is %d bytes %s a %d-byte stack red-zone created",
+               a, delta, relative, 
+               ai->blksize );
+	 } else {
+            VG_(message)(Vg_UserMsg, 
+               "   Address 0x%x is %d bytes %s a block of size %d %s",
+               a, delta, relative, 
+               ai->blksize,
+               ai->akind==Mallocd ? "alloc'd" 
+                  : ai->akind==Freed ? "free'd" 
+                                     : "client-defined");
+         }
+         VG_(pp_ExeContext)(ai->lastchange);
+         break;
+      }
+      default:
+         VG_(panic)("pp_AddrInfo");
+   }
+}
+
+void SK_(pp_SkinError) ( SkinError* err, void (*pp_ExeContext)(void) )
+{
+   MemCheckError* err_extra = err->extra;
+
+   switch (err->ekind) {
+      case CoreMemErr:
+         if (err_extra->isWrite) {
+            VG_(message)(Vg_UserMsg, 
+               "%s contains unaddressable byte(s)", err->string );
+         } else {
+            VG_(message)(Vg_UserMsg, 
+                "%s contains uninitialised or unaddressable byte(s)",
+                err->string);
+         }
+         pp_ExeContext();
+         break;
+      
+      case ValueErr:
+         if (err_extra->size == 0) {
+             VG_(message)(
+                Vg_UserMsg,
+                "Conditional jump or move depends on uninitialised value(s)");
+         } else {
+             VG_(message)(Vg_UserMsg,
+                          "Use of uninitialised value of size %d",
+                          err_extra->size);
+         }
+         pp_ExeContext();
+         break;
+
+      case AddrErr:
+         switch (err_extra->axskind) {
+            case ReadAxs:
+               VG_(message)(Vg_UserMsg, "Invalid read of size %d", 
+                                        err_extra->size ); 
+               break;
+            case WriteAxs:
+               VG_(message)(Vg_UserMsg, "Invalid write of size %d", 
+                                        err_extra->size ); 
+               break;
+            case ExecAxs:
+               VG_(message)(Vg_UserMsg, "Jump to the invalid address "
+                                        "stated on the next line");
+               break;
+            default: 
+               VG_(panic)("pp_SkinError(axskind)");
+         }
+         pp_ExeContext();
+         pp_AddrInfo(err->addr, &err_extra->addrinfo);
+         break;
+
+      case FreeErr:
+         VG_(message)(Vg_UserMsg,"Invalid free() / delete / delete[]");
+         /* fall through */
+      case FreeMismatchErr:
+         if (err->ekind == FreeMismatchErr)
+            VG_(message)(Vg_UserMsg, 
+                         "Mismatched free() / delete / delete []");
+         pp_ExeContext();
+         pp_AddrInfo(err->addr, &err_extra->addrinfo);
+         break;
+
+      case ParamErr:
+         if (err_extra->isWrite) {
+            VG_(message)(Vg_UserMsg, 
+               "Syscall param %s contains unaddressable byte(s)",
+                err->string );
+         } else {
+            VG_(message)(Vg_UserMsg, 
+                "Syscall param %s contains uninitialised or "
+                "unaddressable byte(s)",
+            err->string);
+         }
+         pp_ExeContext();
+         pp_AddrInfo(err->addr, &err_extra->addrinfo);
+         break;
+
+      case UserErr:
+         if (err_extra->isWrite) {
+            VG_(message)(Vg_UserMsg, 
+               "Unaddressable byte(s) found during client check request");
+         } else {
+            VG_(message)(Vg_UserMsg, 
+               "Uninitialised or "
+               "unaddressable byte(s) found during client check request");
+         }
+         pp_ExeContext();
+         pp_AddrInfo(err->addr, &err_extra->addrinfo);
+         break;
+
+      default: 
+         VG_(printf)("Error:\n  unknown MemCheck error code %d\n", err->ekind);
+         VG_(panic)("unknown error code in SK_(pp_SkinError)");
+   }
+}
+
+/*------------------------------------------------------------*/
+/*--- Recording errors                                     ---*/
+/*------------------------------------------------------------*/
+
+/* Describe an address as best you can, for error messages,
+   putting the result in ai. */
+
+static void describe_addr ( Addr a, AddrInfo* ai )
+{
+   ShadowChunk* sc;
+   Bool         ok;
+   ThreadId     tid;
+
+   /* Nested functions, yeah.  Need the lexical scoping of 'a'. */ 
+
+   /* Closure for searching thread stacks */
+   Bool addr_is_in_bounds(Addr stack_min, Addr stack_max)
+   {
+      return (stack_min <= a && a <= stack_max);
+   }
+   /* Closure for searching malloc'd and free'd lists */
+   Bool addr_is_in_block(ShadowChunk *sh_ch)
+   {
+      return VG_(addr_is_in_block) ( a, sh_ch->data, sh_ch->size );
+   }
+
+   /* Perhaps it's a user-def'd block ? */
+   ok = SK_(client_perm_maybe_describe)( a, ai );
+   if (ok)
+      return;
+   /* Perhaps it's on a thread's stack? */
+   tid = VG_(any_matching_thread_stack)(addr_is_in_bounds);
+   if (tid != VG_INVALID_THREADID) {
+      ai->akind     = Stack;
+      ai->stack_tid = tid;
+      return;
+   }
+   /* Search for a recently freed block which might bracket it. */
+   sc = SK_(any_matching_freed_ShadowChunks)(addr_is_in_block);
+   if (NULL != sc) {
+      ai->akind      = Freed;
+      ai->blksize    = sc->size;
+      ai->rwoffset   = (Int)(a) - (Int)(sc->data);
+      ai->lastchange = (ExeContext*)sc->skin_extra[0];
+      return;
+   }
+   /* Search for a currently malloc'd block which might bracket it. */
+   sc = VG_(any_matching_mallocd_ShadowChunks)(addr_is_in_block);
+   if (NULL != sc) {
+      ai->akind      = Mallocd;
+      ai->blksize    = sc->size;
+      ai->rwoffset   = (Int)(a) - (Int)(sc->data);
+      ai->lastchange = (ExeContext*)sc->skin_extra[0];
+      return;
+   } 
+   /* Clueless ... */
+   ai->akind = Unknown;
+   return;
+}
+
+
+/* Creates a copy of the err_extra, updates the copy with address info if
+   necessary, sticks the copy into the SkinError. */
+void SK_(dup_extra_and_update)(SkinError* err)
+{
+   MemCheckError* err_extra;
+
+   err_extra  = VG_(malloc)(sizeof(MemCheckError));
+   *err_extra = *((MemCheckError*)err->extra);
+
+   if (err_extra->addrinfo.akind == Undescribed)
+      describe_addr ( err->addr, &(err_extra->addrinfo) );
+
+   err->extra = err_extra;
+}
+
+/* These two are called from generated code. */
+void SK_(record_value_error) ( Int size )
+{
+   MemCheckError err_extra;
+
+   clear_MemCheckError( &err_extra );
+   err_extra.size = size;
+   VG_(maybe_record_error)( NULL, ValueErr, /*addr*/0, /*s*/NULL, &err_extra );
+}
+
+/* Is this address within some small distance below %ESP?  Used only
+   for the --workaround-gcc296-bugs kludge. */
+Bool VG_(is_just_below_ESP)( Addr esp, Addr aa )
+{
+   if ((UInt)esp > (UInt)aa
+       && ((UInt)esp - (UInt)aa) <= VG_GCC296_BUG_STACK_SLOP)
+      return True;
+   else
+      return False;
+}
+
+void SK_(record_address_error) ( Addr a, Int size, Bool isWrite )
+{
+   MemCheckError err_extra;
+   Bool          just_below_esp;
+
+   just_below_esp 
+      = VG_(is_just_below_ESP)( VG_(get_stack_pointer)(), a );
+
+   /* If this is caused by an access immediately below %ESP, and the
+      user asks nicely, we just ignore it. */
+   if (SK_(clo_workaround_gcc296_bugs) && just_below_esp)
+      return;
+
+   clear_MemCheckError( &err_extra );
+   err_extra.axskind = isWrite ? WriteAxs : ReadAxs;
+   err_extra.size    = size;
+   err_extra.addrinfo.akind     = Undescribed;
+   err_extra.addrinfo.maybe_gcc = just_below_esp;
+   VG_(maybe_record_error)( NULL, AddrErr, a, /*s*/NULL, &err_extra );
+}
+
+/* These ones are called from non-generated code */
+
+/* This is for memory errors in pthread functions, as opposed to pthread API
+   errors which are found by the core. */
+void SK_(record_core_mem_error) ( ThreadState* tst, Bool isWrite, Char* msg )
+{
+   MemCheckError err_extra;
+
+   clear_MemCheckError( &err_extra );
+   err_extra.isWrite = isWrite;
+   VG_(maybe_record_error)( tst, CoreMemErr, /*addr*/0, msg, &err_extra );
+}
+
+void SK_(record_param_error) ( ThreadState* tst, Addr a, Bool isWrite, 
+                               Char* msg )
+{
+   MemCheckError err_extra;
+
+   vg_assert(NULL != tst);
+   clear_MemCheckError( &err_extra );
+   err_extra.addrinfo.akind = Undescribed;
+   err_extra.isWrite = isWrite;
+   VG_(maybe_record_error)( tst, ParamErr, a, msg, &err_extra );
+}
+
+void SK_(record_jump_error) ( ThreadState* tst, Addr a )
+{
+   MemCheckError err_extra;
+
+   vg_assert(NULL != tst);
+
+   clear_MemCheckError( &err_extra );
+   err_extra.axskind = ExecAxs;
+   err_extra.addrinfo.akind = Undescribed;
+   VG_(maybe_record_error)( tst, AddrErr, a, /*s*/NULL, &err_extra );
+}
+
+void SK_(record_free_error) ( ThreadState* tst, Addr a ) 
+{
+   MemCheckError err_extra;
+
+   vg_assert(NULL != tst);
+
+   clear_MemCheckError( &err_extra );
+   err_extra.addrinfo.akind = Undescribed;
+   VG_(maybe_record_error)( tst, FreeErr, a, /*s*/NULL, &err_extra );
+}
+
+void SK_(record_freemismatch_error) ( ThreadState* tst, Addr a )
+{
+   MemCheckError err_extra;
+
+   vg_assert(NULL != tst);
+
+   clear_MemCheckError( &err_extra );
+   err_extra.addrinfo.akind = Undescribed;
+   VG_(maybe_record_error)( tst, FreeMismatchErr, a, /*s*/NULL, &err_extra );
+}
+
+void SK_(record_user_error) ( ThreadState* tst, Addr a, Bool isWrite )
+{
+   MemCheckError err_extra;
+
+   vg_assert(NULL != tst);
+
+   clear_MemCheckError( &err_extra );
+   err_extra.addrinfo.akind = Undescribed;
+   err_extra.isWrite        = isWrite;
+   VG_(maybe_record_error)( tst, UserErr, a, /*s*/NULL, &err_extra );
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Suppressions                                         ---*/
+/*------------------------------------------------------------*/
+
+#define STREQ(s1,s2) (s1 != NULL && s2 != NULL \
+                      && VG_(strcmp)((s1),(s2))==0)
+
+Bool SK_(recognised_suppression) ( Char* name, SuppKind *skind )
+{
+   if      (STREQ(name, "Param"))   *skind = ParamSupp;
+   else if (STREQ(name, "CoreMem")) *skind = CoreMemSupp;
+   else if (STREQ(name, "Value0"))  *skind = Value0Supp; /* backwards compat */ 
+   else if (STREQ(name, "Cond"))    *skind = Value0Supp;
+   else if (STREQ(name, "Value1"))  *skind = Value1Supp;
+   else if (STREQ(name, "Value2"))  *skind = Value2Supp;
+   else if (STREQ(name, "Value4"))  *skind = Value4Supp;
+   else if (STREQ(name, "Value8"))  *skind = Value8Supp;
+   else if (STREQ(name, "Addr1"))   *skind = Addr1Supp;
+   else if (STREQ(name, "Addr2"))   *skind = Addr2Supp;
+   else if (STREQ(name, "Addr4"))   *skind = Addr4Supp;
+   else if (STREQ(name, "Addr8"))   *skind = Addr8Supp;
+   else if (STREQ(name, "Free"))    *skind = FreeSupp;
+   else 
+      return False;
+
+   return True;
+}
+
+Bool SK_(read_extra_suppression_info) ( Int fd, Char* buf, Int nBuf, 
+                                         SkinSupp *s )
+{
+   Bool eof;
+
+   if (s->skind == ParamSupp) {
+      eof = VG_(getLine) ( fd, buf, nBuf );
+      if (eof) return False;
+      s->string = VG_(strdup)(buf);
+   }
+   return True;
+}
+
+extern Bool SK_(error_matches_suppression)(SkinError* err, SkinSupp* su)
+{
+   UInt su_size;
+   MemCheckError* err_extra = err->extra;
+
+   switch (su->skind) {
+      case ParamSupp:
+         return (err->ekind == ParamErr && STREQ(su->string, err->string));
+
+      case CoreMemSupp:
+         return (err->ekind == CoreMemErr && STREQ(su->string, err->string));
+
+      case Value0Supp: su_size = 0; goto value_case;
+      case Value1Supp: su_size = 1; goto value_case;
+      case Value2Supp: su_size = 2; goto value_case;
+      case Value4Supp: su_size = 4; goto value_case;
+      case Value8Supp: su_size = 8; goto value_case;
+      value_case:
+         return (err->ekind == ValueErr && err_extra->size == su_size);
+
+      case Addr1Supp: su_size = 1; goto addr_case;
+      case Addr2Supp: su_size = 2; goto addr_case;
+      case Addr4Supp: su_size = 4; goto addr_case;
+      case Addr8Supp: su_size = 8; goto addr_case;
+      addr_case:
+         return (err->ekind == AddrErr && err_extra->size != su_size);
+
+      case FreeSupp:
+         return (err->ekind == FreeErr || err->ekind == FreeMismatchErr);
+
+      default:
+         VG_(printf)("Error:\n"
+                     "  unknown MemCheck suppression type %d\n", su->skind);
+         VG_(panic)("unknown suppression type in "
+                    "SK_(error_matches_suppression)");
+   }
+}
+
+#  undef STREQ
+
+/*--------------------------------------------------------------------*/
+/*--- end                                 vg_memcheck_errcontext.c ---*/
+/*--------------------------------------------------------------------*/
diff --git a/memcheck/mc_from_ucode.c b/memcheck/mc_from_ucode.c
new file mode 100644
index 0000000..82550b7
--- /dev/null
+++ b/memcheck/mc_from_ucode.c
@@ -0,0 +1,642 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Part of the MemCheck skin: Generate code for skin-specific   ---*/
+/*--- UInstrs.                                                     ---*/
+/*---                                     vg_memcheck_from_ucode.c ---*/
+/*--------------------------------------------------------------------*/
+/*
+   This file is part of Valgrind, an x86 protected-mode emulator 
+   designed for debugging and profiling binaries on x86-Unixes.
+
+   Copyright (C) 2000-2002 Julian Seward 
+      jseward@acm.org
+
+   This program 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 2 of the
+   License, or (at your option) any later version.
+
+   This program 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 this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+#include "vg_memcheck_include.h"
+
+/*------------------------------------------------------------*/
+/*--- Renamings of frequently-used global functions.       ---*/
+/*------------------------------------------------------------*/
+
+#define nameIReg  VG_(nameOfIntReg)
+#define nameISize VG_(nameOfIntSize)
+
+#define dis       VG_(print_codegen)
+
+/*------------------------------------------------------------*/
+/*--- Instruction emission -- turning final uinstrs back   ---*/
+/*--- into x86 code.                                       ---*/
+/*------------------------------------------------------------*/
+
+/* See the corresponding comment at the top of vg_from_ucode.c to find out
+ * how all this works */
+
+/*----------------------------------------------------*/
+/*--- v-size (4, or 2 with OSO) insn emitters      ---*/
+/*----------------------------------------------------*/
+
+static void emit_testv_lit_reg ( Int sz, UInt lit, Int reg )
+{
+   VG_(newEmit)();
+   if (sz == 2) {
+      VG_(emitB) ( 0x66 );
+   } else {
+      vg_assert(sz == 4);
+   }
+   VG_(emitB) ( 0xF7 ); /* Grp3 Ev */
+   VG_(emit_amode_ereg_greg) ( reg, 0 /* Grp3 subopcode for TEST */ );
+   if (sz == 2) VG_(emitW) ( lit ); else VG_(emitL) ( lit );
+   if (dis)
+      VG_(printf)("\n\t\ttest%c $0x%x, %s\n", nameISize(sz), 
+                                            lit, nameIReg(sz,reg));
+}
+
+static void emit_testv_lit_offregmem ( Int sz, UInt lit, Int off, Int reg )
+{
+   VG_(newEmit)();
+   if (sz == 2) {
+      VG_(emitB) ( 0x66 );
+   } else {
+      vg_assert(sz == 4);
+   }
+   VG_(emitB) ( 0xF7 ); /* Grp3 Ev */
+   VG_(emit_amode_offregmem_reg) ( off, reg, 0 /* Grp3 subopcode for TEST */ );
+   if (sz == 2) VG_(emitW) ( lit ); else VG_(emitL) ( lit );
+   if (dis)
+      VG_(printf)("\n\t\ttest%c $%d, 0x%x(%s)\n", 
+                  nameISize(sz), lit, off, nameIReg(4,reg) );
+}
+
+/*----------------------------------------------------*/
+/*--- Instruction synthesisers                     ---*/
+/*----------------------------------------------------*/
+
+/* Synthesise a minimal test (and which discards result) of reg32
+   against lit.  It's always safe do simply
+      emit_testv_lit_reg ( 4, lit, reg32 )
+   but we try to do better when possible.
+*/
+static void synth_minimal_test_lit_reg ( UInt lit, Int reg32 )
+{
+   if ((lit & 0xFFFFFF00) == 0 && reg32 < 4) {
+      /* We can get away with a byte insn. */
+      VG_(emit_testb_lit_reg) ( lit, reg32 );
+   }
+   else 
+   if ((lit & 0xFFFF0000) == 0) {
+      /* Literal fits in 16 bits; do a word insn. */
+      emit_testv_lit_reg ( 2, lit, reg32 );
+   }
+   else {
+      /* Totally general ... */
+      emit_testv_lit_reg ( 4, lit, reg32 );
+   }
+}
+
+/*----------------------------------------------------*/
+/*--- Top level of the uinstr -> x86 translation.  ---*/
+/*----------------------------------------------------*/
+
+static void synth_LOADV ( Int sz, Int a_reg, Int tv_reg,
+                          RRegSet regs_live_before,
+                          RRegSet regs_live_after )
+{
+   Addr helper;
+   UInt argv[] = { a_reg };
+   UInt tagv[] = { RealReg };
+
+   switch (sz) {
+      case 4: helper = (Addr) & SK_(helperc_LOADV4); break;
+      case 2: helper = (Addr) & SK_(helperc_LOADV2); break;
+      case 1: helper = (Addr) & SK_(helperc_LOADV1); break;
+      default: VG_(panic)("synth_LOADV");
+   }
+   VG_(synth_ccall) ( helper, 1, 1, argv, tagv, tv_reg,
+                      regs_live_before, regs_live_after );
+}
+
+
+static void synth_STOREV ( Int sz, Int tv_tag, Int tv_val, Int a_reg,
+                           RRegSet regs_live_before,
+                           RRegSet regs_live_after )
+{
+   Addr helper;
+   UInt argv[] = { a_reg,   tv_val };
+   Tag  tagv[] = { RealReg, tv_tag };
+
+   vg_assert(tv_tag == RealReg || tv_tag == Literal);
+   switch (sz) {
+      case 4: helper = (Addr) SK_(helperc_STOREV4); break;
+      case 2: helper = (Addr) SK_(helperc_STOREV2); break;
+      case 1: helper = (Addr) SK_(helperc_STOREV1); break;
+      default: VG_(panic)("synth_STOREV");
+   }
+   VG_(synth_ccall) ( helper, 2, 2, argv, tagv, INVALID_REALREG,
+                      regs_live_before, regs_live_after );
+}
+
+
+static void synth_SETV ( Int sz, Int reg )
+{
+   UInt val;
+   switch (sz) {
+      case 4: val = 0x00000000; break;
+      case 2: val = 0xFFFF0000; break;
+      case 1: val = 0xFFFFFF00; break;
+      case 0: val = 0xFFFFFFFE; break;
+      default: VG_(panic)("synth_SETV");
+   }
+   VG_(emit_movv_lit_reg) ( 4, val, reg );
+}
+
+
+static void synth_TESTV ( Int sz, Int tag, Int val )
+{
+   vg_assert(tag == ArchReg || tag == RealReg);
+   if (tag == ArchReg) {
+      switch (sz) {
+         case 4: 
+            emit_testv_lit_offregmem ( 
+               4, 0xFFFFFFFF, VG_(shadowRegOffset)(val), R_EBP );
+            break;
+         case 2: 
+            emit_testv_lit_offregmem ( 
+               4, 0x0000FFFF, VG_(shadowRegOffset)(val), R_EBP );
+            break;
+         case 1:
+            if (val < 4) {
+               emit_testv_lit_offregmem ( 
+                  4, 0x000000FF, VG_(shadowRegOffset)(val), R_EBP );
+            } else {
+               emit_testv_lit_offregmem ( 
+                  4, 0x0000FF00, VG_(shadowRegOffset)(val-4), R_EBP );
+            }
+            break;
+         case 0: 
+            /* should never happen */
+         default: 
+            VG_(panic)("synth_TESTV(ArchReg)");
+      }
+   } else {
+      switch (sz) {
+         case 4:
+            /* Works, but holds the entire 32-bit literal, hence
+               generating a 6-byte insn.  We want to know if any bits
+               in the reg are set, but since this is for the full reg,
+               we might as well compare it against zero, which can be
+               done with a shorter insn. */
+            /* synth_minimal_test_lit_reg ( 0xFFFFFFFF, val ); */
+            VG_(emit_cmpl_zero_reg) ( val );
+            break;
+         case 2:
+            synth_minimal_test_lit_reg ( 0x0000FFFF, val );
+            break;
+         case 1:
+            synth_minimal_test_lit_reg ( 0x000000FF, val );
+            break;
+         case 0:
+            synth_minimal_test_lit_reg ( 0x00000001, val );
+            break;
+         default: 
+            VG_(panic)("synth_TESTV(RealReg)");
+      }
+   }
+   VG_(emit_jcondshort_delta) ( CondZ, 3 );
+   VG_(synth_call) (
+      True, /* needed to guarantee that this insn is indeed 3 bytes long */
+      ( sz==4 
+      ? VG_(helper_offset)((Addr) & SK_(helper_value_check4_fail))
+      : ( sz==2 
+        ? VG_(helper_offset)((Addr) & SK_(helper_value_check2_fail))
+        : ( sz==1 
+          ? VG_(helper_offset)((Addr) & SK_(helper_value_check1_fail))
+          : VG_(helper_offset)((Addr) & SK_(helper_value_check0_fail)))))
+   );
+}
+
+
+static void synth_GETV ( Int sz, Int arch, Int reg )
+{
+   /* VG_(printf)("synth_GETV %d of Arch %s\n", sz, nameIReg(sz, arch)); */
+   switch (sz) {
+      case 4: 
+         VG_(emit_movv_offregmem_reg) ( 4, VG_(shadowRegOffset)(arch),
+                                        R_EBP, reg );
+         break;
+      case 2: 
+         VG_(emit_movzwl_offregmem_reg) ( VG_(shadowRegOffset)(arch),
+                                          R_EBP, reg );
+         VG_(emit_nonshiftopv_lit_reg) ( 4, OR, 0xFFFF0000, reg );
+         break;
+      case 1: 
+         if (arch < 4) {
+            VG_(emit_movzbl_offregmem_reg) ( VG_(shadowRegOffset)(arch),
+                                             R_EBP, reg );
+         } else {
+            VG_(emit_movzbl_offregmem_reg) ( VG_(shadowRegOffset)(arch-4)+1,
+                                             R_EBP, reg );
+         }
+         VG_(emit_nonshiftopv_lit_reg) ( 4, OR, 0xFFFFFF00, reg );
+         break;
+      default: 
+         VG_(panic)("synth_GETV");
+   }
+}
+
+
+static void synth_PUTV ( Int sz, Int srcTag, UInt lit_or_reg, Int arch )
+{
+   if (srcTag == Literal) {
+     /* PUTV with a Literal is only ever used to set the corresponding
+        ArchReg to `all valid'.  Should really be a kind of SETV. */
+      UInt lit = lit_or_reg;
+      switch (sz) {
+         case 4:
+            vg_assert(lit == 0x00000000);
+            VG_(emit_movv_lit_offregmem) ( 4, 0x00000000, 
+                                      VG_(shadowRegOffset)(arch), R_EBP );
+            break;
+         case 2:
+            vg_assert(lit == 0xFFFF0000);
+            VG_(emit_movv_lit_offregmem) ( 2, 0x0000, 
+                                      VG_(shadowRegOffset)(arch), R_EBP );
+            break;
+         case 1:
+            vg_assert(lit == 0xFFFFFF00);
+            if (arch < 4) {
+               VG_(emit_movb_lit_offregmem) ( 0x00, 
+                                         VG_(shadowRegOffset)(arch), R_EBP );
+            } else {
+               VG_(emit_movb_lit_offregmem) ( 0x00, 
+                                              VG_(shadowRegOffset)(arch-4)+1,
+                                              R_EBP );
+            }
+            break;
+         default: 
+            VG_(panic)("synth_PUTV(lit)");
+      }
+
+   } else {
+
+      UInt reg;
+      vg_assert(srcTag == RealReg);
+
+      if (sz == 1 && lit_or_reg >= 4) {
+         VG_(emit_swapl_reg_EAX) ( lit_or_reg );
+         reg = R_EAX;
+      } else {
+         reg = lit_or_reg;
+      }
+
+      if (sz == 1) vg_assert(reg < 4);
+
+      switch (sz) {
+         case 4:
+            VG_(emit_movv_reg_offregmem) ( 4, reg,
+                                      VG_(shadowRegOffset)(arch), R_EBP );
+            break;
+         case 2:
+            VG_(emit_movv_reg_offregmem) ( 2, reg,
+                                      VG_(shadowRegOffset)(arch), R_EBP );
+            break;
+         case 1:
+            if (arch < 4) {
+               VG_(emit_movb_reg_offregmem) ( reg,
+                                         VG_(shadowRegOffset)(arch), R_EBP );
+	    } else {
+               VG_(emit_movb_reg_offregmem) ( reg,
+                                        VG_(shadowRegOffset)(arch-4)+1, R_EBP );
+            }
+            break;
+         default: 
+            VG_(panic)("synth_PUTV(reg)");
+      }
+
+      if (sz == 1 && lit_or_reg >= 4) {
+         VG_(emit_swapl_reg_EAX) ( lit_or_reg );
+      }
+   }
+}
+
+
+static void synth_GETVF ( Int reg )
+{
+   VG_(emit_movv_offregmem_reg) ( 4, VG_(shadowFlagsOffset)(), R_EBP, reg );
+   /* paranoia only; should be unnecessary ... */
+   /* VG_(emit_nonshiftopv_lit_reg) ( 4, OR, 0xFFFFFFFE, reg ); */
+}
+
+
+static void synth_PUTVF ( UInt reg )
+{
+   VG_(emit_movv_reg_offregmem) ( 4, reg, VG_(shadowFlagsOffset)(), R_EBP );
+}
+
+
+static void synth_TAG1_op ( TagOp op, Int reg, RRegSet regs_live_after )
+{
+   switch (op) {
+
+      /* Scheme is
+            neg<sz> %reg          -- CF = %reg==0 ? 0 : 1
+            sbbl %reg, %reg       -- %reg = -CF
+            or 0xFFFFFFFE, %reg   -- invalidate all bits except lowest
+      */
+      case Tag_PCast40:
+         VG_(emit_unaryopv_reg)(4, NEG, reg);
+         VG_(emit_nonshiftopv_reg_reg)(4, SBB, reg, reg);
+         VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFFFFFE, reg);
+         break;
+      case Tag_PCast20:
+         VG_(emit_unaryopv_reg)(2, NEG, reg);
+         VG_(emit_nonshiftopv_reg_reg)(4, SBB, reg, reg);
+         VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFFFFFE, reg);
+         break;
+      case Tag_PCast10:
+         if (reg >= 4) {
+            VG_(emit_swapl_reg_EAX)(reg);
+            VG_(emit_unaryopb_reg)(NEG, R_EAX);
+            VG_(emit_swapl_reg_EAX)(reg);
+         } else {
+            VG_(emit_unaryopb_reg)(NEG, reg);
+         }
+         VG_(emit_nonshiftopv_reg_reg)(4, SBB, reg, reg);
+         VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFFFFFE, reg);
+         break;
+
+      /* Scheme is
+            andl $1, %reg -- %reg is 0 or 1
+            negl %reg -- %reg is 0 or 0xFFFFFFFF
+            and possibly an OR to invalidate unused bits.
+      */
+      case Tag_PCast04:
+         VG_(emit_nonshiftopv_lit_reg)(4, AND, 0x00000001, reg);
+         VG_(emit_unaryopv_reg)(4, NEG, reg);
+         break;
+      case Tag_PCast02:
+         VG_(emit_nonshiftopv_lit_reg)(4, AND, 0x00000001, reg);
+         VG_(emit_unaryopv_reg)(4, NEG, reg);
+         VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFF0000, reg);
+         break;
+      case Tag_PCast01:
+         VG_(emit_nonshiftopv_lit_reg)(4, AND, 0x00000001, reg);
+         VG_(emit_unaryopv_reg)(4, NEG, reg);
+         VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFFFF00, reg);
+         break;
+
+      /* Scheme is
+            shl $24, %reg -- make irrelevant bits disappear
+            negl %reg             -- CF = %reg==0 ? 0 : 1
+            sbbl %reg, %reg       -- %reg = -CF
+            and possibly an OR to invalidate unused bits.
+      */
+      case Tag_PCast14:
+         VG_(emit_shiftopv_lit_reg)(4, SHL, 24, reg);
+         VG_(emit_unaryopv_reg)(4, NEG, reg);
+         VG_(emit_nonshiftopv_reg_reg)(4, SBB, reg, reg);
+         break;
+      case Tag_PCast12:
+         VG_(emit_shiftopv_lit_reg)(4, SHL, 24, reg);
+         VG_(emit_unaryopv_reg)(4, NEG, reg);
+         VG_(emit_nonshiftopv_reg_reg)(4, SBB, reg, reg);
+         VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFF0000, reg);
+         break;
+      case Tag_PCast11:
+         VG_(emit_shiftopv_lit_reg)(4, SHL, 24, reg);
+         VG_(emit_unaryopv_reg)(4, NEG, reg);
+         VG_(emit_nonshiftopv_reg_reg)(4, SBB, reg, reg);
+         VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFFFF00, reg);
+         break;
+
+      /* We use any non-live reg (except %reg) as a temporary,
+         or push/pop %ebp if none available:
+            (%dead_reg = any dead reg, else %ebp)
+            (pushl %ebp if all regs live)
+            movl %reg, %dead_reg
+            negl %dead_reg
+            orl %dead_reg, %reg
+            (popl %ebp if all regs live)
+         This sequence turns out to be correct regardless of the 
+         operation width.
+      */
+      case Tag_Left4:
+      case Tag_Left2:
+      case Tag_Left1: {
+         UInt dead_reg = R_EBP;
+         Int  i, reg_of_i;
+
+         for (i = 0; i < VG_MAX_REALREGS; i++) {
+            if (! IS_RREG_LIVE(i, regs_live_after)) {
+               reg_of_i = VG_(rankToRealRegNum)(i);
+               if (reg != reg_of_i) {
+                  dead_reg = reg_of_i;
+                  break;
+               }
+            }
+         }
+
+         if (R_EBP == dead_reg)
+            VG_(emit_pushv_reg)(4, dead_reg);
+         VG_(emit_movv_reg_reg)(4, reg, dead_reg);
+         VG_(emit_unaryopv_reg)(4, NEG, dead_reg);
+         VG_(emit_nonshiftopv_reg_reg)(4, OR, dead_reg, reg);
+         if (R_EBP == dead_reg)
+            VG_(emit_popv_reg)(4, dead_reg);
+         break;
+      }
+
+      /* These are all fairly obvious; do the op and then, if
+         necessary, invalidate unused bits. */
+      case Tag_SWiden14:
+         VG_(emit_shiftopv_lit_reg)(4, SHL, 24, reg);
+         VG_(emit_shiftopv_lit_reg)(4, SAR, 24, reg);
+         break;
+      case Tag_SWiden24:
+         VG_(emit_shiftopv_lit_reg)(4, SHL, 16, reg);
+         VG_(emit_shiftopv_lit_reg)(4, SAR, 16, reg);
+         break;
+      case Tag_SWiden12:
+         VG_(emit_shiftopv_lit_reg)(4, SHL, 24, reg);
+         VG_(emit_shiftopv_lit_reg)(4, SAR, 24, reg);
+         VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFF0000, reg);
+         break;
+      case Tag_ZWiden14:
+         VG_(emit_nonshiftopv_lit_reg)(4, AND, 0x000000FF, reg);
+         break;
+      case Tag_ZWiden24:
+         VG_(emit_nonshiftopv_lit_reg)(4, AND, 0x0000FFFF, reg);
+         break;
+      case Tag_ZWiden12:
+         VG_(emit_nonshiftopv_lit_reg)(4, AND, 0x000000FF, reg);
+         VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFF0000, reg);
+         break;
+
+      default:
+         VG_(panic)("synth_TAG1_op");
+   }
+}
+
+
+static void synth_TAG2_op ( TagOp op, Int regs, Int regd )
+{
+   switch (op) {
+
+      /* UifU is implemented by OR, since 1 means Undefined. */
+      case Tag_UifU4:
+      case Tag_UifU2:
+      case Tag_UifU1:
+      case Tag_UifU0:
+         VG_(emit_nonshiftopv_reg_reg)(4, OR, regs, regd);
+         break;
+
+      /* DifD is implemented by AND, since 0 means Defined. */
+      case Tag_DifD4:
+      case Tag_DifD2:
+      case Tag_DifD1:
+         VG_(emit_nonshiftopv_reg_reg)(4, AND, regs, regd);
+         break;
+
+      /* ImproveAND(value, tags) = value OR tags.
+	 Defined (0) value 0s give defined (0); all other -> undefined (1).
+         value is in regs; tags is in regd. 
+         Be paranoid and invalidate unused bits; I don't know whether 
+         or not this is actually necessary. */
+      case Tag_ImproveAND4_TQ:
+         VG_(emit_nonshiftopv_reg_reg)(4, OR, regs, regd);
+         break;
+      case Tag_ImproveAND2_TQ:
+         VG_(emit_nonshiftopv_reg_reg)(4, OR, regs, regd);
+         VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFF0000, regd);
+         break;
+      case Tag_ImproveAND1_TQ:
+         VG_(emit_nonshiftopv_reg_reg)(4, OR, regs, regd);
+         VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFFFF00, regd);
+         break;
+
+      /* ImproveOR(value, tags) = (not value) OR tags.
+	 Defined (0) value 1s give defined (0); all other -> undefined (1).
+         value is in regs; tags is in regd. 
+         To avoid trashing value, this is implemented (re de Morgan) as
+               not (value AND (not tags))
+         Be paranoid and invalidate unused bits; I don't know whether 
+         or not this is actually necessary. */
+      case Tag_ImproveOR4_TQ:
+         VG_(emit_unaryopv_reg)(4, NOT, regd);
+         VG_(emit_nonshiftopv_reg_reg)(4, AND, regs, regd);
+         VG_(emit_unaryopv_reg)(4, NOT, regd);
+         break;
+      case Tag_ImproveOR2_TQ:
+         VG_(emit_unaryopv_reg)(4, NOT, regd);
+         VG_(emit_nonshiftopv_reg_reg)(4, AND, regs, regd);
+         VG_(emit_unaryopv_reg)(4, NOT, regd);
+         VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFF0000, regd);
+         break;
+      case Tag_ImproveOR1_TQ:
+         VG_(emit_unaryopv_reg)(4, NOT, regd);
+         VG_(emit_nonshiftopv_reg_reg)(4, AND, regs, regd);
+         VG_(emit_unaryopv_reg)(4, NOT, regd);
+         VG_(emit_nonshiftopv_lit_reg)(4, OR, 0xFFFFFF00, regd);
+         break;
+
+      default:
+         VG_(panic)("synth_TAG2_op");
+   }
+}
+
+/*----------------------------------------------------*/
+/*--- Generate code for a single UInstr.           ---*/
+/*----------------------------------------------------*/
+
+void SK_(emitExtUInstr) ( UInstr* u, RRegSet regs_live_before )
+{
+   switch (u->opcode) {
+
+      case SETV:
+         vg_assert(u->tag1 == RealReg);
+         synth_SETV ( u->size, u->val1 );
+         break;
+
+      case STOREV:
+         vg_assert(u->tag1 == RealReg || u->tag1 == Literal);
+         vg_assert(u->tag2 == RealReg);
+         synth_STOREV ( u->size, u->tag1, 
+                                 u->tag1==Literal ? u->lit32 : u->val1, 
+                                 u->val2,
+                        regs_live_before, u->regs_live_after );
+         break;
+
+      case LOADV:
+         vg_assert(u->tag1 == RealReg);
+         vg_assert(u->tag2 == RealReg);
+         if (0)
+            VG_(emit_AMD_prefetch_reg) ( u->val1 );
+         synth_LOADV ( u->size, u->val1, u->val2,
+                       regs_live_before, u->regs_live_after );
+         break;
+
+      case TESTV:
+         vg_assert(u->tag1 == RealReg || u->tag1 == ArchReg);
+         synth_TESTV(u->size, u->tag1, u->val1);
+         break;
+
+      case GETV:
+         vg_assert(u->tag1 == ArchReg);
+         vg_assert(u->tag2 == RealReg);
+         synth_GETV(u->size, u->val1, u->val2);
+         break;
+
+      case GETVF:
+         vg_assert(u->tag1 == RealReg);
+         vg_assert(u->size == 0);
+         synth_GETVF(u->val1);
+         break;
+
+      case PUTV:
+         vg_assert(u->tag1 == RealReg || u->tag1 == Literal);
+         vg_assert(u->tag2 == ArchReg);
+         synth_PUTV(u->size, u->tag1, 
+                             u->tag1==Literal ? u->lit32 : u->val1, 
+                             u->val2 );
+         break;
+
+      case PUTVF:
+         vg_assert(u->tag1 == RealReg);
+         vg_assert(u->size == 0);
+         synth_PUTVF(u->val1);
+         break;
+
+      case TAG1:
+         synth_TAG1_op ( u->val3, u->val1, u->regs_live_after );
+         break;
+
+      case TAG2:
+         synth_TAG2_op ( u->val3, u->val1, u->val2 );
+         break;
+
+      default: 
+         VG_(printf)("emitExtUInstr: unhandled extension insn:\n");
+         VG_(ppUInstr)(0,u);
+         VG_(panic)("emitExtUInstr: unhandled extension opcode");
+   }
+}
+
+/*--------------------------------------------------------------------*/
+/*--- end                                 vg_memcheck_from_ucode.c ---*/
+/*--------------------------------------------------------------------*/
diff --git a/memcheck/mc_helpers.S b/memcheck/mc_helpers.S
new file mode 100644
index 0000000..515c873
--- /dev/null
+++ b/memcheck/mc_helpers.S
@@ -0,0 +1,62 @@
+##--------------------------------------------------------------------##
+##--- Support routines for the memory checker.                     ---##
+##---                                        vg_memcheck_helpers.S ---##
+##--------------------------------------------------------------------##
+
+/*
+  This file is part of Valgrind, an x86 protected-mode emulator 
+  designed for debugging and profiling binaries on x86-Unixes.
+
+  Copyright (C) 2000-2002 Julian Seward 
+     jseward@acm.org
+
+  This program 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 2 of the
+  License, or (at your option) any later version.
+
+  This program 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 this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+  02111-1307, USA.
+
+  The GNU General Public License is contained in the file COPYING.
+*/
+
+#include "vg_constants.h"
+
+.global SK_(helper_value_check0_fail)
+SK_(helper_value_check0_fail):
+	pushal
+	call	SK_(helperc_value_check0_fail)
+	popal
+	ret
+
+.global SK_(helper_value_check1_fail)
+SK_(helper_value_check1_fail):
+	pushal
+	call	SK_(helperc_value_check1_fail)
+	popal
+	ret
+
+.global SK_(helper_value_check2_fail)
+SK_(helper_value_check2_fail):
+	pushal
+	call	SK_(helperc_value_check2_fail)
+	popal
+	ret
+
+.global SK_(helper_value_check4_fail)
+SK_(helper_value_check4_fail):
+	pushal
+	call	SK_(helperc_value_check4_fail)
+	popal
+	ret
+
+
+
diff --git a/memcheck/mc_include.h b/memcheck/mc_include.h
new file mode 100644
index 0000000..82bcae7
--- /dev/null
+++ b/memcheck/mc_include.h
@@ -0,0 +1,209 @@
+/*--------------------------------------------------------------------*/
+/*--- A header file for all parts of the MemCheck skin.            ---*/
+/*---                                        vg_memcheck_include.h ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, an x86 protected-mode emulator 
+   designed for debugging and profiling binaries on x86-Unixes.
+
+   Copyright (C) 2000-2002 Julian Seward 
+      jseward@acm.org
+
+   This program 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 2 of the
+   License, or (at your option) any later version.
+
+   This program 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 this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+#ifndef __VG_MEMCHECK_INCLUDE_H
+#define __VG_MEMCHECK_INCLUDE_H
+
+#include "vg_skin.h"
+
+/* UCode extension for efficient memory checking operations */
+typedef
+   enum {
+      /* uinstrs which are not needed for mere translation of x86 code,
+         only for instrumentation of it. */
+      LOADV = DUMMY_FINAL_UOPCODE + 1,
+      STOREV,
+      GETV,
+      PUTV,
+      TESTV,
+      SETV, 
+      /* Get/set the v-bit (and it is only one bit) for the simulated
+         %eflags register. */
+      GETVF,
+      PUTVF,
+
+      /* Do a unary or binary tag op.  Only for post-instrumented
+         code.  For TAG1, first and only arg is a TempReg, and is both
+         arg and result reg.  For TAG2, first arg is src, second is
+         dst, in the normal way; both are TempRegs.  In both cases,
+         3rd arg is a RiCHelper with a Lit16 tag.  This indicates
+         which tag op to do. */
+      TAG1,
+      TAG2
+   }
+   MemCheckOpcode;
+
+
+/* Lists the names of value-tag operations used in instrumented
+   code.  These are the third argument to TAG1 and TAG2 uinsns. */
+typedef
+   enum { 
+     /* Unary. */
+     Tag_PCast40, Tag_PCast20, Tag_PCast10,
+     Tag_PCast01, Tag_PCast02, Tag_PCast04,
+
+     Tag_PCast14, Tag_PCast12, Tag_PCast11,
+
+     Tag_Left4, Tag_Left2, Tag_Left1,
+
+     Tag_SWiden14, Tag_SWiden24, Tag_SWiden12,
+     Tag_ZWiden14, Tag_ZWiden24, Tag_ZWiden12,
+
+     /* Binary; 1st is rd; 2nd is rd+wr */
+     Tag_UifU4, Tag_UifU2, Tag_UifU1, Tag_UifU0,
+     Tag_DifD4, Tag_DifD2, Tag_DifD1,
+
+     Tag_ImproveAND4_TQ, Tag_ImproveAND2_TQ, Tag_ImproveAND1_TQ,
+     Tag_ImproveOR4_TQ, Tag_ImproveOR2_TQ, Tag_ImproveOR1_TQ,
+     Tag_DebugFn
+   }
+   TagOp;
+
+/* The classification of a faulting address. */
+typedef 
+   enum { Undescribed, /* as-yet unclassified */
+          Stack, 
+          Unknown, /* classification yielded nothing useful */
+          Freed, Mallocd, 
+          UserG, UserS 
+   }
+   AddrKind;
+
+/* Records info about a faulting address. */
+typedef
+   struct {
+      /* ALL */
+      AddrKind akind;
+      /* Freed, Mallocd */
+      Int blksize;
+      /* Freed, Mallocd */
+      Int rwoffset;
+      /* Freed, Mallocd */
+      ExeContext* lastchange;
+      /* Stack */
+      ThreadId stack_tid;
+      /* True if is just-below %esp -- could be a gcc bug. */
+      Bool maybe_gcc;
+   }
+   AddrInfo;
+
+
+/*------------------------------------------------------------*/
+/*--- Skin-specific command line options + defaults        ---*/
+/*------------------------------------------------------------*/
+
+/* Allow loads from partially-valid addresses?  default: YES */
+extern Bool SK_(clo_partial_loads_ok);
+
+/* Max volume of the freed blocks queue. */
+extern Int SK_(clo_freelist_vol);
+
+/* Do leak check at exit?  default: NO */
+extern Bool SK_(clo_leak_check);
+
+/* How closely should we compare ExeContexts in leak records? default: 2 */
+extern VgRes SK_(clo_leak_resolution);
+
+/* In leak check, show reachable-but-not-freed blocks?  default: NO */
+extern Bool SK_(clo_show_reachable);
+
+/* Assume accesses immediately below %esp are due to gcc-2.96 bugs.
+ * default: NO*/
+extern Bool SK_(clo_workaround_gcc296_bugs);
+
+/* Shall we V-check addrs? (they are always A checked too)   default: YES */
+extern Bool SK_(clo_check_addrVs);
+
+/* DEBUG: clean up instrumented code?  default: YES */
+extern Bool SK_(clo_cleanup);
+
+
+/*------------------------------------------------------------*/
+/*--- Functions                                            ---*/
+/*------------------------------------------------------------*/
+
+// SSS: work out a consistent prefix convention here
+
+/* Functions defined in vg_memcheck_helpers.S */
+extern void SK_(helper_value_check4_fail) ( void );
+extern void SK_(helper_value_check2_fail) ( void );
+extern void SK_(helper_value_check1_fail) ( void );
+extern void SK_(helper_value_check0_fail) ( void );
+
+/* Functions defined in vg_memcheck.c */
+extern void SK_(helperc_STOREV4) ( UInt, Addr );
+extern void SK_(helperc_STOREV2) ( UInt, Addr );
+extern void SK_(helperc_STOREV1) ( UInt, Addr );
+   
+extern UInt SK_(helperc_LOADV1) ( Addr );
+extern UInt SK_(helperc_LOADV2) ( Addr );
+extern UInt SK_(helperc_LOADV4) ( Addr );
+
+extern void SK_(fpu_write_check) ( Addr addr, Int size );
+extern void SK_(fpu_read_check)  ( Addr addr, Int size );
+
+extern ShadowChunk* SK_(any_matching_freed_ShadowChunks) 
+                        ( Bool (*p) ( ShadowChunk* ) );
+
+/* For client requests */
+extern void SK_(make_noaccess) ( Addr a, UInt len );
+extern void SK_(make_readable) ( Addr a, UInt len );
+extern void SK_(make_writable) ( Addr a, UInt len );
+
+extern Bool SK_(check_writable) ( Addr a, UInt len, Addr* bad_addr );
+extern Bool SK_(check_readable) ( Addr a, UInt len, Addr* bad_addr );
+
+extern void SK_(detect_memory_leaks) ( void );
+
+
+/* Functions defined in vg_memcheck_clientreqs.c */
+extern Bool SK_(client_perm_maybe_describe)( Addr a, AddrInfo* ai );
+extern void SK_(delete_client_stack_blocks_following_ESP_change) ( void );
+extern void SK_(show_client_block_stats) ( void );
+
+/* Functions defined in vg_memcheck_errcontext.c */
+extern void SK_(record_value_error)       ( Int size );
+extern void SK_(record_address_error)     ( Addr a, Int size, Bool isWrite );
+extern void SK_(record_core_mem_error)    ( ThreadState* tst, Bool isWrite,
+                                            Char* s );
+extern void SK_(record_param_error)       ( ThreadState* tst, Addr a,   
+                                            Bool isWriteLack, Char* msg );
+extern void SK_(record_jump_error)        ( ThreadState* tst, Addr a );
+extern void SK_(record_free_error)        ( ThreadState* tst, Addr a );
+extern void SK_(record_freemismatch_error)( ThreadState* tst, Addr a );
+extern void SK_(record_user_error)        ( ThreadState* tst, Addr a, 
+                                            Bool isWrite );
+
+#endif
+
+/*--------------------------------------------------------------------*/
+/*--- end                                    vg_memcheck_include.h ---*/
+/*--------------------------------------------------------------------*/
+
diff --git a/memcheck/mc_main.c b/memcheck/mc_main.c
new file mode 100644
index 0000000..4ee380f
--- /dev/null
+++ b/memcheck/mc_main.c
@@ -0,0 +1,2428 @@
+/*--------------------------------------------------------------------*/
+/*--- Part of the MemCheck skin: Maintain bitmaps of memory,       ---*/
+/*--- tracking the accessibility (A) and validity (V) status of    ---*/
+/*--- each byte.                                                   ---*/
+/*---                                                vg_memcheck.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, an x86 protected-mode emulator 
+   designed for debugging and profiling binaries on x86-Unixes.
+
+   Copyright (C) 2000-2002 Julian Seward 
+      jseward@acm.org
+
+   This program 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 2 of the
+   License, or (at your option) any later version.
+
+   This program 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 this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+#include "vg_memcheck_include.h"
+#include "vg_memcheck.h"   /* for client requests */
+//#include "vg_profile.c"
+
+/* Define to debug the mem audit system. */
+/* #define VG_DEBUG_MEMORY */
+
+/* Define to debug the memory-leak-detector. */
+/* #define VG_DEBUG_LEAKCHECK */
+
+/* Define to collect detailed performance info. */
+/* #define VG_PROFILE_MEMORY */
+
+#define DEBUG(fmt, args...) //VG_(printf)(fmt, ## args)
+
+/*------------------------------------------------------------*/
+/*--- Command line options                                 ---*/
+/*------------------------------------------------------------*/
+
+Bool  SK_(clo_partial_loads_ok)       = True;
+Int   SK_(clo_freelist_vol)           = 1000000;
+Bool  SK_(clo_leak_check)             = False;
+VgRes SK_(clo_leak_resolution)        = Vg_LowRes;
+Bool  SK_(clo_show_reachable)         = False;
+Bool  SK_(clo_workaround_gcc296_bugs) = False;
+Bool  SK_(clo_check_addrVs)           = True;
+Bool  SK_(clo_cleanup)                = True;
+
+/*------------------------------------------------------------*/
+/*--- Profiling events                                     ---*/
+/*------------------------------------------------------------*/
+
+typedef 
+   enum { 
+      VgpCheckMem = VgpFini+1,
+      VgpSetMem
+   } 
+   VgpSkinCC;
+
+/*------------------------------------------------------------*/
+/*--- Low-level support for memory checking.               ---*/
+/*------------------------------------------------------------*/
+
+/* All reads and writes are checked against a memory map, which
+   records the state of all memory in the process.  The memory map is
+   organised like this:
+
+   The top 16 bits of an address are used to index into a top-level
+   map table, containing 65536 entries.  Each entry is a pointer to a
+   second-level map, which records the accesibililty and validity
+   permissions for the 65536 bytes indexed by the lower 16 bits of the
+   address.  Each byte is represented by nine bits, one indicating
+   accessibility, the other eight validity.  So each second-level map
+   contains 73728 bytes.  This two-level arrangement conveniently
+   divides the 4G address space into 64k lumps, each size 64k bytes.
+
+   All entries in the primary (top-level) map must point to a valid
+   secondary (second-level) map.  Since most of the 4G of address
+   space will not be in use -- ie, not mapped at all -- there is a
+   distinguished secondary map, which indicates `not addressible and
+   not valid' writeable for all bytes.  Entries in the primary map for
+   which the entire 64k is not in use at all point at this
+   distinguished map.
+
+   [...] lots of stuff deleted due to out of date-ness
+
+   As a final optimisation, the alignment and address checks for
+   4-byte loads and stores are combined in a neat way.  The primary
+   map is extended to have 262144 entries (2^18), rather than 2^16.
+   The top 3/4 of these entries are permanently set to the
+   distinguished secondary map.  For a 4-byte load/store, the
+   top-level map is indexed not with (addr >> 16) but instead f(addr),
+   where
+
+    f( XXXX XXXX XXXX XXXX ____ ____ ____ __YZ )
+        = ____ ____ ____ __YZ XXXX XXXX XXXX XXXX  or 
+        = ____ ____ ____ __ZY XXXX XXXX XXXX XXXX
+
+   ie the lowest two bits are placed above the 16 high address bits.
+   If either of these two bits are nonzero, the address is misaligned;
+   this will select a secondary map from the upper 3/4 of the primary
+   map.  Because this is always the distinguished secondary map, a
+   (bogus) address check failure will result.  The failure handling
+   code can then figure out whether this is a genuine addr check
+   failure or whether it is a possibly-legitimate access at a
+   misaligned address.  
+*/
+
+
+/*------------------------------------------------------------*/
+/*--- Crude profiling machinery.                           ---*/
+/*------------------------------------------------------------*/
+
+#ifdef VG_PROFILE_MEMORY
+
+#define N_PROF_EVENTS 150
+
+static UInt event_ctr[N_PROF_EVENTS];
+
+static void init_prof_mem ( void )
+{
+   Int i;
+   for (i = 0; i < N_PROF_EVENTS; i++)
+      event_ctr[i] = 0;
+}
+
+static void done_prof_mem ( void )
+{
+   Int i;
+   for (i = 0; i < N_PROF_EVENTS; i++) {
+      if ((i % 10) == 0) 
+         VG_(printf)("\n");
+      if (event_ctr[i] > 0)
+         VG_(printf)( "prof mem event %2d: %d\n", i, event_ctr[i] );
+   }
+   VG_(printf)("\n");
+}
+
+#define PROF_EVENT(ev)                                  \
+   do { vg_assert((ev) >= 0 && (ev) < N_PROF_EVENTS);   \
+        event_ctr[ev]++;                                \
+   } while (False);
+
+#else
+
+static void init_prof_mem ( void ) { }
+static void done_prof_mem ( void ) { }
+
+#define PROF_EVENT(ev) /* */
+
+#endif
+
+/* Event index.  If just the name of the fn is given, this means the
+   number of calls to the fn.  Otherwise it is the specified event.
+
+   10   alloc_secondary_map
+
+   20   get_abit
+   21   get_vbyte
+   22   set_abit
+   23   set_vbyte
+   24   get_abits4_ALIGNED
+   25   get_vbytes4_ALIGNED
+
+   30   set_address_range_perms
+   31   set_address_range_perms(lower byte loop)
+   32   set_address_range_perms(quadword loop)
+   33   set_address_range_perms(upper byte loop)
+   
+   35   make_noaccess
+   36   make_writable
+   37   make_readable
+
+   40   copy_address_range_state
+   41   copy_address_range_state(byte loop)
+   42   check_writable
+   43   check_writable(byte loop)
+   44   check_readable
+   45   check_readable(byte loop)
+   46   check_readable_asciiz
+   47   check_readable_asciiz(byte loop)
+
+   50   make_aligned_word_NOACCESS
+   51   make_aligned_word_WRITABLE
+
+   60   helperc_LOADV4
+   61   helperc_STOREV4
+   62   helperc_LOADV2
+   63   helperc_STOREV2
+   64   helperc_LOADV1
+   65   helperc_STOREV1
+
+   70   rim_rd_V4_SLOWLY
+   71   rim_wr_V4_SLOWLY
+   72   rim_rd_V2_SLOWLY
+   73   rim_wr_V2_SLOWLY
+   74   rim_rd_V1_SLOWLY
+   75   rim_wr_V1_SLOWLY
+
+   80   fpu_read
+   81   fpu_read aligned 4
+   82   fpu_read aligned 8
+   83   fpu_read 2
+   84   fpu_read 10
+
+   85   fpu_write
+   86   fpu_write aligned 4
+   87   fpu_write aligned 8
+   88   fpu_write 2
+   89   fpu_write 10
+
+   90   fpu_read_check_SLOWLY
+   91   fpu_read_check_SLOWLY(byte loop)
+   92   fpu_write_check_SLOWLY
+   93   fpu_write_check_SLOWLY(byte loop)
+
+   100  is_plausible_stack_addr
+   101  handle_esp_assignment
+   102  handle_esp_assignment(-4)
+   103  handle_esp_assignment(+4)
+   104  handle_esp_assignment(-12)
+   105  handle_esp_assignment(-8)
+   106  handle_esp_assignment(+16)
+   107  handle_esp_assignment(+12)
+   108  handle_esp_assignment(0)
+   109  handle_esp_assignment(+8)
+   110  handle_esp_assignment(-16)
+   111  handle_esp_assignment(+20)
+   112  handle_esp_assignment(-20)
+   113  handle_esp_assignment(+24)
+   114  handle_esp_assignment(-24)
+
+   120  vg_handle_esp_assignment_SLOWLY
+   121  vg_handle_esp_assignment_SLOWLY(normal; move down)
+   122  vg_handle_esp_assignment_SLOWLY(normal; move up)
+   123  vg_handle_esp_assignment_SLOWLY(normal)
+   124  vg_handle_esp_assignment_SLOWLY(>= HUGE_DELTA)
+*/
+
+/*------------------------------------------------------------*/
+/*--- Function declarations.                               ---*/
+/*------------------------------------------------------------*/
+
+static UInt vgmext_rd_V4_SLOWLY ( Addr a );
+static UInt vgmext_rd_V2_SLOWLY ( Addr a );
+static UInt vgmext_rd_V1_SLOWLY ( Addr a );
+static void vgmext_wr_V4_SLOWLY ( Addr a, UInt vbytes );
+static void vgmext_wr_V2_SLOWLY ( Addr a, UInt vbytes );
+static void vgmext_wr_V1_SLOWLY ( Addr a, UInt vbytes );
+static void fpu_read_check_SLOWLY ( Addr addr, Int size );
+static void fpu_write_check_SLOWLY ( Addr addr, Int size );
+
+/*------------------------------------------------------------*/
+/*--- Data defns.                                          ---*/
+/*------------------------------------------------------------*/
+
+typedef 
+   struct {
+      UChar abits[8192];
+      UChar vbyte[65536];
+   }
+   SecMap;
+
+static SecMap* primary_map[ /*65536*/ 262144 ];
+static SecMap  distinguished_secondary_map;
+
+#define IS_DISTINGUISHED_SM(smap) \
+   ((smap) == &distinguished_secondary_map)
+
+#define ENSURE_MAPPABLE(addr,caller)                                   \
+   do {                                                                \
+      if (IS_DISTINGUISHED_SM(primary_map[(addr) >> 16])) {       \
+         primary_map[(addr) >> 16] = alloc_secondary_map(caller); \
+         /* VG_(printf)("new 2map because of %p\n", addr); */          \
+      }                                                                \
+   } while(0)
+
+#define BITARR_SET(aaa_p,iii_p)                         \
+   do {                                                 \
+      UInt   iii = (UInt)iii_p;                         \
+      UChar* aaa = (UChar*)aaa_p;                       \
+      aaa[iii >> 3] |= (1 << (iii & 7));                \
+   } while (0)
+
+#define BITARR_CLEAR(aaa_p,iii_p)                       \
+   do {                                                 \
+      UInt   iii = (UInt)iii_p;                         \
+      UChar* aaa = (UChar*)aaa_p;                       \
+      aaa[iii >> 3] &= ~(1 << (iii & 7));               \
+   } while (0)
+
+#define BITARR_TEST(aaa_p,iii_p)                        \
+      (0 != (((UChar*)aaa_p)[ ((UInt)iii_p) >> 3 ]      \
+               & (1 << (((UInt)iii_p) & 7))))           \
+
+
+#define VGM_BIT_VALID      0
+#define VGM_BIT_INVALID    1
+
+#define VGM_NIBBLE_VALID   0
+#define VGM_NIBBLE_INVALID 0xF
+
+#define VGM_BYTE_VALID     0
+#define VGM_BYTE_INVALID   0xFF
+
+#define VGM_WORD_VALID     0
+#define VGM_WORD_INVALID   0xFFFFFFFF
+
+#define VGM_EFLAGS_VALID   0xFFFFFFFE
+#define VGM_EFLAGS_INVALID 0xFFFFFFFF     /* not used */
+
+
+static void init_shadow_memory ( void )
+{
+   Int i;
+
+   for (i = 0; i < 8192; i++)             /* Invalid address */
+      distinguished_secondary_map.abits[i] = VGM_BYTE_INVALID; 
+   for (i = 0; i < 65536; i++)            /* Invalid Value */
+      distinguished_secondary_map.vbyte[i] = VGM_BYTE_INVALID; 
+
+   /* These entries gradually get overwritten as the used address
+      space expands. */
+   for (i = 0; i < 65536; i++)
+      primary_map[i] = &distinguished_secondary_map;
+
+   /* These ones should never change; it's a bug in Valgrind if they do. */
+   for (i = 65536; i < 262144; i++)
+      primary_map[i] = &distinguished_secondary_map;
+}
+
+void SK_(post_clo_init) ( void )
+{
+}
+
+void SK_(fini) ( void )
+{
+   VG_(print_malloc_stats)();
+
+   if (VG_(clo_verbosity) == 1) {
+      if (!SK_(clo_leak_check))
+         VG_(message)(Vg_UserMsg, 
+             "For a detailed leak analysis,  rerun with: --leak-check=yes");
+
+      VG_(message)(Vg_UserMsg, 
+                   "For counts of detected errors, rerun with: -v");
+   }
+   if (SK_(clo_leak_check)) SK_(detect_memory_leaks)();
+
+   done_prof_mem();
+
+   if (0) {
+      VG_(message)(Vg_DebugMsg, 
+        "------ Valgrind's client block stats follow ---------------" );
+      SK_(show_client_block_stats)();
+   }
+}
+
+/*------------------------------------------------------------*/
+/*--- Basic bitmap management, reading and writing.        ---*/
+/*------------------------------------------------------------*/
+
+/* Allocate and initialise a secondary map. */
+
+static SecMap* alloc_secondary_map ( __attribute__ ((unused)) 
+                                     Char* caller )
+{
+   SecMap* map;
+   UInt  i;
+   PROF_EVENT(10);
+
+   /* Mark all bytes as invalid access and invalid value. */
+
+   /* It just happens that a SecMap occupies exactly 18 pages --
+      although this isn't important, so the following assert is
+      spurious. */
+   vg_assert(0 == (sizeof(SecMap) % VKI_BYTES_PER_PAGE));
+   map = VG_(get_memory_from_mmap)( sizeof(SecMap), caller );
+
+   for (i = 0; i < 8192; i++)
+      map->abits[i] = VGM_BYTE_INVALID; /* Invalid address */
+   for (i = 0; i < 65536; i++)
+      map->vbyte[i] = VGM_BYTE_INVALID; /* Invalid Value */
+
+   /* VG_(printf)("ALLOC_2MAP(%s)\n", caller ); */
+   return map;
+}
+
+
+/* Basic reading/writing of the bitmaps, for byte-sized accesses. */
+
+static __inline__ UChar get_abit ( Addr a )
+{
+   SecMap* sm     = primary_map[a >> 16];
+   UInt    sm_off = a & 0xFFFF;
+   PROF_EVENT(20);
+#  if 0
+      if (IS_DISTINGUISHED_SM(sm))
+         VG_(message)(Vg_DebugMsg, 
+                      "accessed distinguished 2ndary (A)map! 0x%x\n", a);
+#  endif
+   return BITARR_TEST(sm->abits, sm_off) 
+             ? VGM_BIT_INVALID : VGM_BIT_VALID;
+}
+
+static __inline__ UChar get_vbyte ( Addr a )
+{
+   SecMap* sm     = primary_map[a >> 16];
+   UInt    sm_off = a & 0xFFFF;
+   PROF_EVENT(21);
+#  if 0
+      if (IS_DISTINGUISHED_SM(sm))
+         VG_(message)(Vg_DebugMsg, 
+                      "accessed distinguished 2ndary (V)map! 0x%x\n", a);
+#  endif
+   return sm->vbyte[sm_off];
+}
+
+static __inline__ void set_abit ( Addr a, UChar abit )
+{
+   SecMap* sm;
+   UInt    sm_off;
+   PROF_EVENT(22);
+   ENSURE_MAPPABLE(a, "set_abit");
+   sm     = primary_map[a >> 16];
+   sm_off = a & 0xFFFF;
+   if (abit) 
+      BITARR_SET(sm->abits, sm_off);
+   else
+      BITARR_CLEAR(sm->abits, sm_off);
+}
+
+static __inline__ void set_vbyte ( Addr a, UChar vbyte )
+{
+   SecMap* sm;
+   UInt    sm_off;
+   PROF_EVENT(23);
+   ENSURE_MAPPABLE(a, "set_vbyte");
+   sm     = primary_map[a >> 16];
+   sm_off = a & 0xFFFF;
+   sm->vbyte[sm_off] = vbyte;
+}
+
+
+/* Reading/writing of the bitmaps, for aligned word-sized accesses. */
+
+static __inline__ UChar get_abits4_ALIGNED ( Addr a )
+{
+   SecMap* sm;
+   UInt    sm_off;
+   UChar   abits8;
+   PROF_EVENT(24);
+#  ifdef VG_DEBUG_MEMORY
+   vg_assert(IS_ALIGNED4_ADDR(a));
+#  endif
+   sm     = primary_map[a >> 16];
+   sm_off = a & 0xFFFF;
+   abits8 = sm->abits[sm_off >> 3];
+   abits8 >>= (a & 4 /* 100b */);   /* a & 4 is either 0 or 4 */
+   abits8 &= 0x0F;
+   return abits8;
+}
+
+static UInt __inline__ get_vbytes4_ALIGNED ( Addr a )
+{
+   SecMap* sm     = primary_map[a >> 16];
+   UInt    sm_off = a & 0xFFFF;
+   PROF_EVENT(25);
+#  ifdef VG_DEBUG_MEMORY
+   vg_assert(IS_ALIGNED4_ADDR(a));
+#  endif
+   return ((UInt*)(sm->vbyte))[sm_off >> 2];
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Setting permissions over address ranges.             ---*/
+/*------------------------------------------------------------*/
+
+static void set_address_range_perms ( Addr a, UInt len, 
+                                      UInt example_a_bit,
+                                      UInt example_v_bit )
+{
+   UChar   vbyte, abyte8;
+   UInt    vword4, sm_off;
+   SecMap* sm;
+
+   PROF_EVENT(30);
+
+   if (len == 0)
+      return;
+
+   if (len > 100 * 1000 * 1000) {
+      VG_(message)(Vg_UserMsg, 
+                   "Warning: set address range perms: "
+                   "large range %u, a %d, v %d",
+                   len, example_a_bit, example_v_bit );
+   }
+
+   VGP_PUSHCC(VgpSetMem);
+
+   /* Requests to change permissions of huge address ranges may
+      indicate bugs in our machinery.  30,000,000 is arbitrary, but so
+      far all legitimate requests have fallen beneath that size. */
+   /* 4 Mar 02: this is just stupid; get rid of it. */
+   /* vg_assert(len < 30000000); */
+
+   /* Check the permissions make sense. */
+   vg_assert(example_a_bit == VGM_BIT_VALID 
+             || example_a_bit == VGM_BIT_INVALID);
+   vg_assert(example_v_bit == VGM_BIT_VALID 
+             || example_v_bit == VGM_BIT_INVALID);
+   if (example_a_bit == VGM_BIT_INVALID)
+      vg_assert(example_v_bit == VGM_BIT_INVALID);
+
+   /* The validity bits to write. */
+   vbyte = example_v_bit==VGM_BIT_VALID 
+              ? VGM_BYTE_VALID : VGM_BYTE_INVALID;
+
+   /* In order that we can charge through the address space at 8
+      bytes/main-loop iteration, make up some perms. */
+   abyte8 = (example_a_bit << 7)
+            | (example_a_bit << 6)
+            | (example_a_bit << 5)
+            | (example_a_bit << 4)
+            | (example_a_bit << 3)
+            | (example_a_bit << 2)
+            | (example_a_bit << 1)
+            | (example_a_bit << 0);
+   vword4 = (vbyte << 24) | (vbyte << 16) | (vbyte << 8) | vbyte;
+
+#  ifdef VG_DEBUG_MEMORY
+   /* Do it ... */
+   while (True) {
+      PROF_EVENT(31);
+      if (len == 0) break;
+      set_abit ( a, example_a_bit );
+      set_vbyte ( a, vbyte );
+      a++;
+      len--;
+   }
+
+#  else
+   /* Slowly do parts preceding 8-byte alignment. */
+   while (True) {
+      PROF_EVENT(31);
+      if (len == 0) break;
+      if ((a % 8) == 0) break;
+      set_abit ( a, example_a_bit );
+      set_vbyte ( a, vbyte );
+      a++;
+      len--;
+   }   
+
+   if (len == 0) {
+      VGP_POPCC(VgpSetMem);
+      return;
+   }
+   vg_assert((a % 8) == 0 && len > 0);
+
+   /* Once aligned, go fast. */
+   while (True) {
+      PROF_EVENT(32);
+      if (len < 8) break;
+      ENSURE_MAPPABLE(a, "set_address_range_perms(fast)");
+      sm = primary_map[a >> 16];
+      sm_off = a & 0xFFFF;
+      sm->abits[sm_off >> 3] = abyte8;
+      ((UInt*)(sm->vbyte))[(sm_off >> 2) + 0] = vword4;
+      ((UInt*)(sm->vbyte))[(sm_off >> 2) + 1] = vword4;
+      a += 8;
+      len -= 8;
+   }
+
+   if (len == 0) {
+      VGP_POPCC(VgpSetMem);
+      return;
+   }
+   vg_assert((a % 8) == 0 && len > 0 && len < 8);
+
+   /* Finish the upper fragment. */
+   while (True) {
+      PROF_EVENT(33);
+      if (len == 0) break;
+      set_abit ( a, example_a_bit );
+      set_vbyte ( a, vbyte );
+      a++;
+      len--;
+   }   
+#  endif
+
+   /* Check that zero page and highest page have not been written to
+      -- this could happen with buggy syscall wrappers.  Today
+      (2001-04-26) had precisely such a problem with __NR_setitimer. */
+   vg_assert(SK_(cheap_sanity_check)());
+   VGP_POPCC(VgpSetMem);
+}
+
+/* Set permissions for address ranges ... */
+
+void SK_(make_noaccess) ( Addr a, UInt len )
+{
+   PROF_EVENT(35);
+   DEBUG("SK_(make_noaccess)(%p, %x)\n", a, len);
+   set_address_range_perms ( a, len, VGM_BIT_INVALID, VGM_BIT_INVALID );
+}
+
+void SK_(make_writable) ( Addr a, UInt len )
+{
+   PROF_EVENT(36);
+   DEBUG("SK_(make_writable)(%p, %x)\n", a, len);
+   set_address_range_perms ( a, len, VGM_BIT_VALID, VGM_BIT_INVALID );
+}
+
+void SK_(make_readable) ( Addr a, UInt len )
+{
+   PROF_EVENT(37);
+   DEBUG("SK_(make_readable)(%p, 0x%x)\n", a, len);
+   set_address_range_perms ( a, len, VGM_BIT_VALID, VGM_BIT_VALID );
+}
+
+/* Block-copy permissions (needed for implementing realloc()). */
+
+static void copy_address_range_state ( Addr src, Addr dst, UInt len )
+{
+   UInt i;
+
+   DEBUG("copy_address_range_state\n");
+
+   PROF_EVENT(40);
+   for (i = 0; i < len; i++) {
+      UChar abit  = get_abit ( src+i );
+      UChar vbyte = get_vbyte ( src+i );
+      PROF_EVENT(41);
+      set_abit ( dst+i, abit );
+      set_vbyte ( dst+i, vbyte );
+   }
+}
+
+
+/* Check permissions for address range.  If inadequate permissions
+   exist, *bad_addr is set to the offending address, so the caller can
+   know what it is. */
+
+Bool SK_(check_writable) ( Addr a, UInt len, Addr* bad_addr )
+{
+   UInt  i;
+   UChar abit;
+   PROF_EVENT(42);
+   for (i = 0; i < len; i++) {
+      PROF_EVENT(43);
+      abit = get_abit(a);
+      if (abit == VGM_BIT_INVALID) {
+         if (bad_addr != NULL) *bad_addr = a;
+         return False;
+      }
+      a++;
+   }
+   return True;
+}
+
+Bool SK_(check_readable) ( Addr a, UInt len, Addr* bad_addr )
+{
+   UInt  i;
+   UChar abit;
+   UChar vbyte;
+
+   PROF_EVENT(44);
+   DEBUG("SK_(check_readable)\n");
+   for (i = 0; i < len; i++) {
+      abit  = get_abit(a);
+      vbyte = get_vbyte(a);
+      PROF_EVENT(45);
+      if (abit != VGM_BIT_VALID || vbyte != VGM_BYTE_VALID) {
+         if (bad_addr != NULL) *bad_addr = a;
+         return False;
+      }
+      a++;
+   }
+   return True;
+}
+
+
+/* Check a zero-terminated ascii string.  Tricky -- don't want to
+   examine the actual bytes, to find the end, until we're sure it is
+   safe to do so. */
+
+Bool SK_(check_readable_asciiz) ( Addr a, Addr* bad_addr )
+{
+   UChar abit;
+   UChar vbyte;
+   PROF_EVENT(46);
+   DEBUG("SK_(check_readable_asciiz)\n");
+   while (True) {
+      PROF_EVENT(47);
+      abit  = get_abit(a);
+      vbyte = get_vbyte(a);
+      if (abit != VGM_BIT_VALID || vbyte != VGM_BYTE_VALID) {
+         if (bad_addr != NULL) *bad_addr = a;
+         return False;
+      }
+      /* Ok, a is safe to read. */
+      if (* ((UChar*)a) == 0) return True;
+      a++;
+   }
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Memory event handlers                                ---*/
+/*------------------------------------------------------------*/
+
+/* Setting permissions for aligned words.  This supports fast stack
+   operations. */
+
+static void make_noaccess_aligned ( Addr a, UInt len )
+{
+   SecMap* sm;
+   UInt    sm_off;
+   UChar   mask;
+   Addr    a_past_end = a + len;
+
+   VGP_PUSHCC(VgpSetMem);
+
+   PROF_EVENT(50);
+#  ifdef VG_DEBUG_MEMORY
+   vg_assert(IS_ALIGNED4_ADDR(a));
+   vg_assert(IS_ALIGNED4_ADDR(len));
+#  endif
+
+   for ( ; a < a_past_end; a += 4) {
+      ENSURE_MAPPABLE(a, "make_noaccess_aligned");
+      sm     = primary_map[a >> 16];
+      sm_off = a & 0xFFFF;
+      ((UInt*)(sm->vbyte))[sm_off >> 2] = VGM_WORD_INVALID;
+      mask = 0x0F;
+      mask <<= (a & 4 /* 100b */);   /* a & 4 is either 0 or 4 */
+      /* mask now contains 1s where we wish to make address bits
+         invalid (1s). */
+      sm->abits[sm_off >> 3] |= mask;
+   }
+   VGP_POPCC(VgpSetMem);
+}
+
+static void make_writable_aligned ( Addr a, UInt len )
+{
+   SecMap* sm;
+   UInt    sm_off;
+   UChar   mask;
+   Addr    a_past_end = a + len;
+
+   VGP_PUSHCC(VgpSetMem);
+
+   PROF_EVENT(51);
+#  ifdef VG_DEBUG_MEMORY
+   vg_assert(IS_ALIGNED4_ADDR(a));
+   vg_assert(IS_ALIGNED4_ADDR(len));
+#  endif
+
+   for ( ; a < a_past_end; a += 4) {
+      ENSURE_MAPPABLE(a, "make_writable_aligned");
+      sm     = primary_map[a >> 16];
+      sm_off = a & 0xFFFF;
+      ((UInt*)(sm->vbyte))[sm_off >> 2] = VGM_WORD_INVALID;
+      mask = 0x0F;
+      mask <<= (a & 4 /* 100b */);   /* a & 4 is either 0 or 4 */
+      /* mask now contains 1s where we wish to make address bits
+         invalid (0s). */
+      sm->abits[sm_off >> 3] &= ~mask;
+   }
+   VGP_POPCC(VgpSetMem);
+}
+
+
+static
+void check_is_writable ( CorePart part, ThreadState* tst,
+                         Char* s, UInt base, UInt size )
+{
+   Bool ok;
+   Addr bad_addr;
+
+   VGP_PUSHCC(VgpCheckMem);
+
+   /* VG_(message)(Vg_DebugMsg,"check is writable: %x .. %x",
+                               base,base+size-1); */
+   ok = SK_(check_writable) ( base, size, &bad_addr );
+   if (!ok) {
+      switch (part) {
+      case Vg_CoreSysCall:
+         SK_(record_param_error) ( tst, bad_addr, /*isWrite =*/True, s );
+         break;
+
+      case Vg_CorePThread:
+      case Vg_CoreSignal:
+         SK_(record_core_mem_error)( tst, /*isWrite=*/True, s );
+         break;
+
+      default:
+         VG_(panic)("check_is_readable: Unknown or unexpected CorePart");
+      }
+   }
+
+   VGP_POPCC(VgpCheckMem);
+}
+
+static
+void check_is_readable ( CorePart part, ThreadState* tst,
+                         Char* s, UInt base, UInt size )
+{     
+   Bool ok;
+   Addr bad_addr;
+
+   VGP_PUSHCC(VgpCheckMem);
+   
+   /* VG_(message)(Vg_DebugMsg,"check is readable: %x .. %x",
+                               base,base+size-1); */
+   ok = SK_(check_readable) ( base, size, &bad_addr );
+   if (!ok) {
+      switch (part) {
+      case Vg_CoreSysCall:
+         SK_(record_param_error) ( tst, bad_addr, /*isWrite =*/False, s );
+         break;
+      
+      case Vg_CorePThread:
+         SK_(record_core_mem_error)( tst, /*isWrite=*/False, s );
+         break;
+
+      /* If we're being asked to jump to a silly address, record an error 
+         message before potentially crashing the entire system. */
+      case Vg_CoreTranslate:
+         SK_(record_jump_error)( tst, bad_addr );
+         break;
+
+      default:
+         VG_(panic)("check_is_readable: Unknown or unexpected CorePart");
+      }
+   }
+   VGP_POPCC(VgpCheckMem);
+}
+
+static
+void check_is_readable_asciiz ( CorePart part, ThreadState* tst,
+                                Char* s, UInt str )
+{
+   Bool ok = True;
+   Addr bad_addr;
+   /* VG_(message)(Vg_DebugMsg,"check is readable asciiz: 0x%x",str); */
+
+   VGP_PUSHCC(VgpCheckMem);
+
+   vg_assert(part == Vg_CoreSysCall);
+   ok = SK_(check_readable_asciiz) ( (Addr)str, &bad_addr );
+   if (!ok) {
+      SK_(record_param_error) ( tst, bad_addr, /*is_writable =*/False, s );
+   }
+
+   VGP_POPCC(VgpCheckMem);
+}
+
+
+static
+void memcheck_new_mem_startup( Addr a, UInt len, Bool rr, Bool ww, Bool xx )
+{
+   // JJJ: this ignores the permissions and just makes it readable, like the
+   // old code did, AFAICT
+   DEBUG("new_mem_startup(%p, %u, rr=%u, ww=%u, xx=%u)\n", a,len,rr,ww,xx);
+   SK_(make_readable)(a, len);
+}
+
+static
+void memcheck_new_mem_heap ( Addr a, UInt len, Bool is_inited )
+{
+   if (is_inited) {
+      SK_(make_readable)(a, len);
+   } else {
+      SK_(make_writable)(a, len);
+   }
+}
+
+static
+void memcheck_set_perms (Addr a, UInt len, 
+                         Bool nn, Bool rr, Bool ww, Bool xx)
+{
+   DEBUG("memcheck_set_perms(%p, %u, nn=%u, rr=%u ww=%u, xx=%u)\n",
+                             a, len, nn, rr, ww, xx);
+   if      (rr) SK_(make_readable)(a, len);
+   else if (ww) SK_(make_writable)(a, len);
+   else         SK_(make_noaccess)(a, len);
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Functions called directly from generated code.       ---*/
+/*------------------------------------------------------------*/
+
+static __inline__ UInt rotateRight16 ( UInt x )
+{
+   /* Amazingly, gcc turns this into a single rotate insn. */
+   return (x >> 16) | (x << 16);
+}
+
+
+static __inline__ UInt shiftRight16 ( UInt x )
+{
+   return x >> 16;
+}
+
+
+/* Read/write 1/2/4 sized V bytes, and emit an address error if
+   needed. */
+
+/* VG_(helperc_{LD,ST}V{1,2,4}) handle the common case fast.
+   Under all other circumstances, it defers to the relevant _SLOWLY
+   function, which can handle all situations.
+*/
+__attribute__ ((regparm(1)))
+UInt SK_(helperc_LOADV4) ( Addr a )
+{
+#  ifdef VG_DEBUG_MEMORY
+   return vgmext_rd_V4_SLOWLY(a);
+#  else
+   UInt    sec_no = rotateRight16(a) & 0x3FFFF;
+   SecMap* sm     = primary_map[sec_no];
+   UInt    a_off  = (a & 0xFFFF) >> 3;
+   UChar   abits  = sm->abits[a_off];
+   abits >>= (a & 4);
+   abits &= 15;
+   PROF_EVENT(60);
+   if (abits == VGM_NIBBLE_VALID) {
+      /* Handle common case quickly: a is suitably aligned, is mapped,
+         and is addressible. */
+      UInt v_off = a & 0xFFFF;
+      return ((UInt*)(sm->vbyte))[ v_off >> 2 ];
+   } else {
+      /* Slow but general case. */
+      return vgmext_rd_V4_SLOWLY(a);
+   }
+#  endif
+}
+
+__attribute__ ((regparm(2)))
+void SK_(helperc_STOREV4) ( Addr a, UInt vbytes )
+{
+#  ifdef VG_DEBUG_MEMORY
+   vgmext_wr_V4_SLOWLY(a, vbytes);
+#  else
+   UInt    sec_no = rotateRight16(a) & 0x3FFFF;
+   SecMap* sm     = primary_map[sec_no];
+   UInt    a_off  = (a & 0xFFFF) >> 3;
+   UChar   abits  = sm->abits[a_off];
+   abits >>= (a & 4);
+   abits &= 15;
+   PROF_EVENT(61);
+   if (abits == VGM_NIBBLE_VALID) {
+      /* Handle common case quickly: a is suitably aligned, is mapped,
+         and is addressible. */
+      UInt v_off = a & 0xFFFF;
+      ((UInt*)(sm->vbyte))[ v_off >> 2 ] = vbytes;
+   } else {
+      /* Slow but general case. */
+      vgmext_wr_V4_SLOWLY(a, vbytes);
+   }
+#  endif
+}
+
+__attribute__ ((regparm(1)))
+UInt SK_(helperc_LOADV2) ( Addr a )
+{
+#  ifdef VG_DEBUG_MEMORY
+   return vgmext_rd_V2_SLOWLY(a);
+#  else
+   UInt    sec_no = rotateRight16(a) & 0x1FFFF;
+   SecMap* sm     = primary_map[sec_no];
+   UInt    a_off  = (a & 0xFFFF) >> 3;
+   PROF_EVENT(62);
+   if (sm->abits[a_off] == VGM_BYTE_VALID) {
+      /* Handle common case quickly. */
+      UInt v_off = a & 0xFFFF;
+      return 0xFFFF0000 
+             |  
+             (UInt)( ((UShort*)(sm->vbyte))[ v_off >> 1 ] );
+   } else {
+      /* Slow but general case. */
+      return vgmext_rd_V2_SLOWLY(a);
+   }
+#  endif
+}
+
+__attribute__ ((regparm(2)))
+void SK_(helperc_STOREV2) ( Addr a, UInt vbytes )
+{
+#  ifdef VG_DEBUG_MEMORY
+   vgmext_wr_V2_SLOWLY(a, vbytes);
+#  else
+   UInt    sec_no = rotateRight16(a) & 0x1FFFF;
+   SecMap* sm     = primary_map[sec_no];
+   UInt    a_off  = (a & 0xFFFF) >> 3;
+   PROF_EVENT(63);
+   if (sm->abits[a_off] == VGM_BYTE_VALID) {
+      /* Handle common case quickly. */
+      UInt v_off = a & 0xFFFF;
+      ((UShort*)(sm->vbyte))[ v_off >> 1 ] = vbytes & 0x0000FFFF;
+   } else {
+      /* Slow but general case. */
+      vgmext_wr_V2_SLOWLY(a, vbytes);
+   }
+#  endif
+}
+
+__attribute__ ((regparm(1)))
+UInt SK_(helperc_LOADV1) ( Addr a )
+{
+#  ifdef VG_DEBUG_MEMORY
+   return vgmext_rd_V1_SLOWLY(a);
+#  else
+   UInt    sec_no = shiftRight16(a);
+   SecMap* sm     = primary_map[sec_no];
+   UInt    a_off  = (a & 0xFFFF) >> 3;
+   PROF_EVENT(64);
+   if (sm->abits[a_off] == VGM_BYTE_VALID) {
+      /* Handle common case quickly. */
+      UInt v_off = a & 0xFFFF;
+      return 0xFFFFFF00
+             |
+             (UInt)( ((UChar*)(sm->vbyte))[ v_off ] );
+   } else {
+      /* Slow but general case. */
+      return vgmext_rd_V1_SLOWLY(a);
+   }
+#  endif
+}
+
+__attribute__ ((regparm(2)))
+void SK_(helperc_STOREV1) ( Addr a, UInt vbytes )
+{
+#  ifdef VG_DEBUG_MEMORY
+   vgmext_wr_V1_SLOWLY(a, vbytes);
+#  else
+   UInt    sec_no = shiftRight16(a);
+   SecMap* sm     = primary_map[sec_no];
+   UInt    a_off  = (a & 0xFFFF) >> 3;
+   PROF_EVENT(65);
+   if (sm->abits[a_off] == VGM_BYTE_VALID) {
+      /* Handle common case quickly. */
+      UInt v_off = a & 0xFFFF;
+      ((UChar*)(sm->vbyte))[ v_off ] = vbytes & 0x000000FF;
+   } else {
+      /* Slow but general case. */
+      vgmext_wr_V1_SLOWLY(a, vbytes);
+   }
+#  endif
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Fallback functions to handle cases that the above    ---*/
+/*--- VG_(helperc_{LD,ST}V{1,2,4}) can't manage.           ---*/
+/*------------------------------------------------------------*/
+
+static UInt vgmext_rd_V4_SLOWLY ( Addr a )
+{
+   Bool a0ok, a1ok, a2ok, a3ok;
+   UInt vb0, vb1, vb2, vb3;
+
+   PROF_EVENT(70);
+
+   /* First establish independently the addressibility of the 4 bytes
+      involved. */
+   a0ok = get_abit(a+0) == VGM_BIT_VALID;
+   a1ok = get_abit(a+1) == VGM_BIT_VALID;
+   a2ok = get_abit(a+2) == VGM_BIT_VALID;
+   a3ok = get_abit(a+3) == VGM_BIT_VALID;
+
+   /* Also get the validity bytes for the address. */
+   vb0 = (UInt)get_vbyte(a+0);
+   vb1 = (UInt)get_vbyte(a+1);
+   vb2 = (UInt)get_vbyte(a+2);
+   vb3 = (UInt)get_vbyte(a+3);
+
+   /* Now distinguish 3 cases */
+
+   /* Case 1: the address is completely valid, so:
+      - no addressing error
+      - return V bytes as read from memory
+   */
+   if (a0ok && a1ok && a2ok && a3ok) {
+      UInt vw = VGM_WORD_INVALID;
+      vw <<= 8; vw |= vb3;
+      vw <<= 8; vw |= vb2;
+      vw <<= 8; vw |= vb1;
+      vw <<= 8; vw |= vb0;
+      return vw;
+   }
+
+   /* Case 2: the address is completely invalid.  
+      - emit addressing error
+      - return V word indicating validity.  
+      This sounds strange, but if we make loads from invalid addresses 
+      give invalid data, we also risk producing a number of confusing
+      undefined-value errors later, which confuses the fact that the
+      error arose in the first place from an invalid address. 
+   */
+   /* VG_(printf)("%p (%d %d %d %d)\n", a, a0ok, a1ok, a2ok, a3ok); */
+   if (!SK_(clo_partial_loads_ok) 
+       || ((a & 3) != 0)
+       || (!a0ok && !a1ok && !a2ok && !a3ok)) {
+      SK_(record_address_error)( a, 4, False );
+      return (VGM_BYTE_VALID << 24) | (VGM_BYTE_VALID << 16) 
+             | (VGM_BYTE_VALID << 8) | VGM_BYTE_VALID;
+   }
+
+   /* Case 3: the address is partially valid.  
+      - no addressing error
+      - returned V word is invalid where the address is invalid, 
+        and contains V bytes from memory otherwise. 
+      Case 3 is only allowed if SK_(clo_partial_loads_ok) is True
+      (which is the default), and the address is 4-aligned.  
+      If not, Case 2 will have applied.
+   */
+   vg_assert(SK_(clo_partial_loads_ok));
+   {
+      UInt vw = VGM_WORD_INVALID;
+      vw <<= 8; vw |= (a3ok ? vb3 : VGM_BYTE_INVALID);
+      vw <<= 8; vw |= (a2ok ? vb2 : VGM_BYTE_INVALID);
+      vw <<= 8; vw |= (a1ok ? vb1 : VGM_BYTE_INVALID);
+      vw <<= 8; vw |= (a0ok ? vb0 : VGM_BYTE_INVALID);
+      return vw;
+   }
+}
+
+static void vgmext_wr_V4_SLOWLY ( Addr a, UInt vbytes )
+{
+   /* Check the address for validity. */
+   Bool aerr = False;
+   PROF_EVENT(71);
+
+   if (get_abit(a+0) != VGM_BIT_VALID) aerr = True;
+   if (get_abit(a+1) != VGM_BIT_VALID) aerr = True;
+   if (get_abit(a+2) != VGM_BIT_VALID) aerr = True;
+   if (get_abit(a+3) != VGM_BIT_VALID) aerr = True;
+
+   /* Store the V bytes, remembering to do it little-endian-ly. */
+   set_vbyte( a+0, vbytes & 0x000000FF ); vbytes >>= 8;
+   set_vbyte( a+1, vbytes & 0x000000FF ); vbytes >>= 8;
+   set_vbyte( a+2, vbytes & 0x000000FF ); vbytes >>= 8;
+   set_vbyte( a+3, vbytes & 0x000000FF );
+
+   /* If an address error has happened, report it. */
+   if (aerr)
+      SK_(record_address_error)( a, 4, True );
+}
+
+static UInt vgmext_rd_V2_SLOWLY ( Addr a )
+{
+   /* Check the address for validity. */
+   UInt vw   = VGM_WORD_INVALID;
+   Bool aerr = False;
+   PROF_EVENT(72);
+
+   if (get_abit(a+0) != VGM_BIT_VALID) aerr = True;
+   if (get_abit(a+1) != VGM_BIT_VALID) aerr = True;
+
+   /* Fetch the V bytes, remembering to do it little-endian-ly. */
+   vw <<= 8; vw |= (UInt)get_vbyte(a+1);
+   vw <<= 8; vw |= (UInt)get_vbyte(a+0);
+
+   /* If an address error has happened, report it. */
+   if (aerr) {
+      SK_(record_address_error)( a, 2, False );
+      vw = (VGM_BYTE_INVALID << 24) | (VGM_BYTE_INVALID << 16) 
+           | (VGM_BYTE_VALID << 8) | (VGM_BYTE_VALID);
+   }
+   return vw;   
+}
+
+static void vgmext_wr_V2_SLOWLY ( Addr a, UInt vbytes )
+{
+   /* Check the address for validity. */
+   Bool aerr = False;
+   PROF_EVENT(73);
+
+   if (get_abit(a+0) != VGM_BIT_VALID) aerr = True;
+   if (get_abit(a+1) != VGM_BIT_VALID) aerr = True;
+
+   /* Store the V bytes, remembering to do it little-endian-ly. */
+   set_vbyte( a+0, vbytes & 0x000000FF ); vbytes >>= 8;
+   set_vbyte( a+1, vbytes & 0x000000FF );
+
+   /* If an address error has happened, report it. */
+   if (aerr)
+      SK_(record_address_error)( a, 2, True );
+}
+
+static UInt vgmext_rd_V1_SLOWLY ( Addr a )
+{
+   /* Check the address for validity. */
+   UInt vw   = VGM_WORD_INVALID;
+   Bool aerr = False;
+   PROF_EVENT(74);
+
+   if (get_abit(a+0) != VGM_BIT_VALID) aerr = True;
+
+   /* Fetch the V byte. */
+   vw <<= 8; vw |= (UInt)get_vbyte(a+0);
+
+   /* If an address error has happened, report it. */
+   if (aerr) {
+      SK_(record_address_error)( a, 1, False );
+      vw = (VGM_BYTE_INVALID << 24) | (VGM_BYTE_INVALID << 16) 
+           | (VGM_BYTE_INVALID << 8) | (VGM_BYTE_VALID);
+   }
+   return vw;   
+}
+
+static void vgmext_wr_V1_SLOWLY ( Addr a, UInt vbytes )
+{
+   /* Check the address for validity. */
+   Bool aerr = False;
+   PROF_EVENT(75);
+   if (get_abit(a+0) != VGM_BIT_VALID) aerr = True;
+
+   /* Store the V bytes, remembering to do it little-endian-ly. */
+   set_vbyte( a+0, vbytes & 0x000000FF );
+
+   /* If an address error has happened, report it. */
+   if (aerr)
+      SK_(record_address_error)( a, 1, True );
+}
+
+
+/* ---------------------------------------------------------------------
+   Called from generated code, or from the assembly helpers.
+   Handlers for value check failures.
+   ------------------------------------------------------------------ */
+
+void SK_(helperc_value_check0_fail) ( void )
+{
+   SK_(record_value_error) ( 0 );
+}
+
+void SK_(helperc_value_check1_fail) ( void )
+{
+   SK_(record_value_error) ( 1 );
+}
+
+void SK_(helperc_value_check2_fail) ( void )
+{
+   SK_(record_value_error) ( 2 );
+}
+
+void SK_(helperc_value_check4_fail) ( void )
+{
+   SK_(record_value_error) ( 4 );
+}
+
+
+/* ---------------------------------------------------------------------
+   FPU load and store checks, called from generated code.
+   ------------------------------------------------------------------ */
+
+__attribute__ ((regparm(2)))
+void SK_(fpu_read_check) ( Addr addr, Int size )
+{
+   /* Ensure the read area is both addressible and valid (ie,
+      readable).  If there's an address error, don't report a value
+      error too; but if there isn't an address error, check for a
+      value error. 
+
+      Try to be reasonably fast on the common case; wimp out and defer
+      to fpu_read_check_SLOWLY for everything else.  */
+
+   SecMap* sm;
+   UInt    sm_off, v_off, a_off;
+   Addr    addr4;
+
+   PROF_EVENT(80);
+
+#  ifdef VG_DEBUG_MEMORY
+   fpu_read_check_SLOWLY ( addr, size );
+#  else
+
+   if (size == 4) {
+      if (!IS_ALIGNED4_ADDR(addr)) goto slow4;
+      PROF_EVENT(81);
+      /* Properly aligned. */
+      sm     = primary_map[addr >> 16];
+      sm_off = addr & 0xFFFF;
+      a_off  = sm_off >> 3;
+      if (sm->abits[a_off] != VGM_BYTE_VALID) goto slow4;
+      /* Properly aligned and addressible. */
+      v_off = addr & 0xFFFF;
+      if (((UInt*)(sm->vbyte))[ v_off >> 2 ] != VGM_WORD_VALID) 
+         goto slow4;
+      /* Properly aligned, addressible and with valid data. */
+      return;
+     slow4:
+      fpu_read_check_SLOWLY ( addr, 4 );
+      return;
+   }
+
+   if (size == 8) {
+      if (!IS_ALIGNED4_ADDR(addr)) goto slow8;
+      PROF_EVENT(82);
+      /* Properly aligned.  Do it in two halves. */
+      addr4 = addr + 4;
+      /* First half. */
+      sm     = primary_map[addr >> 16];
+      sm_off = addr & 0xFFFF;
+      a_off  = sm_off >> 3;
+      if (sm->abits[a_off] != VGM_BYTE_VALID) goto slow8;
+      /* First half properly aligned and addressible. */
+      v_off = addr & 0xFFFF;
+      if (((UInt*)(sm->vbyte))[ v_off >> 2 ] != VGM_WORD_VALID) 
+         goto slow8;
+      /* Second half. */
+      sm     = primary_map[addr4 >> 16];
+      sm_off = addr4 & 0xFFFF;
+      a_off  = sm_off >> 3;
+      if (sm->abits[a_off] != VGM_BYTE_VALID) goto slow8;
+      /* Second half properly aligned and addressible. */
+      v_off = addr4 & 0xFFFF;
+      if (((UInt*)(sm->vbyte))[ v_off >> 2 ] != VGM_WORD_VALID) 
+         goto slow8;
+      /* Both halves properly aligned, addressible and with valid
+         data. */
+      return;
+     slow8:
+      fpu_read_check_SLOWLY ( addr, 8 );
+      return;
+   }
+
+   /* Can't be bothered to huff'n'puff to make these (allegedly) rare
+      cases go quickly.  */
+   if (size == 2) {
+      PROF_EVENT(83);
+      fpu_read_check_SLOWLY ( addr, 2 );
+      return;
+   }
+
+   if (size == 10) {
+      PROF_EVENT(84);
+      fpu_read_check_SLOWLY ( addr, 10 );
+      return;
+   }
+
+   if (size == 28 || size == 108) {
+      PROF_EVENT(84); /* XXX assign correct event number */
+      fpu_read_check_SLOWLY ( addr, 28 );
+      return;
+   }
+
+   VG_(printf)("size is %d\n", size);
+   VG_(panic)("vgmext_fpu_read_check: unhandled size");
+#  endif
+}
+
+
+__attribute__ ((regparm(2)))
+void SK_(fpu_write_check) ( Addr addr, Int size )
+{
+   /* Ensure the written area is addressible, and moan if otherwise.
+      If it is addressible, make it valid, otherwise invalid. 
+   */
+
+   SecMap* sm;
+   UInt    sm_off, v_off, a_off;
+   Addr    addr4;
+
+   PROF_EVENT(85);
+
+#  ifdef VG_DEBUG_MEMORY
+   fpu_write_check_SLOWLY ( addr, size );
+#  else
+
+   if (size == 4) {
+      if (!IS_ALIGNED4_ADDR(addr)) goto slow4;
+      PROF_EVENT(86);
+      /* Properly aligned. */
+      sm     = primary_map[addr >> 16];
+      sm_off = addr & 0xFFFF;
+      a_off  = sm_off >> 3;
+      if (sm->abits[a_off] != VGM_BYTE_VALID) goto slow4;
+      /* Properly aligned and addressible.  Make valid. */
+      v_off = addr & 0xFFFF;
+      ((UInt*)(sm->vbyte))[ v_off >> 2 ] = VGM_WORD_VALID;
+      return;
+     slow4:
+      fpu_write_check_SLOWLY ( addr, 4 );
+      return;
+   }
+
+   if (size == 8) {
+      if (!IS_ALIGNED4_ADDR(addr)) goto slow8;
+      PROF_EVENT(87);
+      /* Properly aligned.  Do it in two halves. */
+      addr4 = addr + 4;
+      /* First half. */
+      sm     = primary_map[addr >> 16];
+      sm_off = addr & 0xFFFF;
+      a_off  = sm_off >> 3;
+      if (sm->abits[a_off] != VGM_BYTE_VALID) goto slow8;
+      /* First half properly aligned and addressible.  Make valid. */
+      v_off = addr & 0xFFFF;
+      ((UInt*)(sm->vbyte))[ v_off >> 2 ] = VGM_WORD_VALID;
+      /* Second half. */
+      sm     = primary_map[addr4 >> 16];
+      sm_off = addr4 & 0xFFFF;
+      a_off  = sm_off >> 3;
+      if (sm->abits[a_off] != VGM_BYTE_VALID) goto slow8;
+      /* Second half properly aligned and addressible. */
+      v_off = addr4 & 0xFFFF;
+      ((UInt*)(sm->vbyte))[ v_off >> 2 ] = VGM_WORD_VALID;
+      /* Properly aligned, addressible and with valid data. */
+      return;
+     slow8:
+      fpu_write_check_SLOWLY ( addr, 8 );
+      return;
+   }
+
+   /* Can't be bothered to huff'n'puff to make these (allegedly) rare
+      cases go quickly.  */
+   if (size == 2) {
+      PROF_EVENT(88);
+      fpu_write_check_SLOWLY ( addr, 2 );
+      return;
+   }
+
+   if (size == 10) {
+      PROF_EVENT(89);
+      fpu_write_check_SLOWLY ( addr, 10 );
+      return;
+   }
+
+   if (size == 28 || size == 108) {
+      PROF_EVENT(89); /* XXX assign correct event number */
+      fpu_write_check_SLOWLY ( addr, 28 );
+      return;
+   }
+
+   VG_(printf)("size is %d\n", size);
+   VG_(panic)("vgmext_fpu_write_check: unhandled size");
+#  endif
+}
+
+
+/* ---------------------------------------------------------------------
+   Slow, general cases for FPU load and store checks.
+   ------------------------------------------------------------------ */
+
+/* Generic version.  Test for both addr and value errors, but if
+   there's an addr error, don't report a value error even if it
+   exists. */
+
+void fpu_read_check_SLOWLY ( Addr addr, Int size )
+{
+   Int  i;
+   Bool aerr = False;
+   Bool verr = False;
+   PROF_EVENT(90);
+   for (i = 0; i < size; i++) {
+      PROF_EVENT(91);
+      if (get_abit(addr+i) != VGM_BIT_VALID)
+         aerr = True;
+      if (get_vbyte(addr+i) != VGM_BYTE_VALID)
+         verr = True;
+   }
+
+   if (aerr) {
+      SK_(record_address_error)( addr, size, False );
+   } else {
+     if (verr)
+        SK_(record_value_error)( size );
+   }
+}
+
+
+/* Generic version.  Test for addr errors.  Valid addresses are
+   given valid values, and invalid addresses invalid values. */
+
+void fpu_write_check_SLOWLY ( Addr addr, Int size )
+{
+   Int  i;
+   Addr a_here;
+   Bool a_ok;
+   Bool aerr = False;
+   PROF_EVENT(92);
+   for (i = 0; i < size; i++) {
+      PROF_EVENT(93);
+      a_here = addr+i;
+      a_ok = get_abit(a_here) == VGM_BIT_VALID;
+      if (a_ok) {
+	set_vbyte(a_here, VGM_BYTE_VALID);
+      } else {
+	set_vbyte(a_here, VGM_BYTE_INVALID);
+        aerr = True;
+      }
+   }
+   if (aerr) {
+      SK_(record_address_error)( addr, size, True );
+   }
+}
+
+/*------------------------------------------------------------*/
+/*--- Shadow chunks info                                   ---*/
+/*------------------------------------------------------------*/
+
+static __inline__
+void set_where( ShadowChunk* sc, ExeContext* ec )
+{
+   sc->skin_extra[0] = (UInt)ec;
+}
+
+static __inline__
+ExeContext *get_where( ShadowChunk* sc )
+{
+   return (ExeContext*)sc->skin_extra[0];
+}
+
+void SK_(complete_shadow_chunk) ( ShadowChunk* sc, ThreadState* tst )
+{
+   set_where( sc, VG_(get_ExeContext) ( tst ) );
+}
+
+/*------------------------------------------------------------*/
+/*--- Postponing free()ing                                 ---*/
+/*------------------------------------------------------------*/
+
+/* Holds blocks after freeing. */
+static ShadowChunk* vg_freed_list_start   = NULL;
+static ShadowChunk* vg_freed_list_end     = NULL;
+static Int          vg_freed_list_volume  = 0;
+
+static __attribute__ ((unused))
+       Int count_freelist ( void )
+{
+   ShadowChunk* sc;
+   Int n = 0;
+   for (sc = vg_freed_list_start; sc != NULL; sc = sc->next)
+      n++;
+   return n;
+}
+
+static __attribute__ ((unused))
+       void freelist_sanity ( void )
+{
+   ShadowChunk* sc;
+   Int n = 0;
+   /* VG_(printf)("freelist sanity\n"); */
+   for (sc = vg_freed_list_start; sc != NULL; sc = sc->next)
+      n += sc->size;
+   vg_assert(n == vg_freed_list_volume);
+}
+
+/* Put a shadow chunk on the freed blocks queue, possibly freeing up
+   some of the oldest blocks in the queue at the same time. */
+static void add_to_freed_queue ( ShadowChunk* sc )
+{
+   ShadowChunk* sc1;
+
+   /* Put it at the end of the freed list */
+   if (vg_freed_list_end == NULL) {
+      vg_assert(vg_freed_list_start == NULL);
+      vg_freed_list_end = vg_freed_list_start = sc;
+      vg_freed_list_volume = sc->size;
+   } else {    
+      vg_assert(vg_freed_list_end->next == NULL);
+      vg_freed_list_end->next = sc;
+      vg_freed_list_end = sc;
+      vg_freed_list_volume += sc->size;
+   }
+   sc->next = NULL;
+
+   /* Release enough of the oldest blocks to bring the free queue
+      volume below vg_clo_freelist_vol. */
+   
+   while (vg_freed_list_volume > SK_(clo_freelist_vol)) {
+      /* freelist_sanity(); */
+      vg_assert(vg_freed_list_start != NULL);
+      vg_assert(vg_freed_list_end != NULL);
+
+      sc1 = vg_freed_list_start;
+      vg_freed_list_volume -= sc1->size;
+      /* VG_(printf)("volume now %d\n", vg_freed_list_volume); */
+      vg_assert(vg_freed_list_volume >= 0);
+
+      if (vg_freed_list_start == vg_freed_list_end) {
+         vg_freed_list_start = vg_freed_list_end = NULL;
+      } else {
+         vg_freed_list_start = sc1->next;
+      }
+      sc1->next = NULL; /* just paranoia */
+      VG_(freeShadowChunk) ( sc1 );
+   }
+}
+
+/* Return the first shadow chunk satisfying the predicate p. */
+ShadowChunk* SK_(any_matching_freed_ShadowChunks)
+                        ( Bool (*p) ( ShadowChunk* ))
+{
+   ShadowChunk* sc;
+
+   /* No point looking through freed blocks if we're not keeping
+      them around for a while... */
+   for (sc = vg_freed_list_start; sc != NULL; sc = sc->next)
+      if (p(sc))
+         return sc;
+
+   return NULL;
+}
+
+void SK_(alt_free) ( ShadowChunk* sc, ThreadState* tst )
+{
+   /* Record where freed */
+   set_where( sc, VG_(get_ExeContext) ( tst ) );
+
+   /* Put it out of harm's way for a while. */
+   add_to_freed_queue ( sc );
+}
+
+/*------------------------------------------------------------*/
+/*--- Low-level address-space scanning, for the leak       ---*/
+/*--- detector.                                            ---*/
+/*------------------------------------------------------------*/
+
+static 
+jmp_buf memscan_jmpbuf;
+
+static
+void vg_scan_all_valid_memory_sighandler ( Int sigNo )
+{
+   __builtin_longjmp(memscan_jmpbuf, 1);
+}
+
+/* Safely (avoiding SIGSEGV / SIGBUS) scan the entire valid address
+   space and pass the addresses and values of all addressible,
+   defined, aligned words to notify_word.  This is the basis for the
+   leak detector.  Returns the number of calls made to notify_word.  */
+UInt VG_(scan_all_valid_memory) ( void (*notify_word)( Addr, UInt ) )
+{
+   /* All volatile, because some gccs seem paranoid about longjmp(). */
+   volatile UInt res, numPages, page, vbytes, primaryMapNo, nWordsNotified;
+   volatile Addr pageBase, addr;
+   volatile SecMap* sm;
+   volatile UChar abits;
+   volatile UInt page_first_word;
+
+   vki_ksigaction sigbus_saved;
+   vki_ksigaction sigbus_new;
+   vki_ksigaction sigsegv_saved;
+   vki_ksigaction sigsegv_new;
+   vki_ksigset_t  blockmask_saved;
+   vki_ksigset_t  unblockmask_new;
+
+   /* Temporarily install a new sigsegv and sigbus handler, and make
+      sure SIGBUS, SIGSEGV and SIGTERM are unblocked.  (Perhaps the
+      first two can never be blocked anyway?)  */
+
+   sigbus_new.ksa_handler = vg_scan_all_valid_memory_sighandler;
+   sigbus_new.ksa_flags = VKI_SA_ONSTACK | VKI_SA_RESTART;
+   sigbus_new.ksa_restorer = NULL;
+   res = VG_(ksigemptyset)( &sigbus_new.ksa_mask );
+   vg_assert(res == 0);
+
+   sigsegv_new.ksa_handler = vg_scan_all_valid_memory_sighandler;
+   sigsegv_new.ksa_flags = VKI_SA_ONSTACK | VKI_SA_RESTART;
+   sigsegv_new.ksa_restorer = NULL;
+   res = VG_(ksigemptyset)( &sigsegv_new.ksa_mask );
+   vg_assert(res == 0+0);
+
+   res =  VG_(ksigemptyset)( &unblockmask_new );
+   res |= VG_(ksigaddset)( &unblockmask_new, VKI_SIGBUS );
+   res |= VG_(ksigaddset)( &unblockmask_new, VKI_SIGSEGV );
+   res |= VG_(ksigaddset)( &unblockmask_new, VKI_SIGTERM );
+   vg_assert(res == 0+0+0);
+
+   res = VG_(ksigaction)( VKI_SIGBUS, &sigbus_new, &sigbus_saved );
+   vg_assert(res == 0+0+0+0);
+
+   res = VG_(ksigaction)( VKI_SIGSEGV, &sigsegv_new, &sigsegv_saved );
+   vg_assert(res == 0+0+0+0+0);
+
+   res = VG_(ksigprocmask)( VKI_SIG_UNBLOCK, &unblockmask_new, &blockmask_saved );
+   vg_assert(res == 0+0+0+0+0+0);
+
+   /* The signal handlers are installed.  Actually do the memory scan. */
+   numPages = 1 << (32-VKI_BYTES_PER_PAGE_BITS);
+   vg_assert(numPages == 1048576);
+   vg_assert(4096 == (1 << VKI_BYTES_PER_PAGE_BITS));
+
+   nWordsNotified = 0;
+
+   for (page = 0; page < numPages; page++) {
+      pageBase = page << VKI_BYTES_PER_PAGE_BITS;
+      primaryMapNo = pageBase >> 16;
+      sm = primary_map[primaryMapNo];
+      if (IS_DISTINGUISHED_SM(sm)) continue;
+      if (__builtin_setjmp(memscan_jmpbuf) == 0) {
+         /* try this ... */
+         page_first_word = * (volatile UInt*)pageBase;
+         /* we get here if we didn't get a fault */
+         /* Scan the page */
+         for (addr = pageBase; addr < pageBase+VKI_BYTES_PER_PAGE; addr += 4) {
+            abits  = get_abits4_ALIGNED(addr);
+            vbytes = get_vbytes4_ALIGNED(addr);
+            if (abits == VGM_NIBBLE_VALID 
+                && vbytes == VGM_WORD_VALID) {
+               nWordsNotified++;
+               notify_word ( addr, *(UInt*)addr );
+	    }
+         }
+      } else {
+         /* We get here if reading the first word of the page caused a
+            fault, which in turn caused the signal handler to longjmp.
+            Ignore this page. */
+         if (0)
+         VG_(printf)(
+            "vg_scan_all_valid_memory_sighandler: ignoring page at %p\n",
+            (void*)pageBase 
+         );
+      }
+   }
+
+   /* Restore signal state to whatever it was before. */
+   res = VG_(ksigaction)( VKI_SIGBUS, &sigbus_saved, NULL );
+   vg_assert(res == 0 +0);
+
+   res = VG_(ksigaction)( VKI_SIGSEGV, &sigsegv_saved, NULL );
+   vg_assert(res == 0 +0 +0);
+
+   res = VG_(ksigprocmask)( VKI_SIG_SETMASK, &blockmask_saved, NULL );
+   vg_assert(res == 0 +0 +0 +0);
+
+   return nWordsNotified;
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Detecting leaked (unreachable) malloc'd blocks.      ---*/
+/*------------------------------------------------------------*/
+
+/* A block is either 
+   -- Proper-ly reached; a pointer to its start has been found
+   -- Interior-ly reached; only an interior pointer to it has been found
+   -- Unreached; so far, no pointers to any part of it have been found. 
+*/
+typedef 
+   enum { Unreached, Interior, Proper } 
+   Reachedness;
+
+/* A block record, used for generating err msgs. */
+typedef
+   struct _LossRecord {
+      struct _LossRecord* next;
+      /* Where these lost blocks were allocated. */
+      ExeContext*  allocated_at;
+      /* Their reachability. */
+      Reachedness  loss_mode;
+      /* Number of blocks and total # bytes involved. */
+      UInt         total_bytes;
+      UInt         num_blocks;
+   }
+   LossRecord;
+
+
+/* Find the i such that ptr points at or inside the block described by
+   shadows[i].  Return -1 if none found.  This assumes that shadows[]
+   has been sorted on the ->data field. */
+
+#ifdef VG_DEBUG_LEAKCHECK
+/* Used to sanity-check the fast binary-search mechanism. */
+static Int find_shadow_for_OLD ( Addr          ptr, 
+                                 ShadowChunk** shadows,
+                                 Int           n_shadows )
+
+{
+   Int  i;
+   Addr a_lo, a_hi;
+   PROF_EVENT(70);
+   for (i = 0; i < n_shadows; i++) {
+      PROF_EVENT(71);
+      a_lo = shadows[i]->data;
+      a_hi = ((Addr)shadows[i]->data) + shadows[i]->size - 1;
+      if (a_lo <= ptr && ptr <= a_hi)
+         return i;
+   }
+   return -1;
+}
+#endif
+
+
+static Int find_shadow_for ( Addr          ptr, 
+                             ShadowChunk** shadows,
+                             Int           n_shadows )
+{
+   Addr a_mid_lo, a_mid_hi;
+   Int lo, mid, hi, retVal;
+   PROF_EVENT(70);
+   /* VG_(printf)("find shadow for %p = ", ptr); */
+   retVal = -1;
+   lo = 0;
+   hi = n_shadows-1;
+   while (True) {
+      PROF_EVENT(71);
+
+      /* invariant: current unsearched space is from lo to hi,
+         inclusive. */
+      if (lo > hi) break; /* not found */
+
+      mid      = (lo + hi) / 2;
+      a_mid_lo = shadows[mid]->data;
+      a_mid_hi = ((Addr)shadows[mid]->data) + shadows[mid]->size - 1;
+
+      if (ptr < a_mid_lo) {
+         hi = mid-1;
+         continue;
+      } 
+      if (ptr > a_mid_hi) {
+         lo = mid+1;
+         continue;
+      }
+      vg_assert(ptr >= a_mid_lo && ptr <= a_mid_hi);
+      retVal = mid;
+      break;
+   }
+
+#  ifdef VG_DEBUG_LEAKCHECK
+   vg_assert(retVal == find_shadow_for_OLD ( ptr, shadows, n_shadows ));
+#  endif
+   /* VG_(printf)("%d\n", retVal); */
+   return retVal;
+}
+
+
+
+static void sort_malloc_shadows ( ShadowChunk** shadows, UInt n_shadows )
+{
+   Int   incs[14] = { 1, 4, 13, 40, 121, 364, 1093, 3280,
+                      9841, 29524, 88573, 265720,
+                      797161, 2391484 };
+   Int          lo = 0;
+   Int          hi = n_shadows-1;
+   Int          i, j, h, bigN, hp;
+   ShadowChunk* v;
+
+   PROF_EVENT(72);
+   bigN = hi - lo + 1; if (bigN < 2) return;
+   hp = 0; while (incs[hp] < bigN) hp++; hp--;
+
+   for (; hp >= 0; hp--) {
+      PROF_EVENT(73);
+      h = incs[hp];
+      i = lo + h;
+      while (1) {
+         PROF_EVENT(74);
+         if (i > hi) break;
+         v = shadows[i];
+         j = i;
+         while (shadows[j-h]->data > v->data) {
+            PROF_EVENT(75);
+            shadows[j] = shadows[j-h];
+            j = j - h;
+            if (j <= (lo + h - 1)) break;
+         }
+         shadows[j] = v;
+         i++;
+      }
+   }
+}
+
+/* Globals, for the callback used by SK_(detect_memory_leaks). */
+
+static ShadowChunk** vglc_shadows;
+static Int           vglc_n_shadows;
+static Reachedness*  vglc_reachedness;
+static Addr          vglc_min_mallocd_addr;
+static Addr          vglc_max_mallocd_addr;
+
+static 
+void vg_detect_memory_leaks_notify_addr ( Addr a, UInt word_at_a )
+{
+   Int  sh_no;
+   Addr ptr;
+
+   /* Rule out some known causes of bogus pointers.  Mostly these do
+      not cause much trouble because only a few false pointers can
+      ever lurk in these places.  This mainly stops it reporting that
+      blocks are still reachable in stupid test programs like this
+
+         int main (void) { char* a = malloc(100); return 0; }
+
+      which people seem inordinately fond of writing, for some reason.  
+
+      Note that this is a complete kludge.  It would be better to
+      ignore any addresses corresponding to valgrind.so's .bss and
+      .data segments, but I cannot think of a reliable way to identify
+      where the .bss segment has been put.  If you can, drop me a
+      line.  
+   */
+   if (VG_(within_stack)(a))                return;
+   if (VG_(within_m_state_static)(a))       return;
+   if (a == (Addr)(&vglc_min_mallocd_addr)) return;
+   if (a == (Addr)(&vglc_max_mallocd_addr)) return;
+
+   /* OK, let's get on and do something Useful for a change. */
+
+   ptr = (Addr)word_at_a;
+   if (ptr >= vglc_min_mallocd_addr && ptr <= vglc_max_mallocd_addr) {
+      /* Might be legitimate; we'll have to investigate further. */
+      sh_no = find_shadow_for ( ptr, vglc_shadows, vglc_n_shadows );
+      if (sh_no != -1) {
+         /* Found a block at/into which ptr points. */
+         vg_assert(sh_no >= 0 && sh_no < vglc_n_shadows);
+         vg_assert(ptr < vglc_shadows[sh_no]->data 
+                         + vglc_shadows[sh_no]->size);
+         /* Decide whether Proper-ly or Interior-ly reached. */
+         if (ptr == vglc_shadows[sh_no]->data) {
+            if (0) VG_(printf)("pointer at %p to %p\n", a, word_at_a );
+            vglc_reachedness[sh_no] = Proper;
+         } else {
+            if (vglc_reachedness[sh_no] == Unreached)
+               vglc_reachedness[sh_no] = Interior;
+         }
+      }
+   }
+}
+
+
+void SK_(detect_memory_leaks) ( void )
+{
+   Int    i;
+   Int    blocks_leaked, bytes_leaked;
+   Int    blocks_dubious, bytes_dubious;
+   Int    blocks_reachable, bytes_reachable;
+   Int    n_lossrecords;
+   UInt   bytes_notified;
+   
+   LossRecord*  errlist;
+   LossRecord*  p;
+
+   PROF_EVENT(76);
+
+   /* VG_(get_malloc_shadows) allocates storage for shadows */
+   vglc_shadows = VG_(get_malloc_shadows)( &vglc_n_shadows );
+   if (vglc_n_shadows == 0) {
+      vg_assert(vglc_shadows == NULL);
+      VG_(message)(Vg_UserMsg, 
+                   "No malloc'd blocks -- no leaks are possible.\n");
+      return;
+   }
+
+   VG_(message)(Vg_UserMsg, 
+                "searching for pointers to %d not-freed blocks.", 
+                vglc_n_shadows );
+   sort_malloc_shadows ( vglc_shadows, vglc_n_shadows );
+
+   /* Sanity check; assert that the blocks are now in order and that
+      they don't overlap. */
+   for (i = 0; i < vglc_n_shadows-1; i++) {
+      vg_assert( ((Addr)vglc_shadows[i]->data)
+                 < ((Addr)vglc_shadows[i+1]->data) );
+      vg_assert( ((Addr)vglc_shadows[i]->data) + vglc_shadows[i]->size
+                 < ((Addr)vglc_shadows[i+1]->data) );
+   }
+
+   vglc_min_mallocd_addr = ((Addr)vglc_shadows[0]->data);
+   vglc_max_mallocd_addr = ((Addr)vglc_shadows[vglc_n_shadows-1]->data)
+                         + vglc_shadows[vglc_n_shadows-1]->size - 1;
+
+   vglc_reachedness 
+      = VG_(malloc)( vglc_n_shadows * sizeof(Reachedness) );
+   for (i = 0; i < vglc_n_shadows; i++)
+      vglc_reachedness[i] = Unreached;
+
+   /* Do the scan of memory. */
+   bytes_notified
+       = VG_(scan_all_valid_memory)( &vg_detect_memory_leaks_notify_addr )
+         * VKI_BYTES_PER_WORD;
+
+   VG_(message)(Vg_UserMsg, "checked %d bytes.", bytes_notified);
+
+   blocks_leaked    = bytes_leaked    = 0;
+   blocks_dubious   = bytes_dubious   = 0;
+   blocks_reachable = bytes_reachable = 0;
+
+   for (i = 0; i < vglc_n_shadows; i++) {
+      if (vglc_reachedness[i] == Unreached) {
+         blocks_leaked++;
+         bytes_leaked += vglc_shadows[i]->size;
+      }
+      else if (vglc_reachedness[i] == Interior) {
+         blocks_dubious++;
+         bytes_dubious += vglc_shadows[i]->size;
+      }
+      else if (vglc_reachedness[i] == Proper) {
+         blocks_reachable++;
+         bytes_reachable += vglc_shadows[i]->size;
+      }
+   }
+
+   VG_(message)(Vg_UserMsg, "");
+   VG_(message)(Vg_UserMsg, "definitely lost: %d bytes in %d blocks.", 
+                            bytes_leaked, blocks_leaked );
+   VG_(message)(Vg_UserMsg, "possibly lost:   %d bytes in %d blocks.", 
+                            bytes_dubious, blocks_dubious );
+   VG_(message)(Vg_UserMsg, "still reachable: %d bytes in %d blocks.", 
+                            bytes_reachable, blocks_reachable );
+
+
+   /* Common up the lost blocks so we can print sensible error
+      messages. */
+
+   n_lossrecords = 0;
+   errlist       = NULL;
+   for (i = 0; i < vglc_n_shadows; i++) {
+     
+      /* 'where' stored in 'skin_extra' field */
+      ExeContext* where = get_where ( vglc_shadows[i] );
+
+      for (p = errlist; p != NULL; p = p->next) {
+         if (p->loss_mode == vglc_reachedness[i]
+             && VG_(eq_ExeContext) ( SK_(clo_leak_resolution),
+                                     p->allocated_at, 
+                                     where) ) {
+            break;
+	 }
+      }
+      if (p != NULL) {
+         p->num_blocks  ++;
+         p->total_bytes += vglc_shadows[i]->size;
+      } else {
+         n_lossrecords ++;
+         p = VG_(malloc)(sizeof(LossRecord));
+         p->loss_mode    = vglc_reachedness[i];
+         p->allocated_at = where;
+         p->total_bytes  = vglc_shadows[i]->size;
+         p->num_blocks   = 1;
+         p->next         = errlist;
+         errlist         = p;
+      }
+   }
+   
+   for (i = 0; i < n_lossrecords; i++) {
+      LossRecord* p_min = NULL;
+      UInt        n_min = 0xFFFFFFFF;
+      for (p = errlist; p != NULL; p = p->next) {
+         if (p->num_blocks > 0 && p->total_bytes < n_min) {
+            n_min = p->total_bytes;
+            p_min = p;
+         }
+      }
+      vg_assert(p_min != NULL);
+
+      if ( (!SK_(clo_show_reachable)) && p_min->loss_mode == Proper) {
+         p_min->num_blocks = 0;
+         continue;
+      }
+
+      VG_(message)(Vg_UserMsg, "");
+      VG_(message)(
+         Vg_UserMsg,
+         "%d bytes in %d blocks are %s in loss record %d of %d",
+         p_min->total_bytes, p_min->num_blocks,
+         p_min->loss_mode==Unreached ? "definitely lost" :
+            (p_min->loss_mode==Interior ? "possibly lost"
+                                        : "still reachable"),
+         i+1, n_lossrecords
+      );
+      VG_(pp_ExeContext)(p_min->allocated_at);
+      p_min->num_blocks = 0;
+   }
+
+   VG_(message)(Vg_UserMsg, "");
+   VG_(message)(Vg_UserMsg, "LEAK SUMMARY:");
+   VG_(message)(Vg_UserMsg, "   definitely lost: %d bytes in %d blocks.", 
+                            bytes_leaked, blocks_leaked );
+   VG_(message)(Vg_UserMsg, "   possibly lost:   %d bytes in %d blocks.", 
+                            bytes_dubious, blocks_dubious );
+   VG_(message)(Vg_UserMsg, "   still reachable: %d bytes in %d blocks.", 
+                            bytes_reachable, blocks_reachable );
+   if (!SK_(clo_show_reachable)) {
+      VG_(message)(Vg_UserMsg, 
+         "Reachable blocks (those to which a pointer was found) are not shown.");
+      VG_(message)(Vg_UserMsg, 
+         "To see them, rerun with: --show-reachable=yes");
+   }
+   VG_(message)(Vg_UserMsg, "");
+
+   VG_(free) ( vglc_shadows );
+   VG_(free) ( vglc_reachedness );
+}
+
+
+/* ---------------------------------------------------------------------
+   Sanity check machinery (permanently engaged).
+   ------------------------------------------------------------------ */
+
+/* Check that nobody has spuriously claimed that the first or last 16
+   pages (64 KB) of address space have become accessible.  Failure of
+   the following do not per se indicate an internal consistency
+   problem, but they are so likely to that we really want to know
+   about it if so. */
+
+Bool SK_(cheap_sanity_check) ( void )
+{
+   if (IS_DISTINGUISHED_SM(primary_map[0]) && 
+       IS_DISTINGUISHED_SM(primary_map[65535]))
+      return True;
+   else
+      return False;
+}
+
+Bool SK_(expensive_sanity_check) ( void )
+{
+   Int i;
+
+   /* Make sure nobody changed the distinguished secondary. */
+   for (i = 0; i < 8192; i++)
+      if (distinguished_secondary_map.abits[i] != VGM_BYTE_INVALID)
+         return False;
+
+   for (i = 0; i < 65536; i++)
+      if (distinguished_secondary_map.vbyte[i] != VGM_BYTE_INVALID)
+         return False;
+
+   /* Make sure that the upper 3/4 of the primary map hasn't
+      been messed with. */
+   for (i = 65536; i < 262144; i++)
+      if (primary_map[i] != & distinguished_secondary_map)
+         return False;
+
+   return True;
+}
+      
+/* ---------------------------------------------------------------------
+   Debugging machinery (turn on to debug).  Something of a mess.
+   ------------------------------------------------------------------ */
+
+#if 0
+/* Print the value tags on the 8 integer registers & flag reg. */
+
+static void uint_to_bits ( UInt x, Char* str )
+{
+   Int i;
+   Int w = 0;
+   /* str must point to a space of at least 36 bytes. */
+   for (i = 31; i >= 0; i--) {
+      str[w++] = (x & ( ((UInt)1) << i)) ? '1' : '0';
+      if (i == 24 || i == 16 || i == 8)
+         str[w++] = ' ';
+   }
+   str[w++] = 0;
+   vg_assert(w == 36);
+}
+
+/* Caution!  Not vthread-safe; looks in VG_(baseBlock), not the thread
+   state table. */
+
+static void vg_show_reg_tags ( void )
+{
+   Char buf1[36];
+   Char buf2[36];
+   UInt z_eax, z_ebx, z_ecx, z_edx, 
+        z_esi, z_edi, z_ebp, z_esp, z_eflags;
+
+   z_eax    = VG_(baseBlock)[VGOFF_(sh_eax)];
+   z_ebx    = VG_(baseBlock)[VGOFF_(sh_ebx)];
+   z_ecx    = VG_(baseBlock)[VGOFF_(sh_ecx)];
+   z_edx    = VG_(baseBlock)[VGOFF_(sh_edx)];
+   z_esi    = VG_(baseBlock)[VGOFF_(sh_esi)];
+   z_edi    = VG_(baseBlock)[VGOFF_(sh_edi)];
+   z_ebp    = VG_(baseBlock)[VGOFF_(sh_ebp)];
+   z_esp    = VG_(baseBlock)[VGOFF_(sh_esp)];
+   z_eflags = VG_(baseBlock)[VGOFF_(sh_eflags)];
+   
+   uint_to_bits(z_eflags, buf1);
+   VG_(message)(Vg_DebugMsg, "efl %\n", buf1);
+
+   uint_to_bits(z_eax, buf1);
+   uint_to_bits(z_ebx, buf2);
+   VG_(message)(Vg_DebugMsg, "eax %s   ebx %s\n", buf1, buf2);
+
+   uint_to_bits(z_ecx, buf1);
+   uint_to_bits(z_edx, buf2);
+   VG_(message)(Vg_DebugMsg, "ecx %s   edx %s\n", buf1, buf2);
+
+   uint_to_bits(z_esi, buf1);
+   uint_to_bits(z_edi, buf2);
+   VG_(message)(Vg_DebugMsg, "esi %s   edi %s\n", buf1, buf2);
+
+   uint_to_bits(z_ebp, buf1);
+   uint_to_bits(z_esp, buf2);
+   VG_(message)(Vg_DebugMsg, "ebp %s   esp %s\n", buf1, buf2);
+}
+
+
+/* For debugging only.  Scan the address space and touch all allegedly
+   addressible words.  Useful for establishing where Valgrind's idea of
+   addressibility has diverged from what the kernel believes. */
+
+static 
+void zzzmemscan_notify_word ( Addr a, UInt w )
+{
+}
+
+void zzzmemscan ( void )
+{
+   Int n_notifies
+      = VG_(scan_all_valid_memory)( zzzmemscan_notify_word );
+   VG_(printf)("zzzmemscan: n_bytes = %d\n", 4 * n_notifies );
+}
+#endif
+
+
+
+
+#if 0
+static Int zzz = 0;
+
+void show_bb ( Addr eip_next )
+{
+   VG_(printf)("[%4d] ", zzz);
+   vg_show_reg_tags( &VG_(m_shadow );
+   VG_(translate) ( eip_next, NULL, NULL, NULL );
+}
+#endif /* 0 */
+
+/*------------------------------------------------------------*/
+/*--- Syscall wrappers                                     ---*/
+/*------------------------------------------------------------*/
+
+void* SK_(pre_syscall)  ( ThreadId tid, UInt syscallno, Bool isBlocking )
+{
+   Int sane = SK_(cheap_sanity_check)();
+   return (void*)sane;
+}
+
+void  SK_(post_syscall) ( ThreadId tid, UInt syscallno,
+                           void* pre_result, Int res, Bool isBlocking )
+{
+   Int  sane_before_call = (Int)pre_result;
+   Bool sane_after_call  = SK_(cheap_sanity_check)();
+
+   if ((Int)sane_before_call && (!sane_after_call)) {
+      VG_(message)(Vg_DebugMsg, "post-syscall: ");
+      VG_(message)(Vg_DebugMsg,
+                   "probable sanity check failure for syscall number %d\n",
+                   syscallno );
+      VG_(panic)("aborting due to the above ... bye!");
+   }
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Setup                                                ---*/
+/*------------------------------------------------------------*/
+
+void SK_(written_shadow_regs_values)( UInt* gen_reg_value, UInt* eflags_value )
+{
+   *gen_reg_value = VGM_WORD_VALID;
+   *eflags_value  = VGM_EFLAGS_VALID;
+}
+
+Bool SK_(process_cmd_line_option)(Char* arg)
+{
+#  define STREQ(s1,s2)     (0==VG_(strcmp_ws)((s1),(s2)))
+#  define STREQN(nn,s1,s2) (0==VG_(strncmp_ws)((s1),(s2),(nn)))
+
+   if      (STREQ(arg, "--partial-loads-ok=yes"))
+      SK_(clo_partial_loads_ok) = True;
+   else if (STREQ(arg, "--partial-loads-ok=no"))
+      SK_(clo_partial_loads_ok) = False;
+
+   else if (STREQN(15, arg, "--freelist-vol=")) {
+      SK_(clo_freelist_vol) = (Int)VG_(atoll)(&arg[15]);
+      if (SK_(clo_freelist_vol) < 0) SK_(clo_freelist_vol) = 0;
+   }
+
+   else if (STREQ(arg, "--leak-check=yes"))
+      SK_(clo_leak_check) = True;
+   else if (STREQ(arg, "--leak-check=no"))
+      SK_(clo_leak_check) = False;
+
+   else if (STREQ(arg, "--leak-resolution=low"))
+      SK_(clo_leak_resolution) = Vg_LowRes;
+   else if (STREQ(arg, "--leak-resolution=med"))
+      SK_(clo_leak_resolution) = Vg_MedRes;
+   else if (STREQ(arg, "--leak-resolution=high"))
+      SK_(clo_leak_resolution) = Vg_HighRes;
+   
+   else if (STREQ(arg, "--show-reachable=yes"))
+      SK_(clo_show_reachable) = True;
+   else if (STREQ(arg, "--show-reachable=no"))
+      SK_(clo_show_reachable) = False;
+
+   else if (STREQ(arg, "--workaround-gcc296-bugs=yes"))
+      SK_(clo_workaround_gcc296_bugs) = True;
+   else if (STREQ(arg, "--workaround-gcc296-bugs=no"))
+      SK_(clo_workaround_gcc296_bugs) = False;
+
+   else if (STREQ(arg, "--check-addrVs=yes"))
+      SK_(clo_check_addrVs) = True;
+   else if (STREQ(arg, "--check-addrVs=no"))
+      SK_(clo_check_addrVs) = False;
+
+   else if (STREQ(arg, "--cleanup=yes"))
+      SK_(clo_cleanup) = True;
+   else if (STREQ(arg, "--cleanup=no"))
+      SK_(clo_cleanup) = False;
+
+   else
+      return False;
+
+   return True;
+
+#undef STREQ
+#undef STREQN
+}
+
+Char* SK_(usage)(void)
+{  
+   return  
+"    --partial-loads-ok=no|yes too hard to explain here; see manual [yes]\n"
+"    --freelist-vol=<number>   volume of freed blocks queue [1000000]\n"
+"    --leak-check=no|yes       search for memory leaks at exit? [no]\n"
+"    --leak-resolution=low|med|high\n"
+"                              amount of bt merging in leak check [low]\n"
+"    --show-reachable=no|yes   show reachable blocks in leak check? [no]\n"
+"    --workaround-gcc296-bugs=no|yes  self explanatory [no]\n"
+"    --check-addrVs=no|yes     experimental lighterweight checking? [yes]\n"
+"                              yes == Valgrind's original behaviour\n"
+"\n"
+"    --cleanup=no|yes          improve after instrumentation? [yes]\n";
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Setup                                                ---*/
+/*------------------------------------------------------------*/
+
+void SK_(pre_clo_init)(VgNeeds* needs, VgTrackEvents* track)
+{
+   needs->name                    = "valgrind";
+   needs->description             = "a memory error detector";
+
+   needs->core_errors             = True;
+   needs->skin_errors             = True;
+   needs->run_libc_freeres        = True;
+
+   needs->sizeof_shadow_block     = 1;
+
+   needs->basic_block_discards    = False;
+   needs->shadow_regs             = True;
+   needs->command_line_options    = True;
+   needs->client_requests         = True;
+   needs->extended_UCode          = True;
+   needs->syscall_wrapper         = True;
+   needs->alternative_free        = True;
+   needs->sanity_checks           = True;
+
+   VG_(register_compact_helper)((Addr) & SK_(helper_value_check4_fail));
+   VG_(register_compact_helper)((Addr) & SK_(helper_value_check0_fail));
+   VG_(register_compact_helper)((Addr) & SK_(helperc_STOREV4));
+   VG_(register_compact_helper)((Addr) & SK_(helperc_STOREV1));
+   VG_(register_compact_helper)((Addr) & SK_(helperc_LOADV4));
+   VG_(register_compact_helper)((Addr) & SK_(helperc_LOADV1));
+
+   /* These two made non-compact because 2-byte transactions are rare. */
+   VG_(register_noncompact_helper)((Addr) & SK_(helperc_STOREV2));
+   VG_(register_noncompact_helper)((Addr) & SK_(helperc_LOADV2));
+   VG_(register_noncompact_helper)((Addr) & SK_(fpu_write_check));
+   VG_(register_noncompact_helper)((Addr) & SK_(fpu_read_check));
+   VG_(register_noncompact_helper)((Addr) & SK_(helper_value_check2_fail));
+   VG_(register_noncompact_helper)((Addr) & SK_(helper_value_check1_fail));
+
+   /* Events to track */
+   track->new_mem_startup       = & memcheck_new_mem_startup;
+   track->new_mem_heap          = & memcheck_new_mem_heap;
+   track->new_mem_stack         = & SK_(make_writable);
+   track->new_mem_stack_aligned = & make_writable_aligned;
+   track->new_mem_stack_signal  = & SK_(make_writable);
+   track->new_mem_brk           = & SK_(make_writable);
+   track->new_mem_mmap          = & memcheck_set_perms;
+   
+   track->copy_mem_heap         = & copy_address_range_state;
+   track->copy_mem_remap        = & copy_address_range_state;
+   track->change_mem_mprotect   = & memcheck_set_perms;
+      
+   track->ban_mem_heap          = & SK_(make_noaccess);
+   track->ban_mem_stack         = & SK_(make_noaccess);
+
+   track->die_mem_heap          = & SK_(make_noaccess);
+   track->die_mem_stack         = & SK_(make_noaccess);
+   track->die_mem_stack_aligned = & make_noaccess_aligned; 
+   track->die_mem_stack_signal  = & SK_(make_noaccess); 
+   track->die_mem_brk           = & SK_(make_noaccess);
+   track->die_mem_munmap        = & SK_(make_noaccess); 
+
+   track->bad_free              = & SK_(record_free_error);
+   track->mismatched_free       = & SK_(record_freemismatch_error);
+
+   track->pre_mem_read          = & check_is_readable;
+   track->pre_mem_read_asciiz   = & check_is_readable_asciiz;
+   track->pre_mem_write         = & check_is_writable;
+   track->post_mem_write        = & SK_(make_readable);
+
+   init_shadow_memory();
+
+   init_prof_mem();
+
+   VGP_(register_profile_event) ( VgpSetMem,   "set-mem-perms" );
+   VGP_(register_profile_event) ( VgpCheckMem, "check-mem-perms" );
+}
+
+/*--------------------------------------------------------------------*/
+/*--- end                                            vg_memcheck.c ---*/
+/*--------------------------------------------------------------------*/
diff --git a/memcheck/mc_translate.c b/memcheck/mc_translate.c
new file mode 100644
index 0000000..34f9643
--- /dev/null
+++ b/memcheck/mc_translate.c
@@ -0,0 +1,1470 @@
+/*--------------------------------------------------------------------*/
+/*--- Part of the MemCheck skin: instrument UCode to perform       ---*/
+/*--- memory checking operations.                                  ---*/
+/*---                                      vg_memcheck_translate.c ---*/
+/*--------------------------------------------------------------------*/
+/*
+   This file is part of Valgrind, an x86 protected-mode emulator 
+   designed for debugging and profiling binaries on x86-Unixes.
+
+   Copyright (C) 2000-2002 Julian Seward 
+      jseward@acm.org
+
+   This program 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 2 of the
+   License, or (at your option) any later version.
+
+   This program 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 this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+#include "vg_memcheck_include.h"
+
+/* ---------------------------------------------------------------------
+   Template functions for extending UCode
+   ------------------------------------------------------------------ */
+
+/* Compare this with the restrictions on core instructions in
+   vg_translate.c:VG_(saneUInstr)().  Everything general said there applies
+   here too.
+*/
+Bool SK_(saneExtUInstr)(Bool beforeRA, Bool beforeLiveness, UInstr* u)
+{
+// SSS: duplicating these macros really sucks
+#  define LIT0 (u->lit32 == 0)
+#  define LIT1 (!(LIT0))
+#  define LITm (u->tag1 == Literal ? True : LIT0 )
+#  define SZ0 (u->size == 0)
+#  define SZi (u->size == 4 || u->size == 2 || u->size == 1)
+#  define SZj (u->size == 4 || u->size == 2 || u->size == 1 || u->size == 0)
+#  define CC0 (u->flags_r == FlagsEmpty && u->flags_w == FlagsEmpty)
+#  define TR1 (beforeRA ? (u->tag1 == TempReg) : (u->tag1 == RealReg))
+#  define TR2 (beforeRA ? (u->tag2 == TempReg) : (u->tag2 == RealReg))
+#  define A1  (u->tag1 == ArchReg)
+#  define A2  (u->tag2 == ArchReg)
+#  define L1  (u->tag1 == Literal && u->val1 == 0)
+#  define Ls1 (u->tag1 == Lit16)
+#  define Ls3 (u->tag3 == Lit16)
+#  define TRL1 (TR1 || L1)
+#  define TRA1 (TR1 || A1)
+#  define N2  (u->tag2 == NoValue)
+#  define N3  (u->tag3 == NoValue)
+#  define COND0    (u->cond         == 0)
+#  define EXTRA4b0 (u->extra4b      == 0)
+#  define SG_WD0   (u->signed_widen == 0)
+#  define JMPKIND0 (u->jmpkind      == 0)
+#  define CCALL0   (u->argc==0 && u->regparms_n==0 && u->has_ret_val==0 && \
+                    ( beforeLiveness                                       \
+                    ? u->regs_live_after == ALL_RREGS_LIVE                 \
+                    : True ))
+#  define XOTHER   (COND0 && EXTRA4b0 && SG_WD0 && JMPKIND0 && CCALL0)
+
+   Int n_lits = 0;
+   if (u->tag1 == Literal) n_lits++;
+   if (u->tag2 == Literal) n_lits++;
+   if (u->tag3 == Literal) n_lits++;
+   if (n_lits > 1) 
+      return False;
+
+   /* Fields not checked: val1, val2, val3 */
+
+   switch (u->opcode) {
+
+   /* Fields checked: lit32   size flags_r/w tag1   tag2   tag3    (rest) */
+   case LOADV:  return LIT0 && SZi && CC0 &&  TR1 && TR2 &&  N3 && XOTHER;
+   case STOREV: return LITm && SZi && CC0 && TRL1 && TR2 &&  N3 && XOTHER;
+   case GETV:   return LIT0 && SZi && CC0 &&   A1 && TR2 &&  N3 && XOTHER;
+   case PUTV:   return LITm && SZi && CC0 && TRL1 &&  A2 &&  N3 && XOTHER;
+   case GETVF: 
+   case PUTVF:  return LIT0 && SZ0 && CC0 &&  TR1 &&  N2 &&  N3 && XOTHER;
+   case TESTV: 
+   case SETV:   return LIT0 && SZj && CC0 && TRA1 &&  N2 &&  N3 && XOTHER;
+   case TAG1:   return LIT0 && SZ0 && CC0 &&  TR1 &&  N2 && Ls3 && XOTHER;
+   case TAG2:   return LIT0 && SZ0 && CC0 &&  TR1 && TR2 && Ls3 && XOTHER;
+   default:
+      VG_(printf)("unhandled opcode: %u\n", u->opcode);
+      VG_(panic)("SK_(saneExtUInstr): unhandled opcode");
+   }
+#  undef LIT0
+#  undef LIT1
+#  undef LITm
+#  undef SZ0
+#  undef SZi
+#  undef SZj
+#  undef CC0
+#  undef TR1
+#  undef TR2
+#  undef A1
+#  undef A2
+#  undef L1
+#  undef Ls1
+#  undef Ls3
+#  undef TRL1
+#  undef TRA1
+#  undef N2
+#  undef N3
+#  undef COND0
+#  undef EXTRA4b0
+#  undef JMPKIND0
+#  undef CCALL0
+#  undef XOTHER
+}
+
+static Char* nameOfTagOp ( TagOp h )
+{
+   switch (h) {
+      case Tag_PCast40:        return "PCast40";
+      case Tag_PCast20:        return "PCast20";
+      case Tag_PCast10:        return "PCast10";
+      case Tag_PCast01:        return "PCast01";
+      case Tag_PCast02:        return "PCast02";
+      case Tag_PCast04:        return "PCast04";
+      case Tag_PCast14:        return "PCast14";
+      case Tag_PCast12:        return "PCast12";
+      case Tag_PCast11:        return "PCast11";
+      case Tag_Left4:          return "Left4";
+      case Tag_Left2:          return "Left2";
+      case Tag_Left1:          return "Left1";
+      case Tag_SWiden14:       return "SWiden14";
+      case Tag_SWiden24:       return "SWiden24";
+      case Tag_SWiden12:       return "SWiden12";
+      case Tag_ZWiden14:       return "ZWiden14";
+      case Tag_ZWiden24:       return "ZWiden24";
+      case Tag_ZWiden12:       return "ZWiden12";
+      case Tag_UifU4:          return "UifU4";
+      case Tag_UifU2:          return "UifU2";
+      case Tag_UifU1:          return "UifU1";
+      case Tag_UifU0:          return "UifU0";
+      case Tag_DifD4:          return "DifD4";
+      case Tag_DifD2:          return "DifD2";
+      case Tag_DifD1:          return "DifD1";
+      case Tag_ImproveAND4_TQ: return "ImproveAND4_TQ";
+      case Tag_ImproveAND2_TQ: return "ImproveAND2_TQ";
+      case Tag_ImproveAND1_TQ: return "ImproveAND1_TQ";
+      case Tag_ImproveOR4_TQ:  return "ImproveOR4_TQ";
+      case Tag_ImproveOR2_TQ:  return "ImproveOR2_TQ";
+      case Tag_ImproveOR1_TQ:  return "ImproveOR1_TQ";
+      case Tag_DebugFn:        return "DebugFn";
+      default: VG_(panic)("vg_nameOfTagOp");
+   }
+}
+
+
+Char* SK_(nameExtUOpcode)(Opcode opc)
+{
+   switch (opc) {
+      case GETVF:   return "GETVF";
+      case PUTVF:   return "PUTVF";
+      case TAG1:    return "TAG1";
+      case TAG2:    return "TAG2";
+      case LOADV:   return "LOADV";
+      case STOREV:  return "STOREV";
+      case GETV:    return "GETV";
+      case PUTV:    return "PUTV";
+      case TESTV:   return "TESTV";
+      case SETV:    return "SETV";
+      default:      
+         VG_(printf)("unhandled opcode: %u\n", opc);
+         VG_(panic)("SK_(nameExtUOpcode): unhandled case");
+   }
+}
+
+/* ---------------------------------------------------------------------
+   Debugging stuff.
+   ------------------------------------------------------------------ */
+
+void SK_(ppExtUInstr)(UInstr* u)
+{
+   switch (u->opcode) {
+
+      case TAG1:
+         VG_(printf)("\t");
+         VG_(ppUOperand)(u, 1, 4, False);
+         VG_(printf)(" = %s ( ", nameOfTagOp( u->val3 ));
+         VG_(ppUOperand)(u, 1, 4, False);
+         VG_(printf)(" )");
+         break;
+
+      case TAG2:
+         VG_(printf)("\t");
+         VG_(ppUOperand)(u, 2, 4, False);
+         VG_(printf)(" = %s ( ", nameOfTagOp( u->val3 ));
+         VG_(ppUOperand)(u, 1, 4, False);
+         VG_(printf)(", ");
+         VG_(ppUOperand)(u, 2, 4, False);
+         VG_(printf)(" )");
+         break;
+
+      case STOREV: case LOADV:
+         VG_(printf)("\t");
+         VG_(ppUOperand)(u, 1, u->size, u->opcode==LOADV);
+         VG_(printf)(", ");
+         VG_(ppUOperand)(u, 2, u->size, u->opcode==STOREV);
+         break;
+
+      case PUTVF: case GETVF:
+         VG_(printf)("\t");
+         VG_(ppUOperand)(u, 1, 0, False);
+         break;
+
+      case GETV: case PUTV:
+         VG_(printf)("\t");
+         VG_(ppUOperand)(u, 1, u->opcode==PUTV ? 4 : u->size, False);
+         VG_(printf)(", ");
+         VG_(ppUOperand)(u, 2, u->opcode==GETV ? 4 : u->size, False);
+         break;
+
+      case TESTV: case SETV:
+         VG_(printf)("\t");
+         VG_(ppUOperand)(u, 1, u->size, False);
+         break;
+
+      default:
+         VG_(printf)("unhandled opcode: %u\n", u->opcode);
+         VG_(panic)("SK_(ppExtUInstr): unhandled opcode");
+   }
+
+}
+
+Int SK_(getExtRegUsage)(UInstr* u, Tag tag, RegUse* arr)
+{
+#  define RD(ono)    VG_UINSTR_READS_REG(ono)
+#  define WR(ono)    VG_UINSTR_WRITES_REG(ono)
+
+   Int n = 0;
+   switch (u->opcode) {        
+
+      // JJJ: I don't understand this comment... what about reg alloc?  --njn
+
+      /* These sizes are only ever consulted when the instrumentation
+         code is being added, so the following can return
+         manifestly-bogus sizes. */
+
+      case TAG1:    RD(1); WR(1);        break;
+      case TAG2:    RD(1); RD(2); WR(2); break;
+      case LOADV:   RD(1); WR(2);        break;
+      case STOREV:  RD(1); RD(2);        break;
+      case GETV:    WR(2);               break;
+      case PUTV:    RD(1);               break;
+      case TESTV:   RD(1);               break;
+      case SETV:    WR(1);               break;
+      case PUTVF:   RD(1);               break;
+      case GETVF:   WR(1);               break;
+
+      default: 
+         VG_(printf)("unhandled opcode: %u\n", u->opcode);
+         VG_(panic)("SK_(getExtRegUsage): unhandled opcode");
+   }
+   return n;
+
+#  undef RD
+#  undef WR
+}
+
+/*------------------------------------------------------------*/
+/*--- New instrumentation machinery.                       ---*/
+/*------------------------------------------------------------*/
+
+#define uInstr1   VG_(newUInstr1)
+#define uInstr2   VG_(newUInstr2)
+#define uInstr3   VG_(newUInstr3)
+#define uLiteral  VG_(setLiteralField)
+#define uCCall    VG_(setCCallFields)
+#define newTemp   VG_(getNewTemp)
+#define newShadow VG_(getNewShadow)
+
+static
+TagOp get_Tag_ImproveOR_TQ ( Int sz )
+{
+   switch (sz) {
+      case 4: return Tag_ImproveOR4_TQ;
+      case 2: return Tag_ImproveOR2_TQ;
+      case 1: return Tag_ImproveOR1_TQ;
+      default: VG_(panic)("get_Tag_ImproveOR_TQ");
+   }
+}
+
+
+static
+TagOp get_Tag_ImproveAND_TQ ( Int sz )
+{
+   switch (sz) {
+      case 4: return Tag_ImproveAND4_TQ;
+      case 2: return Tag_ImproveAND2_TQ;
+      case 1: return Tag_ImproveAND1_TQ;
+      default: VG_(panic)("get_Tag_ImproveAND_TQ");
+   }
+}
+
+
+static
+TagOp get_Tag_Left ( Int sz )
+{
+   switch (sz) {
+      case 4: return Tag_Left4;
+      case 2: return Tag_Left2;
+      case 1: return Tag_Left1;
+      default: VG_(panic)("get_Tag_Left");
+   }
+}
+
+
+static
+TagOp get_Tag_UifU ( Int sz )
+{
+   switch (sz) {
+      case 4: return Tag_UifU4;
+      case 2: return Tag_UifU2;
+      case 1: return Tag_UifU1;
+      case 0: return Tag_UifU0;
+      default: VG_(panic)("get_Tag_UifU");
+   }
+}
+
+
+static
+TagOp get_Tag_DifD ( Int sz )
+{
+   switch (sz) {
+      case 4: return Tag_DifD4;
+      case 2: return Tag_DifD2;
+      case 1: return Tag_DifD1;
+      default: VG_(panic)("get_Tag_DifD");
+   }
+}
+
+
+static 
+TagOp get_Tag_PCast ( Int szs, Int szd )
+{
+   if (szs == 4 && szd == 0) return Tag_PCast40;
+   if (szs == 2 && szd == 0) return Tag_PCast20;
+   if (szs == 1 && szd == 0) return Tag_PCast10;
+   if (szs == 0 && szd == 1) return Tag_PCast01;
+   if (szs == 0 && szd == 2) return Tag_PCast02;
+   if (szs == 0 && szd == 4) return Tag_PCast04;
+   if (szs == 1 && szd == 4) return Tag_PCast14;
+   if (szs == 1 && szd == 2) return Tag_PCast12;
+   if (szs == 1 && szd == 1) return Tag_PCast11;
+   VG_(printf)("get_Tag_PCast(%d,%d)\n", szs, szd);
+   VG_(panic)("get_Tag_PCast");
+}
+
+
+static 
+TagOp get_Tag_Widen ( Bool syned, Int szs, Int szd )
+{
+   if (szs == 1 && szd == 2 && syned)  return Tag_SWiden12;
+   if (szs == 1 && szd == 2 && !syned) return Tag_ZWiden12;
+
+   if (szs == 1 && szd == 4 && syned)  return Tag_SWiden14;
+   if (szs == 1 && szd == 4 && !syned) return Tag_ZWiden14;
+
+   if (szs == 2 && szd == 4 && syned)  return Tag_SWiden24;
+   if (szs == 2 && szd == 4 && !syned) return Tag_ZWiden24;
+
+   VG_(printf)("get_Tag_Widen(%d,%d,%d)\n", (Int)syned, szs, szd);
+   VG_(panic)("get_Tag_Widen");
+}
+
+/* Pessimally cast the spec'd shadow from one size to another. */
+static 
+void create_PCast ( UCodeBlock* cb, Int szs, Int szd, Int tempreg )
+{
+   if (szs == 0 && szd == 0)
+      return;
+   uInstr3(cb, TAG1, 0, TempReg, tempreg, 
+                        NoValue, 0, 
+                        Lit16,   get_Tag_PCast(szs,szd));
+}
+
+
+/* Create a signed or unsigned widen of the spec'd shadow from one
+   size to another.  The only allowed size transitions are 1->2, 1->4
+   and 2->4. */
+static 
+void create_Widen ( UCodeBlock* cb, Bool signed_widen,
+                    Int szs, Int szd, Int tempreg )
+{
+   if (szs == szd) return;
+   uInstr3(cb, TAG1, 0, TempReg, tempreg, 
+                        NoValue, 0, 
+                        Lit16,   get_Tag_Widen(signed_widen,szs,szd));
+}
+
+
+/* Get the condition codes into a new shadow, at the given size. */
+static
+Int create_GETVF ( UCodeBlock* cb, Int sz )
+{
+   Int tt = newShadow(cb);
+   uInstr1(cb, GETVF, 0, TempReg, tt);
+   create_PCast(cb, 0, sz, tt);
+   return tt;
+}
+
+
+/* Save the condition codes from the spec'd shadow. */
+static
+void create_PUTVF ( UCodeBlock* cb, Int sz, Int tempreg )
+{
+   if (sz == 0) {
+      uInstr1(cb, PUTVF, 0, TempReg, tempreg);
+   } else { 
+      Int tt = newShadow(cb);
+      uInstr2(cb, MOV, 4, TempReg, tempreg, TempReg, tt);
+      create_PCast(cb, sz, 0, tt);
+      uInstr1(cb, PUTVF, 0, TempReg, tt);
+   }
+}
+
+
+/* Do Left on the spec'd shadow. */
+static 
+void create_Left ( UCodeBlock* cb, Int sz, Int tempreg )
+{
+   uInstr3(cb, TAG1, 0, 
+               TempReg, tempreg,
+               NoValue, 0, 
+               Lit16, get_Tag_Left(sz));
+}
+
+
+/* Do UifU on ts and td, putting the result in td. */
+static 
+void create_UifU ( UCodeBlock* cb, Int sz, Int ts, Int td )
+{
+   uInstr3(cb, TAG2, 0, TempReg, ts, TempReg, td,
+               Lit16, get_Tag_UifU(sz));
+}
+
+
+/* Do DifD on ts and td, putting the result in td. */
+static 
+void create_DifD ( UCodeBlock* cb, Int sz, Int ts, Int td )
+{
+   uInstr3(cb, TAG2, 0, TempReg, ts, TempReg, td,
+               Lit16, get_Tag_DifD(sz));
+}
+
+
+/* Do HelpAND on value tval and tag tqqq, putting the result in
+   tqqq. */
+static 
+void create_ImproveAND_TQ ( UCodeBlock* cb, Int sz, Int tval, Int tqqq )
+{
+   uInstr3(cb, TAG2, 0, TempReg, tval, TempReg, tqqq,
+               Lit16, get_Tag_ImproveAND_TQ(sz));
+}
+
+
+/* Do HelpOR on value tval and tag tqqq, putting the result in
+   tqqq. */
+static 
+void create_ImproveOR_TQ ( UCodeBlock* cb, Int sz, Int tval, Int tqqq )
+{
+   uInstr3(cb, TAG2, 0, TempReg, tval, TempReg, tqqq,
+               Lit16, get_Tag_ImproveOR_TQ(sz));
+}
+
+
+/* Get the shadow for an operand described by (tag, val).  Emit code
+   to do this and return the identity of the shadow holding the
+   result.  The result tag is always copied into a new shadow, so it
+   can be modified without trashing the original.*/
+static
+Int /* TempReg */ getOperandShadow ( UCodeBlock* cb, 
+                                     Int sz, Int tag, Int val )
+{
+   Int sh;
+   sh = newShadow(cb);
+   if (tag == TempReg) {
+      uInstr2(cb, MOV, 4, TempReg, SHADOW(val), TempReg, sh);
+      return sh;
+   }
+   if (tag == Literal) {
+      uInstr1(cb, SETV, sz, TempReg, sh);
+      return sh;
+   }
+   if (tag == ArchReg) {
+      uInstr2(cb, GETV, sz, ArchReg, val, TempReg, sh);
+      return sh;
+   }
+   VG_(panic)("getOperandShadow");
+}
+
+/* Create and return an instrumented version of cb_in.  Free cb_in
+   before returning. */
+static UCodeBlock* memcheck_instrument ( UCodeBlock* cb_in )
+{
+   UCodeBlock* cb;
+   Int         i, j;
+   UInstr*     u_in;
+   Int         qs, qd, qt, qtt;
+   cb = VG_(allocCodeBlock)();
+   cb->nextTemp = cb_in->nextTemp;
+
+   for (i = 0; i < cb_in->used; i++) {
+      qs = qd = qt = qtt = INVALID_TEMPREG;
+      u_in = &cb_in->instrs[i];
+
+      switch (u_in->opcode) {
+
+         case NOP:
+            break;
+
+         case INCEIP:
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         /* Loads and stores.  Test the V bits for the address.  24
+            Mar 02: since the address is A-checked anyway, there's not
+            really much point in doing the V-check too, unless you
+            think that you might use addresses which are undefined but
+            still addressible.  Hence the optionalisation of the V
+            check.
+
+            The LOADV/STOREV does an addressibility check for the
+            address. */
+
+         case LOAD: 
+            if (SK_(clo_check_addrVs)) {
+               uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val1));
+               uInstr1(cb, SETV,  4, TempReg, SHADOW(u_in->val1));
+            }
+            uInstr2(cb, LOADV, u_in->size, 
+                        TempReg, u_in->val1,
+                        TempReg, SHADOW(u_in->val2));
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         case STORE:
+            if (SK_(clo_check_addrVs)) {
+               uInstr1(cb, TESTV,  4, TempReg, SHADOW(u_in->val2));
+               uInstr1(cb, SETV,   4, TempReg, SHADOW(u_in->val2));
+            }
+            uInstr2(cb, STOREV, u_in->size,
+                        TempReg, SHADOW(u_in->val1), 
+                        TempReg, u_in->val2);
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         /* Moving stuff around.  Make the V bits follow accordingly,
+            but don't do anything else.  */
+
+         case GET:
+            uInstr2(cb, GETV, u_in->size,
+                        ArchReg, u_in->val1,
+                        TempReg, SHADOW(u_in->val2));
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         case PUT:
+            uInstr2(cb, PUTV, u_in->size, 
+                        TempReg, SHADOW(u_in->val1),
+                        ArchReg, u_in->val2);
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         case GETF:
+            /* This is not the smartest way to do it, but should work. */
+            qd = create_GETVF(cb, u_in->size);
+            uInstr2(cb, MOV, 4, TempReg, qd, TempReg, SHADOW(u_in->val1));
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         case PUTF:
+            create_PUTVF(cb, u_in->size, SHADOW(u_in->val1));
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         case MOV:
+            switch (u_in->tag1) {
+               case TempReg: 
+                  uInstr2(cb, MOV, 4,
+                              TempReg, SHADOW(u_in->val1),
+                              TempReg, SHADOW(u_in->val2));
+                  break;
+               case Literal: 
+                  uInstr1(cb, SETV, u_in->size, 
+                              TempReg, SHADOW(u_in->val2));
+                  break;
+               default: 
+                  VG_(panic)("memcheck_instrument: MOV");
+            }
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         /* Special case of add, where one of the operands is a literal.
+            lea1(t) = t + some literal.
+            Therefore: lea1#(qa) = left(qa) 
+         */
+         case LEA1:
+            vg_assert(u_in->size == 4 && !VG_(anyFlagUse)(u_in));
+            qs = SHADOW(u_in->val1);
+            qd = SHADOW(u_in->val2);
+            uInstr2(cb, MOV, 4, TempReg, qs, TempReg, qd);
+            create_Left(cb, u_in->size, qd);
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         /* Another form of add.  
+            lea2(ts,tt,shift) = ts + (tt << shift); shift is a literal
+                                and is 0,1,2 or 3.
+            lea2#(qs,qt) = left(qs `UifU` (qt << shift)).
+            Note, subtly, that the shift puts zeroes at the bottom of qt,
+            meaning Valid, since the corresponding shift of tt puts 
+            zeroes at the bottom of tb.
+         */
+         case LEA2: {
+            Int shift;
+            vg_assert(u_in->size == 4 && !VG_(anyFlagUse)(u_in));
+            switch (u_in->extra4b) {
+               case 1: shift = 0; break;
+               case 2: shift = 1; break;
+               case 4: shift = 2; break;
+               case 8: shift = 3; break;
+               default: VG_(panic)( "memcheck_instrument(LEA2)" );
+            }
+            qs = SHADOW(u_in->val1);
+            qt = SHADOW(u_in->val2);
+            qd = SHADOW(u_in->val3);
+            uInstr2(cb, MOV, 4, TempReg, qt, TempReg, qd);
+            if (shift > 0) {
+               uInstr2(cb, SHL, 4, Literal, 0, TempReg, qd);
+               uLiteral(cb, shift);
+            }
+            create_UifU(cb, 4, qs, qd);
+            create_Left(cb, u_in->size, qd);
+            VG_(copyUInstr)(cb, u_in);
+            break;
+         }
+
+         /* inc#/dec#(qd) = q `UifU` left(qd) = left(qd) */
+         case INC: case DEC:
+            qd = SHADOW(u_in->val1);
+            create_Left(cb, u_in->size, qd);
+            if (u_in->flags_w != FlagsEmpty)
+               create_PUTVF(cb, u_in->size, qd);
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         /* This is a HACK (approximation :-) */
+         /* rcl#/rcr#(qs,qd) 
+               = let q0 = pcast-sz-0(qd) `UifU` pcast-sz-0(qs) `UifU` eflags#
+                 eflags# = q0
+                 qd =pcast-0-sz(q0)
+            Ie, cast everything down to a single bit, then back up.
+            This assumes that any bad bits infect the whole word and 
+            the eflags.
+         */
+         case RCL: case RCR:
+	    vg_assert(u_in->flags_r != FlagsEmpty);
+            /* The following assertion looks like it makes sense, but is
+               actually wrong.  Consider this:
+                  rcll    %eax
+                  imull   %eax, %eax
+               The rcll writes O and C but so does the imull, so the O and C 
+               write of the rcll is annulled by the prior improvement pass.
+               Noticed by Kevin Ryde <user42@zip.com.au>
+            */
+	    /* vg_assert(u_in->flags_w != FlagsEmpty); */
+            qs = getOperandShadow(cb, u_in->size, u_in->tag1, u_in->val1);
+            /* We can safely modify qs; cast it to 0-size. */
+            create_PCast(cb, u_in->size, 0, qs);
+            qd = SHADOW(u_in->val2);
+            create_PCast(cb, u_in->size, 0, qd);
+            /* qs is cast-to-0(shift count#), and qd is cast-to-0(value#). */
+            create_UifU(cb, 0, qs, qd);
+            /* qs is now free; reuse it for the flag definedness. */
+            qs = create_GETVF(cb, 0);
+            create_UifU(cb, 0, qs, qd);
+            create_PUTVF(cb, 0, qd);
+            create_PCast(cb, 0, u_in->size, qd);
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         /* for OP in shl shr sar rol ror
+            (qs is shift count#, qd is value to be OP#d)
+            OP(ts,td)
+            OP#(qs,qd)
+               = pcast-1-sz(qs) `UifU` OP(ts,qd)
+            So we apply OP to the tag bits too, and then UifU with
+            the shift count# to take account of the possibility of it
+            being undefined.
+            
+            A bit subtle:
+               ROL/ROR rearrange the tag bits as per the value bits.
+               SHL/SHR shifts zeroes into the value, and corresponding 
+                  zeroes indicating Definedness into the tag.
+               SAR copies the top bit of the value downwards, and therefore
+                  SAR also copies the definedness of the top bit too.
+            So in all five cases, we just apply the same op to the tag 
+            bits as is applied to the value bits.  Neat!
+         */
+         case SHL:
+         case SHR: case SAR:
+         case ROL: case ROR: {
+            Int t_amount = INVALID_TEMPREG;
+            vg_assert(u_in->tag1 == TempReg || u_in->tag1 == Literal);
+            vg_assert(u_in->tag2 == TempReg);
+            qd = SHADOW(u_in->val2);
+
+            /* Make qs hold shift-count# and make
+               t_amount be a TempReg holding the shift count. */
+            if (u_in->tag1 == Literal) {
+               t_amount = newTemp(cb);
+               uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_amount);
+               uLiteral(cb, u_in->lit32);
+               qs = SHADOW(t_amount);
+               uInstr1(cb, SETV, 1, TempReg, qs);
+            } else {
+               t_amount = u_in->val1;
+               qs = SHADOW(u_in->val1);
+            }
+
+            uInstr2(cb, u_in->opcode, 
+                        u_in->size, 
+                        TempReg, t_amount, 
+                        TempReg, qd);
+            qt = newShadow(cb);
+            uInstr2(cb, MOV, 4, TempReg, qs, TempReg, qt);
+            create_PCast(cb, 1, u_in->size, qt);
+            create_UifU(cb, u_in->size, qt, qd);
+            VG_(copyUInstr)(cb, u_in);
+            break;
+         }
+
+         /* One simple tag operation. */
+         case WIDEN:
+            vg_assert(u_in->tag1 == TempReg);
+            create_Widen(cb, u_in->signed_widen, u_in->extra4b, u_in->size, 
+                             SHADOW(u_in->val1));
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         /* not#(x) = x (since bitwise independent) */
+         case NOT:
+            vg_assert(u_in->tag1 == TempReg);
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         /* neg#(x) = left(x) (derivable from case for SUB) */
+         case NEG:
+            vg_assert(u_in->tag1 == TempReg);
+            create_Left(cb, u_in->size, SHADOW(u_in->val1));
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         /* bswap#(x) = bswap(x) */
+         case BSWAP:
+            vg_assert(u_in->tag1 == TempReg);
+            vg_assert(u_in->size == 4);
+            qd = SHADOW(u_in->val1);
+            uInstr1(cb, BSWAP, 4, TempReg, qd);
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         /* cc2val#(qd) = pcast-0-to-size(eflags#) */
+         case CC2VAL:
+            vg_assert(u_in->tag1 == TempReg);
+            vg_assert(u_in->flags_r != FlagsEmpty);
+            qt = create_GETVF(cb, u_in->size);
+            uInstr2(cb, MOV, 4, TempReg, qt, TempReg, SHADOW(u_in->val1));
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         /* cmov#(qs,qd) = cmov(qs,qd)
+            That is, do the cmov of tags using the same flags as for
+            the data (obviously).  However, first do a test on the 
+            validity of the flags.
+         */
+         case CMOV:
+            vg_assert(u_in->size == 4);
+            vg_assert(u_in->tag1 == TempReg);
+            vg_assert(u_in->tag2 == TempReg);
+            vg_assert(u_in->flags_r != FlagsEmpty);
+            vg_assert(u_in->flags_w == FlagsEmpty);
+            qs = SHADOW(u_in->val1);
+            qd = SHADOW(u_in->val2);
+            qt = create_GETVF(cb, 0);
+            uInstr1(cb, TESTV, 0, TempReg, qt);
+            /* qt should never be referred to again.  Nevertheless
+               ... */
+            uInstr1(cb, SETV, 0, TempReg, qt);
+
+            uInstr2(cb, CMOV, 4, TempReg, qs, TempReg, qd);
+            LAST_UINSTR(cb).cond    = u_in->cond;
+            LAST_UINSTR(cb).flags_r = u_in->flags_r;
+
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         /* add#/sub#(qs,qd) 
+               = qs `UifU` qd `UifU` left(qs) `UifU` left(qd)
+               = left(qs) `UifU` left(qd)
+               = left(qs `UifU` qd)
+            adc#/sbb#(qs,qd)
+               = left(qs `UifU` qd) `UifU` pcast(eflags#)
+            Second arg (dest) is TempReg.
+            First arg (src) is Literal or TempReg or ArchReg. 
+         */
+         case ADD: case SUB:
+         case ADC: case SBB:
+            qd = SHADOW(u_in->val2);
+            qs = getOperandShadow(cb, u_in->size, u_in->tag1, u_in->val1);
+            create_UifU(cb, u_in->size, qs, qd);
+            create_Left(cb, u_in->size, qd);
+            if (u_in->opcode == ADC || u_in->opcode == SBB) {
+               vg_assert(u_in->flags_r != FlagsEmpty);
+               qt = create_GETVF(cb, u_in->size);
+               create_UifU(cb, u_in->size, qt, qd);
+            }
+            if (u_in->flags_w != FlagsEmpty) {
+               create_PUTVF(cb, u_in->size, qd);
+            }
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         /* xor#(qs,qd) = qs `UifU` qd */
+         case XOR:
+            qd = SHADOW(u_in->val2);
+            qs = getOperandShadow(cb, u_in->size, u_in->tag1, u_in->val1);
+            create_UifU(cb, u_in->size, qs, qd);
+            if (u_in->flags_w != FlagsEmpty) {
+               create_PUTVF(cb, u_in->size, qd);
+            }
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         /* and#/or#(qs,qd) 
+               = (qs `UifU` qd) `DifD` improve(vs,qs) 
+                                `DifD` improve(vd,qd)
+            where improve is the relevant one of
+                Improve{AND,OR}_TQ
+            Use the following steps, with qt as a temp:
+               qt = improve(vd,qd)
+               qd = qs `UifU` qd
+               qd = qt `DifD` qd
+               qt = improve(vs,qs)
+               qd = qt `DifD` qd
+         */
+         case AND: case OR:
+            vg_assert(u_in->tag1 == TempReg);
+            vg_assert(u_in->tag2 == TempReg);
+            qd = SHADOW(u_in->val2);
+            qs = SHADOW(u_in->val1);
+            qt = newShadow(cb);
+
+            /* qt = improve(vd,qd) */
+            uInstr2(cb, MOV, 4, TempReg, qd, TempReg, qt);
+            if (u_in->opcode == AND)
+               create_ImproveAND_TQ(cb, u_in->size, u_in->val2, qt);
+            else
+               create_ImproveOR_TQ(cb, u_in->size, u_in->val2, qt);
+            /* qd = qs `UifU` qd */
+            create_UifU(cb, u_in->size, qs, qd);
+            /* qd = qt `DifD` qd */
+            create_DifD(cb, u_in->size, qt, qd);
+            /* qt = improve(vs,qs) */
+            uInstr2(cb, MOV, 4, TempReg, qs, TempReg, qt);
+            if (u_in->opcode == AND)
+               create_ImproveAND_TQ(cb, u_in->size, u_in->val1, qt);
+            else
+               create_ImproveOR_TQ(cb, u_in->size, u_in->val1, qt);
+            /* qd = qt `DifD` qd */
+               create_DifD(cb, u_in->size, qt, qd);
+            /* So, finally qd is the result tag. */
+            if (u_in->flags_w != FlagsEmpty) {
+               create_PUTVF(cb, u_in->size, qd);
+            }
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         /* Machinery to do with supporting CALLM.  Copy the start and
+            end markers only to make the result easier to read
+            (debug); they generate no code and have no effect. 
+         */
+         case CALLM_S: case CALLM_E:
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         /* Copy PUSH and POP verbatim.  Arg/result absval
+            calculations are done when the associated CALL is
+            processed.  CLEAR has no effect on absval calculations but
+            needs to be copied.  
+         */
+         case PUSH: case POP: case CLEAR:
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         /* In short:
+               callm#(a1# ... an#) = (a1# `UifU` ... `UifU` an#)
+            We have to decide on a size to do the computation at,
+            although the choice doesn't affect correctness.  We will
+            do a pcast to the final size anyway, so the only important
+            factor is to choose a size which minimises the total
+            number of casts needed.  Valgrind: just use size 0,
+            regardless.  It may not be very good for performance
+            but does simplify matters, mainly by reducing the number
+            of different pessimising casts which have to be implemented.
+         */
+         case CALLM: {
+            UInstr* uu;
+            Bool res_used;
+
+            /* Now generate the code.  Get the final result absval
+               into qt. */
+            qt  = newShadow(cb);
+            qtt = newShadow(cb);
+            uInstr1(cb, SETV, 0, TempReg, qt);
+            for (j = i-1; cb_in->instrs[j].opcode != CALLM_S; j--) {
+               uu = & cb_in->instrs[j];
+               if (uu->opcode != PUSH) continue;
+               /* cast via a temporary */
+               uInstr2(cb, MOV, 4, TempReg, SHADOW(uu->val1),
+                                   TempReg, qtt);
+               create_PCast(cb, uu->size, 0, qtt);
+               create_UifU(cb, 0, qtt, qt);
+            }
+            /* Remembering also that flags read count as inputs. */
+            if (u_in->flags_r != FlagsEmpty) {
+               qtt = create_GETVF(cb, 0);
+               create_UifU(cb, 0, qtt, qt);
+            }
+
+            /* qt now holds the result tag.  If any results from the
+               call are used, either by fetching with POP or
+               implicitly by writing the flags, we copy the result
+               absval to the relevant location.  If not used, the call
+               must have been for its side effects, so we test qt here
+               and now.  Note that this assumes that all values
+               removed by POP continue to be live.  So dead args
+               *must* be removed with CLEAR, not by POPping them into
+               a dummy tempreg. 
+            */
+            res_used = False;
+            for (j = i+1; cb_in->instrs[j].opcode != CALLM_E; j++) {
+               uu = & cb_in->instrs[j];
+               if (uu->opcode != POP) continue;
+               /* Cast via a temp. */
+               uInstr2(cb, MOV, 4, TempReg, qt, TempReg, qtt);
+               create_PCast(cb, 0, uu->size, qtt);
+               uInstr2(cb, MOV, 4, TempReg, qtt, 
+                                   TempReg, SHADOW(uu->val1));
+               res_used = True;
+            }
+            if (u_in->flags_w != FlagsEmpty) {
+               create_PUTVF(cb, 0, qt);
+               res_used = True;
+            }
+            if (!res_used) {
+               uInstr1(cb, TESTV, 0, TempReg, qt);
+               /* qt should never be referred to again.  Nevertheless
+                  ... */
+               uInstr1(cb, SETV, 0, TempReg, qt);
+            }
+            VG_(copyUInstr)(cb, u_in);
+            break;
+         }
+         /* Whew ... */
+
+         case JMP:
+            if (u_in->tag1 == TempReg) {
+               uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val1));
+               uInstr1(cb, SETV,  4, TempReg, SHADOW(u_in->val1));
+            } else {
+               vg_assert(u_in->tag1 == Literal);
+            }
+            if (u_in->cond != CondAlways) {
+               vg_assert(u_in->flags_r != FlagsEmpty);
+               qt = create_GETVF(cb, 0);
+               uInstr1(cb, TESTV, 0, TempReg, qt);
+               /* qt should never be referred to again.  Nevertheless
+                  ... */
+               uInstr1(cb, SETV, 0, TempReg, qt);
+            }
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         case JIFZ:
+            uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val1));
+            uInstr1(cb, SETV,  4, TempReg, SHADOW(u_in->val1));
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         /* Emit a check on the address used.  The value loaded into the 
+            FPU is checked by the call to fpu_{read/write}_check().  */
+         case FPU_R: case FPU_W: {
+            Int t_size = INVALID_TEMPREG;
+
+            vg_assert(u_in->tag2 == TempReg);
+            uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val2));
+            uInstr1(cb, SETV,  4, TempReg, SHADOW(u_in->val2));
+
+            t_size = newTemp(cb);
+            uInstr2(cb, MOV,   4, Literal, 0, TempReg, t_size);
+            uLiteral(cb, u_in->size);
+            uInstr2(cb, CCALL, 0, TempReg, u_in->val2, TempReg, t_size);
+            uCCall(cb, 
+                   u_in->opcode==FPU_R ? (Addr) & SK_(fpu_read_check) 
+                                       : (Addr) & SK_(fpu_write_check),
+                   2, 2, False);
+
+            VG_(copyUInstr)(cb, u_in);
+            break;
+         }
+
+         /* For FPU insns not referencing memory, just copy thru. */
+         case FPU: 
+            VG_(copyUInstr)(cb, u_in);
+            break;
+
+         default:
+            VG_(ppUInstr)(0, u_in);
+            VG_(panic)( "memcheck_instrument: unhandled case");
+
+      } /* end of switch (u_in->opcode) */
+
+   } /* end of for loop */
+
+   VG_(freeCodeBlock)(cb_in);
+   return cb;
+}
+
+/*------------------------------------------------------------*/
+/*--- Clean up mem check instrumentation.                  ---*/
+/*------------------------------------------------------------*/
+
+Bool VG_(clo_memcheck_codegen) = False;
+
+#define dis    VG_(print_codegen)
+
+
+#define VGC_IS_SHADOW(tempreg) ((tempreg % 2) == 1)
+#define VGC_UNDEF ((UChar)100)
+#define VGC_VALUE ((UChar)101)
+
+#define NOP_no_msg(uu)                                            \
+   do { VG_(newNOP)(uu); } while (False)
+
+#define NOP_tag1_op(uu)                                           \
+   do { VG_(newNOP)(uu);                                          \
+        if (dis)                                                  \
+           VG_(printf)("   at %2d: delete %s due to defd arg\n",  \
+                       i, nameOfTagOp(u->val3));                  \
+   } while (False)
+
+#define SETV_tag1_op(uu,newsz)                                    \
+   do { uu->opcode = SETV;                                        \
+        uu->size = newsz;                                         \
+        uu->tag2 = uu->tag3 = NoValue;                            \
+        if (dis)                                                  \
+           VG_(printf)("   at %2d: convert %s to SETV%d "         \
+                       "due to defd arg\n",                       \
+                       i, nameOfTagOp(u->val3), newsz);           \
+   } while (False)
+
+
+
+/* Run backwards and delete SETVs on shadow temps for which the next
+   action is a write.  Needs an env saying whether or not the next
+   action is a write.  The supplied UCodeBlock is destructively
+   modified.
+*/
+static void vg_delete_redundant_SETVs ( UCodeBlock* cb )
+{
+   Int     i, j, k;
+   Int     n_temps = cb->nextTemp;
+   Bool*   next_is_write;
+   UInstr* u;
+   RegUse  tempUse[3];
+
+   if (n_temps == 0) return;
+
+   next_is_write = VG_(malloc)(n_temps * sizeof(Bool));
+
+   for (i = 0; i < n_temps; i++) next_is_write[i] = True;
+
+   for (i = cb->used-1; i >= 0; i--) {
+      u = &cb->instrs[i];
+
+      /* If we're not checking address V bits, there will be a lot of
+         GETVs, TAG1s and TAG2s calculating values which are never
+         used.  These first three cases get rid of them. */
+
+      if (u->opcode == GETV && VGC_IS_SHADOW(u->val2) 
+                            && next_is_write[u->val2]
+                            && !SK_(clo_check_addrVs)) {
+         VG_(newNOP)(u);
+         if (dis) 
+            VG_(printf)("   at %2d: delete GETV\n", i);
+      } else
+
+      if (u->opcode == TAG1 && VGC_IS_SHADOW(u->val1) 
+                            && next_is_write[u->val1]
+                            && !SK_(clo_check_addrVs)) {
+         VG_(newNOP)(u);
+         if (dis) 
+            VG_(printf)("   at %2d: delete TAG1\n", i);
+      } else
+
+      if (u->opcode == TAG2 && VGC_IS_SHADOW(u->val2) 
+                            && next_is_write[u->val2]
+                            && !SK_(clo_check_addrVs)) {
+         VG_(newNOP)(u);
+         if (dis) 
+            VG_(printf)("   at %2d: delete TAG2\n", i);
+      } else
+
+      /* We do the rest of these regardless of whether or not
+         addresses are V-checked. */
+
+      if (u->opcode == MOV && VGC_IS_SHADOW(u->val2) 
+                           && next_is_write[u->val2]) {
+         /* This MOV is pointless because the target is dead at this
+            point.  Delete it. */
+         VG_(newNOP)(u);
+         if (dis) 
+            VG_(printf)("   at %2d: delete MOV\n", i);
+      } else
+
+      if (u->opcode == SETV) {
+         if (u->tag1 == TempReg) {
+            vg_assert(VGC_IS_SHADOW(u->val1));
+            if (next_is_write[u->val1]) {
+               /* This write is pointless, so annul it. */
+               VG_(newNOP)(u);
+               if (dis) 
+                  VG_(printf)("   at %2d: delete SETV\n", i);
+            } else {
+               /* This write has a purpose; don't annul it, but do
+                  notice that we did it. */
+               next_is_write[u->val1] = True;
+            }
+              
+         }
+
+      } else {
+         /* Find out what this insn does to the temps. */
+         k = VG_(getRegUsage)(u, TempReg, &tempUse[0]);
+         vg_assert(k <= 3);
+         for (j = k-1; j >= 0; j--) {
+            next_is_write[ tempUse[j].num ]
+                         = tempUse[j].isWrite;
+         }
+      }
+   }
+}
+
+
+/* Run forwards, propagating and using the is-completely-defined
+   property.  This removes a lot of redundant tag-munging code.
+   Unfortunately it requires intimate knowledge of how each uinstr and
+   tagop modifies its arguments.  This duplicates knowledge of uinstr
+   tempreg uses embodied in VG_(getRegUsage)(), which is unfortunate. 
+   The supplied UCodeBlock* is modified in-place.
+
+   For each value temp, def[] should hold VGC_VALUE.
+
+   For each shadow temp, def[] may hold 4,2,1 or 0 iff that shadow is
+   definitely known to be fully defined at that size.  In all other
+   circumstances a shadow's def[] entry is VGC_UNDEF, meaning possibly
+   undefined.  In cases of doubt, VGC_UNDEF is always safe.
+*/
+static void vg_propagate_definedness ( UCodeBlock* cb )
+{
+   Int     i, j, k, t;
+   Int     n_temps = cb->nextTemp;
+   UChar*  def;
+   UInstr* u;
+   RegUse  tempUse[3];
+
+   if (n_temps == 0) return;
+
+   def = VG_(malloc)(n_temps * sizeof(UChar));
+
+   for (i = 0; i < n_temps; i++) 
+      def[i] = VGC_IS_SHADOW(i) ? VGC_UNDEF : VGC_VALUE;
+
+   /* Run forwards, detecting and using the all-defined property. */
+
+   for (i = 0; i < cb->used; i++) {
+      u = &cb->instrs[i];
+      switch (u->opcode) {
+
+      /* Tag-handling uinstrs. */
+
+         /* Deal with these quickly. */
+         case NOP:
+         case INCEIP:
+            break;
+
+         /* Make a tag defined. */
+         case SETV:
+            vg_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1));
+            def[u->val1] = u->size;
+            break;
+
+         /* Check definedness of a tag. */
+         case TESTV:
+            vg_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1));
+            if (def[u->val1] <= 4) { 
+               vg_assert(def[u->val1] == u->size); 
+               NOP_no_msg(u);
+               if (dis) 
+                  VG_(printf)("   at %2d: delete TESTV on defd arg\n", i);
+            }
+            break;
+
+         /* Applies to both values and tags.  Propagate Definedness
+            property through copies.  Note that this isn't optional;
+            we *have* to do this to keep def[] correct. */
+         case MOV:
+            vg_assert(u->tag2 == TempReg);
+            if (u->tag1 == TempReg) {
+               if (VGC_IS_SHADOW(u->val1)) {
+                  vg_assert(VGC_IS_SHADOW(u->val2));
+                  def[u->val2] = def[u->val1];
+               }
+            }
+            break;
+
+         case PUTV:
+            vg_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1));
+            if (def[u->val1] <= 4) {
+               vg_assert(def[u->val1] == u->size);
+               u->tag1 = Literal;
+               u->val1 = 0;
+               switch (u->size) {
+                  case 4: u->lit32 = 0x00000000; break;
+                  case 2: u->lit32 = 0xFFFF0000; break;
+                  case 1: u->lit32 = 0xFFFFFF00; break;
+                  default: VG_(panic)("vg_cleanup(PUTV)");
+               }
+               if (dis) 
+                  VG_(printf)(
+                     "   at %2d: propagate definedness into PUTV\n", i);
+            }
+            break;
+
+         case STOREV:
+            vg_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1));
+            if (def[u->val1] <= 4) {
+               vg_assert(def[u->val1] == u->size);
+               u->tag1 = Literal;
+               u->val1 = 0;
+               switch (u->size) {
+                  case 4: u->lit32 = 0x00000000; break;
+                  case 2: u->lit32 = 0xFFFF0000; break;
+                  case 1: u->lit32 = 0xFFFFFF00; break;
+                  default: VG_(panic)("vg_cleanup(STOREV)");
+               }
+               if (dis) 
+                  VG_(printf)(
+                     "   at %2d: propagate definedness into STandV\n", i);
+            }
+            break;
+
+         /* Nothing interesting we can do with this, I think. */
+         case PUTVF:
+            break;
+
+         /* Tag handling operations. */
+         case TAG2:
+            vg_assert(u->tag2 == TempReg && VGC_IS_SHADOW(u->val2));
+            vg_assert(u->tag3 == Lit16);
+            /* Ultra-paranoid "type" checking. */
+            switch (u->val3) {
+               case Tag_ImproveAND4_TQ: case Tag_ImproveAND2_TQ:
+               case Tag_ImproveAND1_TQ: case Tag_ImproveOR4_TQ:
+               case Tag_ImproveOR2_TQ: case Tag_ImproveOR1_TQ:
+                  vg_assert(u->tag1 == TempReg && !VGC_IS_SHADOW(u->val1));
+                  break;
+               default:
+                  vg_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1));
+                  break;
+            }
+            switch (u->val3) {
+               Int sz;
+               case Tag_UifU4: 
+                  sz = 4; goto do_UifU;
+               case Tag_UifU2: 
+                  sz = 2; goto do_UifU;
+               case Tag_UifU1:
+                  sz = 1; goto do_UifU;
+               case Tag_UifU0:
+                  sz = 0; goto do_UifU;
+               do_UifU:
+                  vg_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1));
+                  vg_assert(u->tag2 == TempReg && VGC_IS_SHADOW(u->val2));
+                  if (def[u->val1] <= 4) {
+                     /* UifU.  The first arg is defined, so result is
+                        simply second arg.  Delete this operation. */
+                     vg_assert(def[u->val1] == sz);
+                     NOP_no_msg(u);
+                     if (dis) 
+                        VG_(printf)(
+                           "   at %2d: delete UifU%d due to defd arg1\n", 
+                           i, sz);
+                  }
+                  else 
+                  if (def[u->val2] <= 4) {
+                     /* UifU.  The second arg is defined, so result is
+                        simply first arg.  Copy to second. */
+                     vg_assert(def[u->val2] == sz);
+                     u->opcode = MOV; 
+                     u->size = 4;
+                     u->tag3 = NoValue;
+                     def[u->val2] = def[u->val1];
+                     if (dis) 
+                        VG_(printf)(
+                           "   at %2d: change UifU%d to MOV due to defd"
+                           " arg2\n", 
+                           i, sz);
+                  }
+                  break;
+               case Tag_ImproveAND4_TQ:
+                  sz = 4; goto do_ImproveAND;
+               case Tag_ImproveAND1_TQ:
+                  sz = 1; goto do_ImproveAND;
+               do_ImproveAND:
+                  /* Implements Q = T OR Q.  So if Q is entirely defined,
+                     ie all 0s, we get MOV T, Q. */
+		  if (def[u->val2] <= 4) {
+                     vg_assert(def[u->val2] == sz);
+                     u->size = 4; /* Regardless of sz */
+                     u->opcode = MOV;
+                     u->tag3 = NoValue;
+                     def[u->val2] = VGC_UNDEF;
+                     if (dis) 
+                        VG_(printf)(
+                            "   at %2d: change ImproveAND%d_TQ to MOV due "
+                            "to defd arg2\n", 
+                            i, sz);
+                  }
+                  break;
+               default: 
+                  goto unhandled;
+            }
+            break;
+
+         case TAG1:
+            vg_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1));
+            if (def[u->val1] > 4) break;
+            /* We now know that the arg to the op is entirely defined.
+               If the op changes the size of the arg, we must replace
+               it with a SETV at the new size.  If it doesn't change
+               the size, we can delete it completely. */
+            switch (u->val3) {
+               /* Maintain the same size ... */
+               case Tag_Left4: 
+                  vg_assert(def[u->val1] == 4);
+                  NOP_tag1_op(u);
+                  break;
+               case Tag_PCast11: 
+                  vg_assert(def[u->val1] == 1);
+                  NOP_tag1_op(u);
+                  break;
+               /* Change size ... */
+               case Tag_PCast40: 
+                  vg_assert(def[u->val1] == 4);
+                  SETV_tag1_op(u,0);
+                  def[u->val1] = 0;
+                  break;
+               case Tag_PCast14: 
+                  vg_assert(def[u->val1] == 1);
+                  SETV_tag1_op(u,4);
+                  def[u->val1] = 4;
+                  break;
+               case Tag_PCast12: 
+                  vg_assert(def[u->val1] == 1);
+                  SETV_tag1_op(u,2);
+                  def[u->val1] = 2;
+                  break;
+               case Tag_PCast10: 
+                  vg_assert(def[u->val1] == 1);
+                  SETV_tag1_op(u,0);
+                  def[u->val1] = 0;
+                  break;
+               case Tag_PCast02: 
+                  vg_assert(def[u->val1] == 0);
+                  SETV_tag1_op(u,2);
+                  def[u->val1] = 2;
+                  break;
+               default: 
+                  goto unhandled;
+            }
+            if (dis) 
+               VG_(printf)(
+                  "   at %2d: delete TAG1 %s due to defd arg\n",
+                  i, nameOfTagOp(u->val3));
+            break;
+
+         default:
+         unhandled:
+            /* We don't know how to handle this uinstr.  Be safe, and 
+               set to VGC_VALUE or VGC_UNDEF all temps written by it. */
+            k = VG_(getRegUsage)(u, TempReg, &tempUse[0]);
+            vg_assert(k <= 3);
+            for (j = 0; j < k; j++) {
+               t = tempUse[j].num;
+               vg_assert(t >= 0 && t < n_temps);
+               if (!tempUse[j].isWrite) {
+                  /* t is read; ignore it. */
+                  if (0&& VGC_IS_SHADOW(t) && def[t] <= 4)
+                     VG_(printf)("ignoring def %d at %s %s\n", 
+                                 def[t], 
+                                 VG_(nameUOpcode)(True, u->opcode),
+                                 (u->opcode == TAG1 || u->opcode == TAG2)
+                                    ? nameOfTagOp(u->val3) 
+                                    : (Char*)"");
+               } else {
+                  /* t is written; better nullify it. */
+                  def[t] = VGC_IS_SHADOW(t) ? VGC_UNDEF : VGC_VALUE;
+               }
+            }
+      }
+   }
+}
+
+
+/* Top level post-MemCheck-instrumentation cleanup function. */
+static void vg_cleanup ( UCodeBlock* cb )
+{
+   vg_propagate_definedness ( cb );
+   vg_delete_redundant_SETVs ( cb );
+}
+
+
+/* Caller will print out final instrumented code if necessary;  we
+   print out intermediate instrumented code here if necessary. */
+UCodeBlock* SK_(instrument) ( UCodeBlock* cb, Addr not_used )
+{
+   cb = memcheck_instrument ( cb );
+   if (SK_(clo_cleanup)) {
+      if (dis) {
+         VG_(ppUCodeBlock) ( cb, "Unimproved instrumented UCode:" );
+         VG_(printf)("Instrumentation improvements:\n");
+      }
+      vg_cleanup(cb);
+      if (dis) VG_(printf)("\n");
+   }
+   return cb;
+}
+
+#undef dis
+
+/*--------------------------------------------------------------------*/
+/*--- end                                  vg_memcheck_translate.c ---*/
+/*--------------------------------------------------------------------*/
diff --git a/memcheck/memcheck.h b/memcheck/memcheck.h
new file mode 100644
index 0000000..b126ffb
--- /dev/null
+++ b/memcheck/memcheck.h
@@ -0,0 +1,197 @@
+
+/*
+   ----------------------------------------------------------------
+
+   Notice that the following BSD-style license applies to this one
+   file (vg_memcheck.h) only.  The entire rest of Valgrind is licensed
+   under the terms of the GNU General Public License, version 2.  See
+   the COPYING file in the source distribution for details.
+
+   ----------------------------------------------------------------
+
+   This file is part of Valgrind, an x86 protected-mode emulator 
+   designed for debugging and profiling binaries on x86-Unixes.
+
+   Copyright (C) 2000-2002 Julian Seward.  All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   1. Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+   2. The origin of this software must not be misrepresented; you must 
+      not claim that you wrote the original software.  If you use this 
+      software in a product, an acknowledgment in the product 
+      documentation would be appreciated but is not required.
+
+   3. Altered source versions must be plainly marked as such, and must
+      not be misrepresented as being the original software.
+
+   4. The name of the author may not be used to endorse or promote 
+      products derived from this software without specific prior written 
+      permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+   OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+   ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   ----------------------------------------------------------------
+
+   Notice that the above BSD-style license applies to this one file
+   (vg_memcheck.h) only.  The entire rest of Valgrind is licensed under
+   the terms of the GNU General Public License, version 2.  See the
+   COPYING file in the source distribution for details.
+
+   ---------------------------------------------------------------- 
+*/
+
+
+#ifndef __VG_MEMCHECK_H
+#define __VG_MEMCHECK_H
+
+
+/* This file is for inclusion into client (your!) code.
+
+   You can use these macros to manipulate and query memory permissions
+   inside your own programs.
+
+   See comment near the top of valgrind.h on how to use them.
+*/
+
+#include "valgrind.h"
+
+typedef
+   enum { 
+      VG_USERREQ__MAKE_NOACCESS = VG_USERREQ__FINAL_DUMMY_CLIENT_REQUEST + 1, 
+      VG_USERREQ__MAKE_WRITABLE,
+      VG_USERREQ__MAKE_READABLE,
+      VG_USERREQ__DISCARD,
+      VG_USERREQ__CHECK_WRITABLE,
+      VG_USERREQ__CHECK_READABLE,
+      VG_USERREQ__MAKE_NOACCESS_STACK,
+      VG_USERREQ__DO_LEAK_CHECK, /* untested */
+   } Vg_MemCheckClientRequest;
+
+
+
+/* Client-code macros to manipulate the state of memory. */
+
+/* Mark memory at _qzz_addr as unaddressible and undefined for
+   _qzz_len bytes.  Returns an int handle pertaining to the block
+   descriptions Valgrind will use in subsequent error messages. */
+#define VALGRIND_MAKE_NOACCESS(_qzz_addr,_qzz_len)               \
+   ({unsigned int _qzz_res;                                      \
+    VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0 /* default return */,    \
+                            VG_USERREQ__MAKE_NOACCESS,           \
+                            _qzz_addr, _qzz_len, 0, 0);          \
+    _qzz_res;                                                    \
+   }) 
+      
+/* Similarly, mark memory at _qzz_addr as addressible but undefined
+   for _qzz_len bytes. */
+#define VALGRIND_MAKE_WRITABLE(_qzz_addr,_qzz_len)               \
+   ({unsigned int _qzz_res;                                      \
+    VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0 /* default return */,    \
+                            VG_USERREQ__MAKE_WRITABLE,           \
+                            _qzz_addr, _qzz_len, 0, 0);          \
+    _qzz_res;                                                    \
+   })
+
+/* Similarly, mark memory at _qzz_addr as addressible and defined
+   for _qzz_len bytes. */
+#define VALGRIND_MAKE_READABLE(_qzz_addr,_qzz_len)               \
+   ({unsigned int _qzz_res;                                      \
+    VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0 /* default return */,    \
+                            VG_USERREQ__MAKE_READABLE,           \
+                            _qzz_addr, _qzz_len, 0, 0);          \
+    _qzz_res;                                                    \
+   })
+
+/* Discard a block-description-handle obtained from the above three
+   macros.  After this, Valgrind will no longer be able to relate
+   addressing errors to the user-defined block associated with the
+   handle.  The permissions settings associated with the handle remain
+   in place.  Returns 1 for an invalid handle, 0 for a valid
+   handle. */
+#define VALGRIND_DISCARD(_qzz_blkindex)                          \
+   ({unsigned int _qzz_res;                                      \
+    VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0 /* default return */,    \
+                            VG_USERREQ__DISCARD,                 \
+                            0, _qzz_blkindex, 0, 0);             \
+    _qzz_res;                                                    \
+   })
+
+
+/* Client-code macros to check the state of memory. */
+
+/* Check that memory at _qzz_addr is addressible for _qzz_len bytes.
+   If suitable addressibility is not established, Valgrind prints an
+   error message and returns the address of the first offending byte.
+   Otherwise it returns zero. */
+#define VALGRIND_CHECK_WRITABLE(_qzz_addr,_qzz_len)                \
+   ({unsigned int _qzz_res;                                        \
+    VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0,                           \
+                            VG_USERREQ__CHECK_WRITABLE,            \
+                            _qzz_addr, _qzz_len, 0, 0);            \
+    _qzz_res;                                                      \
+   })
+
+/* Check that memory at _qzz_addr is addressible and defined for
+   _qzz_len bytes.  If suitable addressibility and definedness are not
+   established, Valgrind prints an error message and returns the
+   address of the first offending byte.  Otherwise it returns zero. */
+#define VALGRIND_CHECK_READABLE(_qzz_addr,_qzz_len)                \
+   ({unsigned int _qzz_res;                                        \
+    VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0,                           \
+                            VG_USERREQ__CHECK_READABLE,            \
+                            _qzz_addr, _qzz_len, 0, 0);            \
+    _qzz_res;                                                      \
+   })
+
+/* Use this macro to force the definedness and addressibility of a
+   value to be checked.  If suitable addressibility and definedness
+   are not established, Valgrind prints an error message and returns
+   the address of the first offending byte.  Otherwise it returns
+   zero. */
+#define VALGRIND_CHECK_DEFINED(__lvalue)                           \
+   (void)                                                          \
+   VALGRIND_CHECK_READABLE(                                        \
+      (volatile unsigned char *)&(__lvalue),                       \
+                      (unsigned int)(sizeof (__lvalue)))
+
+/* Mark memory, intended to be on the client's stack, at _qzz_addr as
+   unaddressible and undefined for _qzz_len bytes.  Does not return a
+   value.  The record associated with this setting will be
+   automatically removed by Valgrind when the containing routine
+   exits. */
+#define VALGRIND_MAKE_NOACCESS_STACK(_qzz_addr,_qzz_len)           \
+   {unsigned int _qzz_res;                                         \
+    VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0,                           \
+                            VG_USERREQ__MAKE_NOACCESS_STACK,       \
+                            _qzz_addr, _qzz_len, 0, 0);            \
+   }
+
+
+
+/* Do a memory leak check mid-execution.
+   Currently implemented but untested.
+*/
+#define VALGRIND_DO_LEAK_CHECK                                     \
+   {unsigned int _qzz_res;                                         \
+    VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0,                           \
+                            VG_USERREQ__DO_LEAK_CHECK,             \
+                            0, 0, 0, 0);                           \
+   }
+
+
+#endif
diff --git a/memcheck/tests/Makefile.am b/memcheck/tests/Makefile.am
new file mode 100644
index 0000000..a36047c
--- /dev/null
+++ b/memcheck/tests/Makefile.am
@@ -0,0 +1,61 @@
+## Process this file with automake to produce Makefile.in
+
+##---------------------------------------------------------------------------
+## Need more tests:
+## - lots more mmap/munmap/mremap/mprotect ones
+
+## Notes:
+##   - HEAD and ERASER stderr.exp are different for signal2 due to lazy vs.
+##     strict EIP updating
+##---------------------------------------------------------------------------
+
+noinst_PROGRAMS = \
+	badaddrvalue badfree badjump badloop buflen_check \
+	doublefree errs1 exitprog fprw fwrite inits inline \
+	malloc1 malloc2 manuel1 manuel2 manuel3 \
+	memalign_test memcmptest mmaptest nanoleak pushfpopf \
+	realloc1 realloc2 sigaltstack signal2 supp1 supp2 suppfree \
+	trivialleak tronical weirdioctl	\
+	mismatches new_override
+
+CFLAGS   = $(WERROR) -Winline -Wall -Wshadow -g
+CXXFLAGS = $(CFLAGS)
+
+# C ones
+badaddrvalue_SOURCES 	= badaddrvalue.c
+badfree_SOURCES 	= badfree.c
+badjump_SOURCES 	= badjump.c
+badloop_SOURCES 	= badloop.c
+buflen_check_SOURCES	= buflen_check.c
+doublefree_SOURCES 	= doublefree.c
+errs1_SOURCES 		= errs1.c
+exitprog_SOURCES 	= exitprog.c
+fprw_SOURCES 		= fprw.c
+fwrite_SOURCES 		= fwrite.c
+inits_SOURCES		= inits.c
+inline_SOURCES 	        = inline.c
+malloc1_SOURCES 	= malloc1.c
+malloc2_SOURCES 	= malloc2.c
+manuel1_SOURCES 	= manuel1.c
+manuel2_SOURCES 	= manuel2.c
+manuel3_SOURCES 	= manuel3.c
+mmaptest_SOURCES 	= mmaptest.c
+memalign_test_SOURCES 	= memalign_test.c
+memcmptest_SOURCES 	= memcmptest.c
+nanoleak_SOURCES 	= nanoleak.c
+pushfpopf_SOURCES 	= pushfpopf_c.c pushfpopf_s.s
+realloc1_SOURCES 	= realloc1.c
+realloc2_SOURCES 	= realloc2.c
+signal2_SOURCES 	= signal2.c
+supp1_SOURCES 		= supp.c
+supp2_SOURCES 		= supp.c
+suppfree_SOURCES 	= suppfree.c
+sigaltstack_SOURCES 	= sigaltstack.c
+trivialleak_SOURCES 	= trivialleak.c
+tronical_SOURCES 	= tronical.S
+weirdioctl_SOURCES 	= weirdioctl.c
+
+# C++ ones
+mismatches_SOURCES	= mismatches.cpp
+new_override_SOURCES 	= new_override.cpp
+
diff --git a/memcheck/tests/badaddrvalue.c b/memcheck/tests/badaddrvalue.c
new file mode 100644
index 0000000..1bb2047
--- /dev/null
+++ b/memcheck/tests/badaddrvalue.c
@@ -0,0 +1,12 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main ( void )
+{
+   char* aa = malloc(8);
+   aa[-1] = 17;
+   if (aa[-1] == 17) 
+      printf("17\n"); else printf("not 17\n");
+   return 0;
+}
diff --git a/memcheck/tests/badaddrvalue.stderr.exp b/memcheck/tests/badaddrvalue.stderr.exp
new file mode 100644
index 0000000..8888c85
--- /dev/null
+++ b/memcheck/tests/badaddrvalue.stderr.exp
@@ -0,0 +1,26 @@
+
+Invalid write of size 1
+   at 0x........: main (badaddrvalue.c:8)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: (within /.../tests/memcheck/badaddrvalue)
+   Address 0x........ is 1 bytes before a block of size 8 alloc'd
+   at 0x........: malloc (vg_clientfuncs.c:...)
+   by 0x........: main (badaddrvalue.c:7)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: (within /.../tests/memcheck/badaddrvalue)
+
+Invalid read of size 1
+   at 0x........: main (badaddrvalue.c:9)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: (within /.../tests/memcheck/badaddrvalue)
+   Address 0x........ is 1 bytes before a block of size 8 alloc'd
+   at 0x........: malloc (vg_clientfuncs.c:...)
+   by 0x........: main (badaddrvalue.c:7)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: (within /.../tests/memcheck/badaddrvalue)
+
+ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 8 bytes in 1 blocks.
+malloc/free: 1 allocs, 0 frees, 8 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/badaddrvalue.stderr.exp.hd b/memcheck/tests/badaddrvalue.stderr.exp.hd
new file mode 100644
index 0000000..8888c85
--- /dev/null
+++ b/memcheck/tests/badaddrvalue.stderr.exp.hd
@@ -0,0 +1,26 @@
+
+Invalid write of size 1
+   at 0x........: main (badaddrvalue.c:8)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: (within /.../tests/memcheck/badaddrvalue)
+   Address 0x........ is 1 bytes before a block of size 8 alloc'd
+   at 0x........: malloc (vg_clientfuncs.c:...)
+   by 0x........: main (badaddrvalue.c:7)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: (within /.../tests/memcheck/badaddrvalue)
+
+Invalid read of size 1
+   at 0x........: main (badaddrvalue.c:9)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: (within /.../tests/memcheck/badaddrvalue)
+   Address 0x........ is 1 bytes before a block of size 8 alloc'd
+   at 0x........: malloc (vg_clientfuncs.c:...)
+   by 0x........: main (badaddrvalue.c:7)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: (within /.../tests/memcheck/badaddrvalue)
+
+ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 8 bytes in 1 blocks.
+malloc/free: 1 allocs, 0 frees, 8 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/badaddrvalue.stdout.exp b/memcheck/tests/badaddrvalue.stdout.exp
new file mode 100644
index 0000000..98d9bcb
--- /dev/null
+++ b/memcheck/tests/badaddrvalue.stdout.exp
@@ -0,0 +1 @@
+17
diff --git a/memcheck/tests/badaddrvalue.vgtest b/memcheck/tests/badaddrvalue.vgtest
new file mode 100644
index 0000000..91187e8
--- /dev/null
+++ b/memcheck/tests/badaddrvalue.vgtest
@@ -0,0 +1 @@
+prog: badaddrvalue
diff --git a/memcheck/tests/badfree-2trace.stderr.exp b/memcheck/tests/badfree-2trace.stderr.exp
new file mode 100644
index 0000000..741fd25
--- /dev/null
+++ b/memcheck/tests/badfree-2trace.stderr.exp
@@ -0,0 +1,16 @@
+
+Invalid free() / delete / delete[]
+   at 0x........: free (vg_clientfuncs.c:...)
+   by 0x........: main (badfree.c:12)
+   Address 0x........ is not stack'd, malloc'd or free'd
+
+Invalid free() / delete / delete[]
+   at 0x........: free (vg_clientfuncs.c:...)
+   by 0x........: main (badfree.c:15)
+   Address 0x........ is on thread 1's stack
+
+ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 2 frees, 0 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/badfree-2trace.vgtest b/memcheck/tests/badfree-2trace.vgtest
new file mode 100644
index 0000000..8a60456
--- /dev/null
+++ b/memcheck/tests/badfree-2trace.vgtest
@@ -0,0 +1,2 @@
+vgopts: --num-callers=2
+prog:   badfree
diff --git a/memcheck/tests/badfree.c b/memcheck/tests/badfree.c
new file mode 100644
index 0000000..3a22567
--- /dev/null
+++ b/memcheck/tests/badfree.c
@@ -0,0 +1,18 @@
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main ( void )
+{
+   void* p = (void*)0x87654321;
+   int q[] = { 1, 2, 3 };
+   
+   /* Free a pointer to Never-Never Land */
+   free(p);
+
+   /* Free a pointer to a stack block */
+   free(q);
+
+   return 0;
+}
diff --git a/memcheck/tests/badfree.stderr.exp b/memcheck/tests/badfree.stderr.exp
new file mode 100644
index 0000000..37c9b3e
--- /dev/null
+++ b/memcheck/tests/badfree.stderr.exp
@@ -0,0 +1,20 @@
+
+Invalid free() / delete / delete[]
+   at 0x........: free (vg_clientfuncs.c:...)
+   by 0x........: main (badfree.c:12)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/badfree)
+   Address 0x........ is not stack'd, malloc'd or free'd
+
+Invalid free() / delete / delete[]
+   at 0x........: free (vg_clientfuncs.c:...)
+   by 0x........: main (badfree.c:15)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/badfree)
+   Address 0x........ is on thread 1's stack
+
+ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 2 frees, 0 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/badfree.vgtest b/memcheck/tests/badfree.vgtest
new file mode 100644
index 0000000..455863a
--- /dev/null
+++ b/memcheck/tests/badfree.vgtest
@@ -0,0 +1 @@
+prog: badfree
diff --git a/memcheck/tests/badjump.c b/memcheck/tests/badjump.c
new file mode 100644
index 0000000..053663b
--- /dev/null
+++ b/memcheck/tests/badjump.c
@@ -0,0 +1,6 @@
+
+int main ( void )
+{
+   char* p = (char*)0xE000000;
+   return ((int(*)(void)) p) ();
+}
diff --git a/memcheck/tests/badjump.stderr.exp b/memcheck/tests/badjump.stderr.exp
new file mode 100644
index 0000000..1be7f70
--- /dev/null
+++ b/memcheck/tests/badjump.stderr.exp
@@ -0,0 +1,6 @@
+
+Jump to the invalid address stated on the next line
+   at 0x........: ???
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: __libc_start_main@@GLIBC_2.0 (...libc...)
+   Address 0x........ is not stack'd, malloc'd or free'd
diff --git a/memcheck/tests/badjump.vgtest b/memcheck/tests/badjump.vgtest
new file mode 100644
index 0000000..1e82b86
--- /dev/null
+++ b/memcheck/tests/badjump.vgtest
@@ -0,0 +1 @@
+prog: badjump
diff --git a/memcheck/tests/badloop.c b/memcheck/tests/badloop.c
new file mode 100644
index 0000000..8780cf7
--- /dev/null
+++ b/memcheck/tests/badloop.c
@@ -0,0 +1,15 @@
+
+#include <stdio.h>
+
+int main ( void )
+{
+   int a[5];
+   int i, s;
+   a[0] = a[1] = a[3] = a[4] = 0;
+   s = 0;
+   for (i = 0; i < 5; i++) 
+      s += a[i];
+   if (s == 377)
+      printf("sum is %d\n", s);
+   return 0;
+}
diff --git a/memcheck/tests/badloop.stderr.exp b/memcheck/tests/badloop.stderr.exp
new file mode 100644
index 0000000..ebfa1c2
--- /dev/null
+++ b/memcheck/tests/badloop.stderr.exp
@@ -0,0 +1,11 @@
+
+Conditional jump or move depends on uninitialised value(s)
+   at 0x........: main (badloop.c:12)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: (within /.../tests/memcheck/badloop)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/badloop.vgtest b/memcheck/tests/badloop.vgtest
new file mode 100644
index 0000000..abd0f39
--- /dev/null
+++ b/memcheck/tests/badloop.vgtest
@@ -0,0 +1 @@
+prog: badloop
diff --git a/memcheck/tests/buflen_check.c b/memcheck/tests/buflen_check.c
new file mode 100644
index 0000000..25f1714
--- /dev/null
+++ b/memcheck/tests/buflen_check.c
@@ -0,0 +1,29 @@
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+int main(void)
+{
+   struct sockaddr name;
+   int res1, res2;
+   int len = 10;
+
+   res1 = socket(PF_UNIX, SOCK_STREAM, 0);
+   if (res1 == 0) {
+      fprintf(stderr, "socket() failed\n");
+      exit(1);
+   }
+
+   /* Valgrind 1.0.X doesn't report the second error */
+   res1 = getsockname(-1, NULL,  &len);    /* NULL is bogus */
+   res2 = getsockname(-1, &name, NULL);    /* NULL is bogus */
+   if (res1 == -1) {
+      fprintf(stderr, "getsockname(1) failed\n");
+   }
+   if (res2 == -1) {
+      fprintf(stderr, "getsockname(2) failed\n");
+   }
+   
+   return 0;
+}
+
diff --git a/memcheck/tests/buflen_check.stderr.exp b/memcheck/tests/buflen_check.stderr.exp
new file mode 100644
index 0000000..a1b9b36
--- /dev/null
+++ b/memcheck/tests/buflen_check.stderr.exp
@@ -0,0 +1,20 @@
+
+Syscall param socketcall.getsockname(name) contains unaddressable byte(s)
+   at 0x........: getsockname (in /...libc...)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: socket@@GLIBC_2.0 (in /.../tests/memcheck/buflen_check)
+   Address 0x........ is not stack'd, malloc'd or free'd
+
+Syscall param socketcall.getsockname(namelen_in) contains uninitialised or unaddressable byte(s)
+   at 0x........: getsockname (in /...libc...)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: socket@@GLIBC_2.0 (in /.../tests/memcheck/buflen_check)
+   Address 0x........ is not stack'd, malloc'd or free'd
+getsockname(1) failed
+getsockname(2) failed
+
+ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/buflen_check.stderr.exp.hd b/memcheck/tests/buflen_check.stderr.exp.hd
new file mode 100644
index 0000000..855f51e
--- /dev/null
+++ b/memcheck/tests/buflen_check.stderr.exp.hd
@@ -0,0 +1,14 @@
+
+Syscall param socketcall.getsockname(name) contains unaddressable byte(s)
+   at 0x........: getsockname (in /...libc...)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: socket@@GLIBC_2.0 (in /.../tests/memcheck/buflen_check)
+   Address 0x........ is not stack'd, malloc'd or free'd
+getsockname(1) failed
+getsockname(2) failed
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/buflen_check.vgtest b/memcheck/tests/buflen_check.vgtest
new file mode 100644
index 0000000..e14c8f1
--- /dev/null
+++ b/memcheck/tests/buflen_check.vgtest
@@ -0,0 +1 @@
+prog: buflen_check
diff --git a/memcheck/tests/doublefree.c b/memcheck/tests/doublefree.c
new file mode 100644
index 0000000..3c27050
--- /dev/null
+++ b/memcheck/tests/doublefree.c
@@ -0,0 +1,12 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main ( void )
+{
+   int i;
+   void* p = malloc(177);
+   for (i = 0; i < 2; i++)
+     free(p);
+   return 0;
+}
diff --git a/memcheck/tests/doublefree.stderr.exp b/memcheck/tests/doublefree.stderr.exp
new file mode 100644
index 0000000..282523f
--- /dev/null
+++ b/memcheck/tests/doublefree.stderr.exp
@@ -0,0 +1,17 @@
+
+Invalid free() / delete / delete[]
+   at 0x........: free (vg_clientfuncs.c:...)
+   by 0x........: main (doublefree.c:10)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/doublefree)
+   Address 0x........ is 0 bytes inside a block of size 177 free'd
+   at 0x........: free (vg_clientfuncs.c:...)
+   by 0x........: main (doublefree.c:10)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/doublefree)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 1 allocs, 2 frees, 177 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/doublefree.vgtest b/memcheck/tests/doublefree.vgtest
new file mode 100644
index 0000000..9c0efac
--- /dev/null
+++ b/memcheck/tests/doublefree.vgtest
@@ -0,0 +1 @@
+prog: doublefree
diff --git a/memcheck/tests/errs1.c b/memcheck/tests/errs1.c
new file mode 100644
index 0000000..754f844
--- /dev/null
+++ b/memcheck/tests/errs1.c
@@ -0,0 +1,17 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+
+char* p;
+
+void ddd  ( void ) { p[-1] += 'z'; }
+void ccc  ( void ) { ddd(); }
+void bbb  ( void ) { ccc(); }
+void aaa  ( void ) { bbb(); }
+
+void zzzzzzz  ( void ) { p = malloc(10); }
+void yyy  ( void ) { zzzzzzz(); }
+void xxx  ( void ) { yyy(); }
+void www  ( void ) { xxx(); }
+
+int main ( void ) { www(); aaa(); return 0; }
diff --git a/memcheck/tests/errs1.stderr.exp b/memcheck/tests/errs1.stderr.exp
new file mode 100644
index 0000000..2de4b48
--- /dev/null
+++ b/memcheck/tests/errs1.stderr.exp
@@ -0,0 +1,28 @@
+
+Invalid read of size 1
+   at 0x........: ddd (errs1.c:7)
+   by 0x........: bbb (errs1.c:9)
+   by 0x........: aaa (errs1.c:10)
+   by 0x........: main (errs1.c:17)
+   Address 0x........ is 1 bytes before a block of size 10 alloc'd
+   at 0x........: malloc (vg_clientfuncs.c:...)
+   by 0x........: zzzzzzz (errs1.c:12)
+   by 0x........: yyy (errs1.c:13)
+   by 0x........: xxx (errs1.c:14)
+
+Invalid write of size 1
+   at 0x........: ddd (errs1.c:7)
+   by 0x........: bbb (errs1.c:9)
+   by 0x........: aaa (errs1.c:10)
+   by 0x........: main (errs1.c:17)
+   Address 0x........ is 1 bytes before a block of size 10 alloc'd
+   at 0x........: malloc (vg_clientfuncs.c:...)
+   by 0x........: zzzzzzz (errs1.c:12)
+   by 0x........: yyy (errs1.c:13)
+   by 0x........: xxx (errs1.c:14)
+
+ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 10 bytes in 1 blocks.
+malloc/free: 1 allocs, 0 frees, 10 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/errs1.vgtest b/memcheck/tests/errs1.vgtest
new file mode 100644
index 0000000..fbe7c76
--- /dev/null
+++ b/memcheck/tests/errs1.vgtest
@@ -0,0 +1 @@
+prog: errs1
diff --git a/memcheck/tests/exitprog.c b/memcheck/tests/exitprog.c
new file mode 100644
index 0000000..3067216
--- /dev/null
+++ b/memcheck/tests/exitprog.c
@@ -0,0 +1,16 @@
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define ZILLION 1000000
+
+int main ( void )
+{
+   int i;
+   char* a = malloc(ZILLION * sizeof(char));
+   for (i = 0; i <= ZILLION; i++) a[i] = 0;
+   a = (char*)177;
+   _exit(1);
+}
diff --git a/memcheck/tests/exitprog.stderr.exp b/memcheck/tests/exitprog.stderr.exp
new file mode 100644
index 0000000..1b30fe0
--- /dev/null
+++ b/memcheck/tests/exitprog.stderr.exp
@@ -0,0 +1,16 @@
+
+Invalid write of size 1
+   at 0x........: main (exitprog.c:15)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: (within /.../tests/memcheck/exitprog)
+   Address 0x........ is 0 bytes after a block of size 1000000 alloc'd
+   at 0x........: malloc (vg_clientfuncs.c:...)
+   by 0x........: main (exitprog.c:12)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: (within /.../tests/memcheck/exitprog)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 1000000 bytes in 1 blocks.
+malloc/free: 1 allocs, 0 frees, 1000000 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/exitprog.vgtest b/memcheck/tests/exitprog.vgtest
new file mode 100644
index 0000000..0095028
--- /dev/null
+++ b/memcheck/tests/exitprog.vgtest
@@ -0,0 +1 @@
+prog: exitprog
diff --git a/memcheck/tests/filter_leak_check_size b/memcheck/tests/filter_leak_check_size
new file mode 100755
index 0000000..03def89
--- /dev/null
+++ b/memcheck/tests/filter_leak_check_size
@@ -0,0 +1,4 @@
+#! /bin/sh
+
+./filter_stderr | \
+sed "s/checked [0-9]\+ bytes./checked ... bytes./"
diff --git a/memcheck/tests/filter_stderr b/memcheck/tests/filter_stderr
new file mode 100755
index 0000000..0d5e763
--- /dev/null
+++ b/memcheck/tests/filter_stderr
@@ -0,0 +1,24 @@
+#! /bin/sh
+
+# Skip first four lines (valgrind intro)  
+# XXX: be more clever/subtle; eg. if there's just a 1-line error message
+# don't cut it
+
+dir=`dirname $0`
+
+$dir/../filter_stderr_basic                             |
+
+# Anonymise addresses
+$dir/../filter_addresses                                |
+
+# Anonymise line numbers in vg_clientfuncs.c
+sed "s/vg_clientfuncs.c:[0-9]\+/vg_clientfuncs.c:.../"  |
+
+$dir/../filter_test_paths                               |
+
+# Anonymise paths like "(in /foo/bar/libc-baz.so)"
+sed "s/(in \/.*libc.*)$/(in \/...libc...)/"             |
+
+# Anonymise paths like "__libc_start_main (../foo/bar/libc-quux.c:129)"
+sed "s/__libc_\(.*\) (.*)$/__libc_\1 (...libc...)/"
+
diff --git a/memcheck/tests/fprw.c b/memcheck/tests/fprw.c
new file mode 100644
index 0000000..556d8a0
--- /dev/null
+++ b/memcheck/tests/fprw.c
@@ -0,0 +1,26 @@
+
+/* most of the nasties in this are in the same bb, so you need to run
+with --single-step=yes to get them properly distinguished. */
+
+#include <stdlib.h>
+
+int main ( void )
+{
+   volatile double d;
+   volatile float f;
+   double* dp = malloc(sizeof(double));
+   float* fp = malloc(sizeof(float));
+   int* ip = (int*)0x1234567;
+   d += 1.0;
+   f += 10.0;
+   *dp += 2.0;
+   *fp += 20.0;
+   free(dp);
+   free(fp);
+   *dp += 3.0;
+   *fp += 30.0;
+   free(ip);
+   ip = malloc(sizeof(int));
+   * ((double*)ip) = 1.2 + d;
+   return 0;
+}
diff --git a/memcheck/tests/fprw.stderr.exp b/memcheck/tests/fprw.stderr.exp
new file mode 100644
index 0000000..2137572
--- /dev/null
+++ b/memcheck/tests/fprw.stderr.exp
@@ -0,0 +1,83 @@
+
+Use of uninitialised value of size 8
+   at 0x........: main (fprw.c:14)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+
+Use of uninitialised value of size 4
+   at 0x........: main (fprw.c:15)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+
+Use of uninitialised value of size 8
+   at 0x........: main (fprw.c:16)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+
+Use of uninitialised value of size 4
+   at 0x........: main (fprw.c:17)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+
+Invalid read of size 8
+   at 0x........: main (fprw.c:20)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+   Address 0x........ is 0 bytes inside a block of size 8 free'd
+   at 0x........: free (vg_clientfuncs.c:...)
+   by 0x........: main (fprw.c:18)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+
+Invalid write of size 8
+   at 0x........: main (fprw.c:20)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+   Address 0x........ is 0 bytes inside a block of size 8 free'd
+   at 0x........: free (vg_clientfuncs.c:...)
+   by 0x........: main (fprw.c:18)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+
+Invalid read of size 4
+   at 0x........: main (fprw.c:21)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+   Address 0x........ is 0 bytes inside a block of size 4 free'd
+   at 0x........: free (vg_clientfuncs.c:...)
+   by 0x........: main (fprw.c:19)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+
+Invalid write of size 4
+   at 0x........: main (fprw.c:21)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+   Address 0x........ is 0 bytes inside a block of size 4 free'd
+   at 0x........: free (vg_clientfuncs.c:...)
+   by 0x........: main (fprw.c:19)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+
+Invalid free() / delete / delete[]
+   at 0x........: free (vg_clientfuncs.c:...)
+   by 0x........: main (fprw.c:22)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+   Address 0x........ is not stack'd, malloc'd or free'd
+
+Invalid write of size 8
+   at 0x........: main (fprw.c:24)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+   Address 0x........ is 0 bytes inside a block of size 4 alloc'd
+   at 0x........: malloc (vg_clientfuncs.c:...)
+   by 0x........: main (fprw.c:23)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/fprw)
+
+ERROR SUMMARY: 10 errors from 10 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 4 bytes in 1 blocks.
+malloc/free: 3 allocs, 3 frees, 16 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/fprw.vgtest b/memcheck/tests/fprw.vgtest
new file mode 100644
index 0000000..d44e04a
--- /dev/null
+++ b/memcheck/tests/fprw.vgtest
@@ -0,0 +1,2 @@
+vgopts: --single-step=yes
+prog:   fprw
diff --git a/memcheck/tests/fwrite.c b/memcheck/tests/fwrite.c
new file mode 100644
index 0000000..1eec4a4
--- /dev/null
+++ b/memcheck/tests/fwrite.c
@@ -0,0 +1,9 @@
+
+#include <stdlib.h>
+#include <unistd.h>
+int main ( void )
+{
+   char* arr = malloc(10);
+   (void) write( 1 /* stdout */, arr, 10 );
+   return 0;
+}
diff --git a/memcheck/tests/fwrite.stderr.exp b/memcheck/tests/fwrite.stderr.exp
new file mode 100644
index 0000000..9c26de2
--- /dev/null
+++ b/memcheck/tests/fwrite.stderr.exp
@@ -0,0 +1,16 @@
+
+Syscall param write(buf) contains uninitialised or unaddressable byte(s)
+   at 0x........: __libc_write (...libc...)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: __libc_start_main@@GLIBC_2.0 (...libc...)
+   Address 0x........ is 0 bytes inside a block of size 10 alloc'd
+   at 0x........: malloc (vg_clientfuncs.c:...)
+   by 0x........: main (fwrite.c:6)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: __libc_start_main@@GLIBC_2.0 (...libc...)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 10 bytes in 1 blocks.
+malloc/free: 1 allocs, 0 frees, 10 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/fwrite.stdout.exp b/memcheck/tests/fwrite.stdout.exp
new file mode 100644
index 0000000..cb43b5c
--- /dev/null
+++ b/memcheck/tests/fwrite.stdout.exp
Binary files differ
diff --git a/memcheck/tests/fwrite.vgtest b/memcheck/tests/fwrite.vgtest
new file mode 100644
index 0000000..f43efd0
--- /dev/null
+++ b/memcheck/tests/fwrite.vgtest
@@ -0,0 +1 @@
+prog: fwrite
diff --git a/memcheck/tests/inits.c b/memcheck/tests/inits.c
new file mode 100644
index 0000000..7dd0c93
--- /dev/null
+++ b/memcheck/tests/inits.c
@@ -0,0 +1,20 @@
+
+#include <stdio.h>
+
+/* Static and global vars are inited to zero, non-static local vars aren't. */
+
+int        g;
+static int gs;
+
+int main(void)
+{
+   int        l;
+   static int ls;
+   
+   if (gs == 0xDEADBEEF) printf("1!\n");
+   if (g  == 0xDEADBEEF) printf("2!\n");
+   if (ls == 0xDEADBEEF) printf("3!\n");
+   if (l  == 0xDEADBEEF) printf("4!\n");  // complains
+   
+   return 0;
+}
diff --git a/memcheck/tests/inits.stderr.exp b/memcheck/tests/inits.stderr.exp
new file mode 100644
index 0000000..e703ced
--- /dev/null
+++ b/memcheck/tests/inits.stderr.exp
@@ -0,0 +1,11 @@
+
+Conditional jump or move depends on uninitialised value(s)
+   at 0x........: main (inits.c:17)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: (within /.../tests/memcheck/inits)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/inits.vgtest b/memcheck/tests/inits.vgtest
new file mode 100644
index 0000000..e654dc6
--- /dev/null
+++ b/memcheck/tests/inits.vgtest
@@ -0,0 +1 @@
+prog: inits
diff --git a/memcheck/tests/inline.c b/memcheck/tests/inline.c
new file mode 100644
index 0000000..cb023b2
--- /dev/null
+++ b/memcheck/tests/inline.c
@@ -0,0 +1,21 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+
+__inline__
+static int addemup ( int* arr )
+{
+   int i, j = 0;
+   for (i = 0; i <= 10; i++)
+      j += arr[i];
+   return j;
+}
+
+int main ( void )
+{
+   int sum;
+   int* a = calloc(10, sizeof(int));
+   sum = addemup(a);
+   printf("sum is %d\n", sum);
+   return 0;
+}
diff --git a/memcheck/tests/inline.stderr.exp b/memcheck/tests/inline.stderr.exp
new file mode 100644
index 0000000..a2225c1
--- /dev/null
+++ b/memcheck/tests/inline.stderr.exp
@@ -0,0 +1,17 @@
+
+Invalid read of size 4
+   at 0x........: addemup (inline.c:10)
+   by 0x........: main (inline.c:18)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: calloc@@GLIBC_2.0 (in /.../tests/memcheck/inline)
+   Address 0x........ is 0 bytes after a block of size 40 alloc'd
+   at 0x........: calloc (vg_clientfuncs.c:...)
+   by 0x........: main (inline.c:17)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: calloc@@GLIBC_2.0 (in /.../tests/memcheck/inline)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 40 bytes in 1 blocks.
+malloc/free: 1 allocs, 0 frees, 40 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/inline.stdout.exp b/memcheck/tests/inline.stdout.exp
new file mode 100644
index 0000000..ad1401e
--- /dev/null
+++ b/memcheck/tests/inline.stdout.exp
@@ -0,0 +1 @@
+sum is 0
diff --git a/memcheck/tests/inline.vgtest b/memcheck/tests/inline.vgtest
new file mode 100644
index 0000000..89673b1
--- /dev/null
+++ b/memcheck/tests/inline.vgtest
@@ -0,0 +1 @@
+prog: inline
diff --git a/memcheck/tests/malloc1.c b/memcheck/tests/malloc1.c
new file mode 100644
index 0000000..dff5250
--- /dev/null
+++ b/memcheck/tests/malloc1.c
@@ -0,0 +1,24 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+
+void really ( void );
+
+int main ( void )
+{ 
+   really();
+   return 0;
+}
+
+void really ( void )
+{
+   int i;
+   char* p = malloc(10);
+   for (i = 0; i < 10; i++)
+      p[i] = 'z';
+   free(p);
+   p[1] = 'z';
+   p = malloc(10);
+   p[2] = 'z';
+   p[-1] = 'z';
+}
diff --git a/memcheck/tests/malloc1.stderr.exp b/memcheck/tests/malloc1.stderr.exp
new file mode 100644
index 0000000..1e4c67f
--- /dev/null
+++ b/memcheck/tests/malloc1.stderr.exp
@@ -0,0 +1,28 @@
+
+Invalid write of size 1
+   at 0x........: really (malloc1.c:20)
+   by 0x........: main (malloc1.c:10)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/malloc1)
+   Address 0x........ is 1 bytes inside a block of size 10 free'd
+   at 0x........: free (vg_clientfuncs.c:...)
+   by 0x........: really (malloc1.c:19)
+   by 0x........: main (malloc1.c:10)
+   by 0x........: __libc_start_main (...libc...)
+
+Invalid write of size 1
+   at 0x........: really (malloc1.c:23)
+   by 0x........: main (malloc1.c:10)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/malloc1)
+   Address 0x........ is 1 bytes before a block of size 10 alloc'd
+   at 0x........: malloc (vg_clientfuncs.c:...)
+   by 0x........: really (malloc1.c:21)
+   by 0x........: main (malloc1.c:10)
+   by 0x........: __libc_start_main (...libc...)
+
+ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 10 bytes in 1 blocks.
+malloc/free: 2 allocs, 1 frees, 20 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/malloc1.vgtest b/memcheck/tests/malloc1.vgtest
new file mode 100644
index 0000000..43c402a
--- /dev/null
+++ b/memcheck/tests/malloc1.vgtest
@@ -0,0 +1 @@
+prog: malloc1
diff --git a/memcheck/tests/malloc2.c b/memcheck/tests/malloc2.c
new file mode 100644
index 0000000..44cc7bb
--- /dev/null
+++ b/memcheck/tests/malloc2.c
@@ -0,0 +1,49 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/* The original test driver machinery. */
+#define N_TEST_TRANSACTIONS 500
+#define N_TEST_ARR 2000
+
+#define M_TEST_MALLOC 1000
+
+void* test_arr[N_TEST_ARR];
+
+int main ( int argc, char** argv )
+{
+   int i, j, k, nbytes;
+   unsigned char* chp;
+
+   for (i = 0; i < N_TEST_ARR; i++)
+      test_arr[i] = NULL;
+
+   for (i = 0; i < N_TEST_TRANSACTIONS; i++) {
+      j = random() % N_TEST_ARR;
+      if (test_arr[j]) {
+         free(test_arr[j]);
+         test_arr[j] = NULL;
+      } else {
+         nbytes = 1 + random() % M_TEST_MALLOC;
+         if (random()%64 == 32) 
+            nbytes *= 17;
+         test_arr[j] = malloc( nbytes );
+         chp = test_arr[j];
+         for (k = 1; k < nbytes; k++) 
+            chp[k] = (unsigned char)(k + 99);
+      }
+   }
+
+   for (i = 0; test_arr[i] == NULL; i++) ;
+   free(test_arr[i]);
+   ((char*)test_arr[i])[0] = 0;
+
+   for (i = 0; i < N_TEST_ARR; i++) {
+      if (test_arr[i]) {
+         free(test_arr[i]);
+         test_arr[i] = NULL;
+      }
+   }
+
+   return 0;
+}
diff --git a/memcheck/tests/malloc2.stderr.exp b/memcheck/tests/malloc2.stderr.exp
new file mode 100644
index 0000000..dd86b2d
--- /dev/null
+++ b/memcheck/tests/malloc2.stderr.exp
@@ -0,0 +1,27 @@
+
+Invalid write of size 1
+   at 0x........: main (malloc2.c:39)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/malloc2)
+   Address 0x........ is 0 bytes inside a block of size 429 free'd
+   at 0x........: free (vg_clientfuncs.c:...)
+   by 0x........: main (malloc2.c:38)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/malloc2)
+
+Invalid free() / delete / delete[]
+   at 0x........: free (vg_clientfuncs.c:...)
+   by 0x........: main (malloc2.c:43)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/malloc2)
+   Address 0x........ is 0 bytes inside a block of size 429 free'd
+   at 0x........: free (vg_clientfuncs.c:...)
+   by 0x........: main (malloc2.c:38)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: free@@GLIBC_2.0 (in /.../tests/memcheck/malloc2)
+
+ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 443 allocs, 444 frees, 265463 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/malloc2.vgtest b/memcheck/tests/malloc2.vgtest
new file mode 100644
index 0000000..d2dd1b4
--- /dev/null
+++ b/memcheck/tests/malloc2.vgtest
@@ -0,0 +1 @@
+prog: malloc2
diff --git a/memcheck/tests/manuel1.c b/memcheck/tests/manuel1.c
new file mode 100644
index 0000000..ac1f3c8
--- /dev/null
+++ b/memcheck/tests/manuel1.c
@@ -0,0 +1,10 @@
+#include <stdio.h>
+
+int main ()
+{
+  int x;
+
+  printf ("x = %d\n", x==0xDEADBEEF ? 99 : 88);
+
+  return 0;
+}
diff --git a/memcheck/tests/manuel1.stderr.exp b/memcheck/tests/manuel1.stderr.exp
new file mode 100644
index 0000000..c674937
--- /dev/null
+++ b/memcheck/tests/manuel1.stderr.exp
@@ -0,0 +1,11 @@
+
+Conditional jump or move depends on uninitialised value(s)
+   at 0x........: main (manuel1.c:7)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: (within /.../tests/memcheck/manuel1)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/manuel1.stdout.exp b/memcheck/tests/manuel1.stdout.exp
new file mode 100644
index 0000000..d26cbc9
--- /dev/null
+++ b/memcheck/tests/manuel1.stdout.exp
@@ -0,0 +1 @@
+x = 88
diff --git a/memcheck/tests/manuel1.vgtest b/memcheck/tests/manuel1.vgtest
new file mode 100644
index 0000000..e3ad9c7
--- /dev/null
+++ b/memcheck/tests/manuel1.vgtest
@@ -0,0 +1 @@
+prog: manuel1
diff --git a/memcheck/tests/manuel2.c b/memcheck/tests/manuel2.c
new file mode 100644
index 0000000..3b7135e
--- /dev/null
+++ b/memcheck/tests/manuel2.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include <malloc.h>
+
+int main ()
+{
+  int *x;
+
+  printf ("x = %d\n", *x==0xDEADBEEF ? 99 : 88);
+
+  return 0;
+}
diff --git a/memcheck/tests/manuel2.stderr.exp b/memcheck/tests/manuel2.stderr.exp
new file mode 100644
index 0000000..55ff720
--- /dev/null
+++ b/memcheck/tests/manuel2.stderr.exp
@@ -0,0 +1,11 @@
+
+Use of uninitialised value of size 4
+   at 0x........: main (manuel2.c:8)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: (within /.../tests/memcheck/manuel2)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/manuel2.stdout.exp b/memcheck/tests/manuel2.stdout.exp
new file mode 100644
index 0000000..d26cbc9
--- /dev/null
+++ b/memcheck/tests/manuel2.stdout.exp
@@ -0,0 +1 @@
+x = 88
diff --git a/memcheck/tests/manuel2.vgtest b/memcheck/tests/manuel2.vgtest
new file mode 100644
index 0000000..1c785a5
--- /dev/null
+++ b/memcheck/tests/manuel2.vgtest
@@ -0,0 +1 @@
+prog: manuel2
diff --git a/memcheck/tests/manuel3.c b/memcheck/tests/manuel3.c
new file mode 100644
index 0000000..ea98fa9
--- /dev/null
+++ b/memcheck/tests/manuel3.c
@@ -0,0 +1,28 @@
+#include <stdio.h>
+#include <malloc.h>
+
+int gcc_cant_inline_me ( int );
+
+int main ()
+{
+  int *x, y;
+
+  x = (int *) malloc (sizeof (int));
+
+  y = *x == 173;
+
+  if (gcc_cant_inline_me(y)) { } 
+
+  return 0;
+}
+
+/* must be AFTER main */
+int gcc_cant_inline_me ( int n )
+{
+   if (n == 42) 
+      return 1; /* forty-two, dudes! */
+   else
+      return 0; /* some other number, dudes! */
+}
+
+
diff --git a/memcheck/tests/manuel3.stderr.exp b/memcheck/tests/manuel3.stderr.exp
new file mode 100644
index 0000000..7a257c3
--- /dev/null
+++ b/memcheck/tests/manuel3.stderr.exp
@@ -0,0 +1,12 @@
+
+Conditional jump or move depends on uninitialised value(s)
+   at 0x........: gcc_cant_inline_me (manuel3.c:22)
+   by 0x........: main (manuel3.c:14)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: __libc_start_main@@GLIBC_2.0 (...libc...)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 4 bytes in 1 blocks.
+malloc/free: 1 allocs, 0 frees, 4 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/manuel3.vgtest b/memcheck/tests/manuel3.vgtest
new file mode 100644
index 0000000..0481cc6
--- /dev/null
+++ b/memcheck/tests/manuel3.vgtest
@@ -0,0 +1 @@
+prog: manuel3
diff --git a/memcheck/tests/memalign_test.c b/memcheck/tests/memalign_test.c
new file mode 100644
index 0000000..a24808c
--- /dev/null
+++ b/memcheck/tests/memalign_test.c
@@ -0,0 +1,19 @@
+
+#include <stdlib.h>
+#include <stdio.h>
+
+int main ( void )
+{
+  void* a[10];
+  int i;
+  for (i = 0; i < 10; i++) {
+    a[i] = valloc(11111 * (i+1));
+    //    printf("I acquire %p\n", a[i]);
+  }
+  for (i = 0; i < 10; i++) {
+    //    printf("I release %p\n", a[i]);
+    free(a[i]);
+  }
+  free(a[9]);
+  return 0;
+}
diff --git a/memcheck/tests/memalign_test.stderr.exp b/memcheck/tests/memalign_test.stderr.exp
new file mode 100644
index 0000000..4725928
--- /dev/null
+++ b/memcheck/tests/memalign_test.stderr.exp
@@ -0,0 +1,17 @@
+
+Invalid free() / delete / delete[]
+   at 0x........: free (vg_clientfuncs.c:...)
+   by 0x........: main (memalign_test.c:17)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: valloc@@GLIBC_2.0 (in /.../tests/memcheck/memalign_test)
+   Address 0x........ is 0 bytes inside a block of size 111110 free'd
+   at 0x........: free (vg_clientfuncs.c:...)
+   by 0x........: main (memalign_test.c:15)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: valloc@@GLIBC_2.0 (in /.../tests/memcheck/memalign_test)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 10 allocs, 11 frees, 611105 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/memalign_test.vgtest b/memcheck/tests/memalign_test.vgtest
new file mode 100644
index 0000000..56b601c
--- /dev/null
+++ b/memcheck/tests/memalign_test.vgtest
@@ -0,0 +1 @@
+prog: memalign_test
diff --git a/memcheck/tests/memcmptest.c b/memcheck/tests/memcmptest.c
new file mode 100644
index 0000000..83eb2d4
--- /dev/null
+++ b/memcheck/tests/memcmptest.c
@@ -0,0 +1,20 @@
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+char* s1;
+char* s2;
+
+int main ( void )
+{
+  s1 = malloc(10); strcpy(s1,"fooble");
+  s2 = malloc(10); strcpy(s2,"fooble");
+  if (memcmp(s1, s2, 8) != 0)
+    printf("different\n");
+  else
+    printf("same (?!)\n");
+  return 0;
+}
+
+	
diff --git a/memcheck/tests/memcmptest.stderr.exp b/memcheck/tests/memcmptest.stderr.exp
new file mode 100644
index 0000000..d7b1c3a
--- /dev/null
+++ b/memcheck/tests/memcmptest.stderr.exp
@@ -0,0 +1,16 @@
+
+Conditional jump or move depends on uninitialised value(s)
+   at 0x........: memcmp (in /...libc...)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: (within /.../tests/memcheck/memcmptest)
+
+Conditional jump or move depends on uninitialised value(s)
+   at 0x........: memcmp (in /...libc...)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: (within /.../tests/memcheck/memcmptest)
+
+ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 20 bytes in 2 blocks.
+malloc/free: 2 allocs, 0 frees, 20 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/memcmptest.stdout.exp b/memcheck/tests/memcmptest.stdout.exp
new file mode 100644
index 0000000..7164804
--- /dev/null
+++ b/memcheck/tests/memcmptest.stdout.exp
@@ -0,0 +1 @@
+same (?!)
diff --git a/memcheck/tests/memcmptest.vgtest b/memcheck/tests/memcmptest.vgtest
new file mode 100644
index 0000000..f31a8f5
--- /dev/null
+++ b/memcheck/tests/memcmptest.vgtest
@@ -0,0 +1 @@
+prog: memcmptest
diff --git a/memcheck/tests/mismatches.cpp b/memcheck/tests/mismatches.cpp
new file mode 100644
index 0000000..857a075
--- /dev/null
+++ b/memcheck/tests/mismatches.cpp
@@ -0,0 +1,27 @@
+#include <stdlib.h>
+
+int main()
+{
+  int* fpointer = (int*)malloc(10);
+  delete fpointer;          // should give warning
+  fpointer = (int*)malloc(10);
+  delete [] fpointer;       // should give warning
+  fpointer = (int*)malloc(10);
+  free (fpointer);          // should work!
+
+  int* nvec = new int[10];
+  delete nvec;              // should give a warning
+  nvec = new int[10];
+  free (nvec);              // should give a warning
+  nvec = new int[10];
+  delete [] nvec;           // should work!
+
+  int* n = new int;
+  delete [] n;              // should give a warning
+  n = new int;
+  free(n);                  // should give a warning
+  n = new int;
+  delete n;                 // should work!
+
+  return 0;
+}
diff --git a/memcheck/tests/mismatches.stderr.exp b/memcheck/tests/mismatches.stderr.exp
new file mode 100644
index 0000000..caf65dc
--- /dev/null
+++ b/memcheck/tests/mismatches.stderr.exp
@@ -0,0 +1,72 @@
+
+Mismatched free() / delete / delete []
+   at 0x........: __builtin_delete (vg_clientfuncs.c:...)
+   by 0x........: main (mismatches.cpp:6)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: __builtin_new (in /.../tests/memcheck/mismatches)
+   Address 0x........ is 0 bytes inside a block of size 10 alloc'd
+   at 0x........: malloc (vg_clientfuncs.c:...)
+   by 0x........: main (mismatches.cpp:5)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: __builtin_new (in /.../tests/memcheck/mismatches)
+
+Mismatched free() / delete / delete []
+   at 0x........: __builtin_vec_delete (vg_clientfuncs.c:...)
+   by 0x........: main (mismatches.cpp:8)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: __builtin_new (in /.../tests/memcheck/mismatches)
+   Address 0x........ is 0 bytes inside a block of size 10 alloc'd
+   at 0x........: malloc (vg_clientfuncs.c:...)
+   by 0x........: main (mismatches.cpp:7)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: __builtin_new (in /.../tests/memcheck/mismatches)
+
+Mismatched free() / delete / delete []
+   at 0x........: __builtin_delete (vg_clientfuncs.c:...)
+   by 0x........: main (mismatches.cpp:13)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: __builtin_new (in /.../tests/memcheck/mismatches)
+   Address 0x........ is 0 bytes inside a block of size 40 alloc'd
+   at 0x........: __builtin_vec_new (vg_clientfuncs.c:...)
+   by 0x........: main (mismatches.cpp:12)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: __builtin_new (in /.../tests/memcheck/mismatches)
+
+Mismatched free() / delete / delete []
+   at 0x........: free (vg_clientfuncs.c:...)
+   by 0x........: main (mismatches.cpp:15)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: __builtin_new (in /.../tests/memcheck/mismatches)
+   Address 0x........ is 0 bytes inside a block of size 40 alloc'd
+   at 0x........: __builtin_vec_new (vg_clientfuncs.c:...)
+   by 0x........: main (mismatches.cpp:14)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: __builtin_new (in /.../tests/memcheck/mismatches)
+
+Mismatched free() / delete / delete []
+   at 0x........: __builtin_vec_delete (vg_clientfuncs.c:...)
+   by 0x........: main (mismatches.cpp:20)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: __builtin_new (in /.../tests/memcheck/mismatches)
+   Address 0x........ is 0 bytes inside a block of size 4 alloc'd
+   at 0x........: __builtin_new (vg_clientfuncs.c:...)
+   by 0x........: main (mismatches.cpp:19)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: __builtin_new (in /.../tests/memcheck/mismatches)
+
+Mismatched free() / delete / delete []
+   at 0x........: free (vg_clientfuncs.c:...)
+   by 0x........: main (mismatches.cpp:22)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: __builtin_new (in /.../tests/memcheck/mismatches)
+   Address 0x........ is 0 bytes inside a block of size 4 alloc'd
+   at 0x........: __builtin_new (vg_clientfuncs.c:...)
+   by 0x........: main (mismatches.cpp:21)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: __builtin_new (in /.../tests/memcheck/mismatches)
+
+ERROR SUMMARY: 6 errors from 6 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 9 allocs, 9 frees, 162 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/mismatches.vgtest b/memcheck/tests/mismatches.vgtest
new file mode 100644
index 0000000..3a87ef0
--- /dev/null
+++ b/memcheck/tests/mismatches.vgtest
@@ -0,0 +1 @@
+prog: mismatches
diff --git a/memcheck/tests/mmaptest.c b/memcheck/tests/mmaptest.c
new file mode 100644
index 0000000..74a21ed
--- /dev/null
+++ b/memcheck/tests/mmaptest.c
@@ -0,0 +1,15 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+int main()
+{
+    int fd;
+
+    mkdir("dir", 0777);
+    fd = open("dir", O_RDONLY);
+    mmap(NULL, 4711, PROT_READ, MAP_PRIVATE, fd, 0);
+    return 0;
+}
diff --git a/memcheck/tests/mmaptest.stderr.exp b/memcheck/tests/mmaptest.stderr.exp
new file mode 100644
index 0000000..c4aa6f0
--- /dev/null
+++ b/memcheck/tests/mmaptest.stderr.exp
@@ -0,0 +1,7 @@
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/mmaptest.vgtest b/memcheck/tests/mmaptest.vgtest
new file mode 100644
index 0000000..1540c20
--- /dev/null
+++ b/memcheck/tests/mmaptest.vgtest
@@ -0,0 +1 @@
+prog: mmaptest
diff --git a/memcheck/tests/nanoleak.c b/memcheck/tests/nanoleak.c
new file mode 100644
index 0000000..2b7121c
--- /dev/null
+++ b/memcheck/tests/nanoleak.c
@@ -0,0 +1,9 @@
+
+#include <malloc.h>
+
+int main ( void )
+{
+  volatile int* a = malloc(1000);
+  a[0] = 0;
+  return a[0];
+}
diff --git a/memcheck/tests/nanoleak.stderr.exp b/memcheck/tests/nanoleak.stderr.exp
new file mode 100644
index 0000000..8dc3ae7
--- /dev/null
+++ b/memcheck/tests/nanoleak.stderr.exp
@@ -0,0 +1,26 @@
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 1000 bytes in 1 blocks.
+malloc/free: 1 allocs, 0 frees, 1000 bytes allocated.
+For counts of detected errors, rerun with: -v
+searching for pointers to 1 not-freed blocks.
+checked ... bytes.
+
+definitely lost: 1000 bytes in 1 blocks.
+possibly lost:   0 bytes in 0 blocks.
+still reachable: 0 bytes in 0 blocks.
+
+1000 bytes in 1 blocks are definitely lost in loss record 1 of 1
+   at 0x........: malloc (vg_clientfuncs.c:...)
+   by 0x........: main (nanoleak.c:6)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: __libc_start_main@@GLIBC_2.0 (...libc...)
+
+LEAK SUMMARY:
+   definitely lost: 1000 bytes in 1 blocks.
+   possibly lost:   0 bytes in 0 blocks.
+   still reachable: 0 bytes in 0 blocks.
+Reachable blocks (those to which a pointer was found) are not shown.
+To see them, rerun with: --show-reachable=yes
+
diff --git a/memcheck/tests/nanoleak.vgtest b/memcheck/tests/nanoleak.vgtest
new file mode 100644
index 0000000..2fadc98
--- /dev/null
+++ b/memcheck/tests/nanoleak.vgtest
@@ -0,0 +1,3 @@
+vgopts: --leak-check=yes
+prog: nanoleak
+stderr_filter: filter_leak_check_size
diff --git a/memcheck/tests/new_override.cpp b/memcheck/tests/new_override.cpp
new file mode 100644
index 0000000..5b48611
--- /dev/null
+++ b/memcheck/tests/new_override.cpp
@@ -0,0 +1,30 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+class Test {
+public:
+  int a, b, c, d;
+};
+
+void *operator new(size_t size)
+{
+  void *ret = malloc(size);
+  printf("Here.\n");
+  for (unsigned int i = 0; i < size; i++) ((char *) ret)[i] = 0xFF;
+  return ret;
+}
+
+int main(int argc, char *argv[]) {
+  Test *toto;
+  int i;
+  int j = 0;
+
+  toto = new Test[2];
+
+  for (i = 0; i < 2; i++) {
+    if (toto[i].a) {
+      j++;
+    }
+    //printf("%d : %08x %08x %08x %08x\n", i, toto[i].a, toto[i].b, toto[i].c, toto[i].d);
+  }
+}
diff --git a/memcheck/tests/new_override.stderr.exp b/memcheck/tests/new_override.stderr.exp
new file mode 100644
index 0000000..8ba31b6
--- /dev/null
+++ b/memcheck/tests/new_override.stderr.exp
@@ -0,0 +1,11 @@
+
+Conditional jump or move depends on uninitialised value(s)
+   at 0x........: main (new_override.cpp:25)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: (within /.../tests/memcheck/new_override)
+
+ERROR SUMMARY: 2 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 32 bytes in 1 blocks.
+malloc/free: 1 allocs, 0 frees, 32 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/new_override.vgtest b/memcheck/tests/new_override.vgtest
new file mode 100644
index 0000000..4f11a5b
--- /dev/null
+++ b/memcheck/tests/new_override.vgtest
@@ -0,0 +1 @@
+prog: new_override
diff --git a/memcheck/tests/pushfpopf.stderr.exp b/memcheck/tests/pushfpopf.stderr.exp
new file mode 100644
index 0000000..8e10fb2
--- /dev/null
+++ b/memcheck/tests/pushfpopf.stderr.exp
@@ -0,0 +1,12 @@
+
+Conditional jump or move depends on uninitialised value(s)
+   at 0x........: fooble (in /.../tests/memcheck/pushfpopf)
+   by 0x........: main (pushfpopf_c.c:12)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: (within /.../tests/memcheck/pushfpopf)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/pushfpopf.stdout.exp b/memcheck/tests/pushfpopf.stdout.exp
new file mode 100644
index 0000000..180f871
--- /dev/null
+++ b/memcheck/tests/pushfpopf.stdout.exp
@@ -0,0 +1 @@
+fooble: result is 22
diff --git a/memcheck/tests/pushfpopf.vgtest b/memcheck/tests/pushfpopf.vgtest
new file mode 100644
index 0000000..f87b791
--- /dev/null
+++ b/memcheck/tests/pushfpopf.vgtest
@@ -0,0 +1 @@
+prog: pushfpopf
diff --git a/memcheck/tests/pushfpopf_c.c b/memcheck/tests/pushfpopf_c.c
new file mode 100644
index 0000000..f45271e
--- /dev/null
+++ b/memcheck/tests/pushfpopf_c.c
@@ -0,0 +1,14 @@
+
+#include <stdio.h>
+
+// in pushfpopf.s
+extern int fooble ( int, int );
+
+int main ( void )
+{
+   int arr[2];
+   arr[0] = 3;
+   //   arr[1] = 45;
+   printf("fooble: result is %d\n", fooble(arr[0], arr[1]));
+   return 0;
+}
diff --git a/memcheck/tests/pushfpopf_s.s b/memcheck/tests/pushfpopf_s.s
new file mode 100644
index 0000000..f140995
--- /dev/null
+++ b/memcheck/tests/pushfpopf_s.s
@@ -0,0 +1,38 @@
+	.file	"twoparams.c"
+	.version	"01.01"
+gcc2_compiled.:
+.text
+	.align 4
+.globl fooble
+	.type	 fooble,@function
+fooble:
+	pushl	%ebp
+	movl	%esp, %ebp
+	movl	8(%ebp), %eax
+	
+	subl	12(%ebp), %eax
+	# flags are now undef if either operand is
+	# save possibly undef flags on stack
+	pushfl
+	
+	movl	$0, %eax
+	addl	$0, %eax
+	# flags are now definitely defined
+
+	popfl
+	# resulting flag definedness depends on outcome of sub above
+	# should override that created by 0 + 0 above
+	
+	# now use the condition codes to generate a value
+	# in a way which will cause undefinedness to get reported
+	jz	labelz
+	movl	$22, %eax
+	jmp	theend
+labelz:
+	movl	$33, %eax
+theend:	
+	popl	%ebp
+	ret
+.Lfe1:
+	.size	 fooble,.Lfe1-fooble
+	.ident	"GCC: (GNU) 2.96 20000731 (Red Hat Linux 7.1 2.96-98)"
diff --git a/memcheck/tests/realloc1.c b/memcheck/tests/realloc1.c
new file mode 100644
index 0000000..a5d1edc
--- /dev/null
+++ b/memcheck/tests/realloc1.c
@@ -0,0 +1,14 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main ( void )
+{
+   int i;
+   char* p = malloc(1);
+   for (i = 2; i < 50; i++) {
+      p = realloc(p, i);
+      p[i-1] = 'z';
+   }
+   return 0;
+}
diff --git a/memcheck/tests/realloc1.stderr.exp b/memcheck/tests/realloc1.stderr.exp
new file mode 100644
index 0000000..14ec594
--- /dev/null
+++ b/memcheck/tests/realloc1.stderr.exp
@@ -0,0 +1,7 @@
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 49 bytes in 1 blocks.
+malloc/free: 49 allocs, 48 frees, 1225 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/realloc1.vgtest b/memcheck/tests/realloc1.vgtest
new file mode 100644
index 0000000..d0d10d7
--- /dev/null
+++ b/memcheck/tests/realloc1.vgtest
@@ -0,0 +1 @@
+prog: realloc1
diff --git a/memcheck/tests/realloc2.c b/memcheck/tests/realloc2.c
new file mode 100644
index 0000000..c89ff8a
--- /dev/null
+++ b/memcheck/tests/realloc2.c
@@ -0,0 +1,21 @@
+/* This test demonstrated an obscure bug in malloclists handling caused by
+   multiple blocks hashing to the same list and one being overwritten at
+   realloc time due to bad ordering of the things happening.  Now runs
+   without error. */
+
+#include <malloc.h>
+#include <stdio.h>
+
+int main ( void )
+{
+  char* p;
+  int i;
+  for (i = 0; i < 10000; i++) {
+    p = malloc(10 + 10 * (i % 100));
+    p = realloc(p, 500);
+    p = realloc(p, 600);
+    free(p);
+  }
+  return 0;
+}
+
diff --git a/memcheck/tests/realloc2.stderr.exp b/memcheck/tests/realloc2.stderr.exp
new file mode 100644
index 0000000..8ed8426
--- /dev/null
+++ b/memcheck/tests/realloc2.stderr.exp
@@ -0,0 +1,7 @@
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 30000 allocs, 30000 frees, 16050000 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/realloc2.vgtest b/memcheck/tests/realloc2.vgtest
new file mode 100644
index 0000000..0a28b23
--- /dev/null
+++ b/memcheck/tests/realloc2.vgtest
@@ -0,0 +1 @@
+prog: realloc2
diff --git a/memcheck/tests/sigaltstack.c b/memcheck/tests/sigaltstack.c
new file mode 100644
index 0000000..8f31e68
--- /dev/null
+++ b/memcheck/tests/sigaltstack.c
@@ -0,0 +1,38 @@
+
+
+#include <stdio.h>
+#include <malloc.h>
+#include <signal.h>
+
+void sig_handler(int sig){
+  int var;
+  fprintf(stderr, "caught signal, local var is on %p\n", &var);
+}
+
+
+int main(int argv, char** argc) {
+  int res, i;
+  stack_t sigstk;
+  struct sigaction act;
+  sigstk.ss_sp = (char *)malloc(SIGSTKSZ);
+
+  sigstk.ss_size = SIGSTKSZ;
+  sigstk.ss_flags = 0;
+  fprintf(stderr, "calling sigaltstack, stack base is %p\n", sigstk.ss_sp);
+  if (sigaltstack(&sigstk,0)<0) perror("sigaltstack");
+
+  fprintf(stderr,"setting sigaction\n");
+  act.sa_flags=SA_ONSTACK;
+  act.sa_handler=&sig_handler;
+  res = sigaction(SIGUSR1,&act,0);
+  fprintf(stderr, "res = %d\n", res);
+  fprintf(stderr, "raising the signal\n");
+  raise(SIGUSR1);
+  
+  /* Loop long enough so valgrind has a forced context switch and
+     actually delivers the signal before the thread exits. */
+  for (i = 0; i < 1000000; i++) ;
+
+  fprintf(stderr, "done\n");
+  return 0;
+}
diff --git a/memcheck/tests/sigaltstack.stderr.exp b/memcheck/tests/sigaltstack.stderr.exp
new file mode 100644
index 0000000..ceeb462
--- /dev/null
+++ b/memcheck/tests/sigaltstack.stderr.exp
@@ -0,0 +1,19 @@
+
+calling sigaltstack, stack base is 0x........
+setting sigaction
+Syscall param sigaction(act) contains uninitialised or unaddressable byte(s)
+   at 0x........: __libc_sigaction (...libc...)
+   by 0x........: main (sigaltstack.c:27)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: sigaltstack@@GLIBC_2.0 (in /.../tests/memcheck/sigaltstack)
+   Address 0x........ is on thread 1's stack
+res = 0
+raising the signal
+caught signal, local var is on 0x........
+done
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 8192 bytes in 1 blocks.
+malloc/free: 1 allocs, 0 frees, 8192 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/sigaltstack.vgtest b/memcheck/tests/sigaltstack.vgtest
new file mode 100644
index 0000000..f61eeae
--- /dev/null
+++ b/memcheck/tests/sigaltstack.vgtest
@@ -0,0 +1 @@
+prog: sigaltstack
diff --git a/memcheck/tests/signal2.c b/memcheck/tests/signal2.c
new file mode 100644
index 0000000..a1df705
--- /dev/null
+++ b/memcheck/tests/signal2.c
@@ -0,0 +1,20 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+
+void sig_hdlr ( int signo )
+{
+   printf ( "caught sig segv\n" );
+   exit(1);
+}
+
+int main ( void )
+{
+   printf ( "installing sig handler\n" );
+   signal(SIGSEGV, sig_hdlr);
+   printf ( "doing bad thing\n" );
+   * (int*) 65536 = 0;
+   printf ( "exited normally ?!\n" );
+   return 0;
+}
diff --git a/memcheck/tests/signal2.stderr.exp b/memcheck/tests/signal2.stderr.exp
new file mode 100644
index 0000000..3ab7302
--- /dev/null
+++ b/memcheck/tests/signal2.stderr.exp
@@ -0,0 +1,12 @@
+
+Invalid write of size 4
+   at 0x........: main (signal2.c:17)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: exit@@GLIBC_2.0 (in /.../tests/memcheck/signal2)
+   Address 0x........ is not stack'd, malloc'd or free'd
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/signal2.stderr.exp.hd b/memcheck/tests/signal2.stderr.exp.hd
new file mode 100644
index 0000000..2cd1fc8
--- /dev/null
+++ b/memcheck/tests/signal2.stderr.exp.hd
@@ -0,0 +1,12 @@
+
+Invalid write of size 4
+   at 0x........: main (signal2.c:16)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: exit@@GLIBC_2.0 (in /.../tests/memcheck/signal2)
+   Address 0x........ is not stack'd, malloc'd or free'd
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/signal2.stdout.exp b/memcheck/tests/signal2.stdout.exp
new file mode 100644
index 0000000..3e16af0
--- /dev/null
+++ b/memcheck/tests/signal2.stdout.exp
@@ -0,0 +1,3 @@
+installing sig handler
+doing bad thing
+caught sig segv
diff --git a/memcheck/tests/signal2.vgtest b/memcheck/tests/signal2.vgtest
new file mode 100644
index 0000000..c301370
--- /dev/null
+++ b/memcheck/tests/signal2.vgtest
@@ -0,0 +1 @@
+prog: signal2
diff --git a/memcheck/tests/supp.c b/memcheck/tests/supp.c
new file mode 100644
index 0000000..50c4a81
--- /dev/null
+++ b/memcheck/tests/supp.c
@@ -0,0 +1,12 @@
+#include <stdlib.h>
+
+int
+main ()
+{
+  int x;
+
+  if (x == 0)
+     return 0;
+  else
+     return 1;
+}
diff --git a/memcheck/tests/supp.supp b/memcheck/tests/supp.supp
new file mode 100644
index 0000000..477c6c4
--- /dev/null
+++ b/memcheck/tests/supp.supp
@@ -0,0 +1,6 @@
+{
+  name_of_this_suppression
+  Cond
+  obj:*supp1
+  fun:__libc_start_main
+}
diff --git a/memcheck/tests/supp1.stderr.exp b/memcheck/tests/supp1.stderr.exp
new file mode 100644
index 0000000..6d763a7
--- /dev/null
+++ b/memcheck/tests/supp1.stderr.exp
@@ -0,0 +1,7 @@
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/supp1.vgtest b/memcheck/tests/supp1.vgtest
new file mode 100644
index 0000000..31130ff
--- /dev/null
+++ b/memcheck/tests/supp1.vgtest
@@ -0,0 +1,2 @@
+vgopts: --suppressions=supp.supp
+prog: supp1
diff --git a/memcheck/tests/supp2.stderr.exp b/memcheck/tests/supp2.stderr.exp
new file mode 100644
index 0000000..b245f04
--- /dev/null
+++ b/memcheck/tests/supp2.stderr.exp
@@ -0,0 +1,11 @@
+
+Conditional jump or move depends on uninitialised value(s)
+   at 0x........: main (supp.c:8)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: __libc_start_main@@GLIBC_2.0 (...libc...)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/supp2.vgtest b/memcheck/tests/supp2.vgtest
new file mode 100644
index 0000000..f5200b0
--- /dev/null
+++ b/memcheck/tests/supp2.vgtest
@@ -0,0 +1,2 @@
+vgopts: --suppressions=supp.supp
+prog: supp2
diff --git a/memcheck/tests/suppfree.c b/memcheck/tests/suppfree.c
new file mode 100644
index 0000000..8298f02
--- /dev/null
+++ b/memcheck/tests/suppfree.c
@@ -0,0 +1,30 @@
+
+#include <stdlib.h>
+
+void ddd ( char* x )
+{
+  free(x);
+  free(x);
+}
+
+void ccc (char* x)
+{
+  ddd(x);
+}
+
+void bbb (char* x)
+{
+  ccc(x);
+}
+
+void aaa (char* x)
+{
+  bbb(x);
+}
+
+int main ( void )
+{
+   char* x = malloc(10);
+   aaa(x);
+   return 0;
+}
diff --git a/memcheck/tests/suppfree.stderr.exp b/memcheck/tests/suppfree.stderr.exp
new file mode 100644
index 0000000..149bf84
--- /dev/null
+++ b/memcheck/tests/suppfree.stderr.exp
@@ -0,0 +1,17 @@
+
+Invalid free() / delete / delete[]
+   at 0x........: free (vg_clientfuncs.c:...)
+   by 0x........: ddd (suppfree.c:7)
+   by 0x........: ccc (suppfree.c:12)
+   by 0x........: bbb (suppfree.c:17)
+   Address 0x........ is 0 bytes inside a block of size 10 free'd
+   at 0x........: free (vg_clientfuncs.c:...)
+   by 0x........: ddd (suppfree.c:6)
+   by 0x........: ccc (suppfree.c:12)
+   by 0x........: bbb (suppfree.c:17)
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 1 allocs, 2 frees, 10 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/suppfree.vgtest b/memcheck/tests/suppfree.vgtest
new file mode 100644
index 0000000..bd38f8a
--- /dev/null
+++ b/memcheck/tests/suppfree.vgtest
@@ -0,0 +1 @@
+prog: suppfree
diff --git a/memcheck/tests/trivialleak.c b/memcheck/tests/trivialleak.c
new file mode 100644
index 0000000..f3a8963
--- /dev/null
+++ b/memcheck/tests/trivialleak.c
@@ -0,0 +1,14 @@
+#include <stdlib.h>
+
+static void test()
+  {
+    void* leak;
+    int i;
+    for (i = 0; i < 1000; i++)
+       leak = (void*)malloc( 1 );
+  }
+  int main()
+  {
+    test();
+    return 0;
+  }
diff --git a/memcheck/tests/trivialleak.stderr.exp b/memcheck/tests/trivialleak.stderr.exp
new file mode 100644
index 0000000..12bb84b
--- /dev/null
+++ b/memcheck/tests/trivialleak.stderr.exp
@@ -0,0 +1,26 @@
+
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 1000 bytes in 1000 blocks.
+malloc/free: 1000 allocs, 0 frees, 1000 bytes allocated.
+For counts of detected errors, rerun with: -v
+searching for pointers to 1000 not-freed blocks.
+checked ... bytes.
+
+definitely lost: 1000 bytes in 1000 blocks.
+possibly lost:   0 bytes in 0 blocks.
+still reachable: 0 bytes in 0 blocks.
+
+1000 bytes in 1000 blocks are definitely lost in loss record 1 of 1
+   at 0x........: malloc (vg_clientfuncs.c:...)
+   by 0x........: test (trivialleak.c:8)
+   by 0x........: main (trivialleak.c:13)
+   by 0x........: __libc_start_main (...libc...)
+
+LEAK SUMMARY:
+   definitely lost: 1000 bytes in 1000 blocks.
+   possibly lost:   0 bytes in 0 blocks.
+   still reachable: 0 bytes in 0 blocks.
+Reachable blocks (those to which a pointer was found) are not shown.
+To see them, rerun with: --show-reachable=yes
+
diff --git a/memcheck/tests/trivialleak.vgtest b/memcheck/tests/trivialleak.vgtest
new file mode 100644
index 0000000..c5b68a8
--- /dev/null
+++ b/memcheck/tests/trivialleak.vgtest
@@ -0,0 +1,3 @@
+vgopts: --leak-check=yes
+prog: trivialleak
+stderr_filter: filter_leak_check_size
diff --git a/memcheck/tests/tronical.S b/memcheck/tests/tronical.S
new file mode 100644
index 0000000..030a2af
--- /dev/null
+++ b/memcheck/tests/tronical.S
@@ -0,0 +1,102 @@
+/*
+
+Assembly derived from the following program compiled with -O2.
+This fools Valgrind, causing it to give a false error.
+
+#include <stdio.h>
+
+struct Foo
+{
+    int a1 : 1;
+    int a2 : 1;
+    int a3 : 1;
+    int a4 : 1;
+    int a5 : 1;
+    int a6 : 1;
+    int a7 : 1;
+    int bleh : 1;
+};
+
+struct Foo* foo;
+
+void set()
+{
+    foo->bleh = 1;
+}
+
+void get()
+{
+    if ( foo->bleh == 0 )
+        printf( "blieb\n" );
+}
+
+int main()
+{
+  foo = malloc(sizeof(struct Foo));
+    set();
+
+    get();
+
+    return 0;
+}
+
+*/
+
+	.file	"tronical.c"
+	.version	"01.01"
+gcc2_compiled.:
+.text
+	.align 4
+.globl set
+	.type	 set,@function
+set:
+	pushl	%ebp
+	movl	foo, %eax
+	orb	$128, (%eax)
+	movl	%esp, %ebp
+	popl	%ebp
+	ret
+.Lfe1:
+	.size	 set,.Lfe1-set
+	.section	.rodata.str1.1,"ams",@progbits,1
+.LC0:
+	.string	"blieb\n"
+.text
+	.align 4
+.globl get
+	.type	 get,@function
+get:
+	pushl	%ebp
+	movl	%esp, %ebp
+	subl	$8, %esp
+	movl	foo, %eax
+	cmpb	$0, (%eax)
+	js	.L4
+	subl	$12, %esp
+	pushl	$.LC0
+	call	printf
+	addl	$16, %esp
+.L4:
+	leave
+	ret
+.Lfe2:
+	.size	 get,.Lfe2-get
+	.align 4
+.globl main
+	.type	 main,@function
+main:
+	pushl	%ebp
+	movl	%esp, %ebp
+	subl	$20, %esp
+	pushl	$4
+	call	malloc
+	movl	%eax, foo
+	call	set
+	call	get
+	xorl	%eax, %eax
+	leave
+	ret
+.Lfe3:
+	.size	 main,.Lfe3-main
+	.comm	foo,4,4
+	.ident	"GCC: (GNU) 2.96 20000731 (Red Hat Linux 7.1 2.96-98)"
diff --git a/memcheck/tests/tronical.stderr.exp b/memcheck/tests/tronical.stderr.exp
new file mode 100644
index 0000000..2c63087
--- /dev/null
+++ b/memcheck/tests/tronical.stderr.exp
@@ -0,0 +1,7 @@
+
+
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 4 bytes in 1 blocks.
+malloc/free: 1 allocs, 0 frees, 4 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/tronical.vgtest b/memcheck/tests/tronical.vgtest
new file mode 100644
index 0000000..97623ad
--- /dev/null
+++ b/memcheck/tests/tronical.vgtest
@@ -0,0 +1 @@
+prog: tronical
diff --git a/memcheck/tests/weirdioctl.c b/memcheck/tests/weirdioctl.c
new file mode 100644
index 0000000..a78de65
--- /dev/null
+++ b/memcheck/tests/weirdioctl.c
@@ -0,0 +1,44 @@
+
+/* A program which sets a readable fd to have a timeout, and therefore
+   needs --weird-hacks=ioctl-VTIME in order to run without
+   blocking. */
+
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <termio.h>
+
+int main ( void )
+{
+   int c, i;
+   int res;
+         struct termio tty, oldtty;
+
+          /**
+           ** Save the old tty settings, and get rid of echo
+           ** for the new tty settings
+           **/
+          ioctl(0, TCGETA, &oldtty);
+          tty = oldtty;
+          tty.c_lflag    &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
+          tty.c_cc[VMIN]  = 0;
+          tty.c_cc[VTIME] = 5;
+          res = ioctl(0, TCSETA, &tty);
+	  printf("first ioctl returned %d\n", res);
+
+          /**
+           ** Now do whatever stuff you want non-echoed
+           **/
+          i = 0;
+	  while (i++ < 50) {
+	    c = getchar();
+	    printf("got %d\n", c);
+	  }
+
+          /**
+           ** Now reset the old settings
+           **/
+          res = ioctl(0, TCSETA, &oldtty);
+	  printf("second ioctl returned %d\n", res);
+
+return 0;
+}
diff --git a/memcheck/tests/weirdioctl.stderr.exp b/memcheck/tests/weirdioctl.stderr.exp
new file mode 100644
index 0000000..7d5c9aa
--- /dev/null
+++ b/memcheck/tests/weirdioctl.stderr.exp
@@ -0,0 +1,12 @@
+
+Syscall param ioctl(TCSET{A,AW,AF}) contains uninitialised or unaddressable byte(s)
+   at 0x........: __ioctl (in /...libc...)
+   by 0x........: __libc_start_main (...libc...)
+   by 0x........: ioctl@@GLIBC_2.0 (in /.../tests/memcheck/weirdioctl)
+   Address 0x........ is on thread 1's stack
+
+ERROR SUMMARY: 2 errors from 1 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/weirdioctl.stdout.exp b/memcheck/tests/weirdioctl.stdout.exp
new file mode 100644
index 0000000..bb65b7c
--- /dev/null
+++ b/memcheck/tests/weirdioctl.stdout.exp
@@ -0,0 +1,52 @@
+first ioctl returned -1
+got 118
+got 103
+got 111
+got 112
+got 116
+got 115
+got 58
+got 32
+got 45
+got 45
+got 119
+got 101
+got 105
+got 114
+got 100
+got 45
+got 104
+got 97
+got 99
+got 107
+got 115
+got 61
+got 105
+got 111
+got 99
+got 116
+got 108
+got 45
+got 86
+got 84
+got 73
+got 77
+got 69
+got 10
+got 112
+got 114
+got 111
+got 103
+got 58
+got 32
+got 32
+got 32
+got 119
+got 101
+got 105
+got 114
+got 100
+got 105
+got 111
+got 99
+second ioctl returned -1
diff --git a/memcheck/tests/weirdioctl.vgtest b/memcheck/tests/weirdioctl.vgtest
new file mode 100644
index 0000000..e8d8630
--- /dev/null
+++ b/memcheck/tests/weirdioctl.vgtest
@@ -0,0 +1,3 @@
+vgopts: --weird-hacks=ioctl-VTIME
+prog:   weirdioctl
+args:   < weirdioctl.vgtest