summaryrefslogtreecommitdiff
path: root/sim/common/sim-arange.c
diff options
context:
space:
mode:
Diffstat (limited to 'sim/common/sim-arange.c')
-rw-r--r--sim/common/sim-arange.c301
1 files changed, 301 insertions, 0 deletions
diff --git a/sim/common/sim-arange.c b/sim/common/sim-arange.c
new file mode 100644
index 00000000000..d9955e687e5
--- /dev/null
+++ b/sim/common/sim-arange.c
@@ -0,0 +1,301 @@
+/* Address ranges.
+ Copyright (C) 1998 Free Software Foundation, Inc.
+ Contributed by Cygnus Solutions.
+
+This file is part of the GNU Simulators.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* Tell sim-arange.h it's us. */
+#define SIM_ARANGE_C
+
+#include "libiberty.h"
+#include "sim-basics.h"
+#include "sim-assert.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#define DEFINE_INLINE_P (! defined (SIM_ARANGE_C_INCLUDED))
+#define DEFINE_NON_INLINE_P defined (SIM_ARANGE_C_INCLUDED)
+
+#if DEFINE_NON_INLINE_P
+
+/* Insert a range. */
+
+static void
+insert_range (ADDR_SUBRANGE **pos, ADDR_SUBRANGE *asr)
+{
+ asr->next = *pos;
+ *pos = asr;
+}
+
+/* Delete a range. */
+
+static void
+delete_range (ADDR_SUBRANGE **thisasrp)
+{
+ ADDR_SUBRANGE *thisasr;
+
+ thisasr = *thisasrp;
+ *thisasrp = thisasr->next;
+
+ free (thisasr);
+}
+
+/* Add or delete an address range.
+ This code was borrowed from linux's locks.c:posix_lock_file().
+ ??? Todo: Given our simpler needs this could be simplified
+ (split into two fns). */
+
+static void
+frob_range (ADDR_RANGE *ar, address_word start, address_word end, int delete_p)
+{
+ ADDR_SUBRANGE *asr;
+ ADDR_SUBRANGE *new_asr, *new_asr2;
+ ADDR_SUBRANGE *left = NULL;
+ ADDR_SUBRANGE *right = NULL;
+ ADDR_SUBRANGE **before;
+ ADDR_SUBRANGE init_caller;
+ ADDR_SUBRANGE *caller = &init_caller;
+ int added_p = 0;
+
+ memset (caller, 0, sizeof (ADDR_SUBRANGE));
+ new_asr = ZALLOC (ADDR_SUBRANGE);
+ new_asr2 = ZALLOC (ADDR_SUBRANGE);
+
+ caller->start = start;
+ caller->end = end;
+ before = &ar->ranges;
+
+ while ((asr = *before) != NULL)
+ {
+ if (! delete_p)
+ {
+ /* Try next range if current range preceeds new one and not
+ adjacent or overlapping. */
+ if (asr->end < caller->start - 1)
+ goto next_range;
+
+ /* Break out if new range preceeds current one and not
+ adjacent or overlapping. */
+ if (asr->start > caller->end + 1)
+ break;
+
+ /* If we come here, the new and current ranges are adjacent or
+ overlapping. Make one range yielding from the lower start address
+ of both ranges to the higher end address. */
+ if (asr->start > caller->start)
+ asr->start = caller->start;
+ else
+ caller->start = asr->start;
+ if (asr->end < caller->end)
+ asr->end = caller->end;
+ else
+ caller->end = asr->end;
+
+ if (added_p)
+ {
+ delete_range (before);
+ continue;
+ }
+ caller = asr;
+ added_p = 1;
+ }
+ else /* deleting a range */
+ {
+ /* Try next range if current range preceeds new one. */
+ if (asr->end < caller->start)
+ goto next_range;
+
+ /* Break out if new range preceeds current one. */
+ if (asr->start > caller->end)
+ break;
+
+ added_p = 1;
+
+ if (asr->start < caller->start)
+ left = asr;
+
+ /* If the next range in the list has a higher end
+ address than the new one, insert the new one here. */
+ if (asr->end > caller->end)
+ {
+ right = asr;
+ break;
+ }
+ if (asr->start >= caller->start)
+ {
+ /* The new range completely replaces an old
+ one (This may happen several times). */
+ if (added_p)
+ {
+ delete_range (before);
+ continue;
+ }
+
+ /* Replace the old range with the new one. */
+ asr->start = caller->start;
+ asr->end = caller->end;
+ caller = asr;
+ added_p = 1;
+ }
+ }
+
+ /* Go on to next range. */
+ next_range:
+ before = &asr->next;
+ }
+
+ if (!added_p)
+ {
+ if (delete_p)
+ goto out;
+ new_asr->start = caller->start;
+ new_asr->end = caller->end;
+ insert_range (before, new_asr);
+ new_asr = NULL;
+ }
+ if (right)
+ {
+ if (left == right)
+ {
+ /* The new range breaks the old one in two pieces,
+ so we have to use the second new range. */
+ new_asr2->start = right->start;
+ new_asr2->end = right->end;
+ left = new_asr2;
+ insert_range (before, left);
+ new_asr2 = NULL;
+ }
+ right->start = caller->end + 1;
+ }
+ if (left)
+ {
+ left->end = caller->start - 1;
+ }
+
+ out:
+ if (new_asr)
+ free(new_asr);
+ if (new_asr2)
+ free(new_asr2);
+}
+
+/* Free T and all subtrees. */
+
+static void
+free_search_tree (ADDR_RANGE_TREE *t)
+{
+ if (t != NULL)
+ {
+ free_search_tree (t->lower);
+ free_search_tree (t->higher);
+ free (t);
+ }
+}
+
+/* Subroutine of build_search_tree to recursively build a balanced tree.
+ ??? It's not an optimum tree though. */
+
+static ADDR_RANGE_TREE *
+build_tree_1 (ADDR_SUBRANGE **asrtab, unsigned int n)
+{
+ unsigned int mid = n / 2;
+ ADDR_RANGE_TREE *t;
+
+ if (n == 0)
+ return NULL;
+ t = (ADDR_RANGE_TREE *) xmalloc (sizeof (ADDR_RANGE_TREE));
+ t->start = asrtab[mid]->start;
+ t->end = asrtab[mid]->end;
+ if (mid != 0)
+ t->lower = build_tree_1 (asrtab, mid);
+ else
+ t->lower = NULL;
+ if (n > mid + 1)
+ t->higher = build_tree_1 (asrtab + mid + 1, n - mid - 1);
+ else
+ t->higher = NULL;
+ return t;
+}
+
+/* Build a search tree for address range AR. */
+
+static void
+build_search_tree (ADDR_RANGE *ar)
+{
+ /* ??? Simple version for now. */
+ ADDR_SUBRANGE *asr,**asrtab;
+ unsigned int i, n;
+
+ for (n = 0, asr = ar->ranges; asr != NULL; ++n, asr = asr->next)
+ continue;
+ asrtab = (ADDR_SUBRANGE **) xmalloc (n * sizeof (ADDR_SUBRANGE *));
+ for (i = 0, asr = ar->ranges; i < n; ++i, asr = asr->next)
+ asrtab[i] = asr;
+ ar->range_tree = build_tree_1 (asrtab, n);
+ free (asrtab);
+}
+
+void
+sim_addr_range_add (ADDR_RANGE *ar, address_word start, address_word end)
+{
+ frob_range (ar, start, end, 0);
+
+ /* Rebuild the search tree. */
+ /* ??? Instead of rebuilding it here it could be done in a module resume
+ handler, say by first checking for a `changed' flag, assuming of course
+ this would never be done while the simulation is running. */
+ free_search_tree (ar->range_tree);
+ build_search_tree (ar);
+}
+
+void
+sim_addr_range_delete (ADDR_RANGE *ar, address_word start, address_word end)
+{
+ frob_range (ar, start, end, 1);
+
+ /* Rebuild the search tree. */
+ /* ??? Instead of rebuilding it here it could be done in a module resume
+ handler, say by first checking for a `changed' flag, assuming of course
+ this would never be done while the simulation is running. */
+ free_search_tree (ar->range_tree);
+ build_search_tree (ar);
+}
+
+#endif /* DEFINE_NON_INLINE_P */
+
+#if DEFINE_INLINE_P
+
+SIM_ARANGE_INLINE int
+sim_addr_range_hit_p (ADDR_RANGE *ar, address_word addr)
+{
+ ADDR_RANGE_TREE *t = ar->range_tree;
+
+ while (t != NULL)
+ {
+ if (addr < t->start)
+ t = t->lower;
+ else if (addr > t->end)
+ t = t->higher;
+ else
+ return 1;
+ }
+ return 0;
+}
+
+#endif /* DEFINE_INLINE_P */