-# This shell script emits a C file. -*- C -*-
# Generate the main loop of the simulator.
-# Syntax: genmloop.sh /bin/sh [options] cpu mainloop.in
-# Options: [-mono|-multi] -scache -fast -parallel
+# Copyright (C) 1996, 1997, 1998, 1999 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.
+#
+# 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.
+#
+# This file creates two files: eng.hin and mloop.cin.
+# eng.hin defines a few macros that specify what kind of engine was selected
+# based on the arguments to this script.
+# mloop.cin contains the engine.
+#
+# ??? 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]
+#
+# Options:
+#
+# -mono | -multi
+# - specify single cpu or multiple cpus (number specifyable at runtime),
+# maximum number is a configuration parameter
+# - -multi wip
#
-# -scache: use the scache
# -fast: include support for fast execution in addition to full featured mode
-# -parallel: cpu can execute multiple instructions parallely
#
-# FIXME: "multi" support is wip.
-
+# Full featured mode is for tracing, profiling, etc. and is always
+# provided. Fast mode contains no frills, except speed.
+# A target need only provide a "full" version of one of
+# simple,scache,pbb. If the target wants it can also provide a fast
+# version of same. It can't provide more than this.
+# ??? Later add ability to have another set of full/fast semantics
+# for use in with-devices/with-smp situations (pbb can be inappropriate
+# here).
+#
+# -full-switch: same as -fast but for full featured version of -switch
+# Only needed if -fast present.
+#
+# -simple: simple execution engine (the default)
+#
+# This engine fetches and executes one instruction at a time.
+# 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)
+#
+# This engine caches the extracted instruction before executing it.
+# When executing instructions they are first looked up in the scache.
+#
+# -pbb: same as -scache but extract a (pseudo-) basic block at a time
+#
+# This engine is basically identical to the scache version except that
+# extraction is done a pseudo-basic-block at a time and the address of
+# the scache entry of a branch target is recorded as well.
+# Additional speedups are then possible by defering Ctrl-C checking
+# to the end of basic blocks and by threading the insns together.
+# We call them pseudo-basic-block's instead of just basic-blocks because
+# they're not necessarily basic-blocks, though normally are.
+#
+# -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.
+#
+# 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.
+#
+# -switch file: specify file containing semantics implemented as a switch()
+#
+# -cpu <cpu-family>
+#
+# Specify the cpu family name.
+#
+# -infile <input-file>
+#
+# Specify the mainloop.in input file.
+#
+# Only one of -scache/-pbb may be selected.
+# -simple is the default.
+#
+####
+#
# TODO
-# - move this C code to mainloop.in
-# - keep genmloop.sh
-# - build exec.in from .cpu file
-# - have each cpu provide handwritten cycle.in
-# - integrate with common/sim-engine.[ch]
-# - for sparc, have two main loops, outer one handles delay slot when npc != 0
-# - inner loop does not handle delay slots, pc = pc + 4
+# - build mainloop.in from .cpu file
type=mono
#scache=
#fast=
-#parallel=
-
-shell=$1 ; shift
-
-while true
+#full_switch=
+#pbb=
+parallel=no
+parallel_only=no
+switch=
+cpu="unknown"
+infile=""
+
+while test $# -gt 0
do
case $1 in
-mono) type=mono ;;
-multi) type=multi ;;
- -no-scache) ;;
- -scache) scache=yes ;;
-no-fast) ;;
-fast) fast=yes ;;
+ -full-switch) full_switch=yes ;;
+ -simple) ;;
+ -scache) scache=yes ;;
+ -pbb) pbb=yes ;;
-no-parallel) ;;
- -parallel) parallel=yes ;;
- *) break ;;
+ -parallel-read) parallel=read ;;
+ -parallel-write) parallel=write ;;
+ -parallel-generic-write) parallel=genwrite ;;
+ -parallel-only) parallel_only=yes ;;
+ -switch) shift ; switch=$1 ;;
+ -cpu) shift ; cpu=$1 ;;
+ -infile) shift ; infile=$1 ;;
+ *) echo "unknown option: $1" >&2 ; exit 1 ;;
esac
shift
done
-cpu=$1
-file=$2
+# Argument validation.
-cat <<EOF
-/* This file is is generated by the genmloop script. DO NOT EDIT! */
+if [ x$scache = xyes -a x$pbb = xyes ] ; then
+ echo "only one of -scache and -pbb may be selected" >&2
+ exit 1
+fi
-/* Main loop for CGEN-based simulators.
- Copyright (C) 1996, 1997 Free Software Foundation, Inc.
- Contributed by Cygnus Support.
+if [ "x$cpu" = xunknown ] ; then
+ echo "cpu family not specified" >&2
+ exit 1
+fi
-This file is part of the GNU simulators.
+if [ "x$infile" = x ] ; then
+ echo "mainloop.in not specified" >&2
+ exit 1
+fi
-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.
+lowercase='abcdefghijklmnopqrstuvwxyz'
+uppercase='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+CPU=`echo ${cpu} | tr "${lowercase}" "${uppercase}"`
-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. */
+rm -f eng.hin
+exec 1>eng.hin
-/* We want the scache version of SEM_ARG.
- This is used by the switch() version of the semantic code. */
-EOF
+echo "/* engine configuration for ${cpu} */"
+echo ""
-if [ x$scache = xyes ] ; then
- echo "#define SCACHE_P"
+echo "/* WITH_FAST: non-zero if a fast version of the engine is available"
+echo " in addition to the full-featured version. */"
+if [ x$fast = xyes ] ; then
+ echo "#define WITH_FAST 1"
else
- echo '/*#define SCACHE_P*/'
- echo '#undef WITH_SCACHE'
- echo '#define WITH_SCACHE 0'
+ echo "#define WITH_FAST 0"
fi
-cat <<EOF
+echo ""
+echo "/* WITH_SCACHE_PBB_${CPU}: non-zero if the pbb engine was selected. */"
+if [ x$pbb = xyes ] ; then
+ echo "#define WITH_SCACHE_PBB_${CPU} 1"
+else
+ echo "#define WITH_SCACHE_PBB_${CPU} 0"
+fi
-#define WANT_CPU
+echo ""
+echo "/* HAVE_PARALLEL_INSNS: non-zero if cpu can parallelly execute > 1 insn. */"
+# 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 ""
+ echo "/* WITH_SEM_SWITCH_FULL: non-zero if full-featured engine is"
+ echo " implemented as a switch(). */"
+ if [ x$fast != xyes -o x$full_switch = xyes ] ; then
+ echo "#define WITH_SEM_SWITCH_FULL 1"
+ else
+ echo "#define WITH_SEM_SWITCH_FULL 0"
+ fi
+ echo ""
+ echo "/* WITH_SEM_SWITCH_FAST: non-zero if fast engine is"
+ echo " implemented as a switch(). */"
+ if [ x$fast = xyes ] ; then
+ echo "#define WITH_SEM_SWITCH_FAST 1"
+ else
+ echo "#define WITH_SEM_SWITCH_FAST 0"
+ fi
+fi
+
+# Decls of functions we define.
+
+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;"
+
+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_BRANCH_TYPE, PCADDR);"
+ echo "extern void ${cpu}_pbb_before (SIM_CPU *, SCACHE *);"
+ echo "extern void ${cpu}_pbb_after (SIM_CPU *, SCACHE *);"
+fi
+
+##########################################################################
+
+rm -f tmp-mloop.cin mloop.cin
+exec 1>tmp-mloop.cin
+
+# 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! */
+
+/* Enable switch() support in cgen headers. */
+#define SEM_IN_SWITCH
+
+#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"
-/* Tell sim_main_loop to use the cache if it's active.
- Collecting profile data and tracing slow us down so we don't do them in
- "fast mode".
- There are 2 possibilities on 2 axes:
- - use or don't use the cache
- - run normally (full featured) or run fast
- Supporting all four possibilities in one executable is a bit much but
- supporting full/fast seems reasonable.
- If the cache is configured in it is always used.
- ??? Need to see whether it speeds up profiling significantly or not.
- Speeding up tracing doesn't seem worth it.
- ??? Sometimes supporting more than one set of semantic functions will make
- the simulator too large - this should be configurable.
-*/
+/* Fill in the administrative ARGBUF fields required by all insns,
+ virtual and real. */
+static INLINE void
+@cpu@_fill_argbuf (const SIM_CPU *cpu, ARGBUF *abuf, const IDESC *idesc,
+ PCADDR pc, int fast_p)
+{
#if WITH_SCACHE
-#define RUN_FAST_P(cpu) (STATE_RUN_FAST_P (CPU_STATE (cpu)))
-#else
-#define RUN_FAST_P(cpu) 0
+ SEM_SET_CODE (abuf, idesc, fast_p);
+ ARGBUF_ADDR (abuf) = pc;
#endif
+ ARGBUF_IDESC (abuf) = idesc;
+}
-#ifndef SIM_PRE_EXEC_HOOK
-#define SIM_PRE_EXEC_HOOK(state)
-#endif
+/* Fill in tracing/profiling fields of an ARGBUF. */
-#ifndef SIM_POST_EXEC_HOOK
-#define SIM_POST_EXEC_HOOK(state)
-#endif
+static INLINE void
+@cpu@_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 0 /* FIXME:experiment */
-/* "sc" is local to the calling function.
- It is done this way to keep the internals of the implementation out of
- the description file. */
-#define EXTRACT(cpu, pc, insn, sc, num, fast_p) \
-@cpu@_extract (cpu, pc, insn, sc + num, fast_p)
+#if WITH_SCACHE_PBB
-#define EXECUTE(cpu, sc, num, fast_p) \
-@cpu@_execute (cpu, sc + num, fast_p)
-#endif
+/* 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
+@cpu@_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) [@CPU@_INSN_X_BEFORE];
-#define GET_ATTR(cpu, num, attr) \
-CGEN_INSN_ATTR (sc[num].argbuf.opcode, CGEN_INSN_##attr)
+ abuf->fields.before.first_p = first_p;
+ @cpu@_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
+@cpu@_emit_after (SIM_CPU *current_cpu, SCACHE *sc, PCADDR pc)
+{
+ ARGBUF *abuf = &sc[0].argbuf;
+ const IDESC *id = & CPU_IDESC (current_cpu) [@CPU@_INSN_X_AFTER];
+
+ @cpu@_fill_argbuf (current_cpu, abuf, id, pc, 0);
+ /* no need to set trace_p,profile_p */
+}
+
+#endif /* WITH_SCACHE_PBB */
EOF
-${SHELL} $file support
+${SHELL} $infile support
-cat <<EOF
+##########################################################################
-static volatile int keep_running;
-/* Want to measure simulator speed even in fast mode. */
-static unsigned long insn_count;
-static SIM_ELAPSED_TIME start_time;
+# 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.
-/* Forward decls of cpu-specific functions. */
-static void engine_resume (SIM_DESC, int, int);
-static void engine_resume_full (SIM_DESC);
-${scache+static void engine_resume_fast (SIM_DESC);}
+if [ x$scache != xyes -a x$pbb != xyes ] ; then
-int
-@cpu@_engine_stop (SIM_DESC sd)
-{
- keep_running = 0;
- return 1;
-}
+ cat << EOF
+
+#define FAST_P 0
void
-@cpu@_engine_run (SIM_DESC sd, int step, int siggnal)
+@cpu@_engine_run_full (SIM_CPU *current_cpu)
{
-#if WITH_SCACHE
- if (USING_SCACHE_P (sd))
- scache_flush (sd);
+#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
+
+case x$parallel in
+xread | xwrite)
+ cat << EOF
+ PAREXEC pbufs[MAX_PARALLEL_INSNS];
+ PAREXEC *par_exec;
+
+EOF
+ ;;
+esac
+
+# 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))
+ {
+/* ??? Later maybe paste read.c in when building mainloop.c. */
+#define DEFINE_LABELS
+#include "readx.c"
+ CPU_IDESC_READ_INIT_P (current_cpu) = 1;
+ }
+ }
#endif
- engine_resume (sd, step, siggnal);
-}
-static void
-engine_resume (SIM_DESC sd, int step, int siggnal)
-{
- sim_cpu *current_cpu = STATE_CPU (sd, 0);
- /* These are volatile to survive setjmp. */
- volatile sim_cpu *cpu = current_cpu;
- volatile sim_engine *engine = STATE_ENGINE (sd);
- jmp_buf buf;
- int jmpval;
-
- keep_running = ! step;
- start_time = sim_elapsed_time_get ();
- /* FIXME: Having this global can slow things down a teensy bit.
- After things are working see about moving engine_resume_{full,fast}
- back into this function. */
- insn_count = 0;
-
- engine->jmpbuf = &buf;
- if (setjmp (buf))
+EOF
+fi
+
+cat << EOF
+
+ if (! CPU_IDESC_SEM_INIT_P (current_cpu))
{
- engine->jmpbuf = NULL;
- TRACE_INSN_FINI ((sim_cpu *) cpu);
- PROFILE_EXEC_TIME (CPU_PROFILE_DATA (cpu))
- += sim_elapsed_time_since (start_time);
- PROFILE_TOTAL_INSN_COUNT (CPU_PROFILE_DATA (cpu))
- += insn_count;
- return;
+#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
+ @cpu@_sem_init_idesc_table (current_cpu);
+#endif
+ CPU_IDESC_SEM_INIT_P (current_cpu) = 1;
}
- /* ??? Restart support to be added in time. */
+ do
+ {
+/* begin full-exec-simple */
+EOF
+
+${SHELL} $infile full-exec-simple
+
+cat << EOF
+/* end full-exec-simple */
+
+ ++ CPU_INSN_COUNT (current_cpu);
+ }
+ while (0 /*CPU_RUNNING_P (current_cpu)*/);
+}
- /* The computed goto switch can be used, and while the number of blocks
- may swamp the relatively few that this function contains, when running
- with the scache we put the actual semantic code in their own
- functions. */
+#undef FAST_P
EOF
+####################################
+
+# Simple engine: fast version.
+# ??? A somewhat dubious effort, but for completeness' sake.
+
if [ x$fast = xyes ] ; then
- cat <<EOF
- if (step
- || !RUN_FAST_P (current_cpu))
- engine_resume_full (sd);
- else
- engine_resume_fast (sd);
-EOF
-else
- cat <<EOF
- engine_resume_full (sd);
-EOF
-fi
-cat <<EOF
+ cat << EOF
- /* If the loop exits, either we single-stepped or engine_stop was called.
- In either case we need to call engine_halt: to properly exit this
- function we must go through the setjmp executed above. */
- if (step)
- sim_engine_halt (sd, current_cpu, NULL, NULL_CIA, sim_stopped, SIM_SIGTRAP);
- sim_engine_halt (sd, current_cpu, NULL, NULL_CIA, sim_stopped, SIM_SIGINT);
-}
+#define FAST_P 1
+
+FIXME: "fast simple version unimplemented, delete -fast arg to genmloop.sh."
+
+#undef FAST_P
EOF
-if [ x$scache = xyes ] ; then
- cat <<EOF
+fi # -fast
-static void
-engine_resume_full (SIM_DESC sd)
-{
-#define FAST_P 0
- /* current_{state,cpu} exist for the generated code to use. */
- SIM_DESC current_state = sd;
- sim_cpu *current_cpu = STATE_CPU (sd, 0);
-${parallel+ int icount = 0;}
+fi # simple engine
-EOF
+##########################################################################
-# Any initialization code before looping starts.
-${SHELL} $file init
+# Non-parallel scache engine: lookup insn in scache, fetch if missing,
+# then execute it.
-cat <<EOF
+if [ x$scache = xyes -a x$parallel = xno ] ; then
- do
+ cat << EOF
+
+static INLINE SCACHE *
+@cpu@_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)
{
- /* FIXME: Later check every insn for events and such. */
+ if (! FAST_P)
+ PROFILE_COUNT_SCACHE_MISS (current_cpu);
- SIM_PRE_EXEC_HOOK (current_cpu);
+/* begin extract-scache */
+EOF
- {
- unsigned int hash;
- SCACHE *sc;
- PCADDR pc = PC;
+${SHELL} $infile extract-scache
+
+cat << EOF
+/* end extract-scache */
+ }
+ 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);
+ }
- /* First step: look up current insn in hash table. */
- hash = SCACHE_HASH_PC (sd, pc);
- sc = CPU_SCACHE_CACHE (current_cpu) + hash;
+ return sc;
+}
- /* If the entry isn't the one we want (cache miss),
- fetch and decode the instruction. */
- if (sc->argbuf.addr != pc)
- {
- insn_t insn;
+#define FAST_P 0
- PROFILE_COUNT_SCACHE_MISS (current_cpu);
+void
+@cpu@_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;
-/* begin full-extract-scache */
EOF
-${SHELL} $file full-extract-scache
+# Any initialization code before looping starts.
+# Note that this code may declare some locals.
+${SHELL} $infile init
-cat <<EOF
-/* end full-extract-scache */
- }
- else
- {
- PROFILE_COUNT_SCACHE_HIT (current_cpu);
- }
+cat << EOF
+
+ if (! CPU_IDESC_SEM_INIT_P (current_cpu))
+ {
+#if ! WITH_SEM_SWITCH_FULL
+ @cpu@_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
-${SHELL} $file full-exec-scache
+${SHELL} $infile full-exec-scache
-cat <<EOF
+cat << EOF
/* end full-exec-scache */
- }
- SIM_POST_EXEC_HOOK (current_cpu);
+ SET_H_PC (vpc);
- ++insn_count;
+ ++ CPU_INSN_COUNT (current_cpu);
}
- while (keep_running);
-#undef FAST_P
+ while (0 /*CPU_RUNNING_P (current_cpu)*/);
}
+
+#undef FAST_P
+
EOF
-else # ! WITH_SCACHE
- cat <<EOF
+####################################
+
+# Non-parallel scache engine: fast version.
+
+if [ x$fast = xyes ] ; then
+
+ cat << EOF
+
+#define FAST_P 1
-static void
-engine_resume_full (SIM_DESC sd)
+void
+@cpu@_engine_run_fast (SIM_CPU *current_cpu)
{
-#define FAST_P 0
- SIM_DESC current_state = sd;
- sim_cpu *current_cpu = STATE_CPU (sd, 0);
- SCACHE cache[MAX_LIW_INSNS];
- SCACHE *sc = &cache[0];
-${parallel+ int icount = 0;}
+ 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.
-${SHELL} $file init
+# Note that this code may declare some locals.
+${SHELL} $infile init
-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
+ @cpu@_semf_init_idesc_table (current_cpu);
+#endif
+ CPU_IDESC_SEM_INIT_P (current_cpu) = 1;
+ }
+
+ vpc = GET_H_PC ();
do
{
- /* FIXME: Later check every insn for events and such. */
+ SCACHE *sc;
- SIM_PRE_EXEC_HOOK (current_cpu);
+ sc = @cpu@_scache_lookup (current_cpu, vpc, scache, hash_mask, FAST_P);
- {
-/* begin full-{extract,exec}-noscache */
+/* begin fast-exec-scache */
EOF
-${SHELL} $file full-extract-noscache
-echo ""
-${SHELL} $file full-exec-noscache
+${SHELL} $infile fast-exec-scache
-cat <<EOF
-/* end full-{extract,exec}-noscache */
- }
+cat << EOF
+/* end fast-exec-scache */
- SIM_POST_EXEC_HOOK (current_cpu);
+ SET_H_PC (vpc);
- ++insn_count;
+ ++ CPU_INSN_COUNT (current_cpu);
}
- while (keep_running);
+ 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 *
+@cpu@_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
+
+void
+@cpu@_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))
+ {
+/* ??? Later maybe paste read.c in when building mainloop.c. */
+#define DEFINE_LABELS
+#include "readx.c"
+ CPU_IDESC_READ_INIT_P (current_cpu) = 1;
+ }
+ }
+#endif
+
+EOF
+fi
+
+cat << EOF
+
+ if (! CPU_IDESC_SEM_INIT_P (current_cpu))
+ {
+#if ! WITH_SEM_SWITCH_FULL
+ @cpu@_sem_init_idesc_table (current_cpu);
+#endif
+ CPU_IDESC_SEM_INIT_P (current_cpu) = 1;
+ }
+
+ vpc = GET_H_PC ();
+
+ do
+ {
+/* begin full-exec-scache */
+EOF
+
+${SHELL} $infile full-exec-scache
+
+cat << EOF
+/* end full-exec-scache */
+ }
+ while (0 /*CPU_RUNNING_P (current_cpu)*/);
}
+#undef FAST_P
+
EOF
-fi # ! WITH_SCACHE
+
+####################################
+
+# Parallel scache engine: fast version.
if [ x$fast = xyes ] ; then
- if [ x$scache = xyes ] ; then
- cat <<EOF
-static void
-engine_resume_fast (SIM_DESC sd)
-{
+ cat << EOF
+
#define FAST_P 1
- SIM_DESC current_state = sd;
- sim_cpu *current_cpu = STATE_CPU (sd, 0);
-${parallel+ int icount = 0;}
+
+void
+@cpu@_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;
+ PAREXEC pbufs[MAX_PARALLEL_INSNS];
+ PAREXEC *par_exec;
EOF
# Any initialization code before looping starts.
-${SHELL} $file init
+# Note that this code may declare some locals.
+${SHELL} $infile init
-cat <<EOF
+if [ x$parallel = xread ] ; then
+cat << EOF
-#if defined (WITH_SEM_SWITCH_FAST) && defined (__GNUC__)
+#if defined (__GNUC__)
{
- static decode_init_p = 0;
- if (! decode_init_p)
+ if (! CPU_IDESC_READ_INIT_P (current_cpu))
{
-/* ??? Later maybe paste sem-switch.c in when building mainloop.c. */
+/* ??? Later maybe paste read.c in when building mainloop.c. */
#define DEFINE_LABELS
-#include "sem-switch.c"
- decode_init_p = 1;
+#include "readx.c"
+ CPU_IDESC_READ_INIT_P (current_cpu) = 1;
}
}
#endif
+EOF
+fi
+
+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
+ @cpu@_semf_init_idesc_table (current_cpu);
+#endif
+ CPU_IDESC_SEM_INIT_P (current_cpu) = 1;
+ }
+
+ vpc = GET_H_PC ();
+
do
{
- {
- unsigned int hash;
- SCACHE *sc;
- PCADDR pc = PC;
+/* begin fast-exec-scache */
+EOF
- /* First step: look up current insn in hash table. */
- hash = SCACHE_HASH_PC (sd, pc);
- sc = CPU_SCACHE_CACHE (current_cpu) + hash;
+${SHELL} $infile fast-exec-scache
- /* If the entry isn't the one we want (cache miss),
- fetch and decode the instruction. */
- if (sc->argbuf.addr != pc)
- {
- insn_t insn;
+cat << EOF
+/* end fast-exec-scache */
+ }
+ while (0 /*CPU_RUNNING_P (current_cpu)*/);
+}
+
+#undef FAST_P
-/* begin fast-extract-scache */
EOF
-${SHELL} $file fast-extract-scache
+fi # -fast
-cat <<EOF
-/* end fast-extract-scache */
- }
+fi # -scache && parallel
-/* begin fast-exec-scache */
+##########################################################################
+
+# Compilation engine: lookup insn in scache, extract a pbb
+# (pseudo-basic-block) if missing, then execute the pbb.
+# A "pbb" is a sequence of insns up to the next cti insn or until
+# some prespecified maximum.
+# CTI: control transfer instruction.
+
+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)
+/* Record number of [real] insns in pbb. */
+#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)
+{
+ SEM_PC new_vpc;
+ PCADDR pc;
+ SCACHE *sc;
+ int max_insns = CPU_SCACHE_MAX_CHAIN_LENGTH (current_cpu);
+
+ pc = GET_H_PC ();
+
+ new_vpc = scache_lookup_or_alloc (current_cpu, pc, max_insns, &sc);
+ if (! new_vpc)
+ {
+ /* Leading '_' to avoid collision with mainloop.in. */
+ int _insn_count = 0;
+ SCACHE *orig_sc = sc;
+ SCACHE *_cti_sc = NULL;
+ int slice_insns = CPU_MAX_SLICE_INSNS (current_cpu);
+
+ /* First figure out how many instructions to compile.
+ MAX_INSNS is the size of the allocated buffer, which includes space
+ for before/after handlers if they're being used.
+ SLICE_INSNS is the maxinum number of real insns that can be
+ executed. Zero means "as many as we want". */
+ /* ??? max_insns is serving two incompatible roles.
+ 1) Number of slots available in scache buffer.
+ 2) Number of real insns to execute.
+ They're incompatible because there are virtual insns emitted too
+ (chain,cti-chain,before,after handlers). */
+
+ if (slice_insns == 1)
+ {
+ /* No need to worry about extra slots required for virtual insns
+ and parallel exec support because MAX_CHAIN_LENGTH is
+ guaranteed to be big enough to execute at least 1 insn! */
+ max_insns = 1;
+ }
+ else
+ {
+ /* Allow enough slop so that while compiling insns, if max_insns > 0
+ then there's guaranteed to be enough space to emit one real insn.
+ MAX_CHAIN_LENGTH is typically much longer than
+ the normal number of insns between cti's anyway. */
+ max_insns -= (1 /* one for the trailing chain insn */
+ + (FAST_P
+ ? 0
+ : (1 + MAX_PARALLEL_INSNS) /* before+after */)
+ + (MAX_PARALLEL_INSNS > 1
+ ? (MAX_PARALLEL_INSNS * 2)
+ : 0));
+
+ /* Account for before/after handlers. */
+ if (! FAST_P)
+ slice_insns *= 3;
+
+ if (slice_insns > 0
+ && slice_insns < max_insns)
+ max_insns = slice_insns;
+ }
+
+ new_vpc = sc;
+
+ /* SC,PC must be updated to point passed the last entry used.
+ SET_CTI_VPC must be called if pbb is terminated by a cti.
+ SET_INSN_COUNT must be called to record number of real insns in
+ pbb [could be computed by us of course, extra cpu but perhaps
+ negligible enough]. */
+
+/* begin extract-pbb */
EOF
-${SHELL} $file fast-exec-scache
+${SHELL} $infile extract-pbb
-cat <<EOF
-/* end fast-exec-scache */
+cat << EOF
+/* end extract-pbb */
+ /* The last one is a pseudo-insn to link to the next chain.
+ It is also used to record the insn count for this chain. */
+ {
+ const IDESC *id;
+
+ /* Was pbb terminated by a cti? */
+ if (_cti_sc)
+ {
+ id = & CPU_IDESC (current_cpu) [@CPU@_INSN_X_CTI_CHAIN];
+ }
+ else
+ {
+ id = & CPU_IDESC (current_cpu) [@CPU@_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.next = 0;
+ sc->argbuf.fields.chain.branch_target = 0;
+ ++sc;
}
- ++insn_count;
+ /* 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
+ max_insns too. */
+ if (! FAST_P)
+ PROFILE_COUNT_SCACHE_CHAIN_LENGTH (current_cpu, sc - orig_sc);
}
- while (keep_running);
-#undef FAST_P
+
+ return new_vpc;
}
-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)
+{
+ ARGBUF *abuf = SEM_ARGBUF (sem_arg);
+
+ PBB_UPDATE_INSN_COUNT (current_cpu, sem_arg);
+
+ SET_H_PC (abuf->addr);
+
+ /* If not running forever, exit back to main loop. */
+ 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. */
+ if (abuf->fields.chain.next)
+ return abuf->fields.chain.next;
+ /* See if next block has already been compiled. */
+ abuf->fields.chain.next = scache_lookup (current_cpu, abuf->addr);
+ if (abuf->fields.chain.next)
+ return abuf->fields.chain.next;
+ /* Nope, so next insn is a virtual insn to invoke the compiler
+ (begin a pbb). */
+ return CPU_SCACHE_PBB_BEGIN (current_cpu);
+}
-else # ! WITH_SCACHE
- cat <<EOF
+/* Chain to the next block from a cti terminated previous block.
+ 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
+ BR_TYPE != SEM_BRANCH_UNTAKEN. */
-static void
-engine_resume_fast (SIM_DESC sd)
+INLINE SEM_PC
+@cpu@_pbb_cti_chain (SIM_CPU *current_cpu, SEM_ARG sem_arg,
+ SEM_BRANCH_TYPE br_type, PCADDR new_pc)
{
-#define FAST_P 1
- SIM_DESC current_state = sd;
- sim_cpu *current_cpu = STATE_CPU (sd, 0);
- SCACHE cache[MAX_LIW_INSNS];
- SCACHE *sc = &cache[0];
-${parallel+ int icount = 0;}
+ 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
+ /* 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 (br_type == SEM_BRANCH_UNCACHEABLE)
+ {
+ SET_H_PC (new_pc);
+ return CPU_SCACHE_PBB_BEGIN (current_cpu);
+ }
+
+ /* If branch wasn't taken, update the pc and set BR_ADDR_PTR to our
+ next chain ptr. */
+ if (br_type == SEM_BRANCH_UNTAKEN)
+ {
+ 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, new_pc);
+ if (*new_vpc_ptr)
+ return *new_vpc_ptr;
+ /* Nope, so next insn is a virtual insn to invoke the compiler
+ (begin a pbb). */
+ return CPU_SCACHE_PBB_BEGIN (current_cpu);
+}
+
+/* x-before handler.
+ This is called before each insn. */
+
+void
+@cpu@_pbb_before (SIM_CPU *current_cpu, SCACHE *sc)
+{
+ SEM_ARG sem_arg = sc;
+ const ARGBUF *abuf = SEM_ARGBUF (sem_arg);
+ int first_p = abuf->fields.before.first_p;
+ const ARGBUF *cur_abuf = SEM_ARGBUF (sc + 1);
+ const IDESC *cur_idesc = cur_abuf->idesc;
+ PCADDR pc = cur_abuf->addr;
+
+ 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. */
+
+ if (! first_p)
+ {
+ if (PROFILE_MODEL_P (current_cpu))
+ {
+ 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;
+
+ /* ??? 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);
+ @cpu@_model_insn_after (current_cpu, 0 /*last_p*/, cycles);
+ }
+ }
+
+ 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)
+ && ARGBUF_PROFILE_P (cur_abuf))
+ @cpu@_model_insn_before (current_cpu, first_p);
+
+ TRACE_INSN_INIT (current_cpu, cur_abuf, first_p);
+ TRACE_INSN (current_cpu, cur_idesc->idata, cur_abuf, pc);
+}
+
+/* x-after handler.
+ This is called after a serial insn or at the end of a group of parallel
+ insns. */
+
+void
+@cpu@_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);
+
+ /* ??? May want to measure all insns if doing insn tracing. */
+ if (PROFILE_MODEL_P (current_cpu)
+ && ARGBUF_PROFILE_P (prev_abuf))
+ {
+ 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);
+ }
+ TRACE_INSN_FINI (current_cpu, prev_abuf, 1 /*last_p*/);
+}
+
+#define FAST_P 0
+
+void
+@cpu@_engine_run_full (SIM_CPU *current_cpu)
+{
+ SIM_DESC current_state = CPU_STATE (current_cpu);
+ SCACHE *scache = CPU_SCACHE_CACHE (current_cpu);
+ /* virtual program counter */
+ 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;
+#endif
+
+EOF
+
+case x$parallel in
+xread | xwrite)
+ cat << EOF
+ PAREXEC pbufs[MAX_PARALLEL_INSNS];
+ PAREXEC *par_exec = &pbufs[0];
EOF
+ ;;
+esac
# Any initialization code before looping starts.
-${SHELL} $file init
+# Note that this code may declare some locals.
+${SHELL} $infile init
-cat <<EOF
+cat << EOF
+
+ if (! CPU_IDESC_SEM_INIT_P (current_cpu))
+ {
+ /* ??? 'twould be nice to move this up a level and only call it once.
+ On the other hand, in the "let's go fast" case the test is only done
+ once per pbb (since we only return to the main loop at the end of
+ 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
+#if defined (__GNUC__)
+/* ??? Later maybe paste sem-switch.c in when building mainloop.c. */
+#define DEFINE_LABELS
+#include "$switch"
+#endif
+#else
+ @cpu@_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_SEM_INIT_P (current_cpu) = 1;
+ }
+
+ CPU_RUNNING_P (current_cpu) = 1;
+ /* ??? In the case where we're returning to the main loop after every
+ 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);
do
{
-/* begin fast-{extract,exec}-noscache */
+/* begin full-exec-pbb */
EOF
-${SHELL} $file fast-extract-noscache
-echo ""
-${SHELL} $file fast-exec-noscache
-
-cat <<EOF
-/* end fast-{extract,exec}-noscache */
+${SHELL} $infile full-exec-pbb
- ++insn_count;
+cat << EOF
+/* end full-exec-pbb */
}
- while (keep_running);
-#undef FAST_P
+ while (CPU_RUNNING_P (current_cpu));
}
+#undef FAST_P
+
+EOF
+
+####################################
+
+# Compile engine: fast version.
+
+if [ x$fast = xyes ] ; then
+
+ cat << EOF
+
+#define FAST_P 1
+
+void
+@cpu@_engine_run_fast (SIM_CPU *current_cpu)
+{
+ SIM_DESC current_state = CPU_STATE (current_cpu);
+ SCACHE *scache = CPU_SCACHE_CACHE (current_cpu);
+ /* virtual program counter */
+ 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;
+#endif
+
EOF
- fi # ! WITH_SCACHE
+case x$parallel in
+xread | xwrite)
+ cat << EOF
+ PAREXEC pbufs[MAX_PARALLEL_INSNS];
+ PAREXEC *par_exec = &pbufs[0];
+
+EOF
+ ;;
+esac
+
+# 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))
+ {
+ /* ??? 'twould be nice to move this up a level and only call it once.
+ On the other hand, in the "let's go fast" case the test is only done
+ once per pbb (since we only return to the main loop at the end of
+ 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
+#if defined (__GNUC__)
+/* ??? Later maybe paste sem-switch.c in when building mainloop.c. */
+#define DEFINE_LABELS
+#include "$switch"
+#endif
+#else
+ @cpu@_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_SEM_INIT_P (current_cpu) = 1;
+ }
+
+ CPU_RUNNING_P (current_cpu) = 1;
+ /* ??? In the case where we're returning to the main loop after every
+ 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);
+
+ do
+ {
+/* begin fast-exec-pbb */
+EOF
+
+${SHELL} $infile fast-exec-pbb
+
+cat << EOF
+/* end fast-exec-pbb */
+ }
+ while (CPU_RUNNING_P (current_cpu));
+}
+
+#undef FAST_P
+
+EOF
fi # -fast
+
+fi # -pbb
+
+# Process @cpu@,@CPU@ appearing in mainloop.in.
+sed -e "s/@cpu@/$cpu/g" -e "s/@CPU@/$CPU/g" < tmp-mloop.cin > mloop.cin
+rc=$?
+rm -f tmp-mloop.cin
+
+exit $rc