Allow overriding not-supported auto-detected cache configs

Patch by Philippe Waroquiers, slightly changed.

This actually was a regression from 3.6.1, but the patch
also improves on printed messages, and refactors common
code between cachegrind and callgrind.

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@12013 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/cachegrind/Makefile.am b/cachegrind/Makefile.am
index 1263653..0b6879c 100644
--- a/cachegrind/Makefile.am
+++ b/cachegrind/Makefile.am
@@ -41,10 +41,11 @@
 
 CACHEGRIND_SOURCES_COMMON = \
 	cg_main.c \
+	cg-arch.c \
 	cg-x86-amd64.c \
 	cg-ppc32.c \
 	cg-ppc64.c \
- 	cg-arm.c   \
+	cg-arm.c   \
 	cg-s390x.c
 
 cachegrind_@VGCONF_ARCH_PRI@_@VGCONF_OS@_SOURCES      = \
diff --git a/cachegrind/cg-arch.c b/cachegrind/cg-arch.c
new file mode 100644
index 0000000..77adfc2
--- /dev/null
+++ b/cachegrind/cg-arch.c
@@ -0,0 +1,208 @@
+/*--------------------------------------------------------------------*/
+/*--- Cachegrind: cache configuration.                             ---*/
+/*--- The architecture specific void VG_(configure_caches) are     ---*/
+/*--- located in the cg-<architecture>.c files.                    ---*/
+/*---                                                    cg-arch.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Cachegrind, a Valgrind tool for cache
+   profiling programs.
+
+   Copyright (C) 2011-2011 Nicholas Nethercote
+      njn@valgrind.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 "pub_tool_basics.h"
+#include "pub_tool_libcassert.h"
+#include "pub_tool_libcbase.h"
+#include "pub_tool_libcprint.h"
+#include "pub_tool_options.h"
+
+#include "cg_arch.h"
+
+// Checks cache config is ok.  Returns NULL if ok, or a pointer to an error
+// string otherwise.
+static Char* check_cache(cache_t* cache)
+{
+   // Simulator requires set count to be a power of two.
+   if ((cache->size % (cache->line_size * cache->assoc) != 0) ||
+       (-1 == VG_(log2)(cache->size/cache->line_size/cache->assoc)))
+   {
+      return "Cache set count is not a power of two.\n";
+   }
+
+   // Simulator requires line size to be a power of two.
+   if (-1 == VG_(log2)(cache->line_size)) {
+      return "Cache line size is not a power of two.\n";
+   }
+
+   // Then check line size >= 16 -- any smaller and a single instruction could
+   // straddle three cache lines, which breaks a simulation assertion and is
+   // stupid anyway.
+   if (cache->line_size < MIN_LINE_SIZE) {
+      return "Cache line size is too small.\n";
+   }
+
+   /* Then check cache size > line size (causes seg faults if not). */
+   if (cache->size <= cache->line_size) {
+      return "Cache size <= line size.\n";
+   }
+
+   /* Then check assoc <= (size / line size) (seg faults otherwise). */
+   if (cache->assoc > (cache->size / cache->line_size)) {
+      return "Cache associativity > (size / line size).\n";
+   }
+
+   return NULL;
+}
+
+
+static void parse_cache_opt ( cache_t* cache, Char* opt, Char* optval )
+{
+   Long i1, i2, i3;
+   Char* endptr;
+   Char* checkRes;
+
+   // Option argument looks like "65536,2,64".  Extract them.
+   i1 = VG_(strtoll10)(optval,   &endptr); if (*endptr != ',')  goto bad;
+   i2 = VG_(strtoll10)(endptr+1, &endptr); if (*endptr != ',')  goto bad;
+   i3 = VG_(strtoll10)(endptr+1, &endptr); if (*endptr != '\0') goto bad;
+
+   // Check for overflow.
+   cache->size      = (Int)i1;
+   cache->assoc     = (Int)i2;
+   cache->line_size = (Int)i3;
+   if (cache->size      != i1) goto overflow;
+   if (cache->assoc     != i2) goto overflow;
+   if (cache->line_size != i3) goto overflow;
+
+   checkRes = check_cache(cache);
+   if (checkRes) {
+      VG_(fmsg)("%s", checkRes);
+      goto bad;
+   }
+
+   return;
+
+  bad:
+   VG_(fmsg_bad_option)(opt, "");
+
+  overflow:
+   VG_(fmsg_bad_option)(opt,
+      "One of the cache parameters was too large and overflowed.\n");
+}
+
+
+Bool VG_(str_clo_cache_opt)(Char *arg,
+                            cache_t* clo_I1c,
+                            cache_t* clo_D1c,
+                            cache_t* clo_LLc)
+{
+   Char* tmp_str;
+
+   if      VG_STR_CLO(arg, "--I1", tmp_str) {
+      parse_cache_opt(clo_I1c, arg, tmp_str);
+      return True;
+   } else if VG_STR_CLO(arg, "--D1", tmp_str) {
+      parse_cache_opt(clo_D1c, arg, tmp_str);
+      return True;
+   } else if (VG_STR_CLO(arg, "--L2", tmp_str) || // for backwards compatibility
+              VG_STR_CLO(arg, "--LL", tmp_str)) {
+      parse_cache_opt(clo_LLc, arg, tmp_str);
+      return True;
+   } else
+      return False;
+}
+
+static void umsg_cache_img(Char* desc, cache_t* c)
+{
+   VG_(umsg)("  %s: %'d B, %d-way, %d B lines\n", desc,
+             c->size, c->assoc, c->line_size);
+}
+
+// Verifies if c is a valid cache.
+// An invalid value causes an assert, unless clo_redefined is True.
+static void check_cache_or_override(Char* desc, cache_t* c, Bool clo_redefined)
+{
+   Char* checkRes;
+
+   checkRes = check_cache(c);
+   if (checkRes) {
+      VG_(umsg)("Auto-detected %s cache configuration not supported: %s",
+                desc, checkRes);
+      umsg_cache_img(desc, c);
+      if (!clo_redefined) {
+         VG_(umsg)("As it probably should be supported, please report a bug!\n");
+         VG_(umsg)("Bypass this message by using option --%s=...\n", desc);
+         tl_assert(0);
+      }
+   }
+}
+
+void VG_(post_clo_init_configure_caches)(cache_t* I1c,
+                                         cache_t* D1c,
+                                         cache_t* LLc,
+                                         cache_t* clo_I1c,
+                                         cache_t* clo_D1c,
+                                         cache_t* clo_LLc)
+{
+#define DEFINED(L)   (-1 != L->size  || -1 != L->assoc || -1 != L->line_size)
+
+   // Count how many were defined on the command line.
+   Bool all_caches_clo_defined =
+      (DEFINED(clo_I1c) &&
+       DEFINED(clo_D1c) &&
+       DEFINED(clo_LLc));
+
+   // Set the cache config (using auto-detection, if supported by the
+   // architecture).
+   VG_(configure_caches)( I1c, D1c, LLc, all_caches_clo_defined );
+
+   // Check the default/auto-detected values.
+   // Allow the user to override invalid auto-detected caches
+   // with command line.
+   check_cache_or_override ("I1", I1c, DEFINED(clo_I1c));
+   check_cache_or_override ("D1", D1c, DEFINED(clo_D1c));
+   check_cache_or_override ("LL", LLc, DEFINED(clo_LLc));
+
+   // Then replace with any defined on the command line.  (Already checked in
+   // VG(parse_clo_cache_opt)().)
+   if (DEFINED(clo_I1c)) { *I1c = *clo_I1c; }
+   if (DEFINED(clo_D1c)) { *D1c = *clo_D1c; }
+   if (DEFINED(clo_LLc)) { *LLc = *clo_LLc; }
+
+   if (VG_(clo_verbosity) >= 2) {
+      VG_(umsg)("Cache configuration used:\n");
+      umsg_cache_img ("I1", I1c);
+      umsg_cache_img ("D1", D1c);
+      umsg_cache_img ("LL", LLc);
+   }
+#undef DEFINED
+}
+
+void VG_(print_cache_clo_opts)()
+{
+   VG_(printf)(
+"    --I1=<size>,<assoc>,<line_size>  set I1 cache manually\n"
+"    --D1=<size>,<assoc>,<line_size>  set D1 cache manually\n"
+"    --LL=<size>,<assoc>,<line_size>  set LL cache manually\n"
+               );
+}
diff --git a/cachegrind/cg_arch.h b/cachegrind/cg_arch.h
index 23f1a2c..68f9ab3 100644
--- a/cachegrind/cg_arch.h
+++ b/cachegrind/cg_arch.h
@@ -1,13 +1,13 @@
 
 /*--------------------------------------------------------------------*/
