]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - sim/common/genmloop.sh
* common/genmloop.sh: Add new parameter: -shell to specify the
[thirdparty/binutils-gdb.git] / sim / common / genmloop.sh
index 52e600401e63f5b0cb46351552c0d54272aada32..58c2ff98af092274fef680ef25eba07be9cc6c02 100644 (file)
@@ -1,22 +1,22 @@
 # Generate the main loop of the simulator.
-# Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007, 2008
+# Free Software Foundation, Inc.
 # Contributed by Cygnus Support.
 #
 # This file is part of the GNU simulators.
 #
 # 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, or (at your option)
-# any later version.
+# the Free Software Foundation; either version 3 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.
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 # This file creates two files: eng.hin and mloop.cin.
 # eng.hin defines a few macros that specify what kind of engine was selected
@@ -25,6 +25,7 @@
 #
 # ??? Rename mloop.c to eng.c?
 # ??? Rename mainloop.in to engine.in?
+# ??? Add options to specify output file names?
 # ??? Rename this file to genengine.sh?
 #
 # Syntax: genmloop.sh [options]
 # -simple: simple execution engine (the default)
 #
 #      This engine fetches and executes one instruction at a time.
-#      ??? The implementation is currently slower than necessary for
-#      simplicity.  Instead of storing extract insn fields in ARGBUF,
-#      they should just be extracted from the insn when needed.
+#      Field extraction is done in the semantic routines.
+#
+#      ??? There are two possible flavours of -simple.  One that extracts
+#      fields in the semantic routine (which is what is implemented here),
+#      and one that stores the extracted fields in ARGBUF before calling the
+#      semantic routine.  The latter is essentially the -scache case with a
+#      cache size of one (and the scache lookup code removed).  There are no
+#      current uses of this and it's not clear when doing this would be a win.
+#      More complicated ISA's that want to use -simple may find this a win.
+#      Should this ever be desirable, implement a new engine style here and
+#      call it -extract (or some such).  It's believed that the CGEN-generated
+#      code for the -scache case would be usable here, so no new code
+#      generation option would be needed for CGEN.
 #
 # -scache: use the scache to speed things up (not always a win)
 #
 #      We call them pseudo-basic-block's instead of just basic-blocks because
 #      they're not necessarily basic-blocks, though normally are.
 #
-# -parallel: cpu can execute multiple instructions parallely
+# -parallel-read: support parallel execution with read-before-exec support.
+# -parallel-write: support parallel execution with write-after-exec support.
+# -parallel-generic-write: support parallel execution with generic queued
+#       writes.
 #
-#      This option is specified in addition to -simple, -scache, -pbb.
-#      Note that while the code can determine if the cpu supports parallel
-#      execution with HAVE_PARALLEL_INSNS [and thus this option is
+#      One of these options is specified in addition to -simple, -scache,
+#      -pbb.  Note that while the code can determine if the cpu supports
+#      parallel execution with HAVE_PARALLEL_INSNS [and thus this option is
 #      technically unnecessary], having this option cuts down on the clutter
 #      in the result.
 #
+# -parallel-only: semantic code only supports parallel version of insn
+#
+#      Semantic code only supports parallel versions of each insn.
+#      Things can be sped up by generating both serial and parallel versions
+#      and is better suited to mixed parallel architectures like the m32r.
+#
+# -prefix: string to prepend to function names in mloop.c/eng.h.
+#
+#       If no prefix is specified, the cpu type is used.
+#
 # -switch file: specify file containing semantics implemented as a switch()
 #
 # -cpu <cpu-family>
 #
 #      Specify the mainloop.in input file.
 #
+# -outfile-suffix <output-file-suffix>
+#
+#      Specify the suffix to append to output files.
+#
+# -shell <shell>
+#
+#      Specify the shell to use to execute <input-file>
+#
 # Only one of -scache/-pbb may be selected.
 # -simple is the default.
 #
@@ -103,10 +135,13 @@ type=mono
 #fast=
 #full_switch=
 #pbb=
-#parallel=
+parallel=no
+parallel_only=no
 switch=
 cpu="unknown"
 infile=""
+prefix="unknown"
+outsuffix=""
 
 while test $# -gt 0
 do
@@ -120,10 +155,16 @@ do
        -scache) scache=yes ;;
        -pbb) pbb=yes ;;
        -no-parallel) ;;
-       -parallel) parallel=yes ;;
+       -outfile-suffix) shift ; outsuffix=$1 ;;
+       -parallel-read) parallel=read ;;
+       -parallel-write) parallel=write ;;
+       -parallel-generic-write) parallel=genwrite ;;
+       -parallel-only) parallel_only=yes ;;
+       -prefix) shift ; prefix=$1 ;;
        -switch) shift ; switch=$1 ;;
        -cpu) shift ; cpu=$1 ;;
        -infile) shift ; infile=$1 ;;
+       -shell) shift ; SHELL=$1 ;;
        *) echo "unknown option: $1" >&2 ; exit 1 ;;
        esac
        shift
@@ -146,14 +187,19 @@ if [ "x$infile" = x ] ; then
     exit 1
 fi
 
+if [ "x$prefix" = xunknown ] ; then
+    prefix=$cpu
+fi
+
 lowercase='abcdefghijklmnopqrstuvwxyz'
 uppercase='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
 CPU=`echo ${cpu} | tr "${lowercase}" "${uppercase}"`
+PREFIX=`echo ${prefix} | tr "${lowercase}" "${uppercase}"`
 
 ##########################################################################
 
-rm -f eng.hin
-exec 1>eng.hin
+rm -f eng${outsuffix}.hin
+exec 1>eng${outsuffix}.hin
 
 echo "/* engine configuration for ${cpu} */"
 echo ""
@@ -167,20 +213,45 @@ else
 fi
 
 echo ""
-echo "/* WITH_SCACHE_PBB_${CPU}: non-zero if the pbb engine was selected.  */"
+echo "/* WITH_SCACHE_PBB_${PREFIX}: non-zero if the pbb engine was selected.  */"
 if [ x$pbb = xyes ] ; then
