xref: /netbsd-src/external/gpl3/gdb/dist/sim/common/sim-arange.c (revision 0d3e0572e40d81edb4fdbff937458d47b685c34c)
14e98e3e1Schristos /* Address ranges.
2*0d3e0572Schristos    Copyright (C) 1998-2024 Free Software Foundation, Inc.
34e98e3e1Schristos    Contributed by Cygnus Solutions.
44e98e3e1Schristos 
54e98e3e1Schristos This file is part of the GNU Simulators.
64e98e3e1Schristos 
74e98e3e1Schristos This program is free software; you can redistribute it and/or modify
84e98e3e1Schristos it under the terms of the GNU General Public License as published by
94e98e3e1Schristos the Free Software Foundation; either version 3 of the License, or
104e98e3e1Schristos (at your option) any later version.
114e98e3e1Schristos 
124e98e3e1Schristos This program is distributed in the hope that it will be useful,
134e98e3e1Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of
144e98e3e1Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
154e98e3e1Schristos GNU General Public License for more details.
164e98e3e1Schristos 
174e98e3e1Schristos You should have received a copy of the GNU General Public License
184e98e3e1Schristos along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
194e98e3e1Schristos 
208dffb485Schristos #ifndef _SIM_ARANGE_C_
218dffb485Schristos #define _SIM_ARANGE_C_
224e98e3e1Schristos 
234b169a6bSchristos /* This must come before any other includes.  */
244b169a6bSchristos #include "defs.h"
254b169a6bSchristos 
264b169a6bSchristos #include <stdlib.h>
274b169a6bSchristos #include <string.h>
284b169a6bSchristos 
294e98e3e1Schristos #include "libiberty.h"
304b169a6bSchristos 
314e98e3e1Schristos #include "sim-basics.h"
328dffb485Schristos #include "sim-arange.h"
334e98e3e1Schristos 
344e98e3e1Schristos /* Insert a range.  */
354e98e3e1Schristos 
364e98e3e1Schristos static void
374e98e3e1Schristos insert_range (ADDR_SUBRANGE **pos, ADDR_SUBRANGE *asr)
384e98e3e1Schristos {
394e98e3e1Schristos   asr->next = *pos;
404e98e3e1Schristos   *pos = asr;
414e98e3e1Schristos }
424e98e3e1Schristos 
434e98e3e1Schristos /* Delete a range.  */
444e98e3e1Schristos 
454e98e3e1Schristos static void
464e98e3e1Schristos delete_range (ADDR_SUBRANGE **thisasrp)
474e98e3e1Schristos {
484e98e3e1Schristos   ADDR_SUBRANGE *thisasr;
494e98e3e1Schristos 
504e98e3e1Schristos   thisasr = *thisasrp;
514e98e3e1Schristos   *thisasrp = thisasr->next;
524e98e3e1Schristos 
534e98e3e1Schristos   free (thisasr);
544e98e3e1Schristos }
554e98e3e1Schristos 
564e98e3e1Schristos /* Add or delete an address range.
574e98e3e1Schristos    This code was borrowed from linux's locks.c:posix_lock_file().
584e98e3e1Schristos    ??? Todo: Given our simpler needs this could be simplified
594e98e3e1Schristos    (split into two fns).  */
604e98e3e1Schristos 
614e98e3e1Schristos static void
624e98e3e1Schristos frob_range (ADDR_RANGE *ar, address_word start, address_word end, int delete_p)
634e98e3e1Schristos {
644e98e3e1Schristos   ADDR_SUBRANGE *asr;
654e98e3e1Schristos   ADDR_SUBRANGE *new_asr, *new_asr2;
664e98e3e1Schristos   ADDR_SUBRANGE *left = NULL;
674e98e3e1Schristos   ADDR_SUBRANGE *right = NULL;
684e98e3e1Schristos   ADDR_SUBRANGE **before;
694e98e3e1Schristos   ADDR_SUBRANGE init_caller;
704e98e3e1Schristos   ADDR_SUBRANGE *caller = &init_caller;
714e98e3e1Schristos   int added_p = 0;
724e98e3e1Schristos 
734e98e3e1Schristos   memset (caller, 0, sizeof (ADDR_SUBRANGE));
744e98e3e1Schristos   new_asr = ZALLOC (ADDR_SUBRANGE);
754e98e3e1Schristos   new_asr2 = ZALLOC (ADDR_SUBRANGE);
764e98e3e1Schristos 
774e98e3e1Schristos   caller->start = start;
784e98e3e1Schristos   caller->end = end;
794e98e3e1Schristos   before = &ar->ranges;
804e98e3e1Schristos 
814e98e3e1Schristos   while ((asr = *before) != NULL)
824e98e3e1Schristos     {
834e98e3e1Schristos       if (! delete_p)
844e98e3e1Schristos 	{
854e98e3e1Schristos 	  /* Try next range if current range preceeds new one and not
864e98e3e1Schristos 	     adjacent or overlapping.  */
874e98e3e1Schristos 	  if (asr->end < caller->start - 1)
884e98e3e1Schristos 	    goto next_range;
894e98e3e1Schristos 
904e98e3e1Schristos 	  /* Break out if new range preceeds current one and not
914e98e3e1Schristos 	     adjacent or overlapping.  */
924e98e3e1Schristos 	  if (asr->start > caller->end + 1)
934e98e3e1Schristos 	    break;
944e98e3e1Schristos 
954e98e3e1Schristos 	  /* If we come here, the new and current ranges are adjacent or
964e98e3e1Schristos 	     overlapping. Make one range yielding from the lower start address
974e98e3e1Schristos 	     of both ranges to the higher end address.  */
984e98e3e1Schristos 	  if (asr->start > caller->start)
994e98e3e1Schristos 	    asr->start = caller->start;
1004e98e3e1Schristos 	  else
1014e98e3e1Schristos 	    caller->start = asr->start;
1024e98e3e1Schristos 	  if (asr->end < caller->end)
1034e98e3e1Schristos 	    asr->end = caller->end;
1044e98e3e1Schristos 	  else
1054e98e3e1Schristos 	    caller->end = asr->end;
1064e98e3e1Schristos 
1074e98e3e1Schristos 	  if (added_p)
1084e98e3e1Schristos 	    {
1094e98e3e1Schristos 	      delete_range (before);
1104e98e3e1Schristos 	      continue;
1114e98e3e1Schristos 	    }
1124e98e3e1Schristos 	  caller = asr;
1134e98e3e1Schristos 	  added_p = 1;
1144e98e3e1Schristos 	}
1154e98e3e1Schristos       else /* deleting a range */
1164e98e3e1Schristos 	{
1174e98e3e1Schristos 	  /* Try next range if current range preceeds new one.  */
1184e98e3e1Schristos 	  if (asr->end < caller->start)
1194e98e3e1Schristos 	    goto next_range;
1204e98e3e1Schristos 
1214e98e3e1Schristos 	  /* Break out if new range preceeds current one.  */
1224e98e3e1Schristos 	  if (asr->start > caller->end)
1234e98e3e1Schristos 	    break;
1244e98e3e1Schristos 
1254e98e3e1Schristos 	  added_p = 1;
1264e98e3e1Schristos 
1274e98e3e1Schristos 	  if (asr->start < caller->start)
1284e98e3e1Schristos 	    left = asr;
1294e98e3e1Schristos 
1304e98e3e1Schristos 	  /* If the next range in the list has a higher end
1314e98e3e1Schristos 	     address than the new one, insert the new one here.  */
1324e98e3e1Schristos 	  if (asr->end > caller->end)
1334e98e3e1Schristos 	    {
1344e98e3e1Schristos 	      right = asr;
1354e98e3e1Schristos 	      break;
1364e98e3e1Schristos 	    }
1374e98e3e1Schristos 	  if (asr->start >= caller->start)
1384e98e3e1Schristos 	    {
1394e98e3e1Schristos 	      /* The new range completely replaces an old
1404e98e3e1Schristos 	         one (This may happen several times).  */
1414e98e3e1Schristos 	      if (added_p)
1424e98e3e1Schristos 		{
1434e98e3e1Schristos 		  delete_range (before);
1444e98e3e1Schristos 		  continue;
1454e98e3e1Schristos 		}
1464e98e3e1Schristos 
1474e98e3e1Schristos 	      /* Replace the old range with the new one.  */
1484e98e3e1Schristos 	      asr->start = caller->start;
1494e98e3e1Schristos 	      asr->end = caller->end;
1504e98e3e1Schristos 	      caller = asr;
1514e98e3e1Schristos 	      added_p = 1;
1524e98e3e1Schristos 	    }
1534e98e3e1Schristos 	}
1544e98e3e1Schristos 
1554e98e3e1Schristos       /* Go on to next range.  */
1564e98e3e1Schristos     next_range:
1574e98e3e1Schristos       before = &asr->next;
1584e98e3e1Schristos     }
1594e98e3e1Schristos 
1604e98e3e1Schristos   if (!added_p)
1614e98e3e1Schristos     {
1624e98e3e1Schristos       if (delete_p)
1634e98e3e1Schristos 	goto out;
1644e98e3e1Schristos       new_asr->start = caller->start;
1654e98e3e1Schristos       new_asr->end = caller->end;
1664e98e3e1Schristos       insert_range (before, new_asr);
1674e98e3e1Schristos       new_asr = NULL;
1684e98e3e1Schristos     }
1694e98e3e1Schristos   if (right)
1704e98e3e1Schristos     {
1714e98e3e1Schristos       if (left == right)
1724e98e3e1Schristos 	{
1734e98e3e1Schristos 	  /* The new range breaks the old one in two pieces,
1744e98e3e1Schristos 	     so we have to use the second new range.  */
1754e98e3e1Schristos 	  new_asr2->start = right->start;
1764e98e3e1Schristos 	  new_asr2->end = right->end;
1774e98e3e1Schristos 	  left = new_asr2;
1784e98e3e1Schristos 	  insert_range (before, left);
1794e98e3e1Schristos 	  new_asr2 = NULL;
1804e98e3e1Schristos 	}
1814e98e3e1Schristos       right->start = caller->end + 1;
1824e98e3e1Schristos     }
1834e98e3e1Schristos   if (left)
1844e98e3e1Schristos     {
1854e98e3e1Schristos       left->end = caller->start - 1;
1864e98e3e1Schristos     }
1874e98e3e1Schristos 
1884e98e3e1Schristos  out:
1894e98e3e1Schristos   if (new_asr)
1904e98e3e1Schristos     free (new_asr);
1914e98e3e1Schristos   if (new_asr2)
1924e98e3e1Schristos     free (new_asr2);
1934e98e3e1Schristos }
1944e98e3e1Schristos 
1954e98e3e1Schristos /* Free T and all subtrees.  */
1964e98e3e1Schristos 
1974e98e3e1Schristos static void
1984e98e3e1Schristos free_search_tree (ADDR_RANGE_TREE *t)
1994e98e3e1Schristos {
2004e98e3e1Schristos   if (t != NULL)
2014e98e3e1Schristos     {
2024e98e3e1Schristos       free_search_tree (t->lower);
2034e98e3e1Schristos       free_search_tree (t->higher);
2044e98e3e1Schristos       free (t);
2054e98e3e1Schristos     }
2064e98e3e1Schristos }
2074e98e3e1Schristos 
2084e98e3e1Schristos /* Subroutine of build_search_tree to recursively build a balanced tree.
2094e98e3e1Schristos    ??? It's not an optimum tree though.  */
2104e98e3e1Schristos 
2114e98e3e1Schristos static ADDR_RANGE_TREE *
2124e98e3e1Schristos build_tree_1 (ADDR_SUBRANGE **asrtab, unsigned int n)
2134e98e3e1Schristos {
2144e98e3e1Schristos   unsigned int mid = n / 2;
2154e98e3e1Schristos   ADDR_RANGE_TREE *t;
2164e98e3e1Schristos 
2174e98e3e1Schristos   if (n == 0)
2184e98e3e1Schristos     return NULL;
2194e98e3e1Schristos   t = (ADDR_RANGE_TREE *) xmalloc (sizeof (ADDR_RANGE_TREE));
2204e98e3e1Schristos   t->start = asrtab[mid]->start;
2214e98e3e1Schristos   t->end = asrtab[mid]->end;
2224e98e3e1Schristos   if (mid != 0)
2234e98e3e1Schristos     t->lower = build_tree_1 (asrtab, mid);
2244e98e3e1Schristos   else
2254e98e3e1Schristos     t->lower = NULL;
2264e98e3e1Schristos   if (n > mid + 1)
2274e98e3e1Schristos     t->higher = build_tree_1 (asrtab + mid + 1, n - mid - 1);
2284e98e3e1Schristos   else
2294e98e3e1Schristos     t->higher = NULL;
2304e98e3e1Schristos   return t;
2314e98e3e1Schristos }
2324e98e3e1Schristos 
2334e98e3e1Schristos /* Build a search tree for address range AR.  */
2344e98e3e1Schristos 
2354e98e3e1Schristos static void
2364e98e3e1Schristos build_search_tree (ADDR_RANGE *ar)
2374e98e3e1Schristos {
2384e98e3e1Schristos   /* ??? Simple version for now.  */
2394e98e3e1Schristos   ADDR_SUBRANGE *asr,**asrtab;
2404e98e3e1Schristos   unsigned int i, n;
2414e98e3e1Schristos 
2424e98e3e1Schristos   for (n = 0, asr = ar->ranges; asr != NULL; ++n, asr = asr->next)
2434e98e3e1Schristos     continue;
2444e98e3e1Schristos   asrtab = (ADDR_SUBRANGE **) xmalloc (n * sizeof (ADDR_SUBRANGE *));
2454e98e3e1Schristos   for (i = 0, asr = ar->ranges; i < n; ++i, asr = asr->next)
2464e98e3e1Schristos     asrtab[i] = asr;
2474e98e3e1Schristos   ar->range_tree = build_tree_1 (asrtab, n);
2484e98e3e1Schristos   free (asrtab);
2494e98e3e1Schristos }
2504e98e3e1Schristos 
2518dffb485Schristos INLINE_SIM_ARANGE\
2528dffb485Schristos (void)
2534e98e3e1Schristos sim_addr_range_add (ADDR_RANGE *ar, address_word start, address_word end)
2544e98e3e1Schristos {
2554e98e3e1Schristos   frob_range (ar, start, end, 0);
2564e98e3e1Schristos 
2574e98e3e1Schristos   /* Rebuild the search tree.  */
2584e98e3e1Schristos   /* ??? Instead of rebuilding it here it could be done in a module resume
2594e98e3e1Schristos      handler, say by first checking for a `changed' flag, assuming of course
2604e98e3e1Schristos      this would never be done while the simulation is running.  */
2614e98e3e1Schristos   free_search_tree (ar->range_tree);
2624e98e3e1Schristos   build_search_tree (ar);
2634e98e3e1Schristos }
2644e98e3e1Schristos 
2658dffb485Schristos INLINE_SIM_ARANGE\
2668dffb485Schristos (void)
2674e98e3e1Schristos sim_addr_range_delete (ADDR_RANGE *ar, address_word start, address_word end)
2684e98e3e1Schristos {
2694e98e3e1Schristos   frob_range (ar, start, end, 1);
2704e98e3e1Schristos 
2714e98e3e1Schristos   /* Rebuild the search tree.  */
2724e98e3e1Schristos   /* ??? Instead of rebuilding it here it could be done in a module resume
2734e98e3e1Schristos      handler, say by first checking for a `changed' flag, assuming of course
2744e98e3e1Schristos      this would never be done while the simulation is running.  */
2754e98e3e1Schristos   free_search_tree (ar->range_tree);
2764e98e3e1Schristos   build_search_tree (ar);
2774e98e3e1Schristos }
2784e98e3e1Schristos 
2798dffb485Schristos INLINE_SIM_ARANGE\
2808dffb485Schristos (int)
2814e98e3e1Schristos sim_addr_range_hit_p (ADDR_RANGE *ar, address_word addr)
2824e98e3e1Schristos {
2834e98e3e1Schristos   ADDR_RANGE_TREE *t = ar->range_tree;
2844e98e3e1Schristos 
2854e98e3e1Schristos   while (t != NULL)
2864e98e3e1Schristos     {
2874e98e3e1Schristos       if (addr < t->start)
2884e98e3e1Schristos 	t = t->lower;
2894e98e3e1Schristos       else if (addr > t->end)
2904e98e3e1Schristos 	t = t->higher;
2914e98e3e1Schristos       else
2924e98e3e1Schristos 	return 1;
2934e98e3e1Schristos     }
2944e98e3e1Schristos   return 0;
2954e98e3e1Schristos }
2964e98e3e1Schristos 
2978dffb485Schristos #endif /* _SIM_ARANGE_C_ */
298