123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- /* Copyright (C) 2020-2022 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 "defs.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 (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. */
- gdb_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 (TYPE_TARGET_TYPE (range_type),
- upperbound - lowerbound + 1,
- m_nss == m_ndimensions);
- if (m_nss != m_ndimensions)
- {
- struct type *subarray_type = TYPE_TARGET_TYPE (check_typedef (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 (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 */
|