-       echo "#define WITH_SCACHE_PBB_${CPU} 1"
+       echo "#define WITH_SCACHE_PBB_${PREFIX} 1"
 else
-       echo "#define WITH_SCACHE_PBB_${CPU} 0"
+       echo "#define WITH_SCACHE_PBB_${PREFIX} 0"
 fi
 
 echo ""
 echo "/* HAVE_PARALLEL_INSNS: non-zero if cpu can parallelly execute > 1 insn.  */"
-if [ x$parallel = xyes ] ; then
-       echo "#define HAVE_PARALLEL_INSNS 1"
-else
-       echo "#define HAVE_PARALLEL_INSNS 0"
-fi
+# blah blah blah, other ways to do this, blah blah blah
+case x$parallel in
+xno)
+    echo "#define HAVE_PARALLEL_INSNS 0"
+    echo "#define WITH_PARALLEL_READ 0"
+    echo "#define WITH_PARALLEL_WRITE 0"
+    echo "#define WITH_PARALLEL_GENWRITE 0"
+    ;;
+xread)
+    echo "#define HAVE_PARALLEL_INSNS 1"
+    echo "/* Parallel execution is supported by read-before-exec.  */"
+    echo "#define WITH_PARALLEL_READ 1"
+    echo "#define WITH_PARALLEL_WRITE 0"
+    echo "#define WITH_PARALLEL_GENWRITE 0"
+    ;;
+xwrite)
+    echo "#define HAVE_PARALLEL_INSNS 1"
+    echo "/* Parallel execution is supported by write-after-exec.  */"
+    echo "#define WITH_PARALLEL_READ 0"
+    echo "#define WITH_PARALLEL_WRITE 1"
+    echo "#define WITH_PARALLEL_GENWRITE 0"
+    ;;
+xgenwrite)
+    echo "#define HAVE_PARALLEL_INSNS 1"
+    echo "/* Parallel execution is supported by generic write-after-exec.  */"
+    echo "#define WITH_PARALLEL_READ 0"
+    echo "#define WITH_PARALLEL_WRITE 0"
+    echo "#define WITH_PARALLEL_GENWRITE 1"
+    ;;
+esac
 
 if [ "x$switch" != x ] ; then
        echo ""
@@ -207,25 +278,26 @@ echo ""
 echo "/* Functions defined in the generated mainloop.c file"
 echo "   (which doesn't necessarily have that file name).  */"
 echo ""
-echo "extern ENGINE_FN ${cpu}_engine_run_full;"
-echo "extern ENGINE_FN ${cpu}_engine_run_fast;"
+echo "extern ENGINE_FN ${prefix}_engine_run_full;"
+echo "extern ENGINE_FN ${prefix}_engine_run_fast;"
 
 if [ x$pbb = xyes ] ; then
        echo ""
-       echo "extern SEM_PC ${cpu}_pbb_begin (SIM_CPU *, int);"
-       echo "extern SEM_PC ${cpu}_pbb_chain (SIM_CPU *, SEM_ARG);"
-       echo "extern SEM_PC ${cpu}_pbb_cti_chain (SIM_CPU *, SEM_ARG, SEM_PC *, PCADDR);"
-       echo "extern void ${cpu}_pbb_before (SIM_CPU *, SCACHE *);"
-       echo "extern void ${cpu}_pbb_after (SIM_CPU *, SCACHE *);"
+       echo "extern SEM_PC ${prefix}_pbb_begin (SIM_CPU *, int);"
+       echo "extern SEM_PC ${prefix}_pbb_chain (SIM_CPU *, SEM_ARG);"
+       echo "extern SEM_PC ${prefix}_pbb_cti_chain (SIM_CPU *, SEM_ARG, SEM_BRANCH_TYPE, PCADDR);"
+       echo "extern void ${prefix}_pbb_before (SIM_CPU *, SCACHE *);"
+       echo "extern void ${prefix}_pbb_after (SIM_CPU *, SCACHE *);"
 fi
 
 ##########################################################################
 
-rm -f tmp-mloop.cin mloop.cin
-exec 1>tmp-mloop.cin
+rm -f tmp-mloop-$$.cin mloop${outsuffix}.cin
+exec 1>tmp-mloop-$$.cin
 
-# We use @cpu@ instead of ${cpu} because we still want to run sed to handle
-# transformation of @cpu@ for mainloop.in.
+# We use @cpu@ instead of ${cpu} because we still need to run sed to handle
+# transformation of @cpu@ for mainloop.in, so there's no need to use ${cpu}
+# here.
 
 cat << EOF
 /* This file is generated by the genmloop script.  DO NOT EDIT! */
@@ -233,17 +305,73 @@ cat << EOF
 /* Enable switch() support in cgen headers.  */
 #define SEM_IN_SWITCH
 
-#define WANT_CPU
-#define WANT_CPU_${CPU}
+#define WANT_CPU @cpu@
+#define WANT_CPU_@CPU@
 
 #include "sim-main.h"
 #include "bfd.h"
 #include "cgen-mem.h"
 #include "cgen-ops.h"
-#include "cpu-opc.h"
-#include "cpu-sim.h"
 #include "sim-assert.h"
 
