diff options
author | Ben Elliston <bje@au.ibm.com> | 2002-02-01 11:44:32 +0000 |
---|---|---|
committer | Ben Elliston <bje@au.ibm.com> | 2002-02-01 11:44:32 +0000 |
commit | cbb38b47b329e8b9188f4deadf91b211116ce45c (patch) | |
tree | 3c31a730db7ff67b9321ae5f7cefd5482a901f29 /sim/sh64/mloop-media.c | |
parent | 9ee6f9cc9a4d41796ace893ba52172d97e211ae2 (diff) | |
download | gdb-cbb38b47b329e8b9188f4deadf91b211116ce45c.zip gdb-cbb38b47b329e8b9188f4deadf91b211116ce45c.tar.gz gdb-cbb38b47b329e8b9188f4deadf91b211116ce45c.tar.bz2 |
* Contribute Hitachi SH5 simulator.
Diffstat (limited to 'sim/sh64/mloop-media.c')
-rw-r--r-- | sim/sh64/mloop-media.c | 624 |
1 files changed, 624 insertions, 0 deletions
diff --git a/sim/sh64/mloop-media.c b/sim/sh64/mloop-media.c new file mode 100644 index 0000000..03c5df5 --- /dev/null +++ b/sim/sh64/mloop-media.c @@ -0,0 +1,624 @@ +/* This file is generated by the genmloop script. DO NOT EDIT! */ + +/* Enable switch() support in cgen headers. */ +#define SEM_IN_SWITCH + +#define WANT_CPU sh64 +#define WANT_CPU_SH64 + +#include "sim-main.h" +#include "bfd.h" +#include "cgen-mem.h" +#include "cgen-ops.h" +#include "sim-assert.h" + +/* Fill in the administrative ARGBUF fields required by all insns, + virtual and real. */ + +static INLINE void +sh64_media_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 +sh64_media_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 +sh64_media_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) [SH64_MEDIA_INSN_X_BEFORE]; + + abuf->fields.before.first_p = first_p; + sh64_media_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 +sh64_media_emit_after (SIM_CPU *current_cpu, SCACHE *sc, PCADDR pc) +{ + ARGBUF *abuf = &sc[0].argbuf; + const IDESC *id = & CPU_IDESC (current_cpu) [SH64_MEDIA_INSN_X_AFTER]; + + sh64_media_fill_argbuf (current_cpu, abuf, id, pc, 0); + /* no need to set trace_p,profile_p */ +} + +#endif /* WITH_SCACHE_PBB */ + + +static INLINE const IDESC * +extract (SIM_CPU *current_cpu, PCADDR pc, CGEN_INSN_INT insn, ARGBUF *abuf, + int fast_p) +{ + const IDESC *id = sh64_media_decode (current_cpu, pc, insn, insn, abuf); + + sh64_media_fill_argbuf (current_cpu, abuf, id, pc, fast_p); + if (! fast_p) + { + int trace_p = PC_IN_TRACE_RANGE_P (current_cpu, pc); + int profile_p = PC_IN_PROFILE_RANGE_P (current_cpu, pc); + sh64_media_fill_argbuf_tp (current_cpu, abuf, trace_p, profile_p); + } + return id; +} + +static INLINE SEM_PC +execute (SIM_CPU *current_cpu, SCACHE *sc, int fast_p) +{ + SEM_PC vpc; + + if (fast_p) + { +#if ! WITH_SEM_SWITCH_FAST +#if WITH_SCACHE + vpc = (*sc->argbuf.semantic.sem_fast) (current_cpu, sc); +#else + vpc = (*sc->argbuf.semantic.sem_fast) (current_cpu, &sc->argbuf); +#endif +#else + abort (); +#endif /* WITH_SEM_SWITCH_FAST */ + } + else + { +#if ! WITH_SEM_SWITCH_FULL + ARGBUF *abuf = &sc->argbuf; + const IDESC *idesc = abuf->idesc; +#if WITH_SCACHE_PBB + int virtual_p = CGEN_ATTR_VALUE (NULL, idesc->attrs, CGEN_INSN_VIRTUAL); +#else + int virtual_p = 0; +#endif + + if (! virtual_p) + { + /* FIXME: call x-before */ + if (ARGBUF_PROFILE_P (abuf)) + PROFILE_COUNT_INSN (current_cpu, abuf->addr, idesc->num); + /* FIXME: Later make cover macros: PROFILE_INSN_{INIT,FINI}. */ + if (PROFILE_MODEL_P (current_cpu) + && ARGBUF_PROFILE_P (abuf)) + sh64_media_model_insn_before (current_cpu, 1 /*first_p*/); + TRACE_INSN_INIT (current_cpu, abuf, 1); + TRACE_INSN (current_cpu, idesc->idata, + (const struct argbuf *) abuf, abuf->addr); + } +#if WITH_SCACHE + vpc = (*sc->argbuf.semantic.sem_full) (current_cpu, sc); +#else + vpc = (*sc->argbuf.semantic.sem_full) (current_cpu, abuf); +#endif + if (! virtual_p) + { + /* FIXME: call x-after */ + if (PROFILE_MODEL_P (current_cpu) + && ARGBUF_PROFILE_P (abuf)) + { + int cycles; + + cycles = (*idesc->timing->model_fn) (current_cpu, sc); + sh64_media_model_insn_after (current_cpu, 1 /*last_p*/, cycles); + } + TRACE_INSN_FINI (current_cpu, abuf, 1); + } +#else + abort (); +#endif /* WITH_SEM_SWITCH_FULL */ + } + + return vpc; +} + + +/* 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 +sh64_media_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 */ +{ + const IDESC *idesc; + int icount = 0; + + while (max_insns > 0) + { + USI insn = GETIMEMUSI (current_cpu, pc); + + idesc = extract (current_cpu, pc, insn, &sc->argbuf, FAST_P); + SEM_SKIP_COMPILE (current_cpu, sc, 1); + ++sc; + --max_insns; + ++icount; + pc += idesc->length; + + if (IDESC_CTI_P (idesc)) + { + SET_CTI_VPC (sc - 1); + + if (CGEN_ATTR_VALUE (NULL, idesc->attrs, CGEN_INSN_DELAY_SLOT)) + { + USI insn = GETIMEMUSI (current_cpu, pc); + idesc = extract (current_cpu, pc, insn, &sc->argbuf, FAST_P); + + ++sc; + --max_insns; + ++icount; + pc += idesc->length; + } + break; + } + } + + Finish: + SET_INSN_COUNT (icount); +} +/* 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) [SH64_MEDIA_INSN_X_CTI_CHAIN]; + } + else + { + id = & CPU_IDESC (current_cpu) [SH64_MEDIA_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; + } + + /* 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); + } + + return new_vpc; +} + +/* Chain to the next block from a non-cti terminated previous block. */ + +INLINE SEM_PC +sh64_media_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 | 1); + + /* 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); +} + +/* 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. */ + +INLINE SEM_PC +sh64_media_pbb_cti_chain (SIM_CPU *current_cpu, SEM_ARG sem_arg, + SEM_BRANCH_TYPE br_type, PCADDR new_pc) +{ + SEM_PC *new_vpc_ptr; + + PBB_UPDATE_INSN_COUNT (current_cpu, sem_arg); + + /* If we have switched ISAs, exit back to main loop. + Set idesc to 0 to cause the engine to point to the right insn table. */ + if ((new_pc & 1) == 0) + { + /* Switch to SHcompact. */ + CPU_IDESC_SEM_INIT_P (current_cpu) = 0; + CPU_RUNNING_P (current_cpu) = 0; + } + + /* 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 bit 0 to stay in SHmedia mode. */ + SET_H_PC (new_pc | 1); + 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 +sh64_media_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); + sh64_media_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)) + sh64_media_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 +sh64_media_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); + sh64_media_model_insn_after (current_cpu, 1 /*last_p*/, cycles); + } + TRACE_INSN_FINI (current_cpu, prev_abuf, 1 /*last_p*/); +} + +#define FAST_P 0 + +void +sh64_media_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 + + + 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 "sem-media-switch.c" +#endif +#else + sh64_media_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) [SH64_MEDIA_INSN_X_BEGIN]); + vpc->argbuf.idesc = & CPU_IDESC (current_cpu) [SH64_MEDIA_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 = sh64_media_pbb_begin (current_cpu, FAST_P); + + do + { +/* begin full-exec-pbb */ +{ +#if (! FAST_P && WITH_SEM_SWITCH_FULL) || (FAST_P && WITH_SEM_SWITCH_FAST) +#define DEFINE_SWITCH +#define WITH_ISA_COMPACT +#include "sem-media-switch.c" +#else + vpc = execute (current_cpu, vpc, FAST_P); +#endif +} +/* end full-exec-pbb */ + } + while (CPU_RUNNING_P (current_cpu)); +} + +#undef FAST_P + + +#define FAST_P 1 + +void +sh64_media_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 + + + 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 "sem-media-switch.c" +#endif +#else + sh64_media_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) [SH64_MEDIA_INSN_X_BEGIN]); + vpc->argbuf.idesc = & CPU_IDESC (current_cpu) [SH64_MEDIA_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 = sh64_media_pbb_begin (current_cpu, FAST_P); + + do + { +/* begin fast-exec-pbb */ +{ +#if (! FAST_P && WITH_SEM_SWITCH_FULL) || (FAST_P && WITH_SEM_SWITCH_FAST) +#define DEFINE_SWITCH +#define WITH_ISA_COMPACT +#include "sem-media-switch.c" +#else + vpc = execute (current_cpu, vpc, FAST_P); +#endif +} +/* end fast-exec-pbb */ + } + while (CPU_RUNNING_P (current_cpu)); +} + +#undef FAST_P + |