1 |
24 |
jeremybenn |
/* This file is generated by the genmloop script. DO NOT EDIT! */
|
2 |
|
|
|
3 |
|
|
/* Enable switch() support in cgen headers. */
|
4 |
|
|
#define SEM_IN_SWITCH
|
5 |
|
|
|
6 |
|
|
#define WANT_CPU sh64
|
7 |
|
|
#define WANT_CPU_SH64
|
8 |
|
|
|
9 |
|
|
#include "sim-main.h"
|
10 |
|
|
#include "bfd.h"
|
11 |
|
|
#include "cgen-mem.h"
|
12 |
|
|
#include "cgen-ops.h"
|
13 |
|
|
#include "sim-assert.h"
|
14 |
|
|
|
15 |
|
|
/* Fill in the administrative ARGBUF fields required by all insns,
|
16 |
|
|
virtual and real. */
|
17 |
|
|
|
18 |
|
|
static INLINE void
|
19 |
|
|
sh64_compact_fill_argbuf (const SIM_CPU *cpu, ARGBUF *abuf, const IDESC *idesc,
|
20 |
|
|
PCADDR pc, int fast_p)
|
21 |
|
|
{
|
22 |
|
|
#if WITH_SCACHE
|
23 |
|
|
SEM_SET_CODE (abuf, idesc, fast_p);
|
24 |
|
|
ARGBUF_ADDR (abuf) = pc;
|
25 |
|
|
#endif
|
26 |
|
|
ARGBUF_IDESC (abuf) = idesc;
|
27 |
|
|
}
|
28 |
|
|
|
29 |
|
|
/* Fill in tracing/profiling fields of an ARGBUF. */
|
30 |
|
|
|
31 |
|
|
static INLINE void
|
32 |
|
|
sh64_compact_fill_argbuf_tp (const SIM_CPU *cpu, ARGBUF *abuf,
|
33 |
|
|
int trace_p, int profile_p)
|
34 |
|
|
{
|
35 |
|
|
ARGBUF_TRACE_P (abuf) = trace_p;
|
36 |
|
|
ARGBUF_PROFILE_P (abuf) = profile_p;
|
37 |
|
|
}
|
38 |
|
|
|
39 |
|
|
#if WITH_SCACHE_PBB
|
40 |
|
|
|
41 |
|
|
/* Emit the "x-before" handler.
|
42 |
|
|
x-before is emitted before each insn (serial or parallel).
|
43 |
|
|
This is as opposed to x-after which is only emitted at the end of a group
|
44 |
|
|
of parallel insns. */
|
45 |
|
|
|
46 |
|
|
static INLINE void
|
47 |
|
|
sh64_compact_emit_before (SIM_CPU *current_cpu, SCACHE *sc, PCADDR pc, int first_p)
|
48 |
|
|
{
|
49 |
|
|
ARGBUF *abuf = &sc[0].argbuf;
|
50 |
|
|
const IDESC *id = & CPU_IDESC (current_cpu) [SH64_COMPACT_INSN_X_BEFORE];
|
51 |
|
|
|
52 |
|
|
abuf->fields.before.first_p = first_p;
|
53 |
|
|
sh64_compact_fill_argbuf (current_cpu, abuf, id, pc, 0);
|
54 |
|
|
/* no need to set trace_p,profile_p */
|
55 |
|
|
}
|
56 |
|
|
|
57 |
|
|
/* Emit the "x-after" handler.
|
58 |
|
|
x-after is emitted after a serial insn or at the end of a group of
|
59 |
|
|
parallel insns. */
|
60 |
|
|
|
61 |
|
|
static INLINE void
|
62 |
|
|
sh64_compact_emit_after (SIM_CPU *current_cpu, SCACHE *sc, PCADDR pc)
|
63 |
|
|
{
|
64 |
|
|
ARGBUF *abuf = &sc[0].argbuf;
|
65 |
|
|
const IDESC *id = & CPU_IDESC (current_cpu) [SH64_COMPACT_INSN_X_AFTER];
|
66 |
|
|
|
67 |
|
|
sh64_compact_fill_argbuf (current_cpu, abuf, id, pc, 0);
|
68 |
|
|
/* no need to set trace_p,profile_p */
|
69 |
|
|
}
|
70 |
|
|
|
71 |
|
|
#endif /* WITH_SCACHE_PBB */
|
72 |
|
|
|
73 |
|
|
|
74 |
|
|
static INLINE const IDESC *
|
75 |
|
|
extract (SIM_CPU *current_cpu, PCADDR pc, CGEN_INSN_INT insn, ARGBUF *abuf,
|
76 |
|
|
int fast_p)
|
77 |
|
|
{
|
78 |
|
|
const IDESC *id = sh64_compact_decode (current_cpu, pc, insn, insn, abuf);
|
79 |
|
|
|
80 |
|
|
sh64_compact_fill_argbuf (current_cpu, abuf, id, pc, fast_p);
|
81 |
|
|
if (! fast_p)
|
82 |
|
|
{
|
83 |
|
|
int trace_p = PC_IN_TRACE_RANGE_P (current_cpu, pc);
|
84 |
|
|
int profile_p = PC_IN_PROFILE_RANGE_P (current_cpu, pc);
|
85 |
|
|
sh64_compact_fill_argbuf_tp (current_cpu, abuf, trace_p, profile_p);
|
86 |
|
|
}
|
87 |
|
|
return id;
|
88 |
|
|
}
|
89 |
|
|
|
90 |
|
|
static INLINE SEM_PC
|
91 |
|
|
execute (SIM_CPU *current_cpu, SCACHE *sc, int fast_p)
|
92 |
|
|
{
|
93 |
|
|
SEM_PC vpc;
|
94 |
|
|
|
95 |
|
|
if (fast_p)
|
96 |
|
|
{
|
97 |
|
|
#if ! WITH_SEM_SWITCH_FAST
|
98 |
|
|
#if WITH_SCACHE
|
99 |
|
|
vpc = (*sc->argbuf.semantic.sem_fast) (current_cpu, sc);
|
100 |
|
|
#else
|
101 |
|
|
vpc = (*sc->argbuf.semantic.sem_fast) (current_cpu, &sc->argbuf);
|
102 |
|
|
#endif
|
103 |
|
|
#else
|
104 |
|
|
abort ();
|
105 |
|
|
#endif /* WITH_SEM_SWITCH_FAST */
|
106 |
|
|
}
|
107 |
|
|
else
|
108 |
|
|
{
|
109 |
|
|
#if ! WITH_SEM_SWITCH_FULL
|
110 |
|
|
ARGBUF *abuf = &sc->argbuf;
|
111 |
|
|
const IDESC *idesc = abuf->idesc;
|
112 |
|
|
#if WITH_SCACHE_PBB
|
113 |
|
|
int virtual_p = CGEN_ATTR_VALUE (NULL, idesc->attrs, CGEN_INSN_VIRTUAL);
|
114 |
|
|
#else
|
115 |
|
|
int virtual_p = 0;
|
116 |
|
|
#endif
|
117 |
|
|
|
118 |
|
|
if (! virtual_p)
|
119 |
|
|
{
|
120 |
|
|
/* FIXME: call x-before */
|
121 |
|
|
if (ARGBUF_PROFILE_P (abuf))
|
122 |
|
|
PROFILE_COUNT_INSN (current_cpu, abuf->addr, idesc->num);
|
123 |
|
|
/* FIXME: Later make cover macros: PROFILE_INSN_{INIT,FINI}. */
|
124 |
|
|
if (PROFILE_MODEL_P (current_cpu)
|
125 |
|
|
&& ARGBUF_PROFILE_P (abuf))
|
126 |
|
|
sh64_compact_model_insn_before (current_cpu, 1 /*first_p*/);
|
127 |
|
|
TRACE_INSN_INIT (current_cpu, abuf, 1);
|
128 |
|
|
TRACE_INSN (current_cpu, idesc->idata,
|
129 |
|
|
(const struct argbuf *) abuf, abuf->addr);
|
130 |
|
|
}
|
131 |
|
|
#if WITH_SCACHE
|
132 |
|
|
vpc = (*sc->argbuf.semantic.sem_full) (current_cpu, sc);
|
133 |
|
|
#else
|
134 |
|
|
vpc = (*sc->argbuf.semantic.sem_full) (current_cpu, abuf);
|
135 |
|
|
#endif
|
136 |
|
|
if (! virtual_p)
|
137 |
|
|
{
|
138 |
|
|
/* FIXME: call x-after */
|
139 |
|
|
if (PROFILE_MODEL_P (current_cpu)
|
140 |
|
|
&& ARGBUF_PROFILE_P (abuf))
|
141 |
|
|
{
|
142 |
|
|
int cycles;
|
143 |
|
|
|
144 |
|
|
cycles = (*idesc->timing->model_fn) (current_cpu, sc);
|
145 |
|
|
sh64_compact_model_insn_after (current_cpu, 1 /*last_p*/, cycles);
|
146 |
|
|
}
|
147 |
|
|
TRACE_INSN_FINI (current_cpu, abuf, 1);
|
148 |
|
|
}
|
149 |
|
|
#else
|
150 |
|
|
abort ();
|
151 |
|
|
#endif /* WITH_SEM_SWITCH_FULL */
|
152 |
|
|
}
|
153 |
|
|
|
154 |
|
|
return vpc;
|
155 |
|
|
}
|
156 |
|
|
|
157 |
|
|
|
158 |
|
|
/* Record address of cti terminating a pbb. */
|
159 |
|
|
#define SET_CTI_VPC(sc) do { _cti_sc = (sc); } while (0)
|
160 |
|
|
/* Record number of [real] insns in pbb. */
|
161 |
|
|
#define SET_INSN_COUNT(n) do { _insn_count = (n); } while (0)
|
162 |
|
|
|
163 |
|
|
/* Fetch and extract a pseudo-basic-block.
|
164 |
|
|
FAST_P is non-zero if no tracing/profiling/etc. is wanted. */
|
165 |
|
|
|
166 |
|
|
INLINE SEM_PC
|
167 |
|
|
sh64_compact_pbb_begin (SIM_CPU *current_cpu, int FAST_P)
|
168 |
|
|
{
|
169 |
|
|
SEM_PC new_vpc;
|
170 |
|
|
PCADDR pc;
|
171 |
|
|
SCACHE *sc;
|
172 |
|
|
int max_insns = CPU_SCACHE_MAX_CHAIN_LENGTH (current_cpu);
|
173 |
|
|
|
174 |
|
|
pc = GET_H_PC ();
|
175 |
|
|
|
176 |
|
|
new_vpc = scache_lookup_or_alloc (current_cpu, pc, max_insns, &sc);
|
177 |
|
|
if (! new_vpc)
|
178 |
|
|
{
|
179 |
|
|
/* Leading '_' to avoid collision with mainloop.in. */
|
180 |
|
|
int _insn_count = 0;
|
181 |
|
|
SCACHE *orig_sc = sc;
|
182 |
|
|
SCACHE *_cti_sc = NULL;
|
183 |
|
|
int slice_insns = CPU_MAX_SLICE_INSNS (current_cpu);
|
184 |
|
|
|
185 |
|
|
/* First figure out how many instructions to compile.
|
186 |
|
|
MAX_INSNS is the size of the allocated buffer, which includes space
|
187 |
|
|
for before/after handlers if they're being used.
|
188 |
|
|
SLICE_INSNS is the maxinum number of real insns that can be
|
189 |
|
|
executed. Zero means "as many as we want". */
|
190 |
|
|
/* ??? max_insns is serving two incompatible roles.
|
191 |
|
|
1) Number of slots available in scache buffer.
|
192 |
|
|
2) Number of real insns to execute.
|
193 |
|
|
They're incompatible because there are virtual insns emitted too
|
194 |
|
|
(chain,cti-chain,before,after handlers). */
|
195 |
|
|
|
196 |
|
|
if (slice_insns == 1)
|
197 |
|
|
{
|
198 |
|
|
/* No need to worry about extra slots required for virtual insns
|
199 |
|
|
and parallel exec support because MAX_CHAIN_LENGTH is
|
200 |
|
|
guaranteed to be big enough to execute at least 1 insn! */
|
201 |
|
|
max_insns = 1;
|
202 |
|
|
}
|
203 |
|
|
else
|
204 |
|
|
{
|
205 |
|
|
/* Allow enough slop so that while compiling insns, if max_insns > 0
|
206 |
|
|
then there's guaranteed to be enough space to emit one real insn.
|
207 |
|
|
MAX_CHAIN_LENGTH is typically much longer than
|
208 |
|
|
the normal number of insns between cti's anyway. */
|
209 |
|
|
max_insns -= (1 /* one for the trailing chain insn */
|
210 |
|
|
+ (FAST_P
|
211 |
|
|
? 0
|
212 |
|
|
: (1 + MAX_PARALLEL_INSNS) /* before+after */)
|
213 |
|
|
+ (MAX_PARALLEL_INSNS > 1
|
214 |
|
|
? (MAX_PARALLEL_INSNS * 2)
|
215 |
|
|
: 0));
|
216 |
|
|
|
217 |
|
|
/* Account for before/after handlers. */
|
218 |
|
|
if (! FAST_P)
|
219 |
|
|
slice_insns *= 3;
|
220 |
|
|
|
221 |
|
|
if (slice_insns > 0
|
222 |
|
|
&& slice_insns < max_insns)
|
223 |
|
|
max_insns = slice_insns;
|
224 |
|
|
}
|
225 |
|
|
|
226 |
|
|
new_vpc = sc;
|
227 |
|
|
|
228 |
|
|
/* SC,PC must be updated to point passed the last entry used.
|
229 |
|
|
SET_CTI_VPC must be called if pbb is terminated by a cti.
|
230 |
|
|
SET_INSN_COUNT must be called to record number of real insns in
|
231 |
|
|
pbb [could be computed by us of course, extra cpu but perhaps
|
232 |
|
|
negligible enough]. */
|
233 |
|
|
|
234 |
|
|
/* begin extract-pbb */
|
235 |
|
|
{
|
236 |
|
|
const IDESC *idesc;
|
237 |
|
|
int icount = 0;
|
238 |
|
|
|
239 |
|
|
while (max_insns > 0)
|
240 |
|
|
{
|
241 |
|
|
UHI insn = GETIMEMUHI (current_cpu, pc);
|
242 |
|
|
|
243 |
|
|
idesc = extract (current_cpu, pc, insn, &sc->argbuf, FAST_P);
|
244 |
|
|
SEM_SKIP_COMPILE (current_cpu, sc, 1);
|
245 |
|
|
++sc;
|
246 |
|
|
--max_insns;
|
247 |
|
|
++icount;
|
248 |
|
|
pc += idesc->length;
|
249 |
|
|
|
250 |
|
|
if (IDESC_CTI_P (idesc))
|
251 |
|
|
{
|
252 |
|
|
SET_CTI_VPC (sc - 1);
|
253 |
|
|
|
254 |
|
|
if (CGEN_ATTR_VALUE (NULL, idesc->attrs, CGEN_INSN_DELAY_SLOT))
|
255 |
|
|
{
|
256 |
|
|
USI insn = GETIMEMUHI (current_cpu, pc);
|
257 |
|
|
idesc = extract (current_cpu, pc, insn, &sc->argbuf, FAST_P);
|
258 |
|
|
|
259 |
|
|
if (IDESC_CTI_P (idesc) ||
|
260 |
|
|
CGEN_ATTR_VALUE (NULL, idesc->attrs, CGEN_INSN_ILLSLOT))
|
261 |
|
|
{
|
262 |
|
|
SIM_DESC sd = CPU_STATE (current_cpu);
|
263 |
|
|
sim_io_eprintf (CPU_STATE (current_cpu),
|
264 |
|
|
"malformed program, `%s' insn in delay slot\n",
|
265 |
|
|
CGEN_INSN_NAME (idesc->idata));
|
266 |
|
|
sim_engine_halt (sd, current_cpu, NULL, pc,
|
267 |
|
|
sim_stopped, SIM_SIGILL);
|
268 |
|
|
}
|
269 |
|
|
else
|
270 |
|
|
{
|
271 |
|
|
++sc;
|
272 |
|
|
--max_insns;
|
273 |
|
|
++icount;
|
274 |
|
|
pc += idesc->length;
|
275 |
|
|
}
|
276 |
|
|
}
|
277 |
|
|
break;
|
278 |
|
|
}
|
279 |
|
|
}
|
280 |
|
|
|
281 |
|
|
Finish:
|
282 |
|
|
SET_INSN_COUNT (icount);
|
283 |
|
|
}
|
284 |
|
|
/* end extract-pbb */
|
285 |
|
|
|
286 |
|
|
/* The last one is a pseudo-insn to link to the next chain.
|
287 |
|
|
It is also used to record the insn count for this chain. */
|
288 |
|
|
{
|
289 |
|
|
const IDESC *id;
|
290 |
|
|
|
291 |
|
|
/* Was pbb terminated by a cti? */
|
292 |
|
|
if (_cti_sc)
|
293 |
|
|
{
|
294 |
|
|
id = & CPU_IDESC (current_cpu) [SH64_COMPACT_INSN_X_CTI_CHAIN];
|
295 |
|
|
}
|
296 |
|
|
else
|
297 |
|
|
{
|
298 |
|
|
id = & CPU_IDESC (current_cpu) [SH64_COMPACT_INSN_X_CHAIN];
|
299 |
|
|
}
|
300 |
|
|
SEM_SET_CODE (&sc->argbuf, id, FAST_P);
|
301 |
|
|
sc->argbuf.idesc = id;
|
302 |
|
|
sc->argbuf.addr = pc;
|
303 |
|
|
sc->argbuf.fields.chain.insn_count = _insn_count;
|
304 |
|
|
sc->argbuf.fields.chain.next = 0;
|
305 |
|
|
sc->argbuf.fields.chain.branch_target = 0;
|
306 |
|
|
++sc;
|
307 |
|
|
}
|
308 |
|
|
|
309 |
|
|
/* Update the pointer to the next free entry, may not have used as
|
310 |
|
|
many entries as was asked for. */
|
311 |
|
|
CPU_SCACHE_NEXT_FREE (current_cpu) = sc;
|
312 |
|
|
/* Record length of chain if profiling.
|
313 |
|
|
This includes virtual insns since they count against
|
314 |
|
|
max_insns too. */
|
315 |
|
|
if (! FAST_P)
|
316 |
|
|
PROFILE_COUNT_SCACHE_CHAIN_LENGTH (current_cpu, sc - orig_sc);
|
317 |
|
|
}
|
318 |
|
|
|
319 |
|
|
return new_vpc;
|
320 |
|
|
}
|
321 |
|
|
|
322 |
|
|
/* Chain to the next block from a non-cti terminated previous block. */
|
323 |
|
|
|
324 |
|
|
INLINE SEM_PC
|
325 |
|
|
sh64_compact_pbb_chain (SIM_CPU *current_cpu, SEM_ARG sem_arg)
|
326 |
|
|
{
|
327 |
|
|
ARGBUF *abuf = SEM_ARGBUF (sem_arg);
|
328 |
|
|
|
329 |
|
|
PBB_UPDATE_INSN_COUNT (current_cpu, sem_arg);
|
330 |
|
|
|
331 |
|
|
SET_H_PC (abuf->addr);
|
332 |
|
|
|
333 |
|
|
|
334 |
|
|
/* If not running forever, exit back to main loop. */
|
335 |
|
|
if (CPU_MAX_SLICE_INSNS (current_cpu) != 0
|
336 |
|
|
/* Also exit back to main loop if there's an event.
|
337 |
|
|
Note that if CPU_MAX_SLICE_INSNS != 1, events won't get processed
|
338 |
|
|
at the "right" time, but then that was what was asked for.
|
339 |
|
|
There is no silver bullet for simulator engines.
|
340 |
|
|
??? Clearly this needs a cleaner interface.
|
341 |
|
|
At present it's just so Ctrl-C works. */
|
342 |
|
|
|| STATE_EVENTS (CPU_STATE (current_cpu))->work_pending)
|
343 |
|
|
CPU_RUNNING_P (current_cpu) = 0;
|
344 |
|
|
|
345 |
|
|
/* If chained to next block, go straight to it. */
|
346 |
|
|
if (abuf->fields.chain.next)
|
347 |
|
|
return abuf->fields.chain.next;
|
348 |
|
|
/* See if next block has already been compiled. */
|
349 |
|
|
abuf->fields.chain.next = scache_lookup (current_cpu, abuf->addr);
|
350 |
|
|
if (abuf->fields.chain.next)
|
351 |
|
|
return abuf->fields.chain.next;
|
352 |
|
|
/* Nope, so next insn is a virtual insn to invoke the compiler
|
353 |
|
|
(begin a pbb). */
|
354 |
|
|
return CPU_SCACHE_PBB_BEGIN (current_cpu);
|
355 |
|
|
}
|
356 |
|
|
|
357 |
|
|
/* Chain to the next block from a cti terminated previous block.
|
358 |
|
|
BR_TYPE indicates whether the branch was taken and whether we can cache
|
359 |
|
|
the vpc of the branch target.
|
360 |
|
|
NEW_PC is the target's branch address, and is only valid if
|
361 |
|
|
BR_TYPE != SEM_BRANCH_UNTAKEN. */
|
362 |
|
|
|
363 |
|
|
INLINE SEM_PC
|
364 |
|
|
sh64_compact_pbb_cti_chain (SIM_CPU *current_cpu, SEM_ARG sem_arg,
|
365 |
|
|
SEM_BRANCH_TYPE br_type, PCADDR new_pc)
|
366 |
|
|
{
|
367 |
|
|
SEM_PC *new_vpc_ptr;
|
368 |
|
|
|
369 |
|
|
PBB_UPDATE_INSN_COUNT (current_cpu, sem_arg);
|
370 |
|
|
|
371 |
|
|
/* If we have switched ISAs, exit back to main loop.
|
372 |
|
|
Set idesc to 0 to cause the engine to point to the right insn table. */
|
373 |
|
|
if (new_pc & 1)
|
374 |
|
|
{
|
375 |
|
|
/* Switch to SHmedia. */
|
376 |
|
|
CPU_IDESC_SEM_INIT_P (current_cpu) = 0;
|
377 |
|
|
CPU_RUNNING_P (current_cpu) = 0;
|
378 |
|
|
}
|
379 |
|
|
|
380 |
|
|
/* If not running forever, exit back to main loop. */
|
381 |
|
|
if (CPU_MAX_SLICE_INSNS (current_cpu) != 0
|
382 |
|
|
/* Also exit back to main loop if there's an event.
|
383 |
|
|
Note that if CPU_MAX_SLICE_INSNS != 1, events won't get processed
|
384 |
|
|
at the "right" time, but then that was what was asked for.
|
385 |
|
|
There is no silver bullet for simulator engines.
|
386 |
|
|
??? Clearly this needs a cleaner interface.
|
387 |
|
|
At present it's just so Ctrl-C works. */
|
388 |
|
|
|| STATE_EVENTS (CPU_STATE (current_cpu))->work_pending)
|
389 |
|
|
CPU_RUNNING_P (current_cpu) = 0;
|
390 |
|
|
|
391 |
|
|
/* Restart compiler if we branched to an uncacheable address
|
392 |
|
|
(e.g. "j reg"). */
|
393 |
|
|
if (br_type == SEM_BRANCH_UNCACHEABLE)
|
394 |
|
|
{
|
395 |
|
|
SET_H_PC (new_pc);
|
396 |
|
|
return CPU_SCACHE_PBB_BEGIN (current_cpu);
|
397 |
|
|
}
|
398 |
|
|
|
399 |
|
|
/* If branch wasn't taken, update the pc and set BR_ADDR_PTR to our
|
400 |
|
|
next chain ptr. */
|
401 |
|
|
if (br_type == SEM_BRANCH_UNTAKEN)
|
402 |
|
|
{
|
403 |
|
|
ARGBUF *abuf = SEM_ARGBUF (sem_arg);
|
404 |
|
|
new_pc = abuf->addr;
|
405 |
|
|
SET_H_PC (new_pc);
|
406 |
|
|
new_vpc_ptr = &abuf->fields.chain.next;
|
407 |
|
|
}
|
408 |
|
|
else
|
409 |
|
|
{
|
410 |
|
|
ARGBUF *abuf = SEM_ARGBUF (sem_arg);
|
411 |
|
|
SET_H_PC (new_pc);
|
412 |
|
|
new_vpc_ptr = &abuf->fields.chain.branch_target;
|
413 |
|
|
}
|
414 |
|
|
|
415 |
|
|
/* If chained to next block, go straight to it. */
|
416 |
|
|
if (*new_vpc_ptr)
|
417 |
|
|
return *new_vpc_ptr;
|
418 |
|
|
/* See if next block has already been compiled. */
|
419 |
|
|
*new_vpc_ptr = scache_lookup (current_cpu, new_pc);
|
420 |
|
|
if (*new_vpc_ptr)
|
421 |
|
|
return *new_vpc_ptr;
|
422 |
|
|
/* Nope, so next insn is a virtual insn to invoke the compiler
|
423 |
|
|
(begin a pbb). */
|
424 |
|
|
return CPU_SCACHE_PBB_BEGIN (current_cpu);
|
425 |
|
|
}
|
426 |
|
|
|
427 |
|
|
/* x-before handler.
|
428 |
|
|
This is called before each insn. */
|
429 |
|
|
|
430 |
|
|
void
|
431 |
|
|
sh64_compact_pbb_before (SIM_CPU *current_cpu, SCACHE *sc)
|
432 |
|
|
{
|
433 |
|
|
SEM_ARG sem_arg = sc;
|
434 |
|
|
const ARGBUF *abuf = SEM_ARGBUF (sem_arg);
|
435 |
|
|
int first_p = abuf->fields.before.first_p;
|
436 |
|
|
const ARGBUF *cur_abuf = SEM_ARGBUF (sc + 1);
|
437 |
|
|
const IDESC *cur_idesc = cur_abuf->idesc;
|
438 |
|
|
PCADDR pc = cur_abuf->addr;
|
439 |
|
|
|
440 |
|
|
if (ARGBUF_PROFILE_P (cur_abuf))
|
441 |
|
|
PROFILE_COUNT_INSN (current_cpu, pc, cur_idesc->num);
|
442 |
|
|
|
443 |
|
|
/* If this isn't the first insn, finish up the previous one. */
|
444 |
|
|
|
445 |
|
|
if (! first_p)
|
446 |
|
|
{
|
447 |
|
|
if (PROFILE_MODEL_P (current_cpu))
|
448 |
|
|
{
|
449 |
|
|
const SEM_ARG prev_sem_arg = sc - 1;
|
450 |
|
|
const ARGBUF *prev_abuf = SEM_ARGBUF (prev_sem_arg);
|
451 |
|
|
const IDESC *prev_idesc = prev_abuf->idesc;
|
452 |
|
|
int cycles;
|
453 |
|
|
|
454 |
|
|
/* ??? May want to measure all insns if doing insn tracing. */
|
455 |
|
|
if (ARGBUF_PROFILE_P (prev_abuf))
|
456 |
|
|
{
|
457 |
|
|
cycles = (*prev_idesc->timing->model_fn) (current_cpu, prev_sem_arg);
|
458 |
|
|
sh64_compact_model_insn_after (current_cpu, 0 /*last_p*/, cycles);
|
459 |
|
|
}
|
460 |
|
|
}
|
461 |
|
|
|
462 |
|
|
TRACE_INSN_FINI (current_cpu, cur_abuf, 0 /*last_p*/);
|
463 |
|
|
}
|
464 |
|
|
|
465 |
|
|
/* FIXME: Later make cover macros: PROFILE_INSN_{INIT,FINI}. */
|
466 |
|
|
if (PROFILE_MODEL_P (current_cpu)
|
467 |
|
|
&& ARGBUF_PROFILE_P (cur_abuf))
|
468 |
|
|
sh64_compact_model_insn_before (current_cpu, first_p);
|
469 |
|
|
|
470 |
|
|
TRACE_INSN_INIT (current_cpu, cur_abuf, first_p);
|
471 |
|
|
TRACE_INSN (current_cpu, cur_idesc->idata, cur_abuf, pc);
|
472 |
|
|
}
|
473 |
|
|
|
474 |
|
|
/* x-after handler.
|
475 |
|
|
This is called after a serial insn or at the end of a group of parallel
|
476 |
|
|
insns. */
|
477 |
|
|
|
478 |
|
|
void
|
479 |
|
|
sh64_compact_pbb_after (SIM_CPU *current_cpu, SCACHE *sc)
|
480 |
|
|
{
|
481 |
|
|
SEM_ARG sem_arg = sc;
|
482 |
|
|
const ARGBUF *abuf = SEM_ARGBUF (sem_arg);
|
483 |
|
|
const SEM_ARG prev_sem_arg = sc - 1;
|
484 |
|
|
const ARGBUF *prev_abuf = SEM_ARGBUF (prev_sem_arg);
|
485 |
|
|
|
486 |
|
|
/* ??? May want to measure all insns if doing insn tracing. */
|
487 |
|
|
if (PROFILE_MODEL_P (current_cpu)
|
488 |
|
|
&& ARGBUF_PROFILE_P (prev_abuf))
|
489 |
|
|
{
|
490 |
|
|
const IDESC *prev_idesc = prev_abuf->idesc;
|
491 |
|
|
int cycles;
|
492 |
|
|
|
493 |
|
|
cycles = (*prev_idesc->timing->model_fn) (current_cpu, prev_sem_arg);
|
494 |
|
|
sh64_compact_model_insn_after (current_cpu, 1 /*last_p*/, cycles);
|
495 |
|
|
}
|
496 |
|
|
TRACE_INSN_FINI (current_cpu, prev_abuf, 1 /*last_p*/);
|
497 |
|
|
}
|
498 |
|
|
|
499 |
|
|
#define FAST_P 0
|
500 |
|
|
|
501 |
|
|
void
|
502 |
|
|
sh64_compact_engine_run_full (SIM_CPU *current_cpu)
|
503 |
|
|
{
|
504 |
|
|
SIM_DESC current_state = CPU_STATE (current_cpu);
|
505 |
|
|
SCACHE *scache = CPU_SCACHE_CACHE (current_cpu);
|
506 |
|
|
/* virtual program counter */
|
507 |
|
|
SEM_PC vpc;
|
508 |
|
|
#if WITH_SEM_SWITCH_FULL
|
509 |
|
|
/* For communication between cti's and cti-chain. */
|
510 |
|
|
SEM_BRANCH_TYPE pbb_br_type;
|
511 |
|
|
PCADDR pbb_br_npc;
|
512 |
|
|
#endif
|
513 |
|
|
|
514 |
|
|
|
515 |
|
|
if (! CPU_IDESC_SEM_INIT_P (current_cpu))
|
516 |
|
|
{
|
517 |
|
|
/* ??? 'twould be nice to move this up a level and only call it once.
|
518 |
|
|
On the other hand, in the "let's go fast" case the test is only done
|
519 |
|
|
once per pbb (since we only return to the main loop at the end of
|
520 |
|
|
a pbb). And in the "let's run until we're done" case we don't return
|
521 |
|
|
until the program exits. */
|
522 |
|
|
|
523 |
|
|
#if WITH_SEM_SWITCH_FULL
|
524 |
|
|
#if defined (__GNUC__)
|
525 |
|
|
/* ??? Later maybe paste sem-switch.c in when building mainloop.c. */
|
526 |
|
|
#define DEFINE_LABELS
|
527 |
|
|
#include "sem-compact-switch.c"
|
528 |
|
|
#endif
|
529 |
|
|
#else
|
530 |
|
|
sh64_compact_sem_init_idesc_table (current_cpu);
|
531 |
|
|
#endif
|
532 |
|
|
|
533 |
|
|
/* Initialize the "begin (compile) a pbb" virtual insn. */
|
534 |
|
|
vpc = CPU_SCACHE_PBB_BEGIN (current_cpu);
|
535 |
|
|
SEM_SET_FULL_CODE (SEM_ARGBUF (vpc),
|
536 |
|
|
& CPU_IDESC (current_cpu) [SH64_COMPACT_INSN_X_BEGIN]);
|
537 |
|
|
vpc->argbuf.idesc = & CPU_IDESC (current_cpu) [SH64_COMPACT_INSN_X_BEGIN];
|
538 |
|
|
|
539 |
|
|
CPU_IDESC_SEM_INIT_P (current_cpu) = 1;
|
540 |
|
|
}
|
541 |
|
|
|
542 |
|
|
CPU_RUNNING_P (current_cpu) = 1;
|
543 |
|
|
/* ??? In the case where we're returning to the main loop after every
|
544 |
|
|
pbb we don't want to call pbb_begin each time (which hashes on the pc
|
545 |
|
|
and does a table lookup). A way to speed this up is to save vpc
|
546 |
|
|
between calls. */
|
547 |
|
|
vpc = sh64_compact_pbb_begin (current_cpu, FAST_P);
|
548 |
|
|
|
549 |
|
|
do
|
550 |
|
|
{
|
551 |
|
|
/* begin full-exec-pbb */
|
552 |
|
|
{
|
553 |
|
|
#if (! FAST_P && WITH_SEM_SWITCH_FULL) || (FAST_P && WITH_SEM_SWITCH_FAST)
|
554 |
|
|
#define DEFINE_SWITCH
|
555 |
|
|
#include "sem-compact-switch.c"
|
556 |
|
|
#else
|
557 |
|
|
vpc = execute (current_cpu, vpc, FAST_P);
|
558 |
|
|
#endif
|
559 |
|
|
}
|
560 |
|
|
/* end full-exec-pbb */
|
561 |
|
|
}
|
562 |
|
|
while (CPU_RUNNING_P (current_cpu));
|
563 |
|
|
}
|
564 |
|
|
|
565 |
|
|
#undef FAST_P
|
566 |
|
|
|
567 |
|
|
|
568 |
|
|
#define FAST_P 1
|
569 |
|
|
|
570 |
|
|
void
|
571 |
|
|
sh64_compact_engine_run_fast (SIM_CPU *current_cpu)
|
572 |
|
|
{
|
573 |
|
|
SIM_DESC current_state = CPU_STATE (current_cpu);
|
574 |
|
|
SCACHE *scache = CPU_SCACHE_CACHE (current_cpu);
|
575 |
|
|
/* virtual program counter */
|
576 |
|
|
SEM_PC vpc;
|
577 |
|
|
#if WITH_SEM_SWITCH_FAST
|
578 |
|
|
/* For communication between cti's and cti-chain. */
|
579 |
|
|
SEM_BRANCH_TYPE pbb_br_type;
|
580 |
|
|
PCADDR pbb_br_npc;
|
581 |
|
|
#endif
|
582 |
|
|
|
583 |
|
|
|
584 |
|
|
if (! CPU_IDESC_SEM_INIT_P (current_cpu))
|
585 |
|
|
{
|
586 |
|
|
/* ??? 'twould be nice to move this up a level and only call it once.
|
587 |
|
|
On the other hand, in the "let's go fast" case the test is only done
|
588 |
|
|
once per pbb (since we only return to the main loop at the end of
|
589 |
|
|
a pbb). And in the "let's run until we're done" case we don't return
|
590 |
|
|
until the program exits. */
|
591 |
|
|
|
592 |
|
|
#if WITH_SEM_SWITCH_FAST
|
593 |
|
|
#if defined (__GNUC__)
|
594 |
|
|
/* ??? Later maybe paste sem-switch.c in when building mainloop.c. */
|
595 |
|
|
#define DEFINE_LABELS
|
596 |
|
|
#include "sem-compact-switch.c"
|
597 |
|
|
#endif
|
598 |
|
|
#else
|
599 |
|
|
sh64_compact_semf_init_idesc_table (current_cpu);
|
600 |
|
|
#endif
|
601 |
|
|
|
602 |
|
|
/* Initialize the "begin (compile) a pbb" virtual insn. */
|
603 |
|
|
vpc = CPU_SCACHE_PBB_BEGIN (current_cpu);
|
604 |
|
|
SEM_SET_FAST_CODE (SEM_ARGBUF (vpc),
|
605 |
|
|
& CPU_IDESC (current_cpu) [SH64_COMPACT_INSN_X_BEGIN]);
|
606 |
|
|
vpc->argbuf.idesc = & CPU_IDESC (current_cpu) [SH64_COMPACT_INSN_X_BEGIN];
|
607 |
|
|
|
608 |
|
|
CPU_IDESC_SEM_INIT_P (current_cpu) = 1;
|
609 |
|
|
}
|
610 |
|
|
|
611 |
|
|
CPU_RUNNING_P (current_cpu) = 1;
|
612 |
|
|
/* ??? In the case where we're returning to the main loop after every
|
613 |
|
|
pbb we don't want to call pbb_begin each time (which hashes on the pc
|
614 |
|
|
and does a table lookup). A way to speed this up is to save vpc
|
615 |
|
|
between calls. */
|
616 |
|
|
vpc = sh64_compact_pbb_begin (current_cpu, FAST_P);
|
617 |
|
|
|
618 |
|
|
do
|
619 |
|
|
{
|
620 |
|
|
/* begin fast-exec-pbb */
|
621 |
|
|
{
|
622 |
|
|
#if (! FAST_P && WITH_SEM_SWITCH_FULL) || (FAST_P && WITH_SEM_SWITCH_FAST)
|
623 |
|
|
#define DEFINE_SWITCH
|
624 |
|
|
#include "sem-compact-switch.c"
|
625 |
|
|
#else
|
626 |
|
|
vpc = execute (current_cpu, vpc, FAST_P);
|
627 |
|
|
#endif
|
628 |
|
|
}
|
629 |
|
|
/* end fast-exec-pbb */
|
630 |
|
|
}
|
631 |
|
|
while (CPU_RUNNING_P (current_cpu));
|
632 |
|
|
}
|
633 |
|
|
|
634 |
|
|
#undef FAST_P
|
635 |
|
|
|