+/* Fill in the administrative ARGBUF fields required by all insns,
+   virtual and real.  */
+
+static INLINE void
+@prefix@_fill_argbuf (const SIM_CPU *cpu, ARGBUF *abuf, const IDESC *idesc,
+                   PCADDR pc, int fast_p)
+{
+#if WITH_SCACHE
+  SEM_SET_CODE (abuf, idesc, fast_p);
+  ARGBUF_ADDR (abuf) = pc;
+#endif
+  ARGBUF_IDESC (abuf) = idesc;
+}
+
+/* Fill in tracing/profiling fields of an ARGBUF.  */
+
+static INLINE void
+@prefix@_fill_argbuf_tp (const SIM_CPU *cpu, ARGBUF *abuf,
+                      int trace_p, int profile_p)
+{
+  ARGBUF_TRACE_P (abuf) = trace_p;
+  ARGBUF_PROFILE_P (abuf) = profile_p;
+}
+
+#if WITH_SCACHE_PBB
+
+/* Emit the "x-before" handler.
+   x-before is emitted before each insn (serial or parallel).
+   This is as opposed to x-after which is only emitted at the end of a group
+   of parallel insns.  */
+
+static INLINE void
+@prefix@_emit_before (SIM_CPU *current_cpu, SCACHE *sc, PCADDR pc, int first_p)
+{
+  ARGBUF *abuf = &sc[0].argbuf;
+  const IDESC *id = & CPU_IDESC (current_cpu) [@PREFIX@_INSN_X_BEFORE];
+
+  abuf->fields.before.first_p = first_p;
+  @prefix@_fill_argbuf (current_cpu, abuf, id, pc, 0);
+  /* no need to set trace_p,profile_p */
+}
+
+/* Emit the "x-after" handler.
+   x-after is emitted after a serial insn or at the end of a group of
+   parallel insns.  */
+
+static INLINE void
+@prefix@_emit_after (SIM_CPU *current_cpu, SCACHE *sc, PCADDR pc)
+{
+  ARGBUF *abuf = &sc[0].argbuf;
+  const IDESC *id = & CPU_IDESC (current_cpu) [@PREFIX@_INSN_X_AFTER];
+
+  @prefix@_fill_argbuf (current_cpu, abuf, id, pc, 0);
+  /* no need to set trace_p,profile_p */
+}
+
+#endif /* WITH_SCACHE_PBB */
+
 EOF
 
 ${SHELL} $infile support
@@ -251,6 +379,14 @@ ${SHELL} $infile support
 ##########################################################################
 
 # Simple engine: fetch an instruction, execute the instruction.
+#
+# Instruction fields are not extracted into ARGBUF, they are extracted in
+# the semantic routines themselves.  However, there is still a need to pass
+# and return misc. information to the semantic routines so we still use ARGBUF.
+# [One could certainly implement things differently and remove ARGBUF.
+# It's not clear this is necessarily always a win.]
+# ??? The use of the SCACHE struct is for consistency with the with-scache
+# case though it might be a source of confusion.
 
 if [ x$scache != xyes -a x$pbb != xyes ] ; then
 
@@ -259,31 +395,36 @@ if [ x$scache != xyes -a x$pbb != xyes ] ; then
 #define FAST_P 0
 
 void
-${cpu}_engine_run_full (SIM_CPU *current_cpu)
+@prefix@_engine_run_full (SIM_CPU *current_cpu)
 {
 #define FAST_P 0
   SIM_DESC current_state = CPU_STATE (current_cpu);
+  /* ??? Use of SCACHE is a bit of a hack as we don't actually use the scache.
+     We do however use ARGBUF so for consistency with the other engine flavours
+     the SCACHE type is used.  */
   SCACHE cache[MAX_LIW_INSNS];
   SCACHE *sc = &cache[0];
 
 EOF
 
-if [ x$parallel = xyes ] ; then
-  cat << EOF
+case x$parallel in
+xread | xwrite)
+    cat << EOF
   PAREXEC pbufs[MAX_PARALLEL_INSNS];
   PAREXEC *par_exec;
 
 EOF
-fi
+    ;;
+esac
 
 # Any initialization code before looping starts.
 # Note that this code may declare some locals.
 ${SHELL} $infile init
 
-if [ x$parallel = xyes ] ; then
+if [ x$parallel = xread ] ; then
   cat << EOF
 
