|  | /* Main simulator loop for CGEN-based simulators. | 
|  | Copyright (C) 1998-2023 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 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, see <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | /* ??? 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. | 
|  | */ | 
|  |  | 
|  | /* This must come before any other includes.  */ | 
|  | #include "defs.h" | 
|  |  | 
|  | #include "sim-main.h" | 
|  | #include "sim-assert.h" | 
|  | #include "sim-signal.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); | 
|  |  | 
|  | /* If no profiling or tracing has been enabled, run in fast mode.  */ | 
|  | static int | 
|  | cgen_get_fast_p (SIM_DESC sd) | 
|  | { | 
|  | int i, c; | 
|  | int run_fast_p = 1; | 
|  |  | 
|  | for (c = 0; c < MAX_NR_PROCESSORS; ++c) | 
|  | { | 
|  | SIM_CPU *cpu = STATE_CPU (sd, c); | 
|  |  | 
|  | if (PROFILE_ANY_P (cpu) || TRACE_ANY_P (cpu)) | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* 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; | 
|  | static int fast_p = -1; | 
|  |  | 
|  | ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER); | 
|  |  | 
|  | if (fast_p == -1) | 
|  | fast_p = cgen_get_fast_p (sd); | 
|  |  | 
|  | /* 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*/ | 
|  |  | 
|  | 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); | 
|  | CGEN_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; | 
|  | /* Ensure that engine_fns is fully initialized, this silences a compiler | 
|  | warning when engine_fns is used below.  */ | 
|  | ENGINE_FN *engine_fns[MAX_NR_PROCESSORS] = {}; | 
|  |  | 
|  | SIM_ASSERT (nr_cpus <= MAX_NR_PROCESSORS); | 
|  | SIM_ASSERT (next_cpu_nr >= 0 && next_cpu_nr < nr_cpus); | 
|  |  | 
|  | 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); | 
|  | } | 
|  | } |