/* Main simulator loop for CGEN-based simulators. Copyright (C) 1998 Free Software Foundation, Inc. Contributed by Cygnus Solutions. This file is part of GDB, the GNU debugger. 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. */ /* ??? These are old notes, kept around for now. Collecting profile data and tracing slow us down so we don't do them in "fast mode". There are 6 possibilities on 2 axes: - no-scaching, insn-scaching, basic-block-scaching - run with full features or run fast Supporting all six possibilities in one executable is a bit much but supporting full/fast seems reasonable. If the scache is configured in it is always used. If pbb-scaching is configured in it is always used. ??? Sometimes supporting more than one set of semantic functions will make the simulator too large - this should be configurable. Blah blah blah. ??? Supporting full/fast can be more modular, blah blah blah. When the framework is more modular, this can be. */ #include "sim-main.h" #include "sim-assert.h" #ifndef SIM_ENGINE_PREFIX_HOOK #define SIM_ENGINE_PREFIX_HOOK(sd) #endif #ifndef SIM_ENGINE_POSTFIX_HOOK #define SIM_ENGINE_POSTFIX_HOOK(sd) #endif static sim_event_handler has_stepped; static void prime_cpu (SIM_CPU *, int); static void engine_run_1 (SIM_DESC, int, int); static void engine_run_n (SIM_DESC, int, int, int, int); /* sim_resume for cgen */ void sim_resume (SIM_DESC sd, int step, int siggnal) { sim_engine *engine = STATE_ENGINE (sd); jmp_buf buf; int jmpval; ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER); /* we only want to be single stepping the simulator once */ if (engine->stepper != NULL) { sim_events_deschedule (sd, engine->stepper); engine->stepper = NULL; } if (step) engine->stepper = sim_events_schedule (sd, 1, has_stepped, sd); sim_module_resume (sd); #if WITH_SCACHE if (USING_SCACHE_P (sd)) scache_flush (sd); #endif /* run/resume the simulator */ sim_engine_set_run_state (sd, sim_running, 0); engine->jmpbuf = &buf; jmpval = setjmp (buf); if (jmpval == sim_engine_start_jmpval || jmpval == sim_engine_restart_jmpval) { int last_cpu_nr = sim_engine_last_cpu_nr (sd); int next_cpu_nr = sim_engine_next_cpu_nr (sd); int nr_cpus = sim_engine_nr_cpus (sd); /* ??? Setting max_insns to 0 allows pbb/jit code to run wild and is useful if all one wants to do is run a benchmark. Need some better way to identify this case. */ int max_insns = (step ? 1 : (nr_cpus == 1 /*&& wip:no-events*/ /* Don't do this if running under gdb, need to poll ui for events. */ && STATE_OPEN_KIND (sd) == SIM_OPEN_STANDALONE) ? 0 : 8); /*FIXME: magic number*/ int fast_p = STATE_RUN_FAST_P (sd); sim_events_preprocess (sd, last_cpu_nr >= nr_cpus, next_cpu_nr >= nr_cpus); if (next_cpu_nr >= nr_cpus) next_cpu_nr = 0; if (nr_cpus == 1) engine_run_1 (sd, max_insns, fast_p); else engine_run_n (sd, next_cpu_nr, nr_cpus, max_insns, fast_p); } #if 1 /*wip*/ else { /* Account for the last insn executed. */ SIM_CPU *cpu = STATE_CPU (sd, sim_engine_last_cpu_nr (sd)); ++ CPU_INSN_COUNT (cpu); TRACE_INSN_FINI (cpu, NULL, 1); } #endif engine->jmpbuf = NULL; { int i; int nr_cpus = sim_engine_nr_cpus (sd); #if 0 /*wip,ignore*/ /* If the loop exits, either we single-stepped or @cpu@_engine_stop was called. */ if (step) sim_engine_set_run_state (sd, sim_stopped, SIM_SIGTRAP); else sim_engine_set_run_state (sd, pending_reason, pending_sigrc); #endif for (i = 0; i < nr_cpus; ++i) { SIM_CPU *cpu = STATE_CPU (sd, i); PROFILE_TOTAL_INSN_COUNT (CPU_PROFILE_DATA (cpu)) += CPU_INSN_COUNT (cpu); } } sim_module_suspend (sd); } /* Halt the simulator after just one instruction. */ static void has_stepped (SIM_DESC sd, void *data) { ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER); sim_engine_halt (sd, NULL, NULL, NULL_CIA, sim_stopped, SIM_SIGTRAP); } /* Prepare a cpu for running. MAX_INSNS is the number of insns to execute per time slice. If 0 it means the cpu can run as long as it wants (e.g. until the program completes). ??? Perhaps this should be an argument to the engine_fn. */ static void prime_cpu (SIM_CPU *cpu, int max_insns) { CPU_MAX_SLICE_INSNS (cpu) = max_insns; CPU_INSN_COUNT (cpu) = 0; /* Initialize the insn descriptor table. This has to be done after all initialization so we just defer it to here. */ if (MACH_PREPARE_RUN (CPU_MACH (cpu))) (* MACH_PREPARE_RUN (CPU_MACH (cpu))) (cpu); } /* Main loop, for 1 cpu. */ static void engine_run_1 (SIM_DESC sd, int max_insns, int fast_p) { sim_cpu *cpu = STATE_CPU (sd, 0); ENGINE_FN *fn = fast_p ? CPU_FAST_ENGINE_FN (cpu) : CPU_FULL_ENGINE_FN (cpu); prime_cpu (cpu, max_insns); while (1) { SIM_ENGINE_PREFIX_HOOK (sd); (*fn) (cpu); SIM_ENGINE_POSTFIX_HOOK (sd); /* process any events */ if (sim_events_tick (sd)) sim_events_process (sd); } } /* Main loop, for multiple cpus. */ static void engine_run_n (SIM_DESC sd, int next_cpu_nr, int nr_cpus, int max_insns, int fast_p) { int i; ENGINE_FN *engine_fns[MAX_NR_PROCESSORS]; for (i = 0; i < nr_cpus; ++i) { SIM_CPU *cpu = STATE_CPU (sd, i); engine_fns[i] = fast_p ? CPU_FAST_ENGINE_FN (cpu) : CPU_FULL_ENGINE_FN (cpu); prime_cpu (cpu, max_insns); } while (1) { SIM_ENGINE_PREFIX_HOOK (sd); /* FIXME: proper cycling of all of them, blah blah blah. */ while (next_cpu_nr != nr_cpus) { SIM_CPU *cpu = STATE_CPU (sd, next_cpu_nr); (* engine_fns[next_cpu_nr]) (cpu); ++next_cpu_nr; } SIM_ENGINE_POSTFIX_HOOK (sd); /* process any events */ if (sim_events_tick (sd)) sim_events_process (sd); } }