-#if defined (HAVE_PARALLEL_EXEC) && defined (__GNUC__)
+#if defined (__GNUC__)
   {
     if (! CPU_IDESC_READ_INIT_P (current_cpu))
       {
@@ -300,22 +441,33 @@ fi
 
 cat << EOF
 
+  if (! CPU_IDESC_SEM_INIT_P (current_cpu))
+    {
+#if WITH_SEM_SWITCH_FULL
+#if defined (__GNUC__)
+/* ??? Later maybe paste sem-switch.c in when building mainloop.c.  */
+#define DEFINE_LABELS
+#include "$switch"
+#endif
+#else
+      @prefix@_sem_init_idesc_table (current_cpu);
+#endif
+      CPU_IDESC_SEM_INIT_P (current_cpu) = 1;
+    }
+
   do
     {
-/* begin full-{extract,exec}-simple */
+/* begin full-exec-simple */
 EOF
 
-${SHELL} $infile extract-simple
-echo ""
 ${SHELL} $infile full-exec-simple
 
 cat << EOF
-/* end full-{extract,exec}-simple */
+/* end full-exec-simple */
 
       ++ CPU_INSN_COUNT (current_cpu);
     }
   while (0 /*CPU_RUNNING_P (current_cpu)*/);
-#undef FAST_P
 }
 
 #undef FAST_P
@@ -333,7 +485,7 @@ if [ x$fast = xyes ] ; then
 
 #define FAST_P 1
 
-FIXME
+FIXME: "fast simple version unimplemented, delete -fast arg to genmloop.sh."
 
 #undef FAST_P
 
@@ -345,14 +497,15 @@ fi # simple engine
 
 ##########################################################################
 
-# Scache engine: lookup insn in scache, fetch if missing, then execute it.
+# Non-parallel scache engine: lookup insn in scache, fetch if missing,
+# then execute it.
 
-if [ x$scache = xyes ] ; then
+if [ x$scache = xyes -a x$parallel = xno ] ; then
 
     cat << EOF
 
 static INLINE SCACHE *
-${cpu}_scache_lookup (SIM_CPU *current_cpu, PCADDR vpc, SCACHE *scache,
+@prefix@_scache_lookup (SIM_CPU *current_cpu, PCADDR vpc, SCACHE *scache,
                      unsigned int hash_mask, int FAST_P)
 {
   /* First step: look up current insn in hash table.  */
@@ -362,9 +515,7 @@ ${cpu}_scache_lookup (SIM_CPU *current_cpu, PCADDR vpc, SCACHE *scache,
      fetch and decode the instruction.  */
   if (sc->argbuf.addr != vpc)
     {
-      insn_t insn;
-
-      if (FAST_P)
+      if (! FAST_P)
        PROFILE_COUNT_SCACHE_MISS (current_cpu);
 
 /* begin extract-scache */
@@ -375,7 +526,7 @@ ${SHELL} $infile extract-scache
 cat << EOF
 /* end extract-scache */
     }
-  else if (FAST_P)
+  else if (FAST_P)
     {
       PROFILE_COUNT_SCACHE_HIT (current_cpu);
       /* Make core access statistics come out right.
@@ -389,7 +540,7 @@ cat << EOF
 #define FAST_P 0
 
 void
-${cpu}_engine_run_full (SIM_CPU *current_cpu)
+@prefix@_engine_run_full (SIM_CPU *current_cpu)
 {
   SIM_DESC current_state = CPU_STATE (current_cpu);
   SCACHE *scache = CPU_SCACHE_CACHE (current_cpu);
@@ -398,22 +549,182 @@ ${cpu}_engine_run_full (SIM_CPU *current_cpu)
 
 EOF
 
-if [ x$parallel = xyes ] ; then
-  cat << EOF
-  PAREXEC pbufs[MAX_PARALLEL_INSNS];
-  PAREXEC *par_exec;
+# Any initialization code before looping starts.
+# Note that this code may declare some locals.
+${SHELL} $infile init
+
+cat << EOF
+
+  if (! CPU_IDESC_SEM_INIT_P (current_cpu))
+    {
+#if ! WITH_SEM_SWITCH_FULL
+      @prefix@_sem_init_idesc_table (current_cpu);
+#endif
+      CPU_IDESC_SEM_INIT_P (current_cpu) = 1;
+    }
+
+  vpc = GET_H_PC ();
+
+  do
+    {
+      SCACHE *sc;
+
+      sc = @prefix@_scache_lookup (current_cpu, vpc, scache, hash_mask, FAST_P);
+
+/* begin full-exec-scache */
+EOF
+
+${SHELL} $infile full-exec-scache
+
+cat << EOF
+/* end full-exec-scache */
+
+      SET_H_PC (vpc);
+
+      ++ CPU_INSN_COUNT (current_cpu);
+    }
+  while (0 /*CPU_RUNNING_P (current_cpu)*/);
+}
+
+#undef FAST_P
+
+EOF
+
+####################################
+
+# Non-parallel scache engine: fast version.
+
+if [ x$fast = xyes ] ; then
+
+    cat << EOF
+
+#define FAST_P 1
+
+void
+@prefix@_engine_run_fast (SIM_CPU *current_cpu)
+{
+  SIM_DESC current_state = CPU_STATE (current_cpu);
+  SCACHE *scache = CPU_SCACHE_CACHE (current_cpu);
+  unsigned int hash_mask = CPU_SCACHE_HASH_MASK (current_cpu);
+  SEM_PC vpc;
 
 EOF
-fi
 
 # Any initialization code before looping starts.
 # Note that this code may declare some locals.
 ${SHELL} $infile init
 
-if [ x$parallel = xyes ] ; then
-  cat << EOF
+cat << EOF
+
+  if (! CPU_IDESC_SEM_INIT_P (current_cpu))
+    {
+#if WITH_SEM_SWITCH_FAST
+#if defined (__GNUC__)
+/* ??? Later maybe paste sem-switch.c in when building mainloop.c.  */
+#define DEFINE_LABELS
+#include "$switch"
+#endif
+#else
+      @prefix@_semf_init_idesc_table (current_cpu);
+#endif
+      CPU_IDESC_SEM_INIT_P (current_cpu) = 1;
+    }
+
+  vpc = GET_H_PC ();
+
+  do
+    {
+      SCACHE *sc;
+
+      sc = @prefix@_scache_lookup (current_cpu, vpc, scache, hash_mask, FAST_P);
+
+/* begin fast-exec-scache */
+EOF
+
+${SHELL} $infile fast-exec-scache
+
+cat << EOF
+/* end fast-exec-scache */
+
+      SET_H_PC (vpc);
+
+      ++ CPU_INSN_COUNT (current_cpu);
+    }
+  while (0 /*CPU_RUNNING_P (current_cpu)*/);
+}
+
+#undef FAST_P
+
+EOF
+
+fi # -fast
+
+fi # -scache && ! parallel
+
+##########################################################################
+
+# Parallel scache engine: lookup insn in scache, fetch if missing,
+# then execute it.
+# For the parallel case we give the target more flexibility.
+
+if [ x$scache = xyes -a x$parallel != xno ] ; then
+
+    cat << EOF
+
+static INLINE SCACHE *
+@prefix@_scache_lookup (SIM_CPU *current_cpu, PCADDR vpc, SCACHE *scache,
+                     unsigned int hash_mask, int FAST_P)
+{
+  /* First step: look up current insn in hash table.  */
+  SCACHE *sc = scache + SCACHE_HASH_PC (vpc, hash_mask);
+
+  /* If the entry isn't the one we want (cache miss),
+     fetch and decode the instruction.  */
+  if (sc->argbuf.addr != vpc)
+    {
+      if (! FAST_P)
+       PROFILE_COUNT_SCACHE_MISS (current_cpu);
+
+#define SET_LAST_INSN_P(last_p) do { sc->last_insn_p = (last_p); } while (0)
+/* begin extract-scache */
+EOF
+
+${SHELL} $infile extract-scache
+
+cat << EOF
+/* end extract-scache */
+#undef SET_LAST_INSN_P
+    }
+  else if (! FAST_P)
+    {
+      PROFILE_COUNT_SCACHE_HIT (current_cpu);
+      /* Make core access statistics come out right.
+        The size is a guess, but it's currently not used either.  */
+      PROFILE_COUNT_CORE (current_cpu, vpc, 2, exec_map);
+    }
+
+  return sc;
+}
+
+#define FAST_P 0
 
-#if defined (HAVE_PARALLEL_EXEC) && defined (__GNUC__)
+void
+@prefix@_engine_run_full (SIM_CPU *current_cpu)
+{
+  SIM_DESC current_state = CPU_STATE (current_cpu);
+  SCACHE *scache = CPU_SCACHE_CACHE (current_cpu);
+  unsigned int hash_mask = CPU_SCACHE_HASH_MASK (current_cpu);
+  SEM_PC vpc;
+
+EOF
+
+# Any initialization code before looping starts.
+# Note that this code may declare some locals.
+${SHELL} $infile init
+
+if [ x$parallel = xread ] ; then
+cat << EOF
+#if defined (__GNUC__)
   {
     if (! CPU_IDESC_READ_INIT_P (current_cpu))
       {
@@ -430,14 +741,18 @@ fi
 
 cat << EOF
 
+  if (! CPU_IDESC_SEM_INIT_P (current_cpu))
+    {
+#if ! WITH_SEM_SWITCH_FULL
+      @prefix@_sem_init_idesc_table (current_cpu);
+#endif
+      CPU_IDESC_SEM_INIT_P (current_cpu) = 1;
+    }
+
   vpc = GET_H_PC ();
 
   do
     {
-      SCACHE *sc;
-
-      sc = ${cpu}_scache_lookup (current_cpu, vpc, scache, hash_mask, FAST_P);
-
 /* begin full-exec-scache */
 EOF
 
@@ -445,10 +760,6 @@ ${SHELL} $infile full-exec-scache
 
 cat << EOF
 /* end full-exec-scache */
-
-      SET_H_PC (vpc);
-
-      ++ CPU_INSN_COUNT (current_cpu);
     }
   while (0 /*CPU_RUNNING_P (current_cpu)*/);
 }
@@ -459,7 +770,7 @@ EOF
 
 ####################################
 
-# Scache engine: fast version.
+# Parallel scache engine: fast version.
 
 if [ x$fast = xyes ] ; then
 
@@ -468,31 +779,25 @@ if [ x$fast = xyes ] ; then
 #define FAST_P 1
 
 void
-${cpu}_engine_run_fast (SIM_CPU *current_cpu)
+@prefix@_engine_run_fast (SIM_CPU *current_cpu)
 {
   SIM_DESC current_state = CPU_STATE (current_cpu);
   SCACHE *scache = CPU_SCACHE_CACHE (current_cpu);
   unsigned int hash_mask = CPU_SCACHE_HASH_MASK (current_cpu);
   SEM_PC vpc;
-
-EOF
-
-if [ x$parallel = xyes ] ; then
-  cat << EOF
   PAREXEC pbufs[MAX_PARALLEL_INSNS];
   PAREXEC *par_exec;
 
 EOF
-fi
 
 # Any initialization code before looping starts.
 # Note that this code may declare some locals.
 ${SHELL} $infile init
 
-if [ x$parallel = xyes ] ; then
-  cat << EOF
+if [ x$parallel = xread ] ; then
+cat << EOF
 
-#if defined (HAVE_PARALLEL_EXEC) && defined (__GNUC__)
+#if defined (__GNUC__)
   {
     if (! CPU_IDESC_READ_INIT_P (current_cpu))
       {
@@ -505,30 +810,28 @@ if [ x$parallel = xyes ] ; then
 #endif
 
 EOF
-fi # parallel = yes
+fi
 
 cat << EOF
 
-#if WITH_SEM_SWITCH_FAST && defined (__GNUC__)
-  {
-    if (! CPU_IDESC_SEM_INIT_P (current_cpu))
-      {
+  if (! CPU_IDESC_SEM_INIT_P (current_cpu))
+    {
+#if WITH_SEM_SWITCH_FAST
+#if defined (__GNUC__)
 /* ??? Later maybe paste sem-switch.c in when building mainloop.c.  */
 #define DEFINE_LABELS
 #include "$switch"
-       CPU_IDESC_SEM_INIT_P (current_cpu) = 1;
-      }
-  }
 #endif
+#else
+      @prefix@_semf_init_idesc_table (current_cpu);
+#endif
+      CPU_IDESC_SEM_INIT_P (current_cpu) = 1;
+    }
 
   vpc = GET_H_PC ();
 
   do
     {
-      SCACHE *sc;
-
-      sc = ${cpu}_scache_lookup (current_cpu, vpc, scache, hash_mask, FAST_P);
-
 /* begin fast-exec-scache */
 EOF
 
@@ -536,10 +839,6 @@ ${SHELL} $infile fast-exec-scache
 
 cat << EOF
 /* end fast-exec-scache */
-
-      SET_H_PC (vpc);
-
-      ++ CPU_INSN_COUNT (current_cpu);
     }
   while (0 /*CPU_RUNNING_P (current_cpu)*/);
 }
@@ -550,7 +849,7 @@ EOF
 
 fi # -fast
 
-fi # -scache
+fi # -scache && parallel
 
 ##########################################################################
 
@@ -565,15 +864,15 @@ if [ x$pbb = xyes ] ; then
     cat << EOF
 
 /* Record address of cti terminating a pbb.  */
-#define SET_CTI_VPC(sc) do { cti_sc = (sc); } while (0)
+#define SET_CTI_VPC(sc) do { _cti_sc = (sc); } while (0)
 /* Record number of [real] insns in pbb.  */
-#define SET_INSN_COUNT(n) do { insn_count = (n); } while (0)
+#define SET_INSN_COUNT(n) do { _insn_count = (n); } while (0)
 
 /* Fetch and extract a pseudo-basic-block.
    FAST_P is non-zero if no tracing/profiling/etc. is wanted.  */
 
 INLINE SEM_PC
-${cpu}_pbb_begin (SIM_CPU *current_cpu, int FAST_P)
+@prefix@_pbb_begin (SIM_CPU *current_cpu, int FAST_P)
 {
   SEM_PC new_vpc;
   PCADDR pc;
@@ -585,9 +884,10 @@ ${cpu}_pbb_begin (SIM_CPU *current_cpu, int FAST_P)
   new_vpc = scache_lookup_or_alloc (current_cpu, pc, max_insns, &sc);
   if (! new_vpc)
     {
-      int insn_count = 0;
+      /* Leading '_' to avoid collision with mainloop.in.  */
+      int _insn_count = 0;
       SCACHE *orig_sc = sc;
-      SCACHE *cti_sc = NULL;
+      SCACHE *_cti_sc = NULL;
       int slice_insns = CPU_MAX_SLICE_INSNS (current_cpu);
 
       /* First figure out how many instructions to compile.
@@ -653,23 +953,25 @@ cat << EOF
        const IDESC *id;
 
        /* Was pbb terminated by a cti?  */
-       if (cti_sc)
+       if (_cti_sc)
          {
-           id = & CPU_IDESC (current_cpu) [${CPU}_INSN_X_CTI_CHAIN];
+           id = & CPU_IDESC (current_cpu) [@PREFIX@_INSN_X_CTI_CHAIN];
          }
        else
          {
-           id = & CPU_IDESC (current_cpu) [${CPU}_INSN_X_CHAIN];
+           id = & CPU_IDESC (current_cpu) [@PREFIX@_INSN_X_CHAIN];
          }
        SEM_SET_CODE (&sc->argbuf, id, FAST_P);
        sc->argbuf.idesc = id;
        sc->argbuf.addr = pc;
-       sc->argbuf.fields.chain.insn_count = insn_count;
+       sc->argbuf.fields.chain.insn_count = _insn_count;
        sc->argbuf.fields.chain.next = 0;
+       sc->argbuf.fields.chain.branch_target = 0;
        ++sc;
       }
 
-      /* Update the pointer to the next free entry.  */
+      /* Update the pointer to the next free entry, may not have used as
+        many entries as was asked for.  */
       CPU_SCACHE_NEXT_FREE (current_cpu) = sc;
       /* Record length of chain if profiling.
         This includes virtual insns since they count against
@@ -684,7 +986,7 @@ cat << EOF
 /* Chain to the next block from a non-cti terminated previous block.  */
 
 INLINE SEM_PC
-${cpu}_pbb_chain (SIM_CPU *current_cpu, SEM_ARG sem_arg)
+@prefix@_pbb_chain (SIM_CPU *current_cpu, SEM_ARG sem_arg)
 {
   ARGBUF *abuf = SEM_ARGBUF (sem_arg);
 
@@ -693,7 +995,14 @@ ${cpu}_pbb_chain (SIM_CPU *current_cpu, SEM_ARG sem_arg)
   SET_H_PC (abuf->addr);
 
   /* If not running forever, exit back to main loop.  */
-  if (CPU_MAX_SLICE_INSNS (current_cpu) != 0)
+  if (CPU_MAX_SLICE_INSNS (current_cpu) != 0
+      /* Also exit back to main loop if there's an event.
+         Note that if CPU_MAX_SLICE_INSNS != 1, events won't get processed
+        at the "right" time, but then that was what was asked for.
+        There is no silver bullet for simulator engines.
+         ??? Clearly this needs a cleaner interface.
+        At present it's just so Ctrl-C works.  */
+      || STATE_EVENTS (CPU_STATE (current_cpu))->work_pending)
     CPU_RUNNING_P (current_cpu) = 0;
 
   /* If chained to next block, go straight to it.  */
@@ -709,26 +1018,33 @@ ${cpu}_pbb_chain (SIM_CPU *current_cpu, SEM_ARG sem_arg)
 }
 
 /* Chain to the next block from a cti terminated previous block.
-   NEW_VPC_PTR is one of SEM_BRANCH_UNTAKEN, SEM_BRANCH_UNCACHEABLE, or
-   a pointer to a location containing the SEM_PC of the branch's address.
+   BR_TYPE indicates whether the branch was taken and whether we can cache
+   the vpc of the branch target.
    NEW_PC is the target's branch address, and is only valid if
-   NEW_VPC_PTR != SEM_BRANCH_UNTAKEN.  */
+   BR_TYPE != SEM_BRANCH_UNTAKEN.  */
 
 INLINE SEM_PC
-${cpu}_pbb_cti_chain (SIM_CPU *current_cpu, SEM_ARG sem_arg,
-                    SEM_PC *new_vpc_ptr, PCADDR new_pc)
+@prefix@_pbb_cti_chain (SIM_CPU *current_cpu, SEM_ARG sem_arg,
+                    SEM_BRANCH_TYPE br_type, PCADDR new_pc)
 {
-  ARGBUF *abuf;
+  SEM_PC *new_vpc_ptr;
 
   PBB_UPDATE_INSN_COUNT (current_cpu, sem_arg);
 
   /* If not running forever, exit back to main loop.  */
-  if (CPU_MAX_SLICE_INSNS (current_cpu) != 0)
+  if (CPU_MAX_SLICE_INSNS (current_cpu) != 0
+      /* Also exit back to main loop if there's an event.
+         Note that if CPU_MAX_SLICE_INSNS != 1, events won't get processed
+        at the "right" time, but then that was what was asked for.
+        There is no silver bullet for simulator engines.
+         ??? Clearly this needs a cleaner interface.
+        At present it's just so Ctrl-C works.  */
+      || STATE_EVENTS (CPU_STATE (current_cpu))->work_pending)
     CPU_RUNNING_P (current_cpu) = 0;
 
   /* Restart compiler if we branched to an uncacheable address
      (e.g. "j reg").  */
-  if (new_vpc_ptr == SEM_BRANCH_UNCACHEABLE)
+  if (br_type == SEM_BRANCH_UNCACHEABLE)
     {
       SET_H_PC (new_pc);
       return CPU_SCACHE_PBB_BEGIN (current_cpu);
@@ -736,22 +1052,25 @@ ${cpu}_pbb_cti_chain (SIM_CPU *current_cpu, SEM_ARG sem_arg,
 
   /* If branch wasn't taken, update the pc and set BR_ADDR_PTR to our
      next chain ptr.  */
-  if (new_vpc_ptr == SEM_BRANCH_UNTAKEN)
+  if (br_type == SEM_BRANCH_UNTAKEN)
     {
-      abuf = SEM_ARGBUF (sem_arg);
-      SET_H_PC (abuf->addr);
+      ARGBUF *abuf = SEM_ARGBUF (sem_arg);
+      new_pc = abuf->addr;
+      SET_H_PC (new_pc);
       new_vpc_ptr = &abuf->fields.chain.next;
     }
   else
     {
+      ARGBUF *abuf = SEM_ARGBUF (sem_arg);
       SET_H_PC (new_pc);
+      new_vpc_ptr = &abuf->fields.chain.branch_target;
     }
 
   /* If chained to next block, go straight to it.  */
   if (*new_vpc_ptr)
     return *new_vpc_ptr;
   /* See if next block has already been compiled.  */
-  *new_vpc_ptr = scache_lookup (current_cpu, GET_H_PC ());
+  *new_vpc_ptr = scache_lookup (current_cpu, new_pc);
   if (*new_vpc_ptr)
     return *new_vpc_ptr;
   /* Nope, so next insn is a virtual insn to invoke the compiler
@@ -763,7 +1082,7 @@ ${cpu}_pbb_cti_chain (SIM_CPU *current_cpu, SEM_ARG sem_arg,
    This is called before each insn.  */
 
 void
-${cpu}_pbb_before (SIM_CPU *current_cpu, SCACHE *sc)
+@prefix@_pbb_before (SIM_CPU *current_cpu, SCACHE *sc)
 {
   SEM_ARG sem_arg = sc;
   const ARGBUF *abuf = SEM_ARGBUF (sem_arg);
@@ -772,7 +1091,8 @@ ${cpu}_pbb_before (SIM_CPU *current_cpu, SCACHE *sc)
   const IDESC *cur_idesc = cur_abuf->idesc;
   PCADDR pc = cur_abuf->addr;
 
-  PROFILE_COUNT_INSN (current_cpu, pc, cur_idesc->num);
+  if (ARGBUF_PROFILE_P (cur_abuf))
+    PROFILE_COUNT_INSN (current_cpu, pc, cur_idesc->num);
 
   /* If this isn't the first insn, finish up the previous one.  */
 
@@ -785,19 +1105,24 @@ ${cpu}_pbb_before (SIM_CPU *current_cpu, SCACHE *sc)
          const IDESC *prev_idesc = prev_abuf->idesc;
          int cycles;
 
-         cycles = (*prev_idesc->timing->model_fn) (current_cpu, prev_sem_arg);
-         ${cpu}_model_insn_after (current_cpu, 0 /*last_p*/, cycles);
+         /* ??? May want to measure all insns if doing insn tracing.  */
+         if (ARGBUF_PROFILE_P (prev_abuf))
+           {
+             cycles = (*prev_idesc->timing->model_fn) (current_cpu, prev_sem_arg);
+             @prefix@_model_insn_after (current_cpu, 0 /*last_p*/, cycles);
+           }
        }
 
-      TRACE_INSN_FINI (current_cpu, 0 /*last_p*/);
+      TRACE_INSN_FINI (current_cpu, cur_abuf, 0 /*last_p*/);
     }
 
   /* FIXME: Later make cover macros: PROFILE_INSN_{INIT,FINI}.  */
-  if (PROFILE_MODEL_P (current_cpu))
-    ${cpu}_model_insn_before (current_cpu, first_p);
+  if (PROFILE_MODEL_P (current_cpu)
+      && ARGBUF_PROFILE_P (cur_abuf))
+    @prefix@_model_insn_before (current_cpu, first_p);
 
-  TRACE_INSN_INIT (current_cpu, first_p);
-  TRACE_INSN (current_cpu, cur_idesc->opcode, cur_abuf, cur_abuf->addr);
+  TRACE_INSN_INIT (current_cpu, cur_abuf, first_p);
+  TRACE_INSN (current_cpu, cur_idesc->idata, cur_abuf, pc);
 }
 
 /* x-after handler.
@@ -805,28 +1130,30 @@ ${cpu}_pbb_before (SIM_CPU *current_cpu, SCACHE *sc)
    insns.  */
 
 void
-${cpu}_pbb_after (SIM_CPU *current_cpu, SCACHE *sc)
+@prefix@_pbb_after (SIM_CPU *current_cpu, SCACHE *sc)
 {
   SEM_ARG sem_arg = sc;
   const ARGBUF *abuf = SEM_ARGBUF (sem_arg);
+  const SEM_ARG prev_sem_arg = sc - 1;
+  const ARGBUF *prev_abuf = SEM_ARGBUF (prev_sem_arg);
 
-  if (PROFILE_MODEL_P (current_cpu))
+  /* ??? May want to measure all insns if doing insn tracing.  */
+  if (PROFILE_MODEL_P (current_cpu)
+      && ARGBUF_PROFILE_P (prev_abuf))
     {
-      const SEM_ARG prev_sem_arg = sc - 1;
-      const ARGBUF *prev_abuf = SEM_ARGBUF (prev_sem_arg);
       const IDESC *prev_idesc = prev_abuf->idesc;
       int cycles;
 
       cycles = (*prev_idesc->timing->model_fn) (current_cpu, prev_sem_arg);
-      ${cpu}_model_insn_after (current_cpu, 1 /*last_p*/, cycles);
+      @prefix@_model_insn_after (current_cpu, 1 /*last_p*/, cycles);
     }
-  TRACE_INSN_FINI (current_cpu, 1 /*last_p*/);
+  TRACE_INSN_FINI (current_cpu, prev_abuf, 1 /*last_p*/);
 }
 
 #define FAST_P 0
 
 void
-${cpu}_engine_run_full (SIM_CPU *current_cpu)
+@prefix@_engine_run_full (SIM_CPU *current_cpu)
 {
   SIM_DESC current_state = CPU_STATE (current_cpu);
   SCACHE *scache = CPU_SCACHE_CACHE (current_cpu);
@@ -834,19 +1161,21 @@ ${cpu}_engine_run_full (SIM_CPU *current_cpu)
   SEM_PC vpc;
 #if WITH_SEM_SWITCH_FULL
   /* For communication between cti's and cti-chain.  */
+  SEM_BRANCH_TYPE pbb_br_type;
   PCADDR pbb_br_npc;
-  SEM_PC *pbb_br_npc_ptr;
 #endif
 
 EOF
 
-if [ x$parallel = xyes ] ; then
-  cat << EOF
+case x$parallel in
+xread | xwrite)
+    cat << EOF
   PAREXEC pbufs[MAX_PARALLEL_INSNS];
   PAREXEC *par_exec = &pbufs[0];
 
 EOF
-fi
+    ;;
+esac
 
 # Any initialization code before looping starts.
 # Note that this code may declare some locals.
@@ -862,17 +1191,21 @@ cat << EOF
         a pbb).  And in the "let's run until we're done" case we don't return
         until the program exits.  */
 
-#if WITH_SEM_SWITCH_FULL && defined (__GNUC__)
+#if WITH_SEM_SWITCH_FULL
+#if defined (__GNUC__)
 /* ??? Later maybe paste sem-switch.c in when building mainloop.c.  */
 #define DEFINE_LABELS
 #include "$switch"
+#endif
+#else
+      @prefix@_sem_init_idesc_table (current_cpu);
 #endif
 
       /* Initialize the "begin (compile) a pbb" virtual insn.  */
       vpc = CPU_SCACHE_PBB_BEGIN (current_cpu);
       SEM_SET_FULL_CODE (SEM_ARGBUF (vpc),
-                        & CPU_IDESC (current_cpu) [${CPU}_INSN_X_BEGIN]);
-      vpc->argbuf.idesc = & CPU_IDESC (current_cpu) [${CPU}_INSN_X_BEGIN];
+                        & CPU_IDESC (current_cpu) [@PREFIX@_INSN_X_BEGIN]);
+      vpc->argbuf.idesc = & CPU_IDESC (current_cpu) [@PREFIX@_INSN_X_BEGIN];
 
       CPU_IDESC_SEM_INIT_P (current_cpu) = 1;
     }
@@ -882,7 +1215,7 @@ cat << EOF
      pbb we don't want to call pbb_begin each time (which hashes on the pc
      and does a table lookup).  A way to speed this up is to save vpc
      between calls.  */
-  vpc = ${cpu}_pbb_begin (current_cpu, FAST_P);
+  vpc = @prefix@_pbb_begin (current_cpu, FAST_P);
 
   do
     {
@@ -912,7 +1245,7 @@ if [ x$fast = xyes ] ; then
 #define FAST_P 1
 
 void
-${cpu}_engine_run_fast (SIM_CPU *current_cpu)
+@prefix@_engine_run_fast (SIM_CPU *current_cpu)
 {
   SIM_DESC current_state = CPU_STATE (current_cpu);
   SCACHE *scache = CPU_SCACHE_CACHE (current_cpu);
@@ -920,19 +1253,21 @@ ${cpu}_engine_run_fast (SIM_CPU *current_cpu)
   SEM_PC vpc;
 #if WITH_SEM_SWITCH_FAST
   /* For communication between cti's and cti-chain.  */
+  SEM_BRANCH_TYPE pbb_br_type;
   PCADDR pbb_br_npc;
-  SEM_PC *pbb_br_npc_ptr;
 #endif
 
 EOF
 
-if [ x$parallel = xyes ] ; then
-  cat << EOF
+case x$parallel in
+xread | xwrite)
+    cat << EOF
   PAREXEC pbufs[MAX_PARALLEL_INSNS];
   PAREXEC *par_exec = &pbufs[0];
 
 EOF
-fi
+    ;;
+esac
 
 # Any initialization code before looping starts.
 # Note that this code may declare some locals.
@@ -948,17 +1283,21 @@ cat << EOF
         a pbb).  And in the "let's run until we're done" case we don't return
         until the program exits.  */
 
-#if WITH_SEM_SWITCH_FAST && defined (__GNUC__)
+#if WITH_SEM_SWITCH_FAST
+#if defined (__GNUC__)
 /* ??? Later maybe paste sem-switch.c in when building mainloop.c.  */
 #define DEFINE_LABELS
 #include "$switch"
+#endif
+#else
+      @prefix@_semf_init_idesc_table (current_cpu);
 #endif
 
       /* Initialize the "begin (compile) a pbb" virtual insn.  */
       vpc = CPU_SCACHE_PBB_BEGIN (current_cpu);
       SEM_SET_FAST_CODE (SEM_ARGBUF (vpc),
-                        & CPU_IDESC (current_cpu) [${CPU}_INSN_X_BEGIN]);
-      vpc->argbuf.idesc = & CPU_IDESC (current_cpu) [${CPU}_INSN_X_BEGIN];
+                        & CPU_IDESC (current_cpu) [@PREFIX@_INSN_X_BEGIN]);
+      vpc->argbuf.idesc = & CPU_IDESC (current_cpu) [@PREFIX@_INSN_X_BEGIN];
 
       CPU_IDESC_SEM_INIT_P (current_cpu) = 1;
     }
@@ -968,7 +1307,7 @@ cat << EOF
      pbb we don't want to call pbb_begin each time (which hashes on the pc
      and does a table lookup).  A way to speed this up is to save vpc
      between calls.  */
-  vpc = ${cpu}_pbb_begin (current_cpu, FAST_P);
+  vpc = @prefix@_pbb_begin (current_cpu, FAST_P);
 
   do
     {
@@ -990,8 +1329,11 @@ fi # -fast
 
 fi # -pbb
 
-sed -e "s/@cpu@/$cpu/g" -e "s/@CPU@/$CPU/g" < tmp-mloop.cin > mloop.cin
+# Expand @..@ macros appearing in tmp-mloop-{pid}.cin.
+sed \
+  -e "s/@cpu@/$cpu/g" -e "s/@CPU@/$CPU/g" \
+  -e "s/@prefix@/$prefix/g" -e "s/@PREFIX@/$PREFIX/g" < tmp-mloop-$$.cin > mloop${outsuffix}.cin
 rc=$?
-rm -f tmp-mloop.cin
+rm -f tmp-mloop-$$.cin
 
 exit $rc