-/*--- Arch-specific declarations.                        cg_arch.h ---*/
+/*--- Arch-specific declarations, cache configuration.   cg_arch.h ---*/
 /*--------------------------------------------------------------------*/
 
 /*
    This file is part of Cachegrind, a Valgrind tool for cache
    profiling programs.
 
-   Copyright (C) 2002-2010 Nicholas Nethercote
+   Copyright (C) 2002-2011 Nicholas Nethercote
       njn@valgrind.org
 
    This program is free software; you can redistribute it and/or
@@ -38,11 +38,40 @@
    Int line_size;  // bytes
 } cache_t;
 
-// Gives the configuration of I1, D1 and LL caches.  They get overridden
-// by any cache configurations specified on the command line.
+#define MIN_LINE_SIZE         16
+
+// clo_*c used in the call to VG_(str_clo_cache_opt) should be statically
+// initialized to UNDEFINED_CACHE.
+#define UNDEFINED_CACHE     { -1, -1, -1 }
+
+// Gives the auto-detected configuration of I1, D1 and LL caches.  They get
+// overridden by any cache configurations specified on the command line.
 void VG_(configure_caches)(cache_t* I1c, cache_t* D1c, cache_t* LLc,
                            Bool all_caches_clo_defined);
 
+// If arg is a command line option configuring I1 or D1 or LL cache,
+// then parses arg to set the relevant cache_t elements.
+// Returns True if arg is a cache command line option, False otherwise.
+Bool VG_(str_clo_cache_opt)(Char *arg,
+                            cache_t* clo_I1c,
+                            cache_t* clo_D1c,
+                            cache_t* clo_LLc);
+
+// Checks the correctness of the auto-detected caches.
+// If a cache has been configured by command line options, it
+// replaces the equivalent auto-detected cache.
+// Note that an invalid auto-detected cache will make Valgrind exit
+// with an fatal error, except if the invalid auto-detected cache
+// will be replaced by a command line defined cache.
+void VG_(post_clo_init_configure_caches)(cache_t* I1c,
+                                         cache_t* D1c,
+                                         cache_t* LLc,
+                                         cache_t* clo_I1c,
+                                         cache_t* clo_D1c,
+                                         cache_t* clo_LLc);
+
+void VG_(print_cache_clo_opts)(void);
+
 #endif   // __CG_ARCH_H
 
 /*--------------------------------------------------------------------*/
