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