| /* Copyright (C) 2020-2024 Free Software Foundation, Inc. | |
| This file is part of GDB. | |
| 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. */ | |
| /* Support classes to wrap up the process of iterating over a | |
| multi-dimensional Fortran array. */ | |
| #ifndef F_ARRAY_WALKER_H | |
| #define F_ARRAY_WALKER_H | |
| #include "gdbtypes.h" | |
| #include "f-lang.h" | |
| /* Class for calculating the byte offset for elements within a single | |
| dimension of a Fortran array. */ | |
| class fortran_array_offset_calculator | |
| { | |
| public: | |
| /* Create a new offset calculator for TYPE, which is either an array or a | |
| string. */ | |
| explicit fortran_array_offset_calculator (struct type *type) | |
| { | |
| /* Validate the type. */ | |
| type = check_typedef (type); | |
| if (type->code () != TYPE_CODE_ARRAY | |
| && (type->code () != TYPE_CODE_STRING)) | |
| error (_("can only compute offsets for arrays and strings")); | |
| /* Get the range, and extract the bounds. */ | |
| struct type *range_type = type->index_type (); | |
| if (!get_discrete_bounds (range_type, &m_lowerbound, &m_upperbound)) | |
| error ("unable to read array bounds"); | |
| /* Figure out the stride for this array. */ | |
| struct type *elt_type = check_typedef (type->target_type ()); | |
| m_stride = type->index_type ()->bounds ()->bit_stride (); | |
| if (m_stride == 0) | |
| m_stride = type_length_units (elt_type); | |
| else | |
| { | |
| int unit_size | |
| = gdbarch_addressable_memory_unit_size (elt_type->arch ()); | |
| m_stride /= (unit_size * 8); | |
| } | |
| }; | |
| /* Get the byte offset for element INDEX within the type we are working | |
| on. There is no bounds checking done on INDEX. If the stride is | |
| negative then we still assume that the base address (for the array | |
| object) points to the element with the lowest memory address, we then | |
| calculate an offset assuming that index 0 will be the element at the | |
| highest address, index 1 the next highest, and so on. This is not | |
| quite how Fortran works in reality; in reality the base address of | |
| the object would point at the element with the highest address, and | |
| we would index backwards from there in the "normal" way, however, | |
| GDB's current value contents model doesn't support having the base | |
| address be near to the end of the value contents, so we currently | |
| adjust the base address of Fortran arrays with negative strides so | |
| their base address points at the lowest memory address. This code | |
| here is part of working around this weirdness. */ | |
| LONGEST index_offset (LONGEST index) | |
| { | |
| LONGEST offset; | |
| if (m_stride < 0) | |
| offset = std::abs (m_stride) * (m_upperbound - index); | |
| else | |
| offset = std::abs (m_stride) * (index - m_lowerbound); | |
| return offset; | |
| } | |
| private: | |
| /* The stride for the type we are working with. */ | |
| LONGEST m_stride; | |
| /* The upper bound for the type we are working with. */ | |
| LONGEST m_upperbound; | |
| /* The lower bound for the type we are working with. */ | |
| LONGEST m_lowerbound; | |
| }; | |
| /* A base class used by fortran_array_walker. There's no virtual methods | |
| here, sub-classes should just override the functions they want in order | |
| to specialise the behaviour to their needs. The functionality | |
| provided in these default implementations will visit every array | |
| element, but do nothing for each element. */ | |
| struct fortran_array_walker_base_impl | |
| { | |
| /* Called when iterating between the lower and upper bounds of each | |
| dimension of the array. Return true if GDB should continue iterating, | |
| otherwise, return false. | |
| SHOULD_CONTINUE indicates if GDB is going to stop anyway, and should | |
| be taken into consideration when deciding what to return. If | |
| SHOULD_CONTINUE is false then this function must also return false, | |
| the function is still called though in case extra work needs to be | |
| done as part of the stopping process. */ | |
| bool continue_walking (bool should_continue) | |
| { return should_continue; } | |
| /* Called when GDB starts iterating over a dimension of the array. The | |
| argument INDEX_TYPE is the type of the index used to address elements | |
| in the dimension, NELTS holds the number of the elements there, and | |
| INNER_P is true for the inner most dimension (the dimension containing | |
| the actual elements of the array), and false for more outer dimensions. | |
| For a concrete example of how this function is called see the comment | |
| on process_element below. */ | |
| void start_dimension (struct type *index_type, LONGEST nelts, bool inner_p) | |
| { /* Nothing. */ } | |
| /* Called when GDB finishes iterating over a dimension of the array. The | |
| argument INNER_P is true for the inner most dimension (the dimension | |
| containing the actual elements of the array), and false for more outer | |
| dimensions. LAST_P is true for the last call at a particular | |
| dimension. For a concrete example of how this function is called | |
| see the comment on process_element below. */ | |
| void finish_dimension (bool inner_p, bool last_p) | |
| { /* Nothing. */ } | |
| /* Called when processing dimensions of the array other than the | |
| innermost one. WALK_1 is the walker to normally call, ELT_TYPE is | |
| the type of the element being extracted, and ELT_OFF is the offset | |
| of the element from the start of array being walked. INDEX is the | |
| value of the index the current element is at in the upper dimension. | |
| Finally LAST_P is true only when this is the last element that will | |
| be processed in this dimension. */ | |
| void process_dimension (gdb::function_view<void (struct type *, | |
| int, bool)> walk_1, | |
| struct type *elt_type, LONGEST elt_off, | |
| LONGEST index, bool last_p) | |
| { | |
| walk_1 (elt_type, elt_off, last_p); | |
| } | |
| /* Called when processing the inner most dimension of the array, for | |
| every element in the array. ELT_TYPE is the type of the element being | |
| extracted, and ELT_OFF is the offset of the element from the start of | |
| array being walked. INDEX is the value of the index the current | |
| element is at in the upper dimension. Finally LAST_P is true only | |
| when this is the last element that will be processed in this dimension. | |
| Given this two dimensional array ((1, 2) (3, 4) (5, 6)), the calls to | |
| start_dimension, process_element, and finish_dimension look like this: | |
| start_dimension (INDEX_TYPE, 3, false); | |
| start_dimension (INDEX_TYPE, 2, true); | |
| process_element (TYPE, OFFSET, false); | |
| process_element (TYPE, OFFSET, true); | |
| finish_dimension (true, false); | |
| start_dimension (INDEX_TYPE, 2, true); | |
| process_element (TYPE, OFFSET, false); | |
| process_element (TYPE, OFFSET, true); | |
| finish_dimension (true, true); | |
| start_dimension (INDEX_TYPE, 2, true); | |
| process_element (TYPE, OFFSET, false); | |
| process_element (TYPE, OFFSET, true); | |
| finish_dimension (true, true); | |
| finish_dimension (false, true); */ | |
| void process_element (struct type *elt_type, LONGEST elt_off, | |
| LONGEST index, bool last_p) | |
| { /* Nothing. */ } | |
| }; | |
| /* A class to wrap up the process of iterating over a multi-dimensional | |
| Fortran array. IMPL is used to specialise what happens as we walk over | |
| the array. See class FORTRAN_ARRAY_WALKER_BASE_IMPL (above) for the | |
| methods than can be used to customise the array walk. */ | |
| template<typename Impl> | |
| class fortran_array_walker | |
| { | |
| /* Ensure that Impl is derived from the required base class. This just | |
| ensures that all of the required API methods are available and have a | |
| sensible default implementation. */ | |
| static_assert ((std::is_base_of<fortran_array_walker_base_impl,Impl>::value)); | |
| public: | |
| /* Create a new array walker. TYPE is the type of the array being walked | |
| over, and ADDRESS is the base address for the object of TYPE in | |
| memory. All other arguments are forwarded to the constructor of the | |
| template parameter class IMPL. */ | |
| template <typename ...Args> | |
| fortran_array_walker (struct type *type, CORE_ADDR address, | |
| Args... args) | |
| : m_type (type), | |
| m_address (address), | |
| m_impl (type, address, args...), | |
| m_ndimensions (calc_f77_array_dims (m_type)), | |
| m_nss (0) | |
| { /* Nothing. */ } | |
| /* Walk the array. */ | |
| void | |
| walk () | |
| { | |
| walk_1 (m_type, 0, false); | |
| } | |
| private: | |
| /* The core of the array walking algorithm. TYPE is the type of | |
| the current dimension being processed and OFFSET is the offset | |
| (in bytes) for the start of this dimension. */ | |
| void | |
| walk_1 (struct type *type, int offset, bool last_p) | |
| { | |
| /* Extract the range, and get lower and upper bounds. */ | |
| struct type *range_type = check_typedef (type)->index_type (); | |
| LONGEST lowerbound, upperbound; | |
| if (!get_discrete_bounds (range_type, &lowerbound, &upperbound)) | |
| error ("failed to get range bounds"); | |
| /* CALC is used to calculate the offsets for each element in this | |
| dimension. */ | |
| fortran_array_offset_calculator calc (type); | |
| m_nss++; | |
| gdb_assert (range_type->code () == TYPE_CODE_RANGE); | |
| m_impl.start_dimension (range_type->target_type (), | |
| upperbound - lowerbound + 1, | |
| m_nss == m_ndimensions); | |
| if (m_nss != m_ndimensions) | |
| { | |
| struct type *subarray_type = check_typedef (type)->target_type (); | |
| /* For dimensions other than the inner most, walk each element and | |
| recurse while peeling off one more dimension of the array. */ | |
| for (LONGEST i = lowerbound; | |
| m_impl.continue_walking (i < upperbound + 1); | |
| i++) | |
| { | |
| /* Use the index and the stride to work out a new offset. */ | |
| LONGEST new_offset = offset + calc.index_offset (i); | |
| /* Now print the lower dimension. */ | |
| m_impl.process_dimension | |
| ([this] (struct type *w_type, int w_offset, bool w_last_p) -> void | |
| { | |
| this->walk_1 (w_type, w_offset, w_last_p); | |
| }, | |
| subarray_type, new_offset, i, i == upperbound); | |
| } | |
| } | |
| else | |
| { | |
| struct type *elt_type = check_typedef (type)->target_type (); | |
| /* For the inner most dimension of the array, process each element | |
| within this dimension. */ | |
| for (LONGEST i = lowerbound; | |
| m_impl.continue_walking (i < upperbound + 1); | |
| i++) | |
| { | |
| LONGEST elt_off = offset + calc.index_offset (i); | |
| if (is_dynamic_type (elt_type)) | |
| { | |
| CORE_ADDR e_address = m_address + elt_off; | |
| elt_type = resolve_dynamic_type (elt_type, {}, e_address); | |
| } | |
| m_impl.process_element (elt_type, elt_off, i, i == upperbound); | |
| } | |
| } | |
| m_impl.finish_dimension (m_nss == m_ndimensions, last_p || m_nss == 1); | |
| m_nss--; | |
| } | |
| /* The array type being processed. */ | |
| struct type *m_type; | |
| /* The address in target memory for the object of M_TYPE being | |
| processed. This is required in order to resolve dynamic types. */ | |
| CORE_ADDR m_address; | |
| /* An instance of the template specialisation class. */ | |
| Impl m_impl; | |
| /* The total number of dimensions in M_TYPE. */ | |
| int m_ndimensions; | |
| /* The current dimension number being processed. */ | |
| int m_nss; | |
| }; | |
| #endif /* F_ARRAY_WALKER_H */ |