diff --git a/cachegrind/cg_main.c b/cachegrind/cg_main.c
index 5772520..8e43936 100644
--- a/cachegrind/cg_main.c
+++ b/cachegrind/cg_main.c
@@ -1236,92 +1236,6 @@
 static cache_t clo_D1_cache = UNDEFINED_CACHE;
 static cache_t clo_LL_cache = UNDEFINED_CACHE;
 
-// Checks cache config is ok.  Returns NULL if ok, or a pointer to an error
-// string otherwise.
-static Char* check_cache(cache_t* cache)
-{
-   // Simulator requires set count to be a power of two.
-   if ((cache->size % (cache->line_size * cache->assoc) != 0) ||
-       (-1 == VG_(log2)(cache->size/cache->line_size/cache->assoc)))
-   {
-      return "Cache set count is not a power of two.\n";
-   }
-
-   // Simulator requires line size to be a power of two.
-   if (-1 == VG_(log2)(cache->line_size)) {
-      return "Cache line size is not a power of two.\n";
-   }
-
-   // Then check line size >= 16 -- any smaller and a single instruction could
-   // straddle three cache lines, which breaks a simulation assertion and is
-   // stupid anyway.
-   if (cache->line_size < MIN_LINE_SIZE) {
-      return "Cache line size is too small.\n";
-   }
-
-   /* Then check cache size > line size (causes seg faults if not). */
-   if (cache->size <= cache->line_size) {
-      return "Cache size <= line size.\n";
-   }
-
-   /* Then check assoc <= (size / line size) (seg faults otherwise). */
-   if (cache->assoc > (cache->size / cache->line_size)) {
-      return "Cache associativity > (size / line size).\n";
-   }
-
-   return NULL;
-}
-
-static 
-void configure_caches(cache_t* I1c, cache_t* D1c, cache_t* LLc)
-{
-#define DEFINED(L)   (-1 != L.size  || -1 != L.assoc || -1 != L.line_size)
-
-   Char* checkRes;
-
-   // Count how many were defined on the command line.
-   Bool all_caches_clo_defined =
-      (DEFINED(clo_I1_cache) &&
-       DEFINED(clo_D1_cache) &&
-       DEFINED(clo_LL_cache));
-
-   // Set the cache config (using auto-detection, if supported by the
-   // architecture).
-   VG_(configure_caches)( I1c, D1c, LLc, all_caches_clo_defined );
-
-   if (VG_(clo_verbosity) > 2) {
-      VG_(umsg)("Cache configuration detected:\n");
-      VG_(umsg)("  I1: %dB, %d-way, %dB lines\n",
-                I1c->size, I1c->assoc, I1c->line_size);
-      VG_(umsg)("  D1: %dB, %d-way, %dB lines\n",
-                D1c->size, D1c->assoc, D1c->line_size);
-      VG_(umsg)("  LL: %dB, %d-way, %dB lines\n",
-                LLc->size, LLc->assoc, LLc->line_size);
-   }
-
-   // Check the default/auto-detected values.
-   checkRes = check_cache(I1c);  tl_assert(!checkRes);
-   checkRes = check_cache(D1c);  tl_assert(!checkRes);
-   checkRes = check_cache(LLc);  tl_assert(!checkRes);
-
-   // Then replace with any defined on the command line.  (Already checked in
-   // parse_cache_opt().)
-   if (DEFINED(clo_I1_cache)) { *I1c = clo_I1_cache; }
-   if (DEFINED(clo_D1_cache)) { *D1c = clo_D1_cache; }
-   if (DEFINED(clo_LL_cache)) { *LLc = clo_LL_cache; }
-
-   if (VG_(clo_verbosity) >= 2) {
-      VG_(umsg)("Cache configuration used:\n");
-      VG_(umsg)("  I1: %dB, %d-way, %dB lines\n",
-                I1c->size, I1c->assoc, I1c->line_size);
-      VG_(umsg)("  D1: %dB, %d-way, %dB lines\n",
-                D1c->size, D1c->assoc, D1c->line_size);
-      VG_(umsg)("  LL: %dB, %d-way, %dB lines\n",
-                LLc->size, LLc->assoc, LLc->line_size);
-   }
-#undef CMD_LINE_DEFINED
-}
-
 /*------------------------------------------------------------*/
 /*--- cg_fini() and related function                       ---*/
 /*------------------------------------------------------------*/
