xref: /netbsd-src/external/gpl3/gdb/dist/sim/common/cgen-scache.c (revision 88241920d21b339bf319c0e979ffda80c49a2936)
198b9484cSchristos /* Simulator cache routines for CGEN simulators (and maybe others).
2*88241920Schristos    Copyright (C) 1996-2024 Free Software Foundation, Inc.
398b9484cSchristos    Contributed by Cygnus Support.
498b9484cSchristos 
598b9484cSchristos This file is part of GDB, the GNU debugger.
698b9484cSchristos 
798b9484cSchristos This program is free software; you can redistribute it and/or modify
898b9484cSchristos it under the terms of the GNU General Public License as published by
998b9484cSchristos the Free Software Foundation; either version 3 of the License, or
1098b9484cSchristos (at your option) any later version.
1198b9484cSchristos 
1298b9484cSchristos This program is distributed in the hope that it will be useful,
1398b9484cSchristos but WITHOUT ANY WARRANTY; without even the implied warranty of
1498b9484cSchristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1598b9484cSchristos GNU General Public License for more details.
1698b9484cSchristos 
1798b9484cSchristos You should have received a copy of the GNU General Public License
1898b9484cSchristos along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
1998b9484cSchristos 
204b169a6bSchristos /* This must come before any other includes.  */
214b169a6bSchristos #include "defs.h"
224b169a6bSchristos 
2398b9484cSchristos #define SCACHE_DEFINE_INLINE
2498b9484cSchristos 
2598b9484cSchristos #include <stdlib.h>
264b169a6bSchristos 
2798b9484cSchristos #include "libiberty.h"
284b169a6bSchristos 
294b169a6bSchristos #include "sim-main.h"
3098b9484cSchristos #include "sim-options.h"
3198b9484cSchristos #include "sim-io.h"
3298b9484cSchristos 
3398b9484cSchristos /* Unused address.  */
3498b9484cSchristos #define UNUSED_ADDR 0xffffffff
3598b9484cSchristos 
3698b9484cSchristos /* Scache configuration parameters.
3798b9484cSchristos    ??? Experiments to determine reasonable values is wip.
3898b9484cSchristos    These are just guesses.  */
3998b9484cSchristos 
4098b9484cSchristos /* Default number of scache elements.
4198b9484cSchristos    The size of an element is typically 32-64 bytes, so the size of the
4298b9484cSchristos    default scache will be between 512K and 1M bytes.  */
4398b9484cSchristos #ifdef CONFIG_SIM_CACHE_SIZE
4498b9484cSchristos #define SCACHE_DEFAULT_CACHE_SIZE CONFIG_SIM_CACHE_SIZE
4598b9484cSchristos #else
4698b9484cSchristos #define SCACHE_DEFAULT_CACHE_SIZE 16384
4798b9484cSchristos #endif
4898b9484cSchristos 
4998b9484cSchristos /* Minimum cache size.
5098b9484cSchristos    The m32r port assumes a cache size of at least 2 so it can decode both 16
5198b9484cSchristos    bit insns.  When compiling we need an extra for the chain entry.  And this
5298b9484cSchristos    must be a multiple of 2.  Hence 4 is the minimum (though, for those with
5398b9484cSchristos    featuritis or itchy pedantic bits, we could make this conditional on
5498b9484cSchristos    WITH_SCACHE_PBB).  */
5598b9484cSchristos #define MIN_SCACHE_SIZE 4
5698b9484cSchristos 
5798b9484cSchristos /* Ratio of size of text section to size of scache.
5898b9484cSchristos    When compiling, we don't want to flush the scache more than we have to
5998b9484cSchristos    but we also don't want it to be exorbitantly(sp?) large.  So we pick a high
6098b9484cSchristos    default value, then reduce it by the size of the program being simulated,
6198b9484cSchristos    but we don't override any value specified on the command line.
6298b9484cSchristos    If not specified on the command line, the size to use is computed as
6398b9484cSchristos    max (MIN_SCACHE_SIZE,
6498b9484cSchristos         min (DEFAULT_SCACHE_SIZE,
6598b9484cSchristos              text_size / (base_insn_size * INSN_SCACHE_RATIO))).  */
6698b9484cSchristos /* ??? Interesting idea but not currently used.  */
6798b9484cSchristos #define INSN_SCACHE_RATIO 4
6898b9484cSchristos 
6998b9484cSchristos /* Default maximum insn chain length.
7098b9484cSchristos    The only reason for a maximum is so we can place a maximum size on the
7198b9484cSchristos    profiling table.  Chain lengths are determined by cti's.
7298b9484cSchristos    32 is a more reasonable number, but when profiling, the before/after
7398b9484cSchristos    handlers take up that much more space.  The scache is filled from front to
7498b9484cSchristos    back so all this determines is when the scache needs to be flushed.  */
7598b9484cSchristos #define MAX_CHAIN_LENGTH 64
7698b9484cSchristos 
7798b9484cSchristos /* Default maximum hash list length.  */
7898b9484cSchristos #define MAX_HASH_CHAIN_LENGTH 4
7998b9484cSchristos 
8098b9484cSchristos /* Minimum hash table size.  */
8198b9484cSchristos #define MIN_HASH_CHAINS 32
8298b9484cSchristos 
8398b9484cSchristos /* Ratio of number of scache elements to number of hash lists.
8498b9484cSchristos    Since the user can only specify the size of the scache, we compute the
8598b9484cSchristos    size of the hash table as
8698b9484cSchristos    max (MIN_HASH_CHAINS, scache_size / SCACHE_HASH_RATIO).  */
8798b9484cSchristos #define SCACHE_HASH_RATIO 8
8898b9484cSchristos 
8998b9484cSchristos /* Hash a PC value.
9098b9484cSchristos    FIXME: May wish to make the hashing architecture specific.
9198b9484cSchristos    FIXME: revisit */
9298b9484cSchristos #define HASH_PC(pc) (((pc) >> 2) + ((pc) >> 5))
9398b9484cSchristos 
9498b9484cSchristos static MODULE_INIT_FN scache_init;
9598b9484cSchristos static MODULE_UNINSTALL_FN scache_uninstall;
9698b9484cSchristos 
9798b9484cSchristos static DECLARE_OPTION_HANDLER (scache_option_handler);
9898b9484cSchristos 
9998b9484cSchristos #define OPTION_PROFILE_SCACHE	(OPTION_START + 0)
10098b9484cSchristos 
10198b9484cSchristos static const OPTION scache_options[] = {
10298b9484cSchristos   { {"scache-size", optional_argument, NULL, 'c'},
10398b9484cSchristos       'c', "[SIZE]", "Specify size of simulator execution cache",
10498b9484cSchristos       scache_option_handler },
10598b9484cSchristos #if WITH_SCACHE_PBB
10698b9484cSchristos   /* ??? It might be nice to allow the user to specify the size of the hash
10798b9484cSchristos      table, the maximum hash list length, and the maximum chain length, but
10898b9484cSchristos      for now that might be more akin to featuritis.  */
10998b9484cSchristos #endif
11098b9484cSchristos   { {"profile-scache", optional_argument, NULL, OPTION_PROFILE_SCACHE},
11198b9484cSchristos       '\0', "on|off", "Perform simulator execution cache profiling",
11298b9484cSchristos       scache_option_handler },
11398b9484cSchristos   { {NULL, no_argument, NULL, 0}, '\0', NULL, NULL, NULL }
11498b9484cSchristos };
11598b9484cSchristos 
11698b9484cSchristos static SIM_RC
11798b9484cSchristos scache_option_handler (SIM_DESC sd, sim_cpu *cpu, int opt,
11898b9484cSchristos 		       char *arg, int is_command)
11998b9484cSchristos {
12098b9484cSchristos   switch (opt)
12198b9484cSchristos     {
12298b9484cSchristos     case 'c' :
12398b9484cSchristos       if (WITH_SCACHE)
12498b9484cSchristos 	{
12598b9484cSchristos 	  if (arg != NULL)
12698b9484cSchristos 	    {
127ba340e45Schristos 	      unsigned int n = (unsigned int) strtoul (arg, NULL, 0);
12898b9484cSchristos 	      if (n < MIN_SCACHE_SIZE)
12998b9484cSchristos 		{
130ba340e45Schristos 		  sim_io_eprintf (sd, "invalid scache size `%u', must be at least %u",
131ba340e45Schristos 				  n, MIN_SCACHE_SIZE);
13298b9484cSchristos 		  return SIM_RC_FAIL;
13398b9484cSchristos 		}
13498b9484cSchristos 	      /* Ensure it's a multiple of 2.  */
13598b9484cSchristos 	      if ((n & (n - 1)) != 0)
13698b9484cSchristos 		{
137ba340e45Schristos 		  unsigned int i;
138ba340e45Schristos 		  sim_io_eprintf (sd, "scache size `%u' not a multiple of 2\n", n);
139ba340e45Schristos 		  /* Round up to nearest multiple of 2.  */
140ba340e45Schristos 		  for (i = 1; i && i < n; i <<= 1)
14198b9484cSchristos 		    continue;
142ba340e45Schristos 		  if (i)
143ba340e45Schristos 		    {
14498b9484cSchristos 		      n = i;
145ba340e45Schristos 		      sim_io_eprintf (sd, "rounding scache size up to %u\n", n);
14698b9484cSchristos 		    }
14798b9484cSchristos 		}
14898b9484cSchristos 	      if (cpu == NULL)
14998b9484cSchristos 		STATE_SCACHE_SIZE (sd) = n;
15098b9484cSchristos 	      else
15198b9484cSchristos 		CPU_SCACHE_SIZE (cpu) = n;
15298b9484cSchristos 	    }
15398b9484cSchristos 	  else
15498b9484cSchristos 	    {
15598b9484cSchristos 	      if (cpu == NULL)
15698b9484cSchristos 		STATE_SCACHE_SIZE (sd) = SCACHE_DEFAULT_CACHE_SIZE;
15798b9484cSchristos 	      else
15898b9484cSchristos 		CPU_SCACHE_SIZE (cpu) = SCACHE_DEFAULT_CACHE_SIZE;
15998b9484cSchristos 	    }
16098b9484cSchristos 	}
16198b9484cSchristos       else
16298b9484cSchristos 	sim_io_eprintf (sd, "Simulator execution cache not enabled, `--scache-size' ignored\n");
16398b9484cSchristos       break;
16498b9484cSchristos 
16598b9484cSchristos     case OPTION_PROFILE_SCACHE :
16698b9484cSchristos       if (WITH_SCACHE && WITH_PROFILE_SCACHE_P)
16798b9484cSchristos 	{
16898b9484cSchristos 	  /* FIXME: handle cpu != NULL.  */
16998b9484cSchristos 	  return sim_profile_set_option (sd, "-scache", PROFILE_SCACHE_IDX,
17098b9484cSchristos 					 arg);
17198b9484cSchristos 	}
17298b9484cSchristos       else
17398b9484cSchristos 	sim_io_eprintf (sd, "Simulator cache profiling not compiled in, `--profile-scache' ignored\n");
17498b9484cSchristos       break;
17598b9484cSchristos     }
17698b9484cSchristos 
17798b9484cSchristos   return SIM_RC_OK;
17898b9484cSchristos }
17998b9484cSchristos 
1804b169a6bSchristos /* Provide a prototype to silence -Wmissing-prototypes.  */
1814b169a6bSchristos SIM_RC sim_install_scache (SIM_DESC sd);
1824b169a6bSchristos 
1834b169a6bSchristos /* Install the simulator cache into the simulator.  */
18498b9484cSchristos SIM_RC
1854b169a6bSchristos sim_install_scache (SIM_DESC sd)
18698b9484cSchristos {
18798b9484cSchristos   sim_add_option_table (sd, NULL, scache_options);
18898b9484cSchristos   sim_module_add_init_fn (sd, scache_init);
18998b9484cSchristos   sim_module_add_uninstall_fn (sd, scache_uninstall);
19098b9484cSchristos 
19198b9484cSchristos   /* This is the default, it may be overridden on the command line.  */
19298b9484cSchristos   STATE_SCACHE_SIZE (sd) = WITH_SCACHE;
19398b9484cSchristos 
19498b9484cSchristos   return SIM_RC_OK;
19598b9484cSchristos }
19698b9484cSchristos 
19798b9484cSchristos static SIM_RC
19898b9484cSchristos scache_init (SIM_DESC sd)
19998b9484cSchristos {
20098b9484cSchristos   int c;
20198b9484cSchristos 
20298b9484cSchristos   for (c = 0; c < MAX_NR_PROCESSORS; ++c)
20398b9484cSchristos     {
20498b9484cSchristos       SIM_CPU *cpu = STATE_CPU (sd, c);
20598b9484cSchristos       int elm_size = IMP_PROPS_SCACHE_ELM_SIZE (MACH_IMP_PROPS (CPU_MACH (cpu)));
20698b9484cSchristos 
20798b9484cSchristos       /* elm_size is 0 if the cpu doesn't not have scache support */
20898b9484cSchristos       if (elm_size == 0)
20998b9484cSchristos 	{
21098b9484cSchristos 	  CPU_SCACHE_SIZE (cpu) = 0;
21198b9484cSchristos 	  CPU_SCACHE_CACHE (cpu) = NULL;
21298b9484cSchristos 	}
21398b9484cSchristos       else
21498b9484cSchristos 	{
21598b9484cSchristos 	  if (CPU_SCACHE_SIZE (cpu) == 0)
21698b9484cSchristos 	    CPU_SCACHE_SIZE (cpu) = STATE_SCACHE_SIZE (sd);
21798b9484cSchristos 	  CPU_SCACHE_CACHE (cpu) =
21898b9484cSchristos 	    (SCACHE *) xmalloc (CPU_SCACHE_SIZE (cpu) * elm_size);
21998b9484cSchristos #if WITH_SCACHE_PBB
22098b9484cSchristos 	  CPU_SCACHE_MAX_CHAIN_LENGTH (cpu) = MAX_CHAIN_LENGTH;
22198b9484cSchristos 	  CPU_SCACHE_NUM_HASH_CHAIN_ENTRIES (cpu) = MAX_HASH_CHAIN_LENGTH;
222ba340e45Schristos 	  CPU_SCACHE_NUM_HASH_CHAINS (cpu) = max (MIN_HASH_CHAINS,
22398b9484cSchristos 						  CPU_SCACHE_SIZE (cpu)
22498b9484cSchristos 						  / SCACHE_HASH_RATIO);
22598b9484cSchristos 	  CPU_SCACHE_HASH_TABLE (cpu) =
22698b9484cSchristos 	    (SCACHE_MAP *) xmalloc (CPU_SCACHE_NUM_HASH_CHAINS (cpu)
22798b9484cSchristos 				    * CPU_SCACHE_NUM_HASH_CHAIN_ENTRIES (cpu)
22898b9484cSchristos 				    * sizeof (SCACHE_MAP));
22998b9484cSchristos 	  CPU_SCACHE_PBB_BEGIN (cpu) = (SCACHE *) zalloc (elm_size);
23098b9484cSchristos 	  CPU_SCACHE_CHAIN_LENGTHS (cpu) =
23198b9484cSchristos 	    (unsigned long *) zalloc ((CPU_SCACHE_MAX_CHAIN_LENGTH (cpu) + 1)
23298b9484cSchristos 				      * sizeof (long));
23398b9484cSchristos #endif
23498b9484cSchristos 	}
23598b9484cSchristos     }
23698b9484cSchristos 
23798b9484cSchristos   scache_flush (sd);
23898b9484cSchristos 
23998b9484cSchristos   return SIM_RC_OK;
24098b9484cSchristos }
24198b9484cSchristos 
24298b9484cSchristos static void
24398b9484cSchristos scache_uninstall (SIM_DESC sd)
24498b9484cSchristos {
24598b9484cSchristos   int c;
24698b9484cSchristos 
24798b9484cSchristos   for (c = 0; c < MAX_NR_PROCESSORS; ++c)
24898b9484cSchristos     {
24998b9484cSchristos       SIM_CPU *cpu = STATE_CPU (sd, c);
25098b9484cSchristos 
25198b9484cSchristos       if (CPU_SCACHE_CACHE (cpu) != NULL)
25298b9484cSchristos 	free (CPU_SCACHE_CACHE (cpu));
25398b9484cSchristos #if WITH_SCACHE_PBB
25498b9484cSchristos       if (CPU_SCACHE_HASH_TABLE (cpu) != NULL)
25598b9484cSchristos 	free (CPU_SCACHE_HASH_TABLE (cpu));
25698b9484cSchristos       if (CPU_SCACHE_PBB_BEGIN (cpu) != NULL)
25798b9484cSchristos 	free (CPU_SCACHE_PBB_BEGIN (cpu));
25898b9484cSchristos       if (CPU_SCACHE_CHAIN_LENGTHS (cpu) != NULL)
25998b9484cSchristos 	free (CPU_SCACHE_CHAIN_LENGTHS (cpu));
26098b9484cSchristos #endif
26198b9484cSchristos     }
26298b9484cSchristos }
26398b9484cSchristos 
26498b9484cSchristos void
26598b9484cSchristos scache_flush (SIM_DESC sd)
26698b9484cSchristos {
26798b9484cSchristos   int c;
26898b9484cSchristos 
26998b9484cSchristos   for (c = 0; c < MAX_NR_PROCESSORS; ++c)
27098b9484cSchristos     {
27198b9484cSchristos       SIM_CPU *cpu = STATE_CPU (sd, c);
27298b9484cSchristos       scache_flush_cpu (cpu);
27398b9484cSchristos     }
27498b9484cSchristos }
27598b9484cSchristos 
27698b9484cSchristos void
27798b9484cSchristos scache_flush_cpu (SIM_CPU *cpu)
27898b9484cSchristos {
279*88241920Schristos   int i;
280*88241920Schristos #if WITH_SCACHE_PBB
281*88241920Schristos   int n;
282*88241920Schristos #endif
28398b9484cSchristos 
28498b9484cSchristos   /* Don't bother if cache not in use.  */
28598b9484cSchristos   if (CPU_SCACHE_SIZE (cpu) == 0)
28698b9484cSchristos     return;
28798b9484cSchristos 
28898b9484cSchristos #if WITH_SCACHE_PBB
28998b9484cSchristos   /* It's important that this be reasonably fast as this can be done when
29098b9484cSchristos      the simulation is running.  */
29198b9484cSchristos   CPU_SCACHE_NEXT_FREE (cpu) = CPU_SCACHE_CACHE (cpu);
29298b9484cSchristos   n = CPU_SCACHE_NUM_HASH_CHAINS (cpu) * CPU_SCACHE_NUM_HASH_CHAIN_ENTRIES (cpu);
29398b9484cSchristos   /* ??? Might be faster to just set the first entry, then update the
29498b9484cSchristos      "last entry" marker during allocation.  */
29598b9484cSchristos   for (i = 0; i < n; ++i)
29698b9484cSchristos     CPU_SCACHE_HASH_TABLE (cpu) [i] . pc = UNUSED_ADDR;
29798b9484cSchristos #else
29898b9484cSchristos   {
29998b9484cSchristos     int elm_size = IMP_PROPS_SCACHE_ELM_SIZE (MACH_IMP_PROPS (CPU_MACH (cpu)));
30098b9484cSchristos     SCACHE *sc;
30198b9484cSchristos 
30298b9484cSchristos     /* Technically, this may not be necessary, but it helps debugging.  */
30398b9484cSchristos     memset (CPU_SCACHE_CACHE (cpu), 0,
30498b9484cSchristos 	    CPU_SCACHE_SIZE (cpu) * elm_size);
30598b9484cSchristos 
30698b9484cSchristos     for (i = 0, sc = CPU_SCACHE_CACHE (cpu); i < CPU_SCACHE_SIZE (cpu);
30798b9484cSchristos 	 ++i, sc = (SCACHE *) ((char *) sc + elm_size))
30898b9484cSchristos       {
30998b9484cSchristos 	sc->argbuf.addr = UNUSED_ADDR;
31098b9484cSchristos       }
31198b9484cSchristos   }
31298b9484cSchristos #endif
31398b9484cSchristos }
31498b9484cSchristos 
31598b9484cSchristos #if WITH_SCACHE_PBB
31698b9484cSchristos 
31798b9484cSchristos /* Look up PC in the hash table of scache entry points.
31898b9484cSchristos    Returns the entry or NULL if not found.  */
31998b9484cSchristos 
32098b9484cSchristos SCACHE *
32198b9484cSchristos scache_lookup (SIM_CPU *cpu, IADDR pc)
32298b9484cSchristos {
32398b9484cSchristos   /* FIXME: hash computation is wrong, doesn't take into account
32498b9484cSchristos      NUM_HASH_CHAIN_ENTRIES.  A lot of the hash table will be unused!  */
32598b9484cSchristos   unsigned int slot = HASH_PC (pc) & (CPU_SCACHE_NUM_HASH_CHAINS (cpu) - 1);
32698b9484cSchristos   int i, max_i = CPU_SCACHE_NUM_HASH_CHAIN_ENTRIES (cpu);
32798b9484cSchristos   SCACHE_MAP *scm;
32898b9484cSchristos 
32998b9484cSchristos   /* We don't update hit/miss statistics as this is only used when recording
33098b9484cSchristos      branch target addresses.  */
33198b9484cSchristos 
33298b9484cSchristos   scm = & CPU_SCACHE_HASH_TABLE (cpu) [slot];
33398b9484cSchristos   for (i = 0; i < max_i && scm->pc != UNUSED_ADDR; ++i, ++scm)
33498b9484cSchristos     {
33598b9484cSchristos       if (scm->pc == pc)
33698b9484cSchristos 	return scm->sc;
33798b9484cSchristos     }
33898b9484cSchristos   return 0;
33998b9484cSchristos }
34098b9484cSchristos 
34198b9484cSchristos /* Look up PC and if not found create an entry for it.
34298b9484cSchristos    If found the result is a pointer to the SCACHE entry.
34398b9484cSchristos    If not found the result is NULL, and the address of a buffer of at least
34498b9484cSchristos    N entries is stored in BUFP.
34598b9484cSchristos    It's done this way so the caller can still distinguish found/not-found.
34698b9484cSchristos    If the table is full, it is emptied to make room.
34798b9484cSchristos    If the maximum length of a hash list is reached a random entry is thrown out
34898b9484cSchristos    to make room.
34998b9484cSchristos    ??? One might want to try to make this smarter, but let's see some
35098b9484cSchristos    measurable benefit first.  */
35198b9484cSchristos 
35298b9484cSchristos SCACHE *
35398b9484cSchristos scache_lookup_or_alloc (SIM_CPU *cpu, IADDR pc, int n, SCACHE **bufp)
35498b9484cSchristos {
35598b9484cSchristos   /* FIXME: hash computation is wrong, doesn't take into account
35698b9484cSchristos      NUM_HASH_CHAIN_ENTRIES.  A lot of the hash table will be unused!  */
35798b9484cSchristos   unsigned int slot = HASH_PC (pc) & (CPU_SCACHE_NUM_HASH_CHAINS (cpu) - 1);
35898b9484cSchristos   int i, max_i = CPU_SCACHE_NUM_HASH_CHAIN_ENTRIES (cpu);
35998b9484cSchristos   SCACHE_MAP *scm;
36098b9484cSchristos   SCACHE *sc;
36198b9484cSchristos 
36298b9484cSchristos   scm = & CPU_SCACHE_HASH_TABLE (cpu) [slot];
36398b9484cSchristos   for (i = 0; i < max_i && scm->pc != UNUSED_ADDR; ++i, ++scm)
36498b9484cSchristos     {
36598b9484cSchristos       if (scm->pc == pc)
36698b9484cSchristos 	{
36798b9484cSchristos 	  PROFILE_COUNT_SCACHE_HIT (cpu);
36898b9484cSchristos 	  return scm->sc;
36998b9484cSchristos 	}
37098b9484cSchristos     }
37198b9484cSchristos   PROFILE_COUNT_SCACHE_MISS (cpu);
37298b9484cSchristos 
37398b9484cSchristos   /* The address we want isn't cached.  Bummer.
37498b9484cSchristos      If the hash chain we have for this address is full, throw out an entry
37598b9484cSchristos      to make room.  */
37698b9484cSchristos 
37798b9484cSchristos   if (i == max_i)
37898b9484cSchristos     {
37998b9484cSchristos       /* Rather than do something sophisticated like LRU, we just throw out
38098b9484cSchristos 	 a semi-random entry.  Let someone else have the joy of saying how
38198b9484cSchristos 	 wrong this is.  NEXT_FREE is the entry to throw out and cycles
38298b9484cSchristos 	 through all possibilities.  */
38398b9484cSchristos       static int next_free = 0;
38498b9484cSchristos 
38598b9484cSchristos       scm = & CPU_SCACHE_HASH_TABLE (cpu) [slot];
38698b9484cSchristos       /* FIXME: This seems rather clumsy.  */
38798b9484cSchristos       for (i = 0; i < next_free; ++i, ++scm)
38898b9484cSchristos 	continue;
38998b9484cSchristos       ++next_free;
39098b9484cSchristos       if (next_free == CPU_SCACHE_NUM_HASH_CHAIN_ENTRIES (cpu))
39198b9484cSchristos 	next_free = 0;
39298b9484cSchristos     }
39398b9484cSchristos 
39498b9484cSchristos   /* At this point SCM points to the hash table entry to use.
39598b9484cSchristos      Now make sure there's room in the cache.  */
39698b9484cSchristos   /* FIXME: Kinda weird to use a next_free adjusted scm when cache is
39798b9484cSchristos      flushed.  */
39898b9484cSchristos 
39998b9484cSchristos   {
40098b9484cSchristos     int elm_size = IMP_PROPS_SCACHE_ELM_SIZE (MACH_IMP_PROPS (CPU_MACH (cpu)));
40198b9484cSchristos     int elms_used = (((char *) CPU_SCACHE_NEXT_FREE (cpu)
40298b9484cSchristos 		      - (char *) CPU_SCACHE_CACHE (cpu))
40398b9484cSchristos 		     / elm_size);
40498b9484cSchristos     int elms_left = CPU_SCACHE_SIZE (cpu) - elms_used;
40598b9484cSchristos 
40698b9484cSchristos     if (elms_left < n)
40798b9484cSchristos       {
40898b9484cSchristos 	PROFILE_COUNT_SCACHE_FULL_FLUSH (cpu);
40998b9484cSchristos 	scache_flush_cpu (cpu);
41098b9484cSchristos       }
41198b9484cSchristos   }
41298b9484cSchristos 
41398b9484cSchristos   sc = CPU_SCACHE_NEXT_FREE (cpu);
41498b9484cSchristos   scm->pc = pc;
41598b9484cSchristos   scm->sc = sc;
41698b9484cSchristos 
41798b9484cSchristos   *bufp = sc;
41898b9484cSchristos   return NULL;
41998b9484cSchristos }
42098b9484cSchristos 
42198b9484cSchristos #endif /* WITH_SCACHE_PBB */
42298b9484cSchristos 
42398b9484cSchristos /* Print cache access statics for CPU.  */
42498b9484cSchristos 
42598b9484cSchristos void
426*88241920Schristos scache_print_profile (SIM_CPU *cpu, bool verbose)
42798b9484cSchristos {
42898b9484cSchristos   SIM_DESC sd = CPU_STATE (cpu);
42998b9484cSchristos   unsigned long hits = CPU_SCACHE_HITS (cpu);
43098b9484cSchristos   unsigned long misses = CPU_SCACHE_MISSES (cpu);
43198b9484cSchristos   char buf[20];
43298b9484cSchristos 
43398b9484cSchristos   if (CPU_SCACHE_SIZE (cpu) == 0)
43498b9484cSchristos     return;
43598b9484cSchristos 
43698b9484cSchristos   sim_io_printf (sd, "Simulator Cache Statistics\n\n");
43798b9484cSchristos 
43898b9484cSchristos   /* One could use PROFILE_LABEL_WIDTH here.  I chose not to.  */
43998b9484cSchristos   sim_io_printf (sd, "  Cache size: %s\n",
44098b9484cSchristos 		 sim_add_commas (buf, sizeof (buf), CPU_SCACHE_SIZE (cpu)));
44198b9484cSchristos   sim_io_printf (sd, "  Hits:       %s\n",
44298b9484cSchristos 		 sim_add_commas (buf, sizeof (buf), hits));
44398b9484cSchristos   sim_io_printf (sd, "  Misses:     %s\n",
44498b9484cSchristos 		 sim_add_commas (buf, sizeof (buf), misses));
44598b9484cSchristos   if (hits + misses != 0)
44698b9484cSchristos     sim_io_printf (sd, "  Hit rate:   %.2f%%\n",
44798b9484cSchristos 		   ((double) hits / ((double) hits + (double) misses)) * 100);
44898b9484cSchristos 
44998b9484cSchristos #if WITH_SCACHE_PBB
45098b9484cSchristos   sim_io_printf (sd, "\n");
45198b9484cSchristos   sim_io_printf (sd, "  Hash table size:       %s\n",
45298b9484cSchristos 		 sim_add_commas (buf, sizeof (buf), CPU_SCACHE_NUM_HASH_CHAINS (cpu)));
45398b9484cSchristos   sim_io_printf (sd, "  Max hash list length:  %s\n",
45498b9484cSchristos 		 sim_add_commas (buf, sizeof (buf), CPU_SCACHE_NUM_HASH_CHAIN_ENTRIES (cpu)));
45598b9484cSchristos   sim_io_printf (sd, "  Max insn chain length: %s\n",
45698b9484cSchristos 		 sim_add_commas (buf, sizeof (buf), CPU_SCACHE_MAX_CHAIN_LENGTH (cpu)));
45798b9484cSchristos   sim_io_printf (sd, "  Cache full flushes:    %s\n",
45898b9484cSchristos 		 sim_add_commas (buf, sizeof (buf), CPU_SCACHE_FULL_FLUSHES (cpu)));
45998b9484cSchristos   sim_io_printf (sd, "\n");
46098b9484cSchristos 
46198b9484cSchristos   if (verbose)
46298b9484cSchristos     {
463*88241920Schristos       unsigned long max_val;
464*88241920Schristos       unsigned long *lengths;
465*88241920Schristos       int i;
466*88241920Schristos 
46798b9484cSchristos       sim_io_printf (sd, "  Insn chain lengths:\n\n");
46898b9484cSchristos       max_val = 0;
46998b9484cSchristos       lengths = CPU_SCACHE_CHAIN_LENGTHS (cpu);
47098b9484cSchristos       for (i = 1; i < CPU_SCACHE_MAX_CHAIN_LENGTH (cpu); ++i)
47198b9484cSchristos 	if (lengths[i] > max_val)
47298b9484cSchristos 	  max_val = lengths[i];
47398b9484cSchristos       for (i = 1; i < CPU_SCACHE_MAX_CHAIN_LENGTH (cpu); ++i)
47498b9484cSchristos 	{
47598b9484cSchristos 	  sim_io_printf (sd, "  %2d: %*s: ",
47698b9484cSchristos 			 i,
47798b9484cSchristos 			 max_val < 10000 ? 5 : 10,
47898b9484cSchristos 			 sim_add_commas (buf, sizeof (buf), lengths[i]));
47998b9484cSchristos 	  sim_profile_print_bar (sd, cpu, PROFILE_HISTOGRAM_WIDTH,
48098b9484cSchristos 				 lengths[i], max_val);
48198b9484cSchristos 	  sim_io_printf (sd, "\n");
48298b9484cSchristos 	}
48398b9484cSchristos       sim_io_printf (sd, "\n");
48498b9484cSchristos     }
48598b9484cSchristos #endif /* WITH_SCACHE_PBB */
48698b9484cSchristos }
487