36 """ This module contains the implementation of the Dynamic OpenMP Task
37 Directive node, which is used pre-lowering to represent Task Directives."""
41 from collections
import namedtuple
46 ArrayOfStructuresReference,
57 ArrayOfStructuresMember,
65 OMPFirstprivateClause,
80 Class representing an OpenMP TASK directive in the PSyIR.
82 :param list children: list of Nodes that are children of this Node.
83 :param parent: the Node in the AST that has this directive as a child
84 :type parent: :py:class:`psyclone.psyir.nodes.Node`
87 _children_valid_format = (
88 "Schedule, OMPPrivateClause,"
89 "OMPFirstprivateClause, OMPSharedClause"
90 "OMPDependClause, OMPDependClause"
98 _proxy_vars = namedtuple(
99 'ProxyVars', [
'parent_var',
'parent_node',
100 'loop',
'parent_loop']
107 _clause_lists = namedtuple(
108 'ClauseLists', [
'private_list',
'firstprivate_list',
109 'shared_list',
'in_list',
'out_list']
114 _allowed_intrinsics = [
115 IntrinsicCall.Intrinsic.ABS,
116 IntrinsicCall.Intrinsic.ACOS,
117 IntrinsicCall.Intrinsic.ACOSH,
118 IntrinsicCall.Intrinsic.ASIN,
119 IntrinsicCall.Intrinsic.ASINH,
120 IntrinsicCall.Intrinsic.ATAN,
121 IntrinsicCall.Intrinsic.ATAN2,
122 IntrinsicCall.Intrinsic.ATANH,
123 IntrinsicCall.Intrinsic.CEILING,
124 IntrinsicCall.Intrinsic.COS,
125 IntrinsicCall.Intrinsic.COSH,
126 IntrinsicCall.Intrinsic.ERF,
127 IntrinsicCall.Intrinsic.EXP,
128 IntrinsicCall.Intrinsic.FLOOR,
129 IntrinsicCall.Intrinsic.INT,
130 IntrinsicCall.Intrinsic.LBOUND,
131 IntrinsicCall.Intrinsic.LEN,
132 IntrinsicCall.Intrinsic.LOG,
133 IntrinsicCall.Intrinsic.LOG10,
134 IntrinsicCall.Intrinsic.MAX,
135 IntrinsicCall.Intrinsic.MIN,
136 IntrinsicCall.Intrinsic.MODULO,
137 IntrinsicCall.Intrinsic.REAL,
138 IntrinsicCall.Intrinsic.SIGN,
139 IntrinsicCall.Intrinsic.SIN,
140 IntrinsicCall.Intrinsic.SINH,
141 IntrinsicCall.Intrinsic.SIZE,
142 IntrinsicCall.Intrinsic.SQRT,
143 IntrinsicCall.Intrinsic.TAN,
144 IntrinsicCall.Intrinsic.TANH,
145 IntrinsicCall.Intrinsic.UBOUND,
148 def __init__(self, children=None, parent=None):
150 children=children, parent=parent
177 def _array_for_clause_combination_helper(self, ref, temp_list,
180 Helper function for creating an ArrayReference or StructureReference
181 containing an ArrayMember to place into the
182 relevant in or out dependency list when computing index combinations.
184 :param ref: The reference whose symbol the created ArrayReference is
186 :type ref: :py:class:`psyclone.psyir.nodes.Reference`
187 :param temp_list: The current list of indices to add to the created
189 :type temp_list: List[:py:class:`psyclone.psyir.nodes.Node`]
190 :param base_member: Optional argument containing the array member
191 child of ref to duplicate with
192 the indices specified by temp_list
193 :type base_member: Optional[
194 :py:class:`psyclone.psyir.nodes.ArrayMember`]
196 :returns: an ArrayReference to the provided symbol and with the
197 provided indices, or a StructureReference containing the
198 new ArrayMember if base_member is provided.
199 :rtype: :py:class:`psyclone.psyir.nodes.ArrayReference` or
200 :py:class:`psyclone.psyir.nodes.StructureReference`
203 final_list = [element.copy()
for element
in temp_list]
205 final_member = ArrayMember.create(base_member.name, final_list)
206 sref_copy = ref.copy()
208 members = sref_copy.walk(Member)
213 dclause = ArrayReference.create(ref.symbol, final_list)
216 def _add_dependencies_from_index_list(self, index_list, dependency_list,
217 reference, array_access_member=None):
219 Computes all of the dependency combinations for an array access and
220 adds them to the provided dependency_list.
221 If array_access_member is None, then ArrayReferences will be added,
222 otherwise StructureReferences containing ArrayMembers will be added.
224 :param index_list: A list of (lists of) indices to turn into
226 :type index_list: List[Union[List[
227 :py:class:`psyclone.psyir.nodes.Reference]],
228 :py:class:`psyclone.psyir.nodes.Reference]
229 :param dependency_list: The dependency list to add the newly created
231 :type dependency_list: List[:py:class`psyclone.psyir.nodes.Reference]
232 :param reference: The reference containing the array access to create
234 :type reference: Union[:py:class:`psyclone.psyir.nodes.ArrayReference`,
235 :py:class:`psyclone.psyir.nodes.StructureReference`]
236 :param array_access_member: optional argument provided if reference
237 is a StructureReference to the ArrayMember
238 containing the dependencies.
245 new_index_list = [element
if isinstance(element, list)
else [element]
246 for element
in index_list]
248 combinations = itertools.product(*new_index_list)
249 for temp_list
in combinations:
251 reference, temp_list, array_access_member
254 if new_ref
not in dependency_list:
255 dependency_list.append(new_ref)
257 def _find_parent_loop_vars(self):
259 Finds the loop variable of each parent loop inside the same
260 OMPParallelDirective and stores them in the _parent_loop_vars member.
261 Also stores the parent OMPParallelDirective in _parent_parallel.
263 :raises GenerationError: if no ancestor OMPParallelDirective is
266 anc = self.
ancestorancestor((OMPParallelDirective, Loop))
267 while isinstance(anc, Loop):
273 anc = anc.ancestor((OMPParallelDirective, Loop))
275 if not isinstance(anc, OMPParallelDirective):
277 "OMPParallelDirective which is required "
278 "to compute dependencies of a "
279 "(Dynamic)OMPTaskDirective.")
284 anc.infer_sharing_attributes()
288 def _handle_proxy_loop_index(self, index_list, dim, index,
291 Handles the special case where an index is a proxy loop variable
292 to a parent of the node. In this case, we add a reference to the
293 parent's loop value to the index_list, and create a list of all
294 possible variants, as we might have multiple values set for the
295 reference, e.g. for a boundary condition if statement.
297 :param index_list: The list to contain the new indices.
298 :type index_list: List[:py:class:`psyclone.psyir.nodes.Reference`]
299 :param int dim: The dimension of the index in the containing
301 :param index: The index that is a proxy to a parent loop variable.
302 :type index: :py:class:`psyclone.psyir.nodes.Reference`
303 :param clause_lists: The namedtuple containing the lists storing the
305 :type clause_lists: namedtuple(
307 :py:class:`psyclone.psyir.nodes.Reference`
309 firstprivate_list=List[
310 :py:class:`psyclone.psyir.nodes.Reference`
313 :py:class:`psyclone.psyir.nodes.Reference`
316 :py:class:`psyclone.psyir.nodes.Reference`
319 :py:class:`psyclone.psyir.nodes.Reference`
324 while len(index_list) <= dim:
325 index_list.append([])
330 parent_ref = temp_ref.copy()
331 if isinstance(parent_ref, BinaryOperation):
338 for element
in quick_list:
339 if isinstance(element, list):
340 index_list[dim].extend(element)
342 index_list[dim].append(element)
346 index_list[dim].append(parent_ref)
348 def _is_reference_private(self, ref):
350 Determines whether the provided reference is private or shared in the
351 enclosing parallel region.
353 :param ref: The Reference object to be determined if it is private
355 :type ref: :py:class:`psyclone.psyir.nodes.Reference`
357 :returns: True if ref is private, else False.
361 if ref.symbol.name == parent_sym.name:
365 def _evaluate_readonly_baseref(
366 self, ref, clause_lists
369 Evaluates any read-only References to variables inside the OpenMP task
370 region and adds a copy of the Reference to the appropriate data-sharing
371 list used to construct the clauses for this task region.
373 The basic rules for this are:
374 1. If the Reference is private in the parallel region containing this
375 task, the Reference will be added to the list of firstprivate
376 References unless it has already been added to either the list of
377 private or firstprivate References for this task.
378 2. If the Reference is shared, then the Reference will be added to the
379 input list of References unless it is already present in that list.
381 :param ref: The reference to be evaluated.
382 :type ref: :py:class:`psyclone.psyir.nodes.Reference`
383 :param clause_lists: The namedtuple containing the lists storing the
385 :type clause_lists: namedtuple(
387 :py:class:`psyclone.psyir.nodes.Reference`
389 firstprivate_list=List[
390 :py:class:`psyclone.psyir.nodes.Reference`
393 :py:class:`psyclone.psyir.nodes.Reference`
396 :py:class:`psyclone.psyir.nodes.Reference`
399 :py:class:`psyclone.psyir.nodes.Reference`
409 if (ref
not in clause_lists.private_list
and
410 ref
not in clause_lists.firstprivate_list):
411 clause_lists.firstprivate_list.append(ref.copy())
418 if ref
not in clause_lists.in_list:
419 clause_lists.in_list.append(ref.copy())
421 def _create_binops_from_step_and_divisors(self, node, ref,
425 Takes a node, step_val, divisor, modulo and ref_index from
426 _handle_index_binop and computes the BinaryOperation(s)
427 required for them to be handled in a depend clause.
429 :param node: the BinaryOperation being evaluated.
430 :type node: :py:class:`psyclone.psyir.nodes.BinaryOperation`
431 :param ref: the Reference representing the loop variable inside node
432 :type ref: :py:class:`psyclone.psyir.nodes.Reference`
433 :param int step_val: the step of the loop containing the node in
435 :param int divisor: the ceiling result of literal_val / step, where
436 literal_val is the int value of the Literal child
437 of the node in _handle_index_binop.
438 :param int modulo: the result of literal_val % step in
440 :param int ref_index: The index of the Reference child of node from
443 :returns: the BinaryOperation(s) required to be added to the
444 index_list in _handle_index_binop to satisfy the
446 :rtype: Tuple[:py:class`psyclone.psyir.nodes.BinaryOperation`,
447 Union[:py:class`psyclone.psyir.nodes.BinaryOperation`,
460 step = BinaryOperation.create(
461 BinaryOperation.Operator.MUL,
462 Literal(f
"{divisor}", INTEGER_TYPE),
463 Literal(f
"{step_val}", INTEGER_TYPE),
466 step2 = BinaryOperation.create(
467 BinaryOperation.Operator.MUL,
468 Literal(f
"{divisor-1}", INTEGER_TYPE),
469 Literal(f
"{step_val}", INTEGER_TYPE),
472 step2 =
Literal(f
"{step_val}", INTEGER_TYPE)
474 step =
Literal(f
"{step_val}", INTEGER_TYPE)
484 first_arg = ref.copy()
485 alt_first_arg = first_arg.copy()
486 second_arg = step.copy()
487 alt_second_arg = step2.copy()
if step2
else None
493 first_arg = step.copy()
494 alt_first_arg = step2.copy()
if step2
else None
495 second_arg = ref.copy()
496 alt_second_arg = second_arg.copy()
499 binop = BinaryOperation.create(
500 node.operator, first_arg, second_arg
507 if step2
is not None:
508 binop2 = BinaryOperation.create(
509 node.operator, alt_first_arg, alt_second_arg
516 def _handle_index_binop(
517 self, node, index_list, clause_lists
520 Evaluates an expression consisting a binary operation which is used
521 to index into an array within this OpenMP task.
523 For each expression, the code checks that the expression matches the
524 expected format, which is [Reference] [ADD/SUB] [Literal] (or the
525 opposite ordering). PSyclone does not currently support other binary
526 operation indexing inside an OpenMP task.
528 Once this is confirmed, PSyclone builds the appropriate list of
529 References to correctly express the dependencies of this array access,
530 and appends them to the `index_list` input argument. This can depend on
531 the structure of the Loop inside the task, and any parent Loops.
533 The Reference inside the binary operation must be a private or
534 firstprivate variable inside the task region, else PSyclone does not
535 support using it as an array index.
537 :param node: The BinaryOperation to be evaluated.
538 :type node: :py:class:`psyclone.psyir.nodes.BinaryOperation`
539 :param index_list: A list of Nodes used to handle the dependencies
540 for this array access. This may be reused over
541 multiple calls to this function to avoid duplicating
543 :type index_list: List[:py:class:`psyclone.psyir.nodes.Node`]
544 :param clause_lists: The namedtuple containing the lists storing the
546 :type clause_lists: namedtuple(
548 :py:class:`psyclone.psyir.nodes.Reference`
550 firstprivate_list=List[
551 :py:class:`psyclone.psyir.nodes.Reference`
554 :py:class:`psyclone.psyir.nodes.Reference`
557 :py:class:`psyclone.psyir.nodes.Reference`
560 :py:class:`psyclone.psyir.nodes.Reference`
563 :raises GenerationError: if this BinaryOperation is not an addition or
565 :raises GenerationError: if this BinaryOperation does not contain both
566 a Reference and a Literal.
567 :raises GenerationError: if this BinaryOperation contains a Reference
568 to a shared variable.
573 node.operator
not in [BinaryOperation.Operator.ADD,
574 BinaryOperation.Operator.SUB]
577 f
"Binary Operator of type {node.operator} used "
578 f
"as an array index '{node.debug_string()}' inside an "
579 f
"OMPTaskDirective which is not "
586 isinstance(node.children[0], Reference)
587 and isinstance(node.children[1], Literal)
590 isinstance(node.children[0], Literal)
591 and isinstance(node.children[1], Reference)
595 f
"Children of BinaryOperation are of "
596 f
"types '{type(node.children[0]).__name__}' and "
597 f
"'{type(node.children[1]).__name__}', expected one "
598 f
"Reference and one Literal when"
599 f
" used as an array index inside an "
600 f
"OMPTaskDirective. The containing ArrayReference is "
601 f
"'{node.parent.debug_string()}'."
608 index_private =
False
617 if isinstance(node.children[0], Reference):
622 index_symbol = node.children[ref_index].symbol
625 ref = node.children[ref_index]
627 literal = node.children[1-ref_index]
646 parent_loop = self.
_proxy_loop_vars_proxy_loop_vars[index_symbol].parent_loop
649 real_ref = self.
_proxy_loop_vars_proxy_loop_vars[index_symbol].parent_node.copy()
650 for temp_ref
in self.
_proxy_loop_vars_proxy_loop_vars[index_symbol].parent_node:
651 real_ref = temp_ref.copy()
656 step_val = int(parent_loop.step_expr.value)
657 literal_val = int(literal.value)
658 divisor = math.ceil(literal_val / step_val)
659 modulo = literal_val % step_val
661 node, real_ref, step_val, divisor, modulo, ref_index
664 if binop2
is not None:
665 index_list.append([binop, binop2])
667 index_list.append(binop)
673 if ref
in clause_lists.private_list:
675 if ref.symbol
in child_loop_vars:
677 dim = len(index_list)
679 array_access_member = ref.ancestor(ArrayMember)
680 if array_access_member
is not None:
681 full_range = array_access_member.get_full_range(dim)
683 arrayref = ref.parent.parent
684 full_range = arrayref.get_full_range(dim)
685 index_list.append(full_range)
692 dim = len(index_list)
693 arrayref = ref.parent.parent
694 full_range = arrayref.get_full_range(dim)
695 index_list.append(full_range)
697 if ref
not in clause_lists.firstprivate_list:
698 clause_lists.firstprivate_list.append(ref.copy())
711 step_val = int(parent_loop.step_expr.value)
712 literal_val = int(literal.value)
713 divisor = math.ceil(literal_val / step_val)
714 modulo = literal_val % step_val
716 node, ref, step_val, divisor,
720 if binop2
is not None:
721 index_list.append([binop, binop2])
723 index_list.append(binop)
728 index_list.append(node.copy())
732 f
"Shared variable '{ref.debug_string()}' used "
733 f
"as an array index inside an "
734 f
"OMPTaskDirective which is not "
735 f
"supported. The full access is '{node.debug_string()}'."
738 def _evaluate_readonly_arrayref(
739 self, ref, clause_lists
742 Evaluates a read-only access to an Array inside the task region, and
743 computes any data-sharing clauses and dependency clauses based upon the
746 This is done by evaluating each of the array indices, and determining
748 1. A Literal index, in which case we need a dependency to that
749 specific section of the array.
750 2. A Reference index, in which case we need a dependency to the section
751 of the array represented by that Reference.
752 3. A Binary Operation, in which case the code calls
753 `_handle_index_binop` to evaluate any additional dependencies.
755 Once these have been computed, any new dependencies are added into the
756 in_list, and the array reference itself will be added to the
757 shared_list if not already present.
759 :param node: The Reference to be evaluated.
760 :type node: :py:class:`psyclone.psyir.nodes.Reference`
761 :param clause_lists: The namedtuple containing the lists storing the
763 :type clause_lists: namedtuple(
765 :py:class:`psyclone.psyir.nodes.Reference`
767 firstprivate_list=List[
768 :py:class:`psyclone.psyir.nodes.Reference`
771 :py:class:`psyclone.psyir.nodes.Reference`
774 :py:class:`psyclone.psyir.nodes.Reference`
777 :py:class:`psyclone.psyir.nodes.Reference`
780 :raises GenerationError: If an array index is a shared variable.
781 :raises GenerationError: If an array index is not a Reference, Literal
801 for dim, index
in enumerate(ref.indices):
803 if type(index)
is Reference:
811 index
not in clause_lists.private_list
812 and index
not in clause_lists.firstprivate_list
814 clause_lists.firstprivate_list.append(index.copy())
819 if index.symbol
in child_loop_vars:
821 full_range = ref.get_full_range(dim)
822 index_list.append(full_range)
835 index_list.append(index.copy())
838 f
"Shared variable access used "
839 f
"as an array index inside an "
840 f
"OMPTaskDirective which is not "
841 f
"supported. Variable name is '{index.symbol.name}'. "
842 f
"The full access is '{ref.debug_string()}'."
844 elif isinstance(index, BinaryOperation):
849 index, index_list, clause_lists
851 elif isinstance(index, Literal):
853 index_list.append(index.copy())
857 f
"'{type(index).__name__}' object is not allowed to "
858 f
"appear in an array index "
859 f
"expression inside an "
860 f
"OMPTaskDirective. The index was "
861 f
"'{index.debug_string()}'."
867 clause_lists.in_list, ref)
871 if sclause
not in clause_lists.shared_list:
872 clause_lists.shared_list.append(sclause)
874 def _evaluate_structure_with_array_reference_read(
881 Evaluates a read-only access to an array within a structure inside the
882 task region, and computes any data-sharing clauses and dependency
883 clauses based upon the access.
885 This is done by evaluating each of the array indices, and determining
887 1. A Literal index, in which case we need a dependency to that
888 specific section of the array.
889 2. A Reference index, in which case we need a dependency to the section
890 of the array represented by that Reference.
891 3. A Binary Operation, in which case the code calls
892 `_handle_index_binop` to evaluate any additional dependencies.
894 Once these have been computed, any new dependencies are added into the
895 in_list, and the array reference itself will be added to the
896 shared_list if not already present.
898 :param node: The Reference to be evaluated.
899 :type node: :py:class:`psyclone.psyir.nodes.Reference`
900 :param array_access_member: The ArrayMixin member child of the
902 :type array_access_member:
903 :py:class:psyclone.psyir.nodes.array_mixin.ArrayMixin`
904 :param clause_lists: The namedtuple containing the lists storing the
906 :type clause_lists: namedtuple(
908 :py:class:`psyclone.psyir.nodes.Reference`
910 firstprivate_list=List[
911 :py:class:`psyclone.psyir.nodes.Reference`
914 :py:class:`psyclone.psyir.nodes.Reference`
917 :py:class:`psyclone.psyir.nodes.Reference`
920 :py:class:`psyclone.psyir.nodes.Reference`
929 new_member = ref.member.copy()
931 sref_base.addchild(new_member)
942 index_list, clause_lists.in_list, sref_base,
943 array_access_member=array_access_member
947 if sclause
not in clause_lists.shared_list:
948 clause_lists.shared_list.append(sclause)
950 def _evaluate_readonly_reference(
951 self, ref, clause_lists
954 Evaluates any Reference used in a read context. This is done by
955 calling the appropriate helper functions for ArrayReferences,
956 StructureReferences or other References as appropriate.
958 :param node: The Reference to be evaluated.
959 :type node: :py:class:`psyclone.psyir.nodes.Reference`
960 :param clause_lists: The namedtuple containing the lists storing the
962 :type clause_lists: namedtuple(
964 :py:class:`psyclone.psyir.nodes.Reference`
966 firstprivate_list=List[
967 :py:class:`psyclone.psyir.nodes.Reference`
970 :py:class:`psyclone.psyir.nodes.Reference`
973 :py:class:`psyclone.psyir.nodes.Reference`
976 :py:class:`psyclone.psyir.nodes.Reference`
979 :raises GenerationError: If a StructureReference containing multiple
980 ArrayMember or ArrayOfStructuresMember as
982 :raises GenerationError: If an ArrayOfStructuresReference containing
983 an ArrayMember of ArrayOfStructuresMember as
986 if isinstance(ref, (ArrayReference, ArrayOfStructuresReference)):
989 if isinstance(ref, ArrayOfStructuresReference):
990 array_children = ref.walk((ArrayOfStructuresMember,
994 f
"PSyclone doesn't support an OMPTaskDirective "
995 f
"containing an ArrayOfStructuresReference with "
996 f
"an array accessing member. Found "
997 f
"'{ref.debug_string()}'."
1004 elif isinstance(ref, StructureReference):
1009 array_children = ref.walk((ArrayOfStructuresMember, ArrayMember))
1011 if len(array_children) > 1:
1013 f
"PSyclone doesn't support an OMPTaskDirective "
1014 f
"containing a StructureReference with multiple array"
1015 f
" accessing members. Found '{ref.debug_string()}'."
1030 base_ref, clause_lists
1032 elif isinstance(ref, Reference):
1037 def _evaluate_structure_with_array_reference_indexlist(
1040 array_access_member,
1045 Evaluates an access to an array with a structure inside the task
1046 region, and generates the index_list used by the calling function -
1047 either _evaluate_structure_with_array_reference_{read/write}.
1049 This is done by evaluating each of the array indices, and determining
1051 1. A Literal index, in which case we need a dependency to that
1052 specific section of the array.
1053 2. A Reference index, in which case we need a dependency to the section
1054 of the array represented by that Reference.
1055 3. A Binary Operation, in which case the code calls
1056 `_handle_index_binop` to evaluate any additional dependencies.
1058 Each of these results are added to the index_list, used in the callee.
1060 :param sref_base: A copy of ref containing the members included
1061 in the final reference.
1062 :type sref_base: :py:class:`psyclone.psyir.nodes.StructureReference`
1063 :param array_access_member: The ArrayMixin member child of the
1065 :type array_access_member:
1066 :py:class:psyclone.psyir.nodes.array_mixin.ArrayMixin`
1067 :param clause_lists: The namedtuple containing the lists storing the
1069 :type clause_lists: namedtuple(
1071 :py:class:`psyclone.psyir.nodes.Reference`
1073 firstprivate_list=List[
1074 :py:class:`psyclone.psyir.nodes.Reference`
1077 :py:class:`psyclone.psyir.nodes.Reference`
1080 :py:class:`psyclone.psyir.nodes.Reference`
1083 :py:class:`psyclone.psyir.nodes.Reference`
1085 :param index_list: The output References for this task.
1086 :type index_list: List[:py:class:`psyclone.psyir.nodes.Reference`]
1088 :raises GenerationError: If an array index is a shared variable.
1089 :raises GenerationError: If an array index is not a Reference, Literal
1092 for dim, index
in enumerate(array_access_member.indices):
1094 if type(index)
is Reference:
1102 index
not in clause_lists.private_list
1103 and index
not in clause_lists.firstprivate_list
1105 clause_lists.firstprivate_list.append(index.copy())
1110 if index.symbol
in child_loop_vars:
1112 full_range = sref_base.walk(ArrayMember)[0].\
1114 index_list.append(full_range)
1127 index_list.append(index.copy())
1130 f
"Shared variable access used "
1131 f
"as an array index inside an "
1132 f
"OMPTaskDirective which is not "
1133 f
"supported. Variable name is '{index.symbol.name}'. "
1134 f
"The full access is '{sref_base.debug_string()}'."
1136 elif isinstance(index, BinaryOperation):
1141 index, index_list, clause_lists
1143 elif isinstance(index, Literal):
1145 index_list.append(index.copy())
1149 f
"'{type(index).__name__}' object is not allowed to "
1150 f
"appear in an array index "
1151 f
"expression inside an "
1152 f
"OMPTaskDirective."
1155 def _evaluate_structure_with_array_reference_write(
1158 array_access_member,
1162 Evaluates a write access to an array within a structure inside the
1163 task region, and computes any data-sharing clauses and dependency
1164 clauses based upon the access.
1166 This is done by evaluating each of the array indices, and determining
1168 1. A Literal index, in which case we need a dependency to that
1169 specific section of the array.
1170 2. A Reference index, in which case we need a dependency to the section
1171 of the array represented by that Reference.
1172 3. A Binary Operation, in which case the code calls
1173 `_handle_index_binop` to evaluate any additional dependencies.
1175 Once these have been computed, any new dependencies are added into the
1176 out_list, and the array reference itself will be added to the
1177 shared_list if not already present.
1179 :param ref: The Reference to be evaluated.
1180 :type ref: :py:class:`psyclone.psyir.nodes.Reference`
1181 :param array_access_member: The ArrayMixin member child of the
1183 :type array_access_member:
1184 :py:class:psyclone.psyir.nodes.array_mixin.ArrayMixin`
1185 :param clause_lists: The namedtuple containing the lists storing the
1187 :type clause_lists: namedtuple(
1189 :py:class:`psyclone.psyir.nodes.Reference`
1191 firstprivate_list=List[
1192 :py:class:`psyclone.psyir.nodes.Reference`
1195 :py:class:`psyclone.psyir.nodes.Reference`
1198 :py:class:`psyclone.psyir.nodes.Reference`
1201 :py:class:`psyclone.psyir.nodes.Reference`
1208 new_member = ref.member.copy()
1210 sref_base.addchild(new_member)
1220 array_access_member,
1228 index_list, clause_lists.out_list, sref_base,
1229 array_access_member=array_access_member
1233 if sclause
not in clause_lists.shared_list:
1234 clause_lists.shared_list.append(sclause)
1236 def _evaluate_write_arrayref(
1237 self, ref, clause_lists
1240 Evaluates a write access to an Array inside the task region, and
1241 computes any data-sharing clauses and dependency clauses based upon the
1244 This is done by evaluating each of the array indices, and determining
1246 1. A Literal index, in which case we need a dependency to that
1247 specific section of the array.
1248 2. A Reference index, in which case we need a dependency to the section
1249 of the array represented by that Reference.
1250 3. A Binary Operation, in which case the code calls
1251 `_handle_index_binop` to evaluate any additional dependencies.
1253 Once these have been computed, any new dependencies are added into the
1254 out_list, and the array reference itself will be added to the
1255 shared_list if not already present.
1257 :param ref: The Reference to be evaluated.
1258 :type ref: :py:class:`psyclone.psyir.nodes.Reference`
1259 :param clause_lists: The namedtuple containing the lists storing the
1261 :type clause_lists: namedtuple(
1263 :py:class:`psyclone.psyir.nodes.Reference`
1265 firstprivate_list=List[
1266 :py:class:`psyclone.psyir.nodes.Reference`
1269 :py:class:`psyclone.psyir.nodes.Reference`
1272 :py:class:`psyclone.psyir.nodes.Reference`
1275 :py:class:`psyclone.psyir.nodes.Reference`
1278 :raises GenerationError: If an array index is a shared variable.
1290 for dim, index
in enumerate(ref.indices):
1291 if isinstance(index, Literal):
1293 index_list.append(index.copy())
1294 elif isinstance(index, Reference):
1300 index
not in clause_lists.private_list
1301 and index
not in clause_lists.firstprivate_list
1303 clause_lists.firstprivate_list.append(index.copy())
1308 if index.symbol
in child_loop_vars:
1310 full_range = ref.walk(ArrayMixin)[0].get_full_range(
1313 index_list.append(full_range)
1326 if index
in clause_lists.firstprivate_list:
1327 index_list.append(index.copy())
1333 full_range = ref.walk(ArrayMixin)[0].\
1335 index_list.append(full_range)
1338 f
"Shared variable access used "
1339 f
"as an array index inside an "
1340 f
"OMPTaskDirective which is not "
1341 f
"supported. Variable name is '{index.symbol.name}'. "
1342 f
"The full access is '{ref.debug_string()}'."
1344 elif isinstance(index, BinaryOperation):
1346 index, index_list, clause_lists
1352 index_list, clause_lists.out_list, ref
1356 if sclause
not in clause_lists.shared_list:
1357 clause_lists.shared_list.append(sclause)
1359 def _evaluate_write_baseref(
1360 self, ref, clause_lists
1363 Evaluates a write to a non-ArrayReference reference. If the variable
1364 is declared private in the parent parallel region, then the variable
1365 is added to the private clause for this task.
1367 If the variable is not private (therefore is shared), it is added to
1368 the shared and output dependence lists for this task region.
1370 :param ref: The Reference to be evaluated.
1371 :type ref: :py:class:`psyclone.psyir.nodes.Reference`
1372 :param clause_lists: The namedtuple containing the lists storing the
1374 :type clause_lists: namedtuple(
1376 :py:class:`psyclone.psyir.nodes.Reference`
1378 firstprivate_list=List[
1379 :py:class:`psyclone.psyir.nodes.Reference`
1382 :py:class:`psyclone.psyir.nodes.Reference`
1385 :py:class:`psyclone.psyir.nodes.Reference`
1388 :py:class:`psyclone.psyir.nodes.Reference`
1394 if is_private
and ref
not in clause_lists.private_list:
1395 clause_lists.private_list.append(ref.copy())
1398 if ref
not in clause_lists.shared_list:
1399 clause_lists.shared_list.append(ref.copy())
1400 if ref
not in clause_lists.out_list:
1401 clause_lists.out_list.append(ref.copy())
1403 def _evaluate_write_reference(
1404 self, ref, clause_lists
1407 Evaluates a write to any Reference in the task region. This is done by
1408 calling the appropriate subfunction depending on the type of the
1411 :param ref: The Reference to be evaluated.
1412 :type ref: :py:class:`psyclone.psyir.nodes.Reference`
1413 :param clause_lists: The namedtuple containing the lists storing the
1415 :type clause_lists: namedtuple(
1417 :py:class:`psyclone.psyir.nodes.Reference`
1419 firstprivate_list=List[
1420 :py:class:`psyclone.psyir.nodes.Reference`
1423 :py:class:`psyclone.psyir.nodes.Reference`
1426 :py:class:`psyclone.psyir.nodes.Reference`
1429 :py:class:`psyclone.psyir.nodes.Reference`
1432 :raises GenerationError: If a StructureReference containing multiple
1433 ArrayMember or ArrayOfStructuresMember as
1435 :raises GenerationError: If an ArrayOfStructuresReference containing
1436 an ArrayMember of ArrayOfStructuresMember as
1439 if isinstance(ref, (ArrayReference, ArrayOfStructuresReference)):
1442 if isinstance(ref, ArrayOfStructuresReference):
1443 array_children = ref.walk((ArrayOfStructuresMember,
1447 f
"PSyclone doesn't support an OMPTaskDirective "
1448 f
"containing an ArrayOfStructuresReference with "
1449 f
"an array accessing member. Found "
1450 f
"'{ref.debug_string()}'."
1457 elif isinstance(ref, StructureReference):
1462 array_children = ref.walk((ArrayOfStructuresMember, ArrayMember))
1464 if len(array_children) > 1:
1466 f
"PSyclone doesn't support an OMPTaskDirective "
1467 f
"containing a StructureReference with multiple array"
1468 f
" accessing members. Found '{ref.debug_string()}'."
1480 base_ref, clause_lists
1482 elif isinstance(ref, Reference):
1487 raise InternalError(f
"PSyclone can't handle an OMPTaskDirective "
1488 f
"containing an assignment with a LHS that "
1489 f
"is not a Reference. Found "
1490 f
"'{ref.debug_string()}'.")
1492 def _evaluate_assignment(
1498 Evaluates an Assignment node within this task region. This is done
1499 by calling the appropriate subfunction on each reference on the
1500 LHS and RHS of the Assignment.
1502 :param ref: The Assignment to be evaluated.
1503 :type ref: :py:class:`psyclone.psyir.nodes.Assignment`
1504 :param clause_lists: The namedtuple containing the lists storing the
1506 :type clause_lists: namedtuple(
1508 :py:class:`psyclone.psyir.nodes.Reference`
1510 firstprivate_list=List[
1511 :py:class:`psyclone.psyir.nodes.Reference`
1514 :py:class:`psyclone.psyir.nodes.Reference`
1517 :py:class:`psyclone.psyir.nodes.Reference`
1520 :py:class:`psyclone.psyir.nodes.Reference`
1523 lhs = node.children[0]
1524 rhs = node.children[1]
1531 references = rhs.walk(Reference)
1546 for ref
in references:
1547 if isinstance(ref.parent, ArrayMixin):
1550 if ref.symbol != parent_var:
1562 parent_var, [rhs.copy()], node,
1587 for ref
in references:
1588 if isinstance(ref.parent, ArrayMixin):
1590 for index, proxy_var
in enumerate(key_list):
1591 if ref.symbol == proxy_var:
1614 for intrinsic
in node.walk(IntrinsicCall):
1617 for ref
in references:
1620 if ref.ancestor(IntrinsicCall):
1632 Evaluates a Loop node within this task Region. This is done in several
1634 1. Check the loop variable, start/stop/step values, and ensure that
1635 they are valid. The loop variable must not be shared and the start,
1636 stop, and step variables are not ArrayReferences. It also detects if
1637 the loop variable is a "proxy", i.e. it represents a parent loop's
1638 variable as a chunked loop variable. Any variables that are not yet
1639 private, firstprivate or shared will be declared as firstprivate (as
1640 they are read before being accessed elsewhere).
1641 2. Loop through each of the nodes in the Loop's Schedule child, and
1642 evaluate them through the _evaluate_node call.
1644 :param node: The Loop to be evaluated.
1645 :type node: :py:class:`psyclone.psyir.nodes.Loop`
1646 :param clause_lists: The namedtuple containing the lists storing the
1648 :type clause_lists: namedtuple(
1650 :py:class:`psyclone.psyir.nodes.Reference`
1652 firstprivate_list=List[
1653 :py:class:`psyclone.psyir.nodes.Reference`
1656 :py:class:`psyclone.psyir.nodes.Reference`
1659 :py:class:`psyclone.psyir.nodes.Reference`
1662 :py:class:`psyclone.psyir.nodes.Reference`
1665 :raises GenerationError: If the loop variable is a shared variable.
1666 :raises GenerationError: If the loop start, stop or step expression
1667 contains an ArrayReference.
1668 :raises GenerationError: If the loop step expression contains an
1673 loop_var = node.variable
1674 start_val = node.start_expr
1675 stop_val = node.stop_expr
1676 step_val = node.step_expr
1682 start_val_refs = start_val.walk(Reference)
1683 if len(start_val_refs) == 1
and isinstance(
1684 start_val_refs[0], Reference
1690 if start_val_refs[0].symbol == parent_var:
1691 to_remove = loop_var
1694 parent_var, [
Reference(parent_var)], node,
1700 remove_child_var =
None
1701 if to_remove
is None:
1703 remove_child_var = loop_var
1711 "Found shared loop variable which is "
1712 "not allowed in OpenMP Task directive. "
1713 f
"Variable name is {loop_var_ref.name}"
1715 if loop_var_ref
not in clause_lists.firstprivate_list:
1716 if loop_var_ref
not in clause_lists.private_list:
1717 clause_lists.private_list.append(loop_var_ref)
1721 if to_remove
is not None:
1725 if parent_var_ref
not in clause_lists.firstprivate_list:
1726 clause_lists.firstprivate_list.append(parent_var_ref.copy())
1730 for ref
in start_val_refs:
1731 if isinstance(ref, (ArrayReference, ArrayOfStructuresReference)):
1733 f
"'{type(ref).__name__}' not supported in "
1734 f
"the start variable of a Loop in a "
1735 f
"OMPTaskDirective node. The start expression is "
1736 f
"'{node.start_expr.debug_string()}'."
1739 icall = ref.ancestor(IntrinsicCall)
1740 if icall
and icall.intrinsic.is_inquiry:
1744 if isinstance(ref, StructureReference):
1750 if ref_copy
not in clause_lists.shared_list:
1751 clause_lists.shared_list.append(ref_copy.copy())
1752 if ref_copy
not in clause_lists.in_list:
1753 clause_lists.in_list.append(ref_copy.copy())
1756 ref
not in clause_lists.firstprivate_list
1757 and ref
not in clause_lists.private_list
1758 and ref
not in clause_lists.shared_list
1760 clause_lists.firstprivate_list.append(ref.copy())
1762 stop_val_refs = stop_val.walk(Reference)
1763 for ref
in stop_val_refs:
1764 if isinstance(ref, (ArrayReference, ArrayOfStructuresReference)):
1766 f
"'{type(ref).__name__}' not supported in "
1767 f
"the stop variable of a Loop in a "
1768 f
"OMPTaskDirective node. The stop expression is "
1769 f
"'{node.stop_expr.debug_string()}'."
1772 icall = ref.ancestor(IntrinsicCall)
1773 if icall
and icall.intrinsic.is_inquiry:
1777 if isinstance(ref, StructureReference):
1782 if ref_copy
not in clause_lists.shared_list:
1783 clause_lists.shared_list.append(ref_copy.copy())
1784 if ref_copy
not in clause_lists.in_list:
1785 clause_lists.in_list.append(ref_copy.copy())
1788 ref
not in clause_lists.firstprivate_list
1789 and ref
not in clause_lists.private_list
1790 and ref
not in clause_lists.shared_list
1792 clause_lists.firstprivate_list.append(ref.copy())
1794 step_val_refs = step_val.walk(Reference)
1795 for ref
in step_val_refs:
1796 if isinstance(ref, (ArrayReference, ArrayOfStructuresReference)):
1798 f
"'{type(ref).__name__}' not supported in "
1799 f
"the step variable of a Loop in a "
1800 f
"OMPTaskDirective node. The step expression is "
1801 f
"'{node.step_expr.debug_string()}'."
1805 if ref.ancestor(IntrinsicCall):
1807 f
"IntrinsicCall not supported in the step variable "
1808 f
"of a Loop in an OMPTaskDirective node. The step "
1809 f
"expression is '{node.step_expr.debug_string()}'."
1813 if isinstance(ref, StructureReference):
1818 if ref_copy
not in clause_lists.shared_list:
1819 clause_lists.shared_list.append(ref_copy.copy())
1820 if ref_copy
not in clause_lists.in_list:
1821 clause_lists.in_list.append(ref_copy.copy())
1824 ref
not in clause_lists.firstprivate_list
1825 and ref
not in clause_lists.private_list
1826 and ref
not in clause_lists.shared_list
1828 clause_lists.firstprivate_list.append(ref.copy())
1833 for child_node
in node.children[3].children:
1840 if to_remove
is not None:
1843 if remove_child_var
is not None:
1846 def _evaluate_ifblock(
1852 Evaluates an ifblock inside a task region. This is done by calling
1853 _evaluate_readonly_reference on each Reference inside the if condition,
1854 and by calling _evaluate_node on each Node inside the if_body and
1857 :param node: The IfBlock to be evaluated.
1858 :type node: :py:class:`psyclone.psyir.nodes.IfBlock`
1859 :param clause_lists: The namedtuple containing the lists storing the
1861 :type clause_lists: namedtuple(
1863 :py:class:`psyclone.psyir.nodes.Reference`
1865 firstprivate_list=List[
1866 :py:class:`psyclone.psyir.nodes.Reference`
1869 :py:class:`psyclone.psyir.nodes.Reference`
1872 :py:class:`psyclone.psyir.nodes.Reference`
1875 :py:class:`psyclone.psyir.nodes.Reference`
1878 for ref
in node.condition.walk(Reference):
1885 for child_node
in node.if_body.children:
1891 if node.else_body
is not None:
1892 for child_node
in node.else_body.children:
1898 def _evaluate_intrinsic(
1904 Evaluates an IntrinsicCall node inside the task region.
1905 The function immediately stops if the intrinsic has
1906 `is_inquiry` set to True, as no data dependency occurs
1908 All other allowed intrinsics are read-only, so the relevant
1909 helper functions are called on each argument.
1911 :param node: The IntrinsicCall to be evaluated.
1912 :type node: :py:class:`psyclone.psyir.nodes.IntrinsicCall`
1913 :param clause_lists: The namedtuple containing the lists storing the
1915 :type clause_lists: namedtuple(
1917 :py:class:`psyclone.psyir.nodes.Reference`
1919 firstprivate_list=List[
1920 :py:class:`psyclone.psyir.nodes.Reference`
1923 :py:class:`psyclone.psyir.nodes.Reference`
1926 :py:class:`psyclone.psyir.nodes.Reference`
1929 :py:class:`psyclone.psyir.nodes.Reference`
1934 if node.intrinsic.is_inquiry:
1939 for arg
in node.arguments:
1940 for ref
in arg.walk(Reference):
1951 Evaluates a generic Node inside the task region. Calls the appropriate
1952 handler depending on whether the node is an Assignment, Loop or
1955 :param node: The Node to be evaluated.
1956 :type node: :py:class:`psyclone.psyir.nodes.Node`
1957 :param clause_lists: The namedtuple containing the lists storing the
1959 :type clause_lists: namedtuple(
1961 :py:class:`psyclone.psyir.nodes.Reference`
1963 firstprivate_list=List[
1964 :py:class:`psyclone.psyir.nodes.Reference`
1967 :py:class:`psyclone.psyir.nodes.Reference`
1970 :py:class:`psyclone.psyir.nodes.Reference`
1973 :py:class:`psyclone.psyir.nodes.Reference`
1977 if isinstance(node, Assignment):
1983 elif isinstance(node, Loop):
1989 elif isinstance(node, IfBlock):
2002 def _compute_clauses(self):
2004 Computes the clauses for this OMPTaskDirective.
2006 The OMPTaskDirective must have exactly 1 child, which must be a Loop.
2007 Upon confirming this, the function calls _evaluate_node to compute all
2008 data-sharing attributes and dependencies.
2009 The clauses are then built up from those, and returned.
2011 :returns: The clauses computed for this OMPTaskDirective.
2012 :rtype: List[:py:class:`psyclone.psyir.nodes.OMPPrivateClause`,
2013 :py:class:`psyclone.psyir.nodes.OMPFirstprivateClause`,
2014 :py:class:`psyclone.psyir.nodes.OMPSharedClause`,
2015 :py:class:`psyclone.psyir.nodes.OMPDependClause`,
2016 :py:class:`psyclone.psyir.nodes.OMPDependClause`]
2018 :raises GenerationError: If the OMPTaskDirective has multiple children.
2019 :raises GenerationError: If the OMPTaskDirective's child is not a Loop.
2026 firstprivate_list = []
2030 clause_lists = self.
_clause_lists_clause_lists(private_list, firstprivate_list,
2031 shared_list, in_list, out_list)
2045 "OMPTaskDirective must have exactly one Loop"
2047 f
"{len(self.children[0].children)} "
2052 "OMPTaskDirective must have exactly one Loop"
2054 f
"'{type(self.children[0].children[0])}'"
2066 for ref
in private_list:
2070 firstprivate_list.append(ref)
2072 private_clause.addchild(ref)
2073 for ref
in firstprivate_list:
2074 firstprivate_clause.addchild(ref)
2076 for ref
in shared_list:
2077 shared_clause.addchild(ref)
2080 depend_type=OMPDependClause.DependClauseTypes.IN
2085 if isinstance(ref.symbol, DataSymbol)
and ref.symbol.is_constant:
2087 if (ref.symbol.is_import
and
2088 ref.symbol.get_external_symbol().is_constant):
2090 in_clause.addchild(ref)
2092 depend_type=OMPDependClause.DependClauseTypes.OUT
2094 for ref
in out_list:
2095 out_clause.addchild(ref)
2099 firstprivate_clause,
2107 Lowers the structure of the PSyIR tree inside the Directive
2108 to generate the Clauses that are required for this Directive.
2116 if self.
walkwalk(Kern):
2118 "Attempted to lower to OMPTaskDirective "
2119 "node, but the node contains a Kern "
2120 "which must be inlined first."
2123 for child
in self.
walkwalk(Call):
2124 if not isinstance(child, IntrinsicCall):
2126 "Attempted to lower to OMPTaskDirective "
2127 "node, but the node contains a Call "
2128 "which must be inlined first."
2133 f
"Attempted to lower to OMPTaskDirective "
2134 f
"node, but the node contains a "
2135 f
"'{child.debug_string()}' intrinsic call, which "
2136 f
"is not supported."
2142 firstprivate_clause,
2150 self.
addchildaddchild(old_children[0])
2151 self.
addchildaddchild(private_clause)
2152 self.
addchildaddchild(firstprivate_clause)
2153 self.
addchildaddchild(shared_clause)
def _find_parent_loop_vars(self)
def _handle_index_binop(self, node, index_list, clause_lists)
def _handle_proxy_loop_index(self, index_list, dim, index, clause_lists)
def _evaluate_structure_with_array_reference_read(self, ref, array_access_member, clause_lists)
def _evaluate_structure_with_array_reference_indexlist(self, sref_base, array_access_member, clause_lists, index_list)
def _evaluate_readonly_baseref(self, ref, clause_lists)
def _create_binops_from_step_and_divisors(self, node, ref, step_val, divisor, modulo, ref_index)
def _evaluate_readonly_arrayref(self, ref, clause_lists)
def _add_dependencies_from_index_list(self, index_list, dependency_list, reference, array_access_member=None)
def _evaluate_node(self, node, clause_lists)
def _evaluate_loop(self, node, clause_lists)
def _evaluate_ifblock(self, node, clause_lists)
def _is_reference_private(self, ref)
def _evaluate_structure_with_array_reference_write(self, ref, array_access_member, clause_lists)
def _evaluate_write_reference(self, ref, clause_lists)
def _evaluate_assignment(self, node, clause_lists)
def _evaluate_readonly_reference(self, ref, clause_lists)
def _evaluate_write_arrayref(self, ref, clause_lists)
def lower_to_language_level(self)
def _evaluate_intrinsic(self, node, clause_lists)
def _compute_clauses(self)
def _evaluate_write_baseref(self, ref, clause_lists)
def _array_for_clause_combination_helper(self, ref, temp_list, base_member=None)
def children(self, my_children)
def addchild(self, child, index=None)
def replace_with(self, node, keep_name_in_context=True)
def pop_all_children(self)
def walk(self, my_type, stop_type=None, depth=None)
def ancestor(self, my_type, excluding=None, include_self=False, limit=None, shared_with=None)