@@ -1726,53 +1640,12 @@
 /*--- Command line processing                                      ---*/
 /*--------------------------------------------------------------------*/
 
-static void parse_cache_opt ( cache_t* cache, Char* opt, Char* optval )
-{
-   Long i1, i2, i3;
-   Char* endptr;
-   Char* checkRes;
-
-   // Option argument looks like "65536,2,64".  Extract them.
-   i1 = VG_(strtoll10)(optval,   &endptr); if (*endptr != ',')  goto bad;
-   i2 = VG_(strtoll10)(endptr+1, &endptr); if (*endptr != ',')  goto bad;
-   i3 = VG_(strtoll10)(endptr+1, &endptr); if (*endptr != '\0') goto bad;
-
-   // Check for overflow.
-   cache->size      = (Int)i1;
-   cache->assoc     = (Int)i2;
-   cache->line_size = (Int)i3;
-   if (cache->size      != i1) goto overflow;
-   if (cache->assoc     != i2) goto overflow;
-   if (cache->line_size != i3) goto overflow;
-
-   checkRes = check_cache(cache);
-   if (checkRes) {
-      VG_(fmsg)("%s", checkRes);
-      goto bad;
-   }
-
-   return;
-
-  bad:
-   VG_(fmsg_bad_option)(opt, "");
-
-  overflow:
-   VG_(fmsg_bad_option)(opt,
-      "One of the cache parameters was too large and overflowed.\n");
-}
-
 static Bool cg_process_cmd_line_option(Char* arg)
 {
-   Char* tmp_str;
-
-   // 5 is length of "--I1="
-   if      VG_STR_CLO(arg, "--I1", tmp_str)
-      parse_cache_opt(&clo_I1_cache, arg, tmp_str);
-   else if VG_STR_CLO(arg, "--D1", tmp_str)
-      parse_cache_opt(&clo_D1_cache, arg, tmp_str);
-   else if (VG_STR_CLO(arg, "--L2", tmp_str) || // for backwards compatibility
-            VG_STR_CLO(arg, "--LL", tmp_str))
-      parse_cache_opt(&clo_LL_cache, arg, tmp_str);
+   if (VG_(str_clo_cache_opt)(arg,
+                              &clo_I1_cache,
+                              &clo_D1_cache,
+                              &clo_LL_cache)) {}
 
    else if VG_STR_CLO( arg, "--cachegrind-out-file", clo_cachegrind_out_file) {}
    else if VG_BOOL_CLO(arg, "--cache-sim",  clo_cache_sim)  {}
@@ -1785,10 +1658,8 @@
 
 static void cg_print_usage(void)
 {
+   VG_(print_cache_clo_opts)();
    VG_(printf)(
-"    --I1=<size>,<assoc>,<line_size>  set I1 cache manually\n"
-"    --D1=<size>,<assoc>,<line_size>  set D1 cache manually\n"
-"    --LL=<size>,<assoc>,<line_size>  set LL cache manually\n"
 "    --cache-sim=yes|no  [yes]        collect cache stats?\n"
 "    --branch-sim=yes|no [no]         collect branch prediction stats?\n"
 "    --cachegrind-out-file=<file>     output file name [cachegrind.out.%%p]\n"
@@ -1848,7 +1719,10 @@
                           VG_(malloc), "cg.main.cpci.3",
                           VG_(free));
 
-   configure_caches(&I1c, &D1c, &LLc);
+   VG_(post_clo_init_configure_caches)(&I1c, &D1c, &LLc,
+                                       &clo_I1_cache,
+                                       &clo_D1_cache,
+                                       &clo_LL_cache);
 
    cachesim_I1_initcache(I1c);
    cachesim_D1_initcache(D1c);
diff --git a/callgrind/Makefile.am b/callgrind/Makefile.am
index 86e9313..0a8a2d3 100644
--- a/callgrind/Makefile.am
+++ b/callgrind/Makefile.am
@@ -46,10 +46,11 @@
 	main.c \
 	sim.c \
 	threads.c \
+	../cachegrind/cg-arch.c \
 	../cachegrind/cg-x86-amd64.c \
 	../cachegrind/cg-ppc32.c \
 	../cachegrind/cg-ppc64.c \
- 	../cachegrind/cg-arm.c   \
+	../cachegrind/cg-arm.c   \
 	../cachegrind/cg-s390x.c
 
 CALLGRIND_CFLAGS_COMMON = -I$(top_srcdir)/cachegrind
diff --git a/callgrind/sim.c b/callgrind/sim.c
index 49d719e..17785d9 100644
--- a/callgrind/sim.c
+++ b/callgrind/sim.c
@@ -1266,98 +1266,10 @@
 /*--- Cache configuration                                  ---*/
 /*------------------------------------------------------------*/
 
-#define UNDEFINED_CACHE     ((cache_t) { -1, -1, -1 }) 
-
 static cache_t clo_I1_cache = UNDEFINED_CACHE;
 static cache_t clo_D1_cache = UNDEFINED_CACHE;
 static cache_t clo_LL_cache = UNDEFINED_CACHE;
 
-
-// Checks cache config is ok.  Returns NULL if ok, or a pointer to an error
-// string otherwise.
-static Char* check_cache(cache_t* cache)
-{
-   // Simulator requires line size and set count to be powers of two.
-   if (( cache->size % (cache->line_size * cache->assoc) != 0) ||
-       (-1 == VG_(log2)(cache->size/cache->line_size/cache->assoc)))
-   {
-      return "Cache set count is not a power of two.\n";
-   }
-
-   // Simulator requires line size to be a power of two.
-   if (-1 == VG_(log2)(cache->line_size)) {
-      return "Cache line size is not a power of two.\n";
-   }
-
-   // Then check line size >= 16 -- any smaller and a single instruction could
-   // straddle three cache lines, which breaks a simulation assertion and is
-   // stupid anyway.
-   if (cache->line_size < MIN_LINE_SIZE) {
-      return "Cache line size is too small.\n";
-   }
-
-   /* Then check cache size > line size (causes seg faults if not). */
-   if (cache->size <= cache->line_size) {
-      return "Cache size <= line size.\n";
-   }
-
-   /* Then check assoc <= (size / line size) (seg faults otherwise). */
-   if (cache->assoc > (cache->size / cache->line_size)) {
-      return "Cache associativity > (size / line size).\n";
-   }
-
-   return NULL;
-}
-
-static
-void configure_caches(cache_t* I1c, cache_t* D1c, cache_t* LLc)
-{
-#define DEFINED(L)   (-1 != L.size  || -1 != L.assoc || -1 != L.line_size)
-
-   Char* checkRes;
-
-   Bool all_caches_clo_defined =
-      (DEFINED(clo_I1_cache) &&
-       DEFINED(clo_D1_cache) &&
-       DEFINED(clo_LL_cache));
-
-   // Set the cache config (using auto-detection, if supported by the
-   // architecture).
-   VG_(configure_caches)( I1c, D1c, LLc, all_caches_clo_defined );
-
-   if (VG_(clo_verbosity) > 2) {
-      VG_(umsg)("Cache configuration detected:\n");
-      VG_(umsg)("  I1: %dB, %d-way, %dB lines\n",
-                I1c->size, I1c->assoc, I1c->line_size);
-      VG_(umsg)("  D1: %dB, %d-way, %dB lines\n",
-                D1c->size, D1c->assoc, D1c->line_size);
-      VG_(umsg)("  LL: %dB, %d-way, %dB lines\n",
-                LLc->size, LLc->assoc, LLc->line_size);
-   }
-
-   // Check the default/auto-detected values.
-   checkRes = check_cache(I1c);  tl_assert(!checkRes);
-   checkRes = check_cache(D1c);  tl_assert(!checkRes);
-   checkRes = check_cache(LLc);  tl_assert(!checkRes);
-
-   // Then replace with any defined on the command line.
-   if (DEFINED(clo_I1_cache)) { *I1c = clo_I1_cache; }
-   if (DEFINED(clo_D1_cache)) { *D1c = clo_D1_cache; }
-   if (DEFINED(clo_LL_cache)) { *LLc = clo_LL_cache; }
-
-   if (VG_(clo_verbosity) > 1) {
-      VG_(umsg)("Cache configuration used:\n");
-      VG_(umsg)("  I1: %dB, %d-way, %dB lines\n",
-                I1c->size, I1c->assoc, I1c->line_size);
-      VG_(umsg)("  D1: %dB, %d-way, %dB lines\n",
-                D1c->size, D1c->assoc, D1c->line_size);
-      VG_(umsg)("  LL: %dB, %d-way, %dB lines\n",
-                LLc->size, LLc->assoc, LLc->line_size);
-   }
-#undef CMD_LINE_DEFINED
-}
-
-
 /* Initialize and clear simulator state */
 static void cachesim_post_clo_init(void)
 {
@@ -1386,8 +1298,11 @@
   }
 
   /* Configuration of caches only needed with real cache simulation */
-  configure_caches(&I1c, &D1c, &LLc);
-  
+  VG_(post_clo_init_configure_caches)(&I1c, &D1c, &LLc,
+                                      &clo_I1_cache,
+                                      &clo_D1_cache,
+                                      &clo_LL_cache);
+
   I1.name = "I1";
   D1.name = "D1";
   LL.name = "LL";
@@ -1499,47 +1414,8 @@
 #if CLG_EXPERIMENTAL
 "    --simulate-sectors=no|yes Simulate sectored behaviour [no]\n"
 #endif
-"    --cacheuse=no|yes         Collect cache block use [no]\n"
-"    --I1=<size>,<assoc>,<line_size>  set I1 cache manually\n"
-"    --D1=<size>,<assoc>,<line_size>  set D1 cache manually\n"
-"    --LL=<size>,<assoc>,<line_size>  set LL cache manually\n"
-	      );
-}
-
-static void parse_opt ( cache_t* cache,
-                        char* opt, Char* optval, UChar kind )
-{
-   Long i1, i2, i3;
-   Char* endptr;
-   Char* checkRes;
-
-   // Option argument looks like "65536,2,64".  Extract them.
-   i1 = VG_(strtoll10)(optval,   &endptr); if (*endptr != ',')  goto bad;
-   i2 = VG_(strtoll10)(endptr+1, &endptr); if (*endptr != ',')  goto bad;
-   i3 = VG_(strtoll10)(endptr+1, &endptr); if (*endptr != '\0') goto bad;
-
-   // Check for overflow.
-   cache->size      = (Int)i1;
-   cache->assoc     = (Int)i2;
-   cache->line_size = (Int)i3;
-   if (cache->size      != i1) goto overflow;
-   if (cache->assoc     != i2) goto overflow;
-   if (cache->line_size != i3) goto overflow;
-
-   checkRes = check_cache(cache);
-   if (checkRes) {
-      VG_(fmsg)("%s", checkRes);
-      goto bad;
-   }
-
-   return;
-
-  bad:
-   VG_(fmsg_bad_option)(opt, "");
-
-  overflow:
-   VG_(fmsg_bad_option)(opt,
-      "One of the cache parameters was too large and overflowed.\n");
+"    --cacheuse=no|yes         Collect cache block use [no]\n");
+  VG_(print_cache_clo_opts)();
 }
 
 /* Check for command line option for cache configuration.
@@ -1549,8 +1425,6 @@
  */
 static Bool cachesim_parse_opt(Char* arg)
 {
-   Char* tmp_str;
-
    if      VG_BOOL_CLO(arg, "--simulate-wb",      clo_simulate_writeback) {}
    else if VG_BOOL_CLO(arg, "--simulate-hwpref",  clo_simulate_hwpref)    {}
    else if VG_BOOL_CLO(arg, "--simulate-sectors", clo_simulate_sectors)   {}
@@ -1562,15 +1436,13 @@
       }
    }
 
-   else if VG_STR_CLO(arg, "--I1", tmp_str)
-      parse_opt(&clo_I1_cache, arg, tmp_str, 'i');
-   else if VG_STR_CLO(arg, "--D1", tmp_str)
-      parse_opt(&clo_D1_cache, arg, tmp_str, '1');
-   else if (VG_STR_CLO(arg, "--L2", tmp_str) || // for backwards compatibility
-            VG_STR_CLO(arg, "--LL", tmp_str))
-      parse_opt(&clo_LL_cache, arg, tmp_str, '2');
-  else
-    return False;
+   else if (VG_(str_clo_cache_opt)(arg,
+                                   &clo_I1_cache,
+                                   &clo_D1_cache,
+                                   &clo_LL_cache)) {}
+
+   else
+     return False;
 
   return True;
 }