40 ''' This module contains the implementation of the abstract ArrayMixin. '''
57 ScalarType, ArrayType, UnresolvedType, UnsupportedType, INTEGER_TYPE)
62 Abstract class used to add functionality common to Nodes that represent
67 def _validate_child(position, child):
69 :param int position: the position to be validated.
70 :param child: a child to be validated.
71 :type child: :py:class:`psyclone.psyir.nodes.Node`
73 :return: whether the given child and position are valid for this node.
78 return isinstance(child, (DataNode, Range))
82 ''':returns: if this instance indicates an array access.
90 Constructs the Signature of this array access and a list of the
93 :returns: the Signature of this array reference, and \
94 a list of the indices used for each component (empty list \
95 if an access is not an array). In this base class there is \
96 no other component, so it just returns a list with a list \
98 :rtype: tuple(:py:class:`psyclone.core.Signature`, list of \
102 return (sig, [self.
indicesindices[:]])
104 def _validate_index(self, index):
105 '''Utility function that checks that the supplied index is an integer
106 and is less than the number of array dimensions.
108 :param int index: the array index to check.
110 :raises TypeError: if the index argument is not an integer.
111 :raises ValueError: if the index value is greater than the \
112 number of dimensions in the array (-1).
115 if not isinstance(index, int):
117 f
"The index argument should be an integer but found "
118 f
"'{type(index).__name__}'.")
119 if index > len(self.
indicesindices)-1:
121 f
"In '{type(self).__name__}' '{self.name}' the specified "
122 f
"index '{index}' must be less than the number of dimensions "
123 f
"'{len(self.indices)}'.")
125 def _is_bound_op(self, expr, bound_operator, index):
126 '''Utility function that checks that the provided 'expr' argument is
127 in the form '[UL]BOUND(array_name, index)', where the type of
128 bound operation is determined by the 'bound_operator'
129 argument, array_name is the name of this array and the 'index'
130 argument provides the index value.
132 :param expr: a PSyIR expression.
133 :type expr: :py:class:`psyclone.psyir.nodes.Node`
134 :param bound_operator: the particular bound operation.
135 :type bound_operator:
136 :py:class:`psyclone.psyir.nodes.IntrinsicCall.Intrinsic.LBOUND` |
137 :py:class:`psyclone.psyir.nodes.IntrinsicCall.Intrinsic.UBOUND`
138 :param int index: the bounds index.
140 :returns: True if the expr is in the expected form and False otherwise.
144 if (isinstance(expr, IntrinsicCall)
and
145 expr.intrinsic == bound_operator):
149 if (isinstance(expr.arguments[1], Literal)
and
150 expr.arguments[1].datatype.intrinsic ==
151 ScalarType.Intrinsic.INTEGER
152 and expr.arguments[1].value == str(index+1)):
158 '''Returns whether this array access includes the lower bound of the
159 array for the specified index. Returns True if it is and False
160 if it is not or if it could not be determined.
162 :param int index: the array index to check.
164 :returns: True if it can be determined that the lower bound of \
165 the array is accessed in this array reference for the \
170 return self.
_is_bound_is_bound(index,
"lower")
172 def _get_bound_expression(self, pos, bound):
174 Lookup the upper or lower bound of this ArrayMixin.
176 :param int pos: the dimension of the array for which to lookup the
178 :param str bound: "upper" or "lower" - the bound which to lookup.
180 :returns: the declared bound for the specified dimension of this array
181 or a call to the {U/L}BOUND intrinsic if it is unknown.
182 :rtype: :py:class:`psyclone.psyir.nodes.Node`
184 :raises InternalError: if bound is neither upper or lower.
186 if bound
not in (
"upper",
"lower"):
187 raise InternalError(f
"'bound' argument must be 'lower' or 'upper. "
192 root_ref = self.ancestor(Reference, include_self=
True)
193 cursor_type = root_ref.symbol.datatype
201 while cursor
is not self:
202 cursor = cursor.member
204 if isinstance(cursor, ArrayMixin):
205 new_indices = [idx.copy()
for idx
in cursor.indices]
206 cnames.append((cursor.name.lower(), new_indices))
208 cnames.append(cursor.name.lower())
211 if isinstance(cursor_type, ArrayType):
212 cursor_type = cursor_type.intrinsic
213 if isinstance(cursor_type, DataTypeSymbol):
214 cursor_type = cursor_type.datatype
215 if isinstance(cursor_type, (UnsupportedType, UnresolvedType)):
217 cursor_type = cursor_type.components[cursor.name.lower()].datatype
219 if (isinstance(cursor_type, ArrayType)
and
220 cursor_type.shape[pos]
not in [ArrayType.Extent.DEFERRED,
221 ArrayType.Extent.ATTRIBUTE]):
224 return cursor_type.shape[pos].lower.copy()
225 return cursor_type.shape[pos].upper.copy()
232 if len(cnames[-1]) == 2:
233 cnames[-1] = cnames[-1][0]
238 if isinstance(root_ref, ArrayMixin):
239 new_indices = [idx.copy()
for idx
in root_ref.indices]
240 ref = ArrayOfStructuresReference.create(
241 root_ref.symbol, new_indices, cnames)
243 ref = StructureReference.create(root_ref.symbol, cnames)
249 return IntrinsicCall.create(
250 IntrinsicCall.Intrinsic.LBOUND,
251 [ref, (
"dim",
Literal(str(pos+1), INTEGER_TYPE))])
252 return IntrinsicCall.create(
253 IntrinsicCall.Intrinsic.UBOUND,
254 [ref, (
"dim",
Literal(str(pos+1), INTEGER_TYPE))])
258 Lookup the lower bound of this ArrayMixin. If we don't have the
259 necessary type information then a call to the LBOUND intrinsic is
260 constructed and returned.
262 :param int pos: the dimension of the array for which to lookup the
265 :returns: the declared lower bound for the specified dimension of
266 the array accesed or a call to the LBOUND intrinsic if it is
268 :rtype: :py:class:`psyclone.psyir.nodes.Node`
277 Lookup the upper bound of this ArrayMixin. If we don't have the
278 necessary type information then a call to the UBOUND intrinsic is
279 constructed and returned.
281 :param int pos: the dimension of the array for which to lookup the
284 :returns: the declared upper bound for the specified dimension of
285 the array accesed or a call to the UBOUND intrinsic if it is
287 :rtype: :py:class:`psyclone.psyir.nodes.Node`
296 Returns a Range object that covers the full indexing of the dimension
297 specified by pos for this ArrayMixin object.
299 :param int pos: the dimension of the array for which to lookup the
302 :returns: A Range representing the full range for the dimension of
303 pos for this ArrayMixin.
304 :rtype: :py:class:`psyclone.psyir.nodes.Range`
311 return Range.create(lbound, ubound)
314 '''Returns whether this array access includes the upper bound of
315 the array for the specified index. Returns True if it is and False
316 if it is not or if it could not be determined.
318 :param int index: the array index to check.
320 :returns: True if it can be determined that the upper bound of \
321 the array is accessed in this array reference for the \
326 return self.
_is_bound_is_bound(index,
"upper")
328 def _is_bound(self, index, bound_type):
329 '''Attempts to determines whether this array access includes the lower
330 or upper bound (as specified by the bound_type argument).
332 Checks whether the specified array index contains a Range node
333 which has a starting/stopping value given by the
334 '{LU}BOUND(name,index)' intrinsic where 'name' is the name of
335 the current ArrayReference and 'index' matches the specified
336 array index. Also checks if the starting/stopping value of the
337 access matches the lower/upper value of the declaration.
339 For example, if a Fortran array A was declared as A(n) then
340 the stopping value is n and A(:UBOUND(A,1)), A(:n) or A(n)
341 would access that value. The starting value is 1 and
342 A(LBOUND(A,1):), A(1:) or A(1) would access that value.
344 :param int index: the array index to check.
345 :param str bound_type: the type of bound ("lower" or "upper")
347 :returns: True if the array index access includes the \
348 lower/upper bound of the array declaration and False if it \
349 does not or if it can't be determined.
355 access_shape = self.
indicesindices[index]
359 if isinstance(access_shape, Range):
360 if bound_type ==
"upper":
361 intrinsic = IntrinsicCall.Intrinsic.UBOUND
362 access_bound = access_shape.stop
364 intrinsic = IntrinsicCall.Intrinsic.LBOUND
365 access_bound = access_shape.start
368 if self.
_is_bound_op_is_bound_op(access_bound, intrinsic, index):
371 access_bound = access_shape
381 if not isinstance(self, ArrayReference):
386 if not isinstance(symbol, DataSymbol):
390 datatype = symbol.datatype
392 if not isinstance(datatype, ArrayType):
406 if bound_type ==
"upper":
407 declaration_bound = datatype.shape[index].upper
409 declaration_bound = datatype.shape[index].lower
412 sym_maths = SymbolicMaths.get()
413 return sym_maths.equal(declaration_bound, access_bound)
417 Checks that the provided array is the same as this node (including the
418 chain of parent accessor expressions if the array is part of a
419 Structure). If the array is part of a structure then any indices on
420 the innermost member access are ignored, e.g.
421 A(3)%B%C(1) will match with A(3)%B%C but not with A(2)%B%C(1)
423 :param node: the node representing the access that is to be compared \
425 :type node: :py:class:`psyclone.psyir.nodes.Reference` or \
426 :py:class:`psyclone.psyir.nodes.Member`
428 :returns: True if the array accesses match, False otherwise.
432 if not isinstance(node, (Member, Reference)):
435 if isinstance(self, Member):
442 while current.parent
and not isinstance(current.parent, Reference):
444 current = current.parent
445 parent_ref = current.parent
454 self_sig, self_indices = parent_ref.get_signature_and_indices()
455 node_sig, node_indices = node.get_signature_and_indices()
456 if self_sig[:depth+1] != node_sig[:]:
461 for idx1, idx2
in zip(self_indices[:depth], node_indices[:depth]):
467 '''Returns True if the specified array index is a Range Node that
468 specifies all elements in this index. In the PSyIR this is
469 specified by using LBOUND(name,index) for the lower bound of
470 the range, UBOUND(name,index) for the upper bound of the range
471 and "1" for the range step.
473 :param int index: the array index to check.
475 :returns: True if the access to this array index is a range \
476 that specifies all index elements. Otherwise returns \
483 array_dimension = self.
indicesindices[index]
484 if isinstance(array_dimension, Range):
486 step = array_dimension.children[2]
487 if (isinstance(step, Literal)
and
488 step.datatype.intrinsic == ScalarType.Intrinsic.INTEGER
489 and str(step.value) ==
"1"):
496 Supports semantic-navigation by returning the list of nodes
497 representing the index expressions for this array reference.
499 :returns: the PSyIR nodes representing the array-index expressions.
500 :rtype: list of :py:class:`psyclone.psyir.nodes.Node`
502 :raises InternalError: if this node has no children or if they are \
503 not valid array-index expressions.
506 if not self._children:
508 f
"{type(self).__name__} malformed or incomplete: must have "
509 f
"one or more children representing array-index expressions "
510 f
"but array '{self.name}' has none.")
511 for idx, child
in enumerate(self._children):
514 f
"{type(self).__name__} malformed or incomplete: child "
515 f
"{idx} of array '{self.name}' must be a psyir.nodes."
516 f
"DataNode or Range representing an array-index "
517 f
"expression but found '{type(child).__name__}'")
520 def _extent(self, idx):
522 Create PSyIR for the number of elements in dimension `idx` of this
523 array access. It is given by (stop - start)/step + 1 or, if it is for
524 the full range, by the SIZE intrinsic.
526 :param int idx: the array index for which to compute the number of
529 :returns: the PSyIR expression for the number of elements in the
530 specified array index.
531 :rtype: :py:class:`psyclone.psyir.nodes.BinaryOperation` |
532 :py:class:`psyclone.psyir.nodes.IntrinsicCall`
534 expr = self.
indicesindices[idx]
535 one =
Literal(
"1", INTEGER_TYPE)
537 if isinstance(expr, Range):
545 if (isinstance(start, IntrinsicCall)
and
546 isinstance(stop, IntrinsicCall)
and self.
is_full_rangeis_full_range(idx)):
549 return IntrinsicCall.create(
550 IntrinsicCall.Intrinsic.SIZE,
551 [start.arguments[0].copy(),
552 (
"dim",
Literal(str(idx+1), INTEGER_TYPE))])
554 if start == one
and step == one:
559 extent = BinaryOperation.create(BinaryOperation.Operator.SUB,
560 stop.copy(), start.copy())
563 result = BinaryOperation.create(BinaryOperation.Operator.DIV,
569 return BinaryOperation.create(BinaryOperation.Operator.ADD,
572 def _get_effective_shape(self):
574 :returns: the shape of the array access represented by this node.
575 :rtype: list[:py:class:`psyclone.psyir.nodes.DataNode`]
577 :raises NotImplementedError: if any of the array-indices involve a
578 function call or an expression.
581 for idx, idx_expr
in enumerate(self.
indicesindices):
582 if isinstance(idx_expr, Range):
583 shape.append(self.
_extent_extent(idx))
585 elif isinstance(idx_expr, Reference):
586 dtype = idx_expr.datatype
587 if isinstance(dtype, ArrayType):
590 indirect_array_shape = dtype.shape
591 if len(indirect_array_shape) > 1:
593 f
"An array defining a slice of a dimension of "
594 f
"another array must be 1D but '{idx_expr.name}' "
595 f
"used to index into '{self.name}' has "
596 f
"{len(indirect_array_shape)} dimensions.")
598 shape.append(idx_expr._extent(idx))
600 elif isinstance(idx_expr, (Call, Operation, CodeBlock)):
603 raise NotImplementedError(
604 f
"The array index expressions for access "
605 f
"'{self.debug_string()}' include a function call or "
606 f
"expression. Querying the return type of "
607 f
"such things is yet to be implemented.")
612 ''' Return the index of the child that represents the outermost
613 array dimension with a Range construct.
615 :returns: the outermost index of the children that is a Range node.
618 :raises IndexError: if the array does not contain a Range node.
621 for child
in reversed(self.
indicesindices):
622 if isinstance(child, Range):
623 return child.position
626 def same_range(self, index: int, array2, index2: int) -> bool:
627 ''' This method compares the range of this array node at a given index
628 with the range of a second array at a second index. This is useful to
629 verify if array operations are valid, e.g.: A(3,:,5) + B(:,2,2).
631 Note that this check supports symbolic comparisons, e.g.:
632 A(3:4) has the same range as B(2+1:5-1),
633 and will consider compile-time unknown dimensions as equal, e.g.:
634 A(:) has the same range as B(:).
636 TODO #2485. This method has false negatives: cases when the range
637 is the same but it can not be proved, so we return False.
639 TODO #2004. This method currently compares exact ranges, not just the
640 length of them, which could be done with "(upper-lower)/step" symbolic
641 comparisons. This is because arrayrange2loop does not account for
642 arrays declared with different lbounds, but this could be improved.
644 :param index: the index indicating the location of a range node in
646 :param array2: the array accessor that we want to compare it to.
647 :param index2: the index indicating the location of a range node in
650 :returns: True if the ranges are the same and False if they are not
651 the same, or if it is not possible to determine.
653 :raises: TypeError if any of the arguments are of the wrong type.
657 if not isinstance(index, int):
659 f
"The 'index' argument of the same_range() method should be an"
660 f
" int but found '{type(index).__name__}'.")
661 if not isinstance(array2, ArrayMixin):
663 f
"The 'array2' argument of the same_range() method should be "
664 f
"an ArrayMixin but found '{type(array2).__name__}'.")
665 if not isinstance(index2, int):
667 f
"The 'index2' argument of the same_range() method should be "
668 f
"an int but found '{type(index2).__name__}'.")
669 if not index < len(self.children):
671 f
"The value of the 'index' argument of the same_range() method"
672 f
" is '{index}', but it should be less than the number of "
673 f
"dimensions in the associated array, which is "
674 f
"'{len(self.children)}'.")
675 if not index2 < len(array2.children):
677 f
"The value of the 'index2' argument of the same_range() "
678 f
"method is '{index2}', but it should be less than the number"
679 f
" of dimensions in the associated array 'array2', which is "
680 f
"'{len(array2.children)}'.")
681 if not isinstance(self.children[index], Range):
683 f
"The child of the first array argument at the specified index"
684 f
" '{index}' should be a Range node, but found "
685 f
"'{type(self.children[index]).__name__}'.")
686 if not isinstance(array2.children[index2], Range):
688 f
"The child of the second array argument at the specified "
689 f
"index '{index2}' should be a Range node, but found "
690 f
"'{type(array2.children[index2]).__name__}'.")
692 range1 = self.children[index]
693 range2 = array2.children[index2]
695 sym_maths = SymbolicMaths.get()
697 if self.
is_lower_boundis_lower_bound(index)
and array2.is_lower_bound(index2):
705 elif self.
is_lower_boundis_lower_bound(index)
or array2.is_lower_bound(index2):
711 elif not sym_maths.equal(range1.start, range2.start):
719 if self.
is_upper_boundis_upper_bound(index)
and array2.is_upper_bound(index2):
727 elif self.
is_upper_boundis_upper_bound(index)
or array2.is_upper_bound(index2):
733 elif not sym_maths.equal(range1.stop, range2.stop):
740 if not sym_maths.equal(range1.step, range2.step):
748 __all__ = [
'ArrayMixin']
def is_same_array(self, node)
def _get_bound_expression(self, pos, bound)
def get_lbound_expression(self, pos)
def get_outer_range_index(self)
bool same_range(self, int index, array2, int index2)
def _is_bound_op(self, expr, bound_operator, index)
def get_signature_and_indices(self)
def is_lower_bound(self, index)
def is_upper_bound(self, index)
def _validate_index(self, index)
def get_ubound_expression(self, pos)
def _validate_child(position, child)
def get_full_range(self, pos)
def is_full_range(self, index)
def _is_bound(self, index, bound_type)