1# Simulator main loop for m32rx. -*- C -*-
2#
3# Copyright 1996-2024 Free Software Foundation, Inc.
4#
5# This file is part of the GNU Simulators.
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 3 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20# Syntax:
21# /bin/sh mainloop.in command
22#
23# Command is one of:
24#
25# init
26# support
27# extract-{simple,scache,pbb}
28# {full,fast}-exec-{simple,scache,pbb}
29#
30# A target need only provide a "full" version of one of simple,scache,pbb.
31# If the target wants it can also provide a fast version of same, or if
32# the slow (full featured) version is `simple', then the fast version can be
33# one of scache/pbb.
34# A target can't provide more than this.
35
36# ??? After a few more ports are done, revisit.
37# Will eventually need to machine generate a lot of this.
38
39case "x$1" in
40
41xsupport)
42
43cat <<EOF
44#line $LINENO "$0"
45
46/* Emit insns to write back the results of insns executed in parallel.
47   SC points to a sufficient number of scache entries for the writeback
48   handlers.
49   SC1/ID1 is the first insn (left slot, lower address).
50   SC2/ID2 is the second insn (right slot, higher address).  */
51
52static INLINE void
53emit_par_finish (SIM_CPU *current_cpu, PCADDR pc, SCACHE *sc,
54                     SCACHE *sc1, const IDESC *id1, SCACHE *sc2, const IDESC *id2)
55{
56  ARGBUF *abuf;
57
58  abuf = &sc->argbuf;
59  id1 = id1->par_idesc;
60  abuf->fields.write.abuf = &sc1->argbuf;
61  @cpu@_fill_argbuf (current_cpu, abuf, id1, pc, 0);
62  /* no need to set trace_p,profile_p */
63#if 0 /* not currently needed for id2 since results written directly */
64  abuf = &sc[1].argbuf;
65  id2 = id2->par_idesc;
66  abuf->fields.write.abuf = &sc2->argbuf;
67  @cpu@_fill_argbuf (current_cpu, abuf, id2, pc + 2, 0);
68  /* no need to set trace_p,profile_p */
69#endif
70}
71
72static INLINE const IDESC *
73emit_16 (SIM_CPU *current_cpu, PCADDR pc, CGEN_INSN_INT insn,
74           SCACHE *sc, int fast_p, int parallel_p)
75{
76  ARGBUF *abuf = &sc->argbuf;
77  const IDESC *id = @cpu@_decode (current_cpu, pc, insn, insn, abuf);
78
79  if (parallel_p)
80    id = id->par_idesc;
81  @cpu@_fill_argbuf (current_cpu, abuf, id, pc, fast_p);
82  return id;
83}
84
85static INLINE const IDESC *
86emit_full16 (SIM_CPU *current_cpu, PCADDR pc, CGEN_INSN_INT insn, SCACHE *sc,
87               int trace_p, int profile_p)
88{
89  const IDESC *id;
90
91  @cpu@_emit_before (current_cpu, sc, pc, 1);
92  id = emit_16 (current_cpu, pc, insn, sc + 1, 0, 0);
93  @cpu@_emit_after (current_cpu, sc + 2, pc);
94  @cpu@_fill_argbuf_tp (current_cpu, &sc[1].argbuf, trace_p, profile_p);
95  return id;
96}
97
98static INLINE const IDESC *
99emit_parallel (SIM_CPU *current_cpu, PCADDR pc, CGEN_INSN_INT insn,
100                 SCACHE *sc, int fast_p)
101{
102  const IDESC *id,*id2;
103
104  /* Emit both insns, then emit a finisher-upper.
105     We speed things up by handling the second insn serially
106     [not parallelly].  Then the writeback only has to deal
107     with the first insn.  */
108  /* ??? Revisit to handle exceptions right.  */
109
110  /* FIXME: No need to handle this parallely if second is nop.  */
111  id = emit_16 (current_cpu, pc, insn >> 16, sc, fast_p, 1);
112
113  /* Note that this can never be a cti.  No cti's go in the S pipeline.  */
114  id2 = emit_16 (current_cpu, pc + 2, insn & 0x7fff, sc + 1, fast_p, 0);
115
116  /* Set sc/snc insns notion of where to skip to.  */
117  if (IDESC_SKIP_P (id))
118    SEM_SKIP_COMPILE (current_cpu, sc, 1);
119
120  /* Emit code to finish executing the semantics
121     (write back the results).  */
122  emit_par_finish (current_cpu, pc, sc + 2, sc, id, sc + 1, id2);
123
124  return id;
125}
126
127static INLINE const IDESC *
128emit_full_parallel (SIM_CPU *current_cpu, PCADDR pc, CGEN_INSN_INT insn,
129                        SCACHE *sc, int trace_p, int profile_p)
130{
131  const IDESC *id,*id2;
132
133  /* Emit both insns, then emit a finisher-upper.
134     We speed things up by handling the second insn serially
135     [not parallelly].  Then the writeback only has to deal
136     with the first insn.  */
137  /* ??? Revisit to handle exceptions right.  */
138
139  @cpu@_emit_before (current_cpu, sc, pc, 1);
140
141  /* FIXME: No need to handle this parallelly if second is nop.  */
142  id = emit_16 (current_cpu, pc, insn >> 16, sc + 1, 0, 1);
143  @cpu@_fill_argbuf_tp (current_cpu, &sc[1].argbuf, trace_p, profile_p);
144
145  @cpu@_emit_before (current_cpu, sc + 2, pc, 0);
146
147  /* Note that this can never be a cti.  No cti's go in the S pipeline.  */
148  id2 = emit_16 (current_cpu, pc + 2, insn & 0x7fff, sc + 3, 0, 0);
149  @cpu@_fill_argbuf_tp (current_cpu, &sc[3].argbuf, trace_p, profile_p);
150
151  /* Set sc/snc insns notion of where to skip to.  */
152  if (IDESC_SKIP_P (id))
153    SEM_SKIP_COMPILE (current_cpu, sc, 4);
154
155  /* Emit code to finish executing the semantics
156     (write back the results).  */
157  emit_par_finish (current_cpu, pc, sc + 4, sc + 1, id, sc + 3, id2);
158
159  @cpu@_emit_after (current_cpu, sc + 5, pc);
160
161  return id;
162}
163
164static INLINE const IDESC *
165emit_32 (SIM_CPU *current_cpu, PCADDR pc, CGEN_INSN_INT insn,
166           SCACHE *sc, int fast_p)
167{
168  ARGBUF *abuf = &sc->argbuf;
169  const IDESC *id = @cpu@_decode (current_cpu, pc,
170                                          (USI) insn >> 16, insn, abuf);
171
172  @cpu@_fill_argbuf (current_cpu, abuf, id, pc, fast_p);
173  return id;
174}
175
176static INLINE const IDESC *
177emit_full32 (SIM_CPU *current_cpu, PCADDR pc, CGEN_INSN_INT insn, SCACHE *sc,
178               int trace_p, int profile_p)
179{
180  const IDESC *id;
181
182  @cpu@_emit_before (current_cpu, sc, pc, 1);
183  id = emit_32 (current_cpu, pc, insn, sc + 1, 0);
184  @cpu@_emit_after (current_cpu, sc + 2, pc);
185  @cpu@_fill_argbuf_tp (current_cpu, &sc[1].argbuf, trace_p, profile_p);
186  return id;
187}
188
189EOF
190
191;;
192
193xinit)
194
195# Nothing needed.
196
197;;
198
199xextract-pbb)
200
201# Inputs:  current_cpu, pc, sc, max_insns, FAST_P
202# Outputs: sc, pc
203# sc must be left pointing past the last created entry.
204# pc must be left pointing past the last created entry.
205# If the pbb is terminated by a cti insn, SET_CTI_VPC(sc) must be called
206# to record the vpc of the cti insn.
207# SET_INSN_COUNT(n) must be called to record number of real insns.
208
209cat <<EOF
210#line $LINENO "$0"
211{
212  const IDESC *idesc;
213  int icount = 0;
214
215  if ((pc & 3) != 0)
216    {
217      /* This occurs when single stepping and when compiling the not-taken
218           part of conditional branches.  */
219      UHI insn = GETIMEMUHI (current_cpu, pc);
220      int trace_p = PC_IN_TRACE_RANGE_P (current_cpu, pc);
221      int profile_p = PC_IN_PROFILE_RANGE_P (current_cpu, pc);
222      SCACHE *cti_sc; /* ??? tmp hack */
223
224      /* A parallel insn isn't allowed here, but we don't mind nops.
225           ??? We need to wait until the insn is executed before signalling
226           the error, for situations where such signalling is wanted.  */
227#if 0
228      if ((insn & 0x8000) != 0
229            && (insn & 0x7fff) != 0x7000) /* parallel nops are ok */
230          sim_engine_invalid_insn (current_cpu, pc, 0);
231#endif
232
233      /* Only emit before/after handlers if necessary.  */
234      if (FAST_P || (! trace_p && ! profile_p))
235          {
236            idesc = emit_16 (current_cpu, pc, insn & 0x7fff, sc, FAST_P, 0);
237            cti_sc = sc;
238            ++sc;
239            --max_insns;
240          }
241      else
242          {
243            idesc = emit_full16 (current_cpu, pc, insn & 0x7fff, sc,
244                                     trace_p, profile_p);
245            cti_sc = sc + 1;
246            sc += 3;
247            max_insns -= 3;
248          }
249      ++icount;
250      pc += 2;
251      if (IDESC_CTI_P (idesc))
252          {
253            SET_CTI_VPC (cti_sc);
254            goto Finish;
255          }
256    }
257
258  /* There are two copies of the compiler: full(!fast) and fast.
259     The "full" case emits before/after handlers for each insn.
260     Having two copies of this code is a tradeoff, having one copy
261     seemed a bit more difficult to read (due to constantly testing
262     FAST_P).  ??? On the other hand, with address ranges we'll want to
263     omit before/after handlers for unwanted insns.  Having separate loops
264     for FAST/!FAST avoids constantly doing the test in the loop, but
265     typically FAST_P is a constant and such tests will get optimized out.  */
266
267  if (FAST_P)
268    {
269      while (max_insns > 0)
270          {
271            USI insn = GETIMEMUSI (current_cpu, pc);
272            if ((SI) insn < 0)
273              {
274                /* 32 bit insn */
275                idesc = emit_32 (current_cpu, pc, insn, sc, 1);
276                ++sc;
277                --max_insns;
278                ++icount;
279                pc += 4;
280                if (IDESC_CTI_P (idesc))
281                    {
282                      SET_CTI_VPC (sc - 1);
283                      break;
284                    }
285              }
286            else
287              {
288                if ((insn & 0x8000) != 0) /* parallel? */
289                    {
290                      int up_count;
291
292                      if (((insn >> 16) & 0xfff0) == 0x10f0)
293                        {
294                          /* FIXME: No need to handle this sequentially if system
295                             calls will be able to execute after second insn in
296                             parallel. ( trap #num || insn ) */
297                          /* insn */
298                          idesc = emit_16 (current_cpu, pc + 2, insn & 0x7fff,
299                                               sc, 1, 0);
300                          /* trap */
301                          emit_16 (current_cpu, pc, insn >> 16, sc + 1, 1, 0);
302                          up_count = 2;
303                        }
304                      else
305                        {
306                          /* Yep.  Here's the "interesting" [sic] part.  */
307                          idesc = emit_parallel (current_cpu, pc, insn, sc, 1);
308                          up_count = 3;
309                        }
310                      sc += up_count;
311                      max_insns -= up_count;
312                      icount += 2;
313                      pc += 4;
314                      if (IDESC_CTI_P (idesc))
315                        {
316                          SET_CTI_VPC (sc - up_count);
317                          break;
318                        }
319                    }
320                else /* 2 serial 16 bit insns */
321                    {
322                      idesc = emit_16 (current_cpu, pc, insn >> 16, sc, 1, 0);
323                      ++sc;
324                      --max_insns;
325                      ++icount;
326                      pc += 2;
327                      if (IDESC_CTI_P (idesc))
328                        {
329                          SET_CTI_VPC (sc - 1);
330                          break;
331                        }
332                      /* While we're guaranteed that there's room to extract the
333                         insn, when single stepping we can't; the pbb must stop
334                         after the first insn.  */
335                      if (max_insns == 0)
336                        break;
337                      idesc = emit_16 (current_cpu, pc, insn & 0x7fff, sc, 1, 0);
338                      ++sc;
339                      --max_insns;
340                      ++icount;
341                      pc += 2;
342                      if (IDESC_CTI_P (idesc))
343                        {
344                          SET_CTI_VPC (sc - 1);
345                          break;
346                        }
347                    }
348              }
349          }
350    }
351  else /* ! FAST_P */
352    {
353      while (max_insns > 0)
354          {
355            USI insn = GETIMEMUSI (current_cpu, pc);
356            int trace_p = PC_IN_TRACE_RANGE_P (current_cpu, pc);
357            int profile_p = PC_IN_PROFILE_RANGE_P (current_cpu, pc);
358            SCACHE *cti_sc; /* ??? tmp hack */
359            if ((SI) insn < 0)
360              {
361                /* 32 bit insn
362                     Only emit before/after handlers if necessary.  */
363                if (trace_p || profile_p)
364                    {
365                      idesc = emit_full32 (current_cpu, pc, insn, sc,
366                                               trace_p, profile_p);
367                      cti_sc = sc + 1;
368                      sc += 3;
369                      max_insns -= 3;
370                    }
371                else
372                    {
373                      idesc = emit_32 (current_cpu, pc, insn, sc, 0);
374                      cti_sc = sc;
375                      ++sc;
376                      --max_insns;
377                    }
378                ++icount;
379                pc += 4;
380                if (IDESC_CTI_P (idesc))
381                    {
382                      SET_CTI_VPC (cti_sc);
383                      break;
384                    }
385              }
386            else
387              {
388                if ((insn & 0x8000) != 0) /* parallel? */
389                    {
390                      /* Yep.  Here's the "interesting" [sic] part.
391                         Only emit before/after handlers if necessary.  */
392                      if (trace_p || profile_p)
393                        {
394                          if (((insn >> 16) & 0xfff0) == 0x10f0)
395                              {
396                                /* FIXME: No need to handle this sequentially if
397                                   system calls will be able to execute after second
398                                   insn in parallel. ( trap #num || insn ) */
399                                /* insn */
400                                idesc = emit_full16 (current_cpu, pc + 2,
401                                                         insn & 0x7fff, sc, 0, 0);
402                                /* trap */
403                                emit_full16 (current_cpu, pc, insn >> 16, sc + 3,
404                                               0, 0);
405                              }
406                          else
407                              {
408                              idesc = emit_full_parallel (current_cpu, pc, insn,
409                                                                  sc, trace_p, profile_p);
410                              }
411                          cti_sc = sc + 1;
412                          sc += 6;
413                          max_insns -= 6;
414                        }
415                      else
416                        {
417                          int up_count;
418
419                          if (((insn >> 16) & 0xfff0) == 0x10f0)
420                              {
421                          /* FIXME: No need to handle this sequentially if
422                             system calls will be able to execute after second
423                             insn in parallel. ( trap #num || insn ) */
424                          /* insn */
425                          idesc = emit_16 (current_cpu, pc + 2, insn & 0x7fff,
426                                           sc, 0, 0);
427                          /* trap */
428                          emit_16 (current_cpu, pc, insn >> 16, sc + 1, 0, 0);
429                          up_count = 2;
430                              }
431                          else
432                              {
433                              idesc = emit_parallel (current_cpu, pc, insn, sc, 0);
434                          up_count = 3;
435                              }
436                          cti_sc = sc;
437                          sc += up_count;
438                          max_insns -= up_count;
439                        }
440                      icount += 2;
441                      pc += 4;
442                      if (IDESC_CTI_P (idesc))
443                        {
444                          SET_CTI_VPC (cti_sc);
445                          break;
446                        }
447                    }
448                else /* 2 serial 16 bit insns */
449                    {
450                      /* Only emit before/after handlers if necessary.  */
451                      if (trace_p || profile_p)
452                        {
453                          idesc = emit_full16 (current_cpu, pc, insn >> 16, sc,
454                                                     trace_p, profile_p);
455                          cti_sc = sc + 1;
456                          sc += 3;
457                          max_insns -= 3;
458                        }
459                      else
460                        {
461                          idesc = emit_16 (current_cpu, pc, insn >> 16, sc, 0, 0);
462                          cti_sc = sc;
463                          ++sc;
464                          --max_insns;
465                        }
466                      ++icount;
467                      pc += 2;
468                      if (IDESC_CTI_P (idesc))
469                        {
470                          SET_CTI_VPC (cti_sc);
471                          break;
472                        }
473                      /* While we're guaranteed that there's room to extract the
474                         insn, when single stepping we can't; the pbb must stop
475                         after the first insn.  */
476                      if (max_insns <= 0)
477                        break;
478                      /* Use the same trace/profile address for the 2nd insn.
479                         Saves us having to compute it and they come in pairs
480                         anyway (e.g. can never branch to the 2nd insn).  */
481                      if (trace_p || profile_p)
482                        {
483                          idesc = emit_full16 (current_cpu, pc, insn & 0x7fff, sc,
484                                                     trace_p, profile_p);
485                          cti_sc = sc + 1;
486                          sc += 3;
487                          max_insns -= 3;
488                        }
489                      else
490                        {
491                          idesc = emit_16 (current_cpu, pc, insn & 0x7fff, sc, 0, 0);
492                          cti_sc = sc;
493                          ++sc;
494                          --max_insns;
495                        }
496                      ++icount;
497                      pc += 2;
498                      if (IDESC_CTI_P (idesc))
499                        {
500                          SET_CTI_VPC (cti_sc);
501                          break;
502                        }
503                    }
504              }
505          }
506    }
507
508 Finish:
509  SET_INSN_COUNT (icount);
510}
511EOF
512
513;;
514
515xfull-exec-pbb)
516
517# Inputs: current_cpu, vpc, FAST_P
518# Outputs: vpc
519# vpc is the virtual program counter.
520
521cat <<EOF
522#line $LINENO "$0"
523#define DEFINE_SWITCH
524#include "semx-switch.c"
525EOF
526
527;;
528
529*)
530  echo "Invalid argument to mainloop.in: $1" >&2
531  exit 1
532  ;;
533
534esac
535