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