1/* Copyright (C) 2020-2023 Free Software Foundation, Inc. 2 3 This file is part of GDB. 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 3 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 17 18/* Support classes to wrap up the process of iterating over a 19 multi-dimensional Fortran array. */ 20 21#ifndef F_ARRAY_WALKER_H 22#define F_ARRAY_WALKER_H 23 24#include "defs.h" 25#include "gdbtypes.h" 26#include "f-lang.h" 27 28/* Class for calculating the byte offset for elements within a single 29 dimension of a Fortran array. */ 30class fortran_array_offset_calculator 31{ 32public: 33 /* Create a new offset calculator for TYPE, which is either an array or a 34 string. */ 35 explicit fortran_array_offset_calculator (struct type *type) 36 { 37 /* Validate the type. */ 38 type = check_typedef (type); 39 if (type->code () != TYPE_CODE_ARRAY 40 && (type->code () != TYPE_CODE_STRING)) 41 error (_("can only compute offsets for arrays and strings")); 42 43 /* Get the range, and extract the bounds. */ 44 struct type *range_type = type->index_type (); 45 if (!get_discrete_bounds (range_type, &m_lowerbound, &m_upperbound)) 46 error ("unable to read array bounds"); 47 48 /* Figure out the stride for this array. */ 49 struct type *elt_type = check_typedef (type->target_type ()); 50 m_stride = type->index_type ()->bounds ()->bit_stride (); 51 if (m_stride == 0) 52 m_stride = type_length_units (elt_type); 53 else 54 { 55 int unit_size 56 = gdbarch_addressable_memory_unit_size (elt_type->arch ()); 57 m_stride /= (unit_size * 8); 58 } 59 }; 60 61 /* Get the byte offset for element INDEX within the type we are working 62 on. There is no bounds checking done on INDEX. If the stride is 63 negative then we still assume that the base address (for the array 64 object) points to the element with the lowest memory address, we then 65 calculate an offset assuming that index 0 will be the element at the 66 highest address, index 1 the next highest, and so on. This is not 67 quite how Fortran works in reality; in reality the base address of 68 the object would point at the element with the highest address, and 69 we would index backwards from there in the "normal" way, however, 70 GDB's current value contents model doesn't support having the base 71 address be near to the end of the value contents, so we currently 72 adjust the base address of Fortran arrays with negative strides so 73 their base address points at the lowest memory address. This code 74 here is part of working around this weirdness. */ 75 LONGEST index_offset (LONGEST index) 76 { 77 LONGEST offset; 78 if (m_stride < 0) 79 offset = std::abs (m_stride) * (m_upperbound - index); 80 else 81 offset = std::abs (m_stride) * (index - m_lowerbound); 82 return offset; 83 } 84 85private: 86 87 /* The stride for the type we are working with. */ 88 LONGEST m_stride; 89 90 /* The upper bound for the type we are working with. */ 91 LONGEST m_upperbound; 92 93 /* The lower bound for the type we are working with. */ 94 LONGEST m_lowerbound; 95}; 96 97/* A base class used by fortran_array_walker. There's no virtual methods 98 here, sub-classes should just override the functions they want in order 99 to specialise the behaviour to their needs. The functionality 100 provided in these default implementations will visit every array 101 element, but do nothing for each element. */ 102 103struct fortran_array_walker_base_impl 104{ 105 /* Called when iterating between the lower and upper bounds of each 106 dimension of the array. Return true if GDB should continue iterating, 107 otherwise, return false. 108 109 SHOULD_CONTINUE indicates if GDB is going to stop anyway, and should 110 be taken into consideration when deciding what to return. If 111 SHOULD_CONTINUE is false then this function must also return false, 112 the function is still called though in case extra work needs to be 113 done as part of the stopping process. */ 114 bool continue_walking (bool should_continue) 115 { return should_continue; } 116 117 /* Called when GDB starts iterating over a dimension of the array. The 118 argument INDEX_TYPE is the type of the index used to address elements 119 in the dimension, NELTS holds the number of the elements there, and 120 INNER_P is true for the inner most dimension (the dimension containing 121 the actual elements of the array), and false for more outer dimensions. 122 For a concrete example of how this function is called see the comment 123 on process_element below. */ 124 void start_dimension (struct type *index_type, LONGEST nelts, bool inner_p) 125 { /* Nothing. */ } 126 127 /* Called when GDB finishes iterating over a dimension of the array. The 128 argument INNER_P is true for the inner most dimension (the dimension 129 containing the actual elements of the array), and false for more outer 130 dimensions. LAST_P is true for the last call at a particular 131 dimension. For a concrete example of how this function is called 132 see the comment on process_element below. */ 133 void finish_dimension (bool inner_p, bool last_p) 134 { /* Nothing. */ } 135 136 /* Called when processing dimensions of the array other than the 137 innermost one. WALK_1 is the walker to normally call, ELT_TYPE is 138 the type of the element being extracted, and ELT_OFF is the offset 139 of the element from the start of array being walked. INDEX is the 140 value of the index the current element is at in the upper dimension. 141 Finally LAST_P is true only when this is the last element that will 142 be processed in this dimension. */ 143 void process_dimension (gdb::function_view<void (struct type *, 144 int, bool)> walk_1, 145 struct type *elt_type, LONGEST elt_off, 146 LONGEST index, bool last_p) 147 { 148 walk_1 (elt_type, elt_off, last_p); 149 } 150 151 /* Called when processing the inner most dimension of the array, for 152 every element in the array. ELT_TYPE is the type of the element being 153 extracted, and ELT_OFF is the offset of the element from the start of 154 array being walked. INDEX is the value of the index the current 155 element is at in the upper dimension. Finally LAST_P is true only 156 when this is the last element that will be processed in this dimension. 157 158 Given this two dimensional array ((1, 2) (3, 4) (5, 6)), the calls to 159 start_dimension, process_element, and finish_dimension look like this: 160 161 start_dimension (INDEX_TYPE, 3, false); 162 start_dimension (INDEX_TYPE, 2, true); 163 process_element (TYPE, OFFSET, false); 164 process_element (TYPE, OFFSET, true); 165 finish_dimension (true, false); 166 start_dimension (INDEX_TYPE, 2, true); 167 process_element (TYPE, OFFSET, false); 168 process_element (TYPE, OFFSET, true); 169 finish_dimension (true, true); 170 start_dimension (INDEX_TYPE, 2, true); 171 process_element (TYPE, OFFSET, false); 172 process_element (TYPE, OFFSET, true); 173 finish_dimension (true, true); 174 finish_dimension (false, true); */ 175 void process_element (struct type *elt_type, LONGEST elt_off, 176 LONGEST index, bool last_p) 177 { /* Nothing. */ } 178}; 179 180/* A class to wrap up the process of iterating over a multi-dimensional 181 Fortran array. IMPL is used to specialise what happens as we walk over 182 the array. See class FORTRAN_ARRAY_WALKER_BASE_IMPL (above) for the 183 methods than can be used to customise the array walk. */ 184template<typename Impl> 185class fortran_array_walker 186{ 187 /* Ensure that Impl is derived from the required base class. This just 188 ensures that all of the required API methods are available and have a 189 sensible default implementation. */ 190 gdb_static_assert ((std::is_base_of<fortran_array_walker_base_impl,Impl>::value)); 191 192public: 193 /* Create a new array walker. TYPE is the type of the array being walked 194 over, and ADDRESS is the base address for the object of TYPE in 195 memory. All other arguments are forwarded to the constructor of the 196 template parameter class IMPL. */ 197 template <typename ...Args> 198 fortran_array_walker (struct type *type, CORE_ADDR address, 199 Args... args) 200 : m_type (type), 201 m_address (address), 202 m_impl (type, address, args...), 203 m_ndimensions (calc_f77_array_dims (m_type)), 204 m_nss (0) 205 { /* Nothing. */ } 206 207 /* Walk the array. */ 208 void 209 walk () 210 { 211 walk_1 (m_type, 0, false); 212 } 213 214private: 215 /* The core of the array walking algorithm. TYPE is the type of 216 the current dimension being processed and OFFSET is the offset 217 (in bytes) for the start of this dimension. */ 218 void 219 walk_1 (struct type *type, int offset, bool last_p) 220 { 221 /* Extract the range, and get lower and upper bounds. */ 222 struct type *range_type = check_typedef (type)->index_type (); 223 LONGEST lowerbound, upperbound; 224 if (!get_discrete_bounds (range_type, &lowerbound, &upperbound)) 225 error ("failed to get range bounds"); 226 227 /* CALC is used to calculate the offsets for each element in this 228 dimension. */ 229 fortran_array_offset_calculator calc (type); 230 231 m_nss++; 232 gdb_assert (range_type->code () == TYPE_CODE_RANGE); 233 m_impl.start_dimension (range_type->target_type (), 234 upperbound - lowerbound + 1, 235 m_nss == m_ndimensions); 236 237 if (m_nss != m_ndimensions) 238 { 239 struct type *subarray_type = check_typedef (type)->target_type (); 240 241 /* For dimensions other than the inner most, walk each element and 242 recurse while peeling off one more dimension of the array. */ 243 for (LONGEST i = lowerbound; 244 m_impl.continue_walking (i < upperbound + 1); 245 i++) 246 { 247 /* Use the index and the stride to work out a new offset. */ 248 LONGEST new_offset = offset + calc.index_offset (i); 249 250 /* Now print the lower dimension. */ 251 m_impl.process_dimension 252 ([this] (struct type *w_type, int w_offset, bool w_last_p) -> void 253 { 254 this->walk_1 (w_type, w_offset, w_last_p); 255 }, 256 subarray_type, new_offset, i, i == upperbound); 257 } 258 } 259 else 260 { 261 struct type *elt_type = check_typedef (type)->target_type (); 262 263 /* For the inner most dimension of the array, process each element 264 within this dimension. */ 265 for (LONGEST i = lowerbound; 266 m_impl.continue_walking (i < upperbound + 1); 267 i++) 268 { 269 LONGEST elt_off = offset + calc.index_offset (i); 270 271 if (is_dynamic_type (elt_type)) 272 { 273 CORE_ADDR e_address = m_address + elt_off; 274 elt_type = resolve_dynamic_type (elt_type, {}, e_address); 275 } 276 277 m_impl.process_element (elt_type, elt_off, i, i == upperbound); 278 } 279 } 280 281 m_impl.finish_dimension (m_nss == m_ndimensions, last_p || m_nss == 1); 282 m_nss--; 283 } 284 285 /* The array type being processed. */ 286 struct type *m_type; 287 288 /* The address in target memory for the object of M_TYPE being 289 processed. This is required in order to resolve dynamic types. */ 290 CORE_ADDR m_address; 291 292 /* An instance of the template specialisation class. */ 293 Impl m_impl; 294 295 /* The total number of dimensions in M_TYPE. */ 296 int m_ndimensions; 297 298 /* The current dimension number being processed. */ 299 int m_nss; 300}; 301 302#endif /* F_ARRAY_WALKER